Page MenuHomeHEPForge

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: trunk/share/tests/functional_tests/ref-output/openloops_9.ref
===================================================================
--- trunk/share/tests/functional_tests/ref-output/openloops_9.ref (revision 8293)
+++ trunk/share/tests/functional_tests/ref-output/openloops_9.ref (revision 8294)
@@ -1,99 +1,99 @@
?openmp_logging = false
?vis_history = false
?integration_timer = false
?pacify = true
?use_vamp_equivalences = false
?alphas_is_fixed = false
?alphas_from_mz = true
?alphas_from_lambda_qcd = false
alpha_power = 2
alphas_power = 0
[user variable] pr = PDG(2, 1, -2, -1)
?openloops_use_collier = false
$loop_me_method = "openloops"
openmp_num_threads = 1
| Process library 'openloops_9_lib': recorded process 'openloops_9_p1'
seed = 42
sqrts = 1.30000E+04
| Integrate: current process library needs compilation
| Process library 'openloops_9_lib': compiling ...
| Process library 'openloops_9_lib': writing makefile
| Process library 'openloops_9_lib': removing old files
| Process library 'openloops_9_lib': writing driver
| Process library 'openloops_9_lib': creating source code
| Process library 'openloops_9_lib': compiling sources
| Process library 'openloops_9_lib': linking
| Process library 'openloops_9_lib': loading
| Process library 'openloops_9_lib': ... success.
| Integrate: compilation done
| QCD alpha: using a running strong coupling
| RNG: Initializing TAO random-number generator
| RNG: Setting seed for random-number generator to 42
| Initializing integration for process openloops_9_p1:
| Beam structure: p, p => pdf_builtin
| Beam data (collision):
| p (mass = 0.0000000E+00 GeV)
| p (mass = 0.0000000E+00 GeV)
| sqrts = 1.300000000000E+04 GeV
| Initialized builtin PDF CTEQ6L
| Phase space: generating configuration ...
| Phase space: ... success.
| Phase space: writing configuration file 'openloops_9_p1.i1.phs'
| Phase space: generating configuration ...
| Phase space: ... success.
| Phase space: writing configuration file 'openloops_9_p1.i3.phs'
| One-Loop-Provider: Using OpenLoops
| Loading library: [...]
| One-Loop-Provider: Using OpenLoops
| Loading library: [...]
| ------------------------------------------------------------------------
| Process [scattering]: 'openloops_9_p1'
| Library name = 'openloops_9_lib'
| Process index = 1
| Process components:
| 1: 'openloops_9_p1_i1': u:d:ubar:dbar, u:d:ubar:dbar => e-, e+ [inactive]
| 2: 'openloops_9_p1_i2': gl:dbar:d:ubar:u:dbar:d:ubar:u, dbar:d:ubar:u:gl:dbar:d:ubar:u => e-, e+, d:dbar:u:ubar:d:dbar:u:ubar:gl [inactive], [real]
| 3: 'openloops_9_p1_i3': u:d:ubar:dbar, u:d:ubar:dbar => e-, e+ [openloops], [virtual]
| 4: 'openloops_9_p1_i4': u:d:ubar:dbar, u:d:ubar:dbar => e-, e+ [inactive], [subtraction]
| 5: 'openloops_9_p1_i5': u:d:ubar:dbar, u:d:ubar:dbar => e-, e+ [inactive], [dglap]
| ------------------------------------------------------------------------
| Phase space: 2 channels, 2 dimensions
| Phase space: found 2 channels, collected in 2 groves.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Phase space: 2 channels, 5 dimensions
| Phase space: found 2 channels, collected in 2 groves.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Phase space: 2 channels, 2 dimensions
| Phase space: found 2 channels, collected in 2 groves.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Phase space: 2 channels, 3 dimensions
| Phase space: found 2 channels, collected in 2 groves.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Beam structure: pdf_builtin, none => none, pdf_builtin
| Beam structure: 2 channels, 2 dimensions
| Applying user-defined cuts.
| Using user-defined general scale.
| Starting integration for process 'openloops_9_p1' part 'virtual'
| Integrate: iterations = 1:100:"gw"
| Integrator: 2 chains, 2 channels, 4 dimensions
| Integrator: 100 initial calls, 20 bins, stratified = T
| Integrator: VAMP
|=============================================================================|
| It Calls Integral[fb] Error[fb] Err[%] Acc Eff[%] Chi2 N[It] |
|=============================================================================|
- 1 100 -4.396E+04 3.36E+03 7.63 0.76 45.2
+ 1 100 -3.837E+04 3.88E+03 10.12 1.01 22.5
|-----------------------------------------------------------------------------|
- 1 100 -4.396E+04 3.36E+03 7.63 0.76 45.2
+ 1 100 -3.837E+04 3.88E+03 10.12 1.01 22.5
|=============================================================================|
| Integrate: sum of all components
|=============================================================================|
| It Calls Integral[fb] Error[fb] Err[%] Acc Eff[%] Chi2 N[It] |
|=============================================================================|
- 1 0 -4.396E+04 3.36E+03 7.63 0.00 0.0
+ 1 0 -3.837E+04 3.88E+03 10.12 0.00 0.0
|=============================================================================|
| WHIZARD run finished.
|=============================================================================|
Index: trunk/share/tests/functional_tests/ref-output/nlo_6.ref
===================================================================
--- trunk/share/tests/functional_tests/ref-output/nlo_6.ref (revision 8293)
+++ trunk/share/tests/functional_tests/ref-output/nlo_6.ref (revision 8294)
@@ -1,134 +1,134 @@
?openmp_logging = false
?vis_history = false
?integration_timer = false
?pacify = true
$loop_me_method = "dummy"
openmp_num_threads = 1
SM.wtop => 0.00000E+00
SM.mtop => 1.75000E+02
?use_vamp_equivalences = false
?alphas_is_fixed = false
?alphas_from_mz = true
?nlo_use_real_partition = true
real_partition_scale = 1.00000E+01
$fks_mapping_type = "resonances"
[user variable] pr = PDG(2, 1, -2, -1)
| Process library 'nlo_6_lib': recorded process 'nlo_6_p1'
seed = 1
sqrts = 1.30000E+04
error_threshold = 1.00000E-05
| Integrate: current process library needs compilation
| Process library 'nlo_6_lib': compiling ...
| Process library 'nlo_6_lib': writing makefile
| Process library 'nlo_6_lib': removing old files
| Process library 'nlo_6_lib': writing driver
| Process library 'nlo_6_lib': creating source code
| Process library 'nlo_6_lib': compiling sources
| Process library 'nlo_6_lib': linking
| Process library 'nlo_6_lib': loading
| Process library 'nlo_6_lib': ... success.
| Integrate: compilation done
| QCD alpha: using a running strong coupling
| RNG: Initializing TAO random-number generator
| RNG: Setting seed for random-number generator to 1
| Initializing integration for process nlo_6_p1:
| Beam structure: p, p => pdf_builtin
| Beam data (collision):
| p (mass = 0.0000000E+00 GeV)
| p (mass = 0.0000000E+00 GeV)
| sqrts = 1.300000000000E+04 GeV
| Initialized builtin PDF CTEQ6L
| Phase space: generating configuration ...
| Phase space: ... success.
| Phase space: writing configuration file 'nlo_6_p1.i1.phs'
| Phase space: generating configuration ...
| Phase space: ... success.
| Phase space: writing configuration file 'nlo_6_p1.i3.phs'
| Phase space: generating configuration ...
| Phase space: ... success.
| Phase space: writing configuration file 'nlo_6_p1.i6.phs'
Warning: No resonances found. Proceed in usual FKS mode.
| ------------------------------------------------------------------------
| Process [scattering]: 'nlo_6_p1'
| Library name = 'nlo_6_lib'
| Process index = 1
| Process components:
| 1: 'nlo_6_p1_i1': u:d:ubar:dbar, u:d:ubar:dbar => t, tbar [inactive]
| 2: 'nlo_6_p1_i2': gl:dbar:d:ubar:u:dbar:d:ubar:u, dbar:d:ubar:u:gl:dbar:d:ubar:u => t, tbar, d:dbar:u:ubar:d:dbar:u:ubar:gl [inactive], [real]
| 3: 'nlo_6_p1_i3': u:d:ubar:dbar, u:d:ubar:dbar => t, tbar [dummy], [virtual]
| 4: 'nlo_6_p1_i4': u:d:ubar:dbar, u:d:ubar:dbar => t, tbar [inactive], [subtraction]
| 5: 'nlo_6_p1_i5': u:d:ubar:dbar, u:d:ubar:dbar => t, tbar [inactive], [dglap]
| 6: 'nlo_6_p1_i6': gl:dbar:d:ubar:u:dbar:d:ubar:u, dbar:d:ubar:u:gl:dbar:d:ubar:u => t, tbar, d:dbar:u:ubar:d:dbar:u:ubar:gl [inactive], [real]
| 7: 'nlo_6_p1_i7': u:d:ubar:dbar, u:d:ubar:dbar => t, tbar [inactive], [mismatch]
| ------------------------------------------------------------------------
| Phase space: 1 channels, 2 dimensions
| Phase space: found 1 channel, collected in 1 grove.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Phase space: 1 channels, 5 dimensions
| Phase space: found 1 channel, collected in 1 grove.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Phase space: 1 channels, 2 dimensions
| Phase space: found 1 channel, collected in 1 grove.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Phase space: 1 channels, 3 dimensions
| Phase space: found 1 channel, collected in 1 grove.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Phase space: 27 channels, 5 dimensions
| Phase space: found 27 channels, collected in 7 groves.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Phase space: 1 channels, 5 dimensions
| Phase space: found 1 channel, collected in 1 grove.
| Phase space: no equivalences between channels used.
| Phase space: wood
| Beam structure: pdf_builtin, none => none, pdf_builtin
| Beam structure: 1 channels, 2 dimensions
Warning: No cuts have been defined.
| Starting integration for process 'nlo_6_p1' part 'virtual'
| Integrate: iterations = 1:100
| Integrator: 1 chains, 1 channels, 4 dimensions
| Integrator: 100 initial calls, 20 bins, stratified = T
| Integrator: VAMP
|=============================================================================|
| It Calls Integral[fb] Error[fb] Err[%] Acc Eff[%] Chi2 N[It] |
|=============================================================================|
- 1 100 1.367E+02 6.97E+01 50.98 5.10 2.4
+ 1 100 1.372E+02 7.00E+01 50.99 5.10 2.4
|-----------------------------------------------------------------------------|
- 1 100 1.367E+02 6.97E+01 50.98 5.10 2.4
+ 1 100 1.372E+02 7.00E+01 50.99 5.10 2.4
|=============================================================================|
| Integrate: sum of all components
|=============================================================================|
| It Calls Integral[fb] Error[fb] Err[%] Acc Eff[%] Chi2 N[It] |
|=============================================================================|
- 1 0 1.367E+02 6.97E+01 50.98 0.00 2.4
+ 1 0 1.372E+02 7.00E+01 50.99 0.00 2.4
|=============================================================================|
| There were no errors and 2 warning(s).
| WHIZARD run finished.
|=============================================================================|
Total number of regions: 20
alr || flst_real || i_real || em || mul || nreg || ftuples || flst_born || i_born
1 || [ -2, 2, 6, -6, 21] || 1 || 3 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ -2, 2, 6, -6] || 1
2 || [ -2, 2, 6, -6, 21] || 1 || 4 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ -2, 2, 6, -6] || 1
3 || [ -2, 2, 6, -6, 21] || 1 || 0 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ -2, 2, 6, -6] || 1
4 || [ -2, 21, 6, -6, -2] || 2 || 2 || 1 || 1 || {(2,5)} || [ -2, 2, 6, -6] || 1
5 || [ 2, -2, 6, -6, 21] || 3 || 3 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ 2, -2, 6, -6] || 2
6 || [ 2, -2, 6, -6, 21] || 3 || 4 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ 2, -2, 6, -6] || 2
7 || [ 2, -2, 6, -6, 21] || 3 || 0 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ 2, -2, 6, -6] || 2
8 || [ 2, 21, 6, -6, 2] || 4 || 2 || 1 || 1 || {(2,5)} || [ 2, -2, 6, -6] || 2
9 || [ -1, 1, 6, -6, 21] || 5 || 3 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ -1, 1, 6, -6] || 3
10 || [ -1, 1, 6, -6, 21] || 5 || 4 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ -1, 1, 6, -6] || 3
11 || [ -1, 1, 6, -6, 21] || 5 || 0 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ -1, 1, 6, -6] || 3
12 || [ -1, 21, 6, -6, -1] || 6 || 2 || 1 || 1 || {(2,5)} || [ -1, 1, 6, -6] || 3
13 || [ 1, -1, 6, -6, 21] || 7 || 3 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ 1, -1, 6, -6] || 4
14 || [ 1, -1, 6, -6, 21] || 7 || 4 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ 1, -1, 6, -6] || 4
15 || [ 1, -1, 6, -6, 21] || 7 || 0 || 1 || 3 || {(0,5),(3,5),(4,5)} || [ 1, -1, 6, -6] || 4
16 || [ 1, 21, 6, -6, 1] || 8 || 2 || 1 || 1 || {(2,5)} || [ 1, -1, 6, -6] || 4
17 || [ 21, -2, 6, -6, -2] || 9 || 1 || 1 || 1 || {(1,5)} || [ 2, -2, 6, -6] || 2
18 || [ 21, 2, 6, -6, 2] || 10 || 1 || 1 || 1 || {(1,5)} || [ -2, 2, 6, -6] || 1
19 || [ 21, -1, 6, -6, -1] || 11 || 1 || 1 || 1 || {(1,5)} || [ 1, -1, 6, -6] || 4
20 || [ 21, 1, 6, -6, 1] || 12 || 1 || 1 || 1 || {(1,5)} || [ -1, 1, 6, -6] || 3
------------------------------------------------------------------------
Index: trunk/share/tests/ext_tests_nlo/nlo_fks_delta_o_eejj.sin
===================================================================
--- trunk/share/tests/ext_tests_nlo/nlo_fks_delta_o_eejj.sin (revision 8293)
+++ trunk/share/tests/ext_tests_nlo/nlo_fks_delta_o_eejj.sin (revision 8294)
@@ -1,102 +1,102 @@
!!! Process: ee -> jj
!!! Reported by: PS on 2019-03-04
!!! Purpose: Test fks_delta_o independence of real+virtual component
!!! time ~20min
?use_vamp_equivalences = false
openmp_num_threads = 1
ms = 0
mc = 0
me = 0
alias jet = u:U:d:D:s:S:c:C:gl
$method = "openloops"
sqrts = 1 TeV
jet_algorithm = antikt_algorithm
jet_r = 0.5
?virtual_collinear_resonance_aware = false ! For some strange reason, this is not default. delta_o is only implemented in the non-RA-FKS terms.
cuts = let subevt @clustered_jets = cluster [jet] in
let subevt @pt_selected = select if Pt > 30 GeV [@clustered_jets] in
let subevt @eta_selected = select if abs(Eta) < 4 [@pt_selected] in
count [@eta_selected] >= 2
scale = let int njet = count [jet] in
if njet == 2 then
(eval Pt [extract index 1 [jet]] + eval Pt [extract index 2 [jet]]) / 2
else
(eval Pt [extract index 1 [jet]] + eval Pt [extract index 2 [jet]]
+eval Pt [extract index 3 [jet]]) / 2
endif
alpha_power = 2
alphas_power = 0
process nlo_eejj_p1_real = e1, E1 => jet, jet { nlo_calculation = real }
process nlo_eejj_p2_real = e1, E1 => jet, jet { nlo_calculation = real }
process nlo_eejj_p3_real = e1, E1 => jet, jet { nlo_calculation = real }
process nlo_eejj_p4_real = e1, E1 => jet, jet { nlo_calculation = real }
process nlo_eejj_p1_virt = e1, E1 => jet, jet { nlo_calculation = virtual }
process nlo_eejj_p2_virt = e1, E1 => jet, jet { nlo_calculation = virtual }
process nlo_eejj_p3_virt = e1, E1 => jet, jet { nlo_calculation = virtual }
process nlo_eejj_p4_virt = e1, E1 => jet, jet { nlo_calculation = virtual }
mult_call_real = 2
fks_delta_o = 0.5
seed = 12
integrate (nlo_eejj_p1_real) { iterations = 6:30000:"gw", 3:75000}
seed = 12
integrate (nlo_eejj_p1_virt) { iterations = 4:20000:"gw", 3:40000}
fks_delta_o = 1.0
seed = 12
integrate (nlo_eejj_p2_real) { iterations = 6:30000:"gw", 3:75000}
seed = 12
integrate (nlo_eejj_p2_virt) { iterations = 4:20000:"gw", 3:40000}
fks_delta_o = 1.5
seed = 12
integrate (nlo_eejj_p3_real) { iterations = 6:30000:"gw", 3:75000}
seed = 12
integrate (nlo_eejj_p3_virt) { iterations = 4:20000:"gw", 3:40000}
fks_delta_o = 2.0
seed = 12
integrate (nlo_eejj_p4_real) { iterations = 6:30000:"gw", 3:75000}
seed = 12
integrate (nlo_eejj_p4_virt) { iterations = 4:20000:"gw", 3:40000}
! Output the results
-printf "delta_i total unc real unc virtual unc"
+printf "delta_o total unc real unc virtual unc"
printf "%E %E %E %E %E %E %E" (0.5, integral (nlo_eejj_p1_real) + integral (nlo_eejj_p1_virt), sqrt (error (nlo_eejj_p1_real)**2 + error (nlo_eejj_p1_virt)**2), integral (nlo_eejj_p1_real), error (nlo_eejj_p1_real), integral (nlo_eejj_p1_virt), error (nlo_eejj_p1_virt))
printf "%E %E %E %E %E %E %E" (1.0, integral (nlo_eejj_p2_real) + integral (nlo_eejj_p2_virt), sqrt (error (nlo_eejj_p2_real)**2 + error (nlo_eejj_p2_virt)**2), integral (nlo_eejj_p2_real), error (nlo_eejj_p2_real), integral (nlo_eejj_p2_virt), error (nlo_eejj_p2_virt))
printf "%E %E %E %E %E %E %E" (1.5, integral (nlo_eejj_p3_real) + integral (nlo_eejj_p3_virt), sqrt (error (nlo_eejj_p3_real)**2 + error (nlo_eejj_p3_virt)**2), integral (nlo_eejj_p3_real), error (nlo_eejj_p3_real), integral (nlo_eejj_p3_virt), error (nlo_eejj_p3_virt))
printf "%E %E %E %E %E %E %E" (2.0, integral (nlo_eejj_p4_real) + integral (nlo_eejj_p4_virt), sqrt (error (nlo_eejj_p4_real)**2 + error (nlo_eejj_p4_virt)**2), integral (nlo_eejj_p4_real), error (nlo_eejj_p4_real), integral (nlo_eejj_p4_virt), error (nlo_eejj_p4_virt))
! Check if result is constant within 2 sigma.
expect (
integral(nlo_eejj_p1_real) + integral(nlo_eejj_p1_virt) == integral(nlo_eejj_p2_real) + integral(nlo_eejj_p2_virt)
) {
tolerance = 2 * sqrt (error(nlo_eejj_p1_real)**2 + error(nlo_eejj_p1_virt)**2 + error(nlo_eejj_p2_real)**2 + error(nlo_eejj_p2_virt)**2)
}
expect (
integral(nlo_eejj_p2_real) + integral(nlo_eejj_p2_virt) == integral(nlo_eejj_p3_real) + integral(nlo_eejj_p3_virt)
) {
tolerance = 2 * sqrt (error(nlo_eejj_p2_real)**2 + error(nlo_eejj_p2_virt)**2 + error(nlo_eejj_p3_real)**2 + error(nlo_eejj_p3_virt)**2)
}
expect (
integral(nlo_eejj_p3_real) + integral(nlo_eejj_p3_virt) == integral(nlo_eejj_p4_real) + integral(nlo_eejj_p4_virt)
) {
tolerance = 2 * sqrt (error(nlo_eejj_p3_real)**2 + error(nlo_eejj_p3_virt)**2 + error(nlo_eejj_p4_real)**2 + error(nlo_eejj_p4_virt)**2)
}
expect (
integral(nlo_eejj_p4_real) + integral(nlo_eejj_p4_virt) == integral(nlo_eejj_p1_real) + integral(nlo_eejj_p1_virt)
) {
tolerance = 2 * sqrt (error(nlo_eejj_p4_real)**2 + error(nlo_eejj_p4_virt)**2 + error(nlo_eejj_p1_real)**2 + error(nlo_eejj_p1_virt)**2)
}
Index: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog (revision 8293)
+++ trunk/ChangeLog (revision 8294)
@@ -1,1893 +1,1898 @@
ChangeLog -- Summary of changes to the WHIZARD package
Use svn log to see detailed changes.
Version 2.8.0
2019-10-24
RELEASE: version 2.8.1
+2019-09-05
+ Major progress on NLO QCD for hadron collisions:
+ - correctly assign flavor structures for alpha regions
+ - fix crossing of particles for initial state splittings
+ - correct assignment for PDF factors for real subtractions
+ - fix kinematics for collinear splittings
+ - bug fix for integrated virtual subtraction terms
+
2019-09-03
b and c jet selection in cuts and analysis
2019-08-27
Support for Intel MPI
2019-08-20
Complete (preliminary) HepMC3 support (incl.
backwards HepMC2 write/read mode)
2019-08-08
Bug fix: handle carriage returns in UFO files (non-Unix OS)
##################################################################
2019-08-07
RELEASE: version 2.8.0
2019-07-31
Complete WHIZARD UFO interface:
- general Lorentz structures
- matrix element support for general color factors
- missing features: Majorana fermions and SLHA
2019-07-20
Make WHIZARD compatible with OCaml 4.08.0+
2019-07-19
Fix version testing for LHAPDF 6.2.3 and newer
Minimal required OCaml version is now 4.02.3.
2019-04-18
Correctly generate ordered FKS tuples for alpha regions
from all possible underlying Born processes
2019-04-08
Extended O'Mega/Recola matrix element test suite
2019-03-29
Correct identical particle symmetry factors for FKS subtraction
2019-03-28
Correct assertion of spin-correlated matrix
elements for hadron collisions
2019-03-27
Bug fix for cut-off parameter delta_i for
collinear plus/minus regions
##################################################################
2019-03-27
RELEASE: version 2.7.1
2019-02-19
Further infrastructure for HepMC3 interface (v3.01.00)
2019-02-07
Explicit configure option for using debugging options
Bug fix for performance by removing unnecessary debug operations
2019-01-29
Bug fix for DGLAP remnants with cut-off parameter delta_i
2019-01-24
Radiative decay neu2 -> neu1 A added to MSSM_Hgg model
##################################################################
2019-01-21
RELEASE: version 2.7.0
2018-12-18
Support RECOLA for integrated und unintegrated subtractions
2018-12-11
FCNC top-up sector in model SM_top_anom
2018-12-05
Use libtirpc instead of SunRPC on Arch Linux etc.
2018-11-30
Display rescaling factor for weighted event samples with cuts
2018-11-29
Reintroduce check against different masses in flavor sums
Bug fix for wrong couplings in the Littlest Higgs model(s)
2018-11-22
Bug fix for rescanning events with beam structure
2018-11-09
Major refactoring of internal process data
2018-11-02
PYTHIA8 interface
2018-10-29
Flat phase space parametrization with RAMBO (on diet) implemented
2018-10-17
Revise extended test suite
2018-09-27
Process container for RECOLA processes
2018-09-15
Fixes by M. Berggren for PYTHIA6 interface
2018-09-14
First fixes after HepForge modernization
##################################################################
2018-08-23
RELEASE: version 2.6.4
2018-08-09
Infrastructure to check colored subevents
2018-07-10
Infrastructure for running WHIZARD in batch mode
2018-07-04
MPI available from distribution tarball
2018-06-03
Support Intel Fortran Compiler under MAC OS X
2018-05-07
FKS slicing parameter delta_i (initial state) implementend
2018-05-03
Refactor structure function assignment for NLO
2018-05-02
FKS slicing parameter xi_cut, delta_0 implemented
2018-04-20
Workspace subdirectory for process integration (grid/phs files)
Packing/unpacking of files at job end/start
Exporting integration results from scan loops
2018-04-13
Extended QCD NLO test suite
2018-04-09
Bug fix for Higgs Singlet Extension model
2018-04-06
Workspace subdirectory for process generation and compilation
--job-id option for creating job-specific names
2018-03-20
Bug fix for color flow matching in hadron collisions
with identical initial state quarks
2018-03-08
Structure functions quantum numbers correctly assigned for NLO
2018-02-24
Configure setup includes 'pgfortran' and 'flang'
2018-02-21
Include spin-correlated matrix elements in interactions
2018-02-15
Separate module for QED ISR structure functions
##################################################################
2018-02-10
RELEASE: version 2.6.3
2018-02-08
Improvements in memory management for PS generation
2018-01-31
Partial refactoring: quantum number assigment NLO
Initial-state QCD splittings for hadron collisions
2018-01-25
Bug fix for weighted events with VAMP2
2018-01-17
Generalized interface for Recola versions 1.3+ and 2.1+
2018-01-15
Channel equivalences also for VAMP2 integrator
2018-01-12
Fix for OCaml compiler 4.06 (and newer)
2017-12-19
RECOLA matrix elements with flavor sums can be integrated
2017-12-18
Bug fix for segmentation fault in empty resonance histories
2017-12-16
Fixing a bug in PYTHIA6 PYHEPC routine by omitting CMShowers
from transferral between PYTHIA and WHIZARD event records
2017-12-15
Event index for multiple processes in event file correct
##################################################################
2017-12-13
RELEASE: version 2.6.2
2017-12-07
User can set offset in event numbers
2017-11-29
Possibility to have more than one RECOLA process in one file
2017-11-23
Transversal/mixed (and unitarized) dim-8 operators
2017-11-16
epa_q_max replaces epa_e_max (trivial factor 2)
2017-11-15
O'Mega matrix element compilation silent now
2017-11-14
Complete expanded P-wave form factor for top threshold
2017-11-10
Incoming particles can be accessed in SINDARIN
2017-11-08
Improved handling of resonance insertion, additional parameters
2017-11-04
Added Higgs-electron coupling (SM_Higgs)
##################################################################
2017-11-03
RELEASE: version 2.6.1
2017-10-20
More than 5 NLO components possible at same time
2017-10-19
Gaussian cutoff for shower resonance matching
2017-10-12
Alternative (more efficient) method to generate
phase space file
2017-10-11
Bug fix for shower resonance histories for processes
with multiple components
2017-09-25
Bugfix for process libraries in shower resonance histories
2017-09-21
Correctly generate pT distribution for EPA remnants
2017-09-20
Set branching ratios for unstable particles also by hand
2017-09-14
Correctly generate pT distribution for ISR photons
##################################################################
2017-09-08
RELEASE: version 2.6.0
2017-09-05
Bug fix for initial state NLO QCD flavor structures
Real and virtual NLO QCD hadron collider processes
work with internal interactions
2017-09-04
Fully validated MPI integration and event generation
2017-09-01
Resonance histories for shower: full support
Bug fix in O'Mega model constraints
O'Mega allows to output a parsable form of the DAG
2017-08-24
Resonance histories in events for transferral
to parton shower (e.g. in ee -> jjjj)
2017-08-01
Alpha version of HepMC v3 interface
(not yet really functional)
2017-07-31
Beta version for RECOLA OLP support
2017-07-06
Radiation generator fix for LHC processes
2017-06-30
Fix bug for NLO with structure
functions and/or polarization
2017-06-23
Collinear limit for QED corrections works
2017-06-17
POWHEG grids generated already during integration
2017-06-12
Soft limit for QED corrections works
2017-05-16
Beta version of full MPI parallelization (VAMP2)
Check consistency of POWHEG grid files
Logfile config-summary.log for configure summary
2017-05-12
Allow polarization in top threshold
2017-05-09
Minimal demand automake 1.12.2
Silent rules for make procedures
2017-05-07
Major fix for POWHEG damping
Correctly initialize FKS ISR phasespace
##################################################################
2017-05-06
RELEASE: version 2.5.0
2017-05-05
Full UFO support (SM-like models)
Fixed-beam ISR FKS phase space
2017-04-26
QED splittings in radiation generator
2017-04-10
Retire deprecated O'Mega vertex cache files
##################################################################
2017-03-24
RELEASE: version 2.4.1
2017-03-16
Distinguish resonance charge in phase space channels
Keep track of resonance histories in phase space
Complex mass scheme default for OpenLoops amplitudes
2017-03-13
Fix helicities for polarized OpenLoops calculations
2017-03-09
Possibility to advance RNG state in rng_stream
2017-03-04
General setup for partitioning real emission
phase space
2017-03-06
Bugfix on rescan command for converting event files
2017-02-27
Alternative multi-channel VEGAS implementation
VAMP2: serial backbone for MPI setup
Smoothstep top threshold matching
2017-02-25
Single-beam structure function with
s-channel mapping supported
Safeguard against invalid process libraries
2017-02-16
Radiation generator for photon emission
2017-02-10
Fixes for NLO QCD processes (color correlations)
2017-01-16
LCIO variable takes precedence over LCIO_DIR
2017-01-13
Alternative random number generator
rng_stream (cf. L'Ecuyer et al.)
2017-01-01
Fix for multi-flavor BLHA tree
matrix elements
2016-12-31
Grid path option for VAMP grids
2016-12-28
Alpha version of Recola OLP support
2016-12-27
Dalitz plots for FKS phase space
2016-12-14
NLO multi-flavor events possible
2016-12-09
LCIO event header information added
2016-12-02
Alpha version of RECOLA interface
Bugfix for generator status in LCIO
##################################################################
2016-11-28
RELEASE: version 2.4.0
2016-11-24
Bugfix for OpenLoops interface: EW scheme
is set by WHIZARD
Bugfixes for top threshold implementation
2016-11-11
Refactoring of dispatching
2016-10-18
Bug fix for LCIO output
2016-10-10
First implementation for collinear soft terms
2016-10-06
First full WHIZARD models from UFO files
2016-10-05
WHIZARD does not support legacy gcc 4.7.4 any longer
2016-09-30
Major refactoring of process core and NLO components
2016-09-23
WHIZARD homogeneous entity: discarding subconfigures
for CIRCE1/2, O'Mega, VAMP subpackages; these are
reconstructable by script projectors
2016-09-06
Introduce main configure summary
2016-08-26
Fix memory leak in event generation
##################################################################
2016-08-25
RELEASE: version 2.3.1
2016-08-19
Bug fix for EW-scheme dependence of gluino propagators
2016-08-01
Beta version of complex mass scheme support
2016-07-26
Fix bug in POWHEG damping for the matching
##################################################################
2016-07-21
RELEASE: version 2.3.0
2016-07-20
UFO file support (alpha version) in O'Mega
2016-07-13
New (more) stable of WHIZARD GUI
Support for EW schemes for OpenLoops
Factorized NLO top decays for threshold model
2016-06-15
Passing factorization scale to PYTHIA6
Adding charge and neutral observables
2016-06-14
Correcting angular distribution/tweaked kinematics in
non-collinear structure functions splittings
2016-05-10
Include (Fortran) TAUOLA/PHOTOS for tau decays via PYTHIA6
(backwards validation of LC CDR/TDR samples)
2016-04-27
Within OpenLoops virtuals: support for Collier library
2016-04-25
O'Mega vertex tables only loaded at first usage
2016-04-21
New CJ15 PDF parameterizations added
2016-04-21
Support for hadron collisions at NLO QCD
2016-04-05
Support for different (parameter) schemes in model files
2016-03-31
Correct transferral of lifetime/vertex from PYTHIA/TAUOLA
into the event record
2016-03-21
New internal implementation of polarization
via Bloch vectors, remove pointer constructions
2016-03-13
Extension of cascade syntax for processes:
exclude propagators/vertices etc. possible
2016-02-24
Full support for OpenLoops QCD NLO matrix
elements, inclusion in test suite
2016-02-12
Substantial progress on QCD NLO support
2016-02-02
Automated resonance mapping for FKS subtraction
2015-12-17
New BSM model WZW for diphoton resonances
##################################################################
2015-11-22
RELEASE: version 2.2.8
2015-11-21
Bugfix for fixed-order NLO events
2015-11-20
Anomalous FCNC top-charm vertices
2015-11-19
StdHEP output via HEPEVT/HEPEV4 supported
2015-11-18
Full set of electroweak dim-6 operators included
2015-10-22
Polarized one-loop amplitudes supported
2015-10-21
Fixes for event formats for showered events
2015-10-14
Callback mechanism for event output
2015-09-22
Bypass matrix elements in pure event sample rescans
StdHep frozen final version v5.06.01 included internally
2015-09-21
configure option --with-precision to
demand 64bit, 80bit, or 128bit Fortran
and bind C precision types
2015-09-07
More extensive tests of NLO
infrastructure and POWHEG matching
2015-09-01
NLO decay infrastructure
User-defined squared matrix elements
Inclusive FastJet algorithm plugin
Numerical improvement for small boosts
-
+
##################################################################
2015-08-11
RELEASE: version 2.2.7
2015-08-10
Infrastructure for damped POWHEG
Massive emitters in POWHEG
Born matrix elements via BLHA
GoSam filters via SINDARIN
Minor running coupling bug fixes
Fixed-order NLO events
2015-08-06
CT14 PDFs included (LO, NLO, NNLL)
2015-07-07
Revalidation of ILC WHIZARD-PYTHIA event chain
Extended test suite for showered events
Alpha version of massive FSR for POWHEG
2015-06-09
Fix memory leak in interaction for long cascades
Catch mismatch between beam definition and CIRCE2 spectrum
2015-06-08
Automated POWHEG matching: beta version
Infrastructure for GKS matching
Alpha version of fixed-order NLO events
CIRCE2 polarization averaged spectra with
explicitly polarized beams
2015-05-12
Abstract matching type: OO structure for matching/merging
2015-05-07
Bug fix in event record WHIZARD-PYTHIA6 transferral
Gaussian beam spectra for lepton colliders
##################################################################
2015-05-02
RELEASE: version 2.2.6
2015-05-01
Models for (unitarized) tensor resonances in VBS
2015-04-28
Bug fix in channel weights for event generation.
2015-04-18
Improved event record transfer WHIZARD/PYTHIA6
2015-03-19
POWHEG matching: alpha version
##################################################################
2015-02-27
RELEASE: version 2.2.5
2015-02-26
Abstract types for quantum numbers
2015-02-25
Read-in of StdHEP events, self-tests
-
+
2015-02-22
Bugfix for mother-daughter relations in
showered/hadronized events
2015-02-20
Projection on polarization in intermediate states
-
-2015-02-13
+
+2015-02-13
Correct treatment of beam remnants in
event formats (also LC remnants)
-
+
##################################################################
-
+
2015-02-06
RELEASE: version 2.2.4
2015-02-06
Bugfix in event output
-2015-02-05
+2015-02-05
LCIO event format supported
-
+
2015-01-30
Including state matrices in WHIZARD's internal IO
Versioning for WHIZARD's internal IO
Libtool update from 2.4.3 to 2.4.5
LCIO event output (beta version)
-
+
2015-01-27
Progress on NLO integration
Fixing a bug for multiple processes in a single
event file when using beam event files
-
+
2015-01-19
Bug fix for spin correlations evaluated in the rest
frame of the mother particle
-
+
2015-01-17
Regression fix for statically linked processes
from SARAH and FeynRules
-
+
2015-01-10
NLO: massive FKS emitters supported (experimental)
-
+
2015-01-06
MMHT2014 PDF sets included
-
+
2015-01-05
Handling mass degeneracies in auto_decays
2014-12-19
Fixing bug in rescan of event files
##################################################################
2014-11-30
RELEASE: version 2.2.3
2014-11-29
Beta version of LO continuum/NLL-threshold
matched top threshold model for e+e- physics
2014-11-28
More internal refactoring: disentanglement of module
dependencies
2014-11-21
OVM: O'Mega Virtual Machine, bytecode instructions
instead of compiled Fortran code
2014-11-01
Higgs Singlet extension model included
2014-10-18
Internal restructuring of code; half-way
WHIZARD main code file disassembled
2014-07-09
Alpha version of NLO infrastructure
##################################################################
2014-07-06
RELEASE: version 2.2.2
2014-07-05
CIRCE2: correlated LC beam spectra and
GuineaPig Interface to LC machine parameters
2014-07-01
Reading LHEF for decayed/factorized/showered/
hadronized events
2014-06-25
Configure support for GoSAM/Ninja/Form/QGraf
2014-06-22
LHAPDF6 interface
-2014-06-18
- Module for automatic generation of
+2014-06-18
+ Module for automatic generation of
radiation and loop infrastructure code
-
+
2014-06-11
Improved internal directory structure
-
+
##################################################################
-
+
2014-06-03
RELEASE: version 2.2.1
2014-05-30
Extensions of internal PDG arrays
-
+
2014-05-26
FastJet interface
2014-05-24
CJ12 PDFs included
-
+
2014-05-20
Regression fix for external models (via SARAH
or FeynRules)
-
+
##################################################################
2014-05-18
RELEASE: version 2.2.0
2014-04-11
Multiple components: inclusive process definitions,
syntax: process A + B + ...
2014-03-13
Improved PS mappings for e+e- ISR
ILC TDR and CLIC spectra included in CIRCE1
2014-02-23
New models: AltH w\ Higgs for exclusion purposes,
SM_rx for Dim 6-/Dim-8 operators, SSC for
general strong interactions (w/ Higgs), and
NoH_rx (w\ Higgs)
2014-02-14
Improved s-channel mapping, new on-shell
production mapping (e.g. Drell-Yan)
2014-02-03
PRE-RELEASE: version 2.2.0_beta
2014-01-26
O'Mega: Feynman diagram generation possible (again)
2013-12-16
HOPPET interface for b parton matching
2013-11-15
PRE-RELEASE: version 2.2.0_alpha-4
2013-10-27
LHEF standards 1.0/2.0/3.0 implemented
2013-10-15
PRE-RELEASE: version 2.2.0_alpha-3
2013-10-02
PRE-RELEASE: version 2.2.0_alpha-2
2013-09-25
PRE-RELEASE: version 2.2.0_alpha-1
2013-09-12
PRE-RELEASE: version 2.2.0_alpha
2013-09-03
General 2HDM implemented
-
+
2013-08-18
- Rescanning/recalculating events
-
+ Rescanning/recalculating events
+
2013-06-07
- Reconstruction of complete event
+ Reconstruction of complete event
from 4-momenta possible
-
+
2013-05-06
Process library stacks
-
+
2013-05-02
Process stacks
-
+
2013-04-29
Single-particle phase space module
-
+
2013-04-26
Abstract interface for random
number generator
-
+
2013-04-24
More object-orientation on modules
Midpoint-rule integrator
2013-04-05
Object-oriented integration and
event generation
2013-03-12
Processes recasted object-oriented:
MEs, scales, structure functions
First infrastructure for general Lorentz
structures
2013-01-17
Object-orientated reworking of library and
process core, more variable internal structure,
unit tests
2012-12-14
Update Pythia version to 6.4.27
2012-12-04
Fix the phase in HAZ vertices
2012-11-21
First O'Mega unit tests, some infrastructure
2012-11-13
Bugfix in anom. HVV Lorentz structures
##################################################################
2012-09-18
RELEASE: version 2.1.1
2012-09-11
Model MSSM_Hgg with Hgg and HAA vertices
2012-09-10
First version of implementation of multiple
interactions in WHIZARD
2012-09-05
Infrastructure for internal CKKW matching
2012-09-02
C, C++, Python API
2012-07-19
Fixing particle numbering in HepMC format
##################################################################
2012-06-15
RELEASE: version 2.1.0
2012-06-14
Analytical and kT-ordered shower officially
released
PYTHIA interface officially released
2012-05-09
Intrisince PDFs can be used for showering
2012-05-04
Anomalous Higgs couplings a la hep-ph/9902321
##################################################################
2012-03-19
RELEASE: version 2.0.7
2012-03-15
Run IDs are available now
More event variables in analysis
Modified raw event format (compatibility mode exists)
2012-03-12
Bugfix in decay-integration order
MLM matching steered completely internally now
-
+
2012-03-09
Special phase space mapping for narrow resonances
decaying to 4-particle final states with far off-shell
intermediate states
Running alphas from PDF collaborations with
builtin PDFs
-
+
2012-02-16
Bug fix in cascades decay infrastructure
-
+
2012-02-04
WHIZARD documentation compatible with TeXLive 2011
-
+
2012-02-01
Bug fix in FeynRules interface with --prefix flag
-
+
2012-01-29
Bug fix with name clash of O'Mega variable names
-
+
2012-01-27
Update internal PYTHIA to version 6.4.26
Bug fix in LHEF output
-
+
2012-01-21
Catching stricter automake 1.11.2 rules
-
+
2011-12-23
Bug fix in decay cascade setup
-
+
2011-12-20
Bug fix in helicity selection rules
-
+
2011-12-16
Accuracy goal reimplemented
-
+
2011-12-14
WHIZARD compatible with TeXLive 2011
-
+
2011-12-09
Option --user-target added
##################################################################
2011-12-07
RELEASE: version 2.0.6
2011-12-07
Bug fixes in SM_top_anom
Added missing entries to HepMC format
2011-12-06
Allow to pass options to O'Mega
Bug fix for HEPEVT block for showered/hadronized events
2011-12-01
Reenabled user plug-in for external code for
cuts, structure functions, routines etc.
2011-11-29
Changed model SM_Higgs for Higgs phenomenology
2011-11-25
Supporting a Y, (B-L) Z' model
2011-11-23
Make WHIZARD compatible for MAC OS X Lion/XCode 4
2011-09-25
WHIZARD paper published: Eur.Phys.J. C71 (2011) 1742
2011-08-16
Model SM_QCD: QCD with one EW insertion
2011-07-19
Explicit output channel for dvips avoids printing
2011-07-10
Test suite for WHIZARD unit tests
2011-07-01
Commands for matrix element tests
More OpenMP parallelization of kinematics
Added unit tests
2011-06-23
Conversion of CIRCE2 from F77 to F90, major
clean-up
2011-06-14
Conversion of CIRCE1 from F77 to F90
2011-06-10
OpenMP parallelization of channel kinematics
(by Matthias Trudewind)
-2011-05-31
+2011-05-31
RELEASE: version 1.97
-2011-05-24
- Minor bug fixes: update grids and elsif statement.
+2011-05-24
+ Minor bug fixes: update grids and elsif statement.
##################################################################
-2011-05-10
+2011-05-10
RELEASE: version 2.0.5
2011-05-09
Fixed bug in final state flavor sums
Minor improvements on phase-space setup
2011-05-05
Minor bug fixes
2011-04-15
WHIZARD as a precompiled 64-bit binary available
2011-04-06
Wall clock instead of cpu time for time estimates
2011-04-05
Major improvement on the phase space setup
2011-04-02
OpenMP parallelization for helicity loop in O'Mega
matrix elements
2011-03-31
Tools for relocating WHIZARD and use in batch
environments
2011-03-29
Completely static builds possible, profiling options
2011-03-28
Visualization of integration history
2011-03-27
Fixed broken K-matrix implementation
2011-03-23
Including the GAMELAN manual in the distribution
2011-01-26
WHIZARD analysis can handle hadronized event files
2011-01-17
MSTW2008 and CT10 PDF sets included
2010-12-23
Inclusion of NMSSM with Hgg couplings
2010-12-21
Advanced options for integration passes
2010-11-16
WHIZARD supports CTEQ6 and possibly other PDFs
directly; data files included in the distribution
##################################################################
2010-10-26
RELEASE: version 2.0.4
2010-10-06
Bug fix in MSSM implementation
2010-10-01
Update to libtool 2.4
2010-09-29
Support for anomalous top couplings (form factors etc.)
Bug fix for running gauge Yukawa SUSY couplings
2010-09-28
RELEASE: version 1.96
2010-09-21
Beam remnants and pT spectra for lepton collider re-enabled
Restructuring subevt class
2010-09-16
Shower and matching are disabled by default
PYTHIA as a conditional on these two options
2010-09-14
Possibility to read in beam spectra re-enabled (e.g. Guinea
Pig)
2010-09-13
Energy scan as (pseudo-) structure functions re-implemented
2010-09-10
CIRCE2 included again in WHIZARD 2 and validated
2010-09-02
Re-implementation of asymmetric beam energies and collision
angles, e-p collisions work, inclusion of a HERA DIS test
case
##################################################################
2010-10-18
RELEASE: version 2.0.3
2010-08-08
Bug in CP-violating anomalous triple TGCs fixed
2010-08-06
Solving backwards compatibility problem with O'Caml 3.12.0
2010-07-12
Conserved quantum numbers speed up O'Mega code generation
2010-07-07
Attaching full ISR/FSR parton shower and MPI/ISR
module
Added SM model containing Hgg, HAA, HAZ vertices
2010-07-02
Matching output available as LHEF and STDHEP
2010-06-30
Various bug fixes, missing files, typos
2010-06-26
CIRCE1 completely re-enabled
Chaining structure functions supported
2010-06-25
Partial support for conserved quantum numbers in
O'Mega
2010-06-21
Major upgrade of the graphics package: error bars,
smarter SINDARIN steering, documentation, and all that...
2010-06-17
MLM matching with PYTHIA shower included
-2010-06-16
+2010-06-16
Added full CIRCE1 and CIRCE2 versions including
full documentation and miscellanea to the trunk
2010-06-12
User file management supported, improved variable
and command structure
2010-05-24
Improved handling of variables in local command lists
2010-05-20
PYTHIA interface re-enabled
2010-05-19
ASCII file formats for interfacing ROOT and gnuplot in
data analysis
##################################################################
2010-05-18
RELEASE: version 2.0.2
2010-05-14
Reimplementation of visualization of phase space
channels
Minor bug fixes
2010-05-12
Improved phase space - elimination of redundancies
2010-05-08
Interface for polarization completed: polarized beams etc.
2010-05-06
Full quantum numbers appear in process log
Integration results are usable as user variables
Communication with external programs
2010-05-05
Split module commands into commands, integration,
simulation modules
2010-05-04
FSR+ISR for the first time connected to the WHIZARD 2 core
##################################################################
2010-04-25
RELEASE: version 2.0.1
2010-04-23
Automatic compile and integrate if simulate is called
Minor bug fixes in O'Mega
2010-04-21
Checkpointing for event generation
Flush statements to use WHIZARD inside a pipe
2010-04-20
Reimplementation of signal handling in WGIZARD 2.0
2010-04-19
VAMP is now a separately configurable and installable unit of
WHIZARD, included VAMP self-checks
Support again compilation in quadruple precision
2010-04-06
Allow for logarithmic plots in GAMELAN, reimplement the
possibility to set the number of bins
2010-04-15
Improvement on time estimates for event generation
##################################################################
2010-04-12
RELEASE: version 2.0.0
2010-04-09
Per default, the code for the amplitudes is subdivided to allow
faster compiler optimization
More advanced and unified and straightforward command language
syntax
Final bug fixes
2010-04-07
Improvement on SINDARIN syntax; printf, sprintf function
thorugh a C interface
2010-04-05
Colorizing DAGs instead of model vertices: speed boost
in colored code generation
2010-03-31
Generalized options for normalization of weighted and
unweighted events
Grid and weight histories added again to log files
Weights can be used in analyses
2010-03-28
Cascade decays completely implemented including color and
spin correlations
2010-03-07
Added new WHIZARD header with logo
2010-03-05
Removed conflict in O'Mega amplitudes between flavour sums
and cascades
StdHEP interface re-implemented
2010-03-03
RELEASE: version 2.0.0rc3
Several bug fixes for preventing abuse in input files
OpenMP support for amplitudes
Reimplementation of WHIZARD 1 HEPEVT ASCII event formats
FeynRules interface successfully passed MSSM test
2010-02-26
Eliminating ghost gluons from multi-gluon amplitudes
2010-02-25
RELEASE: version 1.95
HEPEVT format from WHIZARD 1 re-implemented in WHIZARD 2
2010-02-23
Running alpha_s implemented in the FeynRules interface
2010-02-19
MSSM (semi-) automatized self-tests finalized
2010-02-17
RELEASE: version 1.94
2010-02-16
Closed memory corruption in WHIZARD 1
Fixed problems of old MadGraph and CompHep drivers
with modern compilers
Uncolored vertex selection rules for colored amplitudes in
O'Mega
2010-02-15
Infrastructure for color correlation computation in O'Mega
finished
Forbidden processes are warned about, but treated as non-fatal
2010-02-14
Color correlation computation in O'Mega finalized
2010-02-10
Improving phase space mappings for identical particles in
initial and final states
Introduction of more extended multi-line error message
2010-02-08
First O'Caml code for computation of color correlations in
O'Mega
2010-02-07
First MLM matching with e+ e- -> jets
##################################################################
2010-02-06
RELEASE: version 2.0.0rc2
2010-02-05
Reconsidered the Makefile structure and more extended tests
Catch a crash between WHIZARD and O'Mega for forbidden processes
Tensor products of arbitrary color structures in jet definitions
2010-02-04
Color correlation computation in O'Mega finalized
##################################################################
2010-02-03
RELEASE: version 2.0.0rc1
##################################################################
2010-01-31
Reimplemented numerical helicity selection rules
Phase space functionality of version 1 restored and improved
2009-12-05
NMSSM validated with FeynRules in WHIZARD 1 (Felix Braam)
2009-12-04
RELEASE: version 2.0.0alpha
##################################################################
2009-04-16
RELEASE: version 1.93
2009-04-15
Clean-up of Makefiles and configure scripts
Reconfiguration of BSM model implementation
extended supersymmetric models
2008-12-23
New model NMSSM (Felix Braam)
SLHA2 added
Bug in LHAPDF interface fixed
2008-08-16
Bug fixed in K matrix implementation
Gravitino option in the MSSM added
2008-03-20
Improved color and flavor sums
##################################################################
2008-03-12
RELEASE: version 1.92
LHEF (Les Houches Event File) format added
Fortran 2003 command-line interface (if supported by the compiler)
Automated interface to colored models
More bug fixes and workarounds for compiler compatibility
##################################################################
2008-03-06
RELEASE: version 1.91
New model K-matrix (resonances and anom. couplings in WW scattering)
EWA spectrum
Energy-scan pseudo spectrum
Preliminary parton shower module (only from final-state quarks)
Cleanup and improvements of configure process
Improvements for O'Mega parameter files
Quadruple precision works again
More plotting options: lines, symbols, errors
Documentation with PDF bookmarks enabled
Various bug fixes
-2007-11-29
+2007-11-29
New model UED
##################################################################
-2007-11-23
+2007-11-23
RELEASE: version 1.90
O'Mega now part of the WHIZARD tree
Madgraph/CompHEP disabled by default (but still usable)
Support for LHAPDF (preliminary)
Added new models: SMZprime, SM_km, Template
Improved compiler recognition and compatibility
Minor bug fixes
##################################################################
2006-06-15
RELEASE: version 1.51
Support for anomaly-type Higgs couplings (to gluon and photon/Z)
Support for spin 3/2 and spin 2
New models: Little Higgs (4 versions), toy models for extra dimensions
and gravitinos
Fixes to the whizard.nw source documentation to run through LaTeX
Intel 9.0 bug workaround (deallocation of some arrays)
2006-05-15
O'Mega RELEASE: version 0.11
merged JRR's O'Mega extensions
##################################################################
2006-02-07
RELEASE: version 1.50
To avoid confusion: Mention outdated manual example in BUGS file
O'Mega becomes part of the WHIZARD generator
2006-02-02 [bug fix update]
Bug fix: spurious error when writing event files for weighted events
Bug fix: 'r' option for omega produced garbage for some particle names
Workaround for ifort90 bug (crash when compiling whizard_event)
Workaround for ifort90 bug (crash when compiling hepevt_common)
2006-01-27
Added process definition files for MSSM 2->2 processes
Included beam recoil for EPA (T.Barklow)
Updated STDHEP byte counts (for STDHEP 5.04.02)
Fixed STDHEP compatibility (avoid linking of incomplete .so libs)
Fixed issue with comphep requiring Xlibs on Opteron
Fixed issue with ifort 8.x on Opteron (compiling 'signal' interface)
Fixed color-flow code: was broken for omega with option 'c' and 'w'
Workaround hacks for g95 compatibility
2005-11-07
O'Mega RELEASE: version 0.10
O'Mega, merged JRR's and WK's color hack for WHiZard
O'Mega, EXPERIMENTAL: cache fusion tables (required for colors
a la JRR/WK)
O'Mega, make JRR's MSSM official
##################################################################
2005-10-25
RELEASE: version 1.43
Minor fixes in MSSM couplings (Higgs/3rd gen squarks).
This should be final, since the MSSM results agree now completely
with Madgraph and Sherpa
User-defined lower and upper limits for split event file count
Allow for counters (events, bytes) exceeding $2^{31}$
Revised checksum treatment and implementation (now MD5)
Bug fix: missing process energy scale in raw event file
##################################################################
2005-09-30
RELEASE: version 1.42
Graphical display of integration history ('make history')
Allow for switching off signals even if supported (configure option)
2005-09-29
Revised phase space generation code, in particular for flavor sums
Negative cut and histogram codes use initial beams instead of
initial parton momenta. This allows for computing, e.g., E_miss
Support constant-width and zero-width options for O'Mega
Width options now denoted by w:X (X=f,c,z). f option obsolescent
Bug fix: colorized code: flipped indices could screw up result
Bug fix: O'Mega with 'c' and 'w:f' option together (still some problem)
Bug fix: dvips on systems where dvips defaults to lpr
Bug fix: integer overflow if too many events are requested
2005-07-29
Allow for 2 -> 1 processes (if structure functions are on)
2005-07-26
Fixed and expanded the 'test' matrix element:
Unit matrix element with option 'u' / default: normalized phase space
##################################################################
2005-07-15
RELEASE: version 1.41
Bug fix: no result for particle decay processes with width=0
Bug fix: line breaks in O'Mega files with color decomposition
2005-06-02
New self-tests (make test-QED / test-QCD / test-SM)
check lists of 2->2 processes
Bug fix: HELAS calling convention for wwwwxx and jwwwxx (4W-Vertex)
2005-05-25
Revised Makefile structure
Eliminated obsolete references to ISAJET/SUSY (superseded by SLHA)
2005-05-19
Support for color in O'Mega (using color flow decomposition)
New model QCD
Parameter file changes that correspond to replaced SM module in O'Mega
Bug fixes in MSSM (O'Mega) parameter file
2005-05-18
New event file formats, useful for LHC applications:
ATHENA and Les Houches Accord (external fragmentation)
Naive (i.e., leading 1/N) color factor now implemented both for
incoming and outgoing partons
2005-01-26
include missing HELAS files for bundle
pgf90 compatibility issues [note: still internal error in pgf90]
##################################################################
2004-12-13
RELEASE: version 1.40
compatibility fix: preprocessor marks in helas code now commented out
minor bug fix: format string in madgraph source
2004-12-03
support for arbitray beam energies and directions
allow for pT kick in structure functions
bug fix: rounding error could result in zero cross section
(compiler-dependent)
2004-10-07
simulate decay processes
list fraction (of total width/cross section) instead of efficiency
in process summary
new cut/analysis parameters AA, AAD, CTA: absolute polar angle
2004-10-04
Replaced Madgraph I by Madgraph II. Main improvement: model no
longer hardcoded
introduced parameter reset_seed_each_process (useful for debugging)
bug fix: color initialization for some processes was undefined
2004-09-21
don't compile unix_args module if it is not required
##################################################################
2004-09-20
RELEASE: version 1.30
g95 compatibility issues resolved
some (irrelevant) memory leaks closed
removed obsolete warning in circe1
manual update (essentially) finished
2004-08-03
O'Mega RELEASE: version 0.9
O'Mega, src/trie.mli, src/trie.ml: make interface compatible with
the O'Caml 3.08 library (remains compatible with older
versions). Implementation of unused functions still
incomplete.
2004-07-26
minor fixes and improvements in make process
2004-06-29
workarounds for new Intel compiler bugs ...
no rebuild of madgraph/comphep executables after 'make clean'
bug fix in phase space routine:
wrong energy for massive initial particles
bug fix in (new) model interface: name checks for antiparticles
pre-run checks for comphep improved
ww-strong model file extended
Model files particle name fixes, chep SM vertices included
2004-06-22
O'Mega RELEASE: version 0.8
O'Mega MSSM: sign of W+/W-/A and W+/W-/Z couplings
2004-05-05
Fixed bug in PDFLIB interface: p+pbar was initialized as p+p (ThO)
NAG compiler: set number of continuation lines to 200 as default
Extended format for cross section summary; appears now in whizard.out
Fixed 'bundle' feature
2004-04-28
Fixed compatibility with revised O'Mega SM_ac model
Fixed problem with x=0 or x=1 when calling PDFLIB (ThO)
Fixed bug in comphep module: Vtb was overlooked
##################################################################
-2004-04-15
- RELEASE: version 1.28
+2004-04-15
+ RELEASE: version 1.28
Fixed bug: Color factor was missing for O'Mega processes with
four quarks and more
Manual partially updated
-2004-04-08
+2004-04-08
Support for grid files in binary format
New default value show_histories=F (reduce output file size)
Revised phase space switches: removed annihilation_lines,
- removed s_channel_resonance, changed meaning of
+ removed s_channel_resonance, changed meaning of
extra_off_shell_lines, added show_deleted_channels
Bug fixed which lead to omission of some phase space channels
Color flow guessed only if requested by guess_color_flow
-2004-03-10
+2004-03-10
New model interface: Only one model name specified in whizard.prc
All model-dependent files reside in conf/models (modellib removed)
-2004-03-03
+2004-03-03
Support for input/output in SUSY Les Houches Accord format
Split event files if requested
Support for overall time limit
Support for CIRCE and CIRCE2 generator mode
Support for reading beam events from file
-2004-02-05
+2004-02-05
Fixed compiler problems with Intel Fortran 7.1 and 8.0
Support for catching signals
##################################################################
-2003-08-06
- RELEASE: version 1.27
+2003-08-06
+ RELEASE: version 1.27
User-defined PDF libraries as an alternative to the standard PDFLIB
-2003-07-23
+2003-07-23
Revised phase space module: improved mappings for massless particles,
equivalences of phase space channels are exploited
Improved mapping for PDF (hadron colliders)
Madgraph module: increased max number of color flows from 250 to 1000
##################################################################
-2003-06-23
+2003-06-23
RELEASE: version 1.26
CIRCE2 support
Fixed problem with 'TC' integer kind [Intel compiler complained]
-2003-05-28
+2003-05-28
Support for drawing histograms of grids
Bug fixes for MSSM definitions
##################################################################
-2003-05-22
+2003-05-22
RELEASE: version 1.25
Experimental MSSM support with ISAJET interface
Improved capabilities of generating/analyzing weighted events
Optional drawing phase space diagrams using FeynMF
##################################################################
-2003-01-31
+2003-01-31
RELEASE: version 1.24
A few more fixes and workarounds (Intel and Lahey compiler)
-2003-01-15
+2003-01-15
Fixes and workarounds needed for WHIZARD to run with Intel compiler
Command-line option interface for the Lahey compiler
Bug fix: problem with reading whizard.phs
##################################################################
-2002-12-10
+2002-12-10
RELEASE: version 1.23
Command-line options (on some systems)
Allow for initial particles in the event record, ordered:
[beams, initials] - [remnants] - outgoing partons
Support for PYTHIA 6.2: Les Houches external process interface
String pythia_parameters can be up to 1000 characters long
Select color flow states in (internal) analysis
Bug fix in color flow content of raw event files
Support for transversal polarization of fermion beams
Cut codes: PHI now for absolute azimuthal angle, DPHI for distance
'Test' matrix elements optionally respect polarization
User-defined code can be inserted for spectra, structure functions
and fragmentation
Time limits can be specified for adaptation and simulation
User-defined file names and file directory
Initial weights in input file no longer supported
Bug fix in MadGraph (wave function counter could overflow)
Bug fix: Gamelan (graphical analysis) was not built if noweb absent
##################################################################
-2002-03-16
+2002-03-16
RELEASE: version 1.22
Allow for beam remnants in the event record
-2002-03-01
+2002-03-01
Handling of aliases in whizard.prc fixed (aliases are whole tokens)
-2002-02-28
+2002-02-28
Optimized phase space handling routines
(total execution time reduced by 20-60%, depending on process)
##################################################################
-2002-02-26
+2002-02-26
RELEASE: version 1.21
Fixed ISR formula (ISR was underestimated in previous versions).
- New version includes ISR in leading-log approximation up to
+ New version includes ISR in leading-log approximation up to
third order. Parameter ISR_sqrts renamed to ISR_scale.
##################################################################
-2002-02-19
- RELEASE: version 1.20
+2002-02-19
+ RELEASE: version 1.20
New process-generating method 'test' (dummy matrix element)
Compatibility with autoconf 2.50 and current O'Mega version
-2002-02-05
+2002-02-05
Prevent integration channels from being dropped (optionally)
New internal mapping for structure functions improves performance
Old whizard.phx file deleted after recompiling (could cause trouble)
-2002-01-24
+2002-01-24
Support for user-defined cuts and matrix element reweighting
STDHEP output now written by write_events_format=20 (was 3)
-2002-01-16
+2002-01-16
Improved structure function handling; small changes in user interface:
new parameter structured_beams in &process_input
parameter fixed_energy in &beam_input removed
Support for multiple initial states
Eta-phi (cone) cut possible (hadron collider applications)
Fixed bug: Whizard library was not always recompiled when necessary
Fixed bug: Default cuts were insufficient in some cases
Fixed bug: Unusable phase space mappings generated in some cases
-2001-12-06
+2001-12-06
Reorganized document source
-2001-12-05
+2001-12-05
Preliminary CIRCE2 support (no functionality yet)
-2001-11-27
+2001-11-27
Intel compiler support (does not yet work because of compiler bugs)
New cut and analysis mode cos-theta* and related
Fixed circular jetset_interface dependency warning
Some broadcast routines removed (parallel support disabled anyway)
Minor shifts in cleanup targets (Makefiles)
Modified library search, check for pdflib8*
-2001-08-06
+2001-08-06
Fixed bug: I/O unit number could be undefined when reading phase space
- Fixed bug: Unitialized variable could cause segfault when
+ Fixed bug: Unitialized variable could cause segfault when
event generation was disabled
Fixed bug: Undefined subroutine in CIRCE replacement module
Enabled feature: TGCs in O'Mega (not yet CompHEP!) matrix elements
(CompHEP model sm-GF #5, O'Mega model SM_ac)
Fixed portability issue: Makefile did rely on PWD environment variable
Fixed portability issue: PYTHIA library search ambiguity resolved
-2001-08-01
+2001-08-01
Default whizard.prc and whizard.in depend on activated modules
Fixed bug: TEX=latex was not properly enabled when making plots
-2001-07-20
+2001-07-20
Fixed output settings in PERL script calls
Cache enabled in various configure checks
-2001-07-13
- Support for multiple processes in a single WHIZARD run. The
+2001-07-13
+ Support for multiple processes in a single WHIZARD run. The
integrations are kept separate, but the generated events are mixed
The whizard.evx format has changed (incompatible), including now
the color flow information for PYTHIA fragmentation
Output files are now process-specific, except for the event file
Phase space file whizard.phs (if present) is used only as input,
program-generated phase space is now in whizard.phx
-2001-07-10
+2001-07-10
Bug fix: Undefined parameters in parameters_SM_ac.f90 removed
-2001-07-04
+2001-07-04
Bug fix: Compiler options for the case OMEGA is disabled
Small inconsistencies in whizard.out format fixed
-2001-07-01
+2001-07-01
Workaround for missing PDFLIB dummy routines in PYTHIA library
##################################################################
2001-06-30
RELEASE: version 1.13
Default path /cern/pro/lib in configure script
2001-06-20
New fragmentation option: Interface for PYTHIA with full color flow
information, beam remnants etc.
2001-06-18
Severe bug fixed in madgraph interface: 3-gluon coupling was missing
Enabled color flow information in madgraph
2001-06-11
VAMP interface module rewritten
Revised output format: Multiple VAMP iterations count as one WHIZARD
iteration in integration passes 1 and 3
Improved message and error handling
Bug fix in VAMP: handle exceptional cases in rebinning_weights
2001-05-31
new parameters for grid adaptation: accuracy_goal and efficiency_goal
##################################################################
2001-05-29
RELEASE: version 1.12
bug fixes (compilation problems): deleted/modified unused functions
2001-05-16
diagram selection improved and documented
2001-05-06
allow for disabling packages during configuration
2001-05-03
slight changes in whizard.out format; manual extended
##################################################################
2001-04-20
RELEASE: version 1.11
fixed some configuration and compilation problems (PDFLIB etc.)
2001-04-18
linked PDFLIB: support for quark/gluon structure functions
2001-04-05
parameter interface written by PERL script
SM_ac model file: fixed error in continuation line
2001-03-13
O'Mega, O'Caml 3.01: incompatible changes
O'Mega, src/trie.mli: add covariance annotation to T.t
This breaks O'Caml 3.00, but is required for O'Caml 3.01.
O'Mega, many instances: replace `sig include Module.T end' by
`Module.T', since the bug is fixed in O'Caml 3.01
2001-02-28
O'Mega, src/model.mli:
new field Model.vertices required for model functors, will
retire Model.fuse2, Model.fuse3, Model.fusen soon.
##################################################################
2001-03-27
RELEASE: version 1.10
reorganized the modules as libraries
linked PYTHIA: support for parton fragmentation
2000-12-14
fixed some configuration problems (if noweb etc. are absent)
##################################################################
2000-12-01
RELEASE of first public version: version 1.00beta
-
-
-
Index: trunk/src/variables/variables.nw
===================================================================
--- trunk/src/variables/variables.nw (revision 8293)
+++ trunk/src/variables/variables.nw (revision 8294)
@@ -1,6772 +1,6772 @@
% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: variables for processes
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Variables for Processes}
\includemodulegraph{variables}
This part introduces variables as user-controlled objects that
influence the behavior of objects and calculations. Variables contain
objects of intrinsic type or of a type as introced above.
\begin{description}
\item[variables]
Store values of various kind, used by expressions and accessed by
the command interface. This provides an implementation of the [[vars_t]]
abstract type.
\item[observables]
Concrete implementation of observables (functions in the variable tree),
applicable for \whizard.
abstract type.
\end{description}
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Variables: Implementation}
The user interface deals with variables that are handled similarly to
full-flegded programming languages. The system will add a lot of
predefined variables (model parameters, flags, etc.) that are
accessible to the user by the same methods.
Variables can be of various type: logical (boolean/flag), integer,
real (default precision), subevents (used in cut expressions),
arrays of PDG codes (aliases for particles), strings. Furthermore, in
cut expressions we have unary and binary observables, which are used
like real parameters but behave like functions.
<<[[variables.f90]]>>=
<<File header>>
module variables
<<Use kinds>>
<<Use strings>>
use io_units
use format_utils, only: pac_fmt
use format_defs, only: FMT_12, FMT_19
use constants, only: eps0
use os_interface, only: paths_t
use physics_defs, only: LAMBDA_QCD_REF
use system_dependencies
use fastjet !NODEP!
use diagnostics
use pdg_arrays
use subevents
use var_base
<<Standard module head>>
<<Variables: public>>
<<Variables: parameters>>
<<Variables: types>>
<<Variables: interfaces>>
contains
<<Variables: procedures>>
end module variables
@ %def variables
@
\subsection{Variable list entries}
Variable (and constant) values can be of one of the following types:
<<Variables: parameters>>=
integer, parameter, public :: V_NONE = 0, V_LOG = 1, V_INT = 2, V_REAL = 3
integer, parameter, public :: V_CMPLX = 4, V_SEV = 5, V_PDG = 6, V_STR = 7
integer, parameter, public :: V_OBS1_INT = 11, V_OBS2_INT = 12
integer, parameter, public :: V_OBS1_REAL = 21, V_OBS2_REAL = 22
integer, parameter, public :: V_UOBS1_INT = 31, V_UOBS2_INT = 32
integer, parameter, public :: V_UOBS1_REAL = 41, V_UOBS2_REAL = 42
@ %def V_NONE V_LOG V_INT V_REAL V_CMPLX V_PRT V_SEV V_PDG
@ %def V_OBS1_INT V_OBS2_INT V_OBS1_REAL V_OBS2_REAL
@ %def V_UOBS1_INT V_UOBS2_INT V_UOBS1_REAL V_UOBS2_REAL
@
\subsubsection{The type}
This is an entry in the variable list. It can be of any type; in
each case only one value is allocated. It may be physically
allocated upon creation, in which case [[is_allocated]] is true, or
it may contain just a pointer to a value somewhere else, in which case
[[is_allocated]] is false.
The flag [[is_defined]] is set when the variable is given a value, even the
undefined value. (Therefore it is distinct from [[is_known]].) This matters
for variable declaration in the SINDARIN language. The variable is set up in
the compilation step and initially marked as defined, but after compilation
all variables are set undefined. Each variable becomes defined when it is
explicitly set. The difference matters in loops.
[[is_locked]] means that it cannot be given a value using the interface
routines [[var_list_set_XXX]] below. It can only be initialized, or change
automatically due to a side effect.
[[is_copy]] means that this is a local copy of a global variable. The copy
has a pointer to the original, which can be used to restore a previous value.
[[is_intrinsic]] means that this variable is defined by the program, not by
the user. Intrinsic variables cannot be (re)declared, but their values can be
reset unless they are locked. [[is_user_var]] means that the variable has
been declared by the user. It could be a new variable, or a local copy of an
intrinsic variable.
The flag [[is_known]] is a pointer which parallels the use of the
value pointer. For pointer variables, it is set if the value should point to
a known value. For ordinary variables, it should be true.
The value is implemented as a set of alternative type-specific pointers. This
emulates polymorphism, and it allows for actual pointer variables.
Observable-type variables have function pointers as values, so they behave
like macros. The functions make use of the particle objects accessible via
the pointers [[prt1]] and [[prt2]].
Finally, the [[next]] pointer indicates that we are making lists of
variables. A more efficient implementation might switch to hashes or
similar; the current implementation has $O(N)$ lookup.
<<Variables: public>>=
public :: var_entry_t
<<Variables: types>>=
type :: var_entry_t
private
integer :: type = V_NONE
type(string_t) :: name
logical :: is_allocated = .false.
logical :: is_defined = .false.
logical :: is_locked = .false.
logical :: is_intrinsic = .false.
logical :: is_user_var = .false.
logical, pointer :: is_known => null ()
logical, pointer :: lval => null ()
integer, pointer :: ival => null ()
real(default), pointer :: rval => null ()
complex(default), pointer :: cval => null ()
type(subevt_t), pointer :: pval => null ()
type(pdg_array_t), pointer :: aval => null ()
type(string_t), pointer :: sval => null ()
procedure(obs_unary_int), nopass, pointer :: obs1_int => null ()
procedure(obs_unary_real), nopass, pointer :: obs1_real => null ()
procedure(obs_binary_int), nopass, pointer :: obs2_int => null ()
procedure(obs_binary_real), nopass, pointer :: obs2_real => null ()
type(prt_t), pointer :: prt1 => null ()
type(prt_t), pointer :: prt2 => null ()
type(var_entry_t), pointer :: next => null ()
type(var_entry_t), pointer :: previous => null ()
type(string_t) :: description
end type var_entry_t
@ %def var_entry_t
@
\subsubsection{Interfaces for the observable functions}
<<Variables: public>>=
public :: obs_unary_int
public :: obs_unary_real
public :: obs_binary_int
public :: obs_binary_real
<<Variables: interfaces>>=
abstract interface
function obs_unary_int (prt1) result (ival)
import
integer :: ival
type(prt_t), intent(in) :: prt1
end function obs_unary_int
end interface
abstract interface
function obs_unary_real (prt1) result (rval)
import
real(default) :: rval
type(prt_t), intent(in) :: prt1
end function obs_unary_real
end interface
abstract interface
function obs_binary_int (prt1, prt2) result (ival)
import
integer :: ival
type(prt_t), intent(in) :: prt1, prt2
end function obs_binary_int
end interface
abstract interface
function obs_binary_real (prt1, prt2) result (rval)
import
real(default) :: rval
type(prt_t), intent(in) :: prt1, prt2
end function obs_binary_real
end interface
@ %def obs_unary_int obs_unary_real obs_binary_real
@
\subsubsection{Initialization}
Initialize an entry, optionally with a physical value. We also
allocate the [[is_known]] flag and set it if the value is set.
<<Variables: public>>=
public :: var_entry_init_int
<<Variables: procedures>>=
subroutine var_entry_init_log (var, name, lval, intrinsic, user)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
logical, intent(in), optional :: lval
logical, intent(in), optional :: intrinsic, user
var%name = name
var%type = V_LOG
allocate (var%lval, var%is_known)
if (present (lval)) then
var%lval = lval
var%is_defined = .true.
var%is_known = .true.
else
var%is_known = .false.
end if
if (present (intrinsic)) var%is_intrinsic = intrinsic
if (present (user)) var%is_user_var = user
var%is_allocated = .true.
end subroutine var_entry_init_log
subroutine var_entry_init_int (var, name, ival, intrinsic, user)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
integer, intent(in), optional :: ival
logical, intent(in), optional :: intrinsic, user
var%name = name
var%type = V_INT
allocate (var%ival, var%is_known)
if (present (ival)) then
var%ival = ival
var%is_defined = .true.
var%is_known = .true.
else
var%is_known = .false.
end if
if (present (intrinsic)) var%is_intrinsic = intrinsic
if (present (user)) var%is_user_var = user
var%is_allocated = .true.
end subroutine var_entry_init_int
subroutine var_entry_init_real (var, name, rval, intrinsic, user)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
real(default), intent(in), optional :: rval
logical, intent(in), optional :: intrinsic, user
var%name = name
var%type = V_REAL
allocate (var%rval, var%is_known)
if (present (rval)) then
var%rval = rval
var%is_defined = .true.
var%is_known = .true.
else
var%is_known = .false.
end if
if (present (intrinsic)) var%is_intrinsic = intrinsic
if (present (user)) var%is_user_var = user
var%is_allocated = .true.
end subroutine var_entry_init_real
subroutine var_entry_init_cmplx (var, name, cval, intrinsic, user)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
complex(default), intent(in), optional :: cval
logical, intent(in), optional :: intrinsic, user
var%name = name
var%type = V_CMPLX
allocate (var%cval, var%is_known)
if (present (cval)) then
var%cval = cval
var%is_defined = .true.
var%is_known = .true.
else
var%is_known = .false.
end if
if (present (intrinsic)) var%is_intrinsic = intrinsic
if (present (user)) var%is_user_var = user
var%is_allocated = .true.
end subroutine var_entry_init_cmplx
subroutine var_entry_init_subevt (var, name, pval, intrinsic, user)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
type(subevt_t), intent(in), optional :: pval
logical, intent(in), optional :: intrinsic, user
var%name = name
var%type = V_SEV
allocate (var%pval, var%is_known)
if (present (pval)) then
var%pval = pval
var%is_defined = .true.
var%is_known = .true.
else
var%is_known = .false.
end if
if (present (intrinsic)) var%is_intrinsic = intrinsic
if (present (user)) var%is_user_var = user
var%is_allocated = .true.
end subroutine var_entry_init_subevt
subroutine var_entry_init_pdg_array (var, name, aval, intrinsic, user)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
type(pdg_array_t), intent(in), optional :: aval
logical, intent(in), optional :: intrinsic, user
var%name = name
var%type = V_PDG
allocate (var%aval, var%is_known)
if (present (aval)) then
var%aval = aval
var%is_defined = .true.
var%is_known = .true.
else
var%is_known = .false.
end if
if (present (intrinsic)) var%is_intrinsic = intrinsic
if (present (user)) var%is_user_var = user
var%is_allocated = .true.
end subroutine var_entry_init_pdg_array
subroutine var_entry_init_string (var, name, sval, intrinsic, user)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
type(string_t), intent(in), optional :: sval
logical, intent(in), optional :: intrinsic, user
var%name = name
var%type = V_STR
allocate (var%sval, var%is_known)
if (present (sval)) then
var%sval = sval
var%is_defined = .true.
var%is_known = .true.
else
var%is_known = .false.
end if
if (present (intrinsic)) var%is_intrinsic = intrinsic
if (present (user)) var%is_user_var = user
var%is_allocated = .true.
end subroutine var_entry_init_string
@ %def var_entry_init_log
@ %def var_entry_init_int
@ %def var_entry_init_real
@ %def var_entry_init_cmplx
@ %def var_entry_init_subevt
@ %def var_entry_init_pdg_array
@ %def var_entry_init_string
@ Initialize an entry with a pointer to the value and, for numeric/logical
values, a pointer to the [[is_known]] flag.
<<Variables: procedures>>=
subroutine var_entry_init_log_ptr (var, name, lval, is_known, intrinsic)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
logical, intent(in), target :: lval
logical, intent(in), target :: is_known
logical, intent(in), optional :: intrinsic
var%name = name
var%type = V_LOG
var%lval => lval
var%is_known => is_known
if (present (intrinsic)) var%is_intrinsic = intrinsic
var%is_defined = .true.
end subroutine var_entry_init_log_ptr
subroutine var_entry_init_int_ptr (var, name, ival, is_known, intrinsic)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
integer, intent(in), target :: ival
logical, intent(in), target :: is_known
logical, intent(in), optional :: intrinsic
var%name = name
var%type = V_INT
var%ival => ival
var%is_known => is_known
if (present (intrinsic)) var%is_intrinsic = intrinsic
var%is_defined = .true.
end subroutine var_entry_init_int_ptr
subroutine var_entry_init_real_ptr (var, name, rval, is_known, intrinsic)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
real(default), intent(in), target :: rval
logical, intent(in), target :: is_known
logical, intent(in), optional :: intrinsic
var%name = name
var%type = V_REAL
var%rval => rval
var%is_known => is_known
if (present (intrinsic)) var%is_intrinsic = intrinsic
var%is_defined = .true.
end subroutine var_entry_init_real_ptr
subroutine var_entry_init_cmplx_ptr (var, name, cval, is_known, intrinsic)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
complex(default), intent(in), target :: cval
logical, intent(in), target :: is_known
logical, intent(in), optional :: intrinsic
var%name = name
var%type = V_CMPLX
var%cval => cval
var%is_known => is_known
if (present (intrinsic)) var%is_intrinsic = intrinsic
var%is_defined = .true.
end subroutine var_entry_init_cmplx_ptr
subroutine var_entry_init_pdg_array_ptr (var, name, aval, is_known, intrinsic)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
type(pdg_array_t), intent(in), target :: aval
logical, intent(in), target :: is_known
logical, intent(in), optional :: intrinsic
var%name = name
var%type = V_PDG
var%aval => aval
var%is_known => is_known
if (present (intrinsic)) var%is_intrinsic = intrinsic
var%is_defined = .true.
end subroutine var_entry_init_pdg_array_ptr
subroutine var_entry_init_subevt_ptr (var, name, pval, is_known, intrinsic)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
type(subevt_t), intent(in), target :: pval
logical, intent(in), target :: is_known
logical, intent(in), optional :: intrinsic
var%name = name
var%type = V_SEV
var%pval => pval
var%is_known => is_known
if (present (intrinsic)) var%is_intrinsic = intrinsic
var%is_defined = .true.
end subroutine var_entry_init_subevt_ptr
subroutine var_entry_init_string_ptr (var, name, sval, is_known, intrinsic)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
type(string_t), intent(in), target :: sval
logical, intent(in), target :: is_known
logical, intent(in), optional :: intrinsic
var%name = name
var%type = V_STR
var%sval => sval
var%is_known => is_known
if (present (intrinsic)) var%is_intrinsic = intrinsic
var%is_defined = .true.
end subroutine var_entry_init_string_ptr
@ %def var_entry_init_log_ptr
@ %def var_entry_init_int_ptr
@ %def var_entry_init_real_ptr
@ %def var_entry_init_cmplx_ptr
@ %def var_entry_init_pdg_array_ptr
@ %def var_entry_init_subevt_ptr
@ %def var_entry_init_string_ptr
@ Initialize an entry with an observable. The procedure pointer is
not yet set.
<<Variables: procedures>>=
subroutine var_entry_init_obs (var, name, type, prt1, prt2)
type(var_entry_t), intent(out) :: var
type(string_t), intent(in) :: name
integer, intent(in) :: type
type(prt_t), intent(in), target :: prt1
type(prt_t), intent(in), optional, target :: prt2
var%type = type
var%name = name
var%prt1 => prt1
if (present (prt2)) var%prt2 => prt2
var%is_intrinsic = .true.
var%is_defined = .true.
end subroutine var_entry_init_obs
@ %def var_entry_init_obs
@ Mark an entry as undefined it it is a user-defined variable object, so force
re-initialization.
<<Variables: procedures>>=
subroutine var_entry_undefine (var)
type(var_entry_t), intent(inout) :: var
var%is_defined = .not. var%is_user_var
var%is_known = var%is_defined .and. var%is_known
end subroutine var_entry_undefine
@ %def var_entry_undefine
@ Clear an entry: mark it as unknown.
<<Variables: procedures>>=
subroutine var_entry_clear (var)
type(var_entry_t), intent(inout) :: var
var%is_known = .false.
end subroutine var_entry_clear
@ %def var_entry_clear
@ Lock an entry: forbid resetting the entry after initialization.
<<Variables: procedures>>=
subroutine var_entry_lock (var, locked)
type(var_entry_t), intent(inout) :: var
logical, intent(in), optional :: locked
if (present (locked)) then
var%is_locked = locked
else
var%is_locked = .true.
end if
end subroutine var_entry_lock
@ %def var_entry_lock
@
\subsubsection{Finalizer}
<<Variables: procedures>>=
subroutine var_entry_final (var)
type(var_entry_t), intent(inout) :: var
if (var%is_allocated) then
select case (var%type)
case (V_LOG); deallocate (var%lval)
case (V_INT); deallocate (var%ival)
case (V_REAL);deallocate (var%rval)
case (V_CMPLX);deallocate (var%cval)
case (V_SEV); deallocate (var%pval)
case (V_PDG); deallocate (var%aval)
case (V_STR); deallocate (var%sval)
end select
deallocate (var%is_known)
var%is_allocated = .false.
var%is_defined = .false.
end if
end subroutine var_entry_final
@ %def var_entry_final
@
\subsubsection{Output}
<<Variables: procedures>>=
recursive subroutine var_entry_write (var, unit, model_name, &
intrinsic, pacified, descriptions, ascii_output)
type(var_entry_t), intent(in) :: var
integer, intent(in), optional :: unit
type(string_t), intent(in), optional :: model_name
logical, intent(in), optional :: intrinsic
logical, intent(in), optional :: pacified
logical, intent(in), optional :: descriptions
logical, intent(in), optional :: ascii_output
type(string_t) :: col_string
logical :: show_desc, ao
integer :: u
u = given_output_unit (unit); if (u < 0) return
show_desc = .false.; if (present (descriptions)) show_desc = descriptions
ao = .false.; if (present (ascii_output)) ao = ascii_output
if (show_desc) then
if (ao) then
col_string = create_col_string (COL_BLUE)
if (var%is_locked) then
write (u, "(A)", advance="no") char (achar(27) // col_string) // &
char (var%name) // achar(27) // "[0m" //" fixed-value="
else
write (u, "(A)", advance="no") char (achar(27) // col_string) // &
char (var%name) // achar(27) // "[0m" //" default="
end if
col_string = create_col_string (COL_RED)
write (u, "(A)", advance="no") char (achar(27) // col_string)
call var_write_val (var, u, "no", pacified=.true.)
write (u, "(A)") achar(27) // "[0m"
write (u, "(A)") char (var%description)
return
else
write (u, "(A)") "\item"
write (u, "(A)", advance="no") "\ttt{" // char ( &
replace (replace (var%name, "_", "\_", every=.true.), "$", "\$" )) // &
"} "
if (var%is_known) then
if (var%is_locked) then
write (u, "(A)", advance="no") "\qquad (fixed value: \ttt{"
else
write (u, "(A)", advance="no") "\qquad (default: \ttt{"
end if
call var_write_val (var, u, "no", pacified=.true., escape_tex=.true.)
write (u, "(A)", advance="no") "})"
end if
write (u, "(A)") " \newline"
write (u, "(A)") char (var%description)
write (u, "(A)") "%%%%%"
return
end if
end if
if (present (intrinsic)) then
if (var%is_intrinsic .neqv. intrinsic) return
end if
if (.not. var%is_defined) then
write (u, "(A,1x)", advance="no") "[undefined]"
end if
if (.not. var%is_intrinsic) then
write (u, "(A,1x)", advance="no") "[user variable]"
end if
if (present (model_name)) then
write (u, "(A,A)", advance="no") char(model_name), "."
end if
write (u, "(A)", advance="no") char (var%name)
if (var%is_locked) write (u, "(A)", advance="no") "*"
if (var%is_allocated) then
write (u, "(A)", advance="no") " = "
else if (var%type /= V_NONE) then
write (u, "(A)", advance="no") " => "
end if
call var_write_val (var, u, "yes", pacified)
end subroutine var_entry_write
@ %def var_entry_write
@
<<Variables: procedures>>=
subroutine var_write_val (var, u, advance, pacified, escape_tex)
type(var_entry_t), intent(in) :: var
integer, intent(in) :: u
character(*), intent(in) :: advance
logical, intent(in), optional :: pacified, escape_tex
logical :: num_pac, et
real(default) :: rval
complex(default) :: cval
character(len=7) :: fmt
call pac_fmt (fmt, FMT_19, FMT_12, pacified)
num_pac = .false.; if (present (pacified)) num_pac = pacified
et = .false.; if (present (escape_tex)) et = escape_tex
select case (var%type)
case (V_NONE); write (u, '()', advance=advance)
case (V_LOG)
if (var%is_known) then
if (var%lval) then
write (u, "(A)", advance=advance) "true"
else
write (u, "(A)", advance=advance) "false"
end if
else
write (u, "(A)", advance=advance) "[unknown logical]"
end if
case (V_INT)
if (var%is_known) then
write (u, "(I0)", advance=advance) var%ival
else
write (u, "(A)", advance=advance) "[unknown integer]"
end if
case (V_REAL)
if (var%is_known) then
rval = var%rval
if (num_pac) then
call pacify (rval, 10 * eps0)
end if
write (u, "(" // fmt // ")", advance=advance) rval
else
write (u, "(A)", advance=advance) "[unknown real]"
end if
case (V_CMPLX)
if (var%is_known) then
cval = var%cval
if (num_pac) then
call pacify (cval, 10 * eps0)
end if
write (u, "('('," // fmt // ",','," // fmt // ",')')", advance=advance) cval
else
write (u, "(A)", advance=advance) "[unknown complex]"
end if
case (V_SEV)
if (var%is_known) then
call subevt_write (var%pval, u, prefix=" ", &
pacified = pacified)
else
write (u, "(A)", advance=advance) "[unknown subevent]"
end if
case (V_PDG)
if (var%is_known) then
call pdg_array_write (var%aval, u); write (u, *)
else
write (u, "(A)", advance=advance) "[unknown PDG array]"
end if
case (V_STR)
if (var%is_known) then
if (et) then
write (u, "(A)", advance=advance) '"' // char (replace ( &
replace (var%sval, "_", "\_", every=.true.), "$", "\$" )) // '"'
else
write (u, "(A)", advance=advance) '"' // char (var%sval) // '"'
end if
else
write (u, "(A)", advance=advance) "[unknown string]"
end if
case (V_OBS1_INT); write (u, "(A)", advance=advance) "[int] = unary observable"
case (V_OBS2_INT); write (u, "(A)", advance=advance) "[int] = binary observable"
case (V_OBS1_REAL); write (u, "(A)", advance=advance) "[real] = unary observable"
case (V_OBS2_REAL); write (u, "(A)", advance=advance) "[real] = binary observable"
case (V_UOBS1_INT); write (u, "(A)", advance=advance) "[int] = unary user observable"
case (V_UOBS2_INT); write (u, "(A)", advance=advance) "[int] = binary user observable"
case (V_UOBS1_REAL); write (u, "(A)", advance=advance) "[real] = unary user observable"
case (V_UOBS2_REAL); write (u, "(A)", advance=advance) "[real] = binary user observable"
end select
end subroutine var_write_val
@ %def procedure
@
\subsubsection{Accessing contents}
<<Variables: procedures>>=
function var_entry_get_name (var) result (name)
type(string_t) :: name
type(var_entry_t), intent(in) :: var
name = var%name
end function var_entry_get_name
function var_entry_get_type (var) result (type)
integer :: type
type(var_entry_t), intent(in) :: var
type = var%type
end function var_entry_get_type
@ %def var_entry_get_name var_entry_get_type
@ Return true if the variable is defined. This the case if it is allocated
and known, or if it is a pointer.
<<Variables: procedures>>=
function var_entry_is_defined (var) result (defined)
logical :: defined
type(var_entry_t), intent(in) :: var
defined = var%is_defined
end function var_entry_is_defined
@ %def var_entry_is_defined
@ Return true if the variable is locked. If [[force]] is active,
always return false.
<<Variables: procedures>>=
function var_entry_is_locked (var, force) result (locked)
logical :: locked
type(var_entry_t), intent(in) :: var
logical, intent(in), optional :: force
if (present (force)) then
if (force) then
locked = .false.; return
end if
end if
locked = var%is_locked
end function var_entry_is_locked
@ %def var_entry_is_locked
@ Return true if the variable is intrinsic
<<Variables: procedures>>=
function var_entry_is_intrinsic (var) result (flag)
logical :: flag
type(var_entry_t), intent(in) :: var
flag = var%is_intrinsic
end function var_entry_is_intrinsic
@ %def var_entry_is_intrinsic
@ Return components
<<Variables: procedures>>=
function var_entry_is_known (var) result (flag)
logical :: flag
type(var_entry_t), intent(in) :: var
flag = var%is_known
end function var_entry_is_known
function var_entry_get_lval (var) result (lval)
logical :: lval
type(var_entry_t), intent(in) :: var
lval = var%lval
end function var_entry_get_lval
function var_entry_get_ival (var) result (ival)
integer :: ival
type(var_entry_t), intent(in) :: var
ival = var%ival
end function var_entry_get_ival
function var_entry_get_rval (var) result (rval)
real(default) :: rval
type(var_entry_t), intent(in) :: var
rval = var%rval
end function var_entry_get_rval
function var_entry_get_cval (var) result (cval)
complex(default) :: cval
type(var_entry_t), intent(in) :: var
cval = var%cval
end function var_entry_get_cval
function var_entry_get_aval (var) result (aval)
type(pdg_array_t) :: aval
type(var_entry_t), intent(in) :: var
aval = var%aval
end function var_entry_get_aval
function var_entry_get_pval (var) result (pval)
type(subevt_t) :: pval
type(var_entry_t), intent(in) :: var
pval = var%pval
end function var_entry_get_pval
function var_entry_get_sval (var) result (sval)
type(string_t) :: sval
type(var_entry_t), intent(in) :: var
sval = var%sval
end function var_entry_get_sval
@ %def var_entry_get_lval
@ %def var_entry_get_ival
@ %def var_entry_get_rval
@ %def var_entry_get_cval
@ %def var_entry_get_aval
@ %def var_entry_get_pval
@ %def var_entry_get_sval
@ Return pointers to components.
<<Variables: procedures>>=
function var_entry_get_known_ptr (var) result (ptr)
logical, pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%is_known
end function var_entry_get_known_ptr
function var_entry_get_lval_ptr (var) result (ptr)
logical, pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%lval
end function var_entry_get_lval_ptr
function var_entry_get_ival_ptr (var) result (ptr)
integer, pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%ival
end function var_entry_get_ival_ptr
function var_entry_get_rval_ptr (var) result (ptr)
real(default), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%rval
end function var_entry_get_rval_ptr
function var_entry_get_cval_ptr (var) result (ptr)
complex(default), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%cval
end function var_entry_get_cval_ptr
function var_entry_get_pval_ptr (var) result (ptr)
type(subevt_t), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%pval
end function var_entry_get_pval_ptr
function var_entry_get_aval_ptr (var) result (ptr)
type(pdg_array_t), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%aval
end function var_entry_get_aval_ptr
function var_entry_get_sval_ptr (var) result (ptr)
type(string_t), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%sval
end function var_entry_get_sval_ptr
@ %def var_entry_get_known_ptr
@ %def var_entry_get_lval_ptr var_entry_get_ival_ptr var_entry_get_rval_ptr
@ %def var_entry_get_cval_ptr var_entry_get_aval_ptr var_entry_get_pval_ptr
@ %def var_entry_get_sval_ptr
@ Furthermore,
<<Variables: procedures>>=
function var_entry_get_prt1_ptr (var) result (ptr)
type(prt_t), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%prt1
end function var_entry_get_prt1_ptr
function var_entry_get_prt2_ptr (var) result (ptr)
type(prt_t), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%prt2
end function var_entry_get_prt2_ptr
@ %def var_entry_get_prt1_ptr
@ %def var_entry_get_prt2_ptr
@ Subroutines might be safer than functions for procedure pointer transfer
(there was a nagfor bug).
<<Variables: procedures>>=
subroutine var_entry_assign_obs1_int_ptr (ptr, var)
procedure(obs_unary_int), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%obs1_int
end subroutine var_entry_assign_obs1_int_ptr
subroutine var_entry_assign_obs1_real_ptr (ptr, var)
procedure(obs_unary_real), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%obs1_real
end subroutine var_entry_assign_obs1_real_ptr
subroutine var_entry_assign_obs2_int_ptr (ptr, var)
procedure(obs_binary_int), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%obs2_int
end subroutine var_entry_assign_obs2_int_ptr
subroutine var_entry_assign_obs2_real_ptr (ptr, var)
procedure(obs_binary_real), pointer :: ptr
type(var_entry_t), intent(in), target :: var
ptr => var%obs2_real
end subroutine var_entry_assign_obs2_real_ptr
@ %def var_entry_assign_obs1_int_ptr var_entry_assign_obs1_real_ptr
@ %def var_entry_assign_obs2_int_ptr var_entry_assign_obs2_real_ptr
@
\subsection{Setting values}
Undefine the value.
<<Variables: procedures>>=
subroutine var_entry_clear_value (var)
type(var_entry_t), intent(inout) :: var
var%is_known = .false.
end subroutine var_entry_clear_value
@ %def var_entry_clear_value
<<Variables: procedures>>=
recursive subroutine var_entry_set_log &
(var, lval, is_known, verbose, model_name)
type(var_entry_t), intent(inout) :: var
logical, intent(in) :: lval
logical, intent(in) :: is_known
logical, intent(in), optional :: verbose
type(string_t), intent(in), optional :: model_name
integer :: u
u = logfile_unit ()
var%lval = lval
var%is_known = is_known
var%is_defined = .true.
if (present (verbose)) then
if (verbose) then
call var_entry_write (var, model_name=model_name)
call var_entry_write (var, model_name=model_name, unit=u)
if (u >= 0) flush (u)
end if
end if
end subroutine var_entry_set_log
recursive subroutine var_entry_set_int &
(var, ival, is_known, verbose, model_name)
type(var_entry_t), intent(inout) :: var
integer, intent(in) :: ival
logical, intent(in) :: is_known
logical, intent(in), optional :: verbose
type(string_t), intent(in), optional :: model_name
integer :: u
u = logfile_unit ()
var%ival = ival
var%is_known = is_known
var%is_defined = .true.
if (present (verbose)) then
if (verbose) then
call var_entry_write (var, model_name=model_name)
call var_entry_write (var, model_name=model_name, unit=u)
if (u >= 0) flush (u)
end if
end if
end subroutine var_entry_set_int
recursive subroutine var_entry_set_real &
(var, rval, is_known, verbose, model_name, pacified)
type(var_entry_t), intent(inout) :: var
real(default), intent(in) :: rval
logical, intent(in) :: is_known
logical, intent(in), optional :: verbose, pacified
type(string_t), intent(in), optional :: model_name
integer :: u
u = logfile_unit ()
var%rval = rval
var%is_known = is_known
var%is_defined = .true.
if (present (verbose)) then
if (verbose) then
call var_entry_write &
(var, model_name=model_name, pacified = pacified)
call var_entry_write &
(var, model_name=model_name, unit=u, pacified = pacified)
if (u >= 0) flush (u)
end if
end if
end subroutine var_entry_set_real
recursive subroutine var_entry_set_cmplx &
(var, cval, is_known, verbose, model_name, pacified)
type(var_entry_t), intent(inout) :: var
complex(default), intent(in) :: cval
logical, intent(in) :: is_known
logical, intent(in), optional :: verbose, pacified
type(string_t), intent(in), optional :: model_name
integer :: u
u = logfile_unit ()
var%cval = cval
var%is_known = is_known
var%is_defined = .true.
if (present (verbose)) then
if (verbose) then
call var_entry_write &
(var, model_name=model_name, pacified = pacified)
call var_entry_write &
(var, model_name=model_name, unit=u, pacified = pacified)
if (u >= 0) flush (u)
end if
end if
end subroutine var_entry_set_cmplx
recursive subroutine var_entry_set_pdg_array &
(var, aval, is_known, verbose, model_name)
type(var_entry_t), intent(inout) :: var
type(pdg_array_t), intent(in) :: aval
logical, intent(in) :: is_known
logical, intent(in), optional :: verbose
type(string_t), intent(in), optional :: model_name
integer :: u
u = logfile_unit ()
var%aval = aval
var%is_known = is_known
var%is_defined = .true.
if (present (verbose)) then
if (verbose) then
call var_entry_write (var, model_name=model_name)
call var_entry_write (var, model_name=model_name, unit=u)
if (u >= 0) flush (u)
end if
end if
end subroutine var_entry_set_pdg_array
recursive subroutine var_entry_set_subevt &
(var, pval, is_known, verbose, model_name)
type(var_entry_t), intent(inout) :: var
type(subevt_t), intent(in) :: pval
logical, intent(in) :: is_known
logical, intent(in), optional :: verbose
type(string_t), intent(in), optional :: model_name
integer :: u
u = logfile_unit ()
var%pval = pval
var%is_known = is_known
var%is_defined = .true.
if (present (verbose)) then
if (verbose) then
call var_entry_write (var, model_name=model_name)
call var_entry_write (var, model_name=model_name, unit=u)
if (u >= 0) flush (u)
end if
end if
end subroutine var_entry_set_subevt
recursive subroutine var_entry_set_string &
(var, sval, is_known, verbose, model_name)
type(var_entry_t), intent(inout) :: var
type(string_t), intent(in) :: sval
logical, intent(in) :: is_known
logical, intent(in), optional :: verbose
type(string_t), intent(in), optional :: model_name
integer :: u
u = logfile_unit ()
var%sval = sval
var%is_known = is_known
var%is_defined = .true.
if (present (verbose)) then
if (verbose) then
call var_entry_write (var, model_name=model_name)
call var_entry_write (var, model_name=model_name, unit=u)
if (u >= 0) flush (u)
end if
end if
end subroutine var_entry_set_string
@ %def var_entry_set_log
@ %def var_entry_set_int
@ %def var_entry_set_real
@ %def var_entry_set_cmplx
@ %def var_entry_set_pdg_array
@ %def var_entry_set_subevt
@ %def var_entry_set_string
@
<<Variables: public>>=
public :: var_entry_set_description
<<Variables: procedures>>=
pure subroutine var_entry_set_description (var_entry, description)
type(var_entry_t), intent(inout) :: var_entry
type(string_t), intent(in) :: description
var_entry%description = description
end subroutine var_entry_set_description
@ %def var_entry_set_description
@
\subsection{Copies and pointer variables}
Initialize an entry with a copy of an existing variable entry. The
copy is physically allocated with the same type as the original.
<<Variables: procedures>>=
subroutine var_entry_init_copy (var, original, user)
type(var_entry_t), intent(out) :: var
type(var_entry_t), intent(in), target :: original
logical, intent(in), optional :: user
type(string_t) :: name
logical :: intrinsic
name = var_entry_get_name (original)
intrinsic = original%is_intrinsic
select case (original%type)
case (V_LOG)
call var_entry_init_log (var, name, intrinsic=intrinsic, user=user)
case (V_INT)
call var_entry_init_int (var, name, intrinsic=intrinsic, user=user)
case (V_REAL)
call var_entry_init_real (var, name, intrinsic=intrinsic, user=user)
case (V_CMPLX)
call var_entry_init_cmplx (var, name, intrinsic=intrinsic, user=user)
case (V_SEV)
call var_entry_init_subevt (var, name, intrinsic=intrinsic, user=user)
case (V_PDG)
call var_entry_init_pdg_array (var, name, intrinsic=intrinsic, user=user)
case (V_STR)
call var_entry_init_string (var, name, intrinsic=intrinsic, user=user)
end select
end subroutine var_entry_init_copy
@ %def var_entry_init_copy
@ Copy the value of an entry. The target variable entry must be initialized
correctly.
<<Variables: procedures>>=
subroutine var_entry_copy_value (var, original)
type(var_entry_t), intent(inout) :: var
type(var_entry_t), intent(in), target :: original
if (var_entry_is_known (original)) then
select case (original%type)
case (V_LOG)
call var_entry_set_log (var, var_entry_get_lval (original), .true.)
case (V_INT)
call var_entry_set_int (var, var_entry_get_ival (original), .true.)
case (V_REAL)
call var_entry_set_real (var, var_entry_get_rval (original), .true.)
case (V_CMPLX)
call var_entry_set_cmplx (var, var_entry_get_cval (original), .true.)
case (V_SEV)
call var_entry_set_subevt (var, var_entry_get_pval (original), .true.)
case (V_PDG)
call var_entry_set_pdg_array (var, var_entry_get_aval (original), .true.)
case (V_STR)
call var_entry_set_string (var, var_entry_get_sval (original), .true.)
end select
else
call var_entry_clear (var)
end if
end subroutine var_entry_copy_value
@ %def var_entry_copy_value
@
\subsection{Variable lists}
\subsubsection{The type}
Variable lists can be linked together. No initializer needed.
They are deleted separately.
<<Variables: public>>=
public :: var_list_t
<<Variables: types>>=
type, extends (vars_t) :: var_list_t
private
type(var_entry_t), pointer :: first => null ()
type(var_entry_t), pointer :: last => null ()
type(var_list_t), pointer :: next => null ()
contains
<<Variables: var list: TBP>>
end type var_list_t
@ %def var_list_t
@
\subsubsection{Constructors}
Implementation of the [[link]] deferred method. The implementation
restricts itself to var lists of the same type. We might need to
relax this constraint.
<<Variables: var list: TBP>>=
procedure :: link => var_list_link
<<Variables: procedures>>=
subroutine var_list_link (vars, target_vars)
class(var_list_t), intent(inout) :: vars
class(vars_t), intent(in), target :: target_vars
select type (target_vars)
type is (var_list_t)
vars%next => target_vars
class default
call msg_bug ("var_list_link: unsupported target type")
end select
end subroutine var_list_link
@ %def var_list_link
@ Append a new entry to an existing list.
<<Variables: procedures>>=
subroutine var_list_append (var_list, var, verbose)
type(var_list_t), intent(inout), target :: var_list
type(var_entry_t), intent(inout), target :: var
logical, intent(in), optional :: verbose
if (associated (var_list%last)) then
var%previous => var_list%last
var_list%last%next => var
else
var%previous => null ()
var_list%first => var
end if
var_list%last => var
if (present (verbose)) then
if (verbose) call var_entry_write (var)
end if
end subroutine var_list_append
@ %def var_list_append
@ Sort a list.
<<Variables: var list: TBP>>=
procedure :: sort => var_list_sort
<<Variables: procedures>>=
subroutine var_list_sort (var_list)
class(var_list_t), intent(inout) :: var_list
type(var_entry_t), pointer :: var, previous
if (associated (var_list%first)) then
var => var_list%first
do while (associated (var))
previous => var%previous
do while (associated (previous))
if (larger_var (previous, var)) then
call var_list%swap_with_next (previous)
end if
previous => previous%previous
end do
var => var%next
end do
end if
end subroutine var_list_sort
@ %def var_list_sort
@
<<Variables: procedures>>=
pure function larger_var (var1, var2) result (larger)
logical :: larger
type(var_entry_t), intent(in) :: var1, var2
type(string_t) :: str1, str2
str1 = replace (var1%name, "?", "")
str1 = replace (str1, "$", "")
str2 = replace (var2%name, "?", "")
str2 = replace (str2, "$", "")
larger = str1 > str2
end function larger_var
@ %def larger_var
@
<<Variables: var list: TBP>>=
procedure :: get_previous => var_list_get_previous
<<Variables: procedures>>=
function var_list_get_previous (var_list, var_entry) result (previous)
type(var_entry_t), pointer :: previous
class(var_list_t), intent(in) :: var_list
type(var_entry_t), intent(in) :: var_entry
previous => var_list%first
if (previous%name == var_entry%name) then
previous => null ()
else
do while (associated (previous))
if (previous%next%name == var_entry%name) exit
previous => previous%next
end do
end if
end function var_list_get_previous
@ %def var_list_get_previous
@
<<Variables: var list: TBP>>=
procedure :: swap_with_next => var_list_swap_with_next
<<Variables: procedures>>=
subroutine var_list_swap_with_next (var_list, var_entry)
class(var_list_t), intent(inout) :: var_list
type(var_entry_t), intent(in) :: var_entry
type(var_entry_t), pointer :: previous, this, next, next_next
previous => var_list%get_previous (var_entry)
if (.not. associated (previous)) then
this => var_list%first
else
this => previous%next
end if
next => this%next
next_next => next%next
if (associated (previous)) then
previous%next => next
next%previous => previous
else
var_list%first => next
next%previous => null ()
end if
this%next => next_next
if (associated (next_next)) then
next_next%previous => this
end if
next%next => this
this%previous => next
if (.not. associated (next%next)) then
var_list%last => next
end if
end subroutine var_list_swap_with_next
@ %def var_list_swap_with_next
@ Public methods for expanding the variable list (as subroutines)
<<Variables: var list: TBP>>=
generic :: append_log => var_list_append_log_s, var_list_append_log_c
procedure, private :: var_list_append_log_s
procedure, private :: var_list_append_log_c
generic :: append_int => var_list_append_int_s, var_list_append_int_c
procedure, private :: var_list_append_int_s
procedure, private :: var_list_append_int_c
generic :: append_real => var_list_append_real_s, var_list_append_real_c
procedure, private :: var_list_append_real_s
procedure, private :: var_list_append_real_c
generic :: append_cmplx => var_list_append_cmplx_s, var_list_append_cmplx_c
procedure, private :: var_list_append_cmplx_s
procedure, private :: var_list_append_cmplx_c
generic :: append_subevt => var_list_append_subevt_s, var_list_append_subevt_c
procedure, private :: var_list_append_subevt_s
procedure, private :: var_list_append_subevt_c
generic :: append_pdg_array => var_list_append_pdg_array_s, var_list_append_pdg_array_c
procedure, private :: var_list_append_pdg_array_s
procedure, private :: var_list_append_pdg_array_c
generic :: append_string => var_list_append_string_s, var_list_append_string_c
procedure, private :: var_list_append_string_s
procedure, private :: var_list_append_string_c
<<Variables: public>>=
public :: var_list_append_log
public :: var_list_append_int
public :: var_list_append_real
public :: var_list_append_cmplx
public :: var_list_append_subevt
public :: var_list_append_pdg_array
public :: var_list_append_string
<<Variables: interfaces>>=
interface var_list_append_log
module procedure var_list_append_log_s
module procedure var_list_append_log_c
end interface
interface var_list_append_int
module procedure var_list_append_int_s
module procedure var_list_append_int_c
end interface
interface var_list_append_real
module procedure var_list_append_real_s
module procedure var_list_append_real_c
end interface
interface var_list_append_cmplx
module procedure var_list_append_cmplx_s
module procedure var_list_append_cmplx_c
end interface
interface var_list_append_subevt
module procedure var_list_append_subevt_s
module procedure var_list_append_subevt_c
end interface
interface var_list_append_pdg_array
module procedure var_list_append_pdg_array_s
module procedure var_list_append_pdg_array_c
end interface
interface var_list_append_string
module procedure var_list_append_string_s
module procedure var_list_append_string_c
end interface
<<Variables: procedures>>=
subroutine var_list_append_log_s &
(var_list, name, lval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
logical, intent(in), optional :: lval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_log (var, name, lval, intrinsic, user)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_log_s
subroutine var_list_append_int_s &
(var_list, name, ival, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
integer, intent(in), optional :: ival
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_int (var, name, ival, intrinsic, user)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_int_s
subroutine var_list_append_real_s &
(var_list, name, rval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
real(default), intent(in), optional :: rval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_real (var, name, rval, intrinsic, user)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_real_s
subroutine var_list_append_cmplx_s &
(var_list, name, cval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
complex(default), intent(in), optional :: cval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_cmplx (var, name, cval, intrinsic, user)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_cmplx_s
subroutine var_list_append_subevt_s &
(var_list, name, pval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
type(subevt_t), intent(in), optional :: pval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_subevt (var, name, pval, intrinsic, user)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_subevt_s
subroutine var_list_append_pdg_array_s &
(var_list, name, aval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
type(pdg_array_t), intent(in), optional :: aval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_pdg_array (var, name, aval, intrinsic, user)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_pdg_array_s
subroutine var_list_append_string_s &
(var_list, name, sval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
type(string_t), intent(in), optional :: sval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_string (var, name, sval, intrinsic, user)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_string_s
subroutine var_list_append_log_c &
(var_list, name, lval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
character(*), intent(in) :: name
logical, intent(in), optional :: lval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
call var_list_append_log_s &
(var_list, var_str (name), lval, locked, verbose, &
intrinsic, user, description)
end subroutine var_list_append_log_c
subroutine var_list_append_int_c &
(var_list, name, ival, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
character(*), intent(in) :: name
integer, intent(in), optional :: ival
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
call var_list_append_int_s &
(var_list, var_str (name), ival, locked, verbose, &
intrinsic, user, description)
end subroutine var_list_append_int_c
subroutine var_list_append_real_c &
(var_list, name, rval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
character(*), intent(in) :: name
real(default), intent(in), optional :: rval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
call var_list_append_real_s &
(var_list, var_str (name), rval, locked, verbose, &
intrinsic, user, description)
end subroutine var_list_append_real_c
subroutine var_list_append_cmplx_c &
(var_list, name, cval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
character(*), intent(in) :: name
complex(default), intent(in), optional :: cval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
call var_list_append_cmplx_s &
(var_list, var_str (name), cval, locked, verbose, &
intrinsic, user, description)
end subroutine var_list_append_cmplx_c
subroutine var_list_append_subevt_c &
(var_list, name, pval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
character(*), intent(in) :: name
type(subevt_t), intent(in), optional :: pval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
call var_list_append_subevt_s &
(var_list, var_str (name), pval, locked, verbose, &
intrinsic, user, description)
end subroutine var_list_append_subevt_c
subroutine var_list_append_pdg_array_c &
(var_list, name, aval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
character(*), intent(in) :: name
type(pdg_array_t), intent(in), optional :: aval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
call var_list_append_pdg_array_s &
(var_list, var_str (name), aval, locked, verbose, &
intrinsic, user, description)
end subroutine var_list_append_pdg_array_c
subroutine var_list_append_string_c &
(var_list, name, sval, locked, verbose, intrinsic, user, description)
class(var_list_t), intent(inout) :: var_list
character(*), intent(in) :: name
character(*), intent(in), optional :: sval
logical, intent(in), optional :: locked, verbose, intrinsic, user
type(string_t), intent(in), optional :: description
if (present (sval)) then
call var_list_append_string_s &
(var_list, var_str (name), var_str (sval), &
locked, verbose, intrinsic, user, description)
else
call var_list_append_string_s &
(var_list, var_str (name), &
locked=locked, verbose=verbose, intrinsic=intrinsic, &
user=user, description=description)
end if
end subroutine var_list_append_string_c
@ %def var_list_append_log
@ %def var_list_append_int
@ %def var_list_append_real
@ %def var_list_append_cmplx
@ %def var_list_append_subevt
@ %def var_list_append_pdg_array
@ %def var_list_append_string
<<Variables: public>>=
public :: var_list_append_log_ptr
public :: var_list_append_int_ptr
public :: var_list_append_real_ptr
public :: var_list_append_cmplx_ptr
public :: var_list_append_pdg_array_ptr
public :: var_list_append_subevt_ptr
public :: var_list_append_string_ptr
<<Variables: var list: TBP>>=
procedure :: append_log_ptr => var_list_append_log_ptr
procedure :: append_int_ptr => var_list_append_int_ptr
procedure :: append_real_ptr => var_list_append_real_ptr
procedure :: append_cmplx_ptr => var_list_append_cmplx_ptr
procedure :: append_pdg_array_ptr => var_list_append_pdg_array_ptr
procedure :: append_subevt_ptr => var_list_append_subevt_ptr
procedure :: append_string_ptr => var_list_append_string_ptr
<<Variables: procedures>>=
subroutine var_list_append_log_ptr &
(var_list, name, lval, is_known, locked, verbose, intrinsic, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
logical, intent(in), target :: lval
logical, intent(in), target :: is_known
logical, intent(in), optional :: locked, verbose, intrinsic
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_log_ptr (var, name, lval, is_known, intrinsic)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_log_ptr
subroutine var_list_append_int_ptr &
(var_list, name, ival, is_known, locked, verbose, intrinsic, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
integer, intent(in), target :: ival
logical, intent(in), target :: is_known
logical, intent(in), optional :: locked, verbose, intrinsic
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_int_ptr (var, name, ival, is_known, intrinsic)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_int_ptr
subroutine var_list_append_real_ptr &
(var_list, name, rval, is_known, locked, verbose, intrinsic, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
real(default), intent(in), target :: rval
logical, intent(in), target :: is_known
logical, intent(in), optional :: locked, verbose, intrinsic
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_real_ptr (var, name, rval, is_known, intrinsic)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_real_ptr
subroutine var_list_append_cmplx_ptr &
(var_list, name, cval, is_known, locked, verbose, intrinsic, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
complex(default), intent(in), target :: cval
logical, intent(in), target :: is_known
logical, intent(in), optional :: locked, verbose, intrinsic
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_cmplx_ptr (var, name, cval, is_known, intrinsic)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_cmplx_ptr
subroutine var_list_append_pdg_array_ptr &
(var_list, name, aval, is_known, locked, verbose, intrinsic, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
type(pdg_array_t), intent(in), target :: aval
logical, intent(in), target :: is_known
logical, intent(in), optional :: locked, verbose, intrinsic
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_pdg_array_ptr (var, name, aval, is_known, intrinsic)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_pdg_array_ptr
subroutine var_list_append_subevt_ptr &
(var_list, name, pval, is_known, locked, verbose, intrinsic, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
type(subevt_t), intent(in), target :: pval
logical, intent(in), target :: is_known
logical, intent(in), optional :: locked, verbose, intrinsic
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_subevt_ptr (var, name, pval, is_known, intrinsic)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_subevt_ptr
subroutine var_list_append_string_ptr &
(var_list, name, sval, is_known, locked, verbose, intrinsic, description)
class(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
type(string_t), intent(in), target :: sval
logical, intent(in), target :: is_known
logical, intent(in), optional :: locked, verbose, intrinsic
type(string_t), intent(in), optional :: description
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_string_ptr (var, name, sval, is_known, intrinsic)
if (present (description)) call var_entry_set_description (var, description)
if (present (locked)) call var_entry_lock (var, locked)
call var_list_append (var_list, var, verbose)
end subroutine var_list_append_string_ptr
@ %def var_list_append_log_ptr
@ %def var_list_append_int_ptr
@ %def var_list_append_real_ptr
@ %def var_list_append_cmplx_ptr
@ %def var_list_append_pdg_array_ptr
@ %def var_list_append_subevt_ptr
@
\subsubsection{Finalizer}
Finalize, delete the list entry by entry. The link itself is kept
intact. Follow link and delete recursively only if requested
explicitly.
<<Variables: var list: TBP>>=
procedure :: final => var_list_final
<<Variables: procedures>>=
recursive subroutine var_list_final (vars, follow_link)
class(var_list_t), intent(inout) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
vars%last => null ()
do while (associated (vars%first))
var => vars%first
vars%first => var%next
call var_entry_final (var)
deallocate (var)
end do
if (present (follow_link)) then
if (follow_link) then
if (associated (vars%next)) then
call vars%next%final (follow_link)
deallocate (vars%next)
end if
end if
end if
end subroutine var_list_final
@ %def var_list_final
@
\subsubsection{Output}
Show variable list with precise control over options. E.g.,
show only variables of a certain type.
Many options, thus not an ordinary [[write]] method.
<<Variables: public>>=
public :: var_list_write
<<Variables: var list: TBP>>=
procedure :: write => var_list_write
<<Variables: procedures>>=
recursive subroutine var_list_write &
(var_list, unit, follow_link, only_type, prefix, model_name, &
intrinsic, pacified, descriptions, ascii_output)
class(var_list_t), intent(in), target :: var_list
integer, intent(in), optional :: unit
logical, intent(in), optional :: follow_link
integer, intent(in), optional :: only_type
character(*), intent(in), optional :: prefix
type(string_t), intent(in), optional :: model_name
logical, intent(in), optional :: intrinsic
logical, intent(in), optional :: pacified
logical, intent(in), optional :: descriptions
logical, intent(in), optional :: ascii_output
type(var_entry_t), pointer :: var
integer :: u, length
logical :: write_this, write_next
u = given_output_unit (unit); if (u < 0) return
if (present (prefix)) length = len (prefix)
var => var_list%first
if (associated (var)) then
do while (associated (var))
if (present (only_type)) then
write_this = only_type == var%type
else
write_this = .true.
end if
if (write_this .and. present (prefix)) then
if (prefix /= extract (var%name, 1, length)) &
write_this = .false.
end if
if (write_this) then
call var_entry_write &
(var, unit, model_name=model_name, &
intrinsic=intrinsic, pacified=pacified, &
descriptions=descriptions, ascii_output=ascii_output)
end if
var => var%next
end do
end if
if (present (follow_link)) then
write_next = follow_link .and. associated (var_list%next)
else
write_next = associated (var_list%next)
end if
if (write_next) then
call var_list_write (var_list%next, &
unit, follow_link, only_type, prefix, model_name, &
intrinsic, pacified)
end if
end subroutine var_list_write
@ %def var_list_write
@ Write only a certain variable.
<<Variables: public>>=
public :: var_list_write_var
<<Variables: var list: TBP>>=
procedure :: write_var => var_list_write_var
<<Variables: procedures>>=
recursive subroutine var_list_write_var &
(var_list, name, unit, type, follow_link, &
model_name, pacified, defined, descriptions, ascii_output)
class(var_list_t), intent(in), target :: var_list
type(string_t), intent(in) :: name
integer, intent(in), optional :: unit
integer, intent(in), optional :: type
logical, intent(in), optional :: follow_link
type(string_t), intent(in), optional :: model_name
logical, intent(in), optional :: pacified
logical, intent(in), optional :: defined
logical, intent(in), optional :: descriptions
logical, intent(in), optional :: ascii_output
type(var_entry_t), pointer :: var
integer :: u
u = given_output_unit (unit); if (u < 0) return
var => var_list_get_var_ptr &
(var_list, name, type, follow_link=follow_link, defined=defined)
if (associated (var)) then
call var_entry_write &
(var, unit, model_name = model_name, &
pacified = pacified, &
descriptions=descriptions, ascii_output=ascii_output)
else
write (u, "(A)") char (name) // " = [undefined]"
end if
end subroutine var_list_write_var
@ %def var_list_write_var
@
\subsection{Tools}
Return a pointer to the variable list linked to by the current one.
<<Variables: procedures>>=
function var_list_get_next_ptr (var_list) result (next_ptr)
type(var_list_t), pointer :: next_ptr
type(var_list_t), intent(in) :: var_list
next_ptr => var_list%next
end function var_list_get_next_ptr
@ %def var_list_get_next_ptr
@ Used by [[eval_trees]]:
Return a pointer to the variable with the requested name. If no such
name exists, return a null pointer. In that case, try the next list
if present, unless [[follow_link]] is unset. If [[defined]] is set, ignore
entries that exist but are undefined.
<<Variables: public>>=
public :: var_list_get_var_ptr
<<Variables: procedures>>=
recursive function var_list_get_var_ptr &
(var_list, name, type, follow_link, defined) result (var)
type(var_entry_t), pointer :: var
type(var_list_t), intent(in), target :: var_list
type(string_t), intent(in) :: name
integer, intent(in), optional :: type
logical, intent(in), optional :: follow_link, defined
logical :: ignore_undef, search_next
ignore_undef = .true.; if (present (defined)) ignore_undef = .not. defined
var => var_list%first
if (present (type)) then
do while (associated (var))
if (var%type == type) then
if (var%name == name) then
if (ignore_undef .or. var%is_defined) return
end if
end if
var => var%next
end do
else
do while (associated (var))
if (var%name == name) then
if (ignore_undef .or. var%is_defined) return
end if
var => var%next
end do
end if
search_next = associated (var_list%next)
if (present (follow_link)) &
search_next = search_next .and. follow_link
if (search_next) &
var => var_list_get_var_ptr &
(var_list%next, name, type, defined=defined)
end function var_list_get_var_ptr
@ %def var_list_get_var_ptr
@ Return the variable type
<<Variables: var list: TBP>>=
procedure :: get_type => var_list_get_type
<<Variables: procedures>>=
function var_list_get_type (var_list, name, follow_link) result (type)
class(var_list_t), intent(in), target :: var_list
type(string_t), intent(in) :: name
logical, intent(in), optional :: follow_link
integer :: type
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, follow_link=follow_link)
if (associated (var)) then
type = var%type
else
type = V_NONE
end if
end function var_list_get_type
@ %def var_list_get_type
@ Return true if the variable exists in the current list.
<<Variables: var list: TBP>>=
procedure :: contains => var_list_exists
<<Variables: procedures>>=
function var_list_exists (vars, name, follow_link) result (lval)
logical :: lval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
lval = associated (var)
end function var_list_exists
@ %def var_list_exists
@ Return true if the variable is declared as intrinsic. (This is not a
property of the abstract [[vars_t]] type, and therefore the method is
not inherited.)
<<Variables: var list: TBP>>=
procedure :: is_intrinsic => var_list_is_intrinsic
<<Variables: procedures>>=
function var_list_is_intrinsic (vars, name, follow_link) result (lval)
logical :: lval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
lval = var%is_intrinsic
else
lval = .false.
end if
end function var_list_is_intrinsic
@ %def var_list_is_intrinsic
@ Return true if the value is known.
<<Variables: var list: TBP>>=
procedure :: is_known => var_list_is_known
<<Variables: procedures>>=
function var_list_is_known (vars, name, follow_link) result (lval)
logical :: lval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
lval = var%is_known
else
lval = .false.
end if
end function var_list_is_known
@ %def var_list_is_known
@ Return true if the value is locked. (This is not a
property of the abstract [[vars_t]] type, and therefore the method is
not inherited.)
<<Variables: var list: TBP>>=
procedure :: is_locked => var_list_is_locked
<<Variables: procedures>>=
function var_list_is_locked (vars, name, follow_link) result (lval)
logical :: lval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
lval = var_entry_is_locked (var)
else
lval = .false.
end if
end function var_list_is_locked
@ %def var_list_is_locked
@ Return several properties at once.
<<Variables: var list: TBP>>=
procedure :: get_var_properties => var_list_get_var_properties
<<Variables: procedures>>=
subroutine var_list_get_var_properties (vars, name, req_type, follow_link, &
type, is_defined, is_known, is_locked)
class(var_list_t), intent(in) :: vars
type(string_t), intent(in) :: name
integer, intent(in), optional :: req_type
logical, intent(in), optional :: follow_link
integer, intent(out), optional :: type
logical, intent(out), optional :: is_defined, is_known, is_locked
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr &
(vars, name, type=req_type, follow_link=follow_link)
if (associated (var)) then
if (present (type)) type = var_entry_get_type (var)
if (present (is_defined)) is_defined = var_entry_is_defined (var)
if (present (is_known)) is_known = var_entry_is_known (var)
if (present (is_locked)) is_locked = var_entry_is_locked (var)
else
if (present (type)) type = V_NONE
if (present (is_defined)) is_defined = .false.
if (present (is_known)) is_known = .false.
if (present (is_locked)) is_locked = .false.
end if
end subroutine var_list_get_var_properties
@ %def var_list_get_var_properties
@ Return the value, assuming that the type is correct. We consider only
variable entries that have been [[defined]].
For convenience, allow both variable and fixed-length (literal) strings.
<<Variables: var list: TBP>>=
procedure :: get_lval => var_list_get_lval
procedure :: get_ival => var_list_get_ival
procedure :: get_rval => var_list_get_rval
procedure :: get_cval => var_list_get_cval
procedure :: get_pval => var_list_get_pval
procedure :: get_aval => var_list_get_aval
procedure :: get_sval => var_list_get_sval
<<Variables: procedures>>=
function var_list_get_lval (vars, name, follow_link) result (lval)
logical :: lval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr &
(vars, name, V_LOG, follow_link, defined=.true.)
if (associated (var)) then
if (var_has_value (var)) then
lval = var%lval
else
lval = .false.
end if
else
lval = .false.
end if
end function var_list_get_lval
function var_list_get_ival (vars, name, follow_link) result (ival)
integer :: ival
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr &
(vars, name, V_INT, follow_link, defined=.true.)
if (associated (var)) then
if (var_has_value (var)) then
ival = var%ival
else
ival = 0
end if
else
ival = 0
end if
end function var_list_get_ival
function var_list_get_rval (vars, name, follow_link) result (rval)
real(default) :: rval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr &
(vars, name, V_REAL, follow_link, defined=.true.)
if (associated (var)) then
if (var_has_value (var)) then
rval = var%rval
else
rval = 0
end if
else
rval = 0
end if
end function var_list_get_rval
function var_list_get_cval (vars, name, follow_link) result (cval)
complex(default) :: cval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr &
(vars, name, V_CMPLX, follow_link, defined=.true.)
if (associated (var)) then
if (var_has_value (var)) then
cval = var%cval
else
cval = 0
end if
else
cval = 0
end if
end function var_list_get_cval
function var_list_get_aval (vars, name, follow_link) result (aval)
type(pdg_array_t) :: aval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr &
(vars, name, V_PDG, follow_link, defined=.true.)
if (associated (var)) then
if (var_has_value (var)) then
aval = var%aval
end if
end if
end function var_list_get_aval
function var_list_get_pval (vars, name, follow_link) result (pval)
type(subevt_t) :: pval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr &
(vars, name, V_SEV, follow_link, defined=.true.)
if (associated (var)) then
if (var_has_value (var)) then
pval = var%pval
end if
end if
end function var_list_get_pval
function var_list_get_sval (vars, name, follow_link) result (sval)
type(string_t) :: sval
type(string_t), intent(in) :: name
class(var_list_t), intent(in) :: vars
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr &
(vars, name, V_STR, follow_link, defined=.true.)
if (associated (var)) then
if (var_has_value (var)) then
sval = var%sval
else
sval = ""
end if
else
sval = ""
end if
end function var_list_get_sval
@ %def var_list_get_lval
@ %def var_list_get_ival
@ %def var_list_get_rval
@ %def var_list_get_cval
@ %def var_list_get_pval
@ %def var_list_get_aval
@ %def var_list_get_sval
@ Check for a valid value, given a pointer. Issue error messages if invalid.
<<Variables: procedures>>=
function var_has_value (var) result (valid)
logical :: valid
type(var_entry_t), pointer :: var
if (associated (var)) then
if (var%is_known) then
valid = .true.
else
call msg_error ("The value of variable '" // char (var%name) &
// "' is unknown but must be known at this point.")
valid = .false.
end if
else
call msg_error ("Variable '" // char (var%name) &
// "' is undefined but must have a known value at this point.")
valid = .false.
end if
end function var_has_value
@ %def var_has_value
@ Return pointers instead of values, including a pointer to the
[[known]] entry.
<<Variables: var list: TBP>>=
procedure :: get_lptr => var_list_get_lptr
procedure :: get_iptr => var_list_get_iptr
procedure :: get_rptr => var_list_get_rptr
procedure :: get_cptr => var_list_get_cptr
procedure :: get_aptr => var_list_get_aptr
procedure :: get_pptr => var_list_get_pptr
procedure :: get_sptr => var_list_get_sptr
<<Variables: procedures>>=
subroutine var_list_get_lptr (var_list, name, lptr, known)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
logical, pointer, intent(out) :: lptr
logical, pointer, intent(out), optional :: known
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_LOG)
if (associated (var)) then
lptr => var_entry_get_lval_ptr (var)
if (present (known)) known => var_entry_get_known_ptr (var)
else
lptr => null ()
if (present (known)) known => null ()
end if
end subroutine var_list_get_lptr
subroutine var_list_get_iptr (var_list, name, iptr, known)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
integer, pointer, intent(out) :: iptr
logical, pointer, intent(out), optional :: known
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_INT)
if (associated (var)) then
iptr => var_entry_get_ival_ptr (var)
if (present (known)) known => var_entry_get_known_ptr (var)
else
iptr => null ()
if (present (known)) known => null ()
end if
end subroutine var_list_get_iptr
subroutine var_list_get_rptr (var_list, name, rptr, known)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
real(default), pointer, intent(out) :: rptr
logical, pointer, intent(out), optional :: known
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_REAL)
if (associated (var)) then
rptr => var_entry_get_rval_ptr (var)
if (present (known)) known => var_entry_get_known_ptr (var)
else
rptr => null ()
if (present (known)) known => null ()
end if
end subroutine var_list_get_rptr
subroutine var_list_get_cptr (var_list, name, cptr, known)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
complex(default), pointer, intent(out) :: cptr
logical, pointer, intent(out), optional :: known
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_CMPLX)
if (associated (var)) then
cptr => var_entry_get_cval_ptr (var)
if (present (known)) known => var_entry_get_known_ptr (var)
else
cptr => null ()
if (present (known)) known => null ()
end if
end subroutine var_list_get_cptr
subroutine var_list_get_aptr (var_list, name, aptr, known)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
type(pdg_array_t), pointer, intent(out) :: aptr
logical, pointer, intent(out), optional :: known
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_PDG)
if (associated (var)) then
aptr => var_entry_get_aval_ptr (var)
if (present (known)) known => var_entry_get_known_ptr (var)
else
aptr => null ()
if (present (known)) known => null ()
end if
end subroutine var_list_get_aptr
subroutine var_list_get_pptr (var_list, name, pptr, known)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
type(subevt_t), pointer, intent(out) :: pptr
logical, pointer, intent(out), optional :: known
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_SEV)
if (associated (var)) then
pptr => var_entry_get_pval_ptr (var)
if (present (known)) known => var_entry_get_known_ptr (var)
else
pptr => null ()
if (present (known)) known => null ()
end if
end subroutine var_list_get_pptr
subroutine var_list_get_sptr (var_list, name, sptr, known)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
type(string_t), pointer, intent(out) :: sptr
logical, pointer, intent(out), optional :: known
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_STR)
if (associated (var)) then
sptr => var_entry_get_sval_ptr (var)
if (present (known)) known => var_entry_get_known_ptr (var)
else
sptr => null ()
if (present (known)) known => null ()
end if
end subroutine var_list_get_sptr
@ %def var_list_get_lptr
@ %def var_list_get_iptr
@ %def var_list_get_rptr
@ %def var_list_get_cptr
@ %def var_list_get_aptr
@ %def var_list_get_pptr
@ %def var_list_get_sptr
@
This bunch of methods handles the procedure-pointer cases.
<<Variables: var list: TBP>>=
procedure :: get_obs1_iptr => var_list_get_obs1_iptr
procedure :: get_obs2_iptr => var_list_get_obs2_iptr
procedure :: get_obs1_rptr => var_list_get_obs1_rptr
procedure :: get_obs2_rptr => var_list_get_obs2_rptr
<<Variables: procedures>>=
subroutine var_list_get_obs1_iptr (var_list, name, obs1_iptr, p1)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
procedure(obs_unary_int), pointer, intent(out) :: obs1_iptr
type(prt_t), pointer, intent(out) :: p1
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_OBS1_INT)
if (associated (var)) then
call var_entry_assign_obs1_int_ptr (obs1_iptr, var)
p1 => var_entry_get_prt1_ptr (var)
else
obs1_iptr => null ()
p1 => null ()
end if
end subroutine var_list_get_obs1_iptr
subroutine var_list_get_obs2_iptr (var_list, name, obs2_iptr, p1, p2)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
procedure(obs_binary_int), pointer, intent(out) :: obs2_iptr
type(prt_t), pointer, intent(out) :: p1, p2
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_OBS2_INT)
if (associated (var)) then
call var_entry_assign_obs2_int_ptr (obs2_iptr, var)
p1 => var_entry_get_prt1_ptr (var)
p2 => var_entry_get_prt2_ptr (var)
else
obs2_iptr => null ()
p1 => null ()
p2 => null ()
end if
end subroutine var_list_get_obs2_iptr
subroutine var_list_get_obs1_rptr (var_list, name, obs1_rptr, p1)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
procedure(obs_unary_real), pointer, intent(out) :: obs1_rptr
type(prt_t), pointer, intent(out) :: p1
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_OBS1_REAL)
if (associated (var)) then
call var_entry_assign_obs1_real_ptr (obs1_rptr, var)
p1 => var_entry_get_prt1_ptr (var)
else
obs1_rptr => null ()
p1 => null ()
end if
end subroutine var_list_get_obs1_rptr
subroutine var_list_get_obs2_rptr (var_list, name, obs2_rptr, p1, p2)
class(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: name
procedure(obs_binary_real), pointer, intent(out) :: obs2_rptr
type(prt_t), pointer, intent(out) :: p1, p2
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_OBS2_REAL)
if (associated (var)) then
call var_entry_assign_obs2_real_ptr (obs2_rptr, var)
p1 => var_entry_get_prt1_ptr (var)
p2 => var_entry_get_prt2_ptr (var)
else
obs2_rptr => null ()
p1 => null ()
p2 => null ()
end if
end subroutine var_list_get_obs2_rptr
@ %def var_list_get_obs1_iptr
@ %def var_list_get_obs2_iptr
@ %def var_list_get_obs1_rptr
@ %def var_list_get_obs2_rptr
@
\subsection{Process Result Variables}
These variables are associated to process (integration) runs and their
results. Their names contain brackets (so they look like function
evaluations), therefore we need to special-case them.
<<Variables: public>>=
public :: var_list_set_procvar_int
public :: var_list_set_procvar_real
<<Variables: procedures>>=
subroutine var_list_set_procvar_int (var_list, proc_id, name, ival)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: proc_id
type(string_t), intent(in) :: name
integer, intent(in), optional :: ival
type(string_t) :: var_name
type(var_entry_t), pointer :: var
var_name = name // "(" // proc_id // ")"
var => var_list_get_var_ptr (var_list, var_name)
if (.not. associated (var)) then
call var_list%append_int (var_name, ival, intrinsic=.true.)
else if (present (ival)) then
call var_list%set_int (var_name, ival, is_known=.true.)
end if
end subroutine var_list_set_procvar_int
subroutine var_list_set_procvar_real (var_list, proc_id, name, rval)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: proc_id
type(string_t), intent(in) :: name
real(default), intent(in), optional :: rval
type(string_t) :: var_name
type(var_entry_t), pointer :: var
var_name = name // "(" // proc_id // ")"
var => var_list_get_var_ptr (var_list, var_name)
if (.not. associated (var)) then
call var_list%append_real (var_name, rval, intrinsic=.true.)
else if (present (rval)) then
call var_list%set_real (var_name, rval, is_known=.true.)
end if
end subroutine var_list_set_procvar_real
@ %def var_list_set_procvar_int
@ %def var_list_set_procvar_real
@
\subsection{Observable initialization}
Observables are formally treated as variables, which however are
evaluated each time the observable is used. The arguments (pointers)
to evaluate and the function are part of the variable-list entry.
<<Variables: public>>=
public :: var_list_append_obs1_iptr
public :: var_list_append_obs2_iptr
public :: var_list_append_obs1_rptr
public :: var_list_append_obs2_rptr
<<Variables: procedures>>=
subroutine var_list_append_obs1_iptr (var_list, name, obs1_iptr, p1)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
procedure(obs_unary_int) :: obs1_iptr
type(prt_t), intent(in), target :: p1
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_obs (var, name, V_OBS1_INT, p1)
var%obs1_int => obs1_iptr
call var_list_append (var_list, var)
end subroutine var_list_append_obs1_iptr
subroutine var_list_append_obs2_iptr (var_list, name, obs2_iptr, p1, p2)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
procedure(obs_binary_int) :: obs2_iptr
type(prt_t), intent(in), target :: p1, p2
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_obs (var, name, V_OBS2_INT, p1, p2)
var%obs2_int => obs2_iptr
call var_list_append (var_list, var)
end subroutine var_list_append_obs2_iptr
subroutine var_list_append_obs1_rptr (var_list, name, obs1_rptr, p1)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
procedure(obs_unary_real) :: obs1_rptr
type(prt_t), intent(in), target :: p1
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_obs (var, name, V_OBS1_REAL, p1)
var%obs1_real => obs1_rptr
call var_list_append (var_list, var)
end subroutine var_list_append_obs1_rptr
subroutine var_list_append_obs2_rptr (var_list, name, obs2_rptr, p1, p2)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
procedure(obs_binary_real) :: obs2_rptr
type(prt_t), intent(in), target :: p1, p2
type(var_entry_t), pointer :: var
allocate (var)
call var_entry_init_obs (var, name, V_OBS2_REAL, p1, p2)
var%obs2_real => obs2_rptr
call var_list_append (var_list, var)
end subroutine var_list_append_obs2_rptr
@ %def var_list_append_obs1_iptr
@ %def var_list_append_obs2_iptr
@ %def var_list_append_obs1_rptr
@ %def var_list_append_obs2_rptr
@ User observables: no pointer needs to be stored.
<<Variables: public>>=
public :: var_list_append_uobs_int
public :: var_list_append_uobs_real
<<Variables: procedures>>=
subroutine var_list_append_uobs_int (var_list, name, p1, p2)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
type(prt_t), intent(in), target :: p1
type(prt_t), intent(in), target, optional :: p2
type(var_entry_t), pointer :: var
allocate (var)
if (present (p2)) then
call var_entry_init_obs (var, name, V_UOBS2_INT, p1, p2)
else
call var_entry_init_obs (var, name, V_UOBS1_INT, p1)
end if
call var_list_append (var_list, var)
end subroutine var_list_append_uobs_int
subroutine var_list_append_uobs_real (var_list, name, p1, p2)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: name
type(prt_t), intent(in), target :: p1
type(prt_t), intent(in), target, optional :: p2
type(var_entry_t), pointer :: var
allocate (var)
if (present (p2)) then
call var_entry_init_obs (var, name, V_UOBS2_REAL, p1, p2)
else
call var_entry_init_obs (var, name, V_UOBS1_REAL, p1)
end if
call var_list_append (var_list, var)
end subroutine var_list_append_uobs_real
@ %def var_list_append_uobs_int
@ %def var_list_append_uobs_real
@
\subsection{API for variable lists}
Set a new value. If the variable holds a pointer, this pointer is
followed, e.g., a model parameter is actually set. If [[ignore]] is
set, do nothing if the variable does not exist. If [[verbose]] is
set, echo the new value.
Clear a variable (all variables), i.e., undefine the value.
<<Variables: var list: TBP>>=
procedure :: unset => var_list_clear
<<Variables: procedures>>=
subroutine var_list_clear (vars, name, follow_link)
class(var_list_t), intent(inout) :: vars
type(string_t), intent(in) :: name
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
call var_entry_clear (var)
end if
end subroutine var_list_clear
@ %def var_list_clear
@
Setting the value, concise specific versions (implementing deferred TBP):
<<Variables: var list: TBP>>=
procedure :: set_ival => var_list_set_ival
procedure :: set_rval => var_list_set_rval
procedure :: set_cval => var_list_set_cval
procedure :: set_lval => var_list_set_lval
procedure :: set_sval => var_list_set_sval
<<Variables: procedures>>=
subroutine var_list_set_ival (vars, name, ival, follow_link)
class(var_list_t), intent(inout) :: vars
type(string_t), intent(in) :: name
integer, intent(in) :: ival
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
call var_entry_set_int (var, ival, is_known=.true.)
end if
end subroutine var_list_set_ival
subroutine var_list_set_rval (vars, name, rval, follow_link)
class(var_list_t), intent(inout) :: vars
type(string_t), intent(in) :: name
real(default), intent(in) :: rval
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
call var_entry_set_real (var, rval, is_known=.true.)
end if
end subroutine var_list_set_rval
subroutine var_list_set_cval (vars, name, cval, follow_link)
class(var_list_t), intent(inout) :: vars
type(string_t), intent(in) :: name
complex(default), intent(in) :: cval
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
call var_entry_set_cmplx (var, cval, is_known=.true.)
end if
end subroutine var_list_set_cval
subroutine var_list_set_lval (vars, name, lval, follow_link)
class(var_list_t), intent(inout) :: vars
type(string_t), intent(in) :: name
logical, intent(in) :: lval
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
call var_entry_set_log (var, lval, is_known=.true.)
end if
end subroutine var_list_set_lval
subroutine var_list_set_sval (vars, name, sval, follow_link)
class(var_list_t), intent(inout) :: vars
type(string_t), intent(in) :: name
type(string_t), intent(in) :: sval
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (vars, name, follow_link=follow_link)
if (associated (var)) then
call var_entry_set_string (var, sval, is_known=.true.)
end if
end subroutine var_list_set_sval
@ %def var_list_set_ival
@ %def var_list_set_rval
@ %def var_list_set_cval
@ %def var_list_set_lval
@ %def var_list_set_sval
@
Setting the value, verbose specific versions (as subroutines):
<<Variables: var list: TBP>>=
procedure :: set_log => var_list_set_log
procedure :: set_int => var_list_set_int
procedure :: set_real => var_list_set_real
procedure :: set_cmplx => var_list_set_cmplx
procedure :: set_subevt => var_list_set_subevt
procedure :: set_pdg_array => var_list_set_pdg_array
procedure :: set_string => var_list_set_string
<<Variables: procedures>>=
subroutine var_list_set_log &
(var_list, name, lval, is_known, ignore, force, verbose, model_name)
class(var_list_t), intent(inout), target :: var_list
type(string_t), intent(in) :: name
logical, intent(in) :: lval
logical, intent(in) :: is_known
logical, intent(in), optional :: ignore, force, verbose
type(string_t), intent(in), optional :: model_name
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_LOG)
if (associated (var)) then
if (.not. var_entry_is_locked (var, force)) then
select case (var%type)
case (V_LOG)
call var_entry_set_log (var, lval, is_known, verbose, model_name)
case default
call var_mismatch_error (name)
end select
else
call var_locked_error (name)
end if
else
call var_missing_error (name, ignore)
end if
end subroutine var_list_set_log
subroutine var_list_set_int &
(var_list, name, ival, is_known, ignore, force, verbose, model_name)
class(var_list_t), intent(inout), target :: var_list
type(string_t), intent(in) :: name
integer, intent(in) :: ival
logical, intent(in) :: is_known
logical, intent(in), optional :: ignore, force, verbose
type(string_t), intent(in), optional :: model_name
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_INT)
if (associated (var)) then
if (.not. var_entry_is_locked (var, force)) then
select case (var%type)
case (V_INT)
call var_entry_set_int (var, ival, is_known, verbose, model_name)
case default
call var_mismatch_error (name)
end select
else
call var_locked_error (name)
end if
else
call var_missing_error (name, ignore)
end if
end subroutine var_list_set_int
subroutine var_list_set_real &
(var_list, name, rval, is_known, ignore, force, &
verbose, model_name, pacified)
class(var_list_t), intent(inout), target :: var_list
type(string_t), intent(in) :: name
real(default), intent(in) :: rval
logical, intent(in) :: is_known
logical, intent(in), optional :: ignore, force, verbose, pacified
type(string_t), intent(in), optional :: model_name
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_REAL)
if (associated (var)) then
if (.not. var_entry_is_locked (var, force)) then
select case (var%type)
case (V_REAL)
call var_entry_set_real &
(var, rval, is_known, verbose, model_name, pacified)
case default
call var_mismatch_error (name)
end select
else
call var_locked_error (name)
end if
else
call var_missing_error (name, ignore)
end if
end subroutine var_list_set_real
subroutine var_list_set_cmplx &
(var_list, name, cval, is_known, ignore, force, &
verbose, model_name, pacified)
class(var_list_t), intent(inout), target :: var_list
type(string_t), intent(in) :: name
complex(default), intent(in) :: cval
logical, intent(in) :: is_known
logical, intent(in), optional :: ignore, force, verbose, pacified
type(string_t), intent(in), optional :: model_name
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_CMPLX)
if (associated (var)) then
if (.not. var_entry_is_locked (var, force)) then
select case (var%type)
case (V_CMPLX)
call var_entry_set_cmplx &
(var, cval, is_known, verbose, model_name, pacified)
case default
call var_mismatch_error (name)
end select
else
call var_locked_error (name)
end if
else
call var_missing_error (name, ignore)
end if
end subroutine var_list_set_cmplx
subroutine var_list_set_pdg_array &
(var_list, name, aval, is_known, ignore, force, verbose, model_name)
class(var_list_t), intent(inout), target :: var_list
type(string_t), intent(in) :: name
type(pdg_array_t), intent(in) :: aval
logical, intent(in) :: is_known
logical, intent(in), optional :: ignore, force, verbose
type(string_t), intent(in), optional :: model_name
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_PDG)
if (associated (var)) then
if (.not. var_entry_is_locked (var, force)) then
select case (var%type)
case (V_PDG)
call var_entry_set_pdg_array &
(var, aval, is_known, verbose, model_name)
case default
call var_mismatch_error (name)
end select
else
call var_locked_error (name)
end if
else
call var_missing_error (name, ignore)
end if
end subroutine var_list_set_pdg_array
subroutine var_list_set_subevt &
(var_list, name, pval, is_known, ignore, force, verbose, model_name)
class(var_list_t), intent(inout), target :: var_list
type(string_t), intent(in) :: name
type(subevt_t), intent(in) :: pval
logical, intent(in) :: is_known
logical, intent(in), optional :: ignore, force, verbose
type(string_t), intent(in), optional :: model_name
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_SEV)
if (associated (var)) then
if (.not. var_entry_is_locked (var, force)) then
select case (var%type)
case (V_SEV)
call var_entry_set_subevt &
(var, pval, is_known, verbose, model_name)
case default
call var_mismatch_error (name)
end select
else
call var_locked_error (name)
end if
else
call var_missing_error (name, ignore)
end if
end subroutine var_list_set_subevt
subroutine var_list_set_string &
(var_list, name, sval, is_known, ignore, force, verbose, model_name)
class(var_list_t), intent(inout), target :: var_list
type(string_t), intent(in) :: name
type(string_t), intent(in) :: sval
logical, intent(in) :: is_known
logical, intent(in), optional :: ignore, force, verbose
type(string_t), intent(in), optional :: model_name
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name, V_STR)
if (associated (var)) then
if (.not. var_entry_is_locked (var, force)) then
select case (var%type)
case (V_STR)
call var_entry_set_string &
(var, sval, is_known, verbose, model_name)
case default
call var_mismatch_error (name)
end select
else
call var_locked_error (name)
end if
else
call var_missing_error (name, ignore)
end if
end subroutine var_list_set_string
subroutine var_mismatch_error (name)
type(string_t), intent(in) :: name
call msg_fatal ("Type mismatch for variable '" // char (name) // "'")
end subroutine var_mismatch_error
subroutine var_locked_error (name)
type(string_t), intent(in) :: name
call msg_error ("Variable '" // char (name) // "' is not user-definable")
end subroutine var_locked_error
subroutine var_missing_error (name, ignore)
type(string_t), intent(in) :: name
logical, intent(in), optional :: ignore
logical :: error
if (present (ignore)) then
error = .not. ignore
else
error = .true.
end if
if (error) then
call msg_fatal ("Variable '" // char (name) // "' has not been declared")
end if
end subroutine var_missing_error
@ %def var_list_set_log
@ %def var_list_set_int
@ %def var_list_set_real
@ %def var_list_set_cmplx
@ %def var_list_set_subevt
@ %def var_list_set_pdg_array
@ %def var_list_set_string
@ %def var_mismatch_error
@ %def var_missing_error
@
Import values for the current variable list from another list.
<<Variables: public>>=
public :: var_list_import
<<Variables: var list: TBP>>=
procedure :: import => var_list_import
<<Variables: procedures>>=
subroutine var_list_import (var_list, src_list)
class(var_list_t), intent(inout) :: var_list
type(var_list_t), intent(in) :: src_list
type(var_entry_t), pointer :: var, src
var => var_list%first
do while (associated (var))
src => var_list_get_var_ptr (src_list, var%name)
if (associated (src)) then
call var_entry_copy_value (var, src)
end if
var => var%next
end do
end subroutine var_list_import
@ %def var_list_import
@ Mark all entries in the current variable list as undefined. This is done
when a local variable list is discarded. If the local list is used again (by
a loop), the entries will be re-initialized.
<<Variables: public>>=
public :: var_list_undefine
<<Variables: var list: TBP>>=
procedure :: undefine => var_list_undefine
<<Variables: procedures>>=
recursive subroutine var_list_undefine (var_list, follow_link)
class(var_list_t), intent(inout) :: var_list
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var
logical :: rec
rec = .true.; if (present (follow_link)) rec = follow_link
var => var_list%first
do while (associated (var))
call var_entry_undefine (var)
var => var%next
end do
if (rec .and. associated (var_list%next)) then
call var_list_undefine (var_list%next, follow_link=follow_link)
end if
end subroutine var_list_undefine
@ %def var_list_undefine
@ Make a deep copy of a variable list.
<<Variables: public>>=
public :: var_list_init_snapshot
<<Variables: var list: TBP>>=
procedure :: init_snapshot => var_list_init_snapshot
<<Variables: procedures>>=
recursive subroutine var_list_init_snapshot (var_list, vars_in, follow_link)
class(var_list_t), intent(out) :: var_list
type(var_list_t), intent(in) :: vars_in
logical, intent(in), optional :: follow_link
type(var_entry_t), pointer :: var, var_in
type(var_list_t), pointer :: var_list_next
logical :: rec
rec = .true.; if (present (follow_link)) rec = follow_link
var_in => vars_in%first
do while (associated (var_in))
allocate (var)
call var_entry_init_copy (var, var_in)
call var_entry_copy_value (var, var_in)
call var_list_append (var_list, var)
var_in => var_in%next
end do
if (rec .and. associated (vars_in%next)) then
allocate (var_list_next)
call var_list_init_snapshot (var_list_next, vars_in%next)
call var_list%link (var_list_next)
end if
end subroutine var_list_init_snapshot
@ %def var_list_init_snapshot
@ Check if a user variable can be set. The [[new]] flag is set if the user
variable has an explicit declaration. If an error occurs, return [[V_NONE]]
as variable type.
Also determine the actual type of generic numerical variables, which enter the
procedure with type [[V_NONE]].
<<Variables: public>>=
public :: var_list_check_user_var
<<Variables: var list: TBP>>=
procedure :: check_user_var => var_list_check_user_var
<<Variables: procedures>>=
subroutine var_list_check_user_var (var_list, name, type, new)
class(var_list_t), intent(in), target :: var_list
type(string_t), intent(in) :: name
integer, intent(inout) :: type
logical, intent(in) :: new
type(var_entry_t), pointer :: var
var => var_list_get_var_ptr (var_list, name)
if (associated (var)) then
if (type == V_NONE) then
type = var_entry_get_type (var)
end if
if (var_entry_is_locked (var)) then
call msg_fatal ("Variable '" // char (name) &
// "' is not user-definable")
type = V_NONE
return
else if (new) then
if (var_entry_is_intrinsic (var)) then
call msg_fatal ("Intrinsic variable '" &
// char (name) // "' redeclared")
type = V_NONE
return
end if
if (var_entry_get_type (var) /= type) then
call msg_fatal ("Variable '" // char (name) // "' " &
// "redeclared with different type")
type = V_NONE
return
end if
end if
end if
end subroutine var_list_check_user_var
@ %def var_list_check_user_var
@
\subsection{Default values for global var list}
<<Variables: var list: TBP>>=
procedure :: init_defaults => var_list_init_defaults
<<Variables: procedures>>=
subroutine var_list_init_defaults (var_list, seed, paths)
class(var_list_t), intent(out) :: var_list
integer, intent(in) :: seed
type(paths_t), intent(in), optional :: paths
call var_list%set_beams_defaults (paths)
call var_list%set_core_defaults (seed)
call var_list%set_integration_defaults ()
call var_list%set_phase_space_defaults ()
call var_list%set_gamelan_defaults ()
call var_list%set_clustering_defaults ()
call var_list%set_eio_defaults ()
call var_list%set_shower_defaults ()
call var_list%set_hadronization_defaults ()
call var_list%set_tauola_defaults ()
call var_list%set_mlm_matching_defaults ()
call var_list%set_powheg_matching_defaults ()
call var_list%append_log (var_str ("?ckkw_matching"), .false., &
intrinsic=.true., description=var_str ('Master flag that switches ' // &
'on the CKKW(-L) (LO) matching between hard scattering matrix ' // &
'elements and QCD parton showers. Note that this is not yet ' // &
'(completely) implemented in \whizard. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...})'))
call var_list%set_openmp_defaults ()
call var_list%set_mpi_defaults ()
call var_list%set_nlo_defaults ()
end subroutine var_list_init_defaults
@ %def var_list_init_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_beams_defaults => var_list_set_beams_defaults
<<Variables: procedures>>=
subroutine var_list_set_beams_defaults (var_list, paths)
type(paths_t), intent(in), optional :: paths
class(var_list_t), intent(inout) :: var_list
call var_list%append_real (var_str ("sqrts"), &
intrinsic=.true., &
description=var_str ('Real variable in order to set the center-of-mass ' // &
'energy for the collisions (collider energy $\sqrt{s}$, not ' // &
'hard interaction energy $\sqrt{\hat{s}}$): \ttt{sqrts = {\em ' // &
'<num>} [ {\em <phys\_unit>} ]}. The physical unit can be one ' // &
'of the following \ttt{eV}, \ttt{keV}, \ttt{MeV}, \ttt{GeV}, ' // &
'and \ttt{TeV}. If absent, \whizard\ takes \ttt{GeV} as its ' // &
'standard unit. Note that this variable is absolutely mandatory ' // &
'for integration and simulation of scattering processes.'))
call var_list%append_real (var_str ("luminosity"), 0._default, &
intrinsic=.true., &
description=var_str ('This specifier \ttt{luminosity = {\em ' // &
'<num>}} sets the integrated luminosity (in inverse femtobarns, ' // &
'fb${}^{-1}$) for the event generation of the processes in the ' // &
'\sindarin\ input files. Note that WHIZARD itself chooses the ' // &
'number from the \ttt{luminosity} or from the \ttt{n\_events} ' // &
'specifier, whichever would give the larger number of events. ' // &
'As this depends on the cross section under consideration, it ' // &
'might be different for different processes in the process list. ' // &
'(cf. \ttt{n\_events}, \ttt{\$sample}, \ttt{sample\_format}, \ttt{?unweighted})'))
call var_list%append_log (var_str ("?sf_trace"), .false., &
intrinsic=.true., &
description=var_str ('Debug flag that writes out detailed information ' // &
'about the structure function setup into the file \ttt{{\em ' // &
'<proc\_name>}\_sftrace.dat}. This file name can be changed ' // &
'with ($\to$) \ttt{\$sf\_trace\_file}.'))
call var_list%append_string (var_str ("$sf_trace_file"), var_str (""), &
intrinsic=.true., &
description=var_str ('\ttt{\$sf\_trace\_file = "{\em <file\_name>}"} ' // &
'allows to change the detailed structure function information ' // &
'switched on by the debug flag ($\to$) \ttt{?sf\_trace} into ' // &
'a different file \ttt{{\em <file\_name>}} than the default ' // &
'\ttt{{\em <proc\_name>}\_sftrace.dat}.'))
call var_list%append_log (var_str ("?sf_allow_s_mapping"), .true., &
intrinsic=.true., &
description=var_str ('Flag that determines whether special mappings ' // &
'for processes with structure functions and $s$-channel resonances ' // &
'are applied, e.g. Drell-Yan at hadron colliders, or $Z$ production ' // &
'at linear colliders with beamstrahlung and ISR.'))
if (present (paths)) then
call var_list%append_string (var_str ("$lhapdf_dir"), paths%lhapdfdir, &
intrinsic=.true., &
description=var_str ('String variable that tells the path ' // &
'where the \lhapdf\ library and PDF sets can be found. When ' // &
'the library has been correctly recognized during configuration, ' // &
'this is automatically set by \whizard. (cf. also \ttt{lhapdf}, ' // &
'\ttt{\$lhapdf\_file}, \ttt{lhapdf\_photon}, \ttt{\$lhapdf\_photon\_file}, ' // &
'\ttt{lhapdf\_member}, \ttt{lhapdf\_photon\_scheme})'))
else
call var_list%append_string (var_str ("$lhapdf_dir"), var_str(""), &
intrinsic=.true., &
description=var_str ('String variable that tells the path ' // &
'where the \lhapdf\ library and PDF sets can be found. When ' // &
'the library has been correctly recognized during configuration, ' // &
'this is automatically set by \whizard. (cf. also \ttt{lhapdf}, ' // &
'\ttt{\$lhapdf\_file}, \ttt{lhapdf\_photon}, \ttt{\$lhapdf\_photon\_file}, ' // &
'\ttt{lhapdf\_member}, \ttt{lhapdf\_photon\_scheme})'))
end if
call var_list%append_string (var_str ("$lhapdf_file"), var_str (""), &
intrinsic=.true., &
description=var_str ('This string variable \ttt{\$lhapdf\_file ' // &
'= "{\em <pdf\_set>}"} allows to specify the PDF set \ttt{{\em ' // &
'<pdf\_set>}} from the external \lhapdf\ library. It must match ' // &
'the exact name of the PDF set from the \lhapdf\ library. The ' // &
'default is empty, and the default set from \lhapdf\ is taken. ' // &
'Only one argument is possible, the PDF set must be identical ' // &
'for both beams, unless there are fundamentally different beam ' // &
'particles like proton and photon. (cf. also \ttt{lhapdf}, \ttt{\$lhapdf\_dir}, ' // &
'\ttt{lhapdf\_photon}, \ttt{\$lhapdf\_photon\_file}, \ttt{lhapdf\_photon\_scheme}, ' // &
'\ttt{lhapdf\_member})'))
call var_list%append_string (var_str ("$lhapdf_photon_file"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable \ttt{\$lhapdf\_photon\_file ' // &
'= "{\em <pdf\_set>}"} analagous to ($\to$) \ttt{\$lhapdf\_file} ' // &
'for photon PDF structure functions from the external \lhapdf\ ' // &
'library. The name must exactly match the one of the set from ' // &
'\lhapdf. (cf. \ttt{beams}, \ttt{lhapdf}, \ttt{\$lhapdf\_dir}, ' // &
'\ttt{\$lhapdf\_file}, \ttt{\$lhapdf\_photon\_file}, \ttt{lhapdf\_member}, ' // &
'\ttt{lhapdf\_photon\_scheme})'))
call var_list%append_int (var_str ("lhapdf_member"), 0, &
intrinsic=.true., &
description=var_str ('Integer variable that specifies the number ' // &
'of the corresponding PDF set chosen via the command ($\to$) ' // &
'\ttt{\$lhapdf\_file} or ($\to$) \ttt{\$lhapdf\_photon\_file} ' // &
'from the external \lhapdf\ library. E.g. error PDF sets can ' // &
'be chosen by this. (cf. also \ttt{lhapdf}, \ttt{\$lhapdf\_dir}, ' // &
'\ttt{\$lhapdf\_file}, \ttt{lhapdf\_photon}, \ttt{\$lhapdf\_photon\_file}, ' // &
'\ttt{lhapdf\_photon\_scheme})'))
call var_list%append_int (var_str ("lhapdf_photon_scheme"), 0, &
intrinsic=.true., &
description=var_str ('Integer parameter that controls the different ' // &
'available schemes for photon PDFs inside the external \lhapdf\ ' // &
'library. For more details see the \lhapdf\ manual. (cf. also ' // &
'\ttt{lhapdf}, \ttt{\$lhapdf\_dir}, \ttt{\$lhapdf\_file}, \ttt{lhapdf\_photon}, ' // &
'\ttt{\$lhapdf\_photon\_file}, \ttt{lhapdf\_member})'))
call var_list%append_string (var_str ("$pdf_builtin_set"), var_str ("CTEQ6L"), &
intrinsic=.true., &
description=var_str ("For \whizard's internal PDF structure functions " // &
'for hadron colliders, this string variable allows to set the ' // &
'particular PDF set. (cf. also \ttt{pdf\_builtin}, \ttt{pdf\_builtin\_photon})'))
call var_list%append_log (var_str ("?hoppet_b_matching"), .false., &
intrinsic=.true., &
description=var_str ('Flag that switches on the matching between ' // &
'4- and 5-flavor schemes for hadron collider $b$-parton initiated ' // &
'processes. Works either with builtin PDFs or with the external ' // &
'\lhapdf\ interface. Needs the external \ttt{HOPPET} library ' // &
'to be linked. (cf. \ttt{beams}, \ttt{pdf\_builtin}, \ttt{lhapdf})'))
call var_list%append_real (var_str ("isr_alpha"), 0._default, &
intrinsic=.true., &
description=var_str ('For lepton collider initial-state QED ' // &
'radiation (ISR), this real parameter sets the value of $\alpha_{em}$ ' // &
'used in the structure function. If not set, it is taken from ' // &
'the parameter set of the physics model in use (cf. also \ttt{isr}, ' // &
'\ttt{isr\_q\_max}, \ttt{isr\_mass}, \ttt{isr\_order}, \ttt{?isr\_recoil}, ' // &
'\ttt{?isr\_keep\_energy})'))
call var_list%append_real (var_str ("isr_q_max"), 0._default, &
intrinsic=.true., &
description=var_str ('This real parameter allows to set the ' // &
'scale of the initial-state QED radiation (ISR) structure function. ' // &
'If not set, it is taken internally to be $\sqrt{s}$. (cf. ' // &
'also \ttt{isr}, \ttt{isr\_alpha}, \ttt{isr\_mass}, \ttt{isr\_order}, ' // &
'\ttt{?isr\_recoil}, \ttt{?isr\_keep\_energy})'))
call var_list%append_real (var_str ("isr_mass"), 0._default, &
intrinsic=.true., &
description=var_str ('This real parameter allows to set by hand ' // &
'the mass of the incoming particle for lepton collider initial-state ' // &
'QED radiation (ISR). If not set, the mass for the initial beam ' // &
'particle is taken from the model in use. (cf. also \ttt{isr}, ' // &
'\ttt{isr\_q\_max}, \ttt{isr\_alpha}, \ttt{isr\_order}, \ttt{?isr\_recoil}, ' // &
'\ttt{?isr\_keep\_energy})'))
call var_list%append_int (var_str ("isr_order"), 3, &
intrinsic=.true., &
description=var_str ('For lepton collider initial-state QED ' // &
'radiation (ISR), this integer parameter allows to set the order ' // &
'up to which hard-collinear radiation is taken into account. ' // &
'Default is the highest available, namely third order. (cf. ' // &
'also \ttt{isr}, \ttt{isr\_q\_max}, \ttt{isr\_mass}, \ttt{isr\_alpha}, ' // &
'\ttt{?isr\_recoil}, \ttt{?isr\_keep\_energy})'))
call var_list%append_log (var_str ("?isr_recoil"), .false., &
intrinsic=.true., &
description=var_str ('Flag to switch on recoil, i.e. a non-vanishing ' // &
'$p_T$-kick for the lepton collider initial-state QED radiation ' // &
'(ISR). (cf. also \ttt{isr}, \ttt{isr}, \ttt{isr\_alpha}, \ttt{isr\_mass}, ' // &
'\ttt{isr\_order}, \ttt{isr\_q\_max})'))
call var_list%append_log (var_str ("?isr_keep_energy"), .false., &
intrinsic=.true., &
description=var_str ('As the splitting kinematics for the ISR ' // &
'structure function violates Lorentz invariance when the recoil ' // &
'is switched on, this flag forces energy conservation when set ' // &
'to true, otherwise violating energy conservation. (cf. also ' // &
'\ttt{isr}, \ttt{isr\_q\_max}, \ttt{isr\_mass}, \ttt{isr\_order}, ' // &
'\ttt{?isr\_recoil}, \ttt{?isr\_alpha})'))
call var_list%append_log (var_str ("?isr_handler"), .false., &
intrinsic=.true., &
description=var_str ('Activate ISR ' // &
'handler for event generation (no effect on integration). ' // &
'Requires \ttt{isr\_recoil = false}'))
call var_list%append_string (var_str ("$isr_handler_mode"), &
var_str ("trivial"), &
intrinsic=.true., &
description=var_str ('Operation mode for the ISR ' // &
'event handler. Allowed values: \ttt{trivial} (no effect), ' // &
'\ttt{recoil} (recoil kinematics with two photons)'))
call var_list%append_real (var_str ("epa_alpha"), 0._default, &
intrinsic=.true., &
description=var_str ('For the equivalent photon approximation ' // &
'(EPA), this real parameter sets the value of $\alpha_{em}$ ' // &
'used in the structure function. If not set, it is taken from ' // &
'the parameter set of the physics model in use (cf. also \ttt{epa}, ' // &
'\ttt{epa\_x\_min}, \ttt{epa\_mass}, \ttt{epa\_e\_max}, \ttt{epa\_q\_min}, ' // &
'\ttt{?epa\_recoil}, \ttt{?epa\_keep\_energy})'))
call var_list%append_real (var_str ("epa_x_min"), 0._default, &
intrinsic=.true., &
description=var_str ('Real parameter that sets the lower cutoff ' // &
'for the energy fraction in the splitting for the equivalent-photon ' // &
'approximation (EPA). This parameter has to be set by the user ' // &
'to a non-zero value smaller than one. (cf. also \ttt{epa}, ' // &
'\ttt{epa\_e\_max}, \ttt{epa\_mass}, \ttt{epa\_alpha}, \ttt{epa\_q\_min}, ' // &
'\ttt{?epa\_recoil}, \ttt{?epa\_keep\_energy})'))
call var_list%append_real (var_str ("epa_q_min"), 0._default, &
intrinsic=.true., &
description=var_str ('In the equivalent-photon approximation ' // &
'(EPA), this real parameters sets the minimal value for the ' // &
'transferred momentum. Either this parameter or the mass of ' // &
'the beam particle has to be non-zero. (cf. also \ttt{epa}, ' // &
'\ttt{epa\_x\_min}, \ttt{epa\_mass}, \ttt{epa\_alpha}, \ttt{epa\_q\_max}, ' // &
'\ttt{?epa\_recoil}, \ttt{?epa\_keep\_energy})'))
call var_list%append_real (var_str ("epa_q_max"), 0._default, &
intrinsic=.true., &
description=var_str ('This real parameter allows to set the ' // &
'upper energy cutoff for the equivalent-photon approximation ' // &
'(EPA). If not set, \whizard\ simply takes the collider energy, ' // &
'$\sqrt{s}$. (cf. also \ttt{epa}, \ttt{epa\_x\_min}, \ttt{epa\_mass}, ' // &
'\ttt{epa\_alpha}, \ttt{epa\_q\_min}, \ttt{?epa\_recoil}, \ttt{?epa\_keep\_energy})'))
call var_list%append_real (var_str ("epa_mass"), 0._default, &
intrinsic=.true., &
description=var_str ('This real parameter allows to set by hand ' // &
'the mass of the incoming particle for the equivalent-photon ' // &
'approximation (EPA). If not set, the mass for the initial beam ' // &
'particle is taken from the model in use. (cf. also \ttt{epa}, ' // &
'\ttt{epa\_x\_min}, \ttt{epa\_e\_max}, \ttt{epa\_alpha}, \ttt{epa\_q\_min}, ' // &
'\ttt{?epa\_recoil}, \ttt{?epa\_keep\_energy})'))
call var_list%append_log (var_str ("?epa_recoil"), .false., &
intrinsic=.true., &
description=var_str ('Flag to switch on recoil, i.e. a non-vanishing ' // &
'$p_T$-kick for the equivalent-photon approximation (EPA). ' // &
'(cf. also \ttt{epa}, \ttt{epa\_x\_min}, \ttt{epa\_mass}, \ttt{epa\_alpha}, ' // &
'\ttt{epa\_e\_max}, \ttt{epa\_q\_min}, \ttt{?epa\_keep\_energy})'))
call var_list%append_log (var_str ("?epa_keep_energy"), .false., &
intrinsic=.true., &
description=var_str ('As the splitting kinematics for the EPA ' // &
'structure function violates Lorentz invariance when the recoil ' // &
'is switched on, this flag forces energy conservation when set ' // &
'to true, otherwise violating energy conservation. (cf. also ' // &
'\ttt{epa}, \ttt{epa\_x\_min}, \ttt{epa\_mass}, \ttt{epa\_alpha}, ' // &
'\ttt{epa\_q\_min}, \ttt{?epa\_recoil})'))
call var_list%append_log (var_str ("?epa_handler"), .false., &
intrinsic=.true., &
description=var_str ('Activate EPA ' // &
'handler for event generation (no effect on integration). ' // &
'Requires \ttt{epa\_recoil = false}'))
call var_list%append_string (var_str ("$epa_handler_mode"), &
var_str ("trivial"), &
intrinsic=.true., &
description=var_str ('Operation mode for the EPA ' // &
'event handler. Allowed values: \ttt{trivial} (no effect), ' // &
'\ttt{recoil} (recoil kinematics with two beams)'))
call var_list%append_real (var_str ("ewa_x_min"), 0._default, &
intrinsic=.true., &
description=var_str ('Real parameter that sets the lower cutoff ' // &
'for the energy fraction in the splitting for the equivalent ' // &
'$W$ approximation (EWA). This parameter has to be set by the ' // &
'user to a non-zero value smaller than one. (cf. also \ttt{ewa}, ' // &
'\ttt{ewa\_pt\_max}, \ttt{ewa\_mass}, \ttt{?ewa\_keep\_energy}, ' // &
'\ttt{?ewa\_recoil})'))
call var_list%append_real (var_str ("ewa_pt_max"), 0._default, &
intrinsic=.true., &
description=var_str ('This real parameter allows to set the ' // &
'upper $p_T$ cutoff for the equivalent $W$ approximation (EWA). ' // &
'If not set, \whizard\ simply takes the collider energy, $\sqrt{s}$. ' // &
'(cf. also \ttt{ewa}, \ttt{ewa\_x\_min}, \ttt{ewa\_mass}, \ttt{?ewa\_keep\_energy}, ' // &
'\ttt{?ewa\_recoil})'))
call var_list%append_real (var_str ("ewa_mass"), 0._default, &
intrinsic=.true., &
description=var_str ('This real parameter allows to set by hand ' // &
'the mass of the incoming particle for the equivalent $W$ approximation ' // &
'(EWA). If not set, the mass for the initial beam particle is ' // &
'taken from the model in use. (cf. also \ttt{ewa}, \ttt{ewa\_x\_min}, ' // &
'\ttt{ewa\_pt\_max}, \ttt{?ewa\_keep\_energy}, \ttt{?ewa\_recoil})'))
call var_list%append_log (var_str ("?ewa_recoil"), .false., &
intrinsic=.true., &
description=var_str ('For the equivalent $W$ approximation (EWA), ' // &
'this flag switches on recoil, i.e. non-collinear splitting. ' // &
'(cf. also \ttt{ewa}, \ttt{ewa\_x\_min}, \ttt{ewa\_pt\_max}, ' // &
'\ttt{ewa\_mass}, \ttt{?ewa\_keep\_energy})'))
call var_list%append_log (var_str ("?ewa_keep_energy"), .false., &
intrinsic=.true., &
description=var_str ('As the splitting kinematics for the equivalent ' // &
'$W$ approximation (EWA) violates Lorentz invariance when the ' // &
'recoil is switched on, this flag forces energy conservation ' // &
'when set to true, otherwise violating energy conservation. ' // &
'(cf. also \ttt{ewa}, \ttt{ewa\_x\_min}, \ttt{ewa\_pt\_max}, ' // &
'\ttt{ewa\_mass}, \ttt{?ewa\_recoil})'))
call var_list%append_log (var_str ("?circe1_photon1"), .false., &
intrinsic=.true., &
description=var_str ('Flag to tell \whizard\ to use the photon ' // &
'of the \circeone\ beamstrahlung structure function as initiator ' // &
'for the hard scattering process in the first beam. (cf. also ' // &
'\ttt{circe1}, \ttt{?circe1\_photon2}, \ttt{circe1\_sqrts}, ' // &
'\ttt{?circe1\_generate}, \ttt{?circe1\_map}, \ttt{circe1\_eps}, ' // &
'\newline \ttt{circe1\_mapping\_slope}, \ttt{circe1\_ver}, ' // &
'\ttt{circe1\_rev}, \ttt{\$circe1\_acc}, \ttt{circe1\_chat}, \newline' // &
'\ttt{?circe1\_with\_radiation})'))
call var_list%append_log (var_str ("?circe1_photon2"), .false., &
intrinsic=.true., &
description=var_str ('Flag to tell \whizard\ to use the photon ' // &
'of the \circeone\ beamstrahlung structure function as initiator ' // &
'for the hard scattering process in the second beam. (cf. also ' // &
'\ttt{circe1}, \ttt{?circe1\_photon1}, \ttt{circe1\_sqrts}, ' // &
'\ttt{?circe1\_generate}, \ttt{?circe1\_map}, \ttt{circe1\_eps}, ' // &
'\newline \ttt{circe1\_mapping\_slope}, \ttt{circe1\_ver}, ' // &
'\ttt{circe1\_rev}, \ttt{\$circe1\_acc}, \ttt{circe1\_chat}, ' // &
'\newline\ttt{?circe1\_with\_radiation})'))
call var_list%append_real (var_str ("circe1_sqrts"), &
intrinsic=.true., &
description=var_str ('Real parameter that allows to set the ' // &
'value of the collider energy for the lepton collider beamstrahlung ' // &
'structure function \circeone. If not set, $\sqrt{s}$ is taken. ' // &
'(cf. also \ttt{circe1}, \ttt{?circe1\_photon1}, \ttt{?circe1\_photon2}, ' // &
'\ttt{?circe1\_generate}, \ttt{?circe1\_map}, \ttt{circe1\_eps}, ' // &
'\newline \ttt{circe1\_mapping\_slope}, \ttt{circe1\_ver}, ' // &
'\ttt{circe1\_rev}, \ttt{\$circe1\_acc}, \ttt{circe1\_chat}, \newline' // &
'\ttt{?circe1\_with\_radiation})'))
call var_list%append_log (var_str ("?circe1_generate"), .true., &
intrinsic=.true., &
description=var_str ('Flag that determines whether the \circeone\ ' // &
'structure function for lepton collider beamstrahlung uses the ' // &
'generator mode for the spectrum, or a pre-defined (semi-)analytical ' // &
'parameterization. Default is the generator mode. (cf. also ' // &
'\ttt{circe1}, \ttt{?circe1\_photon1}, \newline \ttt{?circe1\_photon2}, ' // &
'\ttt{circe1\_sqrts}, \ttt{?circe1\_map}, \ttt{circe1\_mapping\_slope}, ' // &
'\ttt{circe1\_eps}, \newline \ttt{circe1\_ver}, \ttt{circe1\_rev}, ' // &
'\ttt{\$circe1\_acc}, \ttt{circe1\_chat}, \ttt{?circe1\_with\_radiation})'))
call var_list%append_log (var_str ("?circe1_map"), .true., &
intrinsic=.true., &
description=var_str ('Flag that determines whether the \circeone\ ' // &
'structure function for lepton collider beamstrahlung uses special ' // &
'mappings for $s$-channel resonances. (cf. also \ttt{circe1}, ' // &
'\ttt{?circe1\_photon1}, \newline \ttt{?circe1\_photon2}, ' // &
'\ttt{circe1\_sqrts}, \ttt{?circe1\_generate}, ' // &
'\ttt{circe1\_mapping\_slope}, \ttt{circe1\_eps}, \newline ' // &
'\ttt{circe1\_ver}, \ttt{circe1\_rev}, \ttt{\$circe1\_acc}, ' // &
'\ttt{circe1\_chat}, \ttt{?circe1\_with\_radiation})'))
call var_list%append_real (var_str ("circe1_mapping_slope"), 2._default, &
intrinsic=.true., &
description=var_str ('Real parameter that allows to vary the ' // &
'slope of the mapping function for the \circeone\ structure ' // &
'function for lepton collider beamstrahlung from the default ' // &
'value \ttt{2.}. (cf. also \ttt{circe1}, \ttt{?circe1\_photon1}, ' // &
'\ttt{?circe1\_photon2}, \ttt{circe1\_sqrts}, \ttt{?circe1\_generate}, ' // &
'\ttt{?circe1\_map}, \ttt{circe1\_eps}, \ttt{circe1\_ver}, ' // &
'\ttt{circe1\_rev}, \ttt{\$circe1\_acc}, \ttt{circe1\_chat}, \newline' // &
'\ttt{?circe1\_with\_radiation})'))
call var_list%append_real (var_str ("circe1_eps"), 1e-5_default, &
intrinsic=.true., &
description=var_str ('Real parameter, that takes care of the ' // &
'mapping of the peak in the lepton collider beamstrahlung structure ' // &
'function spectrum of \circeone. (cf. also \ttt{circe1}, \ttt{?circe1\_photons}, ' // &
'\ttt{?circe1\_photon2}, \ttt{circe1\_sqrts}, \ttt{?circe1\_generate}, ' // &
'\ttt{?circe1\_map}, \ttt{circe1\_eps}, \newline ' // &
'\ttt{circe1\_mapping\_slope}, \ttt{circe1\_ver}, \ttt{circe1\_rev}, ' // &
'\ttt{\$circe1\_acc}, \ttt{circe1\_chat}, \newline\ttt{?circe1\_with\_radiation})'))
call var_list%append_int (var_str ("circe1_ver"), 0, intrinsic=.true., &
description=var_str ('Integer parameter that sets the internal ' // &
'versioning number of the \circeone\ structure function for lepton-collider ' // &
'beamstrahlung. It has to be set by the user explicitly, it takes ' // &
'values from one to ten. (cf. also \ttt{circe1}, \ttt{?circe1\_photon1}, ' // &
'\ttt{?circe1\_photon2}, \ttt{?circe1\_generate}, \ttt{?circe1\_map}, ' // &
'\ttt{circe1\_eps}, \ttt{circe1\_mapping\_slope}, \ttt{circe1\_sqrts}, ' // &
'\ttt{circe1\_rev}, \ttt{\$circe1\_acc}, \ttt{circe1\_chat}, ' // &
'\ttt{?circe1\_with\_radiation})'))
call var_list%append_int (var_str ("circe1_rev"), 0, intrinsic=.true., &
description=var_str ('Integer parameter that sets the internal ' // &
'revision number of the \circeone\ structure function for lepton-collider ' // &
'beamstrahlung. The default \ttt{0} translates always into the ' // &
'most recent version; older versions have to be accessed through ' // &
'the explicit revision date. For more details cf.~the \circeone ' // &
'manual. (cf. also \ttt{circe1}, \ttt{?circe1\_photon1}, \ttt{?circe1\_photon2}, ' // &
'\ttt{?circe1\_generate}, \ttt{?circe1\_map}, \ttt{circe1\_eps}, ' // &
'\ttt{circe1\_mapping\_slope}, \ttt{circe1\_sqrts}, \ttt{circe1\_ver}, ' // &
'\ttt{\$circe1\_acc}, \ttt{circe1\_chat}, \ttt{?circe1\_with\_radiation})'))
call var_list%append_string (var_str ("$circe1_acc"), var_str ("SBAND"), &
intrinsic=.true., &
description=var_str ('String variable that specifies the accelerator ' // &
'type for the \circeone\ structure function for lepton-collider ' // &
'beamstrahlung. (\ttt{?circe1\_photons}, \ttt{?circe1\_photon2}, ' // &
'\ttt{circe1\_sqrts}, \ttt{?circe1\_generate}, \ttt{?circe1\_map}, ' // &
'\ttt{circe1\_eps}, \ttt{circe1\_mapping\_slope}, \ttt{circe1\_ver}, ' // &
'\newline \ttt{circe1\_rev}, \ttt{circe1\_chat}, \ttt{?circe1\_with\_radiation})'))
call var_list%append_int (var_str ("circe1_chat"), 0, intrinsic=.true., &
description=var_str ('Chattiness of the \circeone\ structure ' // &
'function for lepton-collider beamstrahlung. The higher the integer ' // &
'value, the more information will be given out by the \circeone\ ' // &
'package. (\ttt{?circe1\_photons}, \ttt{?circe1\_photon2}, ' // &
'\ttt{circe1\_sqrts}, \ttt{?circe1\_generate}, \ttt{?circe1\_map}, ' // &
'\ttt{circe1\_eps}, \ttt{circe1\_mapping\_slope}, \ttt{circe1\_ver}, ' // &
'\newline \ttt{circe1\_rev}, \ttt{\$circe1\_acc}, \ttt{?circe1\_with\_radiation})'))
call var_list%append_log (var_str ("?circe1_with_radiation"), .false., &
intrinsic=.true., &
description=var_str ('This logical decides whether the additional photon ' // &
'or electron ("beam remnant") will be considered in the event record or ' // &
'not. (\ttt{?circe1\_photons}, \ttt{?circe1\_photon2}, ' // &
'\ttt{circe1\_sqrts}, \ttt{?circe1\_generate}, \ttt{?circe1\_map}, ' // &
'\ttt{circe1\_eps}, \ttt{circe1\_mapping\_slope}, \ttt{circe1\_ver}, ' // &
'\newline \ttt{circe1\_rev}, \ttt{\$circe1\_acc})'))
call var_list%append_log (var_str ("?circe2_polarized"), .true., &
intrinsic=.true., &
description=var_str ('Flag whether the photon spectra from the ' // &
'\circetwo\ structure function for lepton colliders should be ' // &
'treated polarized. (cf. also \ttt{circe2}, \ttt{\$circe2\_file}, ' // &
'\ttt{\$circe2\_design})'))
call var_list%append_string (var_str ("$circe2_file"), &
intrinsic=.true., &
description=var_str ('String variable by which the corresponding ' // &
'photon collider spectrum for the \circetwo\ structure function ' // &
'can be selected. (cf. also \ttt{circe2}, \ttt{?circe2\_polarized}, ' // &
'\ttt{\$circe2\_design})'))
call var_list%append_string (var_str ("$circe2_design"), var_str ("*"), &
intrinsic=.true., &
description=var_str ('String variable that sets the collider ' // &
'design for the \circetwo\ structure function for photon collider ' // &
'spectra. (cf. also \ttt{circe2}, \ttt{\$circe2\_file}, \ttt{?circe2\_polarized})'))
call var_list%append_real (var_str ("gaussian_spread1"), 0._default, &
intrinsic=.true., &
description=var_str ('Parameter that sets the energy spread ' // &
'($\sigma$ value) of the first beam for a Gaussian spectrum. ' // &
'(cf. \ttt{gaussian})'))
call var_list%append_real (var_str ("gaussian_spread2"), 0._default, &
intrinsic=.true., &
description=var_str ('Ditto, for the second beam.'))
call var_list%append_string (var_str ("$beam_events_file"), &
intrinsic=.true., &
description=var_str ('String variable that allows to set the ' // &
"name of the external file from which a beamstrahlung's spectrum " // &
'for lepton colliders as pairs of energy fractions is read in. ' // &
'(cf. also \ttt{beam\_events}, \ttt{?beam\_events\_warn\_eof})'))
call var_list%append_log (var_str ("?beam_events_warn_eof"), .true., &
intrinsic=.true., &
description=var_str ('Flag that tells \whizard\ to ' // &
'issue a warning when in a simulation the end of an external ' // &
"file for beamstrahlung's spectra for lepton colliders are reached, " // &
'and energy fractions from the beginning of the file are reused. ' // &
'(cf. also \ttt{beam\_events}, \ttt{\$beam\_events\_file})'))
call var_list%append_log (var_str ("?energy_scan_normalize"), .false., &
intrinsic=.true., &
description=var_str ('Normalization flag for the energy scan ' // &
'structure function: if set the total cross section is normalized ' // &
'to unity. (cf. also \ttt{energy\_scan})'))
end subroutine var_list_set_beams_defaults
@ %def var_list_set_beams_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_core_defaults => var_list_set_core_defaults
<<Variables: procedures>>=
subroutine var_list_set_core_defaults (var_list, seed)
class(var_list_t), intent(inout) :: var_list
integer, intent(in) :: seed
logical, target, save :: known = .true. !!! ??????
real(default), parameter :: real_specimen = 1.
call var_list_append_log_ptr &
(var_list, var_str ("?logging"), logging, known, &
intrinsic=.true., &
description=var_str ('This logical -- when set to \ttt{false} ' // &
'-- suppresses writing out a logfile (default: \ttt{whizard.log}) ' // &
'for the whole \whizard\ run, or when \whizard\ is run with the ' // &
'\ttt{--no-logging} option, to suppress parts of the logging ' // &
'when setting it to \ttt{true} again at a later part of the ' // &
'\sindarin\ input file. Mainly for debugging purposes. ' // &
'(cf. also \ttt{?openmp\_logging}, \ttt{?mpi\_logging})'))
call var_list%append_string (var_str ("$job_id"), &
intrinsic=.true., &
description=var_str ('Arbitrary string that can be used for ' // &
'creating unique names. The variable is initialized with the ' // &
'value of the \ttt{job\_id} option on startup. (cf. also ' // &
'\ttt{\$compile\_workspace}, \ttt{\$run\_id})'))
call var_list%append_string (var_str ("$compile_workspace"), &
intrinsic=.true., &
description=var_str ('If set, create process source code ' // &
'and process-driver library code in a subdirectory with this ' // &
'name. If non-existent, the directory will be created. (cf. ' // &
'also \ttt{\$job\_id}, \ttt{\$run\_id}, \ttt{\$integrate\_workspace})'))
call var_list%append_int (var_str ("seed"), seed, &
intrinsic=.true., &
description=var_str ('Integer variable \ttt{seed = {\em <num>}} ' // &
'that allows to set a specific random seed \ttt{num}. If not ' // &
'set, \whizard\ takes the time from the system clock to determine ' // &
'the random seed.'))
call var_list%append_string (var_str ("$model_name"), &
intrinsic=.true., &
description=var_str ('This variable makes the locally used physics ' // &
'model available as a string, e.g. as \ttt{show (\$model\_name)}. ' // &
'However, the user is not able to change the current model by ' // &
'setting this variable to a different string. (cf. also \ttt{model}, ' // &
'\ttt{\$library\_name}, \ttt{printf}, \ttt{show})'))
call var_list%append_int (var_str ("process_num_id"), &
intrinsic=.true., &
description=var_str ('Using the integer \ttt{process\_num\_id ' // &
'= {\em <int\_var>}} one can set a numerical identifier for processes ' // &
'within a process library. This can be set either just before ' // &
'the corresponding \ttt{process} definition or as an optional ' // &
'local argument of the latter. (cf. also \ttt{process})'))
call var_list%append_string (var_str ("$method"), var_str ("omega"), &
intrinsic=.true., &
description=var_str ('This string variable specifies the method ' // &
'for the matrix elements to be used in the evaluation. The default ' // &
"is the intrinsic \oMega\ matrix element generator " // &
'(\ttt{"omega"}), other options are: \ttt{"ovm"}, \ttt{"unit\_test"}, ' // &
'\ttt{"template\_unity"}, \ttt{"threshold"}. For processes defined ' // &
'\ttt{"template"}, with \ttt{nlo\_calculation = ...}, please refer to ' // &
'\ttt{\$born\_me\_method}, \ttt{\$real\_tree\_me\_method}, ' // &
'\ttt{\$loop\_me\_method} and \ttt{\$correlation\_me\_method}.'))
call var_list%append_log (var_str ("?report_progress"), .true., &
intrinsic=.true., &
description=var_str ('Flag for the \oMega\ matrix element generator ' // &
'whether to print out status messages about progress during ' // &
'matrix element generation. (cf. also \ttt{\$method}, \ttt{\$omega\_flags})'))
call var_list%append_log (var_str ("?me_verbose"), .false., &
description=var_str ("Flag determining whether " // &
"the makefile command for generating and compiling the \oMega\ matrix " // &
"element code is silent or verbose. Default is silent."))
call var_list%append_string (var_str ("$restrictions"), var_str (""), &
intrinsic=.true., &
description=var_str ('This is an optional argument for process ' // &
'definitions for the matrix element method \ttt{"omega"}. Using ' // &
'the following construction, it defines a string variable, \ttt{process ' // &
'\newline {\em <process\_name>} = {\em <particle1>}, {\em <particle2>} ' // &
'=> {\em <particle3>}, {\em <particle4>}, ... \{ \$restrictions ' // &
'= "{\em <restriction\_def>}" \}}. The string argument \ttt{{\em ' // &
'<restriction\_def>}} is directly transferred during the code ' // &
'generation to the ME generator \oMega. It has to be of the form ' // &
'\ttt{n1 + n2 + ... \url{~} {\em <particle (list)>}}, where ' // &
'\ttt{n1} and so on are the numbers of the particles above in ' // &
'the process definition. The tilde specifies a certain intermediate ' // &
'state to be equal to the particle(s) in \ttt{particle (list)}. ' // &
'An example is \ttt{process eemm\_z = e1, E1 => e2, E2 ' // &
'\{ \$restrictions = "1+2 \url{~} Z" \} } restricts the code ' // &
'to be generated for the process $e^- e^+ \to \mu^- \mu^+$ to ' // &
'the $s$-channel $Z$-boson exchange. For more details see Sec.~\ref{sec:omega_me} ' // &
'(cf. also \ttt{process})'))
call var_list%append_log (var_str ("?omega_write_phs_output"), .false., &
intrinsic=.true., &
description=var_str ('This flag decides whether a the phase-space ' // &
'output is produced by the \oMega\ matrix element generator. This ' // &
'output is written to file(s) and contains the Feynman diagrams ' // &
'which belong to the process(es) under consideration. The file is ' // &
'mandatory whenever the variable \ttt{\$phs\_method} has the value ' // &
'\ttt{fast\_wood}, i.e. if the phase-space file is provided by ' // &
'cascades2.'))
call var_list%append_string (var_str ("$omega_flags"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable that allows to pass flags ' // &
'to the \oMega\ matrix element generator. Normally, \whizard\ ' // &
'takes care of all flags automatically. Note that for restrictions ' // &
'of intermediate states, there is a special string variable: ' // &
'(cf. $\to$) \ttt{\$restrictions}.'))
call var_list%append_log (var_str ("?read_color_factors"), .true., &
intrinsic=.true., &
description=var_str ('This flag decides whether to read QCD ' // &
'color factors from the matrix element provided by each method, ' // &
'or to try and calculate the color factors in \whizard\ internally.'))
!!! JRR: WK please check (#529)
! call var_list_append_string &
! (var_list, var_str ("$user_procs_cut"), var_str (""), &
! intrinsic=.true.)
! call var_list_append_string &
! (var_list, var_str ("$user_procs_event_shape"), var_str (""), &
! intrinsic=.true.)
! call var_list_append_string &
! (var_list, var_str ("$user_procs_obs1"), var_str (""), &
! intrinsic=.true.)
! call var_list_append_string &
! (var_list, var_str ("$user_procs_obs2"), var_str (""), &
! intrinsic=.true.)
! call var_list_append_string &
! (var_list, var_str ("$user_procs_sf"), var_str (""), &
! intrinsic=.true.)
call var_list%append_log (var_str ("?slha_read_input"), .true., &
intrinsic=.true., &
description=var_str ('Flag which decides whether \whizard\ reads ' // &
'in the SM and parameter information from the \ttt{SMINPUTS} ' // &
'and \ttt{MINPAR} common blocks of the SUSY Les Houches Accord ' // &
'files. (cf. also \ttt{read\_slha}, \ttt{write\_slha}, \ttt{?slha\_read\_spectrum}, ' // &
'\ttt{?slha\_read\_decays})'))
call var_list%append_log (var_str ("?slha_read_spectrum"), .true., &
intrinsic=.true., &
description=var_str ('Flag which decides whether \whizard\ reads ' // &
'in the whole spectrum and mixing angle information from the ' // &
'common blocks of the SUSY Les Houches Accord files. (cf. also ' // &
'\ttt{read\_slha}, \ttt{write\_slha}, \ttt{?slha\_read\_decays}, ' // &
'\ttt{?slha\_read\_input})'))
call var_list%append_log (var_str ("?slha_read_decays"), .false., &
intrinsic=.true., &
description=var_str ('Flag which decides whether \whizard\ reads ' // &
'in the widths and branching ratios from the \ttt{DCINFO} common ' // &
'block of the SUSY Les Houches Accord files. (cf. also \ttt{read\_slha}, ' // &
'\ttt{write\_slha}, \ttt{?slha\_read\_spectrum}, \ttt{?slha\_read\_input})'))
call var_list%append_string (var_str ("$library_name"), &
intrinsic=.true., &
description=var_str ('Similar to \ttt{\$model\_name}, this string ' // &
'variable is used solely to access the name of the active process ' // &
'library, e.g. in \ttt{printf} statements. (cf. \ttt{compile}, ' // &
'\ttt{library}, \ttt{printf}, \ttt{show}, \ttt{\$model\_name})'))
call var_list%append_log (var_str ("?alphas_is_fixed"), .true., &
intrinsic=.true., &
description=var_str ('Flag that tells \whizard\ to use a non-running ' // &
'$\alpha_s$. Note that this has to be set explicitly to $\ttt{false}$ ' // &
'if the user wants to use one of the running $\alpha_s$ options. ' // &
'(cf. also \ttt{alphas\_order}, \ttt{?alphas\_from\_lhapdf}, ' // &
'\ttt{?alphas\_from\_pdf\_builtin}, \ttt{alphas\_nf}, \ttt{?alphas\_from\_mz}, ' // &
'\newline \ttt{?alphas\_from\_lambda\_qcd}, \ttt{lambda\_qcd})'))
call var_list%append_log (var_str ("?alphas_from_lhapdf"), .false., &
intrinsic=.true., &
description=var_str ('Flag that tells \whizard\ to use a running ' // &
'$\alpha_s$ from the \lhapdf\ library (which has to be correctly ' // &
'linked). Note that \ttt{?alphas\_is\_fixed} has to be set ' // &
'explicitly to $\ttt{false}$. (cf. also \ttt{alphas\_order}, ' // &
'\ttt{?alphas\_is\_fixed}, \ttt{?alphas\_from\_pdf\_builtin}, ' // &
'\ttt{alphas\_nf}, \ttt{?alphas\_from\_mz}, \ttt{?alphas\_from\_lambda\_qcd}, ' // &
'\ttt{lambda\_qcd})'))
call var_list%append_log (var_str ("?alphas_from_pdf_builtin"), .false., &
intrinsic=.true., &
description=var_str ('Flag that tells \whizard\ to use a running ' // &
'$\alpha_s$ from the internal PDFs. Note that in that case \ttt{?alphas\_is\_fixed} ' // &
'has to be set explicitly to $\ttt{false}$. (cf. also ' // &
'\ttt{alphas\_order}, \ttt{?alphas\_is\_fixed}, \ttt{?alphas\_from\_lhapdf}, ' // &
'\ttt{alphas\_nf}, \ttt{?alphas\_from\_mz}, \newline \ttt{?alphas\_from\_lambda\_qcd}, ' // &
'\ttt{lambda\_qcd})'))
call var_list%append_int (var_str ("alphas_order"), 0, &
intrinsic=.true., &
description=var_str ('Integer parameter that sets the order ' // &
'of the internal evolution for running $\alpha_s$ in \whizard: ' // &
'the default, \ttt{0}, is LO running, \ttt{1} is NLO, \ttt{2} ' // &
'is NNLO. (cf. also \ttt{alphas\_is\_fixed}, \ttt{?alphas\_from\_lhapdf}, ' // &
'\ttt{?alphas\_from\_pdf\_builtin}, \ttt{alphas\_nf}, \ttt{?alphas\_from\_mz}, ' // &
'\newline \ttt{?alphas\_from\_lambda\_qcd}, \ttt{lambda\_qcd})'))
call var_list%append_int (var_str ("alphas_nf"), 5, &
intrinsic=.true., &
description=var_str ('Integer parameter that sets the number ' // &
'of active quark flavors for the internal evolution for running ' // &
'$\alpha_s$ in \whizard: the default is \ttt{5}. (cf. also ' // &
'\ttt{alphas\_is\_fixed}, \ttt{?alphas\_from\_lhapdf}, \ttt{?alphas\_from\_pdf\_builtin}, ' // &
'\ttt{alphas\_order}, \ttt{?alphas\_from\_mz}, \newline ' // &
'\ttt{?alphas\_from\_lambda\_qcd}, \ttt{lambda\_qcd})'))
call var_list%append_log (var_str ("?alphas_from_mz"), .false., &
intrinsic=.true., &
description=var_str ('Flag that tells \whizard\ to use its internal ' // &
'running $\alpha_s$ from $\alpha_s(M_Z)$. Note that in that ' // &
'case \ttt{?alphas\_is\_fixed} has to be set explicitly to ' // &
'$\ttt{false}$. (cf. also \ttt{alphas\_order}, \ttt{?alphas\_is\_fixed}, ' // &
'\ttt{?alphas\_from\_lhapdf}, \ttt{alphas\_nf}, \ttt{?alphas\_from\_pdf\_builtin}, ' // &
'\newline \ttt{?alphas\_from\_lambda\_qcd}, \ttt{lambda\_qcd})'))
call var_list%append_log (var_str ("?alphas_from_lambda_qcd"), .false., &
intrinsic=.true., &
description=var_str ('Flag that tells \whizard\ to use its internal ' // &
'running $\alpha_s$ from $\alpha_s(\Lambda_{QCD})$. Note that ' // &
'in that case \ttt{?alphas\_is\_fixed} has to be set explicitly ' // &
'to $\ttt{false}$. (cf. also \ttt{alphas\_order}, \ttt{?alphas\_is\_fixed}, ' // &
'\ttt{?alphas\_from\_lhapdf}, \ttt{alphas\_nf}, \ttt{?alphas\_from\_pdf\_builtin}, ' // &
'\newline \ttt{?alphas\_from\_mz}, \ttt{lambda\_qcd})'))
call var_list%append_real (var_str ("lambda_qcd"), 200.e-3_default, &
intrinsic=.true., &
description=var_str ('Real parameter that sets the value for ' // &
'$\Lambda_{QCD}$ used in the internal evolution for running ' // &
'$\alpha_s$ in \whizard. (cf. also \ttt{alphas\_is\_fixed}, ' // &
'\ttt{?alphas\_from\_lhapdf}, \ttt{alphas\_nf}, ' // &
'\newline \ttt{?alphas\_from\_pdf\_builtin}, ' // &
'\ttt{?alphas\_from\_mz}, \ttt{?alphas\_from\_lambda\_qcd}, ' // &
'\ttt{alphas\_order})'))
call var_list%append_log (var_str ("?fatal_beam_decay"), .true., &
intrinsic=.true., &
description=var_str ('Logical variable that let the user decide ' // &
'whether the possibility of a beam decay is treated as a fatal ' // &
'error or only as a warning. An example is a process $b t \to ' // &
'X$, where the bottom quark as an inital state particle appears ' // &
'as a possible decay product of the second incoming particle, ' // &
'the top quark. This might trigger inconsistencies or instabilities ' // &
'in the phase space set-up.'))
call var_list%append_log (var_str ("?helicity_selection_active"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether \whizard\ uses ' // &
'a numerical selection rule for vanishing helicities: if active, ' // &
'then, if a certain helicity combination yields an absolute ' // &
'(\oMega) matrix element smaller than a certain threshold ($\to$ ' // &
'\ttt{helicity\_selection\_threshold}) more often than a certain ' // &
'cutoff ($\to$ \ttt{helicity\_selection\_cutoff}), it will be dropped.'))
call var_list%append_real (var_str ("helicity_selection_threshold"), &
1E10_default, &
intrinsic=.true., &
description=var_str ('Real parameter that gives the threshold ' // &
'for the absolute value of a certain helicity combination of ' // &
'an (\oMega) amplitude. If a certain number ($\to$ ' // &
'\ttt{helicity\_selection\_cutoff}) of calls stays below this ' // &
'threshold, that combination will be dropped from then on. (cf. ' // &
'also \ttt{?helicity\_selection\_active})'))
call var_list%append_int (var_str ("helicity_selection_cutoff"), 1000, &
intrinsic=.true., &
description=var_str ('Integer parameter that gives the number ' // &
"a certain helicity combination of an (\oMega) amplitude has " // &
'to be below a certain threshold ($\to$ \ttt{helicity\_selection\_threshold}) ' // &
'in order to be dropped from then on. (cf. also \ttt{?helicity\_selection\_active})'))
call var_list%append_string (var_str ("$rng_method"), var_str ("tao"), &
intrinsic=.true., &
description=var_str ('String variable that allows to set the ' // &
'method for the random number generation. Default is Donald ' // &
"Knuth' RNG method \ttt{TAO}."))
call var_list%append_log (var_str ("?vis_diags"), .false., &
intrinsic=.true., &
description=var_str ('Logical variable that allows to give out ' // &
"a Postscript or PDF file for the Feynman diagrams for a \oMega\ " // &
'process. (cf. \ttt{?vis\_diags\_color}).'))
call var_list%append_log (var_str ("?vis_diags_color"), .false., &
intrinsic=.true., &
description=var_str ('Same as \ttt{?vis\_diags}, but switches ' // &
'on color flow instead of Feynman diagram generation. (cf. \ttt{?vis\_diags}).'))
call var_list%append_log (var_str ("?check_event_file"), .true., &
intrinsic=.true., &
description=var_str ('Setting this to false turns off all sanity ' // &
'checks when reading a raw event file with previously generated ' // &
'events. Use this at your own risk; the program may return ' // &
'wrong results or crash if data do not match. (cf. also \ttt{?check\_grid\_file}, ' // &
'\ttt{?check\_phs\_file})'))
call var_list%append_string (var_str ("$event_file_version"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable that allows to set the ' // &
'format version of the \whizard\ internal binary event format.'))
call var_list%append_int (var_str ("n_events"), 0, &
intrinsic=.true., &
description=var_str ('This specifier \ttt{n\_events = {\em <num>}} ' // &
'sets the number of events for the event generation of the processes ' // &
'in the \sindarin\ input files. Note that WHIZARD itself chooses ' // &
'the number from the \ttt{n\_events} or from the \ttt{luminosity} ' // &
'specifier, whichever would give the larger number of events. ' // &
'As this depends on the cross section under consideration, it ' // &
'might be different for different processes in the process list. ' // &
'(cf. \ttt{luminosity}, \ttt{\$sample}, \ttt{sample\_format}, ' // &
'\ttt{?unweighted}, \ttt{event\_index\_offset})'))
call var_list%append_int (var_str ("event_index_offset"), 0, &
intrinsic=.true., &
description=var_str ('The value ' // &
'\ttt{event\_index\_offset = {\em <num>}} ' // &
'initializes the event counter for a subsequent ' // &
'event sample. By default (value 0), the first event ' // &
'gets index value 1, incrementing by one for each generated event ' // &
'within a sample. The event counter is initialized again ' // &
'for each new sample (i.e., \ttt{integrate} command). ' // &
'If events are read from file, and the ' // &
'event file format supports event numbering, the event numbers ' // &
'will be taken from file instead, and the value of ' // &
'\ttt{event\_index\_offset} has no effect. ' // &
'(cf. \ttt{luminosity}, \ttt{\$sample}, \ttt{sample\_format}, ' // &
'\ttt{?unweighted}, \ttt{n\_events})'))
call var_list%append_log (var_str ("?unweighted"), .true., &
intrinsic=.true., &
description=var_str ('Flag that distinguishes between unweighted ' // &
'and weighted event generation. (cf. also \ttt{simulate}, \ttt{n\_events}, ' // &
'\ttt{luminosity}, \ttt{event\_index\_offset})'))
call var_list%append_real (var_str ("safety_factor"), 1._default, &
intrinsic=.true., &
description=var_str ('This real variable \ttt{safety\_factor ' // &
'= {\em <num>}} reduces the acceptance probability for unweighting. ' // &
'If greater than one, excess events become less likely, but ' // &
'the reweighting efficiency also drops. (cf. \ttt{simulate}, \ttt{?unweighted})'))
call var_list%append_log (var_str ("?negative_weights"), .false., &
intrinsic=.true., &
description=var_str ('Flag that tells \whizard\ to allow negative ' // &
'weights in integration and simulation. (cf. also \ttt{simulate}, ' // &
'\ttt{?unweighted})'))
call var_list%append_log (var_str ("?resonance_history"), .false., &
intrinsic=.true., &
description=var_str ( &
'The logical variable \texttt{?resonance\_history ' // &
'= true/false} specifies whether during a simulation pass, ' // &
'the event generator should try to reconstruct intermediate ' // &
'resonances. If activated, appropriate resonant subprocess ' // &
'matrix element code will be automatically generated. '))
call var_list%append_real (var_str ("resonance_on_shell_limit"), &
4._default, &
intrinsic=.true., &
description=var_str ( &
'The real variable \texttt{resonance\_on\_shell\_limit ' // &
'= {\em <num>}} specifies the maximum relative distance from a ' // &
'resonance peak, such that the kinematical configuration ' // &
'can still be considered on-shell. This is relevant only if ' // &
'\texttt{?resonance\_history = true}.'))
call var_list%append_real (var_str ("resonance_on_shell_turnoff"), &
0._default, &
intrinsic=.true., &
description=var_str ( &
'The real variable \texttt{resonance\_on\_shell\_turnoff ' // &
'= {\em <num>}}, if positive, ' // &
'controls the smooth transition from resonance-like ' // &
'to background-like events. The relative strength of a ' // &
'resonance is reduced by a Gaussian with width given by this ' // &
'variable. In any case, events are treated as background-like ' // &
'when the off-shellness is greater than ' // &
'\texttt{resonance\_on\_shell\_limit}. All of this applies ' // &
'only if \texttt{?resonance\_history = true}.'))
call var_list%append_real (var_str ("resonance_background_factor"), &
1._default, &
intrinsic=.true., &
description=var_str ( &
'The real variable \texttt{resonance\_background\_factor} ' // &
'controls resonance insertion if a resonance ' // &
'history applies to a particular event. In determining '// &
'whether event kinematics qualifies as resonant or non-resonant, ' //&
'the non-resonant probability is multiplied by this factor ' // &
'Setting the factor to zero removes the background ' // &
'configuration as long as the kinematics qualifies as on-shell ' // &
'as qualified by \texttt{resonance\_on\_shell\_limit}.'))
call var_list%append_log (var_str ("?keep_beams"), .false., &
intrinsic=.true., &
description=var_str ('The logical variable \ttt{?keep\_beams ' // &
'= true/false} specifies whether beam particles and beam remnants ' // &
'are included when writing event files. For example, in order ' // &
'to read Les Houches accord event files into \pythia, no beam ' // &
'particles are allowed.'))
call var_list%append_log (var_str ("?keep_remnants"), .true., &
intrinsic=.true., &
description=var_str ('The logical variable \ttt{?keep\_beams ' // &
'= true/false} is respected only if \ttt{?keep\_beams} is set. ' // &
'If \ttt{true}, beam remnants are tagged as outgoing particles ' // &
'if they have been neither showered nor hadronized, i.e., have ' // &
'no children. If \ttt{false}, beam remnants are also included ' // &
'in the event record, but tagged as unphysical. Note that for ' // &
'ISR and/or beamstrahlung spectra, the radiated photons are ' // &
'considered as beam remnants.'))
call var_list%append_log (var_str ("?recover_beams"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether the beam particles ' // &
'should be reconstructed when reading event/rescanning files ' // &
'into \whizard. (cf. \ttt{rescan}, \ttt{?update\_event}, \ttt{?update\_sqme}, ' // &
'\newline \ttt{?update\_weight})'))
call var_list%append_log (var_str ("?update_event"), .false., &
intrinsic=.true., &
description=var_str ('Flag that decides whether the events in ' // &
'an event file should be rebuilt from the hard process when ' // &
'reading event/rescanning files into \whizard. (cf. \ttt{rescan}, ' // &
'\ttt{?recover\_beams}, \ttt{?update\_sqme}, \ttt{?update\_weight})'))
call var_list%append_log (var_str ("?update_sqme"), .false., &
intrinsic=.true., &
description=var_str ('Flag that decides whehter the squared ' // &
'matrix element in an event file should be updated/recalculated ' // &
'when reading event/rescanning files into \whizard. (cf. \ttt{rescan}, ' // &
'\newline \ttt{?recover\_beams}, \ttt{?update\_event}, \ttt{?update\_weight})'))
call var_list%append_log (var_str ("?update_weight"), .false., &
intrinsic=.true., &
description=var_str ('Flag that decides whether the weights ' // &
'in an event file should be updated/recalculated when reading ' // &
'event/rescanning files into \whizard. (cf. \ttt{rescan}, \ttt{?recover\_beams}, ' // &
'\newline \ttt{?update\_event}, \ttt{?update\_sqme})'))
call var_list%append_log (var_str ("?use_alphas_from_file"), .false., &
intrinsic=.true., &
description=var_str ('Flag that decides whether the current ' // &
'$\alpha_s$ definition should be used when recalculating matrix ' // &
'elements for events read from file, or the value that is stored ' // &
'in the file for that event. (cf. \ttt{rescan}, \ttt{?update\_sqme}, ' // &
'\ttt{?use\_scale\_from\_file})'))
call var_list%append_log (var_str ("?use_scale_from_file"), .false., &
intrinsic=.true., &
description=var_str ('Flag that decides whether the current ' // &
'energy-scale expression should be used when recalculating matrix ' // &
'elements for events read from file, or the value that is stored ' // &
'in the file for that event. (cf. \ttt{rescan}, \ttt{?update\_sqme}, ' // &
'\ttt{?use\_alphas\_from\_file})'))
call var_list%append_log (var_str ("?allow_decays"), .true., &
intrinsic=.true., &
description=var_str ('Master flag to switch on cascade decays ' // &
'for final state particles as an event transform. As a default, ' // &
'it is switched on. (cf. also \ttt{?auto\_decays}, ' // &
'\ttt{auto\_decays\_multiplicity}, \ttt{?auto\_decays\_radiative}, ' // &
'\ttt{?decay\_rest\_frame})'))
call var_list%append_log (var_str ("?auto_decays"), .false., &
intrinsic=.true., &
description=var_str ('Flag, particularly as optional argument of the ($\to$) ' // &
'\ttt{unstable} command, that tells \whizard\ to automatically ' // &
'determine the decays of that particle up to the final state ' // &
'multplicity ($\to$) \ttt{auto\_decays\_multiplicity}. Depending ' // &
'on the flag ($\to$) \ttt{?auto\_decays\_radiative}, radiative ' // &
'decays will be taken into account or not. (cf. also \ttt{unstable}, ' // &
'\ttt{?isotropic\_decay}, \ttt{?diagonal\_decay})'))
call var_list%append_int (var_str ("auto_decays_multiplicity"), 2, &
intrinsic=.true., &
description=var_str ('Integer parameter, that sets -- ' // &
'for the ($\to$) \ttt{?auto\_decays} option to let \whizard\ ' // &
'automatically determine the decays of a particle set as ($\to$) ' // &
'\ttt{unstable} -- the maximal final state multiplicity that ' // &
'is taken into account. The default is \ttt{2}. The flag \ttt{?auto\_decays\_radiative} ' // &
'decides whether radiative decays are taken into account. (cf.\ ' // &
'also \ttt{unstable}, \ttt{?auto\_decays})'))
call var_list%append_log (var_str ("?auto_decays_radiative"), .false., &
intrinsic=.true., &
description=var_str ("If \whizard's automatic detection " // &
'of decay channels are switched on ($\to$ \ttt{?auto\_decays} ' // &
'for the ($\to$) \ttt{unstable} command, this flags decides ' // &
'whether radiative decays (e.g. containing additional photon(s)/gluon(s)) ' // &
'are taken into account or not. (cf. also \ttt{unstable}, \ttt{auto\_decays\_multiplicity})'))
call var_list%append_log (var_str ("?decay_rest_frame"), .false., &
intrinsic=.true., &
description=var_str ('Flag that allows to force a particle decay ' // &
'to be simulated in its rest frame. This simplifies the calculation ' // &
'for decays as stand-alone processes, but makes the process ' // &
'unsuitable for use in a decay chain.'))
call var_list%append_log (var_str ("?isotropic_decay"), .false., &
intrinsic=.true., &
description=var_str ('Flag that -- in case of using factorized ' // &
'production and decays using the ($\to$) \ttt{unstable} command ' // &
'-- tells \whizard\ to switch off spin correlations completely ' // &
'(isotropic decay). (cf. also \ttt{unstable}, \ttt{?auto\_decays}, ' // &
'\ttt{decay\_helicity}, \ttt{?diagonal\_decay})'))
call var_list%append_log (var_str ("?diagonal_decay"), .false., &
intrinsic=.true., &
description=var_str ('Flag that -- in case of using factorized ' // &
'production and decays using the ($\to$) \ttt{unstable} command ' // &
'-- tells \whizard\ instead of full spin correlations to take ' // &
'only the diagonal entries in the spin-density matrix (i.e. ' // &
'classical spin correlations). (cf. also \ttt{unstable}, \ttt{?auto\_decays}, ' // &
'\ttt{decay\_helicity}, \ttt{?isotropic\_decay})'))
call var_list%append_int (var_str ("decay_helicity"), &
intrinsic=.true., &
description=var_str ('If this parameter is given an integer ' // &
'value, any particle decay triggered by a subsequent \ttt{unstable} ' // &
'declaration will receive a projection on the given helicity ' // &
'state for the unstable particle. (cf. also \ttt{unstable}, ' // &
'\ttt{?isotropic\_decay}, \ttt{?diagonal\_decay}. The latter ' // &
'parameters, if true, take precdence over any \ttt{?decay\_helicity} setting.)'))
call var_list%append_log (var_str ("?polarized_events"), .false., &
intrinsic=.true., &
description=var_str ('Flag that allows to select certain helicity ' // &
'combinations in final state particles in the event files, ' // &
'and perform analysis on polarized event samples. (cf. also ' // &
'\ttt{simulate}, \ttt{polarized}, \ttt{unpolarized})'))
call var_list%append_string (var_str ("$polarization_mode"), &
var_str ("helicity"), &
intrinsic=.true., &
description=var_str ('String variable that specifies the mode in ' // &
'which the polarization of particles is handled when polarized events ' // &
'are written out. Possible options are \ttt{"ignore"}, \ttt{"helicity"}, ' // &
'\ttt{"factorized"}, and \ttt{"correlated"}. For more details cf. the ' // &
'detailed section.'))
call var_list%append_log (var_str ("?colorize_subevt"), .false., &
intrinsic=.true., &
description=var_str ('Flag that enables color-index tracking ' // &
'in the subevent (\ttt{subevt}) objects that are used for ' // &
'internal event analysis.'))
call var_list%append_real (var_str ("tolerance"), 0._default, &
intrinsic=.true., &
description=var_str ('Real variable that defines the absolute ' // &
'tolerance with which the (logical) function \ttt{expect} accepts ' // &
'equality or inequality: \ttt{tolerance = {\em <num>}}. This ' // &
'can e.g. be used for cross-section tests and backwards compatibility ' // &
'checks. (cf. also \ttt{expect})'))
call var_list%append_int (var_str ("checkpoint"), 0, &
intrinsic = .true., &
description=var_str ('Setting this integer variable to a positive ' // &
'integer $n$ instructs simulate to print out a progress summary ' // &
'every $n$ events.'))
call var_list%append_int (var_str ("event_callback_interval"), 0, &
intrinsic = .true., &
description=var_str ('Setting this integer variable to a positive ' // &
'integer $n$ instructs simulate to print out a progress summary ' // &
'every $n$ events.'))
call var_list%append_log (var_str ("?pacify"), .false., &
intrinsic=.true., &
description=var_str ('Flag that allows to suppress numerical ' // &
'noise and give screen and log file output with a lower number ' // &
'of significant digits. Mainly for debugging purposes. (cf. also ' // &
'\ttt{?sample\_pacify})'))
call var_list%append_string (var_str ("$out_file"), var_str (""), &
intrinsic=.true., &
description=var_str ('This character variable allows to specify ' // &
'the name of the data file to which the histogram and plot data ' // &
'are written (cf. also \ttt{write\_analysis}, \ttt{open\_out}, ' // &
'\ttt{close\_out})'))
call var_list%append_log (var_str ("?out_advance"), .true., &
intrinsic=.true., &
description=var_str ('Flag that sets advancing in the \ttt{printf} ' // &
'output commands, i.e. continuous printing with no line feed ' // &
'etc. (cf. also \ttt{printf})'))
!!! JRR: WK please check (#542)
! call var_list%append_log (var_str ("?out_custom"), .false., &
! intrinsic=.true.)
! call var_list%append_string (var_str ("$out_comment"), var_str ("# "), &
! intrinsic=.true.)
! call var_list%append_log (var_str ("?out_header"), .true., &
! intrinsic=.true.)
! call var_list%append_log (var_str ("?out_yerr"), .true., &
! intrinsic=.true.)
! call var_list%append_log (var_str ("?out_xerr"), .true., &
! intrinsic=.true.)
call var_list%append_int (var_str ("real_range"), &
range (real_specimen), intrinsic = .true., locked = .true., &
description=var_str ('This integer gives the decimal exponent ' // &
'range of the numeric model for the real float type in use. It cannot ' // &
'be set by the user. (cf. also \ttt{real\_precision}, ' // &
'\ttt{real\_epsilon}, \ttt{real\_tiny}).'))
call var_list%append_int (var_str ("real_precision"), &
precision (real_specimen), intrinsic = .true., locked = .true., &
description=var_str ('This integer gives the precision of ' // &
'the numeric model for the real float type in use. It cannot ' // &
'be set by the user. (cf. also \ttt{real\_range}, ' // &
'\ttt{real\_epsilon}, \ttt{real\_tiny}).'))
call var_list%append_real (var_str ("real_epsilon"), &
epsilon (real_specimen), intrinsic = .true., locked = .true., &
description=var_str ('This gives the smallest number $E$ ' // &
'of the same kind as the float type for which $1 + E > 1$. ' // &
'It cannot be set by the user. (cf. also \ttt{real\_range}, ' // &
'\ttt{real\_tiny}, \ttt{real\_precision}).'))
call var_list%append_real (var_str ("real_tiny"), &
tiny (real_specimen), intrinsic = .true., locked = .true., &
description=var_str ('This gives the smallest positive (non-zero) ' // &
'number in the numeric model for the real float type in use. ' // &
'It cannot be set by the user. (cf. also \ttt{real\_range}, ' // &
'\ttt{real\_epsilon}, \ttt{real\_precision}).'))
end subroutine var_list_set_core_defaults
@ %def var_list_set_core_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_integration_defaults => var_list_set_integration_defaults
<<Variables: procedures>>=
subroutine var_list_set_integration_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_string (var_str ("$integration_method"), var_str ("vamp"), &
intrinsic=.true., &
description=var_str ('This string variable specifies the method ' // &
'for performing the multi-dimensional phase-space integration. ' // &
'The default is the \vamp\ algorithm (\ttt{"vamp"}), other options ' // &
'are via the numerical midpoint rule (\ttt{"midpoint"}) or an ' // &
'alternate \vamptwo\ implementation that is MPI-parallelizable ' // &
'(\ttt{"vamp2"}).'))
call var_list%append_int (var_str ("threshold_calls"), 10, &
intrinsic=.true., &
description=var_str ('This integer variable gives a limit for ' // &
'the number of calls in a given channel which acts as a lower ' // &
'threshold for the channel weight. If the number of calls in ' // &
'that channel falls below this threshold, the weight is not ' // &
'lowered further but kept at this threshold. (cf. also ' // &
'\ttt{channel\_weights\_power})'))
call var_list%append_int (var_str ("min_calls_per_channel"), 10, &
intrinsic=.true., &
description=var_str ('Integer parameter that modifies the settings ' // &
"of the \vamp\ integrator's grid parameters. It sets the minimal " // &
'number every channel must be called. If the number of calls ' // &
'from the iterations is too small, \whizard\ will automatically ' // &
'increase the number of calls. (cf. \ttt{iterations}, \ttt{min\_calls\_per\_bin}, ' // &
'\ttt{min\_bins}, \ttt{max\_bins})'))
call var_list%append_int (var_str ("min_calls_per_bin"), 10, &
intrinsic=.true., &
description=var_str ('Integer parameter that modifies the settings ' // &
"of the \vamp\ integrator's grid parameters. It sets the minimal " // &
'number every bin in an integration dimension must be called. ' // &
'If the number of calls from the iterations is too small, \whizard\ ' // &
'will automatically increase the number of calls. (cf. \ttt{iterations}, ' // &
'\ttt{min\_calls\_per\_channel}, \ttt{min\_bins}, \ttt{max\_bins})'))
call var_list%append_int (var_str ("min_bins"), 3, &
intrinsic=.true., &
description=var_str ('Integer parameter that modifies the settings ' // &
"of the \vamp\ integrator's grid parameters. It sets the minimal " // &
'number of bins per integration dimension. (cf. \ttt{iterations}, ' // &
'\ttt{max\_bins}, \ttt{min\_calls\_per\_channel}, \ttt{min\_calls\_per\_bin})'))
call var_list%append_int (var_str ("max_bins"), 20, &
intrinsic=.true., &
description=var_str ('Integer parameter that modifies the settings ' // &
"of the \vamp\ integrator's grid parameters. It sets the maximal " // &
'number of bins per integration dimension. (cf. \ttt{iterations}, ' // &
'\ttt{min\_bins}, \ttt{min\_calls\_per\_channel}, \ttt{min\_calls\_per\_bin})'))
call var_list%append_log (var_str ("?stratified"), .true., &
intrinsic=.true., &
description=var_str ('Flag that switches between stratified ' // &
'and importance sampling for the \vamp\ integration method.'))
call var_list%append_log (var_str ("?use_vamp_equivalences"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether equivalence ' // &
'relations (symmetries) between different integration channels ' // &
'are used by the \vamp\ integrator.'))
call var_list%append_log (var_str ("?vamp_verbose"), .false., &
intrinsic=.true., &
description=var_str ('Flag that sets the chattiness of the \vamp\ ' // &
'integrator. If set, not only errors, but also all warnings and ' // &
'messages will be written out (not the default). (cf. also \newline ' // &
'\ttt{?vamp\_history\_global}, \ttt{?vamp\_history\_global\_verbose}, ' // &
'\ttt{?vamp\_history\_channels}, \newline \ttt{?vamp\_history\_channels\_verbose})'))
call var_list%append_log (var_str ("?vamp_history_global"), &
.true., intrinsic=.true., &
description=var_str ('Flag that decides whether the global history ' // &
'of the grid adaptation of the \vamp\ integrator are written ' // &
'into the process logfiles. (cf. also \ttt{?vamp\_history\_global\_verbose}, ' // &
'\ttt{?vamp\_history\_channels}, \ttt{?vamp\_history\_channels\_verbose}, ' // &
'\ttt{?vamp\_verbose})'))
call var_list%append_log (var_str ("?vamp_history_global_verbose"), &
.false., intrinsic=.true., &
description=var_str ('Flag that decides whether the global history ' // &
'of the grid adaptation of the \vamp\ integrator are written ' // &
'into the process logfiles in an extended version. Only for debugging ' // &
'purposes. (cf. also \ttt{?vamp\_history\_global}, \ttt{?vamp\_history\_channels}, ' // &
'\ttt{?vamp\_verbose}, \ttt{?vamp\_history\_channels\_verbose})'))
call var_list%append_log (var_str ("?vamp_history_channels"), &
.false., intrinsic=.true., &
description=var_str ('Flag that decides whether the history of ' // &
'the grid adaptation of the \vamp\ integrator for every single ' // &
'channel are written into the process logfiles. Only for debugging ' // &
'purposes. (cf. also \ttt{?vamp\_history\_global\_verbose}, ' // &
'\ttt{?vamp\_history\_global}, \ttt{?vamp\_verbose}, \newline ' // &
'\ttt{?vamp\_history\_channels\_verbose})'))
call var_list%append_log (var_str ("?vamp_history_channels_verbose"), &
.false., intrinsic=.true., &
description=var_str ('Flag that decides whether the history of ' // &
'the grid adaptation of the \vamp\ integrator for every single ' // &
'channel are written into the process logfiles in an extended ' // &
'version. Only for debugging purposes. (cf. also \ttt{?vamp\_history\_global}, ' // &
'\ttt{?vamp\_history\_channels}, \ttt{?vamp\_verbose}, \ttt{?vamp\_history\_global\_verbose})'))
call var_list%append_string (var_str ("$run_id"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable \ttt{\$run\_id = "{\em ' // &
'<id>}"} that allows to set a special ID for a particular process ' // &
'run, e.g. in a scan. The run ID is then attached to the process ' // &
'log file: \newline \ttt{{\em <proc\_name>}\_{\em <proc\_comp>}.{\em ' // &
'<id>}.log}, the \vamp\ grid file: \newline \ttt{{\em <proc\_name>}\_{\em ' // &
'<proc\_comp>}.{\em <id>}.vg}, and the phase space file: \newline ' // &
'\ttt{{\em <proc\_name>}\_{\em <proc\_comp>}.{\em <id>}.phs}. ' // &
'The run ID string distinguishes among several runs for the ' // &
'same process. It identifies process instances with respect ' // &
'to adapted integration grids and similar run-specific data. ' // &
'The run ID is kept when copying processes for creating instances, ' // &
'however, so it does not distinguish event samples. (cf.\ also ' // &
'\ttt{\$job\_id}, \ttt{\$compile\_workspace}'))
call var_list%append_int (var_str ("n_calls_test"), 0, &
intrinsic=.true., &
description=var_str ('Integer variable that allows to set a ' // &
'certain number of matrix element sampling test calls without ' // &
'actually integrating the process under consideration. (cf. ' // &
'\ttt{integrate})'))
call var_list%append_log (var_str ("?integration_timer"), .true., &
intrinsic=.true., &
description=var_str ('This flag switches the integration timer ' // &
'on and off, that gives the estimate for the duration of the ' // &
'generation of 10,000 unweighted events for each integrated ' // &
'process.'))
call var_list%append_log (var_str ("?check_grid_file"), .true., &
intrinsic=.true., &
description=var_str ('Setting this to false turns off all sanity ' // &
'checks when reading a grid file with previous integration data. ' // &
'Use this at your own risk; the program may return wrong results ' // &
'or crash if data do not match. (cf. also \ttt{?check\_event\_file}, \ttt{?check\_phs\_file}) '))
call var_list%append_real (var_str ("accuracy_goal"), 0._default, &
intrinsic=.true., &
description=var_str ('Real parameter that allows the user to ' // &
'set a minimal accuracy that should be achieved in the Monte-Carlo ' // &
'integration of a certain process. If that goal is reached, ' // &
'grid and weight adapation stop, and this result is used for ' // &
'simulation. (cf. also \ttt{integrate}, \ttt{iterations}, ' // &
'\ttt{error\_goal}, \ttt{relative\_error\_goal}, ' // &
'\ttt{error\_threshold})'))
call var_list%append_real (var_str ("error_goal"), 0._default, &
intrinsic=.true., &
description=var_str ('Real parameter that allows the user to ' // &
'set a minimal absolute error that should be achieved in the ' // &
'Monte-Carlo integration of a certain process. If that goal ' // &
'is reached, grid and weight adapation stop, and this result ' // &
'is used for simulation. (cf. also \ttt{integrate}, \ttt{iterations}, ' // &
'\ttt{accuracy\_goal}, \ttt{relative\_error\_goal}, \ttt{error\_threshold})'))
call var_list%append_real (var_str ("relative_error_goal"), 0._default, &
intrinsic=.true., &
description=var_str ('Real parameter that allows the user to ' // &
'set a minimal relative error that should be achieved in the ' // &
'Monte-Carlo integration of a certain process. If that goal ' // &
'is reached, grid and weight adaptation stop, and this result ' // &
'is used for simulation. (cf. also \ttt{integrate}, \ttt{iterations}, ' // &
'\ttt{accuracy\_goal}, \ttt{error\_goal}, \ttt{error\_threshold})'))
call var_list%append_int (var_str ("integration_results_verbosity"), 1, &
intrinsic=.true., &
description=var_str ('Integer parameter for the verbosity of ' // &
'the integration results in the process-specific logfile.'))
call var_list%append_real (var_str ("error_threshold"), &
0._default, intrinsic=.true., &
description=var_str ('The real parameter \ttt{error\_threshold ' // &
'= {\em <num>}} declares that any error value (in absolute numbers) ' // &
'smaller than \ttt{{\em <num>}} is to be considered zero. The ' // &
'units are \ttt{fb} for scatterings and \ttt{GeV} for decays. ' // &
'(cf. also \ttt{integrate}, \ttt{iterations}, \ttt{accuracy\_goal}, ' // &
'\ttt{error\_goal}, \ttt{relative\_error\_goal})'))
call var_list%append_real (var_str ("channel_weights_power"), 0.25_default, &
intrinsic=.true., &
description=var_str ('Real parameter that allows to vary the ' // &
'exponent of the channel weights for the \vamp\ integrator.'))
call var_list%append_string (var_str ("$integrate_workspace"), &
intrinsic=.true., &
description=var_str ('Character string that tells \whizard\ ' // &
'the subdirectory where to find the run-specific phase-space ' // &
'configuration and the \vamp\ and \vamptwo\ grid files. ' // &
'If undefined (as per default), \whizard\ creates them and ' // &
'searches for them in the ' // &
'current directory. (cf. also \ttt{\$job\_id}, ' // &
'\ttt{\$run\_id}, \ttt{\$compile\_workspace})'))
end subroutine var_list_set_integration_defaults
@ %def var_list_set_integration_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_phase_space_defaults => var_list_set_phase_space_defaults
<<Variables: procedures>>=
subroutine var_list_set_phase_space_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_string (var_str ("$phs_method"), var_str ("default"), &
intrinsic=.true., &
description=var_str ('String variable that allows to choose ' // &
'the phase-space parameterization method. The default is the ' // &
'\ttt{"wood"} method that takes into account electroweak/BSM ' // &
'resonances. Note that this might not be the best choice for ' // &
'(pure) QCD amplitudes. (cf. also \ttt{\$phs\_file})'))
call var_list%append_log (var_str ("?vis_channels"), .false., &
intrinsic=.true., &
description=var_str ('Optional logical argument for the \ttt{integrate} ' // &
'command that demands \whizard\ to generate a PDF or postscript ' // &
'output showing the classification of the found phase space ' // &
'channels (if the phase space method \ttt{wood} has been used) ' // &
'according to their properties: \ttt{integrate (foo) \{ iterations=3:10000 ' // &
'?vis\_channels = true \}}. The default is \ttt{false}. (cf. ' // &
'also \ttt{integrate}, \ttt{?vis\_history})'))
call var_list%append_log (var_str ("?check_phs_file"), .true., &
intrinsic=.true., &
description=var_str ('Setting this to false turns off all sanity ' // &
'checks when reading a previously generated phase-space configuration ' // &
'file. Use this at your own risk; the program may return wrong ' // &
'results or crash if data do not match. (cf. also \ttt{?check\_event\_file}, ' // &
'\ttt{?check\_grid\_file})'))
call var_list%append_string (var_str ("$phs_file"), var_str (""), &
intrinsic=.true., &
description=var_str ('This string variable allows the user to ' // &
'set an individual file name for the phase space parameterization ' // &
'for a particular process: \ttt{\$phs\_file = "{\em <file\_name>}"}. ' // &
'If not set, the default is \ttt{{\em <proc\_name>}\_{\em <proc\_comp>}.{\em ' // &
'<run\_id>}.phs}. (cf. also \ttt{\$phs\_method})'))
call var_list%append_log (var_str ("?phs_only"), .false., &
intrinsic=.true., &
description=var_str ('Flag (particularly as optional argument ' // &
'of the $\to$ \ttt{integrate} command) that allows to only generate ' // &
'the phase space file, but not perform the integration. (cf. ' // &
'also \ttt{\$phs\_method}, \ttt{\$phs\_file})'))
call var_list%append_real (var_str ("phs_threshold_s"), 50._default, &
intrinsic=.true., &
description=var_str ('For the phase space method \ttt{wood}, ' // &
'this real parameter sets the threshold below which particles ' // &
'are assumed to be massless in the $s$-channel like kinematic ' // &
'regions. (cf. also \ttt{phs\_threshold\_t}, \ttt{phs\_off\_shell}, ' // &
'\ttt{phs\_t\_channel}, \ttt{phs\_e\_scale}, \ttt{phs\_m\_scale}, ' // &
'\newline \ttt{phs\_q\_scale}, \ttt{?phs\_keep\_resonant}, \ttt{?phs\_step\_mapping}, ' // &
'\ttt{?phs\_step\_mapping\_exp}, \newline \ttt{?phs\_s\_mapping})'))
call var_list%append_real (var_str ("phs_threshold_t"), 100._default, &
intrinsic=.true., &
description=var_str ('For the phase space method \ttt{wood}, ' // &
'this real parameter sets the threshold below which particles ' // &
'are assumed to be massless in the $t$-channel like kinematic ' // &
'regions. (cf. also \ttt{phs\_threshold\_s}, \ttt{phs\_off\_shell}, ' // &
'\ttt{phs\_t\_channel}, \ttt{phs\_e\_scale}, \ttt{phs\_m\_scale}, ' // &
'\newline \ttt{phs\_q\_scale}, \ttt{?phs\_keep\_resonant}, \ttt{?phs\_step\_mapping}, ' // &
'\ttt{?phs\_step\_mapping\_exp}, \newline \ttt{?phs\_s\_mapping})'))
call var_list%append_int (var_str ("phs_off_shell"), 2, &
intrinsic=.true., &
description=var_str ('Integer parameter that sets the number ' // &
'of off-shell (not $t$-channel-like, non-resonant) lines that ' // &
'are taken into account to find a valid phase-space setup in ' // &
'the \ttt{wood} phase-space method. (cf. also \ttt{phs\_threshold\_t}, ' // &
'\ttt{phs\_threshold\_s}, \ttt{phs\_t\_channel}, \ttt{phs\_e\_scale}, ' // &
'\ttt{phs\_m\_scale}, \ttt{phs\_q\_scale}, \ttt{?phs\_keep\_resonant}, ' // &
'\ttt{?phs\_step\_mapping}, \newline \ttt{?phs\_step\_mapping\_exp}, ' // &
'\ttt{?phs\_s\_mapping})'))
call var_list%append_int (var_str ("phs_t_channel"), 6, &
intrinsic=.true., &
description=var_str ('Integer parameter that sets the number ' // &
'of $t$-channel propagators in multi-peripheral diagrams that ' // &
'are taken into account to find a valid phase-space setup in ' // &
'the \ttt{wood} phase-space method. (cf. also \ttt{phs\_threshold\_t}, ' // &
'\ttt{phs\_threshold\_s}, \ttt{phs\_off\_shell}, \ttt{phs\_e\_scale}, ' // &
'\ttt{phs\_m\_scale}, \ttt{phs\_q\_scale}, \ttt{?phs\_keep\_resonant}, ' // &
'\ttt{?phs\_step\_mapping}, \newline \ttt{?phs\_step\_mapping\_exp}, ' // &
'\ttt{?phs\_s\_mapping})'))
call var_list%append_real (var_str ("phs_e_scale"), 10._default, &
intrinsic=.true., &
description=var_str ('Real parameter that sets the energy scale ' // &
'that acts as a cutoff for parameterizing radiation-like kinematics ' // &
'in the \ttt{wood} phase space method. \whizard\ takes the maximum ' // &
'of this value and the width of the propagating particle as ' // &
'a cutoff. (cf. also \ttt{phs\_threshold\_t}, \ttt{phs\_threshold\_s}, ' // &
'\ttt{phs\_t\_channel}, \ttt{phs\_off\_shell}, \ttt{phs\_m\_scale}, ' // &
'\ttt{phs\_q\_scale}, \newline \ttt{?phs\_keep\_resonant}, \ttt{?phs\_step\_mapping}, ' // &
'\ttt{?phs\_step\_mapping\_exp}, \ttt{?phs\_s\_mapping})'))
call var_list%append_real (var_str ("phs_m_scale"), 10._default, &
intrinsic=.true., &
description=var_str ('Real parameter that sets the mass scale ' // &
'that acts as a cutoff for parameterizing collinear and infrared ' // &
'kinematics in the \ttt{wood} phase space method. \whizard\ ' // &
'takes the maximum of this value and the mass of the propagating ' // &
'particle as a cutoff. (cf. also \ttt{phs\_threshold\_t}, \ttt{phs\_threshold\_s}, ' // &
'\ttt{phs\_t\_channel}, \ttt{phs\_off\_shell}, \ttt{phs\_e\_scale}, ' // &
'\ttt{phs\_q\_scale}, \newline \ttt{?phs\_keep\_resonant}, \ttt{?phs\_step\_mapping}, ' // &
'\ttt{?phs\_step\_mapping\_exp}, \ttt{?phs\_s\_mapping})'))
call var_list%append_real (var_str ("phs_q_scale"), 10._default, &
intrinsic=.true., &
description=var_str ('Real parameter that sets the momentum ' // &
'transfer scale that acts as a cutoff for parameterizing $t$- ' // &
'and $u$-channel like kinematics in the \ttt{wood} phase space ' // &
'method. \whizard\ takes the maximum of this value and the mass ' // &
'of the propagating particle as a cutoff. (cf. also \ttt{phs\_threshold\_t}, ' // &
'\ttt{phs\_threshold\_s}, \ttt{phs\_t\_channel}, \ttt{phs\_off\_shell}, ' // &
'\ttt{phs\_e\_scale}, \ttt{phs\_m\_scale}, \ttt{?phs\_keep\_resonant}, ' // &
'\ttt{?phs\_step\_mapping}, \ttt{?phs\_step\_mapping\_exp}, ' // &
'\newline \ttt{?phs\_s\_mapping})'))
call var_list%append_log (var_str ("?phs_keep_nonresonant"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether the \ttt{wood} ' // &
'phase space method takes into account also non-resonant contributions. ' // &
'(cf. also \ttt{phs\_threshold\_t}, \ttt{phs\_threshold\_s}, ' // &
'\ttt{phs\_t\_channel}, \ttt{phs\_off\_shell}, \ttt{phs\_m\_scale}, ' // &
'\ttt{phs\_q\_scale}, \ttt{phs\_e\_scale}, \ttt{?phs\_step\_mapping}, ' // &
'\newline \ttt{?phs\_step\_mapping\_exp}, \ttt{?phs\_s\_mapping})'))
call var_list%append_log (var_str ("?phs_step_mapping"), .true., &
intrinsic=.true., &
description=var_str ('Flag that switches on (or off) a particular ' // &
'phase space mapping for resonances, where the mass and width ' // &
'of the resonance are explicitly set as channel cutoffs. (cf. ' // &
'also \ttt{phs\_threshold\_t}, \ttt{phs\_threshold\_s}, \ttt{phs\_t\_channel}, ' // &
'\ttt{phs\_off\_shell}, \ttt{phs\_e\_scale}, \newline \ttt{phs\_m\_scale}, ' // &
'\ttt{?phs\_keep\_resonant}, \ttt{?phs\_q\_scale}, \ttt{?phs\_step\_mapping\_exp}, ' // &
'\newline \ttt{?phs\_s\_mapping})'))
call var_list%append_log (var_str ("?phs_step_mapping_exp"), .true., &
intrinsic=.true., &
description=var_str ('Flag that switches on (or off) a particular ' // &
'phase space mapping for resonances, where the mass and width ' // &
'of the resonance are explicitly set as channel cutoffs. This ' // &
'is an exponential mapping in contrast to ($\to$) \ttt{?phs\_step\_mapping}. ' // &
'(cf. also \ttt{phs\_threshold\_t}, \ttt{phs\_threshold\_s}, ' // &
'\ttt{phs\_t\_channel}, \ttt{phs\_off\_shell}, \ttt{phs\_e\_scale}, ' // &
'\ttt{phs\_m\_scale}, \newline \ttt{?phs\_q\_scale}, \ttt{?phs\_keep\_resonant}, ' // &
'\ttt{?phs\_step\_mapping}, \ttt{?phs\_s\_mapping})'))
call var_list%append_log (var_str ("?phs_s_mapping"), .true., &
intrinsic=.true., &
description=var_str ('Flag that allows special mapping for $s$-channel ' // &
'resonances. (cf. also \ttt{phs\_threshold\_t}, \ttt{phs\_threshold\_s}, ' // &
'\ttt{phs\_t\_channel}, \ttt{phs\_off\_shell}, \ttt{phs\_e\_scale}, ' // &
'\ttt{phs\_m\_scale}, \newline \ttt{?phs\_keep\_resonant}, \ttt{?phs\_q\_scale}, ' // &
'\ttt{?phs\_step\_mapping}, \ttt{?phs\_step\_mapping\_exp})'))
call var_list%append_log (var_str ("?vis_history"), .false., &
intrinsic=.true., &
description=var_str ('Optional logical argument for the \ttt{integrate} ' // &
'command that demands \whizard\ to generate a PDF or postscript ' // &
'output showing the adaptation history of the Monte-Carlo integration ' // &
'of the process under consideration. (cf. also \ttt{integrate}, ' // &
'\ttt{?vis\_channels})'))
end subroutine var_list_set_phase_space_defaults
@ %def var_list_set_phase_space_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_gamelan_defaults => var_list_set_gamelan_defaults
<<Variables: procedures>>=
subroutine var_list_set_gamelan_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_int (&
var_str ("n_bins"), 20, &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: integer value that sets the number of bins in histograms. ' // &
'(cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, \ttt{\$obs\_unit}, ' // &
'\ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, \ttt{\$y\_label}, ' // &
'\ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, \ttt{?y\_log}, ' // &
'\ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, ' // &
'\ttt{\$gmlcode\_bg}, \ttt{\$gmlcode\_fg}, \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_base}, \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_errors}, \ttt{?draw\_symbols}, ' // &
'\newline \ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options}, ' // &
'\ttt{\$symbol})'))
call var_list%append_log (&
var_str ("?normalize_bins"), .false., &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that determines whether the weights shall be normalized ' // &
'to the bin width or not. (cf. also \ttt{n\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \ttt{\$gmlcode\_bg}, \ttt{\$gmlcode\_fg}, \ttt{?draw\_histogram}, ' // &
'\newline \ttt{?draw\_base}, \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_errors}, \ttt{\$symbol}, \newline ' // &
'\ttt{?draw\_symbols}, \ttt{\$fill\_options}, \ttt{\$draw\_options}, ' // &
'\ttt{\$err\_options})'))
call var_list%append_string (var_str ("$obs_label"), var_str (""), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: this is a string variable \ttt{\$obs\_label = "{\em ' // &
'<LaTeX\_Code>}"} that allows to attach a label to a plotted ' // &
'or histogrammed observable. (cf. also \ttt{n\_bins}, \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{?y\_log}, \ttt{?x\_log}, \ttt{graph\_width\_mm}, ' // &
'\ttt{graph\_height\_mm}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \ttt{\$gmlcode\_bg}, \ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, ' // &
'\ttt{?draw\_histogram}, \ttt{?draw\_piecewise}, \newline \ttt{?fill\_curve}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_errors}, \ttt{\$symbol}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options})'))
call var_list%append_string (var_str ("$obs_unit"), var_str (""), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: this is a string variable \ttt{\$obs\_unit = "{\em ' // &
'<LaTeX\_Code>}"} that allows to attach a \LaTeX\ physical unit ' // &
'to a plotted or histogrammed observable. (cf. also \ttt{n\_bins}, ' // &
'\ttt{?normalize\_bins}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{?y\_log}, \ttt{?x\_log}, ' // &
'\ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, \ttt{x\_min}, ' // &
'\ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, \ttt{?draw\_histogram}, ' // &
'\ttt{?fill\_curve}, \ttt{?draw\_piecewise}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \ttt{\$symbol}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options})'))
call var_list%append_string (var_str ("$title"), var_str (""), &
intrinsic=.true., &
description=var_str ('This string variable sets the title of ' // &
'a plot in a \whizard\ analysis setup, e.g. a histogram or an ' // &
'observable. The syntax is \ttt{\$title = "{\em <your title>}"}. ' // &
'This title appears as a section header in the analysis file, ' // &
'but not in the screen output of the analysis. (cf. also \ttt{n\_bins}, ' // &
'\ttt{?normalize\_bins}, \ttt{\$obs\_unit}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{?y\_log}, \ttt{?x\_log}, ' // &
'\ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, \ttt{x\_min}, ' // &
'\ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, \ttt{?draw\_histogram}, ' // &
'\ttt{?fill\_curve}, \ttt{?draw\_piecewise}, \newline \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \ttt{\$symbol}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options})'))
call var_list%append_string (var_str ("$description"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable that allows to specify ' // &
'a description text for the analysis, \ttt{\$description = "{\em ' // &
'<LaTeX analysis descr.>}"}. This line appears below the title ' // &
'of a corresponding analysis, on top of the respective plot. ' // &
'(cf. also \ttt{analysis}, \ttt{n\_bins}, \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{?y\_log}, ' // &
'\ttt{?x\_log}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, \ttt{?draw\_histogram}, ' // &
'\ttt{?fill\_curve}, \ttt{?draw\_piecewise}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \ttt{\$symbol}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options})'))
call var_list%append_string (var_str ("$x_label"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable, \ttt{\$x\_label = "{\em ' // &
'<LaTeX code>}"}, that sets the $x$ axis label in a plot or ' // &
'histogram in a \whizard\ analysis. (cf. also \ttt{analysis}, ' // &
'\ttt{n\_bins}, \ttt{?normalize\_bins}, \ttt{\$obs\_unit}, \ttt{\$y\_label}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \newline ' // &
'\ttt{\$gmlcode\_bg}, \ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, ' // &
'\ttt{?draw\_histogram}, \ttt{?fill\_curve}, \newline \ttt{?draw\_piecewise}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_errors}, \ttt{\$symbol}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options})'))
call var_list%append_string (var_str ("$y_label"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable, \ttt{\$y\_label = "{\em ' // &
'<LaTeX\_code>}"}, that sets the $y$ axis label in a plot or ' // &
'histogram in a \whizard\ analysis. (cf. also \ttt{analysis}, ' // &
'\ttt{n\_bins}, \ttt{?normalize\_bins}, \ttt{\$obs\_unit}, \ttt{?y\_log}, ' // &
'\ttt{?x\_log}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \newline ' // &
'\ttt{\$gmlcode\_bg}, \ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, ' // &
'\ttt{?draw\_histogram}, \ttt{?fill\_curve}, \newline \ttt{?draw\_piecewise}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_errors}, \ttt{\$symbol}, \ttt{?draw\_symbols}, ' // &
'\newline \ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options})'))
call var_list%append_int (var_str ("graph_width_mm"), 130, &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: integer value that sets the width of a graph or histogram ' // &
'in millimeters. (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_height\_mm}, \ttt{?y\_log}, \ttt{?x\_log}, ' // &
'\ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_histogram}, \ttt{?draw\_base}, ' // &
'\newline \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \ttt{?draw\_symbols}, \newline \ttt{\$fill\_options}, ' // &
'\ttt{\$draw\_options}, \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_int (var_str ("graph_height_mm"), 90, &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: integer value that sets the height of a graph or histogram ' // &
'in millimeters. (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{?y\_log}, \ttt{?x\_log}, ' // &
'\ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_histogram}, \ttt{?draw\_base}, ' // &
'\newline \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \ttt{?draw\_symbols}, \newline \ttt{\$fill\_options}, ' // &
'\ttt{\$draw\_options}, \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_log (var_str ("?y_log"), .false., &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that makes the $y$ axis logarithmic. (cf. also ' // &
'\ttt{?normalize\_bins}, \ttt{\$obs\_label}, \ttt{\$obs\_unit}, ' // &
'\ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, \ttt{\$y\_label}, ' // &
'\ttt{graph\_height\_mm}, \ttt{graph\_width\_mm}, \ttt{?y\_log}, ' // &
'\ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \newline ' // &
'\ttt{\$gmlcode\_bg}, \ttt{\$gmlcode\_fg}, \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_base}, \ttt{?draw\_piecewise}, \newline \ttt{?fill\_curve}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_errors}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \newline \ttt{\$draw\_options}, \ttt{\$err\_options}, ' // &
'\ttt{\$symbol})'))
call var_list%append_log (var_str ("?x_log"), .false., &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that makes the $x$ axis logarithmic. (cf. also ' // &
'\ttt{?normalize\_bins}, \ttt{\$obs\_label}, \ttt{\$obs\_unit}, ' // &
'\ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, \ttt{\$y\_label}, ' // &
'\ttt{graph\_height\_mm}, \ttt{graph\_width\_mm}, \ttt{?y\_log}, ' // &
'\ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \newline ' // &
'\ttt{\$gmlcode\_bg}, \ttt{\$gmlcode\_fg}, \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_base}, \ttt{?draw\_piecewise}, \newline \ttt{?fill\_curve}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_errors}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \newline \ttt{\$draw\_options}, \ttt{\$err\_options}, ' // &
'\ttt{\$symbol})'))
call var_list%append_real (var_str ("x_min"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: real parameter that sets the lower limit of the $x$ ' // &
'axis plotting or histogram interval. (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \newline \ttt{?x\_log}, \ttt{graph\_width\_mm}, ' // &
'\ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, \newline \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \newline \ttt{?draw\_symbols}, \ttt{\$fill\_options}, ' // &
'\ttt{\$draw\_options}, \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_real (var_str ("x_max"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: real parameter that sets the upper limit of the $x$ ' // &
'axis plotting or histogram interval. (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \newline \ttt{?x\_log}, \ttt{graph\_width\_mm}, ' // &
'\ttt{x\_min}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, \newline \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \newline \ttt{?draw\_symbols}, \ttt{\$fill\_options}, ' // &
'\ttt{\$draw\_options}, \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_real (var_str ("y_min"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: real parameter that sets the lower limit of the $y$ ' // &
'axis plotting or histogram interval. (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \newline \ttt{?x\_log}, \ttt{graph\_width\_mm}, ' // &
'\ttt{x\_max}, \ttt{y\_max}, \ttt{x\_min}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, \newline \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \newline \ttt{?draw\_symbols}, \ttt{\$fill\_options}, ' // &
'\ttt{\$draw\_options}, \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_real (var_str ("y_max"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: real parameter that sets the upper limit of the $y$ ' // &
'axis plotting or histogram interval. (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \newline \ttt{?x\_log}, \ttt{graph\_width\_mm}, ' // &
'\ttt{x\_max}, \ttt{x\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{\$gmlcode\_fg}, \ttt{?draw\_base}, \newline \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \newline \ttt{?draw\_symbols}, \ttt{\$fill\_options}, ' // &
'\ttt{\$draw\_options}, \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_string (var_str ("$gmlcode_bg"), var_str (""), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: string variable that allows to define a background ' // &
'for plots and histograms (i.e. it is overwritten by the plot/histogram), ' // &
'e.g. a grid: \ttt{\$gmlcode\_bg = "standardgrid.lr(5);"}. For ' // &
'more details, see the \gamelan\ manual. (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_width\_mm}, ' // &
'\ttt{graph\_height\_mm}, \ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, ' // &
'\ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_fg}, ' // &
'\ttt{?draw\_histogram}, \ttt{?draw\_base}, \ttt{?draw\_piecewise}, ' // &
'\newline \ttt{?fill\_curve}, \ttt{?draw\_curve}, \ttt{?draw\_errors}, ' // &
'\ttt{?draw\_symbols}, \ttt{\$fill\_options}, \newline \ttt{\$draw\_options}, ' // &
'\ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_string (var_str ("$gmlcode_fg"), var_str (""), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: string variable that allows to define a foreground ' // &
'for plots and histograms (i.e. it overwrites the plot/histogram), ' // &
'e.g. a grid: \ttt{\$gmlcode\_bg = "standardgrid.lr(5);"}. For ' // &
'more details, see the \gamelan\ manual. (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_width\_mm}, ' // &
'\ttt{graph\_height\_mm}, \ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, ' // &
'\ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{?draw\_histogram}, \ttt{?draw\_base}, \ttt{?draw\_piecewise}, ' // &
'\newline \ttt{?fill\_curve}, \ttt{?draw\_curve}, \ttt{?draw\_errors}, ' // &
'\ttt{?draw\_symbols}, \ttt{\$fill\_options}, \newline \ttt{\$draw\_options}, ' // &
'\ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_log (var_str ("?draw_histogram"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that tells \whizard\ to either plot data as a ' // &
'histogram or as a continuous line (if $\to$ \ttt{?draw\_curve} ' // &
'is set \ttt{true}). (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \newline \ttt{\$gmlcode\_fg}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{?draw\_base}, \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_errors}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options}, ' // &
'\ttt{\$symbol})'))
call var_list%append_log (var_str ("?draw_base"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that tells \whizard\ to insert a \ttt{base} statement ' // &
'in the analysis code to calculate the plot data from a data ' // &
'set. (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \newline \ttt{\$gmlcode\_fg}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, ' // &
'\ttt{\$symbol}, \newline \ttt{?draw\_histogram}, \ttt{?draw\_errors}, ' // &
'\ttt{?draw\_symbols}, \ttt{\$fill\_options}, \ttt{\$draw\_options}, ' // &
'\newline \ttt{\$err\_options})'))
call var_list%append_log (var_str ("?draw_piecewise"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that tells \whizard\ to data from a data set piecewise, ' // &
'i.e. histogram style. (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, ' // &
'\ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_fg}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_base}, \ttt{?fill\_curve}, ' // &
'\ttt{\$symbol}, \ttt{?draw\_histogram}, \ttt{?draw\_errors}, ' // &
'\ttt{?draw\_symbols}, \ttt{\$fill\_options}, \ttt{\$draw\_options}, ' // &
'\ttt{\$err\_options})'))
call var_list%append_log (var_str ("?fill_curve"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that tells \whizard\ to fill data curves (e.g. ' // &
'as a histogram). The style can be set with $\to$ \ttt{\$fill\_options ' // &
'= "{\em <LaTeX\_code>}"}. (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_width\_mm}, ' // &
'\ttt{graph\_height\_mm}, \ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, ' // &
'\ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \newline \ttt{\$gmlcode\_fg}, ' // &
'\ttt{\$gmlcode\_bg}, \ttt{?draw\_base}, \ttt{?draw\_piecewise}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_histogram}, \ttt{?draw\_errors}, ' // &
'\ttt{?draw\_symbols}, \ttt{\$fill\_options}, \ttt{\$draw\_options}, ' // &
'\ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_log (var_str ("?draw_curve"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that tells \whizard\ to either plot data as a ' // &
'continuous line or as a histogram (if $\to$ \ttt{?draw\_histogram} ' // &
'is set \ttt{true}). (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \newline \ttt{\$gmlcode\_fg}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{?draw\_base}, \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, ' // &
'\ttt{?draw\_histogram}, \ttt{?draw\_errors}, \ttt{?draw\_symbols}, ' // &
'\ttt{\$fill\_options}, \ttt{\$draw\_options}, \ttt{\$err\_options}, ' // &
'\ttt{\$symbol})'))
call var_list%append_log (var_str ("?draw_errors"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that determines whether error bars should be drawn ' // &
'or not. (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \ttt{\$gmlcode\_fg}, \ttt{\$gmlcode\_bg}, \ttt{?draw\_base}, ' // &
'\ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_symbols}, \ttt{\$fill\_options}, ' // &
'\newline \ttt{\$draw\_options}, \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_log (var_str ("?draw_symbols"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: flag that determines whether particular symbols (specified ' // &
'by $\to$ \ttt{\$symbol = "{\em <LaTeX\_code>}"}) should be ' // &
'used for plotting data points (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_width\_mm}, ' // &
'\ttt{graph\_height\_mm}, \ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, ' // &
'\ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_fg}, ' // &
'\ttt{\$gmlcode\_bg}, \ttt{?draw\_base}, \ttt{?draw\_piecewise}, ' // &
'\ttt{?fill\_curve}, \ttt{?draw\_histogram}, \ttt{?draw\_curve}, ' // &
'\ttt{?draw\_errors}, \ttt{\$fill\_options}, \ttt{\$draw\_options}, ' // &
'\newline \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_string (var_str ("$fill_options"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: \ttt{\$fill\_options = "{\em <LaTeX\_code>}"} is a ' // &
'string variable that allows to set fill options when plotting ' // &
'data as filled curves with the $\to$ \ttt{?fill\_curve} flag. ' // &
'For more details see the \gamelan\ manual. (cf. also \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_label}, \ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, ' // &
'\ttt{\$x\_label}, \ttt{\$y\_label}, \ttt{graph\_width\_mm}, ' // &
'\ttt{graph\_height\_mm}, \ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, ' // &
'\ttt{x\_max}, \ttt{y\_min}, \ttt{y\_max}, \ttt{\$gmlcode\_fg}, ' // &
'\ttt{\$gmlcode\_bg}, \ttt{?draw\_base}, \ttt{?draw\_piecewise}, ' // &
'\ttt{?draw\_curve}, \ttt{?draw\_histogram}, \ttt{?draw\_errors}, ' // &
'\newline \ttt{?draw\_symbols}, \ttt{?fill\_curve}, \ttt{\$draw\_options}, ' // &
'\ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_string (var_str ("$draw_options"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: \ttt{\$draw\_options = "{\em <LaTeX\_code>}"} is a ' // &
'string variable that allows to set specific drawing options ' // &
'for plots and histograms. For more details see the \gamelan\ ' // &
'manual. (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \ttt{\$gmlcode\_fg}, \ttt{\$gmlcode\_bg}, \ttt{?draw\_base}, ' // &
'\newline \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_errors}, \ttt{?draw\_symbols}, \newline \ttt{\$fill\_options}, ' // &
'\ttt{?draw\_histogram}, \ttt{\$err\_options}, \ttt{\$symbol})'))
call var_list%append_string (var_str ("$err_options"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: \ttt{\$err\_options = "{\em <LaTeX\_code>}"} is a string ' // &
'variable that allows to set specific drawing options for errors ' // &
'in plots and histograms. For more details see the \gamelan\ ' // &
'manual. (cf. also \ttt{?normalize\_bins}, \ttt{\$obs\_label}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \ttt{\$gmlcode\_fg}, \ttt{\$gmlcode\_bg}, \ttt{?draw\_base}, ' // &
'\ttt{?draw\_piecewise}, \ttt{?fill\_curve}, \ttt{?draw\_histogram}, ' // &
'\ttt{?draw\_errors}, \newline \ttt{?draw\_symbols}, \ttt{\$fill\_options}, ' // &
'\ttt{?draw\_histogram}, \ttt{\$draw\_options}, \ttt{\$symbol})'))
call var_list%append_string (var_str ("$symbol"), &
intrinsic=.true., &
description=var_str ("Settings for \whizard's internal graphics " // &
'output: \ttt{\$symbol = "{\em <LaTeX\_code>}"} is a string ' // &
'variable for the symbols that should be used for plotting data ' // &
'points. (cf. also \ttt{\$obs\_label}, \ttt{?normalize\_bins}, ' // &
'\ttt{\$obs\_unit}, \ttt{\$title}, \ttt{\$description}, \ttt{\$x\_label}, ' // &
'\ttt{\$y\_label}, \newline \ttt{graph\_width\_mm}, \ttt{graph\_height\_mm}, ' // &
'\ttt{?y\_log}, \ttt{?x\_log}, \ttt{x\_min}, \ttt{x\_max}, \ttt{y\_min}, ' // &
'\ttt{y\_max}, \newline \ttt{\$gmlcode\_fg}, \ttt{\$gmlcode\_bg}, ' // &
'\ttt{?draw\_base}, \ttt{?draw\_piecewise}, \ttt{?fill\_curve}, ' // &
'\newline \ttt{?draw\_histogram}, \ttt{?draw\_curve}, \ttt{?draw\_errors}, ' // &
'\ttt{\$fill\_options}, \ttt{\$draw\_options}, \newline \ttt{\$err\_options}, ' // &
'\ttt{?draw\_symbols})'))
call var_list%append_log (&
var_str ("?analysis_file_only"), .false., &
intrinsic=.true., &
description=var_str ('Allows to specify that only \LaTeX\ files ' // &
"for \whizard's graphical analysis are written out, but not processed. " // &
'(cf. \ttt{compile\_analysis}, \ttt{write\_analysis})'))
end subroutine var_list_set_gamelan_defaults
@ %def var_list_set_gamelan_defaults
@ FastJet parameters and friends
<<Variables: var list: TBP>>=
procedure :: set_clustering_defaults => var_list_set_clustering_defaults
<<Variables: procedures>>=
subroutine var_list_set_clustering_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_int (&
var_str ("kt_algorithm"), &
kt_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the ' // &
'interfaced external \fastjet\ package. (cf. also ' // &
'\ttt{cambridge\_[for\_passive\_]algorithm}, ' // &
'\ttt{plugin\_algorithm}, ' // &
'\newline\ttt{genkt\_[for\_passive\_]algorithm}, ' // &
'\ttt{ee\_[gen]kt\_algorithm}, \ttt{jet\_r})'))
call var_list%append_int (&
var_str ("cambridge_algorithm"), &
cambridge_algorithm, intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the interfaced ' // &
'external \fastjet\ package. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_for\_passive\_algorithm}, \ttt{plugin\_algorithm}, ' // &
'\ttt{genkt\_[for\_passive\_]algorithm}, \ttt{ee\_[gen]kt\_algorithm}, ' // &
'\ttt{jet\_r})'))
call var_list%append_int (&
var_str ("antikt_algorithm"), &
antikt_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the interfaced ' // &
'external \fastjet\ package. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_[for\_passive\_]algorithm}, \ttt{plugin\_algorithm}, ' // &
'\ttt{genkt\_[for\_passive\_]algorithm}, \ttt{ee\_[gen]kt\_algorithm}, ' // &
'\ttt{jet\_r})'))
call var_list%append_int (&
var_str ("genkt_algorithm"), &
genkt_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the interfaced ' // &
'external \fastjet\ package. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_for\_passive\_algorithm}, \ttt{plugin\_algorithm}, ' // &
'\ttt{genkt\_for\_passive\_algorithm}, \ttt{ee\_[gen]kt\_algorithm}, ' // &
'\ttt{jet\_r})'))
call var_list%append_int (&
var_str ("cambridge_for_passive_algorithm"), &
cambridge_for_passive_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the interfaced ' // &
'external \fastjet\ package. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_algorithm}, \ttt{plugin\_algorithm}, \newline ' // &
'\ttt{genkt\_[for\_passive\_]algorithm}, \ttt{ee\_[gen]kt\_algorithm}, ' // &
'\ttt{jet\_r})'))
call var_list%append_int (&
var_str ("genkt_for_passive_algorithm"), &
genkt_for_passive_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the interfaced ' // &
'external \fastjet\ package. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_for\_passive\_algorithm}, \ttt{plugin\_algorithm}, ' // &
'\ttt{genkt\_algorithm}, \ttt{ee\_[gen]kt\_algorithm}, \ttt{jet\_r})'))
call var_list%append_int (&
var_str ("ee_kt_algorithm"), &
ee_kt_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the interfaced ' // &
'external \fastjet\ package. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_[for\_passive\_]algorithm}, \ttt{plugin\_algorithm}, ' // &
'\ttt{genkt\_[for\_passive\_]algorithm}, \ttt{ee\_genkt\_algorithm}, ' // &
'\ttt{jet\_r})'))
call var_list%append_int (&
var_str ("ee_genkt_algorithm"), &
ee_genkt_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the interfaced ' // &
'external \fastjet\ package. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_[for\_passive\_]algorithm}, \ttt{plugin\_algorithm}, ' // &
'\ttt{genkt\_[for\_passive\_]algorithm}, \ttt{ee\_kt\_algorithm}, ' // &
'\ttt{jet\_r})'))
call var_list%append_int (&
var_str ("plugin_algorithm"), &
plugin_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('Specifies a jet algorithm for the ($\to$) ' // &
'\ttt{jet\_algorithm} command, used in the ($\to$) \ttt{cluster} ' // &
'subevent function. At the moment only available for the interfaced ' // &
'external \fastjet\ package. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_for\_passive\_algorithm}, \newline ' // &
'\ttt{genkt\_[for\_passive\_]algorithm}, \ttt{ee\_[gen]kt\_algorithm}, ' // &
'\ttt{jet\_r})'))
call var_list%append_int (&
var_str ("undefined_jet_algorithm"), &
undefined_jet_algorithm, &
intrinsic = .true., locked = .true., &
description=var_str ('This is just a place holder for any kind of jet ' // &
'jet algorithm that is not further specified. (cf. also \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_for\_passive\_algorithm}, \newline ' // &
'\ttt{genkt\_[for\_passive\_]algorithm}, \ttt{ee\_[gen]kt\_algorithm}, ' // &
'\ttt{jet\_r}, \ttt{plugin\_algorithm})'))
call var_list%append_int (&
var_str ("jet_algorithm"), undefined_jet_algorithm, &
intrinsic = .true., &
description=var_str ('Variable that allows to set the type of ' // &
'jet algorithm when using the external \fastjet\ library. It ' // &
'accepts one of the following algorithms: ($\to$) \ttt{kt\_algorithm}, ' // &
'\newline ($\to$) \ttt{cambridge\_[for\_passive\_]algorithm}, ' // &
'($\to$) \ttt{antikt\_algorithm}, ($\to$) \ttt{plugin\_algorithm}, ' // &
'($\to$) \ttt{genkt\_[for\_passive\_]algorithm}, ($\to$) ' // &
'\ttt{ee\_[gen]kt\_algorithm}). (cf. also \ttt{cluster}, ' // &
'\ttt{jet\_p}, \ttt{jet\_r}, \ttt{jet\_ycut})'))
call var_list%append_real (&
var_str ("jet_r"), 0._default, &
intrinsic = .true., &
description=var_str ('Value for the distance measure $R$ used in ' // &
'the (non-Cambridge) algorithms that are available via the interface ' // &
'to the \fastjet\ package. (cf. also \ttt{cluster}, \ttt{combine}, ' // &
'\ttt{jet\_algorithm}, \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_[for\_passive\_]algorithm}, \ttt{antikt\_algorithm}, ' // &
'\newline \ttt{plugin\_algorithm}, \ttt{genkt\_[for\_passive\_]algorithm}, ' // &
'\ttt{ee\_[gen]kt\_algorithm}, \ttt{jet\_p}, \newline\ttt{jet\_ycut})'))
call var_list%append_real (&
var_str ("jet_p"), 0._default, &
intrinsic = .true., &
description=var_str ('Value for the exponent of the distance measure $R$ in ' // &
'the generalized $k_T$ algorithms that are available via the interface ' // &
'to the \fastjet\ package. (cf. also \ttt{cluster}, \ttt{combine}, ' // &
'\ttt{jet\_algorithm}, \ttt{kt\_algorithm}, ' // &
'\ttt{cambridge\_[for\_passive\_]algorithm}, \ttt{antikt\_algorithm}, ' // &
'\newline \ttt{plugin\_algorithm}, \ttt{genkt\_[for\_passive\_]algorithm}, ' // &
'\ttt{ee\_[gen]kt\_algorithm}, \ttt{jet\_r}, \newline\ttt{jet\_ycut})'))
call var_list%append_real (&
var_str ("jet_ycut"), 0._default, &
intrinsic = .true., &
description=var_str ('Value for the $y$ separation measure used in ' // &
'the Cambridge-Aachen algorithms that are available via the interface ' // &
'to the \fastjet\ package. (cf. also \ttt{cluster}, \ttt{combine}, ' // &
'\ttt{kt\_algorithm}, \ttt{jet\_algorithm}, ' // &
'\ttt{cambridge\_[for\_passive\_]algorithm}, \ttt{antikt\_algorithm}, ' // &
'\newline \ttt{plugin\_algorithm}, \ttt{genkt\_[for\_passive\_]algorithm}, ' // &
'\ttt{ee\_[gen]kt\_algorithm}, \ttt{jet\_p}, \newline\ttt{jet\_r})'))
call var_list%append_log (&
var_str ("?keep_flavors_when_clustering"), .false., &
intrinsic = .true., &
description=var_str ('The logical variable \ttt{?keep\_flavors\_when\_clustering ' // &
'= true/false} specifies whether the flavor of a jet should be ' // &
'kept during \ttt{cluster} when a jet consists of one quark and ' // &
'zero or more gluons. Especially useful for cuts on b-tagged ' // &
'jets (cf. also \ttt{cluster}).'))
end subroutine var_list_set_clustering_defaults
@ %def var_list_set_clustering_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_eio_defaults => var_list_set_eio_defaults
<<Variables: procedures>>=
subroutine var_list_set_eio_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_string (var_str ("$sample"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable to set the (base) name ' // &
'of the event output format, e.g. \ttt{\$sample = "foo"} will ' // &
'result in an intrinsic binary format event file \ttt{foo.evx}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{simulate}, \ttt{hepevt}, ' // &
'\ttt{ascii}, \ttt{athena}, \ttt{debug}, \ttt{long}, \ttt{short}, ' // &
'\ttt{hepmc}, \ttt{lhef}, \ttt{lha}, \ttt{stdhep}, \ttt{stdhep\_up}, ' // &
'\ttt{\$sample\_normalization}, \ttt{?sample\_pacify}, \ttt{sample\_max\_tries})'))
call var_list%append_string (var_str ("$sample_normalization"), var_str ("auto"),&
intrinsic=.true., &
description=var_str ('String variable that allows to set the ' // &
'normalization of generated events. There are four options: ' // &
'option \ttt{"1"} (events normalized to one), \ttt{"1/n"} (sum ' // &
'of all events in a sample normalized to one), \ttt{"sigma"} ' // &
'(events normalized to the cross section of the process), and ' // &
'\ttt{"sigma/n"} (sum of all events normalized to the cross ' // &
'section). The default is \ttt{"auto"} where unweighted events ' // &
'are normalized to one, and weighted ones to the cross section. ' // &
'(cf. also \ttt{simulate}, \ttt{\$sample}, \ttt{sample\_format}, ' // &
'\ttt{?sample\_pacify}, \ttt{sample\_max\_tries}, \ttt{sample\_split\_n\_evt}, ' // &
'\ttt{sample\_split\_n\_kbytes})'))
call var_list%append_log (var_str ("?sample_pacify"), .false., &
intrinsic=.true., &
description=var_str ('Flag, mainly for debugging purposes: suppresses ' // &
'numerical noise in the output of a simulation. (cf. also \ttt{simulate}, ' // &
'\ttt{\$sample}, \ttt{sample\_format}, \ttt{\$sample\_normalization}, ' // &
'\ttt{sample\_max\_tries}, \ttt{sample\_split\_n\_evt}, ' // &
'\ttt{sample\_split\_n\_kbytes})'))
call var_list%append_log (var_str ("?sample_select"), .true., &
intrinsic=.true., &
description=var_str ('Logical that determines whether a selection should ' // &
'be applied to the output event format or not. If set to \ttt{false} a ' // &
'selection is only considered for the evaluation of observables. (cf. ' // &
'\ttt{select}, \ttt{selection}, \ttt{analysis})'))
call var_list%append_int (var_str ("sample_max_tries"), 10000, &
intrinsic = .true., &
description=var_str ('Integer variable that sets the maximal ' // &
'number of tries for generating a single event. The event might ' // &
'be vetoed because of a very low unweighting efficiency, errors ' // &
'in the event transforms like decays, shower, matching, hadronization ' // &
'etc. (cf. also \ttt{simulate}, \ttt{\$sample}, \ttt{sample\_format}, ' // &
'\ttt{?sample\_pacify}, \ttt{\$sample\_normalization}, ' // &
'\ttt{sample\_split\_n\_evt}, \newline\ttt{sample\_split\_n\_kbytes})'))
call var_list%append_int (var_str ("sample_split_n_evt"), 0, &
intrinsic = .true., &
description=var_str ('When generating events, this integer parameter ' // &
'\ttt{sample\_split\_n\_evt = {\em <num>}} gives the number \ttt{{\em ' // &
'<num>}} of breakpoints in the event files, i.e. it splits the ' // &
'event files into \ttt{{\em <num>} + 1} parts. The parts are ' // &
'denoted by \ttt{{\em <proc\_name>}.{\em <split\_index>}.{\em ' // &
'<evt\_extension>}}. Here, \ttt{{\em <split\_index>}} is an integer ' // &
'running from \ttt{0} to \ttt{{\em <num>}}. The start can be ' // &
'reset by ($\to$) \ttt{sample\_split\_index}. (cf. also \ttt{simulate}, ' // &
'\ttt{\$sample}, \ttt{sample\_format}, \ttt{sample\_max\_tries}, ' // &
'\ttt{\$sample\_normalization}, \ttt{?sample\_pacify}, ' // &
'\ttt{sample\_split\_n\_kbytes})'))
call var_list%append_int (var_str ("sample_split_n_kbytes"), 0, &
intrinsic = .true., &
description=var_str ('When generating events, this integer parameter ' // &
'\ttt{sample\_split\_n\_kbytes = {\em <num>}} limits the file ' // &
'size of event files. Whenever an event file has exceeded this ' // &
'size, counted in kilobytes, the following events will be written ' // &
'to a new file. The naming conventions are the same as for ' // &
'\ttt{sample\_split\_n\_evt}. (cf. also \ttt{simulate}, \ttt{\$sample}, ' // &
'\ttt{sample\_format}, \ttt{sample\_max\_tries}, \ttt{\$sample\_normalization}, ' // &
'\ttt{?sample\_pacify})'))
call var_list%append_int (var_str ("sample_split_index"), 0, &
intrinsic = .true., &
description=var_str ('Integer number that gives the starting ' // &
'index \ttt{sample\_split\_index = {\em <split\_index>}} for ' // &
'the numbering of event samples \ttt{{\em <proc\_name>}.{\em ' // &
'<split\_index>}.{\em <evt\_extension>}} split by the \ttt{sample\_split\_n\_evt ' // &
'= {\em <num>}}. The index runs from \ttt{{\em <split\_index>}} ' // &
'to \newline \ttt{{\em <split\_index>} + {\em <num>}}. (cf. also \ttt{simulate}, ' // &
'\ttt{\$sample}, \ttt{sample\_format}, \newline\ttt{\$sample\_normalization}, ' // &
'\ttt{sample\_max\_tries}, \ttt{?sample\_pacify})'))
call var_list%append_string (var_str ("$rescan_input_format"), var_str ("raw"), &
intrinsic=.true., &
description=var_str ('String variable that allows to set the ' // &
'event format of the event file that is to be rescanned by the ' // &
'($\to$) \ttt{rescan} command.'))
call var_list%append_log (var_str ("?read_raw"), .true., &
intrinsic=.true., &
description=var_str ('This flag demands \whizard\ to (try to) ' // &
'read events (from the internal binary format) first before ' // &
'generating new ones. (cf. \ttt{simulate}, \ttt{?write\_raw}, ' // &
'\ttt{\$sample}, \ttt{sample\_format})'))
call var_list%append_log (var_str ("?write_raw"), .true., &
intrinsic=.true., &
description=var_str ("Flag to write out events in \whizard's " // &
'internal binary format. (cf. \ttt{simulate}, \ttt{?read\_raw}, ' // &
'\ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_raw"), var_str ("evx"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_raw ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
"to which events in \whizard's internal format are written. If " // &
'not set, the default file name and suffix is \ttt{{\em <process\_name>}.evx}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_default"), var_str ("evt"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_default ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in a the standard \whizard\ verbose ASCII format ' // &
'are written. If not set, the default file name and suffix is ' // &
'\ttt{{\em <process\_name>}.evt}. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample})'))
call var_list%append_string (var_str ("$debug_extension"), var_str ("debug"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$debug\_extension ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in a long verbose format with debugging information ' // &
'are written. If not set, the default file name and suffix is ' // &
'\ttt{{\em <process\_name>}.debug}. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample}, \ttt{?debug\_process}, \ttt{?debug\_transforms}, ' // &
'\ttt{?debug\_decay}, \ttt{?debug\_verbose})'))
call var_list%append_log (var_str ("?debug_process"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether process information ' // &
'will be displayed in the ASCII debug event format ($\to$) \ttt{debug}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample}, \ttt{\$debug\_extension}, ' // &
'\ttt{?debug\_decay}, \ttt{?debug\_transforms}, \ttt{?debug\_verbose})'))
call var_list%append_log (var_str ("?debug_transforms"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether information ' // &
'about event transforms will be displayed in the ASCII debug ' // &
'event format ($\to$) \ttt{debug}. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample}, \ttt{?debug\_decay}, \ttt{\$debug\_extension}, ' // &
'\ttt{?debug\_process}, \ttt{?debug\_verbose})'))
call var_list%append_log (var_str ("?debug_decay"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether decay information ' // &
'will be displayed in the ASCII debug event format ($\to$) \ttt{debug}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample}, \ttt{\$debug\_extension}, ' // &
'\ttt{?debug\_process}, \ttt{?debug\_transforms}, \ttt{?debug\_verbose})'))
call var_list%append_log (var_str ("?debug_verbose"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether extensive verbose ' // &
'information will be included in the ASCII debug event format ' // &
'($\to$) \ttt{debug}. (cf. also \ttt{sample\_format}, \ttt{\$sample}, ' // &
'\ttt{\$debug\_extension}, \ttt{?debug\_decay}, \ttt{?debug\_transforms}, ' // &
'\ttt{?debug\_process})'))
call var_list%append_string (var_str ("$dump_extension"), var_str ("pset.dat"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$dump\_extension ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
"to which events in \whizard's internal particle set format " // &
'are written. If not set, the default file name and suffix is ' // &
'\ttt{{\em <process\_name>}.pset.dat}. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample}, \ttt{dump}, \ttt{?dump\_compressed}, ' // &
'\ttt{?dump\_screen}, \ttt{?dump\_summary}, \ttt{?dump\_weights})'))
call var_list%append_log (var_str ("?dump_compressed"), .false., &
intrinsic=.true., &
description=var_str ('Flag that, if set to \ttt{true}, issues ' // &
'a very compressed and clear version of the \ttt{dump} ($\to$) ' // &
'event format. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample}, \ttt{dump}, \ttt{\$dump\_extension}, ' // &
'\ttt{?dump\_screen}, \ttt{?dump\_summary}, \ttt{?dump\_weights})'))
call var_list%append_log (var_str ("?dump_weights"), .false., &
intrinsic=.true., &
description=var_str ('Flag that, if set to \ttt{true}, includes ' // &
'cross sections, weights and excess in the \ttt{dump} ($\to$) ' // &
'event format. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample}, \ttt{dump}, \ttt{?dump\_compressed}, ' // &
'\ttt{\$dump\_extension}, \ttt{?dump\_screen}, \ttt{?dump\_summary})'))
call var_list%append_log (var_str ("?dump_summary"), .false., &
intrinsic=.true., &
description=var_str ('Flag that, if set to \ttt{true}, includes ' // &
'a summary with momentum sums for incoming and outgoing particles ' // &
'as well as for beam remnants in the \ttt{dump} ($\to$) ' // &
'event format. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample}, \ttt{dump}, \ttt{?dump\_compressed}, ' // &
'\ttt{\$dump\_extension}, \ttt{?dump\_screen}, \ttt{?dump\_weights})'))
call var_list%append_log (var_str ("?dump_screen"), .false., &
intrinsic=.true., &
description=var_str ('Flag that, if set to \ttt{true}, outputs ' // &
'events for the \ttt{dump} ($\to$) event format on screen ' // &
' instead of to a file. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample}, \ttt{dump}, \ttt{?dump\_compressed}, ' // &
'\ttt{\$dump\_extension}, \ttt{?dump\_summary}, \ttt{?dump\_weights})'))
call var_list%append_log (var_str ("?hepevt_ensure_order"), .false., &
intrinsic=.true., &
description=var_str ('Flag to ensure that the particle set confirms ' // &
'the HEPEVT standard. This involves some copying and reordering ' // &
'to guarantee that mothers and daughters are always next to ' // &
'each other. Usually this is not necessary.'))
call var_list%append_string (var_str ("$extension_hepevt"), var_str ("hepevt"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_hepevt ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the \whizard\ version 1 style HEPEVT ASCII ' // &
'format are written. If not set, the default file name and suffix ' // &
'is \ttt{{\em <process\_name>}.hepevt}. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_ascii_short"), &
var_str ("short.evt"), intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_ascii\_short ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the so called short variant of the \whizard\ ' // &
'version 1 style HEPEVT ASCII format are written. If not set, ' // &
'the default file name and suffix is \ttt{{\em <process\_name>}.short.evt}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_ascii_long"), &
var_str ("long.evt"), intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_ascii\_long ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the so called long variant of the \whizard\ ' // &
'version 1 style HEPEVT ASCII format are written. If not set, ' // &
'the default file name and suffix is \ttt{{\em <process\_name>}.long.evt}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_athena"), &
var_str ("athena.evt"), intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_athena ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the ATHENA file format are written. If not ' // &
'set, the default file name and suffix is \ttt{{\em <process\_name>}.athena.evt}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_mokka"), &
var_str ("mokka.evt"), intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_mokka ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the MOKKA format are written. If not set, ' // &
'the default file name and suffix is \ttt{{\em <process\_name>}.mokka.evt}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$lhef_version"), var_str ("2.0"), &
intrinsic = .true., &
description=var_str ('Specifier for the Les Houches Accord (LHEF) ' // &
'event format files with XML headers to discriminate among different ' // &
'versions of this format. (cf. also \ttt{\$sample}, \ttt{sample\_format}, ' // &
'\ttt{lhef}, \ttt{\$lhef\_extension}, \ttt{\$lhef\_extension}, ' // &
'\ttt{?lhef\_write\_sqme\_prc}, \ttt{?lhef\_write\_sqme\_ref}, ' // &
'\ttt{?lhef\_write\_sqme\_alt})'))
call var_list%append_string (var_str ("$lhef_extension"), var_str ("lhe"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$lhef\_extension ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the LHEF format are written. If not set, ' // &
'the default file name and suffix is \ttt{{\em <process\_name>}.lhe}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample}, \ttt{lhef}, ' // &
'\ttt{\$lhef\_extension}, \ttt{\$lhef\_version}, \ttt{?lhef\_write\_sqme\_prc}, ' // &
'\ttt{?lhef\_write\_sqme\_ref}, \ttt{?lhef\_write\_sqme\_alt})'))
call var_list%append_log (var_str ("?lhef_write_sqme_prc"), .true., &
intrinsic = .true., &
description=var_str ('Flag that decides whether in the ($\to$) ' // &
'\ttt{lhef} event format the weights of the squared matrix element ' // &
'of the corresponding process shall be written in the LHE file. ' // &
'(cf. also \ttt{\$sample}, \ttt{sample\_format}, \ttt{lhef}, ' // &
'\ttt{\$lhef\_extension}, \ttt{\$lhef\_extension}, \ttt{?lhef\_write\_sqme\_ref}, ' // &
'\newline \ttt{?lhef\_write\_sqme\_alt})'))
call var_list%append_log (var_str ("?lhef_write_sqme_ref"), .false., &
intrinsic = .true., &
description=var_str ('Flag that decides whether in the ($\to$) ' // &
'\ttt{lhef} event format reference weights of the squared matrix ' // &
'element shall be written in the LHE file. (cf. also \ttt{\$sample}, ' // &
'\ttt{sample\_format}, \ttt{lhef}, \ttt{\$lhef\_extension}, \ttt{\$lhef\_extension}, ' // &
'\ttt{?lhef\_write\_sqme\_prc}, \ttt{?lhef\_write\_sqme\_alt})'))
call var_list%append_log (var_str ("?lhef_write_sqme_alt"), .true., &
intrinsic = .true., &
description=var_str ('Flag that decides whether in the ($\to$) ' // &
'\ttt{lhef} event format alternative weights of the squared matrix ' // &
'element shall be written in the LHE file. (cf. also \ttt{\$sample}, ' // &
'\ttt{sample\_format}, \ttt{lhef}, \ttt{\$lhef\_extension}, \ttt{\$lhef\_extension}, ' // &
'\ttt{?lhef\_write\_sqme\_prc}, \ttt{?lhef\_write\_sqme\_ref})'))
call var_list%append_string (var_str ("$extension_lha"), var_str ("lha"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_lha ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the (deprecated) LHA format are written. ' // &
'If not set, the default file name and suffix is \ttt{{\em <process\_name>}.lha}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_hepmc"), var_str ("hepmc"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_hepmc ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the HepMC format are written. If not set, ' // &
'the default file name and suffix is \ttt{{\em <process\_name>}.hepmc}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_log (var_str ("?hepmc_output_cross_section"), .false., &
intrinsic = .true., &
description=var_str ('Flag for the HepMC event format that allows ' // &
'to write out the cross section (and error) from the integration ' // &
'together with each HepMC event. This can be used by programs ' // &
'like Rivet to scale histograms according to the cross section. ' // &
'(cf. also \ttt{hepmc})'))
call var_list%append_log (var_str ("?hepmc3_hepmc2mode"), .false., &
intrinsic = .true., &
description=var_str ('Flag for the HepMC event format that allows ' // &
'to use HepMC3 to write in HepMC2 backwards compatibility mode. ' // &
'This option has no effect when HepMC2 is linked. ' // &
'(cf. also \ttt{hepmc})'))
call var_list%append_string (var_str ("$extension_lcio"), var_str ("slcio"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_lcio ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the LCIO format are written. If not set, ' // &
'the default file name and suffix is \ttt{{\em <process\_name>}.slcio}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_stdhep"), var_str ("hep"), &
intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_stdhep ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the StdHEP format via the HEPEVT common ' // &
'block are written. If not set, the default file name and suffix ' // &
'is \ttt{{\em <process\_name>}.hep}. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_stdhep_up"), &
var_str ("up.hep"), intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_stdhep\_up ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the StdHEP format via the HEPRUP/HEPEUP ' // &
'common blocks are written. \ttt{{\em <process\_name>}.up.hep} ' // &
'is the default file name and suffix, if this variable not set. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_stdhep_ev4"), &
var_str ("ev4.hep"), intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_stdhep\_ev4 ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the StdHEP format via the HEPEVT/HEPEV4 ' // &
'common blocks are written. \ttt{{\em <process\_name>}.up.hep} ' // &
'is the default file name and suffix, if this variable not set. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_hepevt_verb"), &
var_str ("hepevt.verb"), intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_hepevt\_verb ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the \whizard\ version 1 style extended or ' // &
'verbose HEPEVT ASCII format are written. If not set, the default ' // &
'file name and suffix is \ttt{{\em <process\_name>}.hepevt.verb}. ' // &
'(cf. also \ttt{sample\_format}, \ttt{\$sample})'))
call var_list%append_string (var_str ("$extension_lha_verb"), &
var_str ("lha.verb"), intrinsic=.true., &
description=var_str ('String variable that allows via \ttt{\$extension\_lha\_verb ' // &
'= "{\em <suffix>}"} to specify the suffix for the file \ttt{name.suffix} ' // &
'to which events in the (deprecated) extended or verbose LHA ' // &
'format are written. If not set, the default file name and suffix ' // &
'is \ttt{{\em <process\_name>}.lha.verb}. (cf. also \ttt{sample\_format}, ' // &
'\ttt{\$sample})'))
end subroutine var_list_set_eio_defaults
@ %def var_list_set_eio_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_shower_defaults => var_list_set_shower_defaults
<<Variables: procedures>>=
subroutine var_list_set_shower_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_log (var_str ("?allow_shower"), .true., &
intrinsic=.true., &
description=var_str ('Master flag to switch on (initial and ' // &
'final state) parton shower, matching/merging as an event ' // &
'transform. As a default, it is switched on. (cf. also \ttt{?ps\_ ' // &
'....}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str ("?ps_fsr_active"), .false., &
intrinsic=.true., &
description=var_str ('Flag that switches final-state QCD radiation ' // &
'(FSR) on. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, ' // &
'\ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str ("?ps_isr_active"), .false., &
intrinsic=.true., &
description=var_str ('Flag that switches initial-state QCD ' // &
'radiation (ISR) on. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ' // &
'...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str ("?ps_taudec_active"), .false., &
intrinsic=.true., &
description=var_str ('Flag to switch on $\tau$ decays, at ' // &
'the moment only via the included external package \ttt{TAUOLA} ' // &
'and \ttt{PHOTOS}. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ' // &
'...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str ("?muli_active"), .false., &
intrinsic=.true., &
description=var_str ("Master flag that switches on \whizard's " // &
'module for multiple interaction with interleaved QCD parton ' // &
'showers for hadron colliders. Note that this feature is still ' // &
'experimental. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ' // &
'...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...})'))
call var_list%append_string (var_str ("$shower_method"), var_str ("WHIZARD"), &
intrinsic=.true., &
description=var_str ('String variable that allows to specify ' // &
'which parton shower is being used, the default, \ttt{"WHIZARD"}, ' // &
'is one of the in-house showers of \whizard. Other possibilities ' // &
'at the moment are only \ttt{"PYTHIA6"}.'))
call var_list%append_log (var_str ("?shower_verbose"), .false., &
intrinsic=.true., &
description=var_str ('Flag to switch on verbose messages when ' // &
'using shower and/or hadronization. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...},'))
call var_list%append_string (var_str ("$ps_PYTHIA_PYGIVE"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable that allows to pass options ' // &
'for tunes etc. to the attached \pythia\ parton shower or hadronization, ' // &
'e.g.: \ttt{\$ps\_PYTHIA\_PYGIVE = "MSTJ(41)=1"}. (cf. also ' // &
'\newline \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_string (var_str ("$ps_PYTHIA8_config"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable that allows to pass options ' // &
'for tunes etc. to the attached \pythia\ttt{8} parton shower or hadronization, ' // &
'e.g.: \ttt{\$ps\_PYTHIA8\_config = "PartonLevel:MPI = off"}. (cf. also ' // &
'\newline \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_string (var_str ("$ps_PYTHIA8_config_file"), var_str (""), &
intrinsic=.true., &
description=var_str ('String variable that allows to pass a filename to a ' // &
'\pythia\ttt{8} configuration file.'))
call var_list%append_real (&
var_str ("ps_mass_cutoff"), 1._default, intrinsic = .true., &
description=var_str ('Real value that sets the QCD parton shower ' // &
'lower cutoff scale, where hadronization sets in. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (&
var_str ("ps_fsr_lambda"), 0.29_default, intrinsic = .true., &
description=var_str ('By this real parameter, the value of $\Lambda_{QCD}$ ' // &
'used in running $\alpha_s$ for time-like showers is set (except ' // &
'for showers in the decay of a resonance). (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (&
var_str ("ps_isr_lambda"), 0.29_default, intrinsic = .true., &
description=var_str ('By this real parameter, the value of $\Lambda_{QCD}$ ' // &
'used in running $\alpha_s$ for space-like showers is set. (cf. ' // &
'also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, ' // &
'\ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_int (&
var_str ("ps_max_n_flavors"), 5, intrinsic = .true., &
description=var_str ('This integer parameter sets the maxmimum ' // &
'number of flavors that can be produced in a QCD shower $g\to ' // &
'q\bar q$. It is also used as the maximal number of active flavors ' // &
'for the running of $\alpha_s$ in the shower (with a minimum ' // &
'of 3). (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str ("?ps_isr_alphas_running"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether a running ' // &
'$\alpha_s$ is taken in space-like QCD parton showers. (cf. ' // &
'also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, ' // &
'\ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str ("?ps_fsr_alphas_running"), .true., &
intrinsic=.true., &
description=var_str ('Flag that decides whether a running ' // &
'$\alpha_s$ is taken in time-like QCD parton showers. (cf. ' // &
'also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, ' // &
'\ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str ("ps_fixed_alphas"), &
0._default, intrinsic = .true., &
description=var_str ('This real parameter sets the value of $\alpha_s$ ' // &
'if it is (cf. $\to$ \ttt{?ps\_isr\_alphas\_running}, \newline ' // &
'\ttt{?ps\_fsr\_alphas\_running}) not running in initial and/or ' // &
'final-state QCD showers. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ' // &
'...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str ("?ps_isr_pt_ordered"), .false., &
intrinsic=.true., &
description=var_str ('By this flag, it can be switched between ' // &
'the analytic QCD ISR shower (\ttt{false}, default) and the ' // &
'$p_T$ ISR QCD shower (\ttt{true}). (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str ("?ps_isr_angular_ordered"), .true., &
intrinsic=.true., &
description=var_str ('If switched one, this flag forces opening ' // &
'angles of emitted partons in the QCD ISR shower to be strictly ' // &
'ordered, i.e. increasing towards the hard interaction. (cf. ' // &
'also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, ' // &
'\ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("ps_isr_primordial_kt_width"), 0._default, intrinsic = .true., &
description=var_str ('This real parameter sets the width $\sigma ' // &
'= \braket{k_T^2}$ for the Gaussian primordial $k_T$ distribution ' // &
'inside the hadron, given by: $\exp[-k_T^2/\sigma^2] k_T dk_T$. ' // &
'(cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("ps_isr_primordial_kt_cutoff"), 5._default, intrinsic = .true., &
description=var_str ('Real parameter that sets the upper cutoff ' // &
'for the primordial $k_T$ distribution inside a hadron. (cf. ' // &
'also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, ' // &
'\ttt{?hadronization\_active}, \ttt{?mlm\_ ...})'))
call var_list%append_real (var_str &
("ps_isr_z_cutoff"), 0.999_default, intrinsic = .true., &
description=var_str ('This real parameter allows to set the upper ' // &
'cutoff on the splitting variable $z$ in space-like QCD parton ' // &
'showers. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, ' // &
'\ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("ps_isr_minenergy"), 1._default, intrinsic = .true., &
description=var_str ('By this real parameter, the minimal effective ' // &
'energy (in the c.m. frame) of a time-like or on-shell-emitted ' // &
'parton in a space-like QCD shower is set. For a hard subprocess ' // &
'that is not in the rest frame, this number is roughly reduced ' // &
'by a boost factor $1/\gamma$ to the rest frame of the hard scattering ' // &
'process. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, ' // &
'\ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("ps_isr_tscalefactor"), 1._default, intrinsic = .true., &
description=var_str ('The $Q^2$ scale of the hard scattering ' // &
'process is multiplied by this real factor to define the maximum ' // &
'parton virtuality allowed in time-like QCD showers. This does ' // &
'only apply to $t$- and $u$-channels, while for $s$-channel resonances ' // &
'the maximum virtuality is set by $m^2$. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_log (var_str &
("?ps_isr_only_onshell_emitted_partons"), .false., intrinsic=.true., &
description=var_str ('This flag if set true sets all emitted ' // &
'partons off space-like showers on-shell, i.e. it would not allow ' // &
'associated time-like showers. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?hadronization\_active})'))
end subroutine var_list_set_shower_defaults
@ %def var_list_set_shower_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_hadronization_defaults => var_list_set_hadronization_defaults
<<Variables: procedures>>=
subroutine var_list_set_hadronization_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_log &
(var_str ("?allow_hadronization"), .true., intrinsic=.true., &
description=var_str ('Master flag to switch on hadronization ' // &
'as an event transform. As a default, it is switched on. (cf. ' // &
'also \ttt{?ps\_ ....}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, ' // &
'\ttt{?hadronization\_active})'))
call var_list%append_log &
(var_str ("?hadronization_active"), .false., intrinsic=.true., &
description=var_str ('Master flag to switch hadronization (through ' // &
'the attached \pythia\ package) on or off. As a default, it is ' // &
'off. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...})'))
call var_list%append_string &
(var_str ("$hadronization_method"), var_str ("PYTHIA6"), intrinsic = .true., &
description=var_str ("Determines whether \whizard's own " // &
"hadronization or the (internally included) \pythiasix\ should be used."))
call var_list%append_real &
(var_str ("hadron_enhanced_fraction"), 0.01_default, intrinsic = .true., &
description=var_str ('Fraction of Lund strings that break with enhanced ' // &
'width. [not yet active]'))
call var_list%append_real &
(var_str ("hadron_enhanced_width"), 2.0_default, intrinsic = .true., &
description=var_str ('Enhancement factor for the width of breaking ' // &
'Lund strings. [not yet active]'))
end subroutine var_list_set_hadronization_defaults
@ %def var_list_set_hadronization_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_tauola_defaults => var_list_set_tauola_defaults
<<Variables: procedures>>=
subroutine var_list_set_tauola_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_log (&
var_str ("?ps_tauola_photos"), .false., intrinsic=.true., &
description=var_str ('Flag to switch on \ttt{PHOTOS} for photon ' // &
'showering inside the \ttt{TAUOLA} package. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?ps\_taudec\_active})'))
call var_list%append_log (&
var_str ("?ps_tauola_transverse"), .false., intrinsic=.true., &
description=var_str ('Flag to switch transverse $\tau$ polarization ' // &
'on or off for Higgs decays into $\tau$ leptons. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?ps\_taudec\_active})'))
call var_list%append_log (&
var_str ("?ps_tauola_dec_rad_cor"), .true., intrinsic=.true., &
description=var_str ('Flag to switch radiative corrections for ' // &
'$\tau$ decays in \ttt{TAUOLA} on or off. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?ps\_taudec\_active})'))
call var_list%append_int (&
var_str ("ps_tauola_dec_mode1"), 0, intrinsic = .true., &
description=var_str ('Integer code to request a specific $\tau$ ' // &
'decay within \ttt{TAUOLA} for the decaying $\tau$, and -- ' // &
'in correlated decays -- for the second $\tau$. For more information ' // &
'cf. the comments in the code or the \ttt{TAUOLA} manual. ' // &
'(cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...}, \ttt{?ps\_taudec\_active})'))
call var_list%append_int (&
var_str ("ps_tauola_dec_mode2"), 0, intrinsic = .true., &
description=var_str ('Integer code to request a specific $\tau$ ' // &
'decay within \ttt{TAUOLA} for the decaying $\tau$, and -- ' // &
'in correlated decays -- for the second $\tau$. For more information ' // &
'cf. the comments in the code or the \ttt{TAUOLA} manual. ' // &
'(cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...}, \ttt{?ps\_taudec\_active})'))
call var_list%append_real (&
var_str ("ps_tauola_mh"), 125._default, intrinsic = .true., &
description=var_str ('Real option to set the Higgs mass for Higgs ' // &
'decays into $\tau$ leptons in the interface to \ttt{TAUOLA}. ' // &
'(cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...}, \ttt{?ps\_taudec\_active})'))
call var_list%append_real (&
var_str ("ps_tauola_mix_angle"), 90._default, intrinsic = .true., &
description=var_str ('Option to set the mixing angle between ' // &
'scalar and pseudoscalar Higgs bosons for Higgs decays into $\tau$ ' // &
'leptons in the interface to \ttt{TAUOLA}. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{?mlm\_ ...}, \ttt{?ps\_taudec\_active})'))
call var_list%append_log (&
var_str ("?ps_tauola_pol_vector"), .false., intrinsic = .true., &
description=var_str ('Flag to decide whether for transverse $\tau$ ' // &
'polarization, polarization information should be taken from ' // &
'\ttt{TAUOLA} or not. The default is just based on random numbers. ' // &
'(cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{?mlm\_ ...}, \ttt{?ps\_taudec\_active})'))
end subroutine var_list_set_tauola_defaults
@ %def var_list_set_tauola_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_mlm_matching_defaults => var_list_set_mlm_matching_defaults
<<Variables: procedures>>=
subroutine var_list_set_mlm_matching_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_log (var_str ("?mlm_matching"), .false., &
intrinsic=.true., &
description=var_str ('Master flag to switch on MLM (LO) jet ' // &
'matching between hard matrix elements and the QCD parton ' // &
'shower. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, ' // &
'\ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_Qcut_ME"), 0._default, intrinsic = .true., &
description=var_str ('Real parameter that in the MLM jet matching ' // &
'between hard matrix elements and QCD parton shower sets a possible ' // &
'virtuality cut on jets from the hard matrix element. (cf. also ' // &
'\ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ' // &
'...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_Qcut_PS"), 0._default, intrinsic = .true., &
description=var_str ('Real parameter that in the MLM jet matching ' // &
'between hard matrix elements and QCD parton shower sets a possible ' // &
'virtuality cut on jets from the parton shower. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_ptmin"), 0._default, intrinsic = .true., &
description=var_str ('This real parameter sets a minimal $p_T$ ' // &
'that enters the $y_{cut}$ jet clustering measure in the MLM ' // &
'jet matching between hard matrix elements and QCD parton showers. ' // &
'(cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_etamax"), 0._default, intrinsic = .true., &
description=var_str ('This real parameter sets a maximal pseudorapidity ' // &
'that enters the MLM jet matching between hard matrix elements ' // &
'and QCD parton showers. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ' // &
'...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_Rmin"), 0._default, intrinsic = .true., &
description=var_str ('Real parameter that sets a minimal $R$ ' // &
'distance value that enters the $y_{cut}$ jet clustering measure ' // &
'in the MLM jet matching between hard matrix elements and QCD ' // &
'parton showers. (cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ' // &
'...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_Emin"), 0._default, intrinsic = .true., &
description=var_str ('Real parameter that sets a minimal energy ' // &
'$E_{min}$ value as an infrared cutoff in the MLM jet matching ' // &
'between hard matrix elements and QCD parton showers. (cf. also ' // &
'\ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ' // &
'...}, \ttt{?hadronization\_active})'))
call var_list%append_int (var_str &
("mlm_nmaxMEjets"), 0, intrinsic = .true., &
description=var_str ('This integer sets the maximal number of ' // &
'jets that are available from hard matrix elements in the MLM ' // &
'jet matching between hard matrix elements and QCD parton shower. ' // &
'(cf. also \ttt{?allow\_shower}, \ttt{?ps\_ ...}, \ttt{\$ps\_ ' // &
'...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_ETclusfactor"), 0.2_default, intrinsic = .true., &
description=var_str ('This real parameter is a factor that enters ' // &
'the calculation of the $y_{cut}$ measure for jet clustering ' // &
'after the parton shower in the MLM jet matching between hard ' // &
'matrix elements and QCD parton showers. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_ETclusminE"), 5._default, intrinsic = .true., &
description=var_str ('This real parameter is a minimal energy ' // &
'that enters the calculation of the $y_{cut}$ measure for jet ' // &
'clustering after the parton shower in the MLM jet matching between ' // &
'hard matrix elements and QCD parton showers. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_etaclusfactor"), 1._default, intrinsic = .true., &
description=var_str ('This real parameter is a factor that enters ' // &
'the calculation of the $y_{cut}$ measure for jet clustering ' // &
'after the parton shower in the MLM jet matching between hard ' // &
'matrix elements and QCD parton showers. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_Rclusfactor"), 1._default, intrinsic = .true., &
description=var_str ('This real parameter is a factor that enters ' // &
'the calculation of the $y_{cut}$ measure for jet clustering ' // &
'after the parton shower in the MLM jet matching between hard ' // &
'matrix elements and QCD parton showers. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
call var_list%append_real (var_str &
("mlm_Eclusfactor"), 1._default, intrinsic = .true., &
description=var_str ('This real parameter is a factor that enters ' // &
'the calculation of the $y_{cut}$ measure for jet clustering ' // &
'after the parton shower in the MLM jet matching between hard ' // &
'matrix elements and QCD parton showers. (cf. also \ttt{?allow\_shower}, ' // &
'\ttt{?ps\_ ...}, \ttt{\$ps\_ ...}, \ttt{mlm\_ ...}, \ttt{?hadronization\_active})'))
end subroutine var_list_set_mlm_matching_defaults
@ %def var_list_set_mlm_matching_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_powheg_matching_defaults => &
var_list_set_powheg_matching_defaults
<<Variables: procedures>>=
subroutine var_list_set_powheg_matching_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_log (var_str ("?powheg_matching"), &
.false., intrinsic = .true., &
description=var_str ('Activates Powheg matching. Needs to be ' // &
'combined with the \ttt{?combined\_nlo\_integration}-method.'))
call var_list%append_log (var_str ("?powheg_use_singular_jacobian"), &
.false., intrinsic = .true., &
description=var_str ('This allows to give a different ' // &
'normalization of the Jacobian, resulting in an alternative ' // &
'POWHEG damping in the singular regions.'))
call var_list%append_int (var_str ("powheg_grid_size_xi"), &
5, intrinsic = .true., &
description=var_str ('Number of $\xi$ points in the POWHEG grid.'))
call var_list%append_int (var_str ("powheg_grid_size_y"), &
5, intrinsic = .true., &
description=var_str ('Number of $y$ points in the POWHEG grid.'))
call var_list%append_int (var_str ("powheg_grid_sampling_points"), &
500000, intrinsic = .true., &
description=var_str ('Number of calls used to initialize the ' // &
'POWHEG grid.'))
call var_list%append_real (var_str ("powheg_pt_min"), &
1._default, intrinsic = .true., &
description=var_str ('Lower $p_T$-cut-off for the POWHEG ' // &
'hardest emission.'))
call var_list%append_real (var_str ("powheg_lambda"), &
LAMBDA_QCD_REF, intrinsic = .true., &
description=var_str ('Reference scale of the $\alpha_s$ evolution ' // &
'in the POWHEG matching algorithm.'))
call var_list%append_log (var_str ("?powheg_rebuild_grids"), &
.false., intrinsic = .true., &
description=var_str ('If set to \ttt{true}, the existing POWHEG ' // &
'grid is discarded and a new one is generated.'))
call var_list%append_log (var_str ("?powheg_test_sudakov"), &
.false., intrinsic = .true., &
description=var_str ('Performs an internal consistency check ' // &
'on the POWHEG event generation.'))
call var_list%append_log (var_str ("?powheg_disable_sudakov"), &
.false., intrinsic = .true., &
description=var_str ('This flag allows to set the Sudakov form ' // &
'factor to one. This effectively results in a version of ' // &
'the matrix-element method (MEM) at NLO.'))
end subroutine var_list_set_powheg_matching_defaults
@ %def var_list_set_powheg_matching_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_openmp_defaults => var_list_set_openmp_defaults
<<Variables: procedures>>=
subroutine var_list_set_openmp_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_log (var_str ("?omega_openmp"), &
openmp_is_active (), &
intrinsic=.true., &
description=var_str ('Flag to switch on or off OpenMP multi-threading ' // &
"for \oMega\ matrix elements. (cf. also \ttt{\$method}, \ttt{\$omega\_flag})"))
call var_list%append_log (var_str ("?openmp_is_active"), &
openmp_is_active (), &
locked=.true., intrinsic=.true., &
description=var_str ('Flag to switch on or off OpenMP multi-threading ' // &
'for \whizard. (cf. also \ttt{?openmp\_logging}, \ttt{openmp\_num\_threads}, ' // &
'\ttt{openmp\_num\_threads\_default}, \ttt{?omega\_openmp})'))
call var_list%append_int (var_str ("openmp_num_threads_default"), &
openmp_get_default_max_threads (), &
locked=.true., intrinsic=.true., &
description=var_str ('Integer parameter that shows the number ' // &
'of default OpenMP threads for multi-threading. Note that this ' // &
'parameter can only be accessed, but not reset by the user. (cf. ' // &
'also \ttt{?openmp\_logging}, \ttt{openmp\_num\_threads}, \ttt{?omega\_openmp})'))
call var_list%append_int (var_str ("openmp_num_threads"), &
openmp_get_max_threads (), &
intrinsic=.true., &
description=var_str ('Integer parameter that sets the number ' // &
'of OpenMP threads for multi-threading. (cf. also \ttt{?openmp\_logging}, ' // &
'\ttt{openmp\_num\_threads\_default}, \ttt{?omega\_openmp})'))
call var_list%append_log (var_str ("?openmp_logging"), &
.true., intrinsic=.true., &
description=var_str ('This logical -- when set to \ttt{false} ' // &
'-- suppresses writing out messages about OpenMP parallelization ' // &
'(number of used threads etc.) on screen and into the logfile ' // &
'(default name \ttt{whizard.log}) for the whole \whizard\ run. ' // &
'Mainly for debugging purposes. (cf. also \ttt{?logging}, ' // &
'\ttt{?mpi\_logging})'))
end subroutine var_list_set_openmp_defaults
@ %def var_list_set_openmp_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_mpi_defaults => var_list_set_mpi_defaults
<<Variables: procedures>>=
subroutine var_list_set_mpi_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_log (var_str ("?mpi_logging"), &
.false., intrinsic=.true., &
description=var_str('This logical -- when set to \ttt{false} ' // &
'-- suppresses writing out messages about MPI parallelization ' // &
'(number of used workers etc.) on screen and into the logfile ' // &
'(default name \ttt{whizard.log}) for the whole \whizard\ run. ' // &
'Mainly for debugging purposes. (cf. also \ttt{?logging}, ' // &
'\ttt{?openmp\_logging})'))
end subroutine var_list_set_mpi_defaults
@ %def var_list_set_mpi_defaults
@
<<Variables: var list: TBP>>=
procedure :: set_nlo_defaults => var_list_set_nlo_defaults
<<Variables: procedures>>=
subroutine var_list_set_nlo_defaults (var_list)
class(var_list_t), intent(inout) :: var_list
call var_list%append_string (var_str ("$born_me_method"), &
var_str (""), intrinsic = .true., &
description=var_str ("This string variable specifies the method " // &
"for the matrix elements to be used in the evaluation of the " // &
"Born part of the NLO computation. The default is the empty string, " // &
"i.e. the \ttt{\$method} being the intrinsic \oMega\ matrix element " // &
'generator (\ttt{"omega"}), other options ' // &
'are: \ttt{"ovm"}, \ttt{"unit\_test"}, \ttt{"template"}, ' // &
'\ttt{"template\_unity"}, \ttt{"threshold"}, \ttt{"gosam"}, ' // &
'\ttt{"openloops"}. Note that this option is inoperative if ' // &
'no NLO calculation is specified in the process definition. ' // &
'If you want ot use different matrix element methods in a LO ' // &
'computation, use the usual \ttt{method} command. (cf. also ' // &
'\ttt{\$correlation\_me\_method}, ' // &
'\ttt{\$dglap\_me\_method}, \ttt{\$loop\_me\_method} and ' // &
'\ttt{\$real\_tree\_me\_method}.)'))
call var_list%append_string (var_str ("$loop_me_method"), &
var_str (""), intrinsic = .true., &
description=var_str ('This string variable specifies the method ' // &
'for the matrix elements to be used in the evaluation of the ' // &
'virtual part of the NLO computation. The default is the empty string,' // &
'i.e. the same as \ttt{\$method}. Working options are: ' // &
'\ttt{"threshold"}, \ttt{"openloops"}, \ttt{"recola"}, \ttt{gosam}. ' // &
'(cf. also \ttt{\$real\_tree\_me\_method}, \ttt{\$correlation\_me\_method} ' // &
'and \ttt{\$born\_me\_method}.)'))
call var_list%append_string (var_str ("$correlation_me_method"), &
var_str (""), intrinsic = .true., &
description=var_str ('This string variable specifies ' // &
'the method for the matrix elements to be used in the evaluation ' // &
'of the color (and helicity) correlated part of the NLO computation. ' // &
"The default is the same as the \ttt{\$method}, i.e. the intrinsic " // &
"\oMega\ matrix element generator " // &
'(\ttt{"omega"}), other options are: \ttt{"ovm"}, \ttt{"unit\_test"}, ' // &
'\ttt{"template"}, \ttt{"template\_unity"}, \ttt{"threshold"}, ' // &
'\ttt{"gosam"}, \ttt{"openloops"}. (cf. also ' // &
'\ttt{\$born\_me\_method}, \ttt{\$dglap\_me\_method}, ' // &
'\ttt{\$loop\_me\_method} and \newline' // &
'\ttt{\$real\_tree\_me\_method}.)'))
call var_list%append_string (var_str ("$real_tree_me_method"), &
var_str (""), intrinsic = .true., &
description=var_str ('This string variable specifies the method ' // &
'for the matrix elements to be used in the evaluation of the ' // &
'real part of the NLO computation. The default is the same as ' // &
'the \ttt{\$method}, i.e. the intrinsic ' // &
"\oMega\ matrix element generator " // &
'(\ttt{"omega"}), other options ' // &
'are: \ttt{"ovm"}, \ttt{"unit\_test"}, \ttt{"template"}, \ttt{"template\_unity"}, ' // &
'\ttt{"threshold"}, \ttt{"gosam"}, \ttt{"openloops"}. (cf. also ' // &
'\ttt{\$born\_me\_method}, \ttt{\$correlation\_me\_method}, ' // &
'\ttt{\$dglap\_me\_method} and \ttt{\$loop\_me\_method}.)'))
call var_list%append_string (var_str ("$dglap_me_method"), &
var_str (""), intrinsic = .true., &
description=var_str ('This string variable specifies the method ' // &
'for the matrix elements to be used in the evaluation of the ' // &
'DGLAP remnants of the NLO computation. The default is the same as ' // &
"\ttt{\$method}, i.e. the \oMega\ matrix element generator " // &
'(\ttt{"omega"}), other options ' // &
'are: \ttt{"ovm"}, \ttt{"unit\_test"}, \ttt{"template"}, \ttt{"template\_unity"}, ' // &
'\ttt{"threshold"}, \ttt{"gosam"}, \ttt{"openloops"}. (cf. also \newline' // &
'\ttt{\$born\_me\_method}, \ttt{\$correlation\_me\_method}, ' // &
'\ttt{\$loop\_me\_method} and \ttt{\$real\_tree\_me\_method}.)'))
call var_list%append_log (&
var_str ("?test_soft_limit"), .false., intrinsic = .true., &
- description=var_str ('Sets the fixed values $\tilde{\xi} = 0.0001$ ' // &
+ description=var_str ('Sets the fixed values $\tilde{\xi} = 0.00001$ ' // &
'and $y = 0.5$ as radiation variables. This way, only soft, ' // &
'but non-collinear phase space points are generated, which allows ' // &
'for testing subtraction in this region.'))
call var_list%append_log (&
var_str ("?test_coll_limit"), .false., intrinsic = .true., &
description=var_str ('Sets the fixed values $\tilde{\xi} = 0.5$ ' // &
- 'and $y = 0.999$ as radiation variables. This way, only collinear, ' // &
+ 'and $y = 0.9999999$ as radiation variables. This way, only collinear, ' // &
'but non-soft phase space points are generated, which allows ' // &
'for testing subtraction in this region. Can be combined with ' // &
'\ttt{?test\_soft\_limit} to probe soft-collinear regions.'))
call var_list%append_log (&
var_str ("?test_anti_coll_limit"), .false., intrinsic = .true., &
description=var_str ('Sets the fixed values $\tilde{\xi} = 0.5$ ' // &
- 'and $y = -0.999$ as radiation variables. This way, only anti-collinear, ' // &
+ 'and $y = -0.9999999$ as radiation variables. This way, only anti-collinear, ' // &
'but non-soft phase space points are generated, which allows ' // &
'for testing subtraction in this region. Can be combined with ' // &
'\ttt{?test\_soft\_limit} to probe soft-collinear regions.'))
call var_list%append_string (var_str ("$select_alpha_regions"), &
var_str (""), intrinsic = .true., &
description=var_str ('Fixes the $\alpha_r$ in the real ' // &
' subtraction component. Allows for testing in one individual ' // &
'singular region.'))
call var_list%append_string (var_str ("$virtual_selection"), &
var_str ("Full"), intrinsic = .true., &
description=var_str ('String variable to select either the full ' // &
'or only parts of the virtual components of an NLO calculation. ' // &
'Possible modes are \ttt{"Full"}, \ttt{"OLP"} and ' // &
'\ttt{"Subtraction."}. Mainly for debugging purposes.'))
call var_list%append_log (var_str ("?virtual_collinear_resonance_aware"), &
.true., intrinsic = .true., &
description=var_str ('This flag allows to switch between two ' // &
'different implementations of the collinear subtraction in the ' // &
'resonance-aware FKS setup.'))
call var_list%append_real (&
var_str ("blha_top_yukawa"), -1._default, intrinsic = .true., &
description=var_str ('If this value is set, the given value will ' // &
'be used as the top Yukawa coupling instead of the top mass. ' // &
'Note that having different values for $y_t$ and $m_t$ must be ' // &
'supported by your OLP-library and yield errors if this is not the case.'))
call var_list%append_string (var_str ("$blha_ew_scheme"), &
var_str ("alpha_qed"), intrinsic = .true., &
description=var_str ('String variable that transfers the electroweak ' // &
'renormalization scheme via BLHA to the one-loop provider. Possible ' // &
'values are \ttt{GF} or \ttt{Gmu} for the $G_\mu$ scheme, ' // &
'\ttt{alpha\_qed}, \ttt{alpha\_mz} and \ttt{alpha\_0} or ' // &
'\ttt{alpha\_thompson} for different schemes with $\alpha$ as input.'))
call var_list%append_int (var_str ("openloops_verbosity"), 1, &
intrinsic = .true., &
description=var_str ('Decides how much \openloops\ output is printed. ' // &
'Can have values 0, 1 and 2, where 2 is the highest verbosity level.'))
call var_list%append_log (var_str ("?openloops_use_cms"), &
.true., intrinsic = .true., &
description=var_str ('Activates the complex mass scheme in ' // &
'\openloops. (cf. also ' // &
'\ttt{openloos\_verbosity}, \ttt{\$method}, ' // &
'\ttt{?openloops\_switch\_off\_muon\_yukawa}, ' // &
'\ttt{openloops\_stability\_log}, \newline' // &
'\ttt{\$openloops\_extra\_cmd})'))
call var_list%append_int (var_str ("openloops_phs_tolerance"), 7, &
intrinsic = .true., &
description=var_str ('This integer parameter gives via ' // &
'\ttt{openloops\_phs\_tolerance = <n>} the relative numerical ' // &
'tolerance $10^{-n}$ for the momentum conservation of the ' // &
'external particles within \openloops. (cf. also ' // &
'\ttt{openloos\_verbosity}, \ttt{\$method}, ' // &
'\ttt{?openloops\_switch\_off\_muon\_yukawa}, ' // &
'\newline\ttt{openloops\_stability\_log}, ' // &
'\ttt{\$openloops\_extra\_cmd})'))
call var_list%append_int (var_str ("openloops_stability_log"), 0, &
intrinsic = .true., &
description=var_str ('Creates the directory \ttt{stability\_log} ' // &
'containing information about the performance of the \openloops ' // &
'matrix elements. Possible values are 0 (No output), 1 (On ' // &
'\ttt{finish()}-call), 2 (Adaptive) and 3 (Always).'))
call var_list%append_log (var_str ("?openloops_switch_off_muon_yukawa"), &
.false., intrinsic = .true., &
description=var_str ('Sets the Yukawa coupling of muons for ' // &
'\openloops\ to zero. (cf. also ' // &
'\ttt{openloos\_verbosity}, \ttt{\$method}, ' // &
'\ttt{?openloops\_use\_cms}, \ttt{openloops\_stability\_log}, ' // &
'\ttt{\$openloops\_extra\_cmd})'))
call var_list%append_string (var_str ("$openloops_extra_cmd"), &
var_str (""), intrinsic = .true., &
description=var_str ('String variable to transfer customized ' // &
'special commands to \openloops. The three supported examples ' // &
'\ttt{\$openloops\_extra\_command = "extra approx top/stop/not"} ' // &
'are for selection of subdiagrams in top production. (cf. also ' // &
'\ttt{\$method}, \ttt{openloos\_verbosity}, ' // &
'\ttt{?openloops\_use\_cms}, \ttt{openloops\_stability\_log}, ' // &
'\ttt{?openloops\_switch\_off\_muon\_yukawa})'))
call var_list%append_log (var_str ("?openloops_use_collier"), &
.true., intrinsic = .true., &
description=var_str ('Use \collier\ as the reduction method of ' // &
'\openloops. Otherwise, \ttt{CutTools} will be used. (cf. also ' // &
'\ttt{\$method}, \ttt{openloos\_verbosity}, ' // &
'\ttt{?openloops\_use\_cms}, \ttt{openloops\_stability\_log}, ' // &
'\ttt{?openloops\_switch\_off\_muon\_yukawa})'))
call var_list%append_log (var_str ("?disable_subtraction"), &
.false., intrinsic = .true., &
description=var_str ('Disables the subtraction of soft and collinear ' // &
'divergences from the real matrix element.'))
call var_list%append_real (var_str ("fks_dij_exp1"), &
1._default, intrinsic = .true., &
description=var_str ('Fine-tuning parameters of the FKS ' // &
'partition functions. The exact meaning depends on the mapping ' // &
'implementation. (cf. also \ttt{fks\_dij\_exp2}, ' // &
'\ttt{\$fks\_mapping\_type}, \ttt{fks\_xi\_min}, \ttt{fks\_y\_max})'))
call var_list%append_real (var_str ("fks_dij_exp2"), &
1._default, intrinsic = .true., &
description=var_str ('Fine-tuning parameters of the FKS ' // &
'partition functions. The exact meaning depends on the mapping ' // &
'implementation. (cf. also \ttt{fks\_dij\_exp1}, ' // &
'\ttt{\$fks\_mapping\_type}, \ttt{fks\_xi\_min}, \ttt{fks\_y\_max})'))
call var_list%append_real (var_str ("fks_xi_min"), &
0.0000001_default, intrinsic = .true., &
description=var_str ('Real parameter for the FKS ' // &
'phase space that sets the numerical lower value of the $\xi$ ' // &
'variable. (cf. also \ttt{fks\_dij\_exp1}, ' // &
'\ttt{fks\_dij\_exp2}, \ttt{\$fks\_mapping\_type}, \ttt{fks\_y\_max})'))
call var_list%append_real (var_str ("fks_y_max"), &
1._default, intrinsic = .true., &
description=var_str ('Real parameter for the FKS ' // &
'phase space that sets the numerical upper value of the $y$ ' // &
'variable. (cf. also \ttt{fks\_dij\_exp1}, ' // &
'\ttt{\$fks\_mapping\_type}, \ttt{fks\_dij\_exp2}, \ttt{fks\_y\_max})'))
call var_list%append_log (var_str ("?vis_fks_regions"), &
.false., intrinsic = .true., &
description=var_str ('Logical variable that, if set to ' // &
'\ttt{true}, generates \LaTeX\ code and executes it into a PDF ' // &
' to produce a table of all singular FKS regions and their ' // &
' flavor structures. The default is \ttt{false}.'))
call var_list%append_real (var_str ("fks_xi_cut"), &
1.0_default, intrinsic = .true., &
description = var_str ('Real paramter for the FKS ' // &
'phase space that applies a cut to $\xi$ variable with $0 < \xi_{\text{cut}}' // &
'\leq \xi_{\text{max}}$. The dependence on the parameter vanishes between ' // &
'real subtraction and integrated subtraction term.'))
call var_list%append_real (var_str ("fks_delta_o"), &
2._default, intrinsic = .true., &
description = var_str ('Real paramter for the FKS ' // &
'phase space that applies a cut to the $y$ variable with $0 < \delta_o \leq 2$. ' // &
'The dependence on the parameter vanishes between real subtraction and integrated ' // &
'subtraction term.'))
call var_list%append_real (var_str ("fks_delta_i"), &
2._default, intrinsic = .true., &
description = var_str ('Real paramter for the FKS ' // &
'phase space that applies a cut to the $y$ variable with $0 < \delta_{\mathrm{I}} \leq 2$ '// &
'for initial state singularities only. ' // &
'The dependence on the parameter vanishes between real subtraction and integrated ' // &
'subtraction term.'))
call var_list%append_string (var_str ("$fks_mapping_type"), &
var_str ("default"), intrinsic = .true., &
description=var_str ('Sets the FKS mapping type. Possible values ' // &
'are \ttt{"default"} and \ttt{"resonances"}. The latter option ' // &
'activates the resonance-aware subtraction mode and induces the ' // &
'generation of a soft mismatch component. (cf. also ' // &
'\ttt{fks\_dij\_exp1}, \ttt{fks\_dij\_exp2}, \ttt{fks\_xi\_min}, ' // &
'\ttt{fks\_y\_max})'))
call var_list%append_string (var_str ("$resonances_exclude_particles"), &
var_str ("default"), intrinsic = .true., &
description=var_str ('Accepts a string of particle names. These ' // &
'particles will be ignored when the resonance histories are generated. ' // &
'If \ttt{\$fks\_mapping\_type} is not \ttt{"resonances"}, this ' // &
'option does nothing.'))
call var_list%append_int (var_str ("alpha_power"), &
2, intrinsic = .true., &
description=var_str ('Fixes the electroweak coupling ' // &
'powers used by BLHA matrix element generators. Setting these ' // &
'values is necessary for the correct generation of OLP-files. ' // &
'Having inconsistent values yields to error messages by the corresponding ' // &
'OLP-providers.'))
call var_list%append_int (var_str ("alphas_power"), &
0, intrinsic = .true., &
description=var_str ('Fixes the strong coupling ' // &
'powers used by BLHA matrix element generators. Setting these ' // &
'values is necessary for the correct generation of OLP-files. ' // &
'Having inconsistent values yields to error messages by the corresponding ' // &
'OLP-providers.'))
call var_list%append_log (var_str ("?combined_nlo_integration"), &
.false., intrinsic = .true., &
description=var_str ('When this option is set to \ttt{true}, ' // &
'the NLO integration will not be performed in the separate components, ' // &
'but instead the sum of all components will be integrated directly. ' // &
'When fixed-order NLO events are requested, this integration ' // &
'mode is possible, but not necessary. However, it is necessary ' // &
'for POWHEG events.'))
call var_list%append_log (var_str ("?fixed_order_nlo_events"), &
.false., intrinsic = .true., &
description=var_str ('Induces the generation of fixed-order ' // &
'NLO events. Deprecated name: \ttt{?nlo\_fixed\_order}.'))
call var_list%append_log (var_str ("?check_event_weights_against_xsection"), &
.false., intrinsic = .true., &
description=var_str ('Activates an internal recording of event ' // &
'weights when unweighted events are generated. At the end of ' // &
'the simulation, the mean value of the weights and its standard ' // &
'deviation are displayed. This allows to cross-check event generation ' // &
'and integration, because the value displayed must be equal to ' // &
'the integration result.'))
call var_list%append_log (var_str ("?keep_failed_events"), &
.false., intrinsic = .true., &
description=var_str ('In the context of weighted event generation, ' // &
'if set to \ttt{true}, events with failed kinematics will be ' // &
'written to the event output with an associated weight of zero. ' // &
'This way, the total cross section can be reconstructed from the event output.'))
call var_list%append_int (var_str ("gks_multiplicity"), &
0, intrinsic = .true., &
description=var_str ('Jet multiplicity for the GKS merging scheme.'))
call var_list%append_string (var_str ("$gosam_filter_lo"), &
var_str (""), intrinsic = .true., &
description=var_str ('The filter string given to \gosam\ in order to ' // &
'filter out tree-level diagrams. (cf. also \ttt{\$gosam\_filter\_nlo}, ' // &
'\ttt{\$gosam\_symmetries})'))
call var_list%append_string (var_str ("$gosam_filter_nlo"), &
var_str (""), intrinsic = .true., &
description=var_str ('The same as \ttt{\$gosam\_filter\_lo}, but for ' // &
'loop matrix elements. (cf. also \ttt{\$gosam\_filter\_nlo}, ' // &
'\ttt{\$gosam\_symmetries})'))
call var_list%append_string (var_str ("$gosam_symmetries"), &
var_str ("family,generation"), intrinsic = .true., &
description=var_str ('String variable that is transferred to \gosam\ ' // &
'configuration file to determine whether certain helicity configurations ' // &
'are considered to be equal. Possible values are \ttt{flavour}, ' // &
'\ttt{family} etc. For more info see the \gosam\ manual.'))
call var_list%append_int (var_str ("form_threads"), &
2, intrinsic = .true., &
description=var_str ('The number of threads used by \gosam when ' // &
'matrix elements are evaluated using \ttt{FORM}'))
call var_list%append_int (var_str ("form_workspace"), &
1000, intrinsic = .true., &
description=var_str ('The size of the workspace \gosam requires ' // &
'from \ttt{FORM}. Inside \ttt{FORM}, it corresponds to the heap ' // &
'size used by the algebra processor.'))
call var_list%append_string (var_str ("$gosam_fc"), &
var_str (""), intrinsic = .true., &
description=var_str ('The Fortran compiler used by \gosam.'))
call var_list%append_real (&
var_str ("mult_call_real"), 1._default, &
intrinsic = .true., &
description=var_str ('(Real-valued) multiplier for the number ' // &
'of calls used in the integration of the real subtraction ' // &
'NLO component. This way, a higher accuracy can be achieved for ' // &
'the real component, while simultaneously avoiding redundant ' // &
'integration calls for the other components. (cf. also ' // &
'\ttt{mult\_call\_dglap}, \ttt{mult\_call\_virt})'))
call var_list%append_real (&
var_str ("mult_call_virt"), 1._default, &
intrinsic = .true., &
description=var_str ('(Real-valued) multiplier for the number ' // &
'of calls used in the integration of the virtual NLO ' // &
'component. This way, a higher accuracy can be achieved for ' // &
'this component, while simultaneously avoiding redundant ' // &
'integration calls for the other components. (cf. also ' // &
'\ttt{mult\_call\_dglap}, \ttt{mult\_call\_real})'))
call var_list%append_real (&
var_str ("mult_call_dglap"), 1._default, &
intrinsic = .true., &
description=var_str ('(Real-valued) multiplier for the number ' // &
'of calls used in the integration of the DGLAP remnant NLO ' // &
'component. This way, a higher accuracy can be achieved for ' // &
'this component, while simultaneously avoiding redundant ' // &
'integration calls for the other components. (cf. also ' // &
'\ttt{mult\_call\_real}, \ttt{mult\_call\_virt})'))
call var_list%append_string (var_str ("$dalitz_plot"), &
var_str (''), intrinsic = .true., &
description=var_str ('This string variable has two purposes: ' // &
'when different from the empty string, it switches on generation ' // &
'of the Dalitz plot file (ASCII tables) for the real emitters. ' // &
'The string variable itself provides the file name.'))
call var_list%append_string (var_str ("$nlo_correction_type"), &
var_str ("QCD"), intrinsic = .true., &
description=var_str ('String variable which sets the NLO correction ' // &
'type via \ttt{nlo\_correction\_type = "{\em <type>}"} to either ' // &
'\ttt{"QCD"} or \ttt{"QED"}, or to both with \ttt{\em{<type>}} ' // &
'set to \ttt{"Full"}.'))
call var_list%append_string (var_str ("$exclude_gauge_splittings"), &
var_str ("c:b:t:e2:e3"), intrinsic = .true., &
description=var_str ('String variable that allows via ' // &
'\ttt{\$exclude\_gauge\_splittings = "{\em <prt1>:<prt2>:\dots}"} ' // &
'to exclude fermion flavors from gluon/photon splitting into ' // &
'fermion pairs beyond LO. For example \ttt{\$exclude\_gauge\_splittings ' // &
'= "c:s:b:t"} would lead to \ttt{gl => u U} and \ttt{gl => d ' // &
'D} as possible splittings in QCD. It is important to keep in ' // &
'mind that only the particles listed in the string are excluded! ' // &
'In QED this string would additionally allow for all splittings into ' // &
'lepton pairs \ttt{A => l L}. Therefore, once set the variable ' // &
'acts as a replacement of the default value, not as an addition! ' // &
'Note: \ttt{"\em <prt>"} can be both particle or antiparticle. It ' // &
'will always exclude the corresponding fermion pair. An empty ' // &
'string allows for all fermion flavors to take part in the splitting! ' // &
'Also, particles included in an \ttt{alias} are not excluded by ' // &
'\ttt{\$exclude\_gauge\_splittings}!'))
call var_list%append_log (var_str ("?nlo_use_born_scale"), &
.false., intrinsic = .true., &
description=var_str ('Flag that decides whether a scale expression ' // &
'defined for the Born component of an NLO process shall be applied ' // &
'to all other components as well or not. ' // &
'(cf. also \ttt{?nlo\_cut\_all\_sqmes})'))
call var_list%append_log (var_str ("?nlo_cut_all_sqmes"), &
.false., intrinsic = .true., &
description=var_str ('Flag that decides whether in the case that ' // &
'some NLO component does not pass a cut, all other components ' // &
'shall be discarded for that phase space point as well or not. ' // &
'(cf. also \ttt{?nlo\_use\_born\_scale})'))
call var_list%append_log (var_str ("?nlo_use_real_partition"), &
.false., intrinsic = .true., &
description=var_str (' If set to \ttt{true}, the real matrix ' // &
'element is split into a finite and a singular part using a ' // &
'partition function $f$, such that $\mathcal{R} ' // &
'= [1-f(p_T^2)]\mathcal{R} + f(p_T^2)\mathcal{R} = ' // &
'\mathcal{R}_{\text{fin}} ' // &
'+ \mathcal{R}_{\text{sing}}$. The emission ' // &
'generation is then performed using $\mathcal{R}_{\text{sing}}$, ' // &
'whereas $\mathcal{R}_{\text{fin}}$ is treated separately. ' // &
'(cf. also \ttt{real\_partition\_scale})'))
call var_list%append_real (var_str ("real_partition_scale"), &
10._default, intrinsic = .true., &
description=var_str ('This real variable sets the invariant mass ' // &
'of the FKS pair used as a separator between the singular and the ' // &
'finite part of the real subtraction terms in an NLO calculation, ' // &
'e.g. in $e^+e^- \to ' // &
't\bar tj$. (cf. also \ttt{?nlo\_use\_real\_partition})'))
end subroutine var_list_set_nlo_defaults
@ %def var_list_set_nlo_defaults
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Observables}
In this module we define concrete variables and operators (observables)
that we want to support in expressions.
<<[[observables.f90]]>>=
<<File header>>
module observables
<<Use kinds>>
<<Use strings>>
use io_units
use diagnostics
use lorentz
use subevents
use variables
<<Standard module head>>
<<Observables: public>>
contains
<<Observables: procedures>>
end module observables
@ %def observables
@
\subsection{Process-specific variables}
We allow the user to set a numeric process ID for each declared process.
<<Observables: public>>=
public :: var_list_init_num_id
<<Observables: procedures>>=
subroutine var_list_init_num_id (var_list, proc_id, num_id)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: proc_id
integer, intent(in), optional :: num_id
call var_list_set_procvar_int (var_list, proc_id, &
var_str ("num_id"), num_id)
end subroutine var_list_init_num_id
@ %def var_list_init_num_id
@
Integration results are stored in special variables. They are
initialized by this subroutine. The values may or may not already
known.
Note: the values which are accessible are those that are unique for a
process with multiple MCI records. The rest has been discarded.
<<Observables: public>>=
public :: var_list_init_process_results
<<Observables: procedures>>=
subroutine var_list_init_process_results (var_list, proc_id, &
n_calls, integral, error, accuracy, chi2, efficiency)
type(var_list_t), intent(inout) :: var_list
type(string_t), intent(in) :: proc_id
integer, intent(in), optional :: n_calls
real(default), intent(in), optional :: integral, error, accuracy
real(default), intent(in), optional :: chi2, efficiency
call var_list_set_procvar_real (var_list, proc_id, &
var_str ("integral"), integral)
call var_list_set_procvar_real (var_list, proc_id, &
var_str ("error"), error)
end subroutine var_list_init_process_results
@ %def var_list_init_process_results
@
\subsection{Observables as Pseudo-Variables}
Unary and binary observables are different. Most unary observables
can be equally well evaluated for particle pairs. Binary observables
cannot be evaluated for single particles.
<<Observables: public>>=
public :: var_list_set_observables_unary
public :: var_list_set_observables_binary
<<Observables: procedures>>=
subroutine var_list_set_observables_unary (var_list, prt1)
type(var_list_t), intent(inout) :: var_list
type(prt_t), intent(in), target :: prt1
call var_list_append_obs1_iptr &
(var_list, var_str ("PDG"), obs_pdg1, prt1)
call var_list_append_obs1_iptr &
(var_list, var_str ("Hel"), obs_helicity1, prt1)
call var_list_append_obs1_iptr &
(var_list, var_str ("Ncol"), obs_n_col1, prt1)
call var_list_append_obs1_iptr &
(var_list, var_str ("Nacl"), obs_n_acl1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("M"), obs_signed_mass1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("M2"), obs_mass_squared1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("E"), obs_energy1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Px"), obs_px1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Py"), obs_py1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Pz"), obs_pz1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("P"), obs_p1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Pl"), obs_pl1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Pt"), obs_pt1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Theta"), obs_theta1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Phi"), obs_phi1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Rap"), obs_rap1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Eta"), obs_eta1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Theta_star"), obs_theta_star1, prt1)
call var_list_append_obs1_rptr &
(var_list, var_str ("Dist"), obs_dist1, prt1)
call var_list_append_uobs_real &
(var_list, var_str ("_User_obs_real"), prt1)
call var_list_append_uobs_int &
(var_list, var_str ("_User_obs_int"), prt1)
end subroutine var_list_set_observables_unary
subroutine var_list_set_observables_binary (var_list, prt1, prt2)
type(var_list_t), intent(inout) :: var_list
type(prt_t), intent(in), target :: prt1
type(prt_t), intent(in), optional, target :: prt2
call var_list_append_obs2_iptr &
(var_list, var_str ("PDG"), obs_pdg2, prt1, prt2)
call var_list_append_obs2_iptr &
(var_list, var_str ("Hel"), obs_helicity2, prt1, prt2)
call var_list_append_obs2_iptr &
(var_list, var_str ("Ncol"), obs_n_col2, prt1, prt2)
call var_list_append_obs2_iptr &
(var_list, var_str ("Nacl"), obs_n_acl2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("M"), obs_signed_mass2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("M2"), obs_mass_squared2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("E"), obs_energy2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Px"), obs_px2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Py"), obs_py2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Pz"), obs_pz2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("P"), obs_p2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Pl"), obs_pl2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Pt"), obs_pt2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Theta"), obs_theta2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Phi"), obs_phi2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Rap"), obs_rap2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Eta"), obs_eta2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Theta_star"), obs_theta_star2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("Dist"), obs_dist2, prt1, prt2)
call var_list_append_obs2_rptr &
(var_list, var_str ("kT"), obs_ktmeasure, prt1, prt2)
call var_list_append_uobs_real &
(var_list, var_str ("_User_obs_real"), prt1, prt2)
call var_list_append_uobs_int &
(var_list, var_str ("_User_obs_int"), prt1, prt2)
end subroutine var_list_set_observables_binary
@ %def var_list_set_observables_unary var_list_set_observables_binary
@
\subsection{Checks}
<<Observables: public>>=
public :: var_list_check_observable
<<Observables: procedures>>=
subroutine var_list_check_observable (var_list, name, type)
class(var_list_t), intent(in), target :: var_list
type(string_t), intent(in) :: name
integer, intent(inout) :: type
if (string_is_observable_id (name)) then
call msg_fatal ("Variable name '" // char (name) &
// "' is reserved for an observable")
type = V_NONE
return
end if
end subroutine var_list_check_observable
@ %def var_list_check_observable
@
Check if a variable name is defined as an observable:
<<Observables: procedures>>=
function string_is_observable_id (string) result (flag)
logical :: flag
type(string_t), intent(in) :: string
select case (char (string))
case ("PDG", "Hel", "Ncol", &
"M", "M2", "E", "Px", "Py", "Pz", "P", "Pl", "Pt", &
"Theta", "Phi", "Rap", "Eta", "Theta_star", "Dist", "kT")
flag = .true.
case default
flag = .false.
end select
end function string_is_observable_id
@ %def string_is_observable_id
@ Check for result and process variables.
<<Observables: public>>=
public :: var_list_check_result_var
<<Observables: procedures>>=
subroutine var_list_check_result_var (var_list, name, type)
class(var_list_t), intent(in), target :: var_list
type(string_t), intent(in) :: name
integer, intent(inout) :: type
if (string_is_integer_result_var (name)) type = V_INT
if (.not. var_list%contains (name)) then
if (string_is_result_var (name)) then
call msg_fatal ("Result variable '" // char (name) // "' " &
// "set without prior integration")
type = V_NONE
return
else if (string_is_num_id (name)) then
call msg_fatal ("Numeric process ID '" // char (name) // "' " &
// "set without process declaration")
type = V_NONE
return
end if
end if
end subroutine var_list_check_result_var
@ %def var_list_check_result_var
@
Check if a variable name is a result variable of integer type:
<<Observables: procedures>>=
function string_is_integer_result_var (string) result (flag)
logical :: flag
type(string_t), intent(in) :: string
type(string_t) :: buffer, name, separator
buffer = string
call split (buffer, name, "(", separator=separator) ! ")"
if (separator == "(") then
select case (char (name))
case ("num_id", "n_calls")
flag = .true.
case default
flag = .false.
end select
else
flag = .false.
end if
end function string_is_integer_result_var
@ %def string_is_integer_result_var
@
Check if a variable name is an integration-result variable:
<<Observables: procedures>>=
function string_is_result_var (string) result (flag)
logical :: flag
type(string_t), intent(in) :: string
type(string_t) :: buffer, name, separator
buffer = string
call split (buffer, name, "(", separator=separator) ! ")"
if (separator == "(") then
select case (char (name))
case ("integral", "error")
flag = .true.
case default
flag = .false.
end select
else
flag = .false.
end if
end function string_is_result_var
@ %def string_is_result_var
@
Check if a variable name is a numeric process ID:
<<Observables: procedures>>=
function string_is_num_id (string) result (flag)
logical :: flag
type(string_t), intent(in) :: string
type(string_t) :: buffer, name, separator
buffer = string
call split (buffer, name, "(", separator=separator) ! ")"
if (separator == "(") then
select case (char (name))
case ("num_id")
flag = .true.
case default
flag = .false.
end select
else
flag = .false.
end if
end function string_is_num_id
@ %def string_is_num_id
@
\subsection{Observables}
These are analogous to the unary and binary numeric functions listed
above. An observable takes the [[pval]] component(s) of its one or
two argument nodes and produces an integer or real value.
\subsubsection{Integer-valued unary observables}
The PDG code
<<Observables: procedures>>=
integer function obs_pdg1 (prt1) result (pdg)
type(prt_t), intent(in) :: prt1
pdg = prt_get_pdg (prt1)
end function obs_pdg1
@ %def obs_pdg
@ The helicity. The return value is meaningful only if the particle
is polarized, otherwise an invalid value is returned (-9).
<<Observables: procedures>>=
integer function obs_helicity1 (prt1) result (h)
type(prt_t), intent(in) :: prt1
if (prt_is_polarized (prt1)) then
h = prt_get_helicity (prt1)
else
h = -9
end if
end function obs_helicity1
@ %def obs_helicity1
@ The number of open color (anticolor) lines. The return value is meaningful
only if the particle is colorized (i.e., the subevent has been given color
information), otherwise the function returns zero.
<<Observables: procedures>>=
integer function obs_n_col1 (prt1) result (n)
type(prt_t), intent(in) :: prt1
if (prt_is_colorized (prt1)) then
n = prt_get_n_col (prt1)
else
n = 0
end if
end function obs_n_col1
integer function obs_n_acl1 (prt1) result (n)
type(prt_t), intent(in) :: prt1
if (prt_is_colorized (prt1)) then
n = prt_get_n_acl (prt1)
else
n = 0
end if
end function obs_n_acl1
@ %def obs_n_col1
@ %def obs_n_acl1
@
\subsubsection{Real-valued unary observables}
The invariant mass squared, obtained from the separately stored value.
<<Observables: procedures>>=
real(default) function obs_mass_squared1 (prt1) result (p2)
type(prt_t), intent(in) :: prt1
p2 = prt_get_msq (prt1)
end function obs_mass_squared1
@ %def obs_mass_squared1
@ The signed invariant mass, which is the signed square root of the
previous observable.
<<Observables: procedures>>=
real(default) function obs_signed_mass1 (prt1) result (m)
type(prt_t), intent(in) :: prt1
real(default) :: msq
msq = prt_get_msq (prt1)
m = sign (sqrt (abs (msq)), msq)
end function obs_signed_mass1
@ %def obs_signed_mass1
@ The particle energy
<<Observables: procedures>>=
real(default) function obs_energy1 (prt1) result (e)
type(prt_t), intent(in) :: prt1
e = energy (prt_get_momentum (prt1))
end function obs_energy1
@ %def obs_energy1
@ Particle momentum (components)
<<Observables: procedures>>=
real(default) function obs_px1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = vector4_get_component (prt_get_momentum (prt1), 1)
end function obs_px1
real(default) function obs_py1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = vector4_get_component (prt_get_momentum (prt1), 2)
end function obs_py1
real(default) function obs_pz1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = vector4_get_component (prt_get_momentum (prt1), 3)
end function obs_pz1
real(default) function obs_p1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = space_part_norm (prt_get_momentum (prt1))
end function obs_p1
real(default) function obs_pl1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = longitudinal_part (prt_get_momentum (prt1))
end function obs_pl1
real(default) function obs_pt1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = transverse_part (prt_get_momentum (prt1))
end function obs_pt1
@ %def obs_px1 obs_py1 obs_pz1
@ %def obs_p1 obs_pl1 obs_pt1
@ Polar and azimuthal angle (lab frame).
<<Observables: procedures>>=
real(default) function obs_theta1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = polar_angle (prt_get_momentum (prt1))
end function obs_theta1
real(default) function obs_phi1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = azimuthal_angle (prt_get_momentum (prt1))
end function obs_phi1
@ %def obs_theta1 obs_phi1
@ Rapidity and pseudorapidity
<<Observables: procedures>>=
real(default) function obs_rap1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = rapidity (prt_get_momentum (prt1))
end function obs_rap1
real(default) function obs_eta1 (prt1) result (p)
type(prt_t), intent(in) :: prt1
p = pseudorapidity (prt_get_momentum (prt1))
end function obs_eta1
@ %def obs_rap1 obs_eta1
@ Meaningless: Polar angle in the rest frame of the two arguments
combined.
<<Observables: procedures>>=
real(default) function obs_theta_star1 (prt1) result (dist)
type(prt_t), intent(in) :: prt1
call msg_fatal (" 'Theta_star' is undefined as unary observable")
dist = 0
end function obs_theta_star1
@ %def obs_theta_star1
@ [Obsolete] Meaningless: Polar angle in the rest frame of the 2nd argument.
<<XXX Observables: procedures>>=
real(default) function obs_theta_rf1 (prt1) result (dist)
type(prt_t), intent(in) :: prt1
call msg_fatal (" 'Theta_RF' is undefined as unary observable")
dist = 0
end function obs_theta_rf1
@ %def obs_theta_rf1
@ Meaningless: Distance on the $\eta$-$\phi$ cylinder.
<<Observables: procedures>>=
real(default) function obs_dist1 (prt1) result (dist)
type(prt_t), intent(in) :: prt1
call msg_fatal (" 'Dist' is undefined as unary observable")
dist = 0
end function obs_dist1
@ %def obs_dist1
@
\subsubsection{Integer-valued binary observables}
These observables are meaningless as binary functions.
<<Observables: procedures>>=
integer function obs_pdg2 (prt1, prt2) result (pdg)
type(prt_t), intent(in) :: prt1, prt2
call msg_fatal (" PDG_Code is undefined as binary observable")
pdg = 0
end function obs_pdg2
integer function obs_helicity2 (prt1, prt2) result (h)
type(prt_t), intent(in) :: prt1, prt2
call msg_fatal (" Helicity is undefined as binary observable")
h = 0
end function obs_helicity2
integer function obs_n_col2 (prt1, prt2) result (n)
type(prt_t), intent(in) :: prt1, prt2
call msg_fatal (" Ncol is undefined as binary observable")
n = 0
end function obs_n_col2
integer function obs_n_acl2 (prt1, prt2) result (n)
type(prt_t), intent(in) :: prt1, prt2
call msg_fatal (" Nacl is undefined as binary observable")
n = 0
end function obs_n_acl2
@ %def obs_pdg2
@ %def obs_helicity2
@ %def obs_n_col2
@ %def obs_n_acl2
@
\subsubsection{Real-valued binary observables}
The invariant mass squared, obtained from the separately stored value.
<<Observables: procedures>>=
real(default) function obs_mass_squared2 (prt1, prt2) result (p2)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p2 = prt_get_msq (prt)
end function obs_mass_squared2
@ %def obs_mass_squared2
@ The signed invariant mass, which is the signed square root of the
previous observable.
<<Observables: procedures>>=
real(default) function obs_signed_mass2 (prt1, prt2) result (m)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
real(default) :: msq
call prt_init_combine (prt, prt1, prt2)
msq = prt_get_msq (prt)
m = sign (sqrt (abs (msq)), msq)
end function obs_signed_mass2
@ %def obs_signed_mass2
@ The particle energy
<<Observables: procedures>>=
real(default) function obs_energy2 (prt1, prt2) result (e)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
e = energy (prt_get_momentum (prt))
end function obs_energy2
@ %def obs_energy2
@ Particle momentum (components)
<<Observables: procedures>>=
real(default) function obs_px2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p = vector4_get_component (prt_get_momentum (prt), 1)
end function obs_px2
real(default) function obs_py2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p = vector4_get_component (prt_get_momentum (prt), 2)
end function obs_py2
real(default) function obs_pz2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p = vector4_get_component (prt_get_momentum (prt), 3)
end function obs_pz2
real(default) function obs_p2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p = space_part_norm (prt_get_momentum (prt))
end function obs_p2
real(default) function obs_pl2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p = longitudinal_part (prt_get_momentum (prt))
end function obs_pl2
real(default) function obs_pt2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p = transverse_part (prt_get_momentum (prt))
end function obs_pt2
@ %def obs_px2 obs_py2 obs_pz2
@ %def obs_p2 obs_pl2 obs_pt2
@ Enclosed angle and azimuthal distance (lab frame).
<<Observables: procedures>>=
real(default) function obs_theta2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
p = enclosed_angle (prt_get_momentum (prt1), prt_get_momentum (prt2))
end function obs_theta2
real(default) function obs_phi2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p = azimuthal_distance (prt_get_momentum (prt1), prt_get_momentum (prt2))
end function obs_phi2
@ %def obs_theta2 obs_phi2
@ Rapidity and pseudorapidity distance
<<Observables: procedures>>=
real(default) function obs_rap2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
p = rapidity_distance &
(prt_get_momentum (prt1), prt_get_momentum (prt2))
end function obs_rap2
real(default) function obs_eta2 (prt1, prt2) result (p)
type(prt_t), intent(in) :: prt1, prt2
type(prt_t) :: prt
call prt_init_combine (prt, prt1, prt2)
p = pseudorapidity_distance &
(prt_get_momentum (prt1), prt_get_momentum (prt2))
end function obs_eta2
@ %def obs_rap2 obs_eta2
@ [This doesn't work! The principle of no common particle for momentum
combination prohibits us from combining a decay particle with the momentum
of its parent.] Polar angle in the rest frame of the 2nd argument.
<<XXX Observables: procedures>>=
real(default) function obs_theta_rf2 (prt1, prt2) result (theta)
type(prt_t), intent(in) :: prt1, prt2
theta = enclosed_angle_rest_frame &
(prt_get_momentum (prt1), prt_get_momentum (prt2))
end function obs_theta_rf2
@ %def obs_theta_rf2
@ Polar angle of the first particle in the rest frame of the two particles
combined.
<<Observables: procedures>>=
real(default) function obs_theta_star2 (prt1, prt2) result (theta)
type(prt_t), intent(in) :: prt1, prt2
theta = enclosed_angle_rest_frame &
(prt_get_momentum (prt1), &
prt_get_momentum (prt1) + prt_get_momentum (prt2))
end function obs_theta_star2
@ %def obs_theta_star2
@ Distance on the $\eta$-$\phi$ cylinder.
<<Observables: procedures>>=
real(default) function obs_dist2 (prt1, prt2) result (dist)
type(prt_t), intent(in) :: prt1, prt2
dist = eta_phi_distance &
(prt_get_momentum (prt1), prt_get_momentum (prt2))
end function obs_dist2
@ %def obs_dist2
@ Durham kT measure.
<<Observables: procedures>>=
real(default) function obs_ktmeasure (prt1, prt2) result (kt)
type(prt_t), intent(in) :: prt1, prt2
real (default) :: q2, e1, e2
! Normalized scale to one for now! (#67)
q2 = 1
e1 = energy (prt_get_momentum (prt1))
e2 = energy (prt_get_momentum (prt2))
kt = (2/q2) * min(e1**2,e2**2) * &
(1 - enclosed_angle_ct(prt_get_momentum (prt1), &
prt_get_momentum (prt2)))
end function obs_ktmeasure
@ %def obs_ktmeasure
Index: trunk/src/phase_space/phase_space.nw
===================================================================
--- trunk/src/phase_space/phase_space.nw (revision 8293)
+++ trunk/src/phase_space/phase_space.nw (revision 8294)
@@ -1,27620 +1,27622 @@
% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: phase space
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Phase Space}
\includemodulegraph{phase_space}
The abstract representation of a type that parameterizes phase space,
with methods for construction and evaluation.
\begin{description}
\item[phs\_base]
Abstract phase-space representation.
\end{description}
A simple implementation:
\begin{description}
\item[phs\_1none]
This implements a non-functional dummy module for the phase space.
A process which uses this module cannot be integrated. The purpose
of this module is to provide a placeholder for processes which do
not require phase-space evaluation. They may still allow for evaluating
matrix elements.
\item[phs\_single]
Parameterize the phase space of a single particle, i.e., the solid
angle. This is useful only for very restricted problems, but it
avoids the complexity of a generic approach in those trivial cases.
\end{description}
The standard implementation is called \emph{wood} phase space. It
consists of several auxiliary modules and the actual implementation
module.
\begin{description}
\item[mappings]
Generate invariant masses and decay angles from given
random numbers (or the inverse operation). Each mapping pertains to a
particular node in a phase-space tree. Different mappings account for
uniform distributions, resonances, zero-mass behavior, and so on.
\item[phs\_trees]
Phase space parameterizations for scattering
processes are defined recursively as if there was an initial particle
decaying. This module sets up a representation in terms of abstract
trees, where each node gets a unique binary number. Each tree is
stored as an array of branches, where integers indicate the
connections. This emulates pointers in a transparent way. Real
pointers would also be possible, but seem to be less efficient for
this particular case.
\item[phs\_forests]
The type defined by this module collects the
decay trees corresponding to a given process and the applicable
mappings. To set this up, a file is read which is either written by
the user or by the \textbf{cascades} module functions. The module
also contains the routines that evaluate phase space, i.e., generate
momenta from random numbers and back.
\item[cascades]
This module is a pseudo Feynman diagram generator with the
particular purpose of finding the phase space parameterizations best
suited for a given process. It uses a model file to set up the
possible vertices, generates all possible diagrams, identifies
resonances and singularities, and simplifies the list by merging
equivalent diagrams and dropping irrelevant ones. This process can be
controlled at several points by user-defined parameters. Note that it
depends on the particular values of particle masses, so it cannot be
done before reading the input file.
\item[phs\_wood]
Make the functionality available in form of an implementation of the
abstract phase-space type.
\item[phs\_fks]
Phase-space parameterization with modifications for the FKS scheme.
\end{description}
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Abstract phase-space module}
In this module we define an abstract base type (and a trivial test
implementation) for multi-channel phase-space parameterizations.
<<[[phs_base.f90]]>>=
<<File header>>
module phs_base
<<Use kinds>>
<<Use strings>>
use io_units
use constants, only: TWOPI, TWOPI4
use string_utils, only: split_string
use format_defs, only: FMT_19
use numeric_utils
use diagnostics
use md5
use physics_defs
use lorentz
use model_data
use flavors
use process_constants
<<Standard module head>>
<<PHS base: public>>
<<PHS base: types>>
<<PHS base: interfaces>>
contains
<<PHS base: procedures>>
end module phs_base
@ %def phs_base
@
\subsection{Phase-space channels}
The kinematics configuration may generate multiple parameterizations of phase
space. Some of those have specific properties, such as a resonance in the s
channel.
\subsubsection{Channel properties}
This is the abstract type for the channel properties. We need them as
a data transfer container, so everything is public and transparent.
<<PHS base: public>>=
public :: channel_prop_t
<<PHS base: types>>=
type, abstract :: channel_prop_t
contains
procedure (channel_prop_to_string), deferred :: to_string
generic :: operator (==) => is_equal
procedure (channel_eq), deferred :: is_equal
end type channel_prop_t
@ %def channel_prop_t
<<PHS base: interfaces>>=
abstract interface
function channel_prop_to_string (object) result (string)
import
class(channel_prop_t), intent(in) :: object
type(string_t) :: string
end function channel_prop_to_string
end interface
@ %def channel_prop_to_string
<<PHS base: interfaces>>=
abstract interface
function channel_eq (prop1, prop2) result (flag)
import
class(channel_prop_t), intent(in) :: prop1, prop2
logical :: flag
end function channel_eq
end interface
@ %def channel_prop_to_string
@
Here is a resonance as a channel property. Mass and width are stored
here in physical units.
<<PHS base: public>>=
public :: resonance_t
<<PHS base: types>>=
type, extends (channel_prop_t) :: resonance_t
real(default) :: mass = 0
real(default) :: width = 0
contains
procedure :: to_string => resonance_to_string
procedure :: is_equal => resonance_is_equal
end type resonance_t
@ %def resonance_t
@ Print mass and width.
<<PHS base: procedures>>=
function resonance_to_string (object) result (string)
class(resonance_t), intent(in) :: object
type(string_t) :: string
character(32) :: buffer
string = "resonant: m ="
write (buffer, "(" // FMT_19 // ")") object%mass
string = string // trim (buffer) // " GeV, w ="
write (buffer, "(" // FMT_19 // ")") object%width
string = string // trim (buffer) // " GeV"
end function resonance_to_string
@ %def resonance_to_string
@ Equality.
<<PHS base: procedures>>=
function resonance_is_equal (prop1, prop2) result (flag)
class(resonance_t), intent(in) :: prop1
class(channel_prop_t), intent(in) :: prop2
logical :: flag
select type (prop2)
type is (resonance_t)
flag = prop1%mass == prop2%mass .and. prop1%width == prop2%width
class default
flag = .false.
end select
end function resonance_is_equal
@ %def resonance_is_equal
@
This is the limiting case of a resonance, namely an on-shell particle.
We just store the mass in physical units.
<<PHS base: public>>=
public :: on_shell_t
<<PHS base: types>>=
type, extends (channel_prop_t) :: on_shell_t
real(default) :: mass = 0
contains
procedure :: to_string => on_shell_to_string
procedure :: is_equal => on_shell_is_equal
end type on_shell_t
@ %def on_shell_t
@ Print mass and width.
<<PHS base: procedures>>=
function on_shell_to_string (object) result (string)
class(on_shell_t), intent(in) :: object
type(string_t) :: string
character(32) :: buffer
string = "on shell: m ="
write (buffer, "(" // FMT_19 // ")") object%mass
string = string // trim (buffer) // " GeV"
end function on_shell_to_string
@ %def on_shell_to_string
@ Equality.
<<PHS base: procedures>>=
function on_shell_is_equal (prop1, prop2) result (flag)
class(on_shell_t), intent(in) :: prop1
class(channel_prop_t), intent(in) :: prop2
logical :: flag
select type (prop2)
type is (on_shell_t)
flag = prop1%mass == prop2%mass
class default
flag = .false.
end select
end function on_shell_is_equal
@ %def on_shell_is_equal
@
\subsubsection{Channel equivalences}
This type describes an equivalence. The current channel is equivalent
to channel [[c]]. The equivalence involves a permutation [[perm]] of
integration dimensions and, within each integration dimension, a
mapping [[mode]].
<<PHS base: types>>=
type :: phs_equivalence_t
integer :: c = 0
integer, dimension(:), allocatable :: perm
integer, dimension(:), allocatable :: mode
contains
<<PHS base: phs equivalence: TBP>>
end type phs_equivalence_t
@ %def phs_equivalence_t
@
The mapping modes are
<<PHS base: types>>=
integer, parameter, public :: &
EQ_IDENTITY = 0, EQ_INVERT = 1, EQ_SYMMETRIC = 2, EQ_INVARIANT = 3
@ %def EQ_IDENTITY EQ_INVERT EQ_SYMMETRIC
@ In particular, if a channel is equivalent to itself in the
[[EQ_SYMMETRIC]] mode, the integrand can be assumed to be symmetric
w.r.t.\ a reflection $x\to 1 - x$ of the correponding integration variable.
These are the associated tags, for output:
<<PHS base: types>>=
character, dimension(0:3), parameter :: TAG = ["+", "-", ":", "x"]
@ %def TAG
@ Write an equivalence.
<<PHS base: phs equivalence: TBP>>=
procedure :: write => phs_equivalence_write
<<PHS base: procedures>>=
subroutine phs_equivalence_write (object, unit)
class(phs_equivalence_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u, j
u = given_output_unit (unit)
write (u, "(5x,'=',1x,I0,1x)", advance = "no") object%c
if (allocated (object%perm)) then
write (u, "(A)", advance = "no") "("
do j = 1, size (object%perm)
if (j > 1) write (u, "(1x)", advance = "no")
write (u, "(I0,A1)", advance = "no") &
object%perm(j), TAG(object%mode(j))
end do
write (u, "(A)") ")"
else
write (u, "(A)")
end if
end subroutine phs_equivalence_write
@ %def phs_equivalence_write
@ Initialize an equivalence. This allocates the [[perm]] and [[mode]]
arrays with equal size.
<<PHS base: phs equivalence: TBP>>=
procedure :: init => phs_equivalence_init
<<PHS base: procedures>>=
subroutine phs_equivalence_init (eq, n_dim)
class(phs_equivalence_t), intent(out) :: eq
integer, intent(in) :: n_dim
allocate (eq%perm (n_dim), source = 0)
allocate (eq%mode (n_dim), source = EQ_IDENTITY)
end subroutine phs_equivalence_init
@ %def phs_equivalence_init
@
\subsubsection{Channel objects}
The channel entry holds (optionally) specific properties.
[[sf_channel]] is the structure-function channel that corresponds to this
phase-space channel. The structure-function channel may be set up with a
specific mapping that depends on the phase-space channel properties. (The
default setting is to leave the properties empty.)
<<PHS base: public>>=
public :: phs_channel_t
<<PHS base: types>>=
type :: phs_channel_t
class(channel_prop_t), allocatable :: prop
integer :: sf_channel = 1
type(phs_equivalence_t), dimension(:), allocatable :: eq
contains
<<PHS base: phs channel: TBP>>
end type phs_channel_t
@ %def phs_channel_t
@ Output.
<<PHS base: phs channel: TBP>>=
procedure :: write => phs_channel_write
<<PHS base: procedures>>=
subroutine phs_channel_write (object, unit)
class(phs_channel_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u, j
u = given_output_unit (unit)
write (u, "(1x,I0)", advance="no") object%sf_channel
if (allocated (object%prop)) then
write (u, "(1x,A)") char (object%prop%to_string ())
else
write (u, *)
end if
if (allocated (object%eq)) then
do j = 1, size (object%eq)
call object%eq(j)%write (u)
end do
end if
end subroutine phs_channel_write
@ %def phs_channel_write
@ Identify the channel with an s-channel resonance.
<<PHS base: phs channel: TBP>>=
procedure :: set_resonant => channel_set_resonant
<<PHS base: procedures>>=
subroutine channel_set_resonant (channel, mass, width)
class(phs_channel_t), intent(inout) :: channel
real(default), intent(in) :: mass, width
allocate (resonance_t :: channel%prop)
select type (prop => channel%prop)
type is (resonance_t)
prop%mass = mass
prop%width = width
end select
end subroutine channel_set_resonant
@ %def channel_set_resonant
@ Identify the channel with an on-shell particle.
<<PHS base: phs channel: TBP>>=
procedure :: set_on_shell => channel_set_on_shell
<<PHS base: procedures>>=
subroutine channel_set_on_shell (channel, mass)
class(phs_channel_t), intent(inout) :: channel
real(default), intent(in) :: mass
allocate (on_shell_t :: channel%prop)
select type (prop => channel%prop)
type is (on_shell_t)
prop%mass = mass
end select
end subroutine channel_set_on_shell
@ %def channel_set_on_shell
@
\subsection{Property collection}
We can set up a list of all distinct channel properties for a given
set of channels.
<<PHS base: public>>=
public :: phs_channel_collection_t
<<PHS base: types>>=
type :: prop_entry_t
integer :: i = 0
class(channel_prop_t), allocatable :: prop
type(prop_entry_t), pointer :: next => null ()
end type prop_entry_t
type :: phs_channel_collection_t
integer :: n = 0
type(prop_entry_t), pointer :: first => null ()
contains
<<PHS base: phs channel collection: TBP>>
end type phs_channel_collection_t
@ %def prop_entry_t
@ %def phs_channel_collection_t
@ Finalizer for the list.
<<PHS base: phs channel collection: TBP>>=
procedure :: final => phs_channel_collection_final
<<PHS base: procedures>>=
subroutine phs_channel_collection_final (object)
class(phs_channel_collection_t), intent(inout) :: object
type(prop_entry_t), pointer :: entry
do while (associated (object%first))
entry => object%first
object%first => entry%next
deallocate (entry)
end do
end subroutine phs_channel_collection_final
@ %def phs_channel_collection_final
@ Output.
Note: eliminating the [[string]] auxiliary triggers an ICE in gfortran 4.7.2.
<<PHS base: phs channel collection: TBP>>=
procedure :: write => phs_channel_collection_write
<<PHS base: procedures>>=
subroutine phs_channel_collection_write (object, unit)
class(phs_channel_collection_t), intent(in) :: object
integer, intent(in), optional :: unit
type(prop_entry_t), pointer :: entry
type(string_t) :: string
integer :: u
u = given_output_unit (unit)
entry => object%first
do while (associated (entry))
if (allocated (entry%prop)) then
string = entry%prop%to_string ()
write (u, "(1x,I0,1x,A)") entry%i, char (string)
else
write (u, "(1x,I0)") entry%i
end if
entry => entry%next
end do
end subroutine phs_channel_collection_write
@ %def phs_channel_collection_write
@ Push a new property to the stack if it is not yet included.
Simultaneously, set the [[sf_channel]] entry in the phase-space
channel object to the index of the matching entry, or the new entry if
there was no match.
<<PHS base: phs channel collection: TBP>>=
procedure :: push => phs_channel_collection_push
<<PHS base: procedures>>=
subroutine phs_channel_collection_push (coll, channel)
class(phs_channel_collection_t), intent(inout) :: coll
type(phs_channel_t), intent(inout) :: channel
type(prop_entry_t), pointer :: entry, new
if (associated (coll%first)) then
entry => coll%first
do
if (allocated (entry%prop)) then
if (allocated (channel%prop)) then
if (entry%prop == channel%prop) then
channel%sf_channel = entry%i
return
end if
end if
else if (.not. allocated (channel%prop)) then
channel%sf_channel = entry%i
return
end if
if (associated (entry%next)) then
entry => entry%next
else
exit
end if
end do
allocate (new)
entry%next => new
else
allocate (new)
coll%first => new
end if
coll%n = coll%n + 1
new%i = coll%n
channel%sf_channel = new%i
if (allocated (channel%prop)) then
allocate (new%prop, source = channel%prop)
end if
end subroutine phs_channel_collection_push
@ %def phs_channel_collection_push
@ Return the number of collected distinct channels.
<<PHS base: phs channel collection: TBP>>=
procedure :: get_n => phs_channel_collection_get_n
<<PHS base: procedures>>=
function phs_channel_collection_get_n (coll) result (n)
class(phs_channel_collection_t), intent(in) :: coll
integer :: n
n = coll%n
end function phs_channel_collection_get_n
@ %def phs_channel_collection_get_n
@ Return a specific channel (property object).
<<PHS base: phs channel collection: TBP>>=
procedure :: get_entry => phs_channel_collection_get_entry
<<PHS base: procedures>>=
subroutine phs_channel_collection_get_entry (coll, i, prop)
class(phs_channel_collection_t), intent(in) :: coll
integer, intent(in) :: i
class(channel_prop_t), intent(out), allocatable :: prop
type(prop_entry_t), pointer :: entry
integer :: k
if (i > 0 .and. i <= coll%n) then
entry => coll%first
do k = 2, i
entry => entry%next
end do
if (allocated (entry%prop)) then
if (allocated (prop)) deallocate (prop)
allocate (prop, source = entry%prop)
end if
else
call msg_bug ("PHS channel collection: get entry: illegal index")
end if
end subroutine phs_channel_collection_get_entry
@ %def phs_channel_collection_get_entry
@
\subsection{Kinematics configuration}
Here, we store the universal information that is specifically relevant
for phase-space generation. It is a subset of the process data,
supplemented by basic information on phase-space parameterization
channels.
A concrete implementation will contain more data, that describe the
phase space in detail.
MD5 sums: the phase space setup depends on the process, it depends on
the model parameters (the masses, that is), and on the configuration
parameters. (It does not depend on the QCD setup.)
<<PHS base: public>>=
public :: phs_config_t
<<PHS base: types>>=
type, abstract :: phs_config_t
! private
type(string_t) :: id
integer :: n_in = 0
integer :: n_out = 0
integer :: n_tot = 0
integer :: n_state = 0
integer :: n_par = 0
integer :: n_channel = 0
real(default) :: sqrts = 0
logical :: sqrts_fixed = .true.
logical :: cm_frame = .true.
logical :: azimuthal_dependence = .false.
integer, dimension(:), allocatable :: dim_flat
logical :: provides_equivalences = .false.
logical :: provides_chains = .false.
logical :: vis_channels = .false.
integer, dimension(:), allocatable :: chain
class(model_data_t), pointer :: model => null ()
type(flavor_t), dimension(:,:), allocatable :: flv
type(phs_channel_t), dimension(:), allocatable :: channel
character(32) :: md5sum_process = ""
character(32) :: md5sum_model_par = ""
character(32) :: md5sum_phs_config = ""
integer :: nlo_type
contains
<<PHS base: phs config: TBP>>
end type phs_config_t
@ %def phs_config_t
@ Finalizer, deferred.
<<PHS base: phs config: TBP>>=
procedure (phs_config_final), deferred :: final
<<PHS base: interfaces>>=
abstract interface
subroutine phs_config_final (object)
import
class(phs_config_t), intent(inout) :: object
end subroutine phs_config_final
end interface
@ %def phs_config_final
@ Output. We provide an implementation for the output of the base-type
contents and an interface for the actual write method.
<<PHS base: phs config: TBP>>=
procedure (phs_config_write), deferred :: write
procedure :: base_write => phs_config_write
<<PHS base: procedures>>=
subroutine phs_config_write (object, unit, include_id)
class(phs_config_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: include_id
integer :: u, i, j
integer :: n_tot_flv
logical :: use_id
n_tot_flv = object%n_tot
u = given_output_unit (unit)
use_id = .true.; if (present (include_id)) use_id = include_id
if (use_id) write (u, "(3x,A,A,A)") "ID = '", char (object%id), "'"
write (u, "(3x,A,I0)") "n_in = ", object%n_in
write (u, "(3x,A,I0)") "n_out = ", object%n_out
write (u, "(3x,A,I0)") "n_tot = ", object%n_tot
write (u, "(3x,A,I0)") "n_state = ", object%n_state
write (u, "(3x,A,I0)") "n_par = ", object%n_par
write (u, "(3x,A,I0)") "n_channel = ", object%n_channel
write (u, "(3x,A," // FMT_19 // ")") "sqrts = ", object%sqrts
write (u, "(3x,A,L1)") "s_fixed = ", object%sqrts_fixed
write (u, "(3x,A,L1)") "cm_frame = ", object%cm_frame
write (u, "(3x,A,L1)") "azim.dep. = ", object%azimuthal_dependence
if (allocated (object%dim_flat)) then
write (u, "(3x,A,I0)") "flat dim. = ", object%dim_flat
end if
write (u, "(1x,A)") "Flavor combinations:"
do i = 1, object%n_state
write (u, "(3x,I0,':')", advance="no") i
! do j = 1, object%n_tot
do j = 1, n_tot_flv
write (u, "(1x,A)", advance="no") char (object%flv(j,i)%get_name ())
end do
write (u, "(A)")
end do
if (allocated (object%channel)) then
write (u, "(1x,A)") "Phase-space / structure-function channels:"
do i = 1, object%n_channel
write (u, "(3x,I0,':')", advance="no") i
call object%channel(i)%write (u)
end do
end if
if (object%md5sum_process /= "") then
write (u, "(3x,A,A,A)") "MD5 sum (process) = '", &
object%md5sum_process, "'"
end if
if (object%md5sum_model_par /= "") then
write (u, "(3x,A,A,A)") "MD5 sum (model par) = '", &
object%md5sum_model_par, "'"
end if
if (object%md5sum_phs_config /= "") then
write (u, "(3x,A,A,A)") "MD5 sum (phs config) = '", &
object%md5sum_phs_config, "'"
end if
end subroutine phs_config_write
@ %def phs_config_write
@ Similarly, a basic initializer and an interface. The model pointer is taken
as an argument; we may verify that this has the expected model name.
The intent is [[inout]]. We want to be able to set parameters in advance.
<<PHS base: phs config: TBP>>=
procedure :: init => phs_config_init
<<PHS base: procedures>>=
subroutine phs_config_init (phs_config, data, model)
class(phs_config_t), intent(inout) :: phs_config
type(process_constants_t), intent(in) :: data
class(model_data_t), intent(in), target :: model
integer :: i, j
phs_config%id = data%id
phs_config%n_in = data%n_in
phs_config%n_out = data%n_out
phs_config%n_tot = data%n_in + data%n_out
phs_config%n_state = data%n_flv
if (data%model_name == model%get_name ()) then
phs_config%model => model
else
call msg_bug ("phs_config_init: model name mismatch")
end if
allocate (phs_config%flv (phs_config%n_tot, phs_config%n_state))
do i = 1, phs_config%n_state
do j = 1, phs_config%n_tot
call phs_config%flv(j,i)%init (data%flv_state(j,i), &
phs_config%model)
end do
end do
phs_config%md5sum_process = data%md5sum
end subroutine phs_config_init
@ %def phs_config_init
@
WK 2018-04-05: This procedure appears to be redundant?
<<XXX PHS base: phs config: TBP>>=
procedure :: set_component_index => phs_config_set_component_index
<<XXX PHS base: procedures>>=
subroutine phs_config_set_component_index (phs_config, index)
class(phs_config_t), intent(inout) :: phs_config
integer, intent(in) :: index
type(string_t), dimension(:), allocatable :: id
type(string_t) :: suffix
integer :: i, n
suffix = var_str ('i') // int2string (index)
call split_string (phs_config%id, var_str ('_'), id)
phs_config%id = var_str ('')
n = size (id) - 1
do i = 1, n
phs_config%id = phs_config%id // id(i) // var_str ('_')
end do
phs_config%id = phs_config%id // suffix
end subroutine phs_config_set_component_index
@ %def phs_config_set_component_index
@ This procedure should complete the phase-space configuration. We
need the [[sqrts]] value as overall scale, which is known only after
the beams have been defined. The procedure should determine the number of
channels, their properties (if any), and allocate and fill the [[channel]]
array accordingly.
<<PHS base: phs config: TBP>>=
procedure (phs_config_configure), deferred :: configure
<<PHS base: interfaces>>=
abstract interface
subroutine phs_config_configure (phs_config, sqrts, &
sqrts_fixed, cm_frame, azimuthal_dependence, rebuild, ignore_mismatch, &
nlo_type, subdir)
import
class(phs_config_t), intent(inout) :: phs_config
real(default), intent(in) :: sqrts
logical, intent(in), optional :: sqrts_fixed
logical, intent(in), optional :: cm_frame
logical, intent(in), optional :: azimuthal_dependence
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
integer, intent(in), optional :: nlo_type
type(string_t), intent(in), optional :: subdir
end subroutine phs_config_configure
end interface
@ %def phs_config_configure
@ Manually assign structure-function channel indices to the phase-space
channel objects. (Used by a test routine.)
<<PHS base: phs config: TBP>>=
procedure :: set_sf_channel => phs_config_set_sf_channel
<<PHS base: procedures>>=
subroutine phs_config_set_sf_channel (phs_config, sf_channel)
class(phs_config_t), intent(inout) :: phs_config
integer, dimension(:), intent(in) :: sf_channel
phs_config%channel%sf_channel = sf_channel
end subroutine phs_config_set_sf_channel
@ %def phs_config_set_sf_channel
@ Collect new channels not yet in the collection from this phase-space
configuration object. At the same time, assign structure-function channels.
<<PHS base: phs config: TBP>>=
procedure :: collect_channels => phs_config_collect_channels
<<PHS base: procedures>>=
subroutine phs_config_collect_channels (phs_config, coll)
class(phs_config_t), intent(inout) :: phs_config
type(phs_channel_collection_t), intent(inout) :: coll
integer :: c
do c = 1, phs_config%n_channel
call coll%push (phs_config%channel(c))
end do
end subroutine phs_config_collect_channels
@ %def phs_config_collect_channels
@ Compute the MD5 sum. We abuse the [[write]] method. In
type implementations, [[write]] should only display information that is
relevant for the MD5 sum. The data include the process MD5 sum which is taken
from the process constants, and the MD5 sum of the model parameters. This may
change, so it is computed here.
<<PHS base: phs config: TBP>>=
procedure :: compute_md5sum => phs_config_compute_md5sum
<<PHS base: procedures>>=
subroutine phs_config_compute_md5sum (phs_config, include_id)
class(phs_config_t), intent(inout) :: phs_config
logical, intent(in), optional :: include_id
integer :: u
phs_config%md5sum_model_par = phs_config%model%get_parameters_md5sum ()
phs_config%md5sum_phs_config = ""
u = free_unit ()
open (u, status = "scratch", action = "readwrite")
call phs_config%write (u, include_id)
rewind (u)
phs_config%md5sum_phs_config = md5sum (u)
close (u)
end subroutine phs_config_compute_md5sum
@ %def phs_config_compute_md5sum
@ Print an informative message after phase-space configuration.
<<PHS base: phs config: TBP>>=
procedure (phs_startup_message), deferred :: startup_message
procedure :: base_startup_message => phs_startup_message
<<PHS base: procedures>>=
subroutine phs_startup_message (phs_config, unit)
class(phs_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
write (msg_buffer, "(A,3(1x,I0,1x,A))") &
"Phase space:", &
phs_config%n_channel, "channels,", &
phs_config%n_par, "dimensions"
call msg_message (unit = unit)
end subroutine phs_startup_message
@ %def phs_startup_message
@ This procedure should be implemented such that the phase-space
configuration object allocates a phase-space instance of matching type.
<<PHS base: phs config: TBP>>=
procedure (phs_config_allocate_instance), nopass, deferred :: &
allocate_instance
<<PHS base: interfaces>>=
abstract interface
subroutine phs_config_allocate_instance (phs)
import
class(phs_t), intent(inout), pointer :: phs
end subroutine phs_config_allocate_instance
end interface
@ %def phs_config_allocate_instance
@
\subsection{Extract data}
Return the number of MC input parameters.
<<PHS base: phs config: TBP>>=
procedure :: get_n_par => phs_config_get_n_par
<<PHS base: procedures>>=
function phs_config_get_n_par (phs_config) result (n)
class(phs_config_t), intent(in) :: phs_config
integer :: n
n = phs_config%n_par
end function phs_config_get_n_par
@ %def phs_config_get_n_par
@ Return dimensions (parameter indices) for which the phase-space
dimension is flat, so integration and event generation can be simplified.
<<PHS base: phs config: TBP>>=
procedure :: get_flat_dimensions => phs_config_get_flat_dimensions
<<PHS base: procedures>>=
function phs_config_get_flat_dimensions (phs_config) result (dim_flat)
class(phs_config_t), intent(in) :: phs_config
integer, dimension(:), allocatable :: dim_flat
if (allocated (phs_config%dim_flat)) then
allocate (dim_flat (size (phs_config%dim_flat)))
dim_flat = phs_config%dim_flat
else
allocate (dim_flat (0))
end if
end function phs_config_get_flat_dimensions
@ %def phs_config_get_flat_dimensions
@ Return the number of phase-space channels.
<<PHS base: phs config: TBP>>=
procedure :: get_n_channel => phs_config_get_n_channel
<<PHS base: procedures>>=
function phs_config_get_n_channel (phs_config) result (n)
class(phs_config_t), intent(in) :: phs_config
integer :: n
n = phs_config%n_channel
end function phs_config_get_n_channel
@ %def phs_config_get_n_channel
@ Return the structure-function channel that corresponds to the
phase-space channel [[c]]. If the channel array is not allocated (which
happens if there is no structure function), return zero.
<<PHS base: phs config: TBP>>=
procedure :: get_sf_channel => phs_config_get_sf_channel
<<PHS base: procedures>>=
function phs_config_get_sf_channel (phs_config, c) result (c_sf)
class(phs_config_t), intent(in) :: phs_config
integer, intent(in) :: c
integer :: c_sf
if (allocated (phs_config%channel)) then
c_sf = phs_config%channel(c)%sf_channel
else
c_sf = 0
end if
end function phs_config_get_sf_channel
@ %def phs_config_get_sf_channel
@ Return the mass(es) of the incoming particle(s). We take the first flavor
combination in the array, assuming that masses must be degenerate among
flavors.
<<PHS base: phs config: TBP>>=
procedure :: get_masses_in => phs_config_get_masses_in
<<PHS base: procedures>>=
subroutine phs_config_get_masses_in (phs_config, m)
class(phs_config_t), intent(in) :: phs_config
real(default), dimension(:), intent(out) :: m
integer :: i
do i = 1, phs_config%n_in
m(i) = phs_config%flv(i,1)%get_mass ()
end do
end subroutine phs_config_get_masses_in
@ %def phs_config_get_masses_in
@ Return the MD5 sum of the configuration.
<<PHS base: phs config: TBP>>=
procedure :: get_md5sum => phs_config_get_md5sum
<<PHS base: procedures>>=
function phs_config_get_md5sum (phs_config) result (md5sum)
class(phs_config_t), intent(in) :: phs_config
character(32) :: md5sum
md5sum = phs_config%md5sum_phs_config
end function phs_config_get_md5sum
@ %def phs_config_get_md5sum
@
\subsection{Phase-space point instance}
The [[phs_t]] object holds the workspace for phase-space generation.
In the base object, we have the MC input parameters [[r]] and the
Jacobian factor [[f]], for each channel, and the incoming and outgoing
momenta.
Note: The [[active_channel]] array is not used yet, all elements are
initialized with [[.true.]]. It should be touched by the integrator if it
decides to drop irrelevant channels.
<<PHS base: public>>=
public :: phs_t
<<PHS base: types>>=
type, abstract :: phs_t
class(phs_config_t), pointer :: config => null ()
logical :: r_defined = .false.
integer :: selected_channel = 0
logical, dimension(:), allocatable :: active_channel
real(default), dimension(:,:), allocatable :: r
real(default), dimension(:), allocatable :: f
real(default), dimension(:), allocatable :: m_in
real(default), dimension(:), allocatable :: m_out
real(default) :: flux = 0
real(default) :: volume = 0
type(lorentz_transformation_t) :: lt_cm_to_lab
logical :: p_defined = .false.
real(default) :: sqrts_hat = 0
type(vector4_t), dimension(:), allocatable :: p
logical :: q_defined = .false.
type(vector4_t), dimension(:), allocatable :: q
contains
<<PHS base: phs: TBP>>
end type phs_t
@ %def phs_t
@ Output. Since phase space may get complicated, we include a
[[verbose]] option for the abstract [[write]] procedure.
<<PHS base: phs: TBP>>=
procedure (phs_write), deferred :: write
<<PHS base: interfaces>>=
abstract interface
subroutine phs_write (object, unit, verbose)
import
class(phs_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
end subroutine phs_write
end interface
@ %def phs_write
@ This procedure can be called to print the contents of the base type.
<<PHS base: phs: TBP>>=
procedure :: base_write => phs_base_write
<<PHS base: procedures>>=
subroutine phs_base_write (object, unit)
class(phs_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u, c, i
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "Partonic phase space: parameters"
if (object%r_defined) then
write (u, *)
else
write (u, "(1x,A)") "[undefined]"
end if
write (u, "(3x,A,999(1x," // FMT_19 // "))") "m_in =", object%m_in
write (u, "(3x,A,999(1x," // FMT_19 // "))") "m_out =", object%m_out
write (u, "(3x,A," // FMT_19 // ")") "Flux = ", object%flux
write (u, "(3x,A," // FMT_19 // ")") "Volume = ", object%volume
if (allocated (object%f)) then
do c = 1, size (object%r, 2)
write (u, "(1x,A,I0,A)", advance="no") "Channel #", c, ":"
if (c == object%selected_channel) then
write (u, "(1x,A)") "[selected]"
else
write (u, *)
end if
write (u, "(3x,A)", advance="no") "r ="
do i = 1, size (object%r, 1)
write (u, "(1x,F9.7)", advance="no") object%r(i,c)
end do
write (u, *)
write (u, "(3x,A,1x,ES13.7)") "f =", object%f(c)
end do
end if
write (u, "(1x,A)") "Partonic phase space: momenta"
if (object%p_defined) then
write (u, "(3x,A," // FMT_19 // ")") "sqrts = ", object%sqrts_hat
end if
write (u, "(1x,A)", advance="no") "Incoming:"
if (object%p_defined) then
write (u, *)
else
write (u, "(1x,A)") "[undefined]"
end if
if (allocated (object%p)) then
do i = 1, size (object%p)
call vector4_write (object%p(i), u)
end do
end if
write (u, "(1x,A)", advance="no") "Outgoing:"
if (object%q_defined) then
write (u, *)
else
write (u, "(1x,A)") "[undefined]"
end if
if (allocated (object%q)) then
do i = 1, size (object%q)
call vector4_write (object%q(i), u)
end do
end if
if (object%p_defined .and. .not. object%config%cm_frame) then
write (u, "(1x,A)") "Transformation c.m -> lab frame"
call lorentz_transformation_write (object%lt_cm_to_lab, u)
end if
end subroutine phs_base_write
@ %def phs_base_write
@ Finalizer. The base type does not need it, but extensions may.
<<PHS base: phs: TBP>>=
procedure (phs_final), deferred :: final
<<PHS base: interfaces>>=
abstract interface
subroutine phs_final (object)
import
class(phs_t), intent(inout) :: object
end subroutine phs_final
end interface
@ %def phs_final
@ Initializer. Everything should be contained in the [[process_data]]
configuration object, so we can require a universal interface.
<<PHS base: phs: TBP>>=
procedure (phs_init), deferred :: init
<<PHS base: interfaces>>=
abstract interface
subroutine phs_init (phs, phs_config)
import
class(phs_t), intent(out) :: phs
class(phs_config_t), intent(in), target :: phs_config
end subroutine phs_init
end interface
@ %def phs_init
@ The base version will just allocate the arrays. It should be called
at the beginning of the implementation of [[phs_init]].
<<PHS base: phs: TBP>>=
procedure :: base_init => phs_base_init
<<PHS base: procedures>>=
subroutine phs_base_init (phs, phs_config)
class(phs_t), intent(out) :: phs
class(phs_config_t), intent(in), target :: phs_config
real(default), dimension(phs_config%n_in) :: m_in
real(default), dimension(phs_config%n_out) :: m_out
phs%config => phs_config
allocate (phs%active_channel (phs%config%n_channel))
phs%active_channel = .true.
allocate (phs%r (phs%config%n_par, phs%config%n_channel)); phs%r = 0
allocate (phs%f (phs%config%n_channel)); phs%f = 0
allocate (phs%p (phs%config%n_in))
!!! !!! !!! Workaround for gfortran 5.0 ICE
m_in = phs_config%flv(:phs_config%n_in, 1)%get_mass ()
m_out = phs_config%flv(phs_config%n_in+1:, 1)%get_mass ()
allocate (phs%m_in (phs%config%n_in), source = m_in)
!!! allocate (phs%m_in (phs%config%n_in), &
!!! source = phs_config%flv(:phs_config%n_in, 1)%get_mass ())
allocate (phs%q (phs%config%n_out))
allocate (phs%m_out (phs%config%n_out), source = m_out)
!!! allocate (phs%m_out (phs%config%n_out), &
!!! source = phs_config%flv(phs_config%n_in+1:, 1)%get_mass ())
call phs%compute_flux ()
end subroutine phs_base_init
@ %def phs_base_init
@ Manually select a channel.
<<PHS base: phs: TBP>>=
procedure :: select_channel => phs_base_select_channel
<<PHS base: procedures>>=
subroutine phs_base_select_channel (phs, channel)
class(phs_t), intent(inout) :: phs
integer, intent(in), optional :: channel
if (present (channel)) then
phs%selected_channel = channel
else
phs%selected_channel = 0
end if
end subroutine phs_base_select_channel
@ %def phs_base_select_channel
@ Set incoming momenta. Assume that array shapes match. If
requested, compute the Lorentz transformation from the c.m.\ to the
lab frame and apply that transformation to the incoming momenta.
In the c.m.\ frame, the sum of three-momenta is zero. In a scattering
process, the $z$ axis is the direction of the first beam, the second
beam is along the negative $z$ axis. The transformation from the
c.m.\ to the lab frame is a rotation from the $z$ axis to the boost
axis followed by a boost, such that the c.m.\ momenta are transformed
into the lab-frame momenta. In a decay process, we just boost along
the flight direction, without rotation.
<<PHS base: phs: TBP>>=
procedure :: set_incoming_momenta => phs_set_incoming_momenta
<<PHS base: procedures>>=
subroutine phs_set_incoming_momenta (phs, p)
class(phs_t), intent(inout) :: phs
type(vector4_t), dimension(:), intent(in) :: p
type(vector4_t) :: p0, p1
type(lorentz_transformation_t) :: lt0
integer :: i
phs%p = p
if (phs%config%cm_frame) then
phs%sqrts_hat = phs%config%sqrts
phs%p = p
phs%lt_cm_to_lab = identity
else
p0 = sum (p)
if (phs%config%sqrts_fixed) then
phs%sqrts_hat = phs%config%sqrts
else
phs%sqrts_hat = p0 ** 1
end if
lt0 = boost (p0, phs%sqrts_hat)
select case (phs%config%n_in)
case (1)
phs%lt_cm_to_lab = lt0
case (2)
p1 = inverse (lt0) * p(1)
phs%lt_cm_to_lab = lt0 * rotation_to_2nd (3, space_part (p1))
end select
phs%p = inverse (phs%lt_cm_to_lab) * p
end if
phs%p_defined = .true.
end subroutine phs_set_incoming_momenta
@ %def phs_set_incoming_momenta
@ Set outgoing momenta. Assume that array shapes match. The incoming
momenta must be known, so can apply the Lorentz transformation from
c.m.\ to lab (inverse) to the momenta.
<<PHS base: phs: TBP>>=
procedure :: set_outgoing_momenta => phs_set_outgoing_momenta
<<PHS base: procedures>>=
subroutine phs_set_outgoing_momenta (phs, q)
class(phs_t), intent(inout) :: phs
type(vector4_t), dimension(:), intent(in) :: q
integer :: i
if (phs%p_defined) then
if (phs%config%cm_frame) then
phs%q = q
else
phs%q = inverse (phs%lt_cm_to_lab) * q
end if
phs%q_defined = .true.
end if
end subroutine phs_set_outgoing_momenta
@ %def phs_set_outgoing_momenta
@ Return outgoing momenta. Apply the c.m.\ to lab transformation if
necessary.
<<PHS base: phs: TBP>>=
procedure :: get_outgoing_momenta => phs_get_outgoing_momenta
<<PHS base: procedures>>=
subroutine phs_get_outgoing_momenta (phs, q)
class(phs_t), intent(in) :: phs
type(vector4_t), dimension(:), intent(out) :: q
if (phs%p_defined .and. phs%q_defined) then
if (phs%config%cm_frame) then
q = phs%q
else
q = phs%lt_cm_to_lab * phs%q
end if
else
q = vector4_null
end if
end subroutine phs_get_outgoing_momenta
@ %def phs_get_outgoing_momenta
@
<<PHS base: phs: TBP>>=
procedure :: is_cm_frame => phs_is_cm_frame
<<PHS base: procedures>>=
function phs_is_cm_frame (phs) result (cm_frame)
logical :: cm_frame
class(phs_t), intent(in) :: phs
cm_frame = phs%config%cm_frame
end function phs_is_cm_frame
@ %def phs_is_cm_frame
@
<<PHS base: phs: TBP>>=
procedure :: get_n_tot => phs_get_n_tot
<<PHS base: procedures>>=
elemental function phs_get_n_tot (phs) result (n_tot)
integer :: n_tot
class(phs_t), intent(in) :: phs
n_tot = phs%config%n_tot
end function phs_get_n_tot
@ %def phs_get_n_tot
@
<<PHS base: phs: TBP>>=
procedure :: set_lorentz_transformation => phs_set_lorentz_transformation
<<PHS base: procedures>>=
subroutine phs_set_lorentz_transformation (phs, lt)
class(phs_t), intent(inout) :: phs
type(lorentz_transformation_t), intent(in) :: lt
phs%lt_cm_to_lab = lt
end subroutine phs_set_lorentz_transformation
@ %def phs_set_lorentz_transformation
@
<<PHS base: phs: TBP>>=
procedure :: get_lorentz_transformation => phs_get_lorentz_transformation
<<PHS base: procedures>>=
function phs_get_lorentz_transformation (phs) result (lt)
type(lorentz_transformation_t) :: lt
class(phs_t), intent(in) :: phs
lt = phs%lt_cm_to_lab
end function phs_get_lorentz_transformation
@ %def phs_get_lorentz_transformation
@ Return the input parameter array for a channel.
<<PHS base: phs: TBP>>=
procedure :: get_mcpar => phs_get_mcpar
<<PHS base: procedures>>=
subroutine phs_get_mcpar (phs, c, r)
class(phs_t), intent(in) :: phs
integer, intent(in) :: c
real(default), dimension(:), intent(out) :: r
if (phs%r_defined) then
r = phs%r(:,c)
else
r = 0
end if
end subroutine phs_get_mcpar
@ %def phs_get_mcpar
@ Return the Jacobian factor for a channel.
<<PHS base: phs: TBP>>=
procedure :: get_f => phs_get_f
<<PHS base: procedures>>=
function phs_get_f (phs, c) result (f)
class(phs_t), intent(in) :: phs
integer, intent(in) :: c
real(default) :: f
if (phs%r_defined) then
f = phs%f(c)
else
f = 0
end if
end function phs_get_f
@ %def phs_get_f
@ Return the overall factor, which is the product of the flux factor for the
incoming partons and the phase-space volume for the outgoing partons.
<<PHS base: phs: TBP>>=
procedure :: get_overall_factor => phs_get_overall_factor
<<PHS base: procedures>>=
function phs_get_overall_factor (phs) result (f)
class(phs_t), intent(in) :: phs
real(default) :: f
f = phs%flux * phs%volume
end function phs_get_overall_factor
@ %def phs_get_overall_factor
@ Compute flux factor. We do this during initialization (when the
incoming momenta [[p]] are undefined), unless [[sqrts]] is variable. We do
this again once for each phase-space point, but then we skip the calculation
if [[sqrts]] is fixed.
There are three different flux factors.
\begin{enumerate}
\item
For a decaying massive particle, the factor is
\begin{equation}
f = (2\pi)^4 / (2M)
\end{equation}
\item
For a $2\to n$ scattering process with $n>1$, the factor is
\begin{equation}
f = (2\pi)^4 / (2\sqrt{\lambda})
\end{equation}
where for massless incoming particles, $\sqrt{\lambda} = s$.
\item For a $2\to 1$ on-shell production process, the factor includes
an extra $1/(2\pi)^3$ factor and a $1/m^2$ factor from the
phase-space delta function $\delta (x_1x_2 - m^2/s)$, which
originate from the one-particle phase space that we integrate out.
\begin{equation}
f = 2\pi / (2s m^2)
\end{equation}
The delta function is handled by the structure-function
parameterization.
\end{enumerate}
<<PHS base: phs: TBP>>=
procedure :: compute_flux => phs_compute_flux
<<PHS base: procedures>>=
subroutine phs_compute_flux (phs)
class(phs_t), intent(inout) :: phs
real(default) :: s_hat, lda
select case (phs%config%n_in)
case (1)
if (.not. phs%p_defined) then
phs%flux = twopi4 / (2 * phs%m_in(1))
end if
case (2)
if (phs%p_defined) then
if (phs%config%sqrts_fixed) then
return
else
s_hat = sum (phs%p) ** 2
end if
else
if (phs%config%sqrts_fixed) then
s_hat = phs%config%sqrts ** 2
else
return
end if
end if
select case (phs%config%n_out)
case (2:)
lda = lambda (s_hat, phs%m_in(1) ** 2, phs%m_in(2) ** 2)
if (lda > 0) then
phs%flux = conv * twopi4 / (2 * sqrt (lda))
else
phs%flux = 0
end if
case (1)
phs%flux = conv * twopi &
/ (2 * phs%config%sqrts ** 2 * phs%m_out(1) ** 2)
case default
phs%flux = 0
end select
end select
end subroutine phs_compute_flux
@ %def phs_compute_flux
@ Evaluate the phase-space point for a particular channel and compute momenta,
Jacobian, and phase-space volume. This is, of course, deferred to
the implementation.
<<PHS base: phs: TBP>>=
procedure (phs_evaluate_selected_channel), deferred :: &
evaluate_selected_channel
<<PHS base: interfaces>>=
abstract interface
subroutine phs_evaluate_selected_channel (phs, c_in, r_in)
import
class(phs_t), intent(inout) :: phs
integer, intent(in) :: c_in
real(default), dimension(:), intent(in) :: r_in
end subroutine phs_evaluate_selected_channel
end interface
@ %def phs_evaluate_selected_channel
@ Compute the inverse mappings to completely fill the [[r]] and [[f]] arrays,
for the non-selected channels.
<<PHS base: phs: TBP>>=
procedure (phs_evaluate_other_channels), deferred :: &
evaluate_other_channels
<<PHS base: interfaces>>=
abstract interface
subroutine phs_evaluate_other_channels (phs, c_in)
import
class(phs_t), intent(inout) :: phs
integer, intent(in) :: c_in
end subroutine phs_evaluate_other_channels
end interface
@ %def phs_evaluate_other_channels
@ Inverse evaluation. If all momenta are known, we compute the
inverse mappings to fill the [[r]] and [[f]] arrays.
<<PHS base: phs: TBP>>=
procedure (phs_inverse), deferred :: inverse
<<PHS base: interfaces>>=
abstract interface
subroutine phs_inverse (phs)
import
class(phs_t), intent(inout) :: phs
end subroutine phs_inverse
end interface
@ %def phs_inverse
@
<<PHS base: phs: TBP>>=
procedure :: get_sqrts => phs_get_sqrts
<<PHS base: procedures>>=
function phs_get_sqrts (phs) result (sqrts)
real(default) :: sqrts
class(phs_t), intent(in) :: phs
sqrts = phs%config%sqrts
end function phs_get_sqrts
@ %def phs_get_sqrts
@
\subsubsection{Uniform angular distribution}
These procedures implement the uniform angular distribution, generated
from two parameters $x_1$ and $x_2$:
\begin{equation}
\cos\theta = 1 - 2x_1, \qquad \phi = 2\pi x_2
\end{equation}
We generate a rotation (Lorentz transformation) which rotates the
positive $z$ axis into this point on the unit sphere. This rotation
is applied to the [[p]] momenta, which are assumed to be
back-to-back, on-shell, and with the correct mass.
We do not compute a Jacobian (constant). The uniform distribution is
assumed to be normalized.
<<PHS base: public>>=
public :: compute_kinematics_solid_angle
<<PHS base: procedures>>=
subroutine compute_kinematics_solid_angle (p, q, x)
type(vector4_t), dimension(2), intent(in) :: p
type(vector4_t), dimension(2), intent(out) :: q
real(default), dimension(2), intent(in) :: x
real(default) :: ct, st, phi
type(lorentz_transformation_t) :: rot
integer :: i
ct = 1 - 2*x(1)
st = sqrt (1 - ct**2)
phi = twopi * x(2)
rot = rotation (phi, 3) * rotation (ct, st, 2)
do i = 1, 2
q(i) = rot * p(i)
end do
end subroutine compute_kinematics_solid_angle
@ %def compute_kinematics_solid_angle
@ This is the inverse transformation. We assume that the outgoing
momenta are rotated versions of the incoming momenta, back-to-back.
Thus, we determine the angles from $q(1)$ alone. [[p]] is unused.
<<PHS base: public>>=
public :: inverse_kinematics_solid_angle
<<PHS base: procedures>>=
subroutine inverse_kinematics_solid_angle (p, q, x)
type(vector4_t), dimension(:), intent(in) :: p
type(vector4_t), dimension(2), intent(in) :: q
real(default), dimension(2), intent(out) :: x
real(default) :: ct, phi
ct = polar_angle_ct (q(1))
phi = azimuthal_angle (q(1))
x(1) = (1 - ct) / 2
x(2) = phi / twopi
end subroutine inverse_kinematics_solid_angle
@ %def inverse_kinematics_solid_angle
@
\subsection{Auxiliary stuff}
The [[pacify]] subroutine, which is provided by the Lorentz module,
has the purpose of setting numbers to zero which are (by comparing
with a [[tolerance]] parameter) considered equivalent with zero. This
is useful for numerical checks.
<<PHS base: public>>=
public :: pacify
<<PHS base: interfaces>>=
interface pacify
module procedure pacify_phs
end interface pacify
<<PHS base: procedures>>=
subroutine pacify_phs (phs)
class(phs_t), intent(inout) :: phs
if (phs%p_defined) then
call pacify (phs%p, 30 * epsilon (1._default) * phs%config%sqrts)
call pacify (phs%lt_cm_to_lab, 30 * epsilon (1._default))
end if
if (phs%q_defined) then
call pacify (phs%q, 30 * epsilon (1._default) * phs%config%sqrts)
end if
end subroutine pacify_phs
@ %def pacify
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[phs_base_ut.f90]]>>=
<<File header>>
module phs_base_ut
use unit_tests
use phs_base_uti
<<Standard module head>>
<<PHS base: public test>>
<<PHS base: public test auxiliary>>
contains
<<PHS base: test driver>>
end module phs_base_ut
@ %def phs_base_ut
@
<<[[phs_base_uti.f90]]>>=
<<File header>>
module phs_base_uti
<<Use kinds>>
<<Use strings>>
use diagnostics
use io_units
use format_defs, only: FMT_19
use physics_defs, only: BORN
use lorentz
use flavors
use model_data
use process_constants
use phs_base
<<Standard module head>>
<<PHS base: public test auxiliary>>
<<PHS base: test declarations>>
<<PHS base: test types>>
contains
<<PHS base: tests>>
<<PHS base: test auxiliary>>
end module phs_base_uti
@ %def phs_base_ut
@ API: driver for the unit tests below.
<<PHS base: public test>>=
public :: phs_base_test
<<PHS base: test driver>>=
subroutine phs_base_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<PHS base: execute tests>>
end subroutine phs_base_test
@ %def phs_base_test
@
\subsubsection{Test process data}
We provide a procedure that initializes a test case for the process
constants. This set of process data contains just the minimal
contents that we need for the phase space. The rest is left
uninitialized.
<<PHS base: public test auxiliary>>=
public :: init_test_process_data
<<PHS base: test auxiliary>>=
subroutine init_test_process_data (id, data)
type(process_constants_t), intent(out) :: data
type(string_t), intent(in), optional :: id
if (present (id)) then
data%id = id
else
data%id = "testproc"
end if
data%model_name = "Test"
data%n_in = 2
data%n_out = 2
data%n_flv = 1
allocate (data%flv_state (data%n_in + data%n_out, data%n_flv))
data%flv_state = 25
end subroutine init_test_process_data
@ %def init_test_process_data
@ This is the variant for a decay process.
<<PHS base: public test auxiliary>>=
public :: init_test_decay_data
<<PHS base: test auxiliary>>=
subroutine init_test_decay_data (id, data)
type(process_constants_t), intent(out) :: data
type(string_t), intent(in), optional :: id
if (present (id)) then
data%id = id
else
data%id = "testproc"
end if
data%model_name = "Test"
data%n_in = 1
data%n_out = 2
data%n_flv = 1
allocate (data%flv_state (data%n_in + data%n_out, data%n_flv))
data%flv_state(:,1) = [25, 6, -6]
end subroutine init_test_decay_data
@ %def init_test_decay_data
@
\subsubsection{Test kinematics configuration}
This is a trivial implementation of the [[phs_config_t]] configuration object.
<<PHS base: public test auxiliary>>=
public :: phs_test_config_t
<<PHS base: test types>>=
type, extends (phs_config_t) :: phs_test_config_t
logical :: create_equivalences = .false.
contains
procedure :: final => phs_test_config_final
procedure :: write => phs_test_config_write
procedure :: configure => phs_test_config_configure
procedure :: startup_message => phs_test_config_startup_message
procedure, nopass :: allocate_instance => phs_test_config_allocate_instance
end type phs_test_config_t
@ %def phs_test_config_t
@ The finalizer is empty.
<<PHS base: test auxiliary>>=
subroutine phs_test_config_final (object)
class(phs_test_config_t), intent(inout) :: object
end subroutine phs_test_config_final
@ %def phs_test_config_final
@ The [[cm_frame]] parameter is not tested here; we defer this to the
[[phs_single]] implementation.
<<PHS base: test auxiliary>>=
subroutine phs_test_config_write (object, unit, include_id)
class(phs_test_config_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: include_id
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") "Partonic phase-space configuration:"
call object%base_write (unit)
end subroutine phs_test_config_write
subroutine phs_test_config_configure (phs_config, sqrts, &
sqrts_fixed, cm_frame, azimuthal_dependence, rebuild, &
ignore_mismatch, nlo_type, subdir)
class(phs_test_config_t), intent(inout) :: phs_config
real(default), intent(in) :: sqrts
logical, intent(in), optional :: sqrts_fixed
logical, intent(in), optional :: cm_frame
logical, intent(in), optional :: azimuthal_dependence
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
integer, intent(in), optional :: nlo_type
type(string_t), intent(in), optional :: subdir
phs_config%n_channel = 2
phs_config%n_par = 2
phs_config%sqrts = sqrts
if (.not. present (nlo_type)) &
phs_config%nlo_type = BORN
if (present (sqrts_fixed)) then
phs_config%sqrts_fixed = sqrts_fixed
end if
if (present (cm_frame)) then
phs_config%cm_frame = cm_frame
end if
if (present (azimuthal_dependence)) then
phs_config%azimuthal_dependence = azimuthal_dependence
end if
if (allocated (phs_config%channel)) deallocate (phs_config%channel)
allocate (phs_config%channel (phs_config%n_channel))
if (phs_config%create_equivalences) then
call setup_test_equivalences (phs_config)
call setup_test_channel_props (phs_config)
end if
call phs_config%compute_md5sum ()
end subroutine phs_test_config_configure
@ %def phs_test_config_write
@ %def phs_test_config_configure
@ If requested, we make up an arbitrary set of equivalences.
<<PHS base: test auxiliary>>=
subroutine setup_test_equivalences (phs_config)
class(phs_test_config_t), intent(inout) :: phs_config
integer :: i
associate (channel => phs_config%channel(1))
allocate (channel%eq (2))
do i = 1, size (channel%eq)
call channel%eq(i)%init (phs_config%n_par)
end do
associate (eq => channel%eq(1))
eq%c = 1; eq%perm = [1, 2]; eq%mode = [EQ_IDENTITY, EQ_SYMMETRIC]
end associate
associate (eq => channel%eq(2))
eq%c = 2; eq%perm = [2, 1]; eq%mode = [EQ_INVARIANT, EQ_IDENTITY]
end associate
end associate
end subroutine setup_test_equivalences
@ %def setup_test_equivalences
@ Ditto, for channel properties.
<<PHS base: test auxiliary>>=
subroutine setup_test_channel_props (phs_config)
class(phs_test_config_t), intent(inout) :: phs_config
associate (channel => phs_config%channel(2))
call channel%set_resonant (140._default, 3.1415_default)
end associate
end subroutine setup_test_channel_props
@ %def setup_test_channel_props
@ Startup message
<<PHS base: test auxiliary>>=
subroutine phs_test_config_startup_message (phs_config, unit)
class(phs_test_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
call phs_config%base_startup_message (unit)
write (msg_buffer, "(A)") "Phase space: Test"
call msg_message (unit = unit)
end subroutine phs_test_config_startup_message
@ %def phs_test_config_startup_message
@ The instance type that matches [[phs_test_config_t]] is [[phs_test_t]].
<<PHS base: test auxiliary>>=
subroutine phs_test_config_allocate_instance (phs)
class(phs_t), intent(inout), pointer :: phs
allocate (phs_test_t :: phs)
end subroutine phs_test_config_allocate_instance
@ %def phs_test_config_allocate_instance
@
\subsubsection{Test kinematics implementation}
This implementation of kinematics generates a simple two-particle
configuration from the incoming momenta. The incoming momenta must be
in the c.m.\ system, all masses equal.
There are two channels: one generates $\cos\theta$ and $\phi$
uniformly, in the other channel we map the $r_1$ parameter which
belongs to $\cos\theta$.
We should store the mass parameter that we need.
<<PHS base: public test auxiliary>>=
public :: phs_test_t
<<PHS base: test types>>=
type, extends (phs_t) :: phs_test_t
real(default) :: m = 0
real(default), dimension(:), allocatable :: x
contains
<<PHS base: phs test: TBP>>
end type phs_test_t
@ %def phs_test_t
@ Output. The specific data are displayed only if [[verbose]] is set.
<<PHS base: phs test: TBP>>=
procedure :: write => phs_test_write
<<PHS base: test auxiliary>>=
subroutine phs_test_write (object, unit, verbose)
class(phs_test_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
logical :: verb
u = given_output_unit (unit)
verb = .false.; if (present (verbose)) verb = verbose
if (verb) then
write (u, "(1x,A)") "Partonic phase space: data"
write (u, "(3x,A," // FMT_19 // ")") "m = ", object%m
end if
call object%base_write (u)
end subroutine phs_test_write
@ %def phs_test_write
@ The finalizer is empty.
<<PHS base: phs test: TBP>>=
procedure :: final => phs_test_final
<<PHS base: test auxiliary>>=
subroutine phs_test_final (object)
class(phs_test_t), intent(inout) :: object
end subroutine phs_test_final
@ %def phs_test_final
@ Initialization: set the mass value.
<<PHS base: phs test: TBP>>=
procedure :: init => phs_test_init
<<PHS base: test auxiliary>>=
subroutine phs_test_init (phs, phs_config)
class(phs_test_t), intent(out) :: phs
class(phs_config_t), intent(in), target :: phs_config
call phs%base_init (phs_config)
phs%m = phs%config%flv(1,1)%get_mass ()
allocate (phs%x (phs_config%n_par), source = 0._default)
end subroutine phs_test_init
@ %def phs_test_init
@ Evaluation. In channel 1, we uniformly generate $\cos\theta$ and
$\phi$, with Jacobian normalized to one. In channel 2, we prepend a
mapping $r_1 \to r_1^(1/3)$ with Jacobian $f=3r_1^2$.
The component [[x]] is allocated in the first subroutine, used and deallocated
in the second one.
<<PHS base: phs test: TBP>>=
procedure :: evaluate_selected_channel => phs_test_evaluate_selected_channel
procedure :: evaluate_other_channels => phs_test_evaluate_other_channels
<<PHS base: test auxiliary>>=
subroutine phs_test_evaluate_selected_channel (phs, c_in, r_in)
class(phs_test_t), intent(inout) :: phs
integer, intent(in) :: c_in
real(default), intent(in), dimension(:) :: r_in
if (phs%p_defined) then
call phs%select_channel (c_in)
phs%r(:,c_in) = r_in
select case (c_in)
case (1)
phs%x = r_in
case (2)
phs%x(1) = r_in(1) ** (1 / 3._default)
phs%x(2) = r_in(2)
end select
call compute_kinematics_solid_angle (phs%p, phs%q, phs%x)
phs%volume = 1
phs%q_defined = .true.
end if
end subroutine phs_test_evaluate_selected_channel
subroutine phs_test_evaluate_other_channels (phs, c_in)
class(phs_test_t), intent(inout) :: phs
integer, intent(in) :: c_in
integer :: c, n_channel
if (phs%p_defined) then
n_channel = phs%config%n_channel
do c = 1, n_channel
if (c /= c_in) then
call inverse_kinematics_solid_angle (phs%p, phs%q, phs%x)
select case (c)
case (1)
phs%r(:,c) = phs%x
case (2)
phs%r(1,c) = phs%x(1) ** 3
phs%r(2,c) = phs%x(2)
end select
end if
end do
phs%f(1) = 1
if (phs%r(1,2) /= 0) then
phs%f(2) = 1 / (3 * phs%r(1,2) ** (2/3._default))
else
phs%f(2) = 0
end if
phs%r_defined = .true.
end if
end subroutine phs_test_evaluate_other_channels
@ %def phs_test_evaluate_selected_channels
@ %def phs_test_evaluate_other_channels
@ Inverse evaluation.
<<PHS base: phs test: TBP>>=
procedure :: inverse => phs_test_inverse
<<PHS base: test auxiliary>>=
subroutine phs_test_inverse (phs)
class(phs_test_t), intent(inout) :: phs
integer :: c, n_channel
real(default), dimension(:), allocatable :: x
if (phs%p_defined .and. phs%q_defined) then
call phs%select_channel ()
n_channel = phs%config%n_channel
allocate (x (phs%config%n_par))
do c = 1, n_channel
call inverse_kinematics_solid_angle (phs%p, phs%q, x)
select case (c)
case (1)
phs%r(:,c) = x
case (2)
phs%r(1,c) = x(1) ** 3
phs%r(2,c) = x(2)
end select
end do
phs%f(1) = 1
if (phs%r(1,2) /= 0) then
phs%f(2) = 1 / (3 * phs%r(1,2) ** (2/3._default))
else
phs%f(2) = 0
end if
phs%volume = 1
phs%r_defined = .true.
end if
end subroutine phs_test_inverse
@ %def phs_test_inverse
@
\subsubsection{Phase-space configuration data}
Construct and display a test phase-space configuration object.
<<PHS base: execute tests>>=
call test (phs_base_1, "phs_base_1", &
"phase-space configuration", &
u, results)
<<PHS base: test declarations>>=
public :: phs_base_1
<<PHS base: tests>>=
subroutine phs_base_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
class(phs_config_t), allocatable :: phs_data
write (u, "(A)") "* Test output: phs_base_1"
write (u, "(A)") "* Purpose: initialize and display &
&test phase-space configuration data"
write (u, "(A)")
call model%init_test ()
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_base_1"), process_data)
allocate (phs_test_config_t :: phs_data)
call phs_data%init (process_data, model)
call phs_data%write (u)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_base_1"
end subroutine phs_base_1
@ %def phs_base_1
@
\subsubsection{Phase space evaluation}
Compute kinematics for given parameters, also invert the calculation.
<<PHS base: execute tests>>=
call test (phs_base_2, "phs_base_2", &
"phase-space evaluation", &
u, results)
<<PHS base: test declarations>>=
public :: phs_base_2
<<PHS base: tests>>=
subroutine phs_base_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(process_constants_t) :: process_data
real(default) :: sqrts, E
class(phs_config_t), allocatable, target :: phs_data
class(phs_t), pointer :: phs => null ()
type(vector4_t), dimension(2) :: p, q
write (u, "(A)") "* Test output: phs_base_2"
write (u, "(A)") "* Purpose: test simple two-channel phase space"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_base_2"), process_data)
allocate (phs_test_config_t :: phs_data)
call phs_data%init (process_data, model)
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
select type (phs)
type is (phs_test_t)
call phs%init (phs_data)
end select
call phs%write (u, verbose=.true.)
write (u, "(A)")
write (u, "(A)") "* Set incoming momenta"
write (u, "(A)")
E = sqrts / 2
p(1) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
p(2) = vector4_moving (E,-sqrt (E**2 - flv%get_mass ()**2), 3)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point in channel 1 &
&for x = 0.5, 0.125"
write (u, "(A)")
call phs%evaluate_selected_channel (1, [0.5_default, 0.125_default])
call phs%evaluate_other_channels (1)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point in channel 2 &
&for x = 0.125, 0.125"
write (u, "(A)")
call phs%evaluate_selected_channel (2, [0.125_default, 0.125_default])
call phs%evaluate_other_channels (2)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
sqrts = 1000._default
select type (phs_data)
type is (phs_test_config_t)
call phs_data%configure (sqrts)
end select
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call phs%write (u)
call phs%final ()
deallocate (phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_base_2"
end subroutine phs_base_2
@ %def phs_base_2
@
\subsubsection{Phase-space equivalences}
Construct a test phase-space configuration which contains channel
equivalences.
<<PHS base: execute tests>>=
call test (phs_base_3, "phs_base_3", &
"channel equivalences", &
u, results)
<<PHS base: test declarations>>=
public :: phs_base_3
<<PHS base: tests>>=
subroutine phs_base_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
class(phs_config_t), allocatable :: phs_data
write (u, "(A)") "* Test output: phs_base_3"
write (u, "(A)") "* Purpose: construct phase-space configuration data &
&with equivalences"
write (u, "(A)")
call model%init_test ()
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_base_3"), process_data)
allocate (phs_test_config_t :: phs_data)
call phs_data%init (process_data, model)
select type (phs_data)
type is (phs_test_config_t)
phs_data%create_equivalences = .true.
end select
call phs_data%configure (1000._default)
call phs_data%write (u)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_base_3"
end subroutine phs_base_3
@ %def phs_base_3
@
\subsubsection{MD5 sum checks}
Construct a test phase-space configuration, compute and compare MD5 sums.
<<PHS base: execute tests>>=
call test (phs_base_4, "phs_base_4", &
"MD5 sum", &
u, results)
<<PHS base: test declarations>>=
public :: phs_base_4
<<PHS base: tests>>=
subroutine phs_base_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
class(phs_config_t), allocatable :: phs_data
write (u, "(A)") "* Test output: phs_base_4"
write (u, "(A)") "* Purpose: compute and compare MD5 sums"
write (u, "(A)")
call model%init_test ()
write (u, "(A)") "* Model parameters"
write (u, "(A)")
call model%write (unit = u, &
show_parameters = .true., &
show_particles = .false., show_vertices = .false.)
write (u, "(A)")
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_base_4"), process_data)
process_data%md5sum = "test_process_data_m6sum_12345678"
allocate (phs_test_config_t :: phs_data)
call phs_data%init (process_data, model)
call phs_data%compute_md5sum ()
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Modify model parameter"
write (u, "(A)")
call model%set_par (var_str ("ms"), 100._default)
call model%write (show_parameters = .true., &
show_particles = .false., show_vertices = .false.)
write (u, "(A)")
write (u, "(A)") "* PHS configuration"
write (u, "(A)")
call phs_data%compute_md5sum ()
call phs_data%write (u)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_base_4"
end subroutine phs_base_4
@ %def phs_base_4
@
\subsubsection{Phase-space channel collection}
Set up an array of various phase-space channels and collect them in a list.
<<PHS base: execute tests>>=
call test (phs_base_5, "phs_base_5", &
"channel collection", &
u, results)
<<PHS base: test declarations>>=
public :: phs_base_5
<<PHS base: tests>>=
subroutine phs_base_5 (u)
integer, intent(in) :: u
type(phs_channel_t), dimension(:), allocatable :: channel
type(phs_channel_collection_t) :: coll
integer :: i, n
write (u, "(A)") "* Test output: phs_base_5"
write (u, "(A)") "* Purpose: collect channel properties"
write (u, "(A)")
write (u, "(A)") "* Set up an array of channels"
write (u, "(A)")
n = 6
allocate (channel (n))
call channel(2)%set_resonant (75._default, 3._default)
call channel(4)%set_resonant (130._default, 1._default)
call channel(5)%set_resonant (75._default, 3._default)
call channel(6)%set_on_shell (33._default)
do i = 1, n
write (u, "(1x,I0)", advance="no") i
call channel(i)%write (u)
end do
write (u, "(A)")
write (u, "(A)") "* Collect distinct properties"
write (u, "(A)")
do i = 1, n
call coll%push (channel(i))
end do
write (u, "(1x,A,I0)") "n = ", coll%get_n ()
write (u, "(A)")
call coll%write (u)
write (u, "(A)")
write (u, "(A)") "* Channel array with collection index assigned"
write (u, "(A)")
do i = 1, n
write (u, "(1x,I0)", advance="no") i
call channel(i)%write (u)
end do
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call coll%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_base_5"
end subroutine phs_base_5
@ %def phs_base_5
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\clearpage
\section{Dummy phase space}
This module implements a dummy phase space module for cases where the
program structure demands the existence of a phase-space module, but
no phase space integration is performed.
<<[[phs_none.f90]]>>=
<<File header>>
module phs_none
<<Use kinds>>
<<Use strings>>
use io_units, only: given_output_unit
use diagnostics, only: msg_message, msg_fatal
use phs_base, only: phs_config_t, phs_t
<<Standard module head>>
<<PHS none: public>>
<<PHS none: types>>
contains
<<PHS none: procedures>>
end module phs_none
@ %def phs_none
@
\subsection{Configuration}
Nothing to configure, but we provide the type and methods.
<<PHS none: public>>=
public :: phs_none_config_t
<<PHS none: types>>=
type, extends (phs_config_t) :: phs_none_config_t
contains
<<PHS none: phs none config: TBP>>
end type phs_none_config_t
@ %def phs_none_config_t
@ The finalizer is empty.
<<PHS none: phs none config: TBP>>=
procedure :: final => phs_none_config_final
<<PHS none: procedures>>=
subroutine phs_none_config_final (object)
class(phs_none_config_t), intent(inout) :: object
end subroutine phs_none_config_final
@ %def phs_none_final
@ Output. No contents, just an informative line.
<<PHS none: phs none config: TBP>>=
procedure :: write => phs_none_config_write
<<PHS none: procedures>>=
subroutine phs_none_config_write (object, unit, include_id)
class(phs_none_config_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: include_id
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") "Partonic phase-space configuration: non-functional dummy"
end subroutine phs_none_config_write
@ %def phs_none_config_write
@ Configuration: we have to implement this method, but it obviously does nothing.
<<PHS none: phs none config: TBP>>=
procedure :: configure => phs_none_config_configure
<<PHS none: procedures>>=
subroutine phs_none_config_configure (phs_config, sqrts, &
sqrts_fixed, cm_frame, azimuthal_dependence, rebuild, ignore_mismatch, &
nlo_type, subdir)
class(phs_none_config_t), intent(inout) :: phs_config
real(default), intent(in) :: sqrts
logical, intent(in), optional :: sqrts_fixed
logical, intent(in), optional :: cm_frame
logical, intent(in), optional :: azimuthal_dependence
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
integer, intent(in), optional :: nlo_type
type(string_t), intent(in), optional :: subdir
end subroutine phs_none_config_configure
@ %def phs_none_config_configure
@ Startup message, after configuration is complete.
<<PHS none: phs none config: TBP>>=
procedure :: startup_message => phs_none_config_startup_message
<<PHS none: procedures>>=
subroutine phs_none_config_startup_message (phs_config, unit)
class(phs_none_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
call msg_message ("Phase space: none")
end subroutine phs_none_config_startup_message
@ %def phs_none_config_startup_message
@ Allocate an instance: the actual phase-space object.
<<PHS none: phs none config: TBP>>=
procedure, nopass :: allocate_instance => phs_none_config_allocate_instance
<<PHS none: procedures>>=
subroutine phs_none_config_allocate_instance (phs)
class(phs_t), intent(inout), pointer :: phs
allocate (phs_none_t :: phs)
end subroutine phs_none_config_allocate_instance
@ %def phs_none_config_allocate_instance
@
\subsection{Kinematics implementation}
This is considered as empty, but we have to implement the minimal set of methods.
<<PHS none: public>>=
public :: phs_none_t
<<PHS none: types>>=
type, extends (phs_t) :: phs_none_t
contains
<<PHS none: phs none: TBP>>
end type phs_none_t
@ %def phs_none_t
@ Output.
<<PHS none: phs none: TBP>>=
procedure :: write => phs_none_write
<<PHS none: procedures>>=
subroutine phs_none_write (object, unit, verbose)
class(phs_none_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit)
write (u, "(A)") "Partonic phase space: none"
end subroutine phs_none_write
@ %def phs_none_write
@ The finalizer is empty.
<<PHS none: phs none: TBP>>=
procedure :: final => phs_none_final
<<PHS none: procedures>>=
subroutine phs_none_final (object)
class(phs_none_t), intent(inout) :: object
end subroutine phs_none_final
@ %def phs_none_final
@ Initialization, trivial.
<<PHS none: phs none: TBP>>=
procedure :: init => phs_none_init
<<PHS none: procedures>>=
subroutine phs_none_init (phs, phs_config)
class(phs_none_t), intent(out) :: phs
class(phs_config_t), intent(in), target :: phs_config
call phs%base_init (phs_config)
end subroutine phs_none_init
@ %def phs_none_init
@ Evaluation. This must not be called at all.
<<PHS none: phs none: TBP>>=
procedure :: evaluate_selected_channel => phs_none_evaluate_selected_channel
procedure :: evaluate_other_channels => phs_none_evaluate_other_channels
<<PHS none: procedures>>=
subroutine phs_none_evaluate_selected_channel (phs, c_in, r_in)
class(phs_none_t), intent(inout) :: phs
integer, intent(in) :: c_in
real(default), intent(in), dimension(:) :: r_in
call msg_fatal ("Phase space: attempt to evaluate with the 'phs_none' method")
end subroutine phs_none_evaluate_selected_channel
subroutine phs_none_evaluate_other_channels (phs, c_in)
class(phs_none_t), intent(inout) :: phs
integer, intent(in) :: c_in
end subroutine phs_none_evaluate_other_channels
@ %def phs_none_evaluate_selected_channel
@ %def phs_none_evaluate_other_channels
@ Inverse evaluation, likewise.
<<PHS none: phs none: TBP>>=
procedure :: inverse => phs_none_inverse
<<PHS none: procedures>>=
subroutine phs_none_inverse (phs)
class(phs_none_t), intent(inout) :: phs
call msg_fatal ("Phase space: attempt to evaluate inverse with the 'phs_none' method")
end subroutine phs_none_inverse
@ %def phs_none_inverse
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[phs_none_ut.f90]]>>=
<<File header>>
module phs_none_ut
use unit_tests
use phs_none_uti
<<Standard module head>>
<<PHS none: public test>>
contains
<<PHS none: test driver>>
end module phs_none_ut
@ %def phs_none_ut
@
<<[[phs_none_uti.f90]]>>=
<<File header>>
module phs_none_uti
<<Use kinds>>
<<Use strings>>
use flavors
use lorentz
use model_data
use process_constants
use phs_base
use phs_none
use phs_base_ut, only: init_test_process_data, init_test_decay_data
<<Standard module head>>
<<PHS none: test declarations>>
contains
<<PHS none: tests>>
end module phs_none_uti
@ %def phs_none_ut
@ API: driver for the unit tests below.
<<PHS none: public test>>=
public :: phs_none_test
<<PHS none: test driver>>=
subroutine phs_none_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<PHS none: execute tests>>
end subroutine phs_none_test
@ %def phs_none_test
@
\subsubsection{Phase-space configuration data}
Construct and display a test phase-space configuration object. Also
check the [[azimuthal_dependence]] flag.
<<PHS none: execute tests>>=
call test (phs_none_1, "phs_none_1", &
"phase-space configuration dummy", &
u, results)
<<PHS none: test declarations>>=
public :: phs_none_1
<<PHS none: tests>>=
subroutine phs_none_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
class(phs_config_t), allocatable :: phs_data
real(default) :: sqrts
write (u, "(A)") "* Test output: phs_none_1"
write (u, "(A)") "* Purpose: display &
&phase-space configuration data"
write (u, "(A)")
allocate (phs_none_config_t :: phs_data)
call phs_data%init (process_data, model)
sqrts = 1000._default
call phs_data%configure (sqrts, azimuthal_dependence=.false.)
call phs_data%write (u)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_none_1"
end subroutine phs_none_1
@ %def phs_none_1
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\clearpage
\section{Single-particle phase space}
This module implements the phase space for a single particle, i.e., the solid
angle, in a straightforward parameterization with a single channel. The
phase-space implementation may be used either for $1\to 2$ decays or for $2\to
2$ scattering processes, so the number of incoming particles is the only free
parameter in the configuration. In the latter case, we should restrict its
use to non-resonant s-channel processes, because there is no mapping of the
scattering angle.
(We might extend this later to account for generic $2\to 2$ situations, e.g.,
account for a Coulomb singularity or detect an s-channel resonance structure
that requires matching structure-function mappings.)
This is derived from the [[phs_test]] implementation in the
[[phs_base]] module above, even more simplified, but intended for
actual use.
<<[[phs_single.f90]]>>=
<<File header>>
module phs_single
<<Use kinds>>
<<Use strings>>
use io_units
use constants
use numeric_utils
use diagnostics
use os_interface
use lorentz
use physics_defs
use model_data
use flavors
use process_constants
use phs_base
<<Standard module head>>
<<PHS single: public>>
<<PHS single: types>>
contains
<<PHS single: procedures>>
end module phs_single
@ %def phs_single
@
\subsection{Configuration}
<<PHS single: public>>=
public :: phs_single_config_t
<<PHS single: types>>=
type, extends (phs_config_t) :: phs_single_config_t
contains
<<PHS single: phs single config: TBP>>
end type phs_single_config_t
@ %def phs_single_config_t
@ The finalizer is empty.
<<PHS single: phs single config: TBP>>=
procedure :: final => phs_single_config_final
<<PHS single: procedures>>=
subroutine phs_single_config_final (object)
class(phs_single_config_t), intent(inout) :: object
end subroutine phs_single_config_final
@ %def phs_single_final
@ Output.
<<PHS single: phs single config: TBP>>=
procedure :: write => phs_single_config_write
<<PHS single: procedures>>=
subroutine phs_single_config_write (object, unit, include_id)
class(phs_single_config_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: include_id
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") "Partonic phase-space configuration (single-particle):"
call object%base_write (unit)
end subroutine phs_single_config_write
@ %def phs_single_config_write
@ Configuration: there is only one channel and two parameters. The
second parameter is the azimuthal angle, which may be a flat dimension.
<<PHS single: phs single config: TBP>>=
procedure :: configure => phs_single_config_configure
<<PHS single: procedures>>=
subroutine phs_single_config_configure (phs_config, sqrts, &
sqrts_fixed, cm_frame, azimuthal_dependence, rebuild, ignore_mismatch, &
nlo_type, subdir)
class(phs_single_config_t), intent(inout) :: phs_config
real(default), intent(in) :: sqrts
logical, intent(in), optional :: sqrts_fixed
logical, intent(in), optional :: cm_frame
logical, intent(in), optional :: azimuthal_dependence
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
integer, intent(in), optional :: nlo_type
type(string_t), intent(in), optional :: subdir
if (.not. present (nlo_type)) &
phs_config%nlo_type = BORN
if (phs_config%n_out == 2) then
phs_config%n_channel = 1
phs_config%n_par = 2
phs_config%sqrts = sqrts
if (present (sqrts_fixed)) phs_config%sqrts_fixed = sqrts_fixed
if (present (cm_frame)) phs_config%cm_frame = cm_frame
if (present (azimuthal_dependence)) then
phs_config%azimuthal_dependence = azimuthal_dependence
if (.not. azimuthal_dependence) then
allocate (phs_config%dim_flat (1))
phs_config%dim_flat(1) = 2
end if
end if
if (allocated (phs_config%channel)) deallocate (phs_config%channel)
allocate (phs_config%channel (1))
call phs_config%compute_md5sum ()
else
call msg_fatal ("Single-particle phase space requires n_out = 2")
end if
end subroutine phs_single_config_configure
@ %def phs_single_config_configure
@ Startup message, after configuration is complete.
<<PHS single: phs single config: TBP>>=
procedure :: startup_message => phs_single_config_startup_message
<<PHS single: procedures>>=
subroutine phs_single_config_startup_message (phs_config, unit)
class(phs_single_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
call phs_config%base_startup_message (unit)
write (msg_buffer, "(A,2(1x,I0,1x,A))") &
"Phase space: single-particle"
call msg_message (unit = unit)
end subroutine phs_single_config_startup_message
@ %def phs_single_config_startup_message
@ Allocate an instance: the actual phase-space object.
<<PHS single: phs single config: TBP>>=
procedure, nopass :: allocate_instance => phs_single_config_allocate_instance
<<PHS single: procedures>>=
subroutine phs_single_config_allocate_instance (phs)
class(phs_t), intent(inout), pointer :: phs
allocate (phs_single_t :: phs)
end subroutine phs_single_config_allocate_instance
@ %def phs_single_config_allocate_instance
@
\subsection{Kinematics implementation}
We generate $\cos\theta$ and $\phi$ uniformly, covering the solid angle.
Note: The incoming momenta must be in the c.m. system.
<<PHS single: public>>=
public :: phs_single_t
<<PHS single: types>>=
type, extends (phs_t) :: phs_single_t
contains
<<PHS single: phs single: TBP>>
end type phs_single_t
@ %def phs_single_t
@ Output. The [[verbose]] setting is irrelevant, we just display the contents
of the base object.
<<PHS single: phs single: TBP>>=
procedure :: write => phs_single_write
<<PHS single: procedures>>=
subroutine phs_single_write (object, unit, verbose)
class(phs_single_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit)
call object%base_write (u)
end subroutine phs_single_write
@ %def phs_single_write
@ The finalizer is empty.
<<PHS single: phs single: TBP>>=
procedure :: final => phs_single_final
<<PHS single: procedures>>=
subroutine phs_single_final (object)
class(phs_single_t), intent(inout) :: object
end subroutine phs_single_final
@ %def phs_single_final
@ Initialization. We allocate arrays ([[base_init]]) and adjust the
phase-space volume. The massless two-particle phase space volume is
\begin{equation}
\Phi_2 = \frac{1}{4(2\pi)^5} = 2.55294034614 \times 10^{-5}
\end{equation}
For a decay with nonvanishing masses ($m_3$, $m_4$), there is a correction
factor
\begin{equation}
\Phi_2(m) / \Phi_2(0) = \frac{1}{\hat s}
\lambda^{1/2}(\hat s, m_3^2, m_4^2).
\end{equation}
For a scattering process with nonvanishing masses, the correction
factor is
\begin{equation}
\Phi_2(m) / \Phi_2(0) = \frac{1}{\hat s ^ 2}
\lambda^{1/2}(\hat s, m_1^2, m_2^2)\,
\lambda^{1/2}(\hat s, m_3^2, m_4^2).
\end{equation}
If the energy is fixed, this is constant. Otherwise, we have to account for
varying $\hat s$.
<<PHS single: phs single: TBP>>=
procedure :: init => phs_single_init
<<PHS single: procedures>>=
subroutine phs_single_init (phs, phs_config)
class(phs_single_t), intent(out) :: phs
class(phs_config_t), intent(in), target :: phs_config
call phs%base_init (phs_config)
phs%volume = 1 / (4 * twopi5)
call phs%compute_factor ()
end subroutine phs_single_init
@ %def phs_single_init
@ Compute the correction factor for nonzero masses. We do this during
initialization (when the incoming momenta [[p]] are undefined), unless
[[sqrts]] is variable. We do this again once for each phase-space point, but
then we skip the calculation if [[sqrts]] is fixed.
<<PHS single: phs single: TBP>>=
procedure :: compute_factor => phs_single_compute_factor
<<PHS single: procedures>>=
subroutine phs_single_compute_factor (phs)
class(phs_single_t), intent(inout) :: phs
real(default) :: s_hat
select case (phs%config%n_in)
case (1)
if (.not. phs%p_defined) then
if (sum (phs%m_out) < phs%m_in(1)) then
s_hat = phs%m_in(1) ** 2
phs%f(1) = 1 / s_hat &
* sqrt (lambda (s_hat, phs%m_out(1)**2, phs%m_out(2)**2))
else
print *, "m_in = ", phs%m_in
print *, "m_out = ", phs%m_out
call msg_fatal ("Decay is kinematically forbidden")
end if
end if
case (2)
if (phs%config%sqrts_fixed) then
if (phs%p_defined) return
s_hat = phs%config%sqrts ** 2
else
if (.not. phs%p_defined) return
s_hat = sum (phs%p) ** 2
end if
if (sum (phs%m_in)**2 < s_hat .and. sum (phs%m_out)**2 < s_hat) then
phs%f(1) = 1 / s_hat * &
( lambda (s_hat, phs%m_in (1)**2, phs%m_in (2)**2) &
* lambda (s_hat, phs%m_out(1)**2, phs%m_out(2)**2) ) &
** 0.25_default
else
phs%f(1) = 0
end if
end select
end subroutine phs_single_compute_factor
@ %def phs_single_compute_factor
@ Evaluation. We uniformly generate $\cos\theta$ and
$\phi$, with Jacobian normalized to one.
There is only a single channel, so the second subroutine does nothing.
Note: the current implementation works for elastic scattering only.
<<PHS single: phs single: TBP>>=
procedure :: evaluate_selected_channel => phs_single_evaluate_selected_channel
procedure :: evaluate_other_channels => phs_single_evaluate_other_channels
<<PHS single: procedures>>=
subroutine phs_single_evaluate_selected_channel (phs, c_in, r_in)
class(phs_single_t), intent(inout) :: phs
integer, intent(in) :: c_in
real(default), intent(in), dimension(:) :: r_in
!!! !!! !!! Catching a gfortran bogus warning
type(vector4_t), dimension(2) :: p_dum
if (phs%p_defined) then
call phs%select_channel (c_in)
phs%r(:,c_in) = r_in
select case (phs%config%n_in)
case (2)
if (all (phs%m_in == phs%m_out)) then
call compute_kinematics_solid_angle (phs%p, phs%q, r_in)
else
call msg_bug ("PHS single: inelastic scattering not implemented")
end if
case (1)
!!! !!! !!! Catching a gfortran bogus warning
!!! call compute_kinematics_solid_angle (phs%decay_p (), phs%q, x)
p_dum = phs%decay_p ()
call compute_kinematics_solid_angle (p_dum, phs%q, r_in)
end select
call phs%compute_factor ()
phs%q_defined = .true.
phs%r_defined = .true.
end if
end subroutine phs_single_evaluate_selected_channel
subroutine phs_single_evaluate_other_channels (phs, c_in)
class(phs_single_t), intent(inout) :: phs
integer, intent(in) :: c_in
end subroutine phs_single_evaluate_other_channels
@ %def phs_single_evaluate_selected_channel
@ %def phs_single_evaluate_other_channels
@ Auxiliary: split a decaying particle at rest into the decay products,
aligned along the $z$ axis.
<<PHS single: phs single: TBP>>=
procedure :: decay_p => phs_single_decay_p
<<PHS single: procedures>>=
function phs_single_decay_p (phs) result (p)
class(phs_single_t), intent(in) :: phs
type(vector4_t), dimension(2) :: p
real(default) :: k
real(default), dimension(2) :: E
k = sqrt (lambda (phs%m_in(1) ** 2, phs%m_out(1) ** 2, phs%m_out(2) ** 2)) &
/ (2 * phs%m_in(1))
E = sqrt (phs%m_out ** 2 + k ** 2)
p(1) = vector4_moving (E(1), k, 3)
p(2) = vector4_moving (E(2),-k, 3)
end function phs_single_decay_p
@ %def phs_single_decay_p
@ Inverse evaluation.
<<PHS single: phs single: TBP>>=
procedure :: inverse => phs_single_inverse
<<PHS single: procedures>>=
subroutine phs_single_inverse (phs)
class(phs_single_t), intent(inout) :: phs
real(default), dimension(:), allocatable :: x
if (phs%p_defined .and. phs%q_defined) then
call phs%select_channel ()
allocate (x (phs%config%n_par))
call inverse_kinematics_solid_angle (phs%p, phs%q, x)
phs%r(:,1) = x
call phs%compute_factor ()
phs%r_defined = .true.
end if
end subroutine phs_single_inverse
@ %def phs_single_inverse
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[phs_single_ut.f90]]>>=
<<File header>>
module phs_single_ut
use unit_tests
use phs_single_uti
<<Standard module head>>
<<PHS single: public test>>
contains
<<PHS single: test driver>>
end module phs_single_ut
@ %def phs_single_ut
@
<<[[phs_single_uti.f90]]>>=
<<File header>>
module phs_single_uti
<<Use kinds>>
<<Use strings>>
use flavors
use lorentz
use model_data
use process_constants
use phs_base
use phs_single
use phs_base_ut, only: init_test_process_data, init_test_decay_data
<<Standard module head>>
<<PHS single: test declarations>>
contains
<<PHS single: tests>>
end module phs_single_uti
@ %def phs_single_ut
@ API: driver for the unit tests below.
<<PHS single: public test>>=
public :: phs_single_test
<<PHS single: test driver>>=
subroutine phs_single_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<PHS single: execute tests>>
end subroutine phs_single_test
@ %def phs_single_test
@
\subsubsection{Phase-space configuration data}
Construct and display a test phase-space configuration object. Also
check the [[azimuthal_dependence]] flag.
<<PHS single: execute tests>>=
call test (phs_single_1, "phs_single_1", &
"phase-space configuration", &
u, results)
<<PHS single: test declarations>>=
public :: phs_single_1
<<PHS single: tests>>=
subroutine phs_single_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
class(phs_config_t), allocatable :: phs_data
real(default) :: sqrts
write (u, "(A)") "* Test output: phs_single_1"
write (u, "(A)") "* Purpose: initialize and display &
&phase-space configuration data"
write (u, "(A)")
call model%init_test ()
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_single_1"), process_data)
allocate (phs_single_config_t :: phs_data)
call phs_data%init (process_data, model)
sqrts = 1000._default
call phs_data%configure (sqrts, azimuthal_dependence=.false.)
call phs_data%write (u)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_single_1"
end subroutine phs_single_1
@ %def phs_single_1
@
\subsubsection{Phase space evaluation}
Compute kinematics for given parameters, also invert the calculation.
<<PHS single: execute tests>>=
call test (phs_single_2, "phs_single_2", &
"phase-space evaluation", &
u, results)
<<PHS single: test declarations>>=
public :: phs_single_2
<<PHS single: tests>>=
subroutine phs_single_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(process_constants_t) :: process_data
real(default) :: sqrts, E
class(phs_config_t), allocatable, target :: phs_data
class(phs_t), pointer :: phs => null ()
type(vector4_t), dimension(2) :: p, q
write (u, "(A)") "* Test output: phs_single_2"
write (u, "(A)") "* Purpose: test simple two-channel phase space"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_single_2"), process_data)
allocate (phs_single_config_t :: phs_data)
call phs_data%init (process_data, model)
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%write (u, verbose=.true.)
write (u, "(A)")
write (u, "(A)") "* Set incoming momenta"
write (u, "(A)")
E = sqrts / 2
p(1) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
p(2) = vector4_moving (E,-sqrt (E**2 - flv%get_mass ()**2), 3)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point &
&for x = 0.5, 0.125"
write (u, "(A)")
call phs%evaluate_selected_channel (1, [0.5_default, 0.125_default])
call phs%evaluate_other_channels (1)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call phs%write (u)
call phs%final ()
deallocate (phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_single_2"
end subroutine phs_single_2
@ %def phs_single_2
@
\subsubsection{Phase space for non-c.m. system}
Compute kinematics for given parameters, also invert the calculation.
Since this will involve cancellations, we call [[pacify]] to eliminate
numerical noise.
<<PHS single: execute tests>>=
call test (phs_single_3, "phs_single_3", &
"phase-space evaluation in lab frame", &
u, results)
<<PHS single: test declarations>>=
public :: phs_single_3
<<PHS single: tests>>=
subroutine phs_single_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(process_constants_t) :: process_data
real(default) :: sqrts, E
class(phs_config_t), allocatable, target :: phs_data
class(phs_t), pointer :: phs => null ()
type(vector4_t), dimension(2) :: p, q
type(lorentz_transformation_t) :: lt
write (u, "(A)") "* Test output: phs_single_3"
write (u, "(A)") "* Purpose: test simple two-channel phase space"
write (u, "(A)") "* without c.m. kinematics assumption"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_single_3"), process_data)
allocate (phs_single_config_t :: phs_data)
call phs_data%init (process_data, model)
sqrts = 1000._default
call phs_data%configure (sqrts, cm_frame=.false., sqrts_fixed=.false.)
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%write (u, verbose=.true.)
write (u, "(A)")
write (u, "(A)") "* Set incoming momenta in lab system"
write (u, "(A)")
lt = boost (0.1_default, 1) * boost (0.3_default, 3)
E = sqrts / 2
p(1) = lt * vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
p(2) = lt * vector4_moving (E,-sqrt (E**2 - flv%get_mass ()**2), 3)
call vector4_write (p(1), u)
call vector4_write (p(2), u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point &
&for x = 0.5, 0.125"
write (u, "(A)")
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%evaluate_selected_channel (1, [0.5_default, 0.125_default])
call phs%evaluate_other_channels (1)
call pacify (phs)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Extract outgoing momenta in lab system"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
call vector4_write (q(1), u)
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call pacify (phs)
call phs%write (u)
call phs%final ()
deallocate (phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_single_3"
end subroutine phs_single_3
@ %def phs_single_3
@
\subsubsection{Decay Phase space evaluation}
Compute kinematics for given parameters, also invert the calculation. This
time, implement a decay process.
<<PHS single: execute tests>>=
call test (phs_single_4, "phs_single_4", &
"decay phase-space evaluation", &
u, results)
<<PHS single: test declarations>>=
public :: phs_single_4
<<PHS single: tests>>=
subroutine phs_single_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(process_constants_t) :: process_data
class(phs_config_t), allocatable, target :: phs_data
class(phs_t), pointer :: phs => null ()
type(vector4_t), dimension(1) :: p
type(vector4_t), dimension(2) :: q
write (u, "(A)") "* Test output: phs_single_4"
write (u, "(A)") "* Purpose: test simple two-channel phase space"
write (u, "(A)")
call model%init_test ()
call model%set_par (var_str ("ff"), 0.4_default)
call model%set_par (var_str ("mf"), &
model%get_real (var_str ("ff")) * model%get_real (var_str ("ms")))
call flv%init (25, model)
write (u, "(A)") "* Initialize a decay and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_decay_data (var_str ("phs_single_4"), process_data)
allocate (phs_single_config_t :: phs_data)
call phs_data%init (process_data, model)
call phs_data%configure (flv%get_mass ())
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%write (u, verbose=.true.)
write (u, "(A)")
write (u, "(A)") "* Set incoming momenta"
write (u, "(A)")
p(1) = vector4_at_rest (flv%get_mass ())
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point &
&for x = 0.5, 0.125"
write (u, "(A)")
call phs%evaluate_selected_channel (1, [0.5_default, 0.125_default])
call phs%evaluate_other_channels (1)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs_data%configure (flv%get_mass ())
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call phs%write (u)
call phs%final ()
deallocate (phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_single_4"
end subroutine phs_single_4
@ %def phs_single_4
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Flat RAMBO phase space}
This module implements the flat \texttt{RAMBO} phase space for massless and massive particles using the minimal d.o.f $3n - 4$ in a straightforward parameterization with a single channel.
We generate $n$ mass systems $M_i$ with $M_0 = \sqrt{s}$ and $M_{n} = 0$.
We let each mass system decay $1 \rightarrow 2$ in a four-momentum conserving way.
The four-momenta of the two particles are generated back-to-back where we map the d.o.f. to energy, azimuthal and polar angle.
The particle momenta are then boosted to CMS by an appriopriate boost using the kinematics of the parent mass system.
<<[[phs_rambo.f90]]>>=
<<File header>>
module phs_rambo
<<Use kinds>>
<<Use strings>>
use io_units
use constants
use numeric_utils
use format_defs, only: FMT_19
use permutations, only: factorial
use diagnostics
use os_interface
use lorentz
use physics_defs
use model_data
use flavors
use process_constants
use phs_base
<<Standard module head>>
<<PHS rambo: parameters>>
<<PHS rambo: types>>
<<PHS rambo: public>>
contains
<<PHS rambo: procedures>>
end module phs_rambo
@ %def phs_rambo
@
\subsection{Configuration}
<<PHS rambo: public>>=
public :: phs_rambo_config_t
<<PHS rambo: types>>=
type, extends (phs_config_t) :: phs_rambo_config_t
contains
<<PHS rambo: phs rambo config: TBP>>
end type phs_rambo_config_t
@ %def phs_rambo_config_t
@ The finalizer is empty.
<<PHS rambo: phs rambo config: TBP>>=
procedure :: final => phs_rambo_config_final
<<PHS rambo: procedures>>=
subroutine phs_rambo_config_final (object)
class(phs_rambo_config_t), intent(inout) :: object
end subroutine phs_rambo_config_final
@ %def phs_rambo_final
@ Output.
<<PHS rambo: phs rambo config: TBP>>=
procedure :: write => phs_rambo_config_write
<<PHS rambo: procedures>>=
subroutine phs_rambo_config_write (object, unit, include_id)
class(phs_rambo_config_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: include_id
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") "Partonic, flat phase-space configuration (RAMBO):"
call object%base_write (unit)
end subroutine phs_rambo_config_write
@ %def phs_rambo_config_write
@ Configuration: there is only one channel and $3n - 4$ parameters.
<<PHS rambo: phs rambo config: TBP>>=
procedure :: configure => phs_rambo_config_configure
<<PHS rambo: procedures>>=
subroutine phs_rambo_config_configure (phs_config, sqrts, &
sqrts_fixed, cm_frame, azimuthal_dependence, rebuild, ignore_mismatch, &
nlo_type, subdir)
class(phs_rambo_config_t), intent(inout) :: phs_config
real(default), intent(in) :: sqrts
logical, intent(in), optional :: sqrts_fixed
logical, intent(in), optional :: cm_frame
logical, intent(in), optional :: azimuthal_dependence
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
integer, intent(in), optional :: nlo_type
type(string_t), intent(in), optional :: subdir
if (.not. present (nlo_type)) &
phs_config%nlo_type = BORN
if (phs_config%n_out < 2) then
call msg_fatal ("RAMBO phase space requires n_out >= 2")
end if
phs_config%n_channel = 1
phs_config%n_par = 3 * phs_config%n_out - 4
phs_config%sqrts = sqrts
if (present (sqrts_fixed)) phs_config%sqrts_fixed = sqrts_fixed
if (present (cm_frame)) phs_config%cm_frame = cm_frame
if (allocated (phs_config%channel)) deallocate (phs_config%channel)
allocate (phs_config%channel (1))
call phs_config%compute_md5sum ()
end subroutine phs_rambo_config_configure
@ %def phs_rambo_config_configure
@ Startup message, after configuration is complete.
<<PHS rambo: phs rambo config: TBP>>=
procedure :: startup_message => phs_rambo_config_startup_message
<<PHS rambo: procedures>>=
subroutine phs_rambo_config_startup_message (phs_config, unit)
class(phs_rambo_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
call phs_config%base_startup_message (unit)
write (msg_buffer, "(A,2(1x,I0,1x,A))") &
"Phase space: flat (RAMBO)"
call msg_message (unit = unit)
end subroutine phs_rambo_config_startup_message
@ %def phs_rambo_config_startup_message
@ Allocate an instance: the actual phase-space object.
<<PHS rambo: phs rambo config: TBP>>=
procedure, nopass :: allocate_instance => phs_rambo_config_allocate_instance
<<PHS rambo: procedures>>=
subroutine phs_rambo_config_allocate_instance (phs)
class(phs_t), intent(inout), pointer :: phs
allocate (phs_rambo_t :: phs)
end subroutine phs_rambo_config_allocate_instance
@ %def phs_rambo_config_allocate_instance
@
\subsection{Kinematics implementation}
We generate $n - 2$ mass systems $M_i$ with $M_0 = \sqrt{s}$ and $M_n = 0$...
Note: The incoming momenta must be in the c.m. system.
<<PHS rambo: public>>=
public :: phs_rambo_t
<<PHS rambo: types>>=
type, extends (phs_t) :: phs_rambo_t
real(default), dimension(:), allocatable :: k
real(default), dimension(:), allocatable :: m
contains
<<PHS rambo: phs rambo: TBP>>
end type phs_rambo_t
@ %def phs_rambo_t
@ Output.
<<PHS rambo: phs rambo: TBP>>=
procedure :: write => phs_rambo_write
<<PHS rambo: procedures>>=
subroutine phs_rambo_write (object, unit, verbose)
class(phs_rambo_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit)
call object%base_write (u)
write (u, "(1X,A)") "Intermediate masses (massless):"
write (u, "(3X,999(" // FMT_19 // "))") object%k
write (u, "(1X,A)") "Intermediate masses (massive):"
write (u, "(3X,999(" // FMT_19 // "))") object%m
end subroutine phs_rambo_write
@ %def phs_rambo_write
@ The finalizer is empty.
<<PHS rambo: phs rambo: TBP>>=
procedure :: final => phs_rambo_final
<<PHS rambo: procedures>>=
subroutine phs_rambo_final (object)
class(phs_rambo_t), intent(inout) :: object
end subroutine phs_rambo_final
@ %def phs_rambo_final
@ Initialization. We allocate arrays ([[base_init]]) and adjust the
phase-space volume.
The energy dependent factor of $s^{n - 2}$ is applied later.
<<PHS rambo: phs rambo: TBP>>=
procedure :: init => phs_rambo_init
<<PHS rambo: procedures>>=
subroutine phs_rambo_init (phs, phs_config)
class(phs_rambo_t), intent(out) :: phs
class(phs_config_t), intent(in), target :: phs_config
call phs%base_init (phs_config)
associate (n => phs%config%n_out)
select case (n)
case (1)
if (sum (phs%m_out) > phs%m_in (1)) then
print *, "m_in = ", phs%m_in
print *, "m_out = ", phs%m_out
call msg_fatal ("[phs_rambo_init] Decay is kinematically forbidden.")
end if
end select
allocate (phs%k(n), source = 0._default)
allocate (phs%m(n), source = 0._default)
phs%volume = 1. / (twopi)**(3 * n) &
* (pi / 2.)**(n - 1) / (factorial(n - 1) * factorial(n - 2))
end associate
end subroutine phs_rambo_init
@ %def phs_rambo_init
@ Evaluation. There is only one channel for RAMBO, so the second subroutine does nothing.
Note: the current implementation works for elastic scattering only.
<<PHS rambo: phs rambo: TBP>>=
procedure :: evaluate_selected_channel => phs_rambo_evaluate_selected_channel
procedure :: evaluate_other_channels => phs_rambo_evaluate_other_channels
<<PHS rambo: procedures>>=
subroutine phs_rambo_evaluate_selected_channel (phs, c_in, r_in)
class(phs_rambo_t), intent(inout) :: phs
integer, intent(in) :: c_in
real(default), intent(in), dimension(:) :: r_in
type(vector4_t), dimension(2) :: p_rest, p_boosted
type(vector4_t) :: q
real(default), dimension(2) :: r_angle
integer :: i
if (.not. phs%p_defined) return
call phs%select_channel (c_in)
phs%r(:,c_in) = r_in
associate (n => phs%config%n_out, m => phs%m)
call phs%generate_intermediates (r_in(:n - 2))
q = sum (phs%p)
do i = 2, n
r_angle(1) = r_in(n - 5 + 2 * i)
r_angle(2) = r_in(n - 4 + 2 * i)
call phs%decay_intermediate (i, r_angle, p_rest)
p_boosted = boost(q, m(i - 1)) * p_rest
q = p_boosted(1)
phs%q(i - 1) = p_boosted(2)
end do
phs%q(n) = q
end associate
phs%q_defined = .true.
phs%r_defined = .true.
end subroutine phs_rambo_evaluate_selected_channel
subroutine phs_rambo_evaluate_other_channels (phs, c_in)
class(phs_rambo_t), intent(inout) :: phs
integer, intent(in) :: c_in
end subroutine phs_rambo_evaluate_other_channels
@ %def phs_rambo_evaluate_selected_channel
@ %def phs_rambo_evaluate_other_channels
@ Decay intermediate mass system $M_{i - 1}$ into a on-shell particle with mass
$m_{i - 1}$ and subsequent intermediate mass system with fixed $M_i$.
<<PHS rambo: phs rambo: TBP>>=
procedure, private :: decay_intermediate => phs_rambo_decay_intermediate
<<PHS rambo: procedures>>=
subroutine phs_rambo_decay_intermediate (phs, i, r_angle, p)
class(phs_rambo_t), intent(in) :: phs
integer, intent(in) :: i
real(default), dimension(2), intent(in) :: r_angle
type(vector4_t), dimension(2), intent(out) :: p
real(default) :: k_abs, cos_theta, phi
type(vector3_t):: k
real(default), dimension(2) :: E
cos_theta = 2. * r_angle(1) - 1.
phi = twopi * r_angle(2)
if (phi > pi) phi = phi - twopi
k_abs = sqrt (lambda (phs%m(i - 1)**2, phs%m(i)**2, phs%m_out(i - 1)**2)) &
/ (2. * phs%m(i - 1))
k = k_abs * [cos(phi) * sqrt(1. - cos_theta**2), &
sin(phi) * sqrt(1. - cos_theta**2), cos_theta]
E(1) = sqrt (phs%m(i)**2 + k_abs**2)
E(2) = sqrt (phs%m_out(i - 1)**2 + k_abs**2)
p(1) = vector4_moving (E(1), -k)
p(2) = vector4_moving (E(2), k)
end subroutine phs_rambo_decay_intermediate
@ %def phs_rambo_decay_intermediate
@ Generate intermediate masses.
<<PHS rambo: parameters>>=
integer, parameter :: BISECT_MAX_ITERATIONS = 1000
real(default), parameter :: BISECT_MIN_PRECISION = tiny_10
<<PHS rambo: phs rambo: TBP>>=
procedure, private :: generate_intermediates => phs_rambo_generate_intermediates
procedure, private :: invert_intermediates => phs_rambo_invert_intermediates
<<PHS rambo: procedures>>=
subroutine phs_rambo_generate_intermediates (phs, r)
class(phs_rambo_t), intent(inout) :: phs
real(default), dimension(:), intent(in) :: r
integer :: i, j
associate (n => phs%config%n_out, k => phs%k, m => phs%m)
m(1) = invariant_mass (sum (phs%p))
m(n) = phs%m_out (n)
call calculate_k (r)
do i = 2, n - 1
m(i) = k(i) + sum (phs%m_out (i:n))
end do
! Massless volume times reweighting for massive volume
phs%f(1) = k(1)**(2 * n - 4) &
* 8. * rho(m(n - 1), phs%m_out(n), phs%m_out(n - 1))
do i = 2, n - 1
phs%f(1) = phs%f(1) * &
rho(m(i - 1), m(i), phs%m_out(i - 1)) / &
rho(k(i - 1), k(i), 0._default) * &
M(i) / K(i)
end do
end associate
contains
subroutine calculate_k (r)
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), allocatable :: u
integer :: i
associate (n => phs%config%n_out, k => phs%k, m => phs%m)
k = 0
k(1) = m(1) - sum(phs%m_out(1:n))
allocate (u(2:n - 1), source=0._default)
call solve_for_u (r, u)
do i = 2, n - 1
k(i) = sqrt (u(i) * k(i - 1)**2)
end do
end associate
end subroutine calculate_k
subroutine solve_for_u (r, u)
real(default), dimension(phs%config%n_out - 2), intent(in) :: r
real(default), dimension(2:phs%config%n_out - 1), intent(out) :: u
integer :: i, j
real(default) :: f, f_mid, xl, xr, xmid
associate (n => phs%config%n_out)
do i = 2, n - 1
xl = 0
xr = 1
if (r(i - 1) == 1 .or. r(i - 1) == 0) then
u(i) = r(i - 1)
else
do j = 1, BISECT_MAX_ITERATIONS
xmid = (xl + xr) / 2.
f = f_rambo (xl, n - i) - r(i - 1)
f_mid = f_rambo (xmid, n - i) - r(i - 1)
if (f * f_mid > 0) then
xl = xmid
else
xr = xmid
end if
if (abs(xl - xr) < BISECT_MIN_PRECISION) exit
end do
u(i) = xmid
end if
end do
end associate
end subroutine solve_for_u
real(default) function f_rambo(u, n)
real(default), intent(in) :: u
integer, intent(in) :: n
f_rambo = (n + 1) * u**n - n * u**(n + 1)
end function f_rambo
real(default) function rho (M1, M2, m)
real(default), intent(in) :: M1, M2, m
real(default) :: MP, MM
rho = sqrt ((M1**2 - (M2 + m)**2) * (M1**2 - (M2 - m)**2))
! MP = (M1 - (M2 + m)) * (M1 + (M2 + m))
! MM = (M1 - (M2 - m)) * (M1 + (M2 - m))
! rho = sqrt (MP) * sqrt (MM)
rho = rho / (8._default * M1**2)
end function rho
end subroutine phs_rambo_generate_intermediates
subroutine phs_rambo_invert_intermediates (phs)
class(phs_rambo_t), intent(inout) :: phs
real(default) :: u
integer :: i
associate (n => phs%config%n_out, k => phs%k, m => phs%m)
k = m
do i = 1, n - 1
k(i) = k(i) - sum (phs%m_out(i:n))
end do
do i = 2, n - 1
u = (k(i) / k(i - 1))**2
phs%r(i - 1, 1) = (n + 1 - i) * u**(n - i) &
- (n - i) * u**(n + 1 - i)
end do
end associate
end subroutine phs_rambo_invert_intermediates
@ %def phs_rambo_generate_intermediates
@ Inverse evaluation.
<<PHS rambo: phs rambo: TBP>>=
procedure :: inverse => phs_rambo_inverse
<<PHS rambo: procedures>>=
subroutine phs_rambo_inverse (phs)
class(phs_rambo_t), intent(inout) :: phs
type(vector4_t), dimension(:), allocatable :: q
type(vector4_t) :: p
type(lorentz_transformation_t) :: L
real(default) :: phi, cos_theta
integer :: i
if (.not. (phs%p_defined .and. phs%q_defined)) return
call phs%select_channel ()
associate (n => phs%config%n_out, m => phs%m)
allocate(q(n))
m(1) = invariant_mass (sum (phs%p))
q(1) = vector4_at_rest (m(1))
q(n) = phs%q(n)
do i = 2, n - 1
q(i) = q(i) + sum (phs%q(i:n))
m(i) = invariant_mass (q(i))
end do
call phs%invert_intermediates ()
do i = 2, n
L = inverse (boost (q(i - 1), m(i - 1)))
p = L * phs%q(i - 1)
phi = azimuthal_angle (p); cos_theta = polar_angle_ct (p)
phs%r(n - 5 + 2 * i, 1) = (cos_theta + 1.) / 2.
phs%r(n - 4 + 2 * i, 1) = phi / twopi
end do
end associate
phs%r_defined = .true.
end subroutine phs_rambo_inverse
@ %def phs_rambo_inverse
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[phs_rambo_ut.f90]]>>=
<<File header>>
module phs_rambo_ut
use unit_tests
use phs_rambo_uti
<<Standard module head>>
<<PHS rambo: public test>>
contains
<<PHS rambo: test driver>>
end module phs_rambo_ut
@ %def phs_rambo_ut
@
<<[[phs_rambo_uti.f90]]>>=
<<File header>>
module phs_rambo_uti
<<Use kinds>>
<<Use strings>>
use flavors
use lorentz
use model_data
use process_constants
use phs_base
use phs_rambo
use phs_base_ut, only: init_test_process_data, init_test_decay_data
<<Standard module head>>
<<PHS rambo: test declarations>>
contains
<<PHS rambo: tests>>
end module phs_rambo_uti
@ %def phs_rambo_ut
@ API: driver for the unit tests below.
<<PHS rambo: public test>>=
public :: phs_rambo_test
<<PHS rambo: test driver>>=
subroutine phs_rambo_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<PHS rambo: execute tests>>
end subroutine phs_rambo_test
@ %def phs_rambo_test
@
\subsubsection{Phase-space configuration data}
Construct and display a test phase-space configuration object. Also
check the [[azimuthal_dependence]] flag.
<<PHS rambo: execute tests>>=
call test (phs_rambo_1, "phs_rambo_1", &
"phase-space configuration", &
u, results)
<<PHS rambo: test declarations>>=
public :: phs_rambo_1
<<PHS rambo: tests>>=
subroutine phs_rambo_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
class(phs_config_t), allocatable :: phs_data
real(default) :: sqrts
write (u, "(A)") "* Test output: phs_rambo_1"
write (u, "(A)") "* Purpose: initialize and display &
&phase-space configuration data"
write (u, "(A)")
call model%init_test ()
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_rambo_1"), process_data)
allocate (phs_rambo_config_t :: phs_data)
call phs_data%init (process_data, model)
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs_data%write (u)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_rambo_1"
end subroutine phs_rambo_1
@ %def phs_rambo_1
@
\subsubsection{Phase space evaluation}
Compute kinematics for given parameters, also invert the calculation.
<<PHS rambo: execute tests>>=
call test (phs_rambo_2, "phs_rambo_2", &
"phase-space evaluation", &
u, results)
<<PHS rambo: test declarations>>=
public :: phs_rambo_2
<<PHS rambo: tests>>=
subroutine phs_rambo_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(process_constants_t) :: process_data
real(default) :: sqrts, E
class(phs_config_t), allocatable, target :: phs_data
class(phs_t), pointer :: phs => null ()
type(vector4_t), dimension(2) :: p, q
write (u, "(A)") "* Test output: phs_rambo_2"
write (u, "(A)") "* Purpose: test simple two-channel phase space"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_rambo_2"), process_data)
allocate (phs_rambo_config_t :: phs_data)
call phs_data%init (process_data, model)
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%write (u, verbose=.true.)
write (u, "(A)")
write (u, "(A)") "* Set incoming momenta"
write (u, "(A)")
E = sqrts / 2
p(1) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
p(2) = vector4_moving (E,-sqrt (E**2 - flv%get_mass ()**2), 3)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point &
&for x = 0.5, 0.125"
write (u, "(A)")
call phs%evaluate_selected_channel (1, [0.5_default, 0.125_default])
call phs%evaluate_other_channels (1)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call phs%write (u)
call phs%final ()
deallocate (phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_rambo_2"
end subroutine phs_rambo_2
@ %def phs_rambo_2
@
\subsubsection{Phase space for non-c.m. system}
Compute kinematics for given parameters, also invert the calculation.
Since this will involve cancellations, we call [[pacify]] to eliminate
numerical noise.
<<PHS rambo: execute tests>>=
call test (phs_rambo_3, "phs_rambo_3", &
"phase-space evaluation in lab frame", &
u, results)
<<PHS rambo: test declarations>>=
public :: phs_rambo_3
<<PHS rambo: tests>>=
subroutine phs_rambo_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(process_constants_t) :: process_data
real(default) :: sqrts, E
class(phs_config_t), allocatable, target :: phs_data
class(phs_t), pointer :: phs => null ()
type(vector4_t), dimension(2) :: p, q
type(lorentz_transformation_t) :: lt
write (u, "(A)") "* Test output: phs_rambo_3"
write (u, "(A)") "* Purpose: phase-space evaluation in lab frame"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_rambo_3"), process_data)
allocate (phs_rambo_config_t :: phs_data)
call phs_data%init (process_data, model)
sqrts = 1000._default
call phs_data%configure (sqrts, cm_frame=.false., sqrts_fixed=.false.)
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%write (u, verbose=.true.)
write (u, "(A)")
write (u, "(A)") "* Set incoming momenta in lab system"
write (u, "(A)")
lt = boost (0.1_default, 1) * boost (0.3_default, 3)
E = sqrts / 2
p(1) = lt * vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
p(2) = lt * vector4_moving (E,-sqrt (E**2 - flv%get_mass ()**2), 3)
call vector4_write (p(1), u)
call vector4_write (p(2), u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point &
&for x = 0.5, 0.125"
write (u, "(A)")
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%evaluate_selected_channel (1, [0.5_default, 0.125_default])
call phs%evaluate_other_channels (1)
call pacify (phs)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Extract outgoing momenta in lab system"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
call vector4_write (q(1), u)
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call pacify (phs)
call phs%write (u)
call phs%final ()
deallocate (phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_rambo_3"
end subroutine phs_rambo_3
@ %def phs_rambo_3
@
\subsubsection{Decay Phase space evaluation}
Compute kinematics for given parameters, also invert the calculation. This
time, implement a decay process.
<<PHS rambo: execute tests>>=
call test (phs_rambo_4, "phs_rambo_4", &
"decay phase-space evaluation", &
u, results)
<<PHS rambo: test declarations>>=
public :: phs_rambo_4
<<PHS rambo: tests>>=
subroutine phs_rambo_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(process_constants_t) :: process_data
class(phs_config_t), allocatable, target :: phs_data
class(phs_t), pointer :: phs => null ()
type(vector4_t), dimension(1) :: p
type(vector4_t), dimension(2) :: q
write (u, "(A)") "* Test output: phs_rambo_4"
write (u, "(A)") "* Purpose: test simple two-channel phase space"
write (u, "(A)")
call model%init_test ()
call model%set_par (var_str ("ff"), 0.4_default)
call model%set_par (var_str ("mf"), &
model%get_real (var_str ("ff")) * model%get_real (var_str ("ms")))
call flv%init (25, model)
write (u, "(A)") "* Initialize a decay and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_decay_data (var_str ("phs_rambo_4"), process_data)
allocate (phs_rambo_config_t :: phs_data)
call phs_data%init (process_data, model)
call phs_data%configure (flv%get_mass ())
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%write (u, verbose=.true.)
write (u, "(A)")
write (u, "(A)") "* Set incoming momenta"
write (u, "(A)")
p(1) = vector4_at_rest (flv%get_mass ())
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point &
&for x = 0.5, 0.125"
write (u, "(A)")
call phs%evaluate_selected_channel (1, [0.5_default, 0.125_default])
call phs%evaluate_other_channels (1)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs_data%configure (flv%get_mass ())
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call phs%write (u)
call phs%final ()
deallocate (phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_rambo_4"
end subroutine phs_rambo_4
@ %def phs_rambo_4
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Resonance Handler}
For various purposes (e.g., shower histories), we should extract the set of
resonances and resonant channels from a phase-space tree set. A few methods
do kinematics calculations specifically for those resonance data.
<<[[resonances.f90]]>>=
<<File header>>
module resonances
<<Use kinds>>
<<Use strings>>
<<Use debug>>
use string_utils, only: str
use format_utils, only: write_indent
use io_units
use diagnostics
use lorentz
use constants, only: one
use model_data, only: model_data_t
use flavors, only: flavor_t
<<Standard module head>>
<<Resonances: public>>
<<Resonances: parameters>>
<<Resonances: types>>
contains
<<Resonances: procedures>>
end module resonances
@ %def resonances
@
\subsection{Decay products (contributors)}
This stores the indices of the particles that contribute to a resonance, i.e.,
the decay products.
<<Resonances: public>>=
public :: resonance_contributors_t
<<Resonances: types>>=
type :: resonance_contributors_t
integer, dimension(:), allocatable :: c
contains
<<Resonances: resonance contributors: TBP>>
end type resonance_contributors_t
@ %def resonance_contributors_t
@ Equality (comparison)
<<Resonances: resonance contributors: TBP>>=
procedure, private :: resonance_contributors_equal
generic :: operator(==) => resonance_contributors_equal
<<Resonances: procedures>>=
elemental function resonance_contributors_equal (c1, c2) result (equal)
logical :: equal
class(resonance_contributors_t), intent(in) :: c1, c2
equal = allocated (c1%c) .and. allocated (c2%c)
if (equal) equal = size (c1%c) == size (c2%c)
if (equal) equal = all (c1%c == c2%c)
end function resonance_contributors_equal
@ %def resonance_contributors_equal
@ Assignment
<<Resonances: resonance contributors: TBP>>=
procedure, private :: resonance_contributors_assign
generic :: assignment(=) => resonance_contributors_assign
<<Resonances: procedures>>=
pure subroutine resonance_contributors_assign (contributors_out, contributors_in)
class(resonance_contributors_t), intent(inout) :: contributors_out
class(resonance_contributors_t), intent(in) :: contributors_in
if (allocated (contributors_out%c)) deallocate (contributors_out%c)
if (allocated (contributors_in%c)) then
allocate (contributors_out%c (size (contributors_in%c)))
contributors_out%c = contributors_in%c
end if
end subroutine resonance_contributors_assign
@ %def resonance_contributors_assign
@
\subsection{Resonance info object}
This data structure augments the set of resonance contributors by a flavor
object, such that we can perform calculations that take into
account the particle properties, including mass and width.
Avoiding nameclash with similar but different [[resonance_t]] of
[[phs_base]]:
<<Resonances: public>>=
public :: resonance_info_t
<<Resonances: types>>=
type :: resonance_info_t
type(flavor_t) :: flavor
type(resonance_contributors_t) :: contributors
contains
<<Resonances: resonance info: TBP>>
end type resonance_info_t
@ %def resonance_info_t
@
<<Resonances: resonance info: TBP>>=
procedure :: copy => resonance_info_copy
<<Resonances: procedures>>=
subroutine resonance_info_copy (resonance_in, resonance_out)
class(resonance_info_t), intent(in) :: resonance_in
type(resonance_info_t), intent(out) :: resonance_out
resonance_out%flavor = resonance_in%flavor
if (allocated (resonance_in%contributors%c)) then
associate (c => resonance_in%contributors%c)
allocate (resonance_out%contributors%c (size (c)))
resonance_out%contributors%c = c
end associate
end if
end subroutine resonance_info_copy
@ %def resonance_info_copy
@
<<Resonances: resonance info: TBP>>=
procedure :: write => resonance_info_write
<<Resonances: procedures>>=
subroutine resonance_info_write (resonance, unit, verbose)
class(resonance_info_t), intent(in) :: resonance
integer, optional, intent(in) :: unit
logical, optional, intent(in) :: verbose
integer :: u, i
logical :: verb
u = given_output_unit (unit); if (u < 0) return
verb = .true.; if (present (verbose)) verb = verbose
if (verb) then
write (u, '(A)', advance='no') "Resonance contributors: "
else
write (u, '(1x)', advance="no")
end if
if (allocated (resonance%contributors%c)) then
do i = 1, size(resonance%contributors%c)
write (u, '(I0,1X)', advance='no') resonance%contributors%c(i)
end do
else if (verb) then
write (u, "(A)", advance="no") "[not allocated]"
end if
if (resonance%flavor%is_defined ()) call resonance%flavor%write (u)
write (u, '(A)')
end subroutine resonance_info_write
@ %def resonance_info_write
@ Create a resonance-info object. The particle info may be available
in term of a flavor object or as a PDG code; in the latter case we
have to require a model data object that provides mass and width information.
<<Resonances: resonance info: TBP>>=
procedure, private :: resonance_info_init_pdg
procedure, private :: resonance_info_init_flv
generic :: init => resonance_info_init_pdg, resonance_info_init_flv
<<Resonances: procedures>>=
subroutine resonance_info_init_pdg (resonance, mom_id, pdg, model, n_out)
class(resonance_info_t), intent(out) :: resonance
integer, intent(in) :: mom_id
integer, intent(in) :: pdg, n_out
class(model_data_t), intent(in), target :: model
type(flavor_t) :: flv
if (debug_on) call msg_debug (D_PHASESPACE, "resonance_info_init_pdg")
call flv%init (pdg, model)
call resonance%init (mom_id, flv, n_out)
end subroutine resonance_info_init_pdg
subroutine resonance_info_init_flv (resonance, mom_id, flv, n_out)
class(resonance_info_t), intent(out) :: resonance
integer, intent(in) :: mom_id
type(flavor_t), intent(in) :: flv
integer, intent(in) :: n_out
integer :: i
logical, dimension(n_out) :: contrib
integer, dimension(n_out) :: tmp
if (debug_on) call msg_debug (D_PHASESPACE, "resonance_info_init_flv")
resonance%flavor = flv
do i = 1, n_out
tmp(i) = i
end do
contrib = btest (mom_id, tmp - 1)
allocate (resonance%contributors%c (count (contrib)))
resonance%contributors%c = pack (tmp, contrib)
end subroutine resonance_info_init_flv
@ %def resonance_info_init
@
<<Resonances: resonance info: TBP>>=
procedure, private :: resonance_info_equal
generic :: operator(==) => resonance_info_equal
<<Resonances: procedures>>=
elemental function resonance_info_equal (r1, r2) result (equal)
logical :: equal
class(resonance_info_t), intent(in) :: r1, r2
equal = r1%flavor == r2%flavor .and. r1%contributors == r2%contributors
end function resonance_info_equal
@ %def resonance_info_equal
@ With each resonance region we associate a Breit-Wigner function
\begin{equation*}
P = \frac{M_{res}^4}{(s - M_{res}^2)^2 + \Gamma_{res}^2 M_{res}^2},
\end{equation*}
where $s$ denotes the invariant mass of the outgoing momenta originating
from this resonance. Note that the $M_{res}^4$ in the nominator makes
the mapping a dimensionless quantity.
<<Resonances: resonance info: TBP>>=
procedure :: mapping => resonance_info_mapping
<<Resonances: procedures>>=
function resonance_info_mapping (resonance, s) result (bw)
real(default) :: bw
class(resonance_info_t), intent(in) :: resonance
real(default), intent(in) :: s
real(default) :: m, gamma
if (resonance%flavor%is_defined ()) then
m = resonance%flavor%get_mass ()
gamma = resonance%flavor%get_width ()
bw = m**4 / ((s - m**2)**2 + gamma**2 * m**2)
else
bw = one
end if
end function resonance_info_mapping
@ %def resonance_info_mapping
@ Used for building a resonance tree below.
<<Resonances: resonance info: TBP>>=
procedure, private :: get_n_contributors => resonance_info_get_n_contributors
procedure, private :: contains => resonance_info_contains
<<Resonances: procedures>>=
elemental function resonance_info_get_n_contributors (resonance) result (n)
class(resonance_info_t), intent(in) :: resonance
integer :: n
if (allocated (resonance%contributors%c)) then
n = size (resonance%contributors%c)
else
n = 0
end if
end function resonance_info_get_n_contributors
elemental function resonance_info_contains (resonance, c) result (flag)
class(resonance_info_t), intent(in) :: resonance
integer, intent(in) :: c
logical :: flag
if (allocated (resonance%contributors%c)) then
flag = any (resonance%contributors%c == c)
else
flag = .false.
end if
end function resonance_info_contains
@ %def resonance_info_get_n_contributors
@ %def resonance_info_contains
@
\subsection{Resonance history object}
This data structure stores a set of resonances, i.e., the resonances that
appear in a particular Feynman graph or, in the context of phase space, phase
space diagram.
<<Resonances: public>>=
public :: resonance_history_t
<<Resonances: types>>=
type :: resonance_history_t
type(resonance_info_t), dimension(:), allocatable :: resonances
integer :: n_resonances = 0
contains
<<Resonances: resonance history: TBP>>
end type resonance_history_t
@ %def resonance_history_t
@ Clear the resonance history. Assuming that there are no
pointer-allocated parts, a straightforward [[intent(out)]] will do.
<<Resonances: resonance history: TBP>>=
procedure :: clear => resonance_history_clear
<<Resonances: procedures>>=
subroutine resonance_history_clear (res_hist)
class(resonance_history_t), intent(out) :: res_hist
end subroutine resonance_history_clear
@ %def resonance_history_clear
@
<<Resonances: resonance history: TBP>>=
procedure :: copy => resonance_history_copy
<<Resonances: procedures>>=
subroutine resonance_history_copy (res_hist_in, res_hist_out)
class(resonance_history_t), intent(in) :: res_hist_in
type(resonance_history_t), intent(out) :: res_hist_out
integer :: i
res_hist_out%n_resonances = res_hist_in%n_resonances
allocate (res_hist_out%resonances (size (res_hist_in%resonances)))
do i = 1, size (res_hist_in%resonances)
call res_hist_in%resonances(i)%copy (res_hist_out%resonances(i))
end do
end subroutine resonance_history_copy
@ %def resonance_history_copy
@
<<Resonances: resonance history: TBP>>=
procedure :: write => resonance_history_write
<<Resonances: procedures>>=
subroutine resonance_history_write (res_hist, unit, verbose, indent)
class(resonance_history_t), intent(in) :: res_hist
integer, optional, intent(in) :: unit
logical, optional, intent(in) :: verbose
integer, optional, intent(in) :: indent
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
call write_indent (u, indent)
write(u, '(A,I0,A)') "Resonance history with ", &
res_hist%n_resonances, " resonances:"
do i = 1, res_hist%n_resonances
call write_indent (u, indent)
write (u, "(2x)", advance="no")
call res_hist%resonances(i)%write (u, verbose)
end do
end subroutine resonance_history_write
@ %def resonance_history_write
@ Assignment. Indirectly calls type-bound assignment for the contributors.
Strictly speaking, this is redundant. But NAGfor 6.208 intrinsic assignment
crashes under certain conditions.
<<Resonances: resonance history: TBP>>=
procedure, private :: resonance_history_assign
generic :: assignment(=) => resonance_history_assign
<<Resonances: procedures>>=
subroutine resonance_history_assign (res_hist_out, res_hist_in)
class(resonance_history_t), intent(out) :: res_hist_out
class(resonance_history_t), intent(in) :: res_hist_in
if (allocated (res_hist_in%resonances)) then
res_hist_out%resonances = res_hist_in%resonances
res_hist_out%n_resonances = res_hist_in%n_resonances
end if
end subroutine resonance_history_assign
@ %def resonance_history_assign
@ Equality. If this turns out to slow down the program, we should
change the implementation or use hash codes.
<<Resonances: resonance history: TBP>>=
procedure, private :: resonance_history_equal
generic :: operator(==) => resonance_history_equal
<<Resonances: procedures>>=
elemental function resonance_history_equal (rh1, rh2) result (equal)
logical :: equal
class(resonance_history_t), intent(in) :: rh1, rh2
integer :: i
equal = .false.
if (rh1%n_resonances == rh2%n_resonances) then
do i = 1, rh1%n_resonances
if (.not. rh1%resonances(i) == rh2%resonances(i)) then
return
end if
end do
equal = .true.
end if
end function resonance_history_equal
@ %def resonance_history_equal
@ Check if a resonance history is a strict superset of another one. This is
true if the first one is nonempty and the second one is empty.
Otherwise, we check if each entry of the second argument appears in
the first one.
<<Resonances: resonance history: TBP>>=
procedure, private :: resonance_history_contains
generic :: operator(.contains.) => resonance_history_contains
@
<<Resonances: procedures>>=
elemental function resonance_history_contains (rh1, rh2) result (flag)
logical :: flag
class(resonance_history_t), intent(in) :: rh1, rh2
integer :: i
if (rh1%n_resonances > rh2%n_resonances) then
flag = .true.
do i = 1, rh2%n_resonances
flag = flag .and. any (rh1%resonances == rh2%resonances(i))
end do
else
flag = .false.
end if
end function resonance_history_contains
@ %def resonance_history_contains
@ Number of entries for dynamically extending the resonance-info array.
<<Resonances: parameters>>=
integer, parameter :: n_max_resonances = 10
@
<<Resonances: resonance history: TBP>>=
procedure :: add_resonance => resonance_history_add_resonance
<<Resonances: procedures>>=
subroutine resonance_history_add_resonance (res_hist, resonance)
class(resonance_history_t), intent(inout) :: res_hist
type(resonance_info_t), intent(in) :: resonance
type(resonance_info_t), dimension(:), allocatable :: tmp
integer :: n, i
if (debug_on) call msg_debug (D_PHASESPACE, "resonance_history_add_resonance")
if (.not. allocated (res_hist%resonances)) then
n = 0
allocate (res_hist%resonances (1))
else
n = res_hist%n_resonances
allocate (tmp (n))
do i = 1, n
call res_hist%resonances(i)%copy (tmp(i))
end do
deallocate (res_hist%resonances)
allocate (res_hist%resonances (n+1))
do i = 1, n
call tmp(i)%copy (res_hist%resonances(i))
end do
deallocate (tmp)
end if
call resonance%copy (res_hist%resonances(n+1))
res_hist%n_resonances = n + 1
if (debug_on) call msg_debug &
(D_PHASESPACE, "res_hist%n_resonances", res_hist%n_resonances)
end subroutine resonance_history_add_resonance
@ %def resonance_history_add_resonance
@
<<Resonances: resonance history: TBP>>=
procedure :: remove_resonance => resonance_history_remove_resonance
<<Resonances: procedures>>=
subroutine resonance_history_remove_resonance (res_hist, i_res)
class(resonance_history_t), intent(inout) :: res_hist
integer, intent(in) :: i_res
type(resonance_info_t), dimension(:), allocatable :: tmp_1, tmp_2
integer :: i, j, n
n = res_hist%n_resonances
res_hist%n_resonances = n - 1
if (res_hist%n_resonances == 0) then
deallocate (res_hist%resonances)
else
if (i_res > 1) allocate (tmp_1(1:i_res-1))
if (i_res < n) allocate (tmp_2(i_res+1:n))
if (allocated (tmp_1)) then
do i = 1, i_res - 1
call res_hist%resonances(i)%copy (tmp_1(i))
end do
end if
if (allocated (tmp_2)) then
do i = i_res + 1, n
call res_hist%resonances(i)%copy (tmp_2(i))
end do
end if
deallocate (res_hist%resonances)
allocate (res_hist%resonances (res_hist%n_resonances))
j = 1
if (allocated (tmp_1)) then
do i = 1, i_res - 1
call tmp_1(i)%copy (res_hist%resonances(j))
j = j + 1
end do
deallocate (tmp_1)
end if
if (allocated (tmp_2)) then
do i = i_res + 1, n
call tmp_2(i)%copy (res_hist%resonances(j))
j = j + 1
end do
deallocate (tmp_2)
end if
end if
end subroutine resonance_history_remove_resonance
@ %def resonance_history_remove_resonance
@
<<Resonances: resonance history: TBP>>=
procedure :: add_offset => resonance_history_add_offset
<<Resonances: procedures>>=
subroutine resonance_history_add_offset (res_hist, n)
class(resonance_history_t), intent(inout) :: res_hist
integer, intent(in) :: n
integer :: i_res
do i_res = 1, res_hist%n_resonances
associate (contributors => res_hist%resonances(i_res)%contributors%c)
contributors = contributors + n
end associate
end do
end subroutine resonance_history_add_offset
@ %def resonance_history_add_offset
@
<<Resonances: resonance history: TBP>>=
procedure :: contains_leg => resonance_history_contains_leg
<<Resonances: procedures>>=
function resonance_history_contains_leg (res_hist, i_leg) result (val)
logical :: val
class(resonance_history_t), intent(in) :: res_hist
integer, intent(in) :: i_leg
integer :: i_res
val = .false.
do i_res = 1, res_hist%n_resonances
if (any (res_hist%resonances(i_res)%contributors%c == i_leg)) then
val = .true.
exit
end if
end do
end function resonance_history_contains_leg
@ %def resonance_history_contains_leg
@
<<Resonances: resonance history: TBP>>=
procedure :: mapping => resonance_history_mapping
<<Resonances: procedures>>=
function resonance_history_mapping (res_hist, p, i_gluon) result (p_map)
real(default) :: p_map
class(resonance_history_t), intent(in) :: res_hist
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in), optional :: i_gluon
integer :: i_res
real(default) :: s
p_map = one
do i_res = 1, res_hist%n_resonances
associate (res => res_hist%resonances(i_res))
s = compute_resonance_mass (p, res%contributors%c, i_gluon)**2
p_map = p_map * res%mapping (s)
end associate
end do
end function resonance_history_mapping
@ %def resonance_history_mapping
@ This predicate is true if all resonances in the history have exactly
[[n]] contributors. For instance, if $n=2$, all resonances have a
two-particle decay.
<<Resonances: resonance history: TBP>>=
procedure :: only_has_n_contributors => resonance_history_only_has_n_contributors
<<Resonances: procedures>>=
function resonance_history_only_has_n_contributors (res_hist, n) result (value)
logical :: value
class(resonance_history_t), intent(in) :: res_hist
integer, intent(in) :: n
integer :: i_res
value = .true.
do i_res = 1, res_hist%n_resonances
associate (res => res_hist%resonances(i_res))
value = value .and. size (res%contributors%c) == n
end associate
end do
end function resonance_history_only_has_n_contributors
@ %def resonance_history_only_has_n_contributors
@
<<Resonances: resonance history: TBP>>=
procedure :: has_flavor => resonance_history_has_flavor
<<Resonances: procedures>>=
function resonance_history_has_flavor (res_hist, flv) result (has_flv)
logical :: has_flv
class(resonance_history_t), intent(in) :: res_hist
type(flavor_t), intent(in) :: flv
integer :: i
has_flv = .false.
do i = 1, res_hist%n_resonances
has_flv = has_flv .or. res_hist%resonances(i)%flavor == flv
end do
end function resonance_history_has_flavor
@ %def resonance_history_has_flavor
@
\subsection{Kinematics}
Evaluate the distance from a resonance. The distance is given by
$|p^2-m^2|/(m\Gamma)$. For $\Gamma\ll m$, this is the relative
distance from the resonance peak in units of the half-width.
<<Resonances: resonance info: TBP>>=
procedure :: evaluate_distance => resonance_info_evaluate_distance
<<Resonances: procedures>>=
subroutine resonance_info_evaluate_distance (res_info, p, dist)
class(resonance_info_t), intent(in) :: res_info
type(vector4_t), dimension(:), intent(in) :: p
real(default), intent(out) :: dist
real(default) :: m, w
type(vector4_t) :: q
m = res_info%flavor%get_mass ()
w = res_info%flavor%get_width ()
q = sum (p(res_info%contributors%c))
dist = abs (q**2 - m**2) / (m * w)
end subroutine resonance_info_evaluate_distance
@ %def resonance_info_evaluate_distance
@
Evaluate the array of distances from a resonance history. We assume that the
array has been allocated with correct size, namely the number of resonances in
this history.
<<Resonances: resonance history: TBP>>=
procedure :: evaluate_distances => resonance_history_evaluate_distances
<<Resonances: procedures>>=
subroutine resonance_history_evaluate_distances (res_hist, p, dist)
class(resonance_history_t), intent(in) :: res_hist
type(vector4_t), dimension(:), intent(in) :: p
real(default), dimension(:), intent(out) :: dist
integer :: i
do i = 1, res_hist%n_resonances
call res_hist%resonances(i)%evaluate_distance (p, dist(i))
end do
end subroutine resonance_history_evaluate_distances
@ %def resonance_history_evaluate_distances
@ Use the distance to determine a Gaussian turnoff factor for a
resonance. The factor is given by a Gaussian function
$e^{-d^2/\sigma^2}$, where $\sigma$ is the [[gw]] parameter multiplied
by the resonance width, and $d$ is the distance (see above). So, for
$d=\sigma$, the factor is $0.37$, and for $d=2\sigma$ we get $0.018$.
If the [[gw]] factor is less or equal to zero, return $1$.
<<Resonances: resonance info: TBP>>=
procedure :: evaluate_gaussian => resonance_info_evaluate_gaussian
<<Resonances: procedures>>=
function resonance_info_evaluate_gaussian (res_info, p, gw) result (factor)
class(resonance_info_t), intent(in) :: res_info
type(vector4_t), dimension(:), intent(in) :: p
real(default), intent(in) :: gw
real(default) :: factor
real(default) :: dist, w
if (gw > 0) then
w = res_info%flavor%get_width ()
call res_info%evaluate_distance (p, dist)
factor = exp (- (dist / (gw * w)) **2)
else
factor = 1
end if
end function resonance_info_evaluate_gaussian
@ %def resonance_info_evaluate_gaussian
@ The Gaussian factor of the history is the product of all factors.
<<Resonances: resonance history: TBP>>=
procedure :: evaluate_gaussian => resonance_history_evaluate_gaussian
<<Resonances: procedures>>=
function resonance_history_evaluate_gaussian (res_hist, p, gw) result (factor)
class(resonance_history_t), intent(in) :: res_hist
type(vector4_t), dimension(:), intent(in) :: p
real(default), intent(in) :: gw
real(default), dimension(:), allocatable :: dist
real(default) :: factor
integer :: i
factor = 1
do i = 1, res_hist%n_resonances
factor = factor * res_hist%resonances(i)%evaluate_gaussian (p, gw)
end do
end function resonance_history_evaluate_gaussian
@ %def resonance_history_evaluate_gaussian
@
Use the distances to determine whether the resonance history can qualify as
on-shell. The criterion is whether the distance is greater than the number of
width values as given by [[on_shell_limit]].
<<Resonances: resonance info: TBP>>=
procedure :: is_on_shell => resonance_info_is_on_shell
<<Resonances: procedures>>=
function resonance_info_is_on_shell (res_info, p, on_shell_limit) &
result (flag)
class(resonance_info_t), intent(in) :: res_info
type(vector4_t), dimension(:), intent(in) :: p
real(default), intent(in) :: on_shell_limit
logical :: flag
real(default) :: dist
call res_info%evaluate_distance (p, dist)
flag = dist < on_shell_limit
end function resonance_info_is_on_shell
@ %def resonance_info_is_on_shell
@
<<Resonances: resonance history: TBP>>=
procedure :: is_on_shell => resonance_history_is_on_shell
<<Resonances: procedures>>=
function resonance_history_is_on_shell (res_hist, p, on_shell_limit) &
result (flag)
class(resonance_history_t), intent(in) :: res_hist
type(vector4_t), dimension(:), intent(in) :: p
real(default), intent(in) :: on_shell_limit
logical :: flag
integer :: i
flag = .true.
do i = 1, res_hist%n_resonances
flag = flag .and. res_hist%resonances(i)%is_on_shell (p, on_shell_limit)
end do
end function resonance_history_is_on_shell
@ %def resonance_history_is_on_shell
@
\subsection{OMega restriction strings}
One application of the resonance module is creating restriction
strings that can be fed into process definitions with the OMega
generator. Since OMega counts the incoming particles first, we have
to supply [[n_in]] as an offset.
<<Resonances: resonance info: TBP>>=
procedure :: as_omega_string => resonance_info_as_omega_string
<<Resonances: resonance history: TBP>>=
procedure :: as_omega_string => resonance_history_as_omega_string
<<Resonances: procedures>>=
function resonance_info_as_omega_string (res_info, n_in) result (string)
class(resonance_info_t), intent(in) :: res_info
integer, intent(in) :: n_in
type(string_t) :: string
integer :: i
string = ""
if (allocated (res_info%contributors%c)) then
do i = 1, size (res_info%contributors%c)
if (i > 1) string = string // "+"
string = string // str (res_info%contributors%c(i) + n_in)
end do
string = string // "~" // res_info%flavor%get_name ()
end if
end function resonance_info_as_omega_string
function resonance_history_as_omega_string (res_hist, n_in) result (string)
class(resonance_history_t), intent(in) :: res_hist
integer, intent(in) :: n_in
type(string_t) :: string
integer :: i
string = ""
do i = 1, res_hist%n_resonances
if (i > 1) string = string // " && "
string = string // res_hist%resonances(i)%as_omega_string (n_in)
end do
end function resonance_history_as_omega_string
@ %def resonance_info_as_omega_string
@ %def resonance_history_as_omega_string
@
\subsection{Resonance history as tree}
If we want to organize the resonances and their decay products, it can be
useful to have them explicitly as a tree structure. We implement this in the
traditional event-record form with the resonances sorted by decreasing number
of contributors, and their decay products added as an extra array.
<<Resonances: public>>=
public :: resonance_tree_t
<<Resonances: types>>=
type :: resonance_branch_t
integer :: i = 0
type(flavor_t) :: flv
integer, dimension(:), allocatable :: r_child
integer, dimension(:), allocatable :: o_child
end type resonance_branch_t
type :: resonance_tree_t
private
integer :: n = 0
type(resonance_branch_t), dimension(:), allocatable :: branch
contains
<<Resonances: resonance tree: TBP>>
end type resonance_tree_t
@ %def resonance_branch_t resonance_tree_t
@
<<Resonances: resonance tree: TBP>>=
procedure :: write => resonance_tree_write
<<Resonances: procedures>>=
subroutine resonance_tree_write (tree, unit, indent)
class(resonance_tree_t), intent(in) :: tree
integer, intent(in), optional :: unit, indent
integer :: u, b, c
u = given_output_unit (unit)
call write_indent (u, indent)
write (u, "(A)", advance="no") "Resonance tree:"
if (tree%n > 0) then
write (u, *)
do b = 1, tree%n
call write_indent (u, indent)
write (u, "(2x,'r',I0,':',1x)", advance="no") b
associate (branch => tree%branch(b))
call branch%flv%write (u)
write (u, "(1x,'=>')", advance="no")
if (allocated (branch%r_child)) then
do c = 1, size (branch%r_child)
write (u, "(1x,'r',I0)", advance="no") branch%r_child(c)
end do
end if
if (allocated (branch%o_child)) then
do c = 1, size (branch%o_child)
write (u, "(1x,I0)", advance="no") branch%o_child(c)
end do
end if
write (u, *)
end associate
end do
else
write (u, "(1x,A)") "[empty]"
end if
end subroutine resonance_tree_write
@ %def resonance_tree_write
@ Contents.
<<Resonances: resonance tree: TBP>>=
procedure :: get_n_resonances => resonance_tree_get_n_resonances
procedure :: get_flv => resonance_tree_get_flv
<<Resonances: procedures>>=
function resonance_tree_get_n_resonances (tree) result (n)
class(resonance_tree_t), intent(in) :: tree
integer :: n
n = tree%n
end function resonance_tree_get_n_resonances
function resonance_tree_get_flv (tree, i) result (flv)
class(resonance_tree_t), intent(in) :: tree
integer, intent(in) :: i
type(flavor_t) :: flv
flv = tree%branch(i)%flv
end function resonance_tree_get_flv
@ %def resonance_tree_get_n_resonances
@ %def resonance_tree_get_flv
@ Return the shifted indices of the resonance children for branch [[i]]. For
a child which is itself a resonance, add [[offset_r]] to the index value. For
the others, add [[offset_o]]. Combine both in a single array.
<<Resonances: resonance tree: TBP>>=
procedure :: get_children => resonance_tree_get_children
<<Resonances: procedures>>=
function resonance_tree_get_children (tree, i, offset_r, offset_o) &
result (child)
class(resonance_tree_t), intent(in) :: tree
integer, intent(in) :: i, offset_r, offset_o
integer, dimension(:), allocatable :: child
integer :: nr, no
associate (branch => tree%branch(i))
nr = size (branch%r_child)
no = size (branch%o_child)
allocate (child (nr + no))
child(1:nr) = branch%r_child + offset_r
child(nr+1:nr+no) = branch%o_child + offset_o
end associate
end function resonance_tree_get_children
@ %def resonance_tree_get_children
@ Transform a resonance history into a resonance tree.
Algorithm:
\begin{enumerate}
\item
Determine a mapping of the resonance array, such that in the new array the
resonances are ordered by decreasing number of contributors.
\item
Copy the flavor entries to the mapped array.
\item
Scan all resonances and, for each one, find a resonance that is its parent.
Since the resonances are ordered, later matches overwrite earlier ones. The
last match is the correct one. Then scan again and, for each resonance,
collect the resonances that have it as parent. This is the set of child
resonances.
\item
Analogously, scan all outgoing particles that appear in any of the
contributors list. Determine their immediate parent as above, and set the
child outgoing parents for the resonances, as above.
\end{enumerate}
<<Resonances: resonance history: TBP>>=
procedure :: to_tree => resonance_history_to_tree
<<Resonances: procedures>>=
subroutine resonance_history_to_tree (res_hist, tree)
class(resonance_history_t), intent(in) :: res_hist
type(resonance_tree_t), intent(out) :: tree
integer :: nr
integer, dimension(:), allocatable :: r_branch, r_source
nr = res_hist%n_resonances
tree%n = nr
allocate (tree%branch (tree%n), r_branch (tree%n), r_source (tree%n))
if (tree%n > 0) then
call find_branch_ordering ()
call set_flavors ()
call set_child_resonances ()
call set_child_outgoing ()
end if
contains
subroutine find_branch_ordering ()
integer, dimension(:), allocatable :: nc_array
integer :: r, ir, nc
allocate (nc_array (tree%n))
nc_array(:) = res_hist%resonances%get_n_contributors ()
ir = 0
do nc = maxval (nc_array), minval (nc_array), -1
do r = 1, nr
if (nc_array(r) == nc) then
ir = ir + 1
r_branch(r) = ir
r_source(ir) = r
end if
end do
end do
end subroutine find_branch_ordering
subroutine set_flavors ()
integer :: r
do r = 1, nr
tree%branch(r_branch(r))%flv = res_hist%resonances(r)%flavor
end do
end subroutine set_flavors
subroutine set_child_resonances ()
integer, dimension(:), allocatable :: r_child, r_parent
integer :: r, ir, pr
allocate (r_parent (nr), source = 0)
SCAN_RES: do r = 1, nr
associate (this_res => res_hist%resonances(r))
SCAN_PARENT: do ir = 1, nr
pr = r_source(ir)
if (pr == r) cycle SCAN_PARENT
if (all (res_hist%resonances(pr)%contains &
(this_res%contributors%c))) then
r_parent (r) = pr
end if
end do SCAN_PARENT
end associate
end do SCAN_RES
allocate (r_child (nr), source = [(r, r = 1, nr)])
do r = 1, nr
ir = r_branch(r)
tree%branch(ir)%r_child = r_branch (pack (r_child, r_parent == r))
end do
end subroutine set_child_resonances
subroutine set_child_outgoing ()
integer, dimension(:), allocatable :: o_child, o_parent
integer :: o_max, r, o, ir
o_max = 0
do r = 1, nr
associate (this_res => res_hist%resonances(r))
o_max = max (o_max, maxval (this_res%contributors%c))
end associate
end do
allocate (o_parent (o_max), source=0)
SCAN_OUT: do o = 1, o_max
SCAN_PARENT: do ir = 1, nr
r = r_source(ir)
associate (this_res => res_hist%resonances(r))
if (this_res%contains (o)) o_parent(o) = r
end associate
end do SCAN_PARENT
end do SCAN_OUT
allocate (o_child (o_max), source = [(o, o = 1, o_max)])
do r = 1, nr
ir = r_branch(r)
tree%branch(ir)%o_child = pack (o_child, o_parent == r)
end do
end subroutine set_child_outgoing
end subroutine resonance_history_to_tree
@ %def resonance_history_to_tree
@
\subsection{Resonance history set}
This is an array of resonance histories. The elements are supposed to
be unique. That is, entering a new element is successful only if the
element does not already exist.
The current implementation uses a straightforward linear search for
comparison. If this should become an issue, we may change the
implementation to a hash table. To keep this freedom, the set should
be an opaque object. In fact, we expect to use it as a transient data
structure. Once the set is complete, we transform it into a
contiguous array.
<<Resonances: public>>=
public :: resonance_history_set_t
<<Resonances: types>>=
type :: index_array_t
integer, dimension(:), allocatable :: i
end type index_array_t
type :: resonance_history_set_t
private
logical :: complete = .false.
integer :: n_filter = 0
type(resonance_history_t), dimension(:), allocatable :: history
type(index_array_t), dimension(:), allocatable :: contains_this
type(resonance_tree_t), dimension(:), allocatable :: tree
integer :: last = 0
contains
<<Resonances: resonance history set: TBP>>
end type resonance_history_set_t
@ %def resonance_history_set_t
@ Display.
The tree-format version of the histories is displayed only upon request.
<<Resonances: resonance history set: TBP>>=
procedure :: write => resonance_history_set_write
<<Resonances: procedures>>=
subroutine resonance_history_set_write (res_set, unit, indent, show_trees)
class(resonance_history_set_t), intent(in) :: res_set
integer, intent(in), optional :: unit
integer, intent(in), optional :: indent
logical, intent(in), optional :: show_trees
logical :: s_trees
integer :: u, i, j, ind
u = given_output_unit (unit)
s_trees = .false.; if (present (show_trees)) s_trees = show_trees
ind = 0; if (present (indent)) ind = indent
call write_indent (u, indent)
write (u, "(A)", advance="no") "Resonance history set:"
if (res_set%complete) then
write (u, *)
else
write (u, "(1x,A)") "[incomplete]"
end if
do i = 1, res_set%last
write (u, "(1x,I0,1x)", advance="no") i
call res_set%history(i)%write (u, verbose=.false., indent=indent)
if (allocated (res_set%contains_this)) then
call write_indent (u, indent)
write (u, "(3x,A)", advance="no") "contained in ("
do j = 1, size (res_set%contains_this(i)%i)
if (j>1) write (u, "(',')", advance="no")
write (u, "(I0)", advance="no") res_set%contains_this(i)%i(j)
end do
write (u, "(A)") ")"
end if
if (s_trees .and. allocated (res_set%tree)) then
call res_set%tree(i)%write (u, ind + 1)
end if
end do
end subroutine resonance_history_set_write
@ %def resonance_history_set_write
@ Initialization. The default initial size is 16 elements, to be doubled in
size repeatedly as needed.
<<Resonances: parameters>>=
integer, parameter :: resonance_history_set_initial_size = 16
@ %def resonance_history_set_initial_size = 16
<<Resonances: resonance history set: TBP>>=
procedure :: init => resonance_history_set_init
<<Resonances: procedures>>=
subroutine resonance_history_set_init (res_set, n_filter, initial_size)
class(resonance_history_set_t), intent(out) :: res_set
integer, intent(in), optional :: n_filter
integer, intent(in), optional :: initial_size
if (present (n_filter)) res_set%n_filter = n_filter
if (present (initial_size)) then
allocate (res_set%history (initial_size))
else
allocate (res_set%history (resonance_history_set_initial_size))
end if
end subroutine resonance_history_set_init
@ %def resonance_history_set_init
@ Enter an entry: append to the array if it does not yet exist, expand
as needed. If a [[n_filter]] value has been provided, enter the
resonance only if it fulfils the requirement.
An empty resonance history is entered only if the [[trivial]] flag is set.
<<Resonances: resonance history set: TBP>>=
procedure :: enter => resonance_history_set_enter
<<Resonances: procedures>>=
subroutine resonance_history_set_enter (res_set, res_history, trivial)
class(resonance_history_set_t), intent(inout) :: res_set
type(resonance_history_t), intent(in) :: res_history
logical, intent(in), optional :: trivial
integer :: i, new
if (res_history%n_resonances == 0) then
if (present (trivial)) then
if (.not. trivial) return
else
return
end if
end if
if (res_set%n_filter > 0) then
if (.not. res_history%only_has_n_contributors (res_set%n_filter)) return
end if
do i = 1, res_set%last
if (res_set%history(i) == res_history) return
end do
new = res_set%last + 1
if (new > size (res_set%history)) call res_set%expand ()
res_set%history(new) = res_history
res_set%last = new
end subroutine resonance_history_set_enter
@ %def resonance_history_set_enter
@ Freeze the resonance history set: determine the array that determines
in which other resonance histories a particular history is contained.
This can only be done once, and once this is done, no further histories can be
entered.
<<Resonances: resonance history set: TBP>>=
procedure :: freeze => resonance_history_set_freeze
<<Resonances: procedures>>=
subroutine resonance_history_set_freeze (res_set)
class(resonance_history_set_t), intent(inout) :: res_set
integer :: i, n, c
logical, dimension(:), allocatable :: contains_this
integer, dimension(:), allocatable :: index_array
n = res_set%last
allocate (contains_this (n))
allocate (index_array (n), source = [(i, i=1, n)])
allocate (res_set%contains_this (n))
do i = 1, n
contains_this = resonance_history_contains &
(res_set%history(1:n), res_set%history(i))
c = count (contains_this)
allocate (res_set%contains_this(i)%i (c))
res_set%contains_this(i)%i = pack (index_array, contains_this)
end do
allocate (res_set%tree (n))
do i = 1, n
call res_set%history(i)%to_tree (res_set%tree(i))
end do
res_set%complete = .true.
end subroutine resonance_history_set_freeze
@ %def resonance_history_set_freeze
@ Determine the histories (in form of their indices in the array) that can be
considered on-shell, given a set of momenta and a maximum distance. The
distance from the resonance is measured in multiples of the resonance width.
Note that the momentum array must only contain the outgoing particles.
If a particular history is on-shell, but there is another history which
contains this and also is on-shell, only the latter is retained.
<<Resonances: resonance history set: TBP>>=
procedure :: determine_on_shell_histories &
=> resonance_history_set_determine_on_shell_histories
<<Resonances: procedures>>=
subroutine resonance_history_set_determine_on_shell_histories &
(res_set, p, on_shell_limit, index_array)
class(resonance_history_set_t), intent(in) :: res_set
type(vector4_t), dimension(:), intent(in) :: p
real(default), intent(in) :: on_shell_limit
integer, dimension(:), allocatable, intent(out) :: index_array
integer :: n, i
integer, dimension(:), allocatable :: i_array
if (res_set%complete) then
n = res_set%last
allocate (i_array (n), source=0)
do i = 1, n
if (res_set%history(i)%is_on_shell (p, on_shell_limit)) i_array(i) = i
end do
do i = 1, n
if (any (i_array(res_set%contains_this(i)%i) /= 0)) then
i_array(i) = 0
end if
end do
allocate (index_array (count (i_array /= 0)))
index_array(:) = pack (i_array, i_array /= 0)
end if
end subroutine resonance_history_set_determine_on_shell_histories
@ %def resonance_history_set_determine_on_shell_histories
@ For the selected history, compute the Gaussian turnoff factor.
The turnoff parameter is [[gw]].
<<Resonances: resonance history set: TBP>>=
procedure :: evaluate_gaussian => resonance_history_set_evaluate_gaussian
<<Resonances: procedures>>=
function resonance_history_set_evaluate_gaussian (res_set, p, gw, i) &
result (factor)
class(resonance_history_set_t), intent(in) :: res_set
type(vector4_t), dimension(:), intent(in) :: p
real(default), intent(in) :: gw
integer, intent(in) :: i
real(default) :: factor
factor = res_set%history(i)%evaluate_gaussian (p, gw)
end function resonance_history_set_evaluate_gaussian
@ %def resonance_history_set_evaluate_gaussian
@ Return the number of histories. This is zero if there are none, or
if [[freeze]] has not been called yet.
<<Resonances: resonance history set: TBP>>=
procedure :: get_n_history => resonance_history_set_get_n_history
<<Resonances: procedures>>=
function resonance_history_set_get_n_history (res_set) result (n)
class(resonance_history_set_t), intent(in) :: res_set
integer :: n
if (res_set%complete) then
n = res_set%last
else
n = 0
end if
end function resonance_history_set_get_n_history
@ %def resonance_history_set_get_n_history
@ Return a single history.
<<Resonances: resonance history set: TBP>>=
procedure :: get_history => resonance_history_set_get_history
<<Resonances: procedures>>=
function resonance_history_set_get_history (res_set, i) result (res_history)
class(resonance_history_set_t), intent(in) :: res_set
integer, intent(in) :: i
type(resonance_history_t) :: res_history
if (res_set%complete .and. i <= res_set%last) then
res_history = res_set%history(i)
end if
end function resonance_history_set_get_history
@ %def resonance_history_set_get_history
@ Conversion to a plain array, sized correctly.
<<Resonances: resonance history set: TBP>>=
procedure :: to_array => resonance_history_set_to_array
<<Resonances: procedures>>=
subroutine resonance_history_set_to_array (res_set, res_history)
class(resonance_history_set_t), intent(in) :: res_set
type(resonance_history_t), dimension(:), allocatable, intent(out) :: res_history
if (res_set%complete) then
allocate (res_history (res_set%last))
res_history(:) = res_set%history(1:res_set%last)
end if
end subroutine resonance_history_set_to_array
@ %def resonance_history_set_to_array
@ Return a selected history in tree form.
<<Resonances: resonance history set: TBP>>=
procedure :: get_tree => resonance_history_set_get_tree
<<Resonances: procedures>>=
subroutine resonance_history_set_get_tree (res_set, i, res_tree)
class(resonance_history_set_t), intent(in) :: res_set
integer, intent(in) :: i
type(resonance_tree_t), intent(out) :: res_tree
if (res_set%complete) then
res_tree = res_set%tree(i)
end if
end subroutine resonance_history_set_get_tree
@ %def resonance_history_set_to_array
@ Expand: double the size of the array. We do not need this in the API.
<<Resonances: resonance history set: TBP>>=
procedure, private :: expand => resonance_history_set_expand
<<Resonances: procedures>>=
subroutine resonance_history_set_expand (res_set)
class(resonance_history_set_t), intent(inout) :: res_set
type(resonance_history_t), dimension(:), allocatable :: history_new
integer :: s
s = size (res_set%history)
allocate (history_new (2 * s))
history_new(1:s) = res_set%history(1:s)
call move_alloc (history_new, res_set%history)
end subroutine resonance_history_set_expand
@ %def resonance_history_set_expand
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[resonances_ut.f90]]>>=
<<File header>>
module resonances_ut
use unit_tests
use resonances_uti
<<Standard module head>>
<<Resonances: public test>>
contains
<<Resonances: test driver>>
end module resonances_ut
@ %def resonances_ut
@
<<[[resonances_uti.f90]]>>=
<<File header>>
module resonances_uti
<<Use kinds>>
<<Use strings>>
use format_defs, only: FMF_12
use lorentz, only: vector4_t, vector4_at_rest
use model_data, only: model_data_t
use flavors, only: flavor_t
use resonances, only: resonance_history_t
use resonances
<<Standard module head>>
<<Resonances: test declarations>>
contains
<<Resonances: tests>>
end module resonances_uti
@ %def resonances_ut
@ API: driver for the unit tests below.
<<Resonances: public test>>=
public :: resonances_test
<<Resonances: test driver>>=
subroutine resonances_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Resonances: execute tests>>
end subroutine resonances_test
@ %def resonances_test
@ Basic operations on a resonance history object.
<<Resonances: execute tests>>=
call test (resonances_1, "resonances_1", &
"check resonance history setup", &
u, results)
<<Resonances: test declarations>>=
public :: resonances_1
<<Resonances: tests>>=
subroutine resonances_1 (u)
integer, intent(in) :: u
type(resonance_info_t) :: res_info
type(resonance_history_t) :: res_history
type(model_data_t), target :: model
write (u, "(A)") "* Test output: resonances_1"
write (u, "(A)") "* Purpose: test resonance history setup"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Empty resonance history"
write (u, "(A)")
call res_history%write (u)
write (u, "(A)")
write (u, "(A)") "* Add resonance"
write (u, "(A)")
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
write (u, "(A)")
write (u, "(A)") "* Add another resonance"
write (u, "(A)")
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
write (u, "(A)")
write (u, "(A)") "* Remove resonance"
write (u, "(A)")
call res_history%remove_resonance (1)
call res_history%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: resonances_1"
end subroutine resonances_1
@ %def resonances_1
@ Basic operations on a resonance history object.
<<Resonances: execute tests>>=
call test (resonances_2, "resonances_2", &
"check O'Mega restriction strings", &
u, results)
<<Resonances: test declarations>>=
public :: resonances_2
<<Resonances: tests>>=
subroutine resonances_2 (u)
integer, intent(in) :: u
type(resonance_info_t) :: res_info
type(resonance_history_t) :: res_history
type(model_data_t), target :: model
type(string_t) :: restrictions
write (u, "(A)") "* Test output: resonances_2"
write (u, "(A)") "* Purpose: test OMega restrictions strings &
&for resonance history"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Empty resonance history"
write (u, "(A)")
restrictions = res_history%as_omega_string (2)
write (u, "(A,A,A)") "restrictions = '", char (restrictions), "'"
write (u, "(A)")
write (u, "(A)") "* Add resonance"
write (u, "(A)")
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
restrictions = res_history%as_omega_string (2)
write (u, "(A,A,A)") "restrictions = '", char (restrictions), "'"
write (u, "(A)")
write (u, "(A)") "* Add another resonance"
write (u, "(A)")
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
restrictions = res_history%as_omega_string (2)
write (u, "(A,A,A)") "restrictions = '", char (restrictions), "'"
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: resonances_2"
end subroutine resonances_2
@ %def resonances_2
@ Basic operations on a resonance history set.
<<Resonances: execute tests>>=
call test (resonances_3, "resonances_3", &
"check resonance history set", &
u, results)
<<Resonances: test declarations>>=
public :: resonances_3
<<Resonances: tests>>=
subroutine resonances_3 (u)
integer, intent(in) :: u
type(resonance_info_t) :: res_info
type(resonance_history_t) :: res_history
type(resonance_history_t), dimension(:), allocatable :: res_histories
type(resonance_history_set_t) :: res_set
type(model_data_t), target :: model
integer :: i
write (u, "(A)") "* Test output: resonances_3"
write (u, "(A)") "* Purpose: test resonance history set"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Initialize resonance history set"
write (u, "(A)")
call res_set%init (initial_size = 2)
write (u, "(A)") "* Add resonance histories, one at a time"
write (u, "(A)")
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
write (u, *)
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
write (u, *)
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
write (u, *)
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
write (u, *)
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
write (u, *)
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_info%init (7, 25, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
call res_set%freeze ()
write (u, "(A)")
write (u, "(A)") "* Result"
write (u, "(A)")
call res_set%write (u)
write (u, "(A)")
write (u, "(A)") "* Queries"
write (u, "(A)")
write (u, "(A,1x,I0)") "n_history =", res_set%get_n_history ()
write (u, "(A)")
write (u, "(A)") "History #2:"
res_history = res_set%get_history (2)
call res_history%write (u, indent=1)
call res_history%clear ()
write (u, "(A)")
write (u, "(A)") "* Result in array form"
call res_set%to_array (res_histories)
do i = 1, size (res_histories)
write (u, *)
call res_histories(i)%write (u)
end do
write (u, "(A)")
write (u, "(A)") "* Re-initialize resonance history set with filter n=2"
write (u, "(A)")
call res_set%init (n_filter = 2)
write (u, "(A)") "* Add resonance histories, one at a time"
write (u, "(A)")
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
write (u, *)
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
write (u, *)
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
write (u, *)
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
call res_set%enter (res_history)
call res_history%clear ()
call res_set%freeze ()
write (u, "(A)")
write (u, "(A)") "* Result"
write (u, "(A)")
call res_set%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: resonances_3"
end subroutine resonances_3
@ %def resonances_3
@ Probe momenta for resonance histories
<<Resonances: execute tests>>=
call test (resonances_4, "resonances_4", &
"resonance history: distance evaluation", &
u, results)
<<Resonances: test declarations>>=
public :: resonances_4
<<Resonances: tests>>=
subroutine resonances_4 (u)
integer, intent(in) :: u
type(resonance_info_t) :: res_info
type(resonance_history_t) :: res_history
type(model_data_t), target :: model
type(flavor_t) :: fw, fz
real(default) :: mw, mz, ww, wz
type(vector4_t), dimension(3) :: p
real(default), dimension(2) :: dist
real(default) :: gw, factor
integer :: i
write (u, "(A)") "* Test output: resonances_4"
write (u, "(A)") "* Purpose: test resonance history evaluation"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* W and Z parameters"
write (u, "(A)")
call fw%init (24, model)
call fz%init (23, model)
mw = fw%get_mass ()
ww = fw%get_width ()
mz = fz%get_mass ()
wz = fz%get_width ()
write (u, "(A,1x," // FMF_12 // ")") "mW =", mw
write (u, "(A,1x," // FMF_12 // ")") "wW =", ww
write (u, "(A,1x," // FMF_12 // ")") "mZ =", mz
write (u, "(A,1x," // FMF_12 // ")") "wZ =", wz
write (u, "(A)")
write (u, "(A)") "* Gaussian width parameter"
write (u, "(A)")
gw = 2
write (u, "(A,1x," // FMF_12 // ")") "gw =", gw
write (u, "(A)")
write (u, "(A)") "* Setup resonance histories"
write (u, "(A)")
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
write (u, "(A)")
write (u, "(A)") "* Setup zero momenta"
write (u, "(A)")
do i = 1, 3
call p(i)%write (u)
end do
write (u, "(A)")
write (u, "(A)") "* Evaluate distances from resonances"
write (u, "(A)")
call res_history%evaluate_distances (p, dist)
write (u, "(A,1x," // FMF_12 // ")") "distance (W) =", dist(1)
write (u, "(A,1x," // FMF_12 // ")") "m/w (W) =", mw / ww
write (u, "(A,1x," // FMF_12 // ")") "distance (Z) =", dist(2)
write (u, "(A,1x," // FMF_12 // ")") "m/w (Z) =", mz / wz
write (u, "(A)")
write (u, "(A)") "* Evaluate Gaussian turnoff factor"
write (u, "(A)")
factor = res_history%evaluate_gaussian (p, gw)
write (u, "(A,1x," // FMF_12 // ")") "gaussian fac =", factor
write (u, "(A)")
write (u, "(A)") "* Set momenta on W peak"
write (u, "(A)")
p(1) = vector4_at_rest (mw/2)
p(2) = vector4_at_rest (mw/2)
do i = 1, 3
call p(i)%write (u)
end do
write (u, "(A)")
write (u, "(A)") "* Evaluate distances from resonances"
write (u, "(A)")
call res_history%evaluate_distances (p, dist)
write (u, "(A,1x," // FMF_12 // ")") "distance (W) =", dist(1)
write (u, "(A,1x," // FMF_12 // ")") "distance (Z) =", dist(2)
write (u, "(A,1x," // FMF_12 // ")") "expected =", &
abs (mz**2 - mw**2) / (mz*wz)
write (u, "(A)")
write (u, "(A)") "* Evaluate Gaussian turnoff factor"
write (u, "(A)")
factor = res_history%evaluate_gaussian (p, gw)
write (u, "(A,1x," // FMF_12 // ")") "gaussian fac =", factor
write (u, "(A,1x," // FMF_12 // ")") "expected =", &
exp (- (abs (mz**2 - mw**2) / (mz*wz))**2 / (gw * wz)**2)
write (u, "(A)")
write (u, "(A)") "* Set momenta on both peaks"
write (u, "(A)")
p(3) = vector4_at_rest (mz - mw)
do i = 1, 3
call p(i)%write (u)
end do
write (u, "(A)")
write (u, "(A)") "* Evaluate distances from resonances"
write (u, "(A)")
call res_history%evaluate_distances (p, dist)
write (u, "(A,1x," // FMF_12 // ")") "distance (W) =", dist(1)
write (u, "(A,1x," // FMF_12 // ")") "distance (Z) =", dist(2)
write (u, "(A)")
write (u, "(A)") "* Evaluate Gaussian turnoff factor"
write (u, "(A)")
factor = res_history%evaluate_gaussian (p, gw)
write (u, "(A,1x," // FMF_12 // ")") "gaussian fac =", factor
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: resonances_4"
end subroutine resonances_4
@ %def resonances_4
@ Probe on-shell test for resonance histories
<<Resonances: execute tests>>=
call test (resonances_5, "resonances_5", &
"resonance history: on-shell test", &
u, results)
<<Resonances: test declarations>>=
public :: resonances_5
<<Resonances: tests>>=
subroutine resonances_5 (u)
integer, intent(in) :: u
type(resonance_info_t) :: res_info
type(resonance_history_t) :: res_history
type(resonance_history_set_t) :: res_set
type(model_data_t), target :: model
type(flavor_t) :: fw, fz
real(default) :: mw, mz, ww, wz
real(default) :: on_shell_limit
integer, dimension(:), allocatable :: on_shell
type(vector4_t), dimension(4) :: p
write (u, "(A)") "* Test output: resonances_5"
write (u, "(A)") "* Purpose: resonance history on-shell test"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* W and Z parameters"
write (u, "(A)")
call fw%init (24, model)
call fz%init (23, model)
mw = fw%get_mass ()
ww = fw%get_width ()
mz = fz%get_mass ()
wz = fz%get_width ()
write (u, "(A,1x," // FMF_12 // ")") "mW =", mw
write (u, "(A,1x," // FMF_12 // ")") "wW =", ww
write (u, "(A,1x," // FMF_12 // ")") "mZ =", mz
write (u, "(A,1x," // FMF_12 // ")") "wZ =", wz
write (u, "(A)")
write (u, "(A)") "* On-shell parameter: distance as multiple of width"
write (u, "(A)")
on_shell_limit = 3
write (u, "(A,1x," // FMF_12 // ")") "on-shell limit =", on_shell_limit
write (u, "(A)")
write (u, "(A)") "* Setup resonance history set"
write (u, "(A)")
call res_set%init ()
call res_info%init (3, -24, model, 6)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_info%init (12, 24, model, 6)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_info%init (15, 23, model, 6)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_info%init (3, -24, model, 6)
call res_history%add_resonance (res_info)
call res_info%init (15, 23, model, 6)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_info%init (12, 24, model, 6)
call res_history%add_resonance (res_info)
call res_info%init (15, 23, model, 6)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_set%freeze ()
call res_set%write (u)
write (u, "(A)")
write (u, "(A)") "* Setup zero momenta"
write (u, "(A)")
call write_momenta (p)
call res_set%determine_on_shell_histories (p, on_shell_limit, on_shell)
call write_on_shell_histories (on_shell)
write (u, "(A)")
write (u, "(A)") "* Setup momenta near W- resonance (2 widths off)"
write (u, "(A)")
p(1) = vector4_at_rest (82.5_default)
call write_momenta (p)
call res_set%determine_on_shell_histories (p, on_shell_limit, on_shell)
call write_on_shell_histories (on_shell)
write (u, "(A)")
write (u, "(A)") "* Setup momenta near W- resonance (4 widths off)"
write (u, "(A)")
p(1) = vector4_at_rest (84.5_default)
call write_momenta (p)
call res_set%determine_on_shell_histories (p, on_shell_limit, on_shell)
call write_on_shell_histories (on_shell)
write (u, "(A)")
write (u, "(A)") "* Setup momenta near Z resonance"
write (u, "(A)")
p(1) = vector4_at_rest (45._default)
p(3) = vector4_at_rest (45._default)
call write_momenta (p)
call res_set%determine_on_shell_histories (p, on_shell_limit, on_shell)
call write_on_shell_histories (on_shell)
write (u, "(A)")
write (u, "(A)") "* Setup momenta near W- and W+ resonances"
write (u, "(A)")
p(1) = vector4_at_rest (40._default)
p(2) = vector4_at_rest (40._default)
p(3) = vector4_at_rest (40._default)
p(4) = vector4_at_rest (40._default)
call write_momenta (p)
call res_set%determine_on_shell_histories (p, on_shell_limit, on_shell)
call write_on_shell_histories (on_shell)
write (u, "(A)")
write (u, "(A)") "* Setup momenta near W- and Z resonances, &
&shadowing single resonances"
write (u, "(A)")
p(1) = vector4_at_rest (40._default)
p(2) = vector4_at_rest (40._default)
p(3) = vector4_at_rest (10._default)
p(4) = vector4_at_rest ( 0._default)
call write_momenta (p)
call res_set%determine_on_shell_histories (p, on_shell_limit, on_shell)
call write_on_shell_histories (on_shell)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: resonances_5"
contains
subroutine write_momenta (p)
type(vector4_t), dimension(:), intent(in) :: p
integer :: i
do i = 1, size (p)
call p(i)%write (u)
end do
end subroutine write_momenta
subroutine write_on_shell_histories (on_shell)
integer, dimension(:), intent(in) :: on_shell
integer :: i
write (u, *)
write (u, "(A)", advance="no") "on-shell = ("
do i = 1, size (on_shell)
if (i > 1) write (u, "(',')", advance="no")
write (u, "(I0)", advance="no") on_shell(i)
end do
write (u, "(')')")
end subroutine write_on_shell_histories
end subroutine resonances_5
@ %def resonances_5
@ Organize the resonance history as a tree structure.
<<Resonances: execute tests>>=
call test (resonances_6, "resonances_6", &
"check resonance history setup", &
u, results)
<<Resonances: test declarations>>=
public :: resonances_6
<<Resonances: tests>>=
subroutine resonances_6 (u)
integer, intent(in) :: u
type(resonance_info_t) :: res_info
type(resonance_history_t) :: res_history
type(resonance_tree_t) :: res_tree
type(model_data_t), target :: model
write (u, "(A)") "* Test output: resonances_6"
write (u, "(A)") "* Purpose: retrieve resonance histories as trees"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Empty resonance history"
write (u, "(A)")
call res_history%write (u)
write (u, "(A)")
call res_history%to_tree (res_tree)
call res_tree%write (u)
write (u, "(A)")
write (u, "(A)") "* Single resonance"
write (u, "(A)")
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
write (u, "(A)")
call res_history%to_tree (res_tree)
call res_tree%write (u)
write (u, "(A)")
write (u, "(A)") "* Nested resonances"
write (u, "(A)")
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_history%write (u)
write (u, "(A)")
call res_history%to_tree (res_tree)
call res_tree%write (u)
write (u, "(A)")
write (u, "(A)") "* Disjunct resonances"
write (u, "(A)")
call res_history%clear ()
call res_info%init (5, 24, model, 7)
call res_history%add_resonance (res_info)
call res_info%init (7, 6, model, 7)
call res_history%add_resonance (res_info)
call res_info%init (80, -24, model, 7)
call res_history%add_resonance (res_info)
call res_info%init (112, -6, model, 7)
call res_history%add_resonance (res_info)
call res_history%write (u)
write (u, "(A)")
call res_history%to_tree (res_tree)
call res_tree%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: resonances_6"
end subroutine resonances_6
@ %def resonances_6
@ Basic operations on a resonance history set.
<<Resonances: execute tests>>=
call test (resonances_7, "resonances_7", &
"display tree format of history set elements", &
u, results)
<<Resonances: test declarations>>=
public :: resonances_7
<<Resonances: tests>>=
subroutine resonances_7 (u)
integer, intent(in) :: u
type(resonance_info_t) :: res_info
type(resonance_history_t) :: res_history
type(resonance_tree_t) :: res_tree
type(resonance_history_set_t) :: res_set
type(model_data_t), target :: model
type(flavor_t) :: flv
write (u, "(A)") "* Test output: resonances_7"
write (u, "(A)") "* Purpose: test tree format"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Initialize, fill and freeze resonance history set"
write (u, "(A)")
call res_set%init (initial_size = 2)
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_history%clear ()
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_info%init (7, 23, model, 5)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_info%init (3, -24, model, 5)
call res_history%add_resonance (res_info)
call res_info%init (7, 25, model, 5)
call res_history%add_resonance (res_info)
call res_set%enter (res_history)
call res_history%clear ()
call res_set%freeze ()
call res_set%write (u, show_trees = .true.)
write (u, "(A)")
write (u, "(A)") "* Extract tree #1"
write (u, "(A)")
call res_set%get_tree (1, res_tree)
call res_tree%write (u)
write (u, *)
write (u, "(1x,A,1x,I0)") "n_resonances =", res_tree%get_n_resonances ()
write (u, *)
write (u, "(1x,A,1x)", advance="no") "flv(r1) ="
flv = res_tree%get_flv (1)
call flv%write (u)
write (u, *)
write (u, "(1x,A,1x)", advance="no") "flv(r2) ="
flv = res_tree%get_flv (2)
call flv%write (u)
write (u, *)
write (u, *)
write (u, "(1x,A)") "[offset = 2, 4]"
write (u, "(1x,A,9(1x,I0))") "children(r1) =", &
res_tree%get_children(1, 2, 4)
write (u, "(1x,A,9(1x,I0))") "children(r2) =", &
res_tree%get_children(2, 2, 4)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: resonances_7"
end subroutine resonances_7
@ %def resonances_7
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\clearpage
\section{Mappings}
Mappings are objects that encode the transformation of the interval
$(0,1)$ to a physical variable $m^2$ or $\cos\theta$ (and back), as it
is used in the phase space parameterization. The mapping objects
contain fixed parameters, the associated methods implement the mapping
and inverse mapping operations, including the computation of the
Jacobian (phase space factor).
<<[[mappings.f90]]>>=
<<File header>>
module mappings
<<Use kinds>>
use kinds, only: TC
<<Use strings>>
use io_units
use constants, only: pi
use format_defs, only: FMT_19
use diagnostics
use md5
use model_data
use flavors
<<Standard module head>>
<<Mappings: public>>
<<Mappings: parameters>>
<<Mappings: types>>
<<Mappings: interfaces>>
contains
<<Mappings: procedures>>
end module mappings
@ %def mappings
@
\subsection{Default parameters}
This type holds the default parameters, needed for setting the scale
in cases where no mass parameter is available. The contents are public.
<<Mappings: public>>=
public :: mapping_defaults_t
<<Mappings: types>>=
type :: mapping_defaults_t
real(default) :: energy_scale = 10
real(default) :: invariant_mass_scale = 10
real(default) :: momentum_transfer_scale = 10
logical :: step_mapping = .true.
logical :: step_mapping_exp = .true.
logical :: enable_s_mapping = .false.
contains
<<Mappings: mapping defaults: TBP>>
end type mapping_defaults_t
@ %def mapping_defaults_t
@ Output.
<<Mappings: mapping defaults: TBP>>=
procedure :: write => mapping_defaults_write
<<Mappings: procedures>>=
subroutine mapping_defaults_write (object, unit)
class(mapping_defaults_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(3x,A," // FMT_19 // ")") "energy scale = ", &
object%energy_scale
write (u, "(3x,A," // FMT_19 // ")") "mass scale = ", &
object%invariant_mass_scale
write (u, "(3x,A," // FMT_19 // ")") "q scale = ", &
object%momentum_transfer_scale
write (u, "(3x,A,L1)") "step mapping = ", &
object%step_mapping
write (u, "(3x,A,L1)") "step exp. mode = ", &
object%step_mapping_exp
write (u, "(3x,A,L1)") "allow s mapping = ", &
object%enable_s_mapping
end subroutine mapping_defaults_write
@ %def mapping_defaults_write
@
<<Mappings: public>>=
public :: mapping_defaults_md5sum
<<Mappings: procedures>>=
function mapping_defaults_md5sum (mapping_defaults) result (md5sum_map)
character(32) :: md5sum_map
type(mapping_defaults_t), intent(in) :: mapping_defaults
integer :: u
u = free_unit ()
open (u, status = "scratch")
write (u, *) mapping_defaults%energy_scale
write (u, *) mapping_defaults%invariant_mass_scale
write (u, *) mapping_defaults%momentum_transfer_scale
write (u, *) mapping_defaults%step_mapping
write (u, *) mapping_defaults%step_mapping_exp
write (u, *) mapping_defaults%enable_s_mapping
rewind (u)
md5sum_map = md5sum (u)
close (u)
end function mapping_defaults_md5sum
@ %def mapping_defaults_md5sum
@
\subsection{The Mapping type}
Each mapping has a type (e.g., s-channel, infrared), a binary code
(redundant, but useful for debugging), and a reference particle. The
flavor code of this particle is stored for bookkeeping reasons, what
matters are the mass and width of this particle. Furthermore,
depending on the type, various mapping parameters can be set and used.
The parameters [[a1]] to [[a3]] (for $m^2$ mappings) and [[b1]] to
[[b3]] (for $\cos\theta$ mappings) are values that are stored once to
speed up the calculation, if [[variable_limits]] is false. The exact
meaning of these parameters depends on the mapping type. The limits
are fixed if there is a fixed c.m. energy.
<<Mappings: public>>=
public :: mapping_t
<<Mappings: types>>=
type :: mapping_t
private
integer :: type = NO_MAPPING
integer(TC) :: bincode
type(flavor_t) :: flv
real(default) :: mass = 0
real(default) :: width = 0
logical :: a_unknown = .true.
real(default) :: a1 = 0
real(default) :: a2 = 0
real(default) :: a3 = 0
logical :: b_unknown = .true.
real(default) :: b1 = 0
real(default) :: b2 = 0
real(default) :: b3 = 0
logical :: variable_limits = .true.
contains
<<Mappings: mapping: TBP>>
end type mapping_t
@ %def mapping_t
@ The valid mapping types. The extra type [[STEP_MAPPING]] is used
only internally.
<<Mappings: parameters>>=
<<Mapping modes>>
@
\subsection{Screen output}
Do not write empty mappings.
<<Mappings: public>>=
public :: mapping_write
<<Mappings: procedures>>=
subroutine mapping_write (map, unit, verbose)
type(mapping_t), intent(in) :: map
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
character(len=9) :: str
u = given_output_unit (unit); if (u < 0) return
select case(map%type)
case(S_CHANNEL); str = "s_channel"
case(COLLINEAR); str = "collinear"
case(INFRARED); str = "infrared "
case(RADIATION); str = "radiation"
case(T_CHANNEL); str = "t_channel"
case(U_CHANNEL); str = "u_channel"
case(STEP_MAPPING_E); str = "step_exp"
case(STEP_MAPPING_H); str = "step_hyp"
case(ON_SHELL); str = "on_shell"
case default; str = "????????"
end select
if (map%type /= NO_MAPPING) then
write (u, '(1x,A,I4,A)') &
"Branch #", map%bincode, ": " // &
"Mapping (" // str // ") for particle " // &
'"' // char (map%flv%get_name ()) // '"'
if (present (verbose)) then
if (verbose) then
select case (map%type)
case (S_CHANNEL, RADIATION, STEP_MAPPING_E, STEP_MAPPING_H)
write (u, "(1x,A,3(" // FMT_19 // "))") &
" m/w = ", map%mass, map%width
case default
write (u, "(1x,A,3(" // FMT_19 // "))") &
" m = ", map%mass
end select
select case (map%type)
case (S_CHANNEL, T_CHANNEL, U_CHANNEL, &
STEP_MAPPING_E, STEP_MAPPING_H, &
COLLINEAR, INFRARED, RADIATION)
write (u, "(1x,A,3(" // FMT_19 // "))") &
" a1/2/3 = ", map%a1, map%a2, map%a3
end select
select case (map%type)
case (T_CHANNEL, U_CHANNEL, COLLINEAR)
write (u, "(1x,A,3(" // FMT_19 // "))") &
" b1/2/3 = ", map%b1, map%b2, map%b3
end select
end if
end if
end if
end subroutine mapping_write
@ %def mapping_write
@
\subsection{Define a mapping}
The initialization routine sets the mapping type and the particle
(binary code and flavor code) for which the mapping applies (e.g., a
$Z$ resonance in branch \#3).
<<Mappings: public>>=
public :: mapping_init
<<Mappings: procedures>>=
subroutine mapping_init (mapping, bincode, type, f, model)
type(mapping_t), intent(inout) :: mapping
integer(TC), intent(in) :: bincode
type(string_t), intent(in) :: type
integer, intent(in), optional :: f
class(model_data_t), intent(in), optional, target :: model
mapping%bincode = bincode
select case (char (type))
case ("s_channel"); mapping%type = S_CHANNEL
case ("collinear"); mapping%type = COLLINEAR
case ("infrared"); mapping%type = INFRARED
case ("radiation"); mapping%type = RADIATION
case ("t_channel"); mapping%type = T_CHANNEL
case ("u_channel"); mapping%type = U_CHANNEL
case ("step_exp"); mapping%type = STEP_MAPPING_E
case ("step_hyp"); mapping%type = STEP_MAPPING_H
case ("on_shell"); mapping%type = ON_SHELL
case default
call msg_bug ("Mappings: encountered undefined mapping key '" &
// char (type) // "'")
end select
if (present (f) .and. present (model)) call mapping%flv%init (f, model)
end subroutine mapping_init
@ %def mapping_init
@ This sets the actual mass and width, using a parameter set. Since
the auxiliary parameters will only be determined when the mapping is
first called, they are marked as unknown.
<<Mappings: public>>=
public :: mapping_set_parameters
<<Mappings: procedures>>=
subroutine mapping_set_parameters (map, mapping_defaults, variable_limits)
type(mapping_t), intent(inout) :: map
type(mapping_defaults_t), intent(in) :: mapping_defaults
logical, intent(in) :: variable_limits
if (map%type /= NO_MAPPING) then
map%mass = map%flv%get_mass ()
map%width = map%flv%get_width ()
map%variable_limits = variable_limits
map%a_unknown = .true.
map%b_unknown = .true.
select case (map%type)
case (S_CHANNEL)
if (map%mass <= 0) then
call mapping_write (map)
call msg_fatal &
& (" S-channel resonance must have positive mass")
else if (map%width <= 0) then
call mapping_write (map)
call msg_fatal &
& (" S-channel resonance must have positive width")
end if
case (RADIATION)
map%width = max (map%width, mapping_defaults%energy_scale)
case (INFRARED, COLLINEAR)
map%mass = max (map%mass, mapping_defaults%invariant_mass_scale)
case (T_CHANNEL, U_CHANNEL)
map%mass = max (map%mass, mapping_defaults%momentum_transfer_scale)
end select
end if
end subroutine mapping_set_parameters
@ %def mapping_set_code mapping_set_parameters
@ For a step mapping the mass and width are set directly, instead of
being determined from the flavor parameter (which is meaningless
here). They correspond to the effective upper bound of phase space
due to a resonance, as opposed to the absolute upper bound.
<<Mappings: public>>=
public :: mapping_set_step_mapping_parameters
<<Mappings: procedures>>=
subroutine mapping_set_step_mapping_parameters (map, &
mass, width, variable_limits)
type(mapping_t), intent(inout) :: map
real(default), intent(in) :: mass, width
logical, intent(in) :: variable_limits
select case (map%type)
case (STEP_MAPPING_E, STEP_MAPPING_H)
map%variable_limits = variable_limits
map%a_unknown = .true.
map%b_unknown = .true.
map%mass = mass
map%width = width
end select
end subroutine mapping_set_step_mapping_parameters
@ %def mapping_set_step_mapping_parameters
@
\subsection{Retrieve contents}
Return true if there is any / an s-channel mapping.
<<Mappings: public>>=
public :: mapping_is_set
public :: mapping_is_s_channel
public :: mapping_is_on_shell
<<Mappings: mapping: TBP>>=
procedure :: is_set => mapping_is_set
procedure :: is_s_channel => mapping_is_s_channel
procedure :: is_on_shell => mapping_is_on_shell
<<Mappings: procedures>>=
function mapping_is_set (mapping) result (flag)
class(mapping_t), intent(in) :: mapping
logical :: flag
flag = mapping%type /= NO_MAPPING
end function mapping_is_set
function mapping_is_s_channel (mapping) result (flag)
class(mapping_t), intent(in) :: mapping
logical :: flag
flag = mapping%type == S_CHANNEL
end function mapping_is_s_channel
function mapping_is_on_shell (mapping) result (flag)
class(mapping_t), intent(in) :: mapping
logical :: flag
flag = mapping%type == ON_SHELL
end function mapping_is_on_shell
@ %def mapping_is_set
@ %def mapping_is_s_channel
@ %def mapping_is_on_shell
@ Return the binary code for the mapped particle.
<<Mappings: mapping: TBP>>=
procedure :: get_bincode => mapping_get_bincode
<<Mappings: procedures>>=
function mapping_get_bincode (mapping) result (bincode)
class(mapping_t), intent(in) :: mapping
integer(TC) :: bincode
bincode = mapping%bincode
end function mapping_get_bincode
@ %def mapping_get_bincode
@ Return the flavor object for the mapped particle.
<<Mappings: mapping: TBP>>=
procedure :: get_flv => mapping_get_flv
<<Mappings: procedures>>=
function mapping_get_flv (mapping) result (flv)
class(mapping_t), intent(in) :: mapping
type(flavor_t) :: flv
flv = mapping%flv
end function mapping_get_flv
@ %def mapping_get_flv
@ Return stored mass and width, respectively.
<<Mappings: public>>=
public :: mapping_get_mass
public :: mapping_get_width
<<Mappings: procedures>>=
function mapping_get_mass (mapping) result (mass)
real(default) :: mass
type(mapping_t), intent(in) :: mapping
mass = mapping%mass
end function mapping_get_mass
function mapping_get_width (mapping) result (width)
real(default) :: width
type(mapping_t), intent(in) :: mapping
width = mapping%width
end function mapping_get_width
@ %def mapping_get_mass
@ %def mapping_get_width
@
\subsection{Compare mappings}
Equality for single mappings and arrays
<<Mappings: public>>=
public :: operator(==)
<<Mappings: interfaces>>=
interface operator(==)
module procedure mapping_equal
end interface
<<Mappings: procedures>>=
function mapping_equal (m1, m2) result (equal)
type(mapping_t), intent(in) :: m1, m2
logical :: equal
if (m1%type == m2%type) then
select case (m1%type)
case (NO_MAPPING)
equal = .true.
case (S_CHANNEL, RADIATION, STEP_MAPPING_E, STEP_MAPPING_H)
equal = (m1%mass == m2%mass) .and. (m1%width == m2%width)
case default
equal = (m1%mass == m2%mass)
end select
else
equal = .false.
end if
end function mapping_equal
@ %def mapping_equal
@
\subsection{Mappings of the invariant mass}
Inserting an $x$ value between 0 and 1, we want to compute the
corresponding invariant mass $m^2(x)$ and the jacobian, aka phase
space factor $f(x)$. We also need the reverse operation.
In general, the phase space factor $f$ is defined by
\begin{equation}
\frac{1}{s}\int_{m^2_{\textrm{min}}}^{m^2_{\textrm{max}}} dm^2\,g(m^2)
= \int_0^1 dx\,\frac{1}{s}\,\frac{dm^2}{dx}\,g(m^2(x))
= \int_0^1 dx\,f(x)\,g(x),
\end{equation}
where thus
\begin{equation}
f(x) = \frac{1}{s}\,\frac{dm^2}{dx}.
\end{equation}
With this mapping, a function of the form
\begin{equation}
g(m^2) = c\frac{dx(m^2)}{dm^2}
\end{equation}
is mapped to a constant:
\begin{equation}
\frac{1}{s}\int_{m^2_{\textrm{min}}}^{m^2_{\textrm{max}}} dm^2\,g(m^2)
= \int_0^1 dx\,f(x)\,g(m^2(x)) = \int_0^1 dx\,\frac{c}{s}.
\end{equation}
Here is the mapping routine. Input are the available energy
squared [[s]], the limits for $m^2$, and the $x$ value. Output are
the $m^2$ value and the phase space factor $f$.
<<Mappings: public>>=
public :: mapping_compute_msq_from_x
<<Mappings: procedures>>=
subroutine mapping_compute_msq_from_x (map, s, msq_min, msq_max, msq, f, x)
type(mapping_t), intent(inout) :: map
real(default), intent(in) :: s, msq_min, msq_max
real(default), intent(out) :: msq, f
real(default), intent(in) :: x
real(default) :: z, msq0, msq1, tmp
integer :: type
type = map%type
if (s == 0) &
call msg_fatal (" Applying msq mapping for zero energy")
<<Modify mapping type if necessary>>
select case(type)
case (NO_MAPPING)
<<Constants for trivial msq mapping>>
<<Apply trivial msq mapping>>
case (S_CHANNEL)
<<Constants for s-channel resonance mapping>>
<<Apply s-channel resonance mapping>>
case (COLLINEAR, INFRARED, RADIATION)
<<Constants for s-channel pole mapping>>
<<Apply s-channel pole mapping>>
case (T_CHANNEL, U_CHANNEL)
<<Constants for t-channel pole mapping>>
<<Apply t-channel pole mapping>>
case (STEP_MAPPING_E)
<<Constants for exponential step mapping>>
<<Apply exponential step mapping>>
case (STEP_MAPPING_H)
<<Constants for hyperbolic step mapping>>
<<Apply hyperbolic step mapping>>
case default
call msg_fatal ( " Attempt to apply undefined msq mapping")
end select
end subroutine mapping_compute_msq_from_x
@ %def mapping_compute_msq_from_x
@ The inverse mapping
<<Mappings: public>>=
public :: mapping_compute_x_from_msq
<<Mappings: procedures>>=
subroutine mapping_compute_x_from_msq (map, s, msq_min, msq_max, msq, f, x)
type(mapping_t), intent(inout) :: map
real(default), intent(in) :: s, msq_min, msq_max
real(default), intent(in) :: msq
real(default), intent(out) :: f, x
real(default) :: msq0, msq1, tmp, z
integer :: type
type = map%type
if (s == 0) &
call msg_fatal (" Applying inverse msq mapping for zero energy")
<<Modify mapping type if necessary>>
select case (type)
case (NO_MAPPING)
<<Constants for trivial msq mapping>>
<<Apply inverse trivial msq mapping>>
case (S_CHANNEL)
<<Constants for s-channel resonance mapping>>
<<Apply inverse s-channel resonance mapping>>
case (COLLINEAR, INFRARED, RADIATION)
<<Constants for s-channel pole mapping>>
<<Apply inverse s-channel pole mapping>>
case (T_CHANNEL, U_CHANNEL)
<<Constants for t-channel pole mapping>>
<<Apply inverse t-channel pole mapping>>
case (STEP_MAPPING_E)
<<Constants for exponential step mapping>>
<<Apply inverse exponential step mapping>>
case (STEP_MAPPING_H)
<<Constants for hyperbolic step mapping>>
<<Apply inverse hyperbolic step mapping>>
case default
call msg_fatal ( " Attempt to apply undefined msq mapping")
end select
end subroutine mapping_compute_x_from_msq
@ %def mapping_compute_x_from_msq
@
\subsubsection{Trivial mapping}
We simply map the boundaries of the interval $(m_{\textrm{min}},
m_{\textrm{max}})$ to $(0,1)$:
\begin{equation}
m^2 = (1-x) m_{\textrm{min}}^2 + x m_{\textrm{max}}^2;
\end{equation}
the inverse is
\begin{equation}
x = \frac{m^2 - m_{\textrm{min}}^2}{m_{\textrm{max}}^2- m_{\textrm{min}}^2}.
\end{equation}
Hence
\begin{equation}
f(x) = \frac{m_{\textrm{max}}^2 - m_{\textrm{min}}^2}{s},
\end{equation}
and we have, as required,
\begin{equation}
f(x)\,\frac{dx}{dm^2} = \frac{1}{s}.
\end{equation}
We store the constant parameters the first time the mapping is called
-- or, if limits vary, recompute them each time.
<<Constants for trivial msq mapping>>=
if (map%variable_limits .or. map%a_unknown) then
map%a1 = 0
map%a2 = msq_max - msq_min
map%a3 = map%a2 / s
map%a_unknown = .false.
end if
<<Apply trivial msq mapping>>=
msq = (1-x) * msq_min + x * msq_max
f = map%a3
<<Apply inverse trivial msq mapping>>=
if (map%a2 /= 0) then
x = (msq - msq_min) / map%a2
else
x = 0
end if
f = map%a3
@ Resonance or step mapping does not make much sense if the resonance mass is
outside the kinematical bounds. If this is the case, revert to
[[NO_MAPPING]]. This is possible even if the kinematical bounds vary
from event to event.
<<Modify mapping type if necessary>>=
select case (type)
case (S_CHANNEL, STEP_MAPPING_E, STEP_MAPPING_H)
msq0 = map%mass**2
if (msq0 < msq_min .or. msq0 > msq_max) type = NO_MAPPING
end select
@
\subsubsection{Breit-Wigner mapping}
A Breit-Wigner resonance with mass $M$ and width $\Gamma$ is flattened
by the following mapping:
This mapping does not make much sense if the resonance mass is too low.
If this is the case, revert to [[NO_MAPPING]]. There is a tricky
point with this if the mass is too high: [[msq_max]] is not a
constant if structure functions are around. However, switching the
type depending on the overall energy does not change the integral, it
is just another branching point.
\begin{equation}
m^2 = M(M+t\Gamma),
\end{equation}
where
\begin{equation}
t = \tan\left[(1-x)\arctan\frac{m^2_{\textrm{min}} - M^2}{M\Gamma}
+ x \arctan\frac{m^2_{\textrm{max}} - M^2}{M\Gamma}\right].
\end{equation}
The inverse:
\begin{equation}
x = \frac{ \arctan\frac{m^2 - M^2}{M\Gamma}
- \arctan\frac{m^2_{\textrm{min}} - M^2}{M\Gamma}}
{ \arctan\frac{m^2_{\textrm{max}} - M^2}{M\Gamma}
- \arctan\frac{m^2_{\textrm{min}} - M^2}{M\Gamma}}
\end{equation}
The phase-space factor of this transformation is
\begin{equation}
f(x) = \frac{M\Gamma}{s}\left(
\arctan\frac{m^2_{\textrm{max}} - M^2}{M\Gamma}
- \arctan\frac{m^2_{\textrm{min}} - M^2}{M\Gamma}\right)
(1 + t^2).
\end{equation}
This maps any function proportional to
\begin{equation}
g(m^2) = \frac{M\Gamma}{(m^2-M^2)^2 + M^2\Gamma^2}
\end{equation}
to a constant times $1/s$.
<<Constants for s-channel resonance mapping>>=
if (map%variable_limits .or. map%a_unknown) then
msq0 = map%mass ** 2
map%a1 = atan ((msq_min - msq0) / (map%mass * map%width))
map%a2 = atan ((msq_max - msq0) / (map%mass * map%width))
map%a3 = (map%a2 - map%a1) * (map%mass * map%width) / s
map%a_unknown = .false.
end if
<<Apply s-channel resonance mapping>>=
z = (1-x) * map%a1 + x * map%a2
if (-pi/2 < z .and. z < pi/2) then
tmp = tan (z)
msq = map%mass * (map%mass + map%width * tmp)
f = map%a3 * (1 + tmp**2)
else
msq = 0
f = 0
end if
<<Apply inverse s-channel resonance mapping>>=
tmp = (msq - msq0) / (map%mass * map%width)
x = (atan (tmp) - map%a1) / (map%a2 - map%a1)
f = map%a3 * (1 + tmp**2)
@
\subsubsection{Mapping for massless splittings}
This mapping accounts for approximately scale-invariant behavior where
$\ln M^2$ is evenly distributed.
\begin{equation}
m^2 = m_{\textrm{min}}^2 + M^2\left(\exp(xL)-1\right)
\end{equation}
where
\begin{equation}
L = \ln\left(\frac{m_{\textrm{max}}^2 - m_{\textrm{min}}^2}{M^2} + 1\right).
\end{equation}
The inverse:
\begin{equation}
x = \frac1L\ln\left(\frac{m^2-m_{\textrm{min}}^2}{M^2} + 1\right)
\end{equation}
The constant $M$ is a characteristic scale. Above this scale
($m^2-m_{\textrm{min}}^2 \gg M^2$), this mapping behaves like
$x\propto\ln m^2$, while below the scale it reverts to a linear
mapping.
The phase-space factor is
\begin{equation}
f(x) = \frac{M^2}{s}\,\exp(xL)\,L.
\end{equation}
A function proportional to
\begin{equation}
g(m^2) = \frac{1}{(m^2-m_{\textrm{min}}^2) + M^2}
\end{equation}
is mapped to a constant, i.e., a simple pole near $m_{\textrm{min}}$
with a regulator mass $M$.
This type of mapping is useful for massless collinear and infrared
singularities, where the scale is stored as the mass parameter. In
the radiation case (IR radiation off massive particle), the heavy
particle width is the characteristic scale.
<<Constants for s-channel pole mapping>>=
if (map%variable_limits .or. map%a_unknown) then
if (type == RADIATION) then
msq0 = map%width**2
else
msq0 = map%mass**2
end if
map%a1 = msq0
map%a2 = log ((msq_max - msq_min) / msq0 + 1)
map%a3 = map%a2 / s
map%a_unknown = .false.
end if
<<Apply s-channel pole mapping>>=
msq1 = map%a1 * exp (x * map%a2)
msq = msq1 - map%a1 + msq_min
f = map%a3 * msq1
<<Apply inverse s-channel pole mapping>>=
msq1 = msq - msq_min + map%a1
x = log (msq1 / map%a1) / map%a2
f = map%a3 * msq1
@
\subsubsection{Mapping for t-channel poles}
This is also approximately scale-invariant, and we use the same type
of mapping as before. However, we map $1/x$ singularities at both
ends of the interval; again, the mapping becomes linear when the
distance is less than $M^2$:
\begin{equation}
m^2 =
\begin{cases}
m_{\textrm{min}}^2 + M^2\left(\exp(xL)-1\right)
&
\text{for $0 < x < \frac12$}
\\
m_{\textrm{max}}^2 - M^2\left(\exp((1-x)L)-1\right)
&
\text{for $\frac12 \leq x < 1$}
\end{cases}
\end{equation}
where
\begin{equation}
L = 2\ln\left(\frac{m_{\textrm{max}}^2 - m_{\textrm{min}}^2}{2M^2}
+ 1\right).
\end{equation}
The inverse:
\begin{equation}
x =
\begin{cases}
\frac1L\ln\left(\frac{m^2-m_{\textrm{min}}^2}{M^2} + 1\right)
&
\text{for $m^2 < (m_{\textrm{max}}^2 - m_{\textrm{min}}^2)/2$}
\\
1 - \frac1L\ln\left(\frac{m_{\textrm{max}}-m^2}{M^2} + 1\right)
&
\text{for $m^2 \geq (m_{\textrm{max}}^2 - m_{\textrm{min}}^2)/2$}
\end{cases}
\end{equation}
The phase-space factor is
\begin{equation}
f(x) =
\begin{cases}
\frac{M^2}{s}\,\exp(xL)\,L.
&
\text{for $0 < x < \frac12$}
\\
\frac{M^2}{s}\,\exp((1-x)L)\,L.
&
\text{for $\frac12 \leq x < 1$}
\end{cases}
\end{equation}
A (continuous) function proportional to
\begin{equation}
g(m^2) =
\begin{cases}
1/(m^2-m_{\textrm{min}}^2) + M^2)
&
\text{for $m^2 < (m_{\textrm{max}}^2 - m_{\textrm{min}}^2)/2$}
\\
1/((m_{\textrm{max}}^2 - m^2) + M^2)
&
\text{for $m^2 \leq (m_{\textrm{max}}^2 - m_{\textrm{min}}^2)/2$}
\end{cases}
\end{equation}
is mapped to a constant by this mapping, i.e., poles near both ends of
the interval.
<<Constants for t-channel pole mapping>>=
if (map%variable_limits .or. map%a_unknown) then
msq0 = map%mass**2
map%a1 = msq0
map%a2 = 2 * log ((msq_max - msq_min)/(2*msq0) + 1)
map%a3 = map%a2 / s
map%a_unknown = .false.
end if
<<Apply t-channel pole mapping>>=
if (x < .5_default) then
msq1 = map%a1 * exp (x * map%a2)
msq = msq1 - map%a1 + msq_min
else
msq1 = map%a1 * exp ((1-x) * map%a2)
msq = -(msq1 - map%a1) + msq_max
end if
f = map%a3 * msq1
<<Apply inverse t-channel pole mapping>>=
if (msq < (msq_max + msq_min)/2) then
msq1 = msq - msq_min + map%a1
x = log (msq1/map%a1) / map%a2
else
msq1 = msq_max - msq + map%a1
x = 1 - log (msq1/map%a1) / map%a2
end if
f = map%a3 * msq1
@
\subsection{Step mapping}
Step mapping is useful when the allowed range for a squared-mass
variable is large, but only a fraction at the lower end is populated
because the particle in question is an (off-shell) decay product of a
narrow resonance. I.e., if the resonance was forced to be on-shell,
the upper end of the range would be the resonance mass, minus the
effective (real or resonance) mass of the particle(s) in the sibling
branch of the decay.
The edge of this phase space section has a width which is determined
by the width of the parent, plus the width of the sibling branch. (The
widths might be added in quadrature, but this precision is probably
not important.)
\subsubsection{Fermi function}
A possible mapping is derived from the Fermi function which has
precisely this behavior. The Fermi function is given by
\begin{equation}
f(x) = \frac{1}{1 + \exp\frac{x-\mu}{\gamma}}
\end{equation}
where $x$ is taken as the invariant mass squared, $\mu$ is the
invariant mass squared of the edge, and $\gamma$ is the effective
width which is given by the widths of the parent and the sibling
branch. (Widths might be added in quadrature, but we do not require
this level of precision.)
\begin{align}
x &= \frac{m^2 - m_{\text{min}}^2}{\Delta m^2}
\\
\mu &=
\frac{m_{\text{max,eff}}^2 - m_{\text{min}}^2}
{\Delta m^2}
\\
\gamma &= \frac{2m_{\text{max,eff}}\Gamma}{\Delta m^2}
\end{align}
with
\begin{equation}
\Delta m^2 = m_{\text{max}}^2 - m_{\text{min}}^2
\end{equation}
$m^2$ is thus given by
\begin{equation}
m^2(x) = xm_{\text{max}}^2 + (1-x)m_{\text{min}}^2
\end{equation}
For the mapping, we compute the integral $g(x)$ of the Fermi function,
normalized such that $g(0)=0$ and $g(1)=1$. We introduce the abbreviations
\begin{align}
\alpha &= 1 - \gamma\ln\frac{1 + \beta e^{1/\gamma}}{1 + \beta}
\\
\beta &= e^{- \mu/\gamma}
\end{align}
and obtain
\begin{equation}
g(x) = \frac{1}{\alpha}
\left(x - \gamma\ln\frac{1 + \beta e^{x/\gamma}}
{1 + \beta}\right)
\end{equation}
The actual mapping is the inverse function $h(y) = g^{-1}(y)$,
\begin{equation}
h(y) = -\gamma\ln\left(e^{-\alpha y/\gamma}(1 + \beta) - \beta\right)
\end{equation}
The Jacobian is
\begin{equation}
\frac{dh}{dy} = \alpha\left(1 - e^{\alpha y/\gamma}
\frac{\beta}{1 + \beta}\right)^{-1}
\end{equation}
which is equal to $1/(dg/dx)$, namely
\begin{equation}
\frac{dg}{dx} = \frac{1}{\alpha}\,\frac{1}{1 + \beta e^{x/\gamma}}
\end{equation}
The final result is
\begin{align}
\int_{m_{\text{min}}^2}^{m_{\text{max}}^2} dm^2\,F(m^2)
&= \Delta m^2\int_0^1\,dx\,F(m^2(x))
\\
&= \Delta m^2\int_0^1\,dy\,F(m^2(h(y)))\,\frac{dh}{dy}
\end{align}
Here is the implementation. We fill [[a1]], [[a2]], [[a3]] with
$\alpha,\beta,\gamma$, respectively.
<<Constants for exponential step mapping>>=
if (map%variable_limits .or. map%a_unknown) then
map%a3 = max (2 * map%mass * map%width / (msq_max - msq_min), 0.01_default)
map%a2 = exp (- (map%mass**2 - msq_min) / (msq_max - msq_min) &
/ map%a3)
map%a1 = 1 - map%a3 * log ((1 + map%a2 * exp (1 / map%a3)) / (1 + map%a2))
end if
<<Apply exponential step mapping>>=
tmp = exp (- x * map%a1 / map%a3) * (1 + map%a2)
z = - map%a3 * log (tmp - map%a2)
msq = z * msq_max + (1 - z) * msq_min
f = map%a1 / (1 - map%a2 / tmp) * (msq_max - msq_min) / s
<<Apply inverse exponential step mapping>>=
z = (msq - msq_min) / (msq_max - msq_min)
tmp = 1 + map%a2 * exp (z / map%a3)
x = (z - map%a3 * log (tmp / (1 + map%a2))) &
/ map%a1
f = map%a1 * tmp * (msq_max - msq_min) / s
@
\subsubsection{Hyperbolic mapping}
The Fermi function has the drawback that it decreases exponentially.
It might be preferable to take a function with a power-law decrease,
such that the high-mass region is not completely depopulated.
Here, we start with the actual mapping which we take as
\begin{equation}
h(y) = \frac{b}{a-y} - \frac{b}{a} + \mu y
\end{equation}
with the abbreviation
\begin{equation}
a = \frac12\left(1 + \sqrt{1 + \frac{4b}{1-\mu}}\right)
\end{equation}
This is a hyperbola in the $xy$ plane. The derivative is
\begin{equation}
\frac{dh}{dy} = \frac{b}{(a-y)^2} + \mu
\end{equation}
The constants correspond to
\begin{align}
\mu &=
\frac{m_{\text{max,eff}}^2 - m_{\text{min}}^2}
{\Delta m^2}
\\
b &= \frac{1}{\mu}\left(\frac{2m_{\text{max,eff}}\Gamma}{\Delta m^2}\right)^2
\end{align}
The inverse function is the solution of a quadratic equation,
\begin{equation}
g(x) = \frac{1}{2}
\left[\left(a + \frac{x}{\mu} + \frac{b}{a\mu}\right)
- \sqrt{\left(a-\frac{x}{\mu}\right)^2
+ 2\frac{b}{a\mu}\left(a + \frac{x}{\mu}\right)
+ \left(\frac{b}{a\mu}\right)^2}\right]
\end{equation}
The constants $a_{1,2,3}$ are identified with $a,b,\mu$.
<<Constants for hyperbolic step mapping>>=
if (map%variable_limits .or. map%a_unknown) then
map%a3 = (map%mass**2 - msq_min) / (msq_max - msq_min)
map%a2 = max ((2 * map%mass * map%width / (msq_max - msq_min))**2 &
/ map%a3, 1e-6_default)
map%a1 = (1 + sqrt (1 + 4 * map%a2 / (1 - map%a3))) / 2
end if
<<Apply hyperbolic step mapping>>=
z = map%a2 / (map%a1 - x) - map%a2 / map%a1 + map%a3 * x
msq = z * msq_max + (1 - z) * msq_min
f = (map%a2 / (map%a1 - x)**2 + map%a3) * (msq_max - msq_min) / s
<<Apply inverse hyperbolic step mapping>>=
z = (msq - msq_min) / (msq_max - msq_min)
tmp = map%a2 / (map%a1 * map%a3)
x = ((map%a1 + z / map%a3 + tmp) &
- sqrt ((map%a1 - z / map%a3)**2 + 2 * tmp * (map%a1 + z / map%a3) &
+ tmp**2)) / 2
f = (map%a2 / (map%a1 - x)**2 + map%a3) * (msq_max - msq_min) / s
@
\subsection{Mappings of the polar angle}
The other type of singularity, a simple pole just outside the
integration region, can occur in the integration over $\cos\theta$.
This applies to exchange of massless (or light) particles.
Double poles (Coulomb scattering) are also possible, but only in
certain cases. These are also handled by the single-pole mapping.
The mapping is analogous to the previous $m^2$ pole mapping, but with
a different normalization and notation of variables:
\begin{equation}
\frac12\int_{-1}^1 d\cos\theta\,g(\theta)
= \int_0^1 dx\,\frac{d\cos\theta}{dx}\,g(\theta(x))
= \int_0^1 dx\,f(x)\,g(x),
\end{equation}
where thus
\begin{equation}
f(x) = \frac12\,\frac{d\cos\theta}{dx}.
\end{equation}
With this mapping, a function of the form
\begin{equation}
g(\theta) = c\frac{dx(\cos\theta)}{d\cos\theta}
\end{equation}
is mapped to a constant:
\begin{equation}
\int_{-1}^1 d\cos\theta\,g(\theta)
= \int_0^1 dx\,f(x)\,g(\theta(x)) = \int_0^1 dx\,c.
\end{equation}
<<Mappings: public>>=
public :: mapping_compute_ct_from_x
<<Mappings: procedures>>=
subroutine mapping_compute_ct_from_x (map, s, ct, st, f, x)
type(mapping_t), intent(inout) :: map
real(default), intent(in) :: s
real(default), intent(out) :: ct, st, f
real(default), intent(in) :: x
real(default) :: tmp, ct1
select case (map%type)
case (NO_MAPPING, S_CHANNEL, INFRARED, RADIATION, &
STEP_MAPPING_E, STEP_MAPPING_H)
<<Apply trivial ct mapping>>
case (T_CHANNEL, U_CHANNEL, COLLINEAR)
<<Constants for ct pole mapping>>
<<Apply ct pole mapping>>
case default
call msg_fatal (" Attempt to apply undefined ct mapping")
end select
end subroutine mapping_compute_ct_from_x
@ %def mapping_compute_ct_from_x
<<Mappings: public>>=
public :: mapping_compute_x_from_ct
<<Mappings: procedures>>=
subroutine mapping_compute_x_from_ct (map, s, ct, f, x)
type(mapping_t), intent(inout) :: map
real(default), intent(in) :: s
real(default), intent(in) :: ct
real(default), intent(out) :: f, x
real(default) :: ct1
select case (map%type)
case (NO_MAPPING, S_CHANNEL, INFRARED, RADIATION, &
STEP_MAPPING_E, STEP_MAPPING_H)
<<Apply inverse trivial ct mapping>>
case (T_CHANNEL, U_CHANNEL, COLLINEAR)
<<Constants for ct pole mapping>>
<<Apply inverse ct pole mapping>>
case default
call msg_fatal (" Attempt to apply undefined inverse ct mapping")
end select
end subroutine mapping_compute_x_from_ct
@ %def mapping_compute_x_from_ct
@
\subsubsection{Trivial mapping}
This is just the mapping of the interval $(-1,1)$ to $(0,1)$:
\begin{equation}
\cos\theta = -1 + 2x
\end{equation}
and
\begin{equation}
f(x) = 1
\end{equation}
with the inverse
\begin{equation}
x = \frac{1+\cos\theta}{2}
\end{equation}
<<Apply trivial ct mapping>>=
tmp = 2 * (1-x)
ct = 1 - tmp
st = sqrt (tmp * (2-tmp))
f = 1
<<Apply inverse trivial ct mapping>>=
x = (ct + 1) / 2
f = 1
@
\subsubsection{Pole mapping}
As above for $m^2$, we simultaneously map poles at both ends of the
$\cos\theta$ interval. The formulae are completely analogous:
\begin{equation}
\cos\theta =
\begin{cases}
\frac{M^2}{s}\left[\exp(xL)-1\right] - 1
&
\text{for $x<\frac12$}
\\
-\frac{M^2}{s}\left[\exp((1-x)L)-1\right] + 1
&
\text{for $x\geq\frac12$}
\end{cases}
\end{equation}
where
\begin{equation}
L = 2\ln\frac{M^2+s}{M^2}.
\end{equation}
Inverse:
\begin{equation}
x =
\begin{cases}
\frac{1}{2L}\ln\frac{1 + \cos\theta + M^2/s}{M^2/s}
&
\text{for $\cos\theta < 0$}
\\
1 - \frac{1}{2L}\ln\frac{1 - \cos\theta + M^2/s}{M^2/s}
&
\text{for $\cos\theta \geq 0$}
\end{cases}
\end{equation}
The phase-space factor:
\begin{equation}
f(x) =
\begin{cases}
\frac{M^2}{s}\exp(xL)\,L
&
\text{for $x<\frac12$}
\\
\frac{M^2}{s}\exp((1-x)L)\,L
&
\text{for $x\geq\frac12$}
\end{cases}
\end{equation}
<<Constants for ct pole mapping>>=
if (map%variable_limits .or. map%b_unknown) then
map%b1 = map%mass**2 / s
map%b2 = log ((map%b1 + 1) / map%b1)
map%b3 = 0
map%b_unknown = .false.
end if
<<Apply ct pole mapping>>=
if (x < .5_default) then
ct1 = map%b1 * exp (2 * x * map%b2)
ct = ct1 - map%b1 - 1
else
ct1 = map%b1 * exp (2 * (1-x) * map%b2)
ct = -(ct1 - map%b1) + 1
end if
if (ct >= -1 .and. ct <= 1) then
st = sqrt (1 - ct**2)
f = ct1 * map%b2
else
ct = 1; st = 0; f = 0
end if
<<Apply inverse ct pole mapping>>=
if (ct < 0) then
ct1 = ct + map%b1 + 1
x = log (ct1 / map%b1) / (2 * map%b2)
else
ct1 = -ct + map%b1 + 1
x = 1 - log (ct1 / map%b1) / (2 * map%b2)
end if
f = ct1 * map%b2
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\clearpage
\section{Phase-space trees}
The phase space evaluation is organized in terms of trees, where each
branch corresponds to three integrations: $m^2$, $\cos\theta$, and
$\phi$. The complete tree thus makes up a specific parameterization
of the multidimensional phase-space integral. For the multi-channel
integration, the phase-space tree is a single channel.
The trees imply mappings of formal Feynman tree graphs into arrays of
integer numbers: Each branch, corresponding to a particular line in
the graph, is assigned an integer code $c$ (with kind value [[TC]] =
tree code).
In this integer, each bit determines whether a particular external
momentum flows through the line. The external branches therefore have
codes $1,2,4,8,\ldots$. An internal branch has those bits ORed
corresponding to the momenta flowing through it. For example, a
branch with momentum $p_1+p_4$ has code $2^0+2^3=1+8=9$.
There is a two-fold ambiguity: Momentum conservation implies that the
branch with code
\begin{equation}
c_0 = \sum_{i=1}^{n(\rm{ext})} 2^{i-1}
\end{equation}
i.e. the branch with momentum $p_1+p_2+\ldots p_n$ has momentum zero,
which is equivalent to tree code $0$ by definition. Correspondingly,
\begin{equation}
c \quad\textrm{and}\quad c_0 - c = c\;\textrm{XOR}\;c_0
\end{equation}
are equivalent. E.g., if there are five externals with codes
$c=1,2,4,8,16$, then $c=9$ and $\bar c=31-9=22$ are equivalent.
This ambiguity may be used to assign a direction to the line: If all
momenta are understood as outgoing, $c=9$ in the example above means
$p_1+p_4$, but $c=22$ means $p_2+p_3+p_5 = -(p_1+p_4)$.
Here we make use of the ambiguity in a slightly different way. First,
the initial particles are singled out as those externals with the
highest bits, the IN-bits. (Here: $8$ and $16$ for a $2\to 3$
scattering process, $16$ only for a $1\to 4$ decay.) Then we invert
those codes where all IN-bits are set. For a decay process this maps
each tree of an equivalence class onto a unique representative (that one
with the smallest integer codes). For a scattering process we proceed
further:
The ambiguity remains in all branches where only one IN-bit is set,
including the initial particles. If there are only externals with
this property, we have an $s$-channel graph which we leave as it is.
In all other cases, an internal with only one IN-bit is a $t$-channel
line, which for phase space integration should be associated with one
of the initial momenta as a reference axis. We take that one whose
bit is set in the current tree code. (E.g., for branch $c=9$ we use
the initial particle $c=8$ as reference axis, whereas for the same
branch we would take $c=16$ if it had been assigned $\bar c=31-9=22$
as tree code.) Thus, different ways of coding the same $t$-channel
graph imply different phase space parameterizations.
$s$-channel graphs have a unique parameterization. The same sets of
parameterizations are used for $t$-channel graphs, except for the
reference frames of their angular parts. We map each
$t$-channel graph onto an $s$-channel graph as follows:
Working in ascending order, for each $t$-channel line (whose code has
exactly one IN-bit set) the attached initial line is flipped upstream,
while the outgoing line is flipped downstream. (This works only if
$t$-channel graphs are always parameterized beginning at their outer
vertices, which we require as a restriction.) After all possible
flips have been applied, we have an $s$-channel graph. We only have
to remember the initial particle a vertex was originally attached to.
<<[[phs_trees.f90]]>>=
<<File header>>
module phs_trees
<<Use kinds>>
use kinds, only: TC
<<Use strings>>
use io_units
use constants, only: twopi, twopi2, twopi5
use format_defs, only: FMT_19
use numeric_utils, only: vanishes
use diagnostics
use lorentz
use permutations, only: permutation_t, permutation_size
use permutations, only: permutation_init, permutation_find
use permutations, only: tc_decay_level, tc_permute
use model_data
use flavors
use resonances, only: resonance_history_t, resonance_info_t
use mappings
<<Standard module head>>
<<PHS trees: public>>
<<PHS trees: types>>
contains
<<PHS trees: procedures>>
end module phs_trees
@ %def phs_trees
@
\subsection{Particles}
We define a particle type which contains only four-momentum and
invariant mass squared, and a flag that tells whether the momentum is
filled or not.
<<PHS trees: public>>=
public :: phs_prt_t
<<PHS trees: types>>=
type :: phs_prt_t
private
logical :: defined = .false.
type(vector4_t) :: p
real(default) :: p2
end type phs_prt_t
@ %def phs_prt_t
@ Set contents:
<<PHS trees: public>>=
public :: phs_prt_set_defined
public :: phs_prt_set_undefined
public :: phs_prt_set_momentum
public :: phs_prt_set_msq
<<PHS trees: procedures>>=
elemental subroutine phs_prt_set_defined (prt)
type(phs_prt_t), intent(inout) :: prt
prt%defined = .true.
end subroutine phs_prt_set_defined
elemental subroutine phs_prt_set_undefined (prt)
type(phs_prt_t), intent(inout) :: prt
prt%defined = .false.
end subroutine phs_prt_set_undefined
elemental subroutine phs_prt_set_momentum (prt, p)
type(phs_prt_t), intent(inout) :: prt
type(vector4_t), intent(in) :: p
prt%p = p
end subroutine phs_prt_set_momentum
elemental subroutine phs_prt_set_msq (prt, p2)
type(phs_prt_t), intent(inout) :: prt
real(default), intent(in) :: p2
prt%p2 = p2
end subroutine phs_prt_set_msq
@ %def phs_prt_set_defined phs_prt_set_momentum phs_prt_set_msq
@ Access methods:
<<PHS trees: public>>=
public :: phs_prt_is_defined
public :: phs_prt_get_momentum
public :: phs_prt_get_msq
<<PHS trees: procedures>>=
elemental function phs_prt_is_defined (prt) result (defined)
logical :: defined
type(phs_prt_t), intent(in) :: prt
defined = prt%defined
end function phs_prt_is_defined
elemental function phs_prt_get_momentum (prt) result (p)
type(vector4_t) :: p
type(phs_prt_t), intent(in) :: prt
p = prt%p
end function phs_prt_get_momentum
elemental function phs_prt_get_msq (prt) result (p2)
real(default) :: p2
type(phs_prt_t), intent(in) :: prt
p2 = prt%p2
end function phs_prt_get_msq
@ %def phs_prt_is_defined phs_prt_get_momentum phs_prt_get_msq
@ Addition of momenta (invariant mass square is computed).
<<PHS trees: public>>=
public :: phs_prt_combine
<<PHS trees: procedures>>=
elemental subroutine phs_prt_combine (prt, prt1, prt2)
type(phs_prt_t), intent(inout) :: prt
type(phs_prt_t), intent(in) :: prt1, prt2
prt%defined = .true.
prt%p = prt1%p + prt2%p
prt%p2 = prt%p ** 2
call phs_prt_check (prt)
end subroutine phs_prt_combine
@ %def phs_prt_combine
@ Output
<<PHS trees: public>>=
public :: phs_prt_write
<<PHS trees: procedures>>=
subroutine phs_prt_write (prt, unit)
type(phs_prt_t), intent(in) :: prt
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
if (prt%defined) then
call vector4_write (prt%p, u)
write (u, "(1x,A,1x," // FMT_19 // ")") "T = ", prt%p2
else
write (u, "(3x,A)") "[undefined]"
end if
end subroutine phs_prt_write
@ %def phs_prt_write
<<PHS trees: public>>=
public :: phs_prt_check
<<PHS trees: procedures>>=
elemental subroutine phs_prt_check (prt)
type(phs_prt_t), intent(inout) :: prt
if (prt%p2 < 0._default) then
prt%p2 = 0._default
end if
end subroutine phs_prt_check
@ %def phs_prt_check
@
\subsection{The phase-space tree type}
\subsubsection{Definition}
In the concrete implementation, each branch $c$ may have two
\emph{daughters} $c_1$ and $c_2$ such that $c_1+c_2=c$, a
\emph{sibling} $c_s$ and a \emph{mother} $c_m$ such that $c+c_s =
c_m$, and a \emph{friend} which is kept during flips, such that it can
indicate a fixed reference frame. Absent entries are set $c=0$.
First, declare the branch type. There is some need to have this
public. Give initializations for all components, so no [[init]]
routine is necessary. The branch has some information about the
associated coordinates and about connections.
<<PHS trees: types>>=
type :: phs_branch_t
private
logical :: set = .false.
logical :: inverted_decay = .false.
logical :: inverted_axis = .false.
integer(TC) :: mother = 0
integer(TC) :: sibling = 0
integer(TC) :: friend = 0
integer(TC) :: origin = 0
integer(TC), dimension(2) :: daughter = 0
integer :: firstborn = 0
logical :: has_children = .false.
logical :: has_friend = .false.
logical :: is_real = .false.
end type phs_branch_t
@ %def phs_branch_t
@ The tree type: No initialization, this is done by
[[phs_tree_init]]. In addition to the branch array which
The branches are collected in an array which holds all possible
branches, of which only a few are set. After flips have been applied,
the branch $c_M=\sum_{i=1}^{n({\rm fin})}2^{i-1}$ must be there,
indicating the mother of all decay products. In addition, we should
check for consistency at the beginning.
[[n_branches]] is the number of those actually set. [[n_externals]]
defines the number of significant bit, and [[mask]] is a code where all
bits are set. Analogous: [[n_in]] and [[mask_in]] for the incoming
particles.
The [[mapping]] array contains the mappings associated to the branches
(corresponding indices). The array [[mass_sum]] contains the sum of
the real masses of the external final-state particles associated to
the branch. During phase-space evaluation, this determines the
boundaries.
<<PHS trees: public>>=
public :: phs_tree_t
<<PHS trees: types>>=
type :: phs_tree_t
private
integer :: n_branches, n_externals, n_in, n_msq, n_angles
integer(TC) :: n_branches_tot, n_branches_out
integer(TC) :: mask, mask_in, mask_out
type(phs_branch_t), dimension(:), allocatable :: branch
type(mapping_t), dimension(:), allocatable :: mapping
real(default), dimension(:), allocatable :: mass_sum
real(default), dimension(:), allocatable :: effective_mass
real(default), dimension(:), allocatable :: effective_width
logical :: real_phsp = .false.
integer, dimension(:), allocatable :: momentum_link
contains
<<PHS trees: phs tree: TBP>>
end type phs_tree_t
@ %def phs_tree_t
@ The maximum number of external particles that can be represented is
related to the bit size of the integer that stores binary codes. With
the default integer of 32 bit on common machines, this is more than
enough space. If [[TC]] is actually the default integer kind, there
is no need to keep it separate, but doing so marks this as a
special type of integer. So, just state that the maximum number is 32:
<<Limits: public parameters>>=
integer, parameter, public :: MAX_EXTERNAL = 32
@ %def MAX_EXTERNAL
@
\subsubsection{Constructor and destructor}
Allocate memory for a phase-space tree with given number of externals and
incoming. The number of allocated branches can easily become large,
but appears manageable for realistic cases, e.g., for [[n_in=2]] and
[[n_out=8]] we get $2^{10}-1=1023$.
<<PHS trees: public>>=
public :: phs_tree_init
public :: phs_tree_final
@ Here we set the masks for incoming and for all externals.
<<PHS trees: phs tree: TBP>>=
procedure :: init => phs_tree_init
procedure :: final => phs_tree_final
<<PHS trees: procedures>>=
elemental subroutine phs_tree_init (tree, n_in, n_out, n_masses, n_angles)
class(phs_tree_t), intent(inout) :: tree
integer, intent(in) :: n_in, n_out, n_masses, n_angles
integer(TC) :: i
tree%n_externals = n_in + n_out
tree%n_branches_tot = 2**(n_in+n_out) - 1
tree%n_branches_out = 2**n_out - 1
tree%mask = 0
do i = 0, n_in + n_out - 1
tree%mask = ibset (tree%mask, i)
end do
tree%n_in = n_in
tree%mask_in = 0
do i = n_out, n_in + n_out - 1
tree%mask_in = ibset (tree%mask_in, i)
end do
tree%mask_out = ieor (tree%mask, tree%mask_in)
tree%n_msq = n_masses
tree%n_angles = n_angles
allocate (tree%branch (tree%n_branches_tot))
tree%n_branches = 0
allocate (tree%mapping (tree%n_branches_out))
allocate (tree%mass_sum (tree%n_branches_out))
allocate (tree%effective_mass (tree%n_branches_out))
allocate (tree%effective_width (tree%n_branches_out))
end subroutine phs_tree_init
elemental subroutine phs_tree_final (tree)
class(phs_tree_t), intent(inout) :: tree
deallocate (tree%branch)
deallocate (tree%mapping)
deallocate (tree%mass_sum)
deallocate (tree%effective_mass)
deallocate (tree%effective_width)
end subroutine phs_tree_final
@ %def phs_tree_init phs_tree_final
@
\subsubsection{Screen output}
Write only the branches that are set:
<<PHS trees: public>>=
public :: phs_tree_write
<<PHS trees: phs tree: TBP>>=
procedure :: write => phs_tree_write
<<PHS trees: procedures>>=
subroutine phs_tree_write (tree, unit)
class(phs_tree_t), intent(in) :: tree
integer, intent(in), optional :: unit
integer :: u
integer(TC) :: k
u = given_output_unit (unit); if (u < 0) return
write (u, '(3X,A,1x,I0,5X,A,I3)') &
'External:', tree%n_externals, 'Mask:', tree%mask
write (u, '(3X,A,1x,I0,5X,A,I3)') &
'Incoming:', tree%n_in, 'Mask:', tree%mask_in
write (u, '(3X,A,1x,I0,5X,A,I3)') &
'Branches:', tree%n_branches
do k = size (tree%branch), 1, -1
if (tree%branch(k)%set) &
call phs_branch_write (tree%branch(k), unit=unit, kval=k)
end do
do k = 1, size (tree%mapping)
call mapping_write (tree%mapping (k), unit, verbose=.true.)
end do
write (u, "(3x,A)") "Arrays: mass_sum, effective_mass, effective_width"
do k = 1, size (tree%mass_sum)
if (tree%branch(k)%set) then
write (u, "(5x,I0,3(2x," // FMT_19 // "))") k, tree%mass_sum(k), &
tree%effective_mass(k), tree%effective_width(k)
end if
end do
end subroutine phs_tree_write
subroutine phs_branch_write (b, unit, kval)
type(phs_branch_t), intent(in) :: b
integer, intent(in), optional :: unit
integer(TC), intent(in), optional :: kval
integer :: u
integer(TC) :: k
character(len=6) :: tmp
character(len=1) :: firstborn(2), sign_decay, sign_axis
integer :: i
u = given_output_unit (unit); if (u < 0) return
k = 0; if (present (kval)) k = kval
if (b%origin /= 0) then
write(tmp, '(A,I4,A)') '(', b%origin, ')'
else
tmp = ' '
end if
do i=1, 2
if (b%firstborn == i) then
firstborn(i) = "*"
else
firstborn(i) = " "
end if
end do
if (b%inverted_decay) then
sign_decay = "-"
else
sign_decay = "+"
end if
if (b%inverted_axis) then
sign_axis = "-"
else
sign_axis = "+"
end if
if (b%has_children) then
if (b%has_friend) then
write(u,'(4X,A1,I0,3x,A,1X,A,I0,A1,1x,I0,A1,1X,A1,1X,A,1x,I0)') &
& '*', k, tmp, &
& 'Daughters: ', &
& b%daughter(1), firstborn(1), &
& b%daughter(2), firstborn(2), sign_decay, &
& 'Friend: ', b%friend
else
write(u,'(4X,A1,I0,3x,A,1X,A,I0,A1,1x,I0,A1,1X,A1,1X,A)') &
& '*', k, tmp, &
& 'Daughters: ', &
& b%daughter(1), firstborn(1), &
& b%daughter(2), firstborn(2), sign_decay, &
& '(axis '//sign_axis//')'
end if
else
write(u,'(5X,I0)') k
end if
end subroutine phs_branch_write
@ %def phs_tree_write phs_branch_write
@
\subsection{PHS tree setup}
\subsubsection{Transformation into an array of branch codes and back}
Assume that the tree/array has been created before with the
appropriate length and is empty.
<<PHS trees: public>>=
public :: phs_tree_from_array
<<PHS trees: phs tree: TBP>>=
procedure :: from_array => phs_tree_from_array
<<PHS trees: procedures>>=
subroutine phs_tree_from_array (tree, a)
class(phs_tree_t), intent(inout) :: tree
integer(TC), dimension(:), intent(in) :: a
integer :: i
integer(TC) :: k
<<Set branches from array [[a]]>>
<<Set external branches if necessary>>
<<Check number of branches>>
<<Determine the connections>>
contains
<<Subroutine: set relatives>>
end subroutine phs_tree_from_array
@ %def phs_tree_from_array
@ First, set all branches specified by the user. If all IN-bits
are set, we invert the branch code.
<<Set branches from array [[a]]>>=
do i=1, size(a)
k = a(i)
if (iand(k, tree%mask_in) == tree%mask_in) k = ieor(tree%mask, k)
tree%branch(k)%set = .true.
tree%n_branches = tree%n_branches+1
end do
@ The external branches are understood, so set them now if not yet
done. In all cases ensure that the representative with one bit set is
used, except for decays where the in-particle is represented by all
OUT-bits set instead.
<<Set external branches if necessary>>=
do i=0, tree%n_externals-1
k = ibset(0,i)
if (iand(k, tree%mask_in) == tree%mask_in) k = ieor(tree%mask, k)
if (tree%branch(ieor(tree%mask, k))%set) then
tree%branch(ieor(tree%mask, k))%set = .false.
tree%branch(k)%set = .true.
else if (.not.tree%branch(k)%set) then
tree%branch(k)%set = .true.
tree%n_branches = tree%n_branches+1
end if
end do
@ Now the number of branches set can be checked. Here we assume that
the tree is binary. For three externals there are three branches in
total, and for each additional external branch we get another internal
one.
<<Check number of branches>>=
if (tree%n_branches /= tree%n_externals*2-3) then
call phs_tree_write (tree)
call msg_bug &
& (" Wrong number of branches set in phase space tree")
end if
@ For all branches that are set, except for the externals, we try to
find the daughter branches:
<<Determine the connections>>=
do k=1, size (tree%branch)
if (tree%branch(k)%set .and. tc_decay_level (k) /= 1) then
call branch_set_relatives(k)
end if
end do
@ To this end, we scan all codes less than the current code, whether
we can find two branches which are set and which together give the
current code. After that, the tree may still not be connected, but at
least we know if a branch does not have daughters: This indicates some
inconsistency.
The algorithm ensures that, at this stage, the first daughter has a
smaller code value than the second one.
<<Subroutine: set relatives>>=
subroutine branch_set_relatives (k)
integer(TC), intent(in) :: k
integer(TC) :: m,n
do m=1, k-1
if(iand(k,m)==m) then
n = ieor(k,m)
if ( tree%branch(m)%set .and. tree%branch(n)%set ) then
tree%branch(k)%daughter(1) = m; tree%branch(k)%daughter(2) = n
tree%branch(m)%mother = k; tree%branch(n)%mother = k
tree%branch(m)%sibling = n; tree%branch(n)%sibling = m
tree%branch(k)%has_children = .true.
return
end if
end if
end do
call phs_tree_write (tree)
call msg_bug &
& (" Missing daughter branch(es) in phase space tree")
end subroutine branch_set_relatives
@ The inverse: this is trivial, fortunately.
@
\subsubsection{Flip $t$-channel into $s$-channel}
Flipping the tree is done upwards, beginning from the decay products.
First we select a $t$-channel branch [[k]]: one which is set, which
does have an IN-bit, and which is not an external particle.
Next, we determine the adjacent in-particle (called the 'friend' [[f]]
here, since it will provide the reference axis for the angular
integration). In addition, we look for the 'mother' and 'sibling' of
this particle. If the latter field is empty, we select the (unique)
other out-particle which has no mother, calling the internal
subroutine [[find_orphan]].
The flip is done as follows: We assume that the first daughter [[d]]
is an $s$-channel line, which is true if the daughters are sorted.
This will stay the first daughter. The second one is a $t$-channel
line; it is exchanged with the 'sibling' [[s]]. The new line which
replaces the branch [[k]] is just the sum of [[s]] and [[d]]. In
addition, we have to rearrange the relatives of [[s]] and [[d]], as
well of [[f]].
Finally, we flip 'sibling' and 'friend' and set the new $s$-channel
branch [[n]] which replaces the $t$-channel branch [[k]]. After this
is complete, we are ready to execute another flip.
[Although the friend is not needed for the final flip, since it would
be an initial particle anyway, we need to know whether we have $t$- or
$u$-channel.]
<<PHS trees: public>>=
public :: phs_tree_flip_t_to_s_channel
<<PHS trees: procedures>>=
subroutine phs_tree_flip_t_to_s_channel (tree)
type(phs_tree_t), intent(inout) :: tree
integer(TC) :: k, f, m, n, d, s
if (tree%n_in == 2) then
FLIP: do k=3, tree%mask-1
if (.not. tree%branch(k)%set) cycle FLIP
f = iand(k,tree%mask_in)
if (f==0 .or. f==k) cycle FLIP
m = tree%branch(k)%mother
s = tree%branch(k)%sibling
if (s==0) call find_orphan(s)
d = tree%branch(k)%daughter(1)
n = ior(d,s)
tree%branch(k)%set = .false.
tree%branch(n)%set = .true.
tree%branch(n)%origin = k
tree%branch(n)%daughter(1) = d; tree%branch(d)%mother = n
tree%branch(n)%daughter(2) = s; tree%branch(s)%mother = n
tree%branch(n)%has_children = .true.
tree%branch(d)%sibling = s; tree%branch(s)%sibling = d
tree%branch(n)%sibling = f; tree%branch(f)%sibling = n
tree%branch(n)%mother = m
tree%branch(f)%mother = m
if (m/=0) then
tree%branch(m)%daughter(1) = n
tree%branch(m)%daughter(2) = f
end if
tree%branch(n)%friend = f
tree%branch(n)%has_friend = .true.
tree%branch(n)%firstborn = 2
end do FLIP
end if
contains
subroutine find_orphan(s)
integer(TC) :: s
do s=1, tree%mask_out
if (tree%branch(s)%set .and. tree%branch(s)%mother==0) return
end do
call phs_tree_write (tree)
call msg_bug (" Can't flip phase space tree to channel")
end subroutine find_orphan
end subroutine phs_tree_flip_t_to_s_channel
@ %def phs_tree_flip_t_to_s_channel
@ After the tree has been flipped, one may need to determine what has
become of a particular $t$-channel branch. This function gives the
bincode of the flipped tree. If the original bincode does not contain
IN-bits, we leave it as it is.
<<PHS trees: procedures>>=
function tc_flipped (tree, kt) result (ks)
type(phs_tree_t), intent(in) :: tree
integer(TC), intent(in) :: kt
integer(TC) :: ks
if (iand (kt, tree%mask_in) == 0) then
ks = kt
else
ks = tree%branch(iand (kt, tree%mask_out))%mother
end if
end function tc_flipped
@ %def tc_flipped
@ Scan a tree and make sure that the first daughter has always a
smaller code than the second one. Furthermore, delete any [[friend]]
entry in the root branch -- this branching has the incoming particle
direction as axis anyway. Keep track of reordering by updating
[[inverted_axis]], [[inverted_decay]] and [[firstborn]].
<<PHS trees: public>>=
public :: phs_tree_canonicalize
<<PHS trees: procedures>>=
subroutine phs_tree_canonicalize (tree)
type(phs_tree_t), intent(inout) :: tree
integer :: n_out
integer(TC) :: k_out
call branch_canonicalize (tree%branch(tree%mask_out))
n_out = tree%n_externals - tree%n_in
k_out = tree%mask_out
if (tree%branch(k_out)%has_friend &
& .and. tree%branch(k_out)%friend == ibset (0, n_out)) then
tree%branch(k_out)%inverted_axis = .not.tree%branch(k_out)%inverted_axis
end if
tree%branch(k_out)%has_friend = .false.
tree%branch(k_out)%friend = 0
contains
recursive subroutine branch_canonicalize (b)
type(phs_branch_t), intent(inout) :: b
integer(TC) :: d1, d2
if (b%has_children) then
d1 = b%daughter(1)
d2 = b%daughter(2)
if (d1 > d2) then
b%daughter(1) = d2
b%daughter(2) = d1
b%inverted_decay = .not.b%inverted_decay
if (b%firstborn /= 0) b%firstborn = 3 - b%firstborn
end if
call branch_canonicalize (tree%branch(b%daughter(1)))
call branch_canonicalize (tree%branch(b%daughter(2)))
end if
end subroutine branch_canonicalize
end subroutine phs_tree_canonicalize
@ %def phs_tree_canonicalize
@
\subsubsection{Mappings}
Initialize a mapping for the current tree. This is done while reading
from file, so the mapping parameters are read, but applied to the
flipped tree. Thus, the size of the array of mappings is given by the
number of outgoing particles only.
<<PHS trees: public>>=
public :: phs_tree_init_mapping
<<PHS trees: phs tree: TBP>>=
procedure :: init_mapping => phs_tree_init_mapping
<<PHS trees: procedures>>=
subroutine phs_tree_init_mapping (tree, k, type, pdg, model)
class(phs_tree_t), intent(inout) :: tree
integer(TC), intent(in) :: k
type(string_t), intent(in) :: type
integer, intent(in) :: pdg
class(model_data_t), intent(in), target :: model
integer(TC) :: kk
kk = tc_flipped (tree, k)
call mapping_init (tree%mapping(kk), kk, type, pdg, model)
end subroutine phs_tree_init_mapping
@ %def phs_tree_init_mapping
@ Set the physical parameters for the mapping, using a specific
parameter set. Also set the mass sum array.
<<PHS trees: public>>=
public :: phs_tree_set_mapping_parameters
<<PHS trees: phs tree: TBP>>=
procedure :: set_mapping_parameters => phs_tree_set_mapping_parameters
<<PHS trees: procedures>>=
subroutine phs_tree_set_mapping_parameters &
(tree, mapping_defaults, variable_limits)
class(phs_tree_t), intent(inout) :: tree
type(mapping_defaults_t), intent(in) :: mapping_defaults
logical, intent(in) :: variable_limits
integer(TC) :: k
do k = 1, tree%n_branches_out
call mapping_set_parameters &
(tree%mapping(k), mapping_defaults, variable_limits)
end do
end subroutine phs_tree_set_mapping_parameters
@ %def phs_tree_set_mapping_parameters
@ Return the mapping for the sum of all outgoing particles. This
should either be no mapping or a global s-channel mapping.
<<PHS trees: public>>=
public :: phs_tree_assign_s_mapping
<<PHS trees: procedures>>=
subroutine phs_tree_assign_s_mapping (tree, mapping)
type(phs_tree_t), intent(in) :: tree
type(mapping_t), intent(out) :: mapping
mapping = tree%mapping(tree%mask_out)
end subroutine phs_tree_assign_s_mapping
@ %def phs_tree_assign_s_mapping
@
\subsubsection{Kinematics}
Fill the mass sum array, starting from the external particles and
working down to the tree root. For each bincode [[k]] we scan the
bits in [[k]]; if only one is set, we take the physical mass of the
corresponding external particle; if more then one is set, we sum up
the two masses (which we know have already been set).
<<PHS trees: public>>=
public :: phs_tree_set_mass_sum
<<PHS trees: phs tree: TBP>>=
procedure :: set_mass_sum => phs_tree_set_mass_sum
<<PHS trees: procedures>>=
subroutine phs_tree_set_mass_sum (tree, flv)
class(phs_tree_t), intent(inout) :: tree
type(flavor_t), dimension(:), intent(in) :: flv
integer(TC) :: k
integer :: i
tree%mass_sum = 0
do k = 1, tree%n_branches_out
do i = 0, size (flv) - 1
if (btest(k,i)) then
if (ibclr(k,i) == 0) then
tree%mass_sum(k) = flv(i+1)%get_mass ()
else
tree%mass_sum(k) = &
tree%mass_sum(ibclr(k,i)) + tree%mass_sum(ibset(0,i))
end if
exit
end if
end do
end do
end subroutine phs_tree_set_mass_sum
@ %def phs_tree_set_mass_sum
@ Set the effective masses and widths. For each non-resonant branch
in a tree, the effective mass is equal to the sum of the effective
masses of the children (and analogous for the width). External
particles have their real mass and width zero. For resonant branches,
we insert mass and width from the corresponding mapping.
This routine has [[phs_tree_set_mass_sum]] and
[[phs_tree_set_mapping_parameters]] as prerequisites.
<<PHS trees: public>>=
public :: phs_tree_set_effective_masses
<<PHS trees: phs tree: TBP>>=
procedure :: set_effective_masses => phs_tree_set_effective_masses
<<PHS trees: procedures>>=
subroutine phs_tree_set_effective_masses (tree)
class(phs_tree_t), intent(inout) :: tree
tree%effective_mass = 0
tree%effective_width = 0
call set_masses_x (tree%mask_out)
contains
recursive subroutine set_masses_x (k)
integer(TC), intent(in) :: k
integer(TC) :: k1, k2
if (tree%branch(k)%has_children) then
k1 = tree%branch(k)%daughter(1)
k2 = tree%branch(k)%daughter(2)
call set_masses_x (k1)
call set_masses_x (k2)
if (mapping_is_s_channel (tree%mapping(k))) then
tree%effective_mass(k) = mapping_get_mass (tree%mapping(k))
tree%effective_width(k) = mapping_get_width (tree%mapping(k))
else
tree%effective_mass(k) = &
tree%effective_mass(k1) + tree%effective_mass(k2)
tree%effective_width(k) = &
tree%effective_width(k1) + tree%effective_width(k2)
end if
else
tree%effective_mass(k) = tree%mass_sum(k)
end if
end subroutine set_masses_x
end subroutine phs_tree_set_effective_masses
@ %def phs_tree_set_effective_masses
@ Define step mappings, recursively, for the decay products of all
intermediate resonances. Step mappings account for the fact that a
branch may originate from a resonance, which almost replaces the
upper limit on the possible invariant mass. The step
mapping implements a smooth cutoff that interpolates between the
resonance and the real kinematic limit. The mapping width determines
the sharpness of the cutoff.
Step mappings are inserted only for branches that are not mapped
otherwise.
At each branch, we record the mass that is effectively available for
phase space, by taking the previous limit and subtracting the
effective mass of the sibling branch. Widths are added, not subtracted.
If we encounter a resonance decay, we discard the previous limit and
replace it by the mass and width of the resonance, also subtracting
the sibling branch.
Initially, the limit is zero, so it becomes negative at any branch. Only
if there is a resonance, the limit becomes positive. Whenever the
limit is positive, and the current branch decays, we activate a step
mapping for the current branch.
As a result, step mappings are implemented for all internal lines that
originate from an intermediate resonance decay.
The flag [[variable_limits]] applies to the ultimate limit from the
available energy, not to the intermediate resonances whose masses are
always fixed.
This routine requires [[phs_tree_set_effective_masses]]
<<PHS trees: public>>=
public :: phs_tree_set_step_mappings
<<PHS trees: procedures>>=
subroutine phs_tree_set_step_mappings (tree, exp_type, variable_limits)
type(phs_tree_t), intent(inout) :: tree
logical, intent(in) :: exp_type
logical, intent(in) :: variable_limits
type(string_t) :: map_str
integer(TC) :: k
if (exp_type) then
map_str = "step_exp"
else
map_str = "step_hyp"
end if
k = tree%mask_out
call set_step_mappings_x (k, 0._default, 0._default)
contains
recursive subroutine set_step_mappings_x (k, m_limit, w_limit)
integer(TC), intent(in) :: k
real(default), intent(in) :: m_limit, w_limit
integer(TC), dimension(2) :: kk
real(default), dimension(2) :: m, w
if (tree%branch(k)%has_children) then
if (m_limit > 0) then
if (.not. mapping_is_set (tree%mapping(k))) then
call mapping_init (tree%mapping(k), k, map_str)
call mapping_set_step_mapping_parameters (tree%mapping(k), &
m_limit, w_limit, &
variable_limits)
end if
end if
kk = tree%branch(k)%daughter
m = tree%effective_mass(kk)
w = tree%effective_width(kk)
if (mapping_is_s_channel (tree%mapping(k))) then
call set_step_mappings_x (kk(1), &
mapping_get_mass (tree%mapping(k)) - m(2), &
mapping_get_width (tree%mapping(k)) + w(2))
call set_step_mappings_x (kk(2), &
mapping_get_mass (tree%mapping(k)) - m(1), &
mapping_get_width (tree%mapping(k)) + w(1))
else if (m_limit > 0) then
call set_step_mappings_x (kk(1), &
m_limit - m(2), &
w_limit + w(2))
call set_step_mappings_x (kk(2), &
m_limit - m(1), &
w_limit + w(1))
else
call set_step_mappings_x (kk(1), &
- m(2), &
+ w(2))
call set_step_mappings_x (kk(2), &
- m(1), &
+ w(1))
end if
end if
end subroutine set_step_mappings_x
end subroutine phs_tree_set_step_mappings
@ %def phs_tree_set_step_mappings
@
\subsubsection{Resonance structure}
We identify the resonances within a tree as the set of s-channel
mappings. The [[resonance_history_t]] type serves as the result
container.
<<PHS trees: phs tree: TBP>>=
procedure :: extract_resonance_history => phs_tree_extract_resonance_history
<<PHS trees: procedures>>=
subroutine phs_tree_extract_resonance_history (tree, res_history)
class(phs_tree_t), intent(in) :: tree
type(resonance_history_t), intent(out) :: res_history
type(resonance_info_t) :: res_info
integer :: i
if (allocated (tree%mapping)) then
do i = 1, size (tree%mapping)
associate (mapping => tree%mapping(i))
if (mapping%is_s_channel ()) then
call res_info%init (mapping%get_bincode (), mapping%get_flv (), &
n_out = tree%n_externals - tree%n_in)
call res_history%add_resonance (res_info)
end if
end associate
end do
end if
end subroutine phs_tree_extract_resonance_history
@ %def phs_tree_extract_resonance_history
@
\subsubsection{Structural comparison}
This function allows to check whether one tree is the permutation of
another one. The permutation is applied to the second tree in the
argument list. We do not make up a temporary permuted tree, but
compare the two trees directly. The branches are scanned recursively,
where for each daughter we check the friend and the mapping as well.
Once a discrepancy is found, the recursion is exited immediately.
<<PHS trees: public>>=
public :: phs_tree_equivalent
<<PHS trees: procedures>>=
function phs_tree_equivalent (t1, t2, perm) result (is_equal)
type(phs_tree_t), intent(in) :: t1, t2
type(permutation_t), intent(in) :: perm
logical :: equal, is_equal
integer(TC) :: k1, k2, mask_in
k1 = t1%mask_out
k2 = t2%mask_out
mask_in = t1%mask_in
equal = .true.
call check (t1%branch(k1), t2%branch(k2), k1, k2)
is_equal = equal
contains
recursive subroutine check (b1, b2, k1, k2)
type(phs_branch_t), intent(in) :: b1, b2
integer(TC), intent(in) :: k1, k2
integer(TC), dimension(2) :: d1, d2, pd2
integer :: i
if (.not.b1%has_friend .and. .not.b2%has_friend) then
equal = .true.
else if (b1%has_friend .and. b2%has_friend) then
equal = (b1%friend == tc_permute (b2%friend, perm, mask_in))
end if
if (equal) then
if (b1%has_children .and. b2%has_children) then
d1 = b1%daughter
d2 = b2%daughter
do i=1, 2
pd2(i) = tc_permute (d2(i), perm, mask_in)
end do
if (d1(1)==pd2(1) .and. d1(2)==pd2(2)) then
equal = (b1%firstborn == b2%firstborn)
if (equal) call check &
& (t1%branch(d1(1)), t2%branch(d2(1)), d1(1), d2(1))
if (equal) call check &
& (t1%branch(d1(2)), t2%branch(d2(2)), d1(2), d2(2))
else if (d1(1)==pd2(2) .and. d1(2)==pd2(1)) then
equal = ( (b1%firstborn == 0 .and. b2%firstborn == 0) &
& .or. (b1%firstborn == 3 - b2%firstborn) )
if (equal) call check &
& (t1%branch(d1(1)), t2%branch(d2(2)), d1(1), d2(2))
if (equal) call check &
& (t1%branch(d1(2)), t2%branch(d2(1)), d1(2), d2(1))
else
equal = .false.
end if
end if
end if
if (equal) then
equal = (t1%mapping(k1) == t2%mapping(k2))
end if
end subroutine check
end function phs_tree_equivalent
@ %def phs_tree_equivalent
@ Scan two decay trees and determine the correspondence of mass
variables, i.e., the permutation that transfers the ordered list of
mass variables belonging to the second tree into the first one. Mass
variables are assigned beginning from branches and ending at the root.
<<PHS trees: public>>=
public :: phs_tree_find_msq_permutation
<<PHS trees: procedures>>=
subroutine phs_tree_find_msq_permutation (tree1, tree2, perm2, msq_perm)
type(phs_tree_t), intent(in) :: tree1, tree2
type(permutation_t), intent(in) :: perm2
type(permutation_t), intent(out) :: msq_perm
type(permutation_t) :: perm1
integer(TC) :: mask_in, root
integer(TC), dimension(:), allocatable :: index1, index2
integer :: i
allocate (index1 (tree1%n_msq), index2 (tree2%n_msq))
call permutation_init (perm1, permutation_size (perm2))
mask_in = tree1%mask_in
root = tree1%mask_out
i = 0
call tree_scan (tree1, root, perm1, index1)
i = 0
call tree_scan (tree2, root, perm2, index2)
call permutation_find (msq_perm, index1, index2)
contains
recursive subroutine tree_scan (tree, k, perm, index)
type(phs_tree_t), intent(in) :: tree
integer(TC), intent(in) :: k
type(permutation_t), intent(in) :: perm
integer, dimension(:), intent(inout) :: index
if (tree%branch(k)%has_children) then
call tree_scan (tree, tree%branch(k)%daughter(1), perm, index)
call tree_scan (tree, tree%branch(k)%daughter(2), perm, index)
i = i + 1
if (i <= size (index)) index(i) = tc_permute (k, perm, mask_in)
end if
end subroutine tree_scan
end subroutine phs_tree_find_msq_permutation
@ %def phs_tree_find_msq_permutation
<<PHS trees: public>>=
public :: phs_tree_find_angle_permutation
<<PHS trees: procedures>>=
subroutine phs_tree_find_angle_permutation &
(tree1, tree2, perm2, angle_perm, sig2)
type(phs_tree_t), intent(in) :: tree1, tree2
type(permutation_t), intent(in) :: perm2
type(permutation_t), intent(out) :: angle_perm
logical, dimension(:), allocatable, intent(out) :: sig2
type(permutation_t) :: perm1
integer(TC) :: mask_in, root
integer(TC), dimension(:), allocatable :: index1, index2
logical, dimension(:), allocatable :: sig1
integer :: i
allocate (index1 (tree1%n_angles), index2 (tree2%n_angles))
allocate (sig1 (tree1%n_angles), sig2 (tree2%n_angles))
call permutation_init (perm1, permutation_size (perm2))
mask_in = tree1%mask_in
root = tree1%mask_out
i = 0
call tree_scan (tree1, root, perm1, index1, sig1)
i = 0
call tree_scan (tree2, root, perm2, index2, sig2)
call permutation_find (angle_perm, index1, index2)
contains
recursive subroutine tree_scan (tree, k, perm, index, sig)
type(phs_tree_t), intent(in) :: tree
integer(TC), intent(in) :: k
type(permutation_t), intent(in) :: perm
integer, dimension(:), intent(inout) :: index
logical, dimension(:), intent(inout) :: sig
integer(TC) :: k1, k2, kp
logical :: s
if (tree%branch(k)%has_children) then
k1 = tree%branch(k)%daughter(1)
k2 = tree%branch(k)%daughter(2)
s = (tc_permute(k1, perm, mask_in) < tc_permute(k2, perm, mask_in))
kp = tc_permute (k, perm, mask_in)
i = i + 1
index(i) = kp
sig(i) = s
i = i + 1
index(i) = - kp
sig(i) = s
call tree_scan (tree, k1, perm, index, sig)
call tree_scan (tree, k2, perm, index, sig)
end if
end subroutine tree_scan
end subroutine phs_tree_find_angle_permutation
@ %def phs_tree_find_angle_permutation
@
\subsection{Phase-space evaluation}
\subsubsection{Phase-space volume}
We compute the phase-space volume recursively, following the same path
as for computing other kinematical variables. However, the volume
depends just on $\sqrt{\hat s}$, not on the momentum configuration.
Note: counting branches, we may replace this by a simple formula.
<<PHS trees: public>>=
public :: phs_tree_compute_volume
<<PHS trees: procedures>>=
subroutine phs_tree_compute_volume (tree, sqrts, volume)
type(phs_tree_t), intent(in) :: tree
real(default), intent(in) :: sqrts
real(default), intent(out) :: volume
integer(TC) :: k
k = tree%mask_out
if (tree%branch(k)%has_children) then
call compute_volume_x (tree%branch(k), k, volume, .true.)
else
volume = 1
end if
contains
recursive subroutine compute_volume_x (b, k, volume, initial)
type(phs_branch_t), intent(in) :: b
integer(TC), intent(in) :: k
real(default), intent(out) :: volume
logical, intent(in) :: initial
integer(TC) :: k1, k2
real(default) :: v1, v2
k1 = b%daughter(1); k2 = b%daughter(2)
if (tree%branch(k1)%has_children) then
call compute_volume_x (tree%branch(k1), k1, v1, .false.)
else
v1 = 1
end if
if (tree%branch(k2)%has_children) then
call compute_volume_x (tree%branch(k2), k2, v2, .false.)
else
v2 = 1
end if
if (initial) then
volume = v1 * v2 / (4 * twopi5)
else
volume = v1 * v2 * sqrts**2 / (4 * twopi2)
end if
end subroutine compute_volume_x
end subroutine phs_tree_compute_volume
@ %def phs_tree_compute_volume
@
\subsubsection{Determine momenta}
This is done in two steps: First the masses are determined. This step
may fail, in which case [[ok]] is set to false. If successful, we
generate angles and the actual momenta. The array [[decay_p]] serves
for transferring the individual three-momenta of the daughter
particles in their mother rest frame from the mass generation to the
momentum generation step.
<<PHS trees: public>>=
public :: phs_tree_compute_momenta_from_x
<<PHS trees: procedures>>=
subroutine phs_tree_compute_momenta_from_x &
(tree, prt, factor, volume, sqrts, x, ok)
type(phs_tree_t), intent(inout) :: tree
type(phs_prt_t), dimension(:), intent(inout) :: prt
real(default), intent(out) :: factor, volume
real(default), intent(in) :: sqrts
real(default), dimension(:), intent(in) :: x
logical, intent(out) :: ok
real(default), dimension(tree%mask_out) :: decay_p
integer :: n1, n2
integer :: n_out
if (tree%real_phsp) then
n_out = tree%n_externals - tree%n_in - 1
n1 = max (n_out-2, 0)
n2 = n1 + max (2*n_out, 0)
else
n1 = tree%n_msq
n2 = n1 + tree%n_angles
end if
call phs_tree_set_msq &
(tree, prt, factor, volume, decay_p, sqrts, x(1:n1), ok)
if (ok) call phs_tree_set_angles &
(tree, prt, factor, decay_p, sqrts, x(n1+1:n2))
end subroutine phs_tree_compute_momenta_from_x
@ %def phs_tree_compute_momenta_from_x
@ Mass generation is done recursively. The [[ok]] flag causes the
filled tree to be discarded if set to [[.false.]]. This happens if a
three-momentum turns out to be imaginary, indicating impossible
kinematics. The index [[ix]] tells us how far we have used up the
input array [[x]].
<<PHS trees: procedures>>=
subroutine phs_tree_set_msq &
(tree, prt, factor, volume, decay_p, sqrts, x, ok)
type(phs_tree_t), intent(inout) :: tree
type(phs_prt_t), dimension(:), intent(inout) :: prt
real(default), intent(out) :: factor, volume
real(default), dimension(:), intent(out) :: decay_p
real(default), intent(in) :: sqrts
real(default), dimension(:), intent(in) :: x
logical, intent(out) :: ok
integer :: ix
integer(TC) :: k
real(default) :: m_tot
ok =.true.
ix = 1
k = tree%mask_out
m_tot = tree%mass_sum(k)
decay_p(k) = 0.
if (m_tot < sqrts .or. k == 1) then
if (tree%branch(k)%has_children) then
call set_msq_x (tree%branch(k), k, factor, volume, .true.)
else
factor = 1
volume = 1
end if
else
ok = .false.
end if
contains
recursive subroutine set_msq_x (b, k, factor, volume, initial)
type(phs_branch_t), intent(in) :: b
integer(TC), intent(in) :: k
real(default), intent(out) :: factor, volume
logical, intent(in) :: initial
real(default) :: msq, m, m_min, m_max, m1, m2, msq1, msq2, lda, rlda
integer(TC) :: k1, k2
real(default) :: f1, f2, v1, v2
k1 = b%daughter(1); k2 = b%daughter(2)
if (tree%branch(k1)%has_children) then
call set_msq_x (tree%branch(k1), k1, f1, v1, .false.)
if (.not.ok) return
else
f1 = 1; v1 = 1
end if
if (tree%branch(k2)%has_children) then
call set_msq_x (tree%branch(k2), k2, f2, v2, .false.)
if (.not.ok) return
else
f2 = 1; v2 = 1
end if
m_min = tree%mass_sum(k)
if (initial) then
msq = sqrts**2
m = sqrts
m_max = sqrts
factor = f1 * f2
volume = v1 * v2 / (4 * twopi5)
else
m_max = sqrts - m_tot + m_min
call mapping_compute_msq_from_x &
(tree%mapping(k), sqrts**2, m_min**2, m_max**2, msq, factor, &
x(ix)); ix = ix + 1
if (msq >= 0) then
m = sqrt (msq)
factor = f1 * f2 * factor
volume = v1 * v2 * sqrts**2 / (4 * twopi2)
call phs_prt_set_msq (prt(k), msq)
call phs_prt_set_defined (prt(k))
else
ok = .false.
end if
end if
if (ok) then
msq1 = phs_prt_get_msq (prt(k1)); m1 = sqrt (msq1)
msq2 = phs_prt_get_msq (prt(k2)); m2 = sqrt (msq2)
lda = lambda (msq, msq1, msq2)
if (lda > 0 .and. m > m1 + m2 .and. m <= m_max) then
rlda = sqrt (lda)
decay_p(k1) = rlda / (2*m)
decay_p(k2) = - decay_p(k1)
factor = rlda / msq * factor
else
ok = .false.
end if
end if
end subroutine set_msq_x
end subroutine phs_tree_set_msq
@ %def phs_tree_set_msq
@
The heart of phase space generation: Now we have the invariant masses,
let us generate angles. At each branch, we take a Lorentz
transformation and augment it by a boost to the current particle
rest frame, and by rotations $\phi$ and $\theta$ around the $z$ and
$y$ axis, respectively. This transformation is passed down to the
daughter particles, if present.
<<PHS trees: procedures>>=
subroutine phs_tree_set_angles (tree, prt, factor, decay_p, sqrts, x)
type(phs_tree_t), intent(inout) :: tree
type(phs_prt_t), dimension(:), intent(inout) :: prt
real(default), intent(inout) :: factor
real(default), dimension(:), intent(in) :: decay_p
real(default), intent(in) :: sqrts
real(default), dimension(:), intent(in) :: x
integer :: ix
integer(TC) :: k
ix = 1
k = tree%mask_out
call set_angles_x (tree%branch(k), k)
contains
recursive subroutine set_angles_x (b, k, L0)
type(phs_branch_t), intent(in) :: b
integer(TC), intent(in) :: k
type(lorentz_transformation_t), intent(in), optional :: L0
real(default) :: m, msq, ct, st, phi, f, E, p, bg
type(lorentz_transformation_t) :: L, LL
integer(TC) :: k1, k2
type(vector3_t) :: axis
p = decay_p(k)
msq = phs_prt_get_msq (prt(k)); m = sqrt (msq)
E = sqrt (msq + p**2)
if (present (L0)) then
call phs_prt_set_momentum (prt(k), L0 * vector4_moving (E,p,3))
else
call phs_prt_set_momentum (prt(k), vector4_moving (E,p,3))
end if
call phs_prt_set_defined (prt(k))
if (b%has_children) then
k1 = b%daughter(1)
k2 = b%daughter(2)
if (m > 0) then
bg = p / m
else
bg = 0
end if
phi = x(ix) * twopi; ix = ix + 1
call mapping_compute_ct_from_x &
(tree%mapping(k), sqrts**2, ct, st, f, x(ix)); ix = ix + 1
factor = factor * f
if (.not. b%has_friend) then
L = LT_compose_r2_r3_b3 (ct, st, cos(phi), sin(phi), bg)
!!! The function above is equivalent to:
! L = boost (bg,3) * rotation (phi,3) * rotation (ct,st,2)
else
LL = boost (-bg,3); if (present (L0)) LL = LL * inverse(L0)
axis = space_part ( &
LL * phs_prt_get_momentum (prt(tree%branch(k)%friend)) )
L = boost(bg,3) * rotation_to_2nd (vector3_canonical(3), axis) &
* LT_compose_r2_r3_b3 (ct, st, cos(phi), sin(phi), 0._default)
end if
if (present (L0)) L = L0 * L
call set_angles_x (tree%branch(k1), k1, L)
call set_angles_x (tree%branch(k2), k2, L)
end if
end subroutine set_angles_x
end subroutine phs_tree_set_angles
@ %def phs_tree_set_angles
@
\subsubsection{Recover random numbers}
For the other channels we want to compute the random numbers that
would have generated the momenta that we already know.
<<PHS trees: public>>=
public :: phs_tree_compute_x_from_momenta
<<PHS trees: procedures>>=
subroutine phs_tree_compute_x_from_momenta (tree, prt, factor, sqrts, x)
type(phs_tree_t), intent(inout) :: tree
type(phs_prt_t), dimension(:), intent(in) :: prt
real(default), intent(out) :: factor
real(default), intent(in) :: sqrts
real(default), dimension(:), intent(inout) :: x
real(default), dimension(tree%mask_out) :: decay_p
integer :: n1, n2
n1 = tree%n_msq
n2 = n1 + tree%n_angles
call phs_tree_get_msq &
(tree, prt, factor, decay_p, sqrts, x(1:n1))
call phs_tree_get_angles &
(tree, prt, factor, decay_p, sqrts, x(n1+1:n2))
end subroutine phs_tree_compute_x_from_momenta
@ %def phs_tree_compute_x_from_momenta
@ The inverse operation follows exactly the same steps. The tree is
[[inout]] because it contains mappings whose parameters can be reset
when the mapping is applied.
<<PHS trees: procedures>>=
subroutine phs_tree_get_msq (tree, prt, factor, decay_p, sqrts, x)
type(phs_tree_t), intent(inout) :: tree
type(phs_prt_t), dimension(:), intent(in) :: prt
real(default), intent(out) :: factor
real(default), dimension(:), intent(out) :: decay_p
real(default), intent(in) :: sqrts
real(default), dimension(:), intent(inout) :: x
integer :: ix
integer(TC) :: k
real(default) :: m_tot
ix = 1
k = tree%mask_out
m_tot = tree%mass_sum(k)
decay_p(k) = 0.
if (tree%branch(k)%has_children) then
call get_msq_x (tree%branch(k), k, factor, .true.)
else
factor = 1
end if
contains
recursive subroutine get_msq_x (b, k, factor, initial)
type(phs_branch_t), intent(in) :: b
integer(TC), intent(in) :: k
real(default), intent(out) :: factor
logical, intent(in) :: initial
real(default) :: msq, m, m_min, m_max, msq1, msq2, lda, rlda
integer(TC) :: k1, k2
real(default) :: f1, f2
k1 = b%daughter(1); k2 = b%daughter(2)
if (tree%branch(k1)%has_children) then
call get_msq_x (tree%branch(k1), k1, f1, .false.)
else
f1 = 1
end if
if (tree%branch(k2)%has_children) then
call get_msq_x (tree%branch(k2), k2, f2, .false.)
else
f2 = 1
end if
m_min = tree%mass_sum(k)
m_max = sqrts - m_tot + m_min
msq = phs_prt_get_msq (prt(k)); m = sqrt (msq)
if (initial) then
factor = f1 * f2
else
call mapping_compute_x_from_msq &
(tree%mapping(k), sqrts**2, m_min**2, m_max**2, msq, factor, &
x(ix)); ix = ix + 1
factor = f1 * f2 * factor
end if
msq1 = phs_prt_get_msq (prt(k1))
msq2 = phs_prt_get_msq (prt(k2))
lda = lambda (msq, msq1, msq2)
if (lda > 0) then
rlda = sqrt (lda)
decay_p(k1) = rlda / (2 * m)
decay_p(k2) = - decay_p(k1)
factor = rlda / msq * factor
else
decay_p(k1) = 0
decay_p(k2) = 0
factor = 0
end if
end subroutine get_msq_x
end subroutine phs_tree_get_msq
@ %def phs_tree_get_msq
@ This subroutine is the most time-critical part of the whole
program. Therefore, we do not exactly parallel the angle generation
routine above but make sure that things get evaluated only if they are
really needed, at the expense of readability. Particularly important
is to have as few multiplications of Lorentz transformations as
possible.
<<PHS trees: procedures>>=
subroutine phs_tree_get_angles (tree, prt, factor, decay_p, sqrts, x)
type(phs_tree_t), intent(inout) :: tree
type(phs_prt_t), dimension(:), intent(in) :: prt
real(default), intent(inout) :: factor
real(default), dimension(:), intent(in) :: decay_p
real(default), intent(in) :: sqrts
real(default), dimension(:), intent(out) :: x
integer :: ix
integer(TC) :: k
ix = 1
k = tree%mask_out
if (tree%branch(k)%has_children) then
call get_angles_x (tree%branch(k), k)
end if
contains
recursive subroutine get_angles_x (b, k, ct0, st0, phi0, L0)
type(phs_branch_t), intent(in) :: b
integer(TC), intent(in) :: k
real(default), intent(in), optional :: ct0, st0, phi0
type(lorentz_transformation_t), intent(in), optional :: L0
real(default) :: cp0, sp0, m, msq, ct, st, phi, bg, f
type(lorentz_transformation_t) :: L, LL
type(vector4_t) :: p1, pf
type(vector3_t) :: n, axis
integer(TC) :: k1, k2, kf
logical :: has_friend, need_L
k1 = b%daughter(1)
k2 = b%daughter(2)
kf = b%friend
has_friend = b%has_friend
if (present(L0)) then
p1 = L0 * phs_prt_get_momentum (prt(k1))
if (has_friend) pf = L0 * phs_prt_get_momentum (prt(kf))
else
p1 = phs_prt_get_momentum (prt(k1))
if (has_friend) pf = phs_prt_get_momentum (prt(kf))
end if
if (present(phi0)) then
cp0 = cos (phi0)
sp0 = sin (phi0)
end if
msq = phs_prt_get_msq (prt(k)); m = sqrt (msq)
if (m > 0) then
bg = decay_p(k) / m
else
bg = 0
end if
if (has_friend) then
if (present (phi0)) then
axis = axis_from_p_r3_r2_b3 (pf, cp0, -sp0, ct0, -st0, -bg)
LL = rotation_to_2nd (axis, vector3_canonical (3)) &
* LT_compose_r3_r2_b3 (cp0, -sp0, ct0, -st0, -bg)
else
axis = axis_from_p_b3 (pf, -bg)
LL = rotation_to_2nd (axis, vector3_canonical(3))
if (.not. vanishes (bg)) LL = LL * boost(-bg, 3)
end if
n = space_part (LL * p1)
else if (present (phi0)) then
n = axis_from_p_r3_r2_b3 (p1, cp0, -sp0, ct0, -st0, -bg)
else
n = axis_from_p_b3 (p1, -bg)
end if
phi = azimuthal_angle (n)
x(ix) = phi / twopi; ix = ix + 1
ct = polar_angle_ct (n)
st = sqrt (1 - ct**2)
call mapping_compute_x_from_ct (tree%mapping(k), sqrts**2, ct, f, &
x(ix)); ix = ix + 1
factor = factor * f
if (tree%branch(k1)%has_children .or. tree%branch(k2)%has_children) then
need_L = .true.
if (has_friend) then
if (present (L0)) then
L = LL * L0
else
L = LL
end if
else if (present (L0)) then
L = LT_compose_r3_r2_b3 (cp0, -sp0, ct0, -st0, -bg) * L0
else if (present (phi0)) then
L = LT_compose_r3_r2_b3 (cp0, -sp0, ct0, -st0, -bg)
else if (bg /= 0) then
L = boost(-bg, 3)
else
need_L = .false.
end if
if (need_L) then
if (tree%branch(k1)%has_children) &
call get_angles_x (tree%branch(k1), k1, ct, st, phi, L)
if (tree%branch(k2)%has_children) &
call get_angles_x (tree%branch(k2), k2, ct, st, phi, L)
else
if (tree%branch(k1)%has_children) &
call get_angles_x (tree%branch(k1), k1, ct, st, phi)
if (tree%branch(k2)%has_children) &
call get_angles_x (tree%branch(k2), k2, ct, st, phi)
end if
end if
end subroutine get_angles_x
end subroutine phs_tree_get_angles
@ %def phs_tree_get_angles
@
\subsubsection{Auxiliary stuff}
This calculates all momenta that are not yet known by summing up
daughter particle momenta. The external particles must be known.
Only composite particles not yet known are calculated.
<<PHS trees: public>>=
public :: phs_tree_combine_particles
<<PHS trees: procedures>>=
subroutine phs_tree_combine_particles (tree, prt)
type(phs_tree_t), intent(in) :: tree
type(phs_prt_t), dimension(:), intent(inout) :: prt
call combine_particles_x (tree%mask_out)
contains
recursive subroutine combine_particles_x (k)
integer(TC), intent(in) :: k
integer :: k1, k2
if (tree%branch(k)%has_children) then
k1 = tree%branch(k)%daughter(1); k2 = tree%branch(k)%daughter(2)
call combine_particles_x (k1)
call combine_particles_x (k2)
if (.not. prt(k)%defined) then
call phs_prt_combine (prt(k), prt(k1), prt(k2))
end if
end if
end subroutine combine_particles_x
end subroutine phs_tree_combine_particles
@ %def phs_tree_combine_particles
@ The previous routine is to be evaluated at runtime. Instead of
scanning trees, we can as well set up a multiplication table. This is
generated here. Note that the table is [[intent(out)]].
<<PHS trees: public>>=
public :: phs_tree_setup_prt_combinations
<<PHS trees: procedures>>=
subroutine phs_tree_setup_prt_combinations (tree, comb)
type(phs_tree_t), intent(in) :: tree
integer, dimension(:,:), intent(out) :: comb
comb = 0
call setup_prt_combinations_x (tree%mask_out)
contains
recursive subroutine setup_prt_combinations_x (k)
integer(TC), intent(in) :: k
integer, dimension(2) :: kk
if (tree%branch(k)%has_children) then
kk = tree%branch(k)%daughter
call setup_prt_combinations_x (kk(1))
call setup_prt_combinations_x (kk(2))
comb(:,k) = kk
end if
end subroutine setup_prt_combinations_x
end subroutine phs_tree_setup_prt_combinations
@ %def phs_tree_setup_prt_combinations
@
<<PHS trees: public>>=
public :: phs_tree_reshuffle_mappings
<<PHS trees: procedures>>=
subroutine phs_tree_reshuffle_mappings (tree)
type(phs_tree_t), intent(inout) :: tree
integer(TC) :: k0, k_old, k_new, k2
integer :: i
type(mapping_t) :: mapping_tmp
real(default) :: mass_tmp
do i = 1, size (tree%momentum_link)
if (i /= tree%momentum_link (i)) then
k_old = 2**(i-tree%n_in-1)
k_new = 2**(tree%momentum_link(i)-tree%n_in-1)
k0 = tree%branch(k_old)%mother
k2 = k_new + tree%branch(k_old)%sibling
mapping_tmp = tree%mapping(k0)
mass_tmp = tree%mass_sum(k0)
tree%mapping(k0) = tree%mapping(k2)
tree%mapping(k2) = mapping_tmp
tree%mass_sum(k0) = tree%mass_sum(k2)
tree%mass_sum(k2) = mass_tmp
end if
end do
end subroutine phs_tree_reshuffle_mappings
@ %def phs_tree_reshuffle_mappings
@
<<PHS trees: public>>=
public :: phs_tree_set_momentum_links
<<PHS trees: procedures>>=
subroutine phs_tree_set_momentum_links (tree, list)
type(phs_tree_t), intent(inout) :: tree
integer, dimension(:), allocatable :: list
tree%momentum_link = list
end subroutine phs_tree_set_momentum_links
@ %def phs_tree_set_momentum_links
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[phs_trees_ut.f90]]>>=
<<File header>>
module phs_trees_ut
use unit_tests
use phs_trees_uti
<<Standard module head>>
<<PHS trees: public test>>
contains
<<PHS trees: test driver>>
end module phs_trees_ut
@ %def phs_trees_ut
@
<<[[phs_trees_uti.f90]]>>=
<<File header>>
module phs_trees_uti
!!!<<Use kinds>>
use kinds, only: TC
<<Use strings>>
use flavors, only: flavor_t
use model_data, only: model_data_t
use resonances, only: resonance_history_t
use mappings, only: mapping_defaults_t
use phs_trees
<<Standard module head>>
<<PHS trees: test declarations>>
contains
<<PHS trees: tests>>
end module phs_trees_uti
@ %def phs_trees_ut
@ API: driver for the unit tests below.
<<PHS trees: public test>>=
public :: phs_trees_test
<<PHS trees: test driver>>=
subroutine phs_trees_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<PHS trees: execute tests>>
end subroutine phs_trees_test
@ %def phs_trees_test
@
Create a simple $2\to 3$ PHS tree and display it.
<<PHS trees: execute tests>>=
call test (phs_tree_1, "phs_tree_1", &
"check phs tree setup", &
u, results)
<<PHS trees: test declarations>>=
public :: phs_tree_1
<<PHS trees: tests>>=
subroutine phs_tree_1 (u)
integer, intent(in) :: u
type(phs_tree_t) :: tree
type(model_data_t), target :: model
type(flavor_t), dimension(5) :: flv
integer :: i
write (u, "(A)") "* Test output: phs_tree_1"
write (u, "(A)") "* Purpose: test PHS tree routines"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Set up flavors"
write (u, "(A)")
call flv%init ([1, -2, 24, 5, -5], model)
do i = 1, 5
write (u, "(1x)", advance="no")
call flv(i)%write (u)
end do
write (u, *)
write (u, "(A)")
write (u, "(A)") "* Create tree"
write (u, "(A)")
call tree%init (2, 3, 0, 0)
call tree%from_array ([integer(TC) :: 1, 2, 3, 4, 7, 8, 16])
call tree%set_mass_sum (flv)
call tree%set_effective_masses ()
call tree%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call tree%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_tree_1"
end subroutine phs_tree_1
@ %def phs_tree_1
@ The analogous tree with resonance (s-channel) mappings.
<<PHS trees: execute tests>>=
call test (phs_tree_2, "phs_tree_2", &
"check phs tree with resonances", &
u, results)
<<PHS trees: test declarations>>=
public :: phs_tree_2
<<PHS trees: tests>>=
subroutine phs_tree_2 (u)
integer, intent(in) :: u
type(phs_tree_t) :: tree
type(model_data_t), target :: model
type(mapping_defaults_t) :: mapping_defaults
type(flavor_t), dimension(5) :: flv
type(resonance_history_t) :: res_history
integer :: i
write (u, "(A)") "* Test output: phs_tree_2"
write (u, "(A)") "* Purpose: test PHS tree with resonances"
write (u, "(A)")
write (u, "(A)") "* Read model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Set up flavors"
write (u, "(A)")
call flv%init ([1, -2, 24, 5, -5], model)
do i = 1, 5
write (u, "(1x)", advance="no")
call flv(i)%write (u)
end do
write (u, *)
write (u, "(A)")
write (u, "(A)") "* Create tree with mappings"
write (u, "(A)")
call tree%init (2, 3, 0, 0)
call tree%from_array ([integer(TC) :: 1, 2, 3, 4, 7, 8, 16])
call tree%set_mass_sum (flv)
call tree%init_mapping (3_TC, var_str ("s_channel"), -24, model)
call tree%init_mapping (7_TC, var_str ("s_channel"), 23, model)
call tree%set_mapping_parameters (mapping_defaults, variable_limits=.false.)
call tree%set_effective_masses ()
call tree%write (u)
write (u, "(A)")
write (u, "(A)") "* Extract resonances from mappings"
write (u, "(A)")
call tree%extract_resonance_history (res_history)
call res_history%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call tree%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_tree_2"
end subroutine phs_tree_2
@ %def phs_tree_2
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{The phase-space forest}
Simply stated, a phase-space forest is a collection of phase-space
trees. More precisely, a [[phs_forest]] object contains all
parameterizations of phase space that \whizard\ will use for a single
hard process, prepared in the form of [[phs_tree]] objects. This is
suitable for evaluation by the \vamp\ integration package: each
parameterization (tree) is a valid channel in the multi-channel
adaptive integration, and each variable in a tree corresponds to an
integration dimension, defined by an appropriate mapping of the
$(0,1)$ interval to the allowed range of the integration variable.
The trees are grouped in groves. The trees (integration channels)
within a grove share a common weight, assuming that they are related
by some approximate symmetry.
Trees/channels that are related by an exact symmetry are connected by
an array of equivalences; each equivalence object holds the data that
relate one channel to another.
The phase-space setup, i.e., the detailed structure of trees and
forest, are read from a file. Therefore, this module also contains
the syntax definition and the parser needed for interpreting this
file.
<<[[phs_forests.f90]]>>=
<<File header>>
module phs_forests
<<Use kinds>>
use kinds, only: TC
<<Use strings>>
use io_units
use format_defs, only: FMT_19
use diagnostics
use lorentz
use numeric_utils
use permutations
use ifiles
use syntax_rules
use lexers
use parser
use model_data
use model_data
use flavors
use interactions
use phs_base
use resonances, only: resonance_history_t
use resonances, only: resonance_history_set_t
use mappings
use phs_trees
<<Standard module head>>
<<PHS forests: public>>
<<PHS forests: types>>
<<PHS forests: interfaces>>
<<PHS forests: variables>>
contains
<<PHS forests: procedures>>
end module phs_forests
@ %def phs_forests
@
\subsection{Phase-space setup parameters}
This transparent container holds the parameters that the algorithm
needs for phase-space setup, with reasonable defaults.
The threshold mass (for considering a particle as effectively
massless) is specified separately for s- and t-channel. The default is
to treat $W$ and $Z$ bosons as massive in the s-channel, but as
massless in the t-channel. The $b$-quark is treated always massless,
the $t$-quark always massive.
<<PHS forests: public>>=
public :: phs_parameters_t
<<PHS forests: types>>=
type :: phs_parameters_t
real(default) :: sqrts = 0
real(default) :: m_threshold_s = 50._default
real(default) :: m_threshold_t = 100._default
integer :: off_shell = 1
integer :: t_channel = 2
logical :: keep_nonresonant = .true.
contains
<<PHS forests: phs parameters: TBP>>
end type phs_parameters_t
@ %def phs_parameters_t
@ Write phase-space parameters to file.
<<PHS forests: phs parameters: TBP>>=
- procedure :: write => phs_parameters_write
+ procedure :: write => phs_parameters_write
<<PHS forests: procedures>>=
subroutine phs_parameters_write (phs_par, unit)
class(phs_parameters_t), intent(in) :: phs_par
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(3x,A," // FMT_19 // ")") "sqrts = ", phs_par%sqrts
write (u, "(3x,A," // FMT_19 // ")") "m_threshold_s = ", phs_par%m_threshold_s
write (u, "(3x,A," // FMT_19 // ")") "m_threshold_t = ", phs_par%m_threshold_t
write (u, "(3x,A,I0)") "off_shell = ", phs_par%off_shell
write (u, "(3x,A,I0)") "t_channel = ", phs_par%t_channel
write (u, "(3x,A,L1)") "keep_nonresonant = ", phs_par%keep_nonresonant
end subroutine phs_parameters_write
@ %def phs_parameters_write
@ Read phase-space parameters from file.
<<PHS forests: public>>=
public :: phs_parameters_read
<<PHS forests: procedures>>=
subroutine phs_parameters_read (phs_par, unit)
type(phs_parameters_t), intent(out) :: phs_par
integer, intent(in) :: unit
character(20) :: dummy
character :: equals
read (unit, *) dummy, equals, phs_par%sqrts
read (unit, *) dummy, equals, phs_par%m_threshold_s
read (unit, *) dummy, equals, phs_par%m_threshold_t
read (unit, *) dummy, equals, phs_par%off_shell
read (unit, *) dummy, equals, phs_par%t_channel
read (unit, *) dummy, equals, phs_par%keep_nonresonant
end subroutine phs_parameters_read
@ %def phs_parameters_write
@ Comparison.
<<PHS forests: interfaces>>=
interface operator(==)
module procedure phs_parameters_eq
end interface
interface operator(/=)
module procedure phs_parameters_ne
end interface
<<PHS forests: procedures>>=
function phs_parameters_eq (phs_par1, phs_par2) result (equal)
logical :: equal
type(phs_parameters_t), intent(in) :: phs_par1, phs_par2
equal = phs_par1%sqrts == phs_par2%sqrts &
.and. phs_par1%m_threshold_s == phs_par2%m_threshold_s &
.and. phs_par1%m_threshold_t == phs_par2%m_threshold_t &
.and. phs_par1%off_shell == phs_par2%off_shell &
.and. phs_par1%t_channel == phs_par2%t_channel &
.and.(phs_par1%keep_nonresonant .eqv. phs_par2%keep_nonresonant)
end function phs_parameters_eq
function phs_parameters_ne (phs_par1, phs_par2) result (ne)
logical :: ne
type(phs_parameters_t), intent(in) :: phs_par1, phs_par2
ne = phs_par1%sqrts /= phs_par2%sqrts &
.or. phs_par1%m_threshold_s /= phs_par2%m_threshold_s &
.or. phs_par1%m_threshold_t /= phs_par2%m_threshold_t &
.or. phs_par1%off_shell /= phs_par2%off_shell &
.or. phs_par1%t_channel /= phs_par2%t_channel &
.or.(phs_par1%keep_nonresonant .neqv. phs_par2%keep_nonresonant)
end function phs_parameters_ne
@ %def phs_parameters_eq phs_parameters_ne
@
\subsection{Equivalences}
This type holds information about equivalences between phase-space
trees. We make a linked list, where each node contains the two
trees which are equivalent and the corresponding permutation of
external particles. Two more arrays are to be filled: The permutation
of mass variables and the permutation of angular variables, where the
signature indicates a necessary exchange of daughter branches.
<<PHS forests: types>>=
type :: equivalence_t
private
integer :: left, right
type(permutation_t) :: perm
type(permutation_t) :: msq_perm, angle_perm
logical, dimension(:), allocatable :: angle_sig
type(equivalence_t), pointer :: next => null ()
end type equivalence_t
@ %def equivalence_t
<<PHS forests: types>>=
type :: equivalence_list_t
private
integer :: length = 0
type(equivalence_t), pointer :: first => null ()
type(equivalence_t), pointer :: last => null ()
end type equivalence_list_t
@ %def equivalence_list_t
@ Append an equivalence to the list
<<PHS forests: procedures>>=
subroutine equivalence_list_add (eql, left, right, perm)
type(equivalence_list_t), intent(inout) :: eql
integer, intent(in) :: left, right
type(permutation_t), intent(in) :: perm
type(equivalence_t), pointer :: eq
allocate (eq)
eq%left = left
eq%right = right
eq%perm = perm
if (associated (eql%last)) then
eql%last%next => eq
else
eql%first => eq
end if
eql%last => eq
eql%length = eql%length + 1
end subroutine equivalence_list_add
@ %def equivalence_list_add
@ Delete the list contents. Has to be pure because it is called from
an elemental subroutine.
<<PHS forests: procedures>>=
pure subroutine equivalence_list_final (eql)
type(equivalence_list_t), intent(inout) :: eql
type(equivalence_t), pointer :: eq
do while (associated (eql%first))
eq => eql%first
eql%first => eql%first%next
deallocate (eq)
end do
eql%last => null ()
eql%length = 0
end subroutine equivalence_list_final
@ %def equivalence_list_final
@ Make a deep copy of the equivalence list. This allows for deep
copies of groves and forests.
<<PHS forests: interfaces>>=
interface assignment(=)
module procedure equivalence_list_assign
end interface
<<PHS forests: procedures>>=
subroutine equivalence_list_assign (eql_out, eql_in)
type(equivalence_list_t), intent(out) :: eql_out
type(equivalence_list_t), intent(in) :: eql_in
type(equivalence_t), pointer :: eq, eq_copy
eq => eql_in%first
do while (associated (eq))
allocate (eq_copy)
eq_copy = eq
eq_copy%next => null ()
if (associated (eql_out%first)) then
eql_out%last%next => eq_copy
else
eql_out%first => eq_copy
end if
eql_out%last => eq_copy
eq => eq%next
end do
end subroutine equivalence_list_assign
@ %def equivalence_list_assign
@ The number of list entries
<<PHS forests: procedures>>=
elemental function equivalence_list_length (eql) result (length)
integer :: length
type(equivalence_list_t), intent(in) :: eql
length = eql%length
end function equivalence_list_length
@ %def equivalence_list_length
@ Recursively write the equivalences list
<<PHS forests: procedures>>=
subroutine equivalence_list_write (eql, unit)
type(equivalence_list_t), intent(in) :: eql
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
if (associated (eql%first)) then
call equivalence_write_rec (eql%first, u)
else
write (u, *) " [empty]"
end if
contains
recursive subroutine equivalence_write_rec (eq, u)
type(equivalence_t), intent(in) :: eq
integer, intent(in) :: u
integer :: i
write (u, "(3x,A,1x,I0,1x,I0,2x,A)", advance="no") &
"Equivalence:", eq%left, eq%right, "Final state permutation:"
call permutation_write (eq%perm, u)
write (u, "(1x,12x,1x,A,1x)", advance="no") &
" msq permutation: "
call permutation_write (eq%msq_perm, u)
write (u, "(1x,12x,1x,A,1x)", advance="no") &
" angle permutation:"
call permutation_write (eq%angle_perm, u)
write (u, "(1x,12x,1x,26x)", advance="no")
do i = 1, size (eq%angle_sig)
if (eq%angle_sig(i)) then
write (u, "(1x,A)", advance="no") "+"
else
write (u, "(1x,A)", advance="no") "-"
end if
end do
write (u, *)
if (associated (eq%next)) call equivalence_write_rec (eq%next, u)
end subroutine equivalence_write_rec
end subroutine equivalence_list_write
@ %def equivalence_list_write
@
\subsection{Groves}
A grove is a group of trees (phase-space channels) that share a common
weight in the integration. Within a grove, channels can be declared
equivalent, so they also share their integration grids (up to
symmetries). The grove contains a list of equivalences. The
[[tree_count_offset]] is the total number of trees of the preceding
groves; when the trees are counted per forest (integration channels),
the offset has to be added to all tree indices.
<<PHS forests: types>>=
type :: phs_grove_t
private
integer :: tree_count_offset
type(phs_tree_t), dimension(:), allocatable :: tree
type(equivalence_list_t) :: equivalence_list
end type phs_grove_t
@ %def phs_grove_t
@ Call [[phs_tree_init]] which is also elemental:
<<PHS forests: procedures>>=
elemental subroutine phs_grove_init &
(grove, n_trees, n_in, n_out, n_masses, n_angles)
type(phs_grove_t), intent(inout) :: grove
integer, intent(in) :: n_trees, n_in, n_out, n_masses, n_angles
grove%tree_count_offset = 0
allocate (grove%tree (n_trees))
call phs_tree_init (grove%tree, n_in, n_out, n_masses, n_angles)
end subroutine phs_grove_init
@ %def phs_grove_init
@ The trees do not have pointer components, thus no call to
[[phs_tree_final]]:
<<PHS forests: procedures>>=
elemental subroutine phs_grove_final (grove)
type(phs_grove_t), intent(inout) :: grove
deallocate (grove%tree)
call equivalence_list_final (grove%equivalence_list)
end subroutine phs_grove_final
@ %def phs_grove_final
@ Deep copy.
<<PHS forests: interfaces>>=
interface assignment(=)
module procedure phs_grove_assign0
module procedure phs_grove_assign1
end interface
<<PHS forests: procedures>>=
subroutine phs_grove_assign0 (grove_out, grove_in)
type(phs_grove_t), intent(out) :: grove_out
type(phs_grove_t), intent(in) :: grove_in
grove_out%tree_count_offset = grove_in%tree_count_offset
if (allocated (grove_in%tree)) then
allocate (grove_out%tree (size (grove_in%tree)))
grove_out%tree = grove_in%tree
end if
grove_out%equivalence_list = grove_in%equivalence_list
end subroutine phs_grove_assign0
subroutine phs_grove_assign1 (grove_out, grove_in)
type(phs_grove_t), dimension(:), intent(out) :: grove_out
type(phs_grove_t), dimension(:), intent(in) :: grove_in
integer :: i
do i = 1, size (grove_in)
call phs_grove_assign0 (grove_out(i), grove_in(i))
end do
end subroutine phs_grove_assign1
@ %def phs_grove_assign
@ Get the global (s-channel) mappings. Implemented as a subroutine
which returns an array (slice).
<<PHS forests: procedures>>=
subroutine phs_grove_assign_s_mappings (grove, mapping)
type(phs_grove_t), intent(in) :: grove
type(mapping_t), dimension(:), intent(out) :: mapping
integer :: i
if (size (mapping) == size (grove%tree)) then
do i = 1, size (mapping)
call phs_tree_assign_s_mapping (grove%tree(i), mapping(i))
end do
else
call msg_bug ("phs_grove_assign_s_mappings: array size mismatch")
end if
end subroutine phs_grove_assign_s_mappings
@ %def phs_grove_assign_s_mappings
@
\subsection{The forest type}
This is a collection of trees and associated particles. In a given
tree, each branch code corresponds to a particle in the [[prt]] array.
Furthermore, we have an array of mass sums which is independent of the
decay tree and of the particular event. The mappings directly
correspond to the decay trees, and the decay groves collect the trees
in classes. The permutation list consists of all permutations of
outgoing particles that map the decay forest onto itself.
The particle codes [[flv]] (one for each external particle) are needed
for determining masses and such. The trees and associated information
are collected in the [[grove]] array, together with a lookup table
that associates tree indices to groves. Finally, the [[prt]] array
serves as workspace for phase-space evaluation.
The [[prt_combination]] is a list of index pairs, namely the particle
momenta pairs that need to be combined in order to provide all
momentum combinations that the phase-space trees need to know.
<<PHS forests: public>>=
public :: phs_forest_t
<<PHS forests: types>>=
type :: phs_forest_t
private
integer :: n_in, n_out, n_tot
integer :: n_masses, n_angles, n_dimensions
integer :: n_trees, n_equivalences
type(flavor_t), dimension(:), allocatable :: flv
type(phs_grove_t), dimension(:), allocatable :: grove
integer, dimension(:), allocatable :: grove_lookup
type(phs_prt_t), dimension(:), allocatable :: prt_in
type(phs_prt_t), dimension(:), allocatable :: prt_out
type(phs_prt_t), dimension(:), allocatable :: prt
integer(TC), dimension(:,:), allocatable :: prt_combination
type(mapping_t), dimension(:), allocatable :: s_mapping
contains
<<PHS forests: phs forest: TBP>>
end type phs_forest_t
@ %def phs_forest_t
@
The initialization merely allocates memory. We have to know how many
trees there are in each grove, so we can initialize everything. The
number of groves is the size of the [[n_tree]] array.
In the [[grove_lookup]] table we store the grove index that belongs to
each absolute tree index. The difference between the absolute index
and the relative (to the grove) index is stored, for each grove, as
[[tree_count_offset]].
The particle array is allocated according to the total number of
branches each tree has, but not filled.
<<PHS forests: public>>=
public :: phs_forest_init
<<PHS forests: procedures>>=
subroutine phs_forest_init (forest, n_tree, n_in, n_out)
type(phs_forest_t), intent(inout) :: forest
integer, dimension(:), intent(in) :: n_tree
integer, intent(in) :: n_in, n_out
integer :: g, count, k_root
forest%n_in = n_in
forest%n_out = n_out
forest%n_tot = n_in + n_out
forest%n_masses = max (n_out - 2, 0)
forest%n_angles = max (2*n_out - 2, 0)
forest%n_dimensions = forest%n_masses + forest%n_angles
forest%n_trees = sum (n_tree)
forest%n_equivalences = 0
allocate (forest%grove (size (n_tree)))
call phs_grove_init &
(forest%grove, n_tree, n_in, n_out, forest%n_masses, &
forest%n_angles)
allocate (forest%grove_lookup (forest%n_trees))
count = 0
do g = 1, size (forest%grove)
forest%grove(g)%tree_count_offset = count
forest%grove_lookup (count+1:count+n_tree(g)) = g
count = count + n_tree(g)
end do
allocate (forest%prt_in (n_in))
allocate (forest%prt_out (forest%n_out))
k_root = 2**forest%n_tot - 1
allocate (forest%prt (k_root))
allocate (forest%prt_combination (2, k_root))
allocate (forest%s_mapping (forest%n_trees))
end subroutine phs_forest_init
@ %def phs_forest_init
@ Assign the global (s-channel) mappings.
<<PHS forests: public>>=
public :: phs_forest_set_s_mappings
<<PHS forests: procedures>>=
subroutine phs_forest_set_s_mappings (forest)
type(phs_forest_t), intent(inout) :: forest
integer :: g, i0, i1, n
do g = 1, size (forest%grove)
call phs_forest_get_grove_bounds (forest, g, i0, i1, n)
call phs_grove_assign_s_mappings &
(forest%grove(g), forest%s_mapping(i0:i1))
end do
end subroutine phs_forest_set_s_mappings
@ %def phs_forest_set_s_mappings
@ The grove finalizer is called because it contains the equivalence list:
<<PHS forests: public>>=
public :: phs_forest_final
<<PHS forests: procedures>>=
subroutine phs_forest_final (forest)
type(phs_forest_t), intent(inout) :: forest
if (allocated (forest%grove)) then
call phs_grove_final (forest%grove)
deallocate (forest%grove)
end if
if (allocated (forest%grove_lookup)) deallocate (forest%grove_lookup)
if (allocated (forest%prt)) deallocate (forest%prt)
if (allocated (forest%s_mapping)) deallocate (forest%s_mapping)
end subroutine phs_forest_final
@ %def phs_forest_final
@
\subsection{Screen output}
Write the particles that are non-null, then the trees which point to
them:
<<PHS forests: public>>=
public :: phs_forest_write
<<PHS forests: phs forest: TBP>>=
procedure :: write => phs_forest_write
<<PHS forests: procedures>>=
subroutine phs_forest_write (forest, unit)
class(phs_forest_t), intent(in) :: forest
integer, intent(in), optional :: unit
integer :: u
integer :: i, g, k
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "Phase space forest:"
write (u, "(3x,A,I0)") "n_in = ", forest%n_in
write (u, "(3x,A,I0)") "n_out = ", forest%n_out
write (u, "(3x,A,I0)") "n_tot = ", forest%n_tot
write (u, "(3x,A,I0)") "n_masses = ", forest%n_masses
write (u, "(3x,A,I0)") "n_angles = ", forest%n_angles
write (u, "(3x,A,I0)") "n_dim = ", forest%n_dimensions
write (u, "(3x,A,I0)") "n_trees = ", forest%n_trees
write (u, "(3x,A,I0)") "n_equiv = ", forest%n_equivalences
write (u, "(3x,A)", advance="no") "flavors ="
if (allocated (forest%flv)) then
do i = 1, size (forest%flv)
write (u, "(1x,I0)", advance="no") forest%flv(i)%get_pdg ()
end do
write (u, "(A)")
else
write (u, "(1x,A)") "[empty]"
end if
write (u, "(1x,A)") "Particle combinations:"
if (allocated (forest%prt_combination)) then
do k = 1, size (forest%prt_combination, 2)
if (forest%prt_combination(1, k) /= 0) then
write (u, "(3x,I0,1x,'<=',1x,I0,1x,'+',1x,I0)") &
k, forest%prt_combination(:,k)
end if
end do
else
write (u, "(3x,A)") " [empty]"
end if
write (u, "(1x,A)") "Groves and trees:"
if (allocated (forest%grove)) then
do g = 1, size (forest%grove)
write (u, "(3x,A,1x,I0)") "Grove ", g
call phs_grove_write (forest%grove(g), unit)
end do
else
write (u, "(3x,A)") " [empty]"
end if
write (u, "(1x,A,I0)") "Total number of equivalences: ", &
forest%n_equivalences
write (u, "(A)")
write (u, "(1x,A)") "Global s-channel mappings:"
if (allocated (forest%s_mapping)) then
do i = 1, size (forest%s_mapping)
associate (mapping => forest%s_mapping(i))
if (mapping_is_s_channel (mapping) &
.or. mapping_is_on_shell (mapping)) then
write (u, "(1x,I0,':',1x)", advance="no") i
call mapping_write (forest%s_mapping(i), unit)
end if
end associate
end do
else
write (u, "(3x,A)") " [empty]"
end if
write (u, "(A)")
write (u, "(1x,A)") "Incoming particles:"
if (allocated (forest%prt_in)) then
if (any (phs_prt_is_defined (forest%prt_in))) then
do i = 1, size (forest%prt_in)
if (phs_prt_is_defined (forest%prt_in(i))) then
write (u, "(1x,A,1x,I0)") "Particle", i
call phs_prt_write (forest%prt_in(i), u)
end if
end do
else
write (u, "(3x,A)") "[all undefined]"
end if
else
write (u, "(3x,A)") " [empty]"
end if
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particles:"
if (allocated (forest%prt_out)) then
if (any (phs_prt_is_defined (forest%prt_out))) then
do i = 1, size (forest%prt_out)
if (phs_prt_is_defined (forest%prt_out(i))) then
write (u, "(1x,A,1x,I0)") "Particle", i
call phs_prt_write (forest%prt_out(i), u)
end if
end do
else
write (u, "(3x,A)") "[all undefined]"
end if
else
write (u, "(1x,A)") " [empty]"
end if
write (u, "(A)")
write (u, "(1x,A)") "Tree particles:"
if (allocated (forest%prt)) then
if (any (phs_prt_is_defined (forest%prt))) then
do i = 1, size (forest%prt)
if (phs_prt_is_defined (forest%prt(i))) then
write (u, "(1x,A,1x,I0)") "Particle", i
call phs_prt_write (forest%prt(i), u)
end if
end do
else
write (u, "(3x,A)") "[all undefined]"
end if
else
write (u, "(3x,A)") " [empty]"
end if
end subroutine phs_forest_write
subroutine phs_grove_write (grove, unit)
type(phs_grove_t), intent(in) :: grove
integer, intent(in), optional :: unit
integer :: u
integer :: t
u = given_output_unit (unit); if (u < 0) return
do t = 1, size (grove%tree)
write (u, "(3x,A,I0)") "Tree ", t
call phs_tree_write (grove%tree(t), unit)
end do
write (u, "(1x,A)") "Equivalence list:"
call equivalence_list_write (grove%equivalence_list, unit)
end subroutine phs_grove_write
@ %def phs_grove_write phs_forest_write
@ Deep copy.
<<PHS forests: public>>=
public :: assignment(=)
<<PHS forests: interfaces>>=
interface assignment(=)
module procedure phs_forest_assign
end interface
<<PHS forests: procedures>>=
subroutine phs_forest_assign (forest_out, forest_in)
type(phs_forest_t), intent(out) :: forest_out
type(phs_forest_t), intent(in) :: forest_in
forest_out%n_in = forest_in%n_in
forest_out%n_out = forest_in%n_out
forest_out%n_tot = forest_in%n_tot
forest_out%n_masses = forest_in%n_masses
forest_out%n_angles = forest_in%n_angles
forest_out%n_dimensions = forest_in%n_dimensions
forest_out%n_trees = forest_in%n_trees
forest_out%n_equivalences = forest_in%n_equivalences
if (allocated (forest_in%flv)) then
allocate (forest_out%flv (size (forest_in%flv)))
forest_out%flv = forest_in%flv
end if
if (allocated (forest_in%grove)) then
allocate (forest_out%grove (size (forest_in%grove)))
forest_out%grove = forest_in%grove
end if
if (allocated (forest_in%grove_lookup)) then
allocate (forest_out%grove_lookup (size (forest_in%grove_lookup)))
forest_out%grove_lookup = forest_in%grove_lookup
end if
if (allocated (forest_in%prt_in)) then
allocate (forest_out%prt_in (size (forest_in%prt_in)))
forest_out%prt_in = forest_in%prt_in
end if
if (allocated (forest_in%prt_out)) then
allocate (forest_out%prt_out (size (forest_in%prt_out)))
forest_out%prt_out = forest_in%prt_out
end if
if (allocated (forest_in%prt)) then
allocate (forest_out%prt (size (forest_in%prt)))
forest_out%prt = forest_in%prt
end if
if (allocated (forest_in%s_mapping)) then
allocate (forest_out%s_mapping (size (forest_in%s_mapping)))
forest_out%s_mapping = forest_in%s_mapping
end if
if (allocated (forest_in%prt_combination)) then
allocate (forest_out%prt_combination &
(2, size (forest_in%prt_combination, 2)))
forest_out%prt_combination = forest_in%prt_combination
end if
end subroutine phs_forest_assign
@ %def phs_forest_assign
@
\subsection{Accessing contents}
Get the number of integration parameters
<<PHS forests: public>>=
public :: phs_forest_get_n_parameters
<<PHS forests: procedures>>=
function phs_forest_get_n_parameters (forest) result (n)
integer :: n
type(phs_forest_t), intent(in) :: forest
n = forest%n_dimensions
end function phs_forest_get_n_parameters
@ %def phs_forest_get_n_parameters
@ Get the number of integration channels
<<PHS forests: public>>=
public :: phs_forest_get_n_channels
<<PHS forests: procedures>>=
function phs_forest_get_n_channels (forest) result (n)
integer :: n
type(phs_forest_t), intent(in) :: forest
n = forest%n_trees
end function phs_forest_get_n_channels
@ %def phs_forest_get_n_channels
@ Get the number of groves
<<PHS forests: public>>=
public :: phs_forest_get_n_groves
<<PHS forests: procedures>>=
function phs_forest_get_n_groves (forest) result (n)
integer :: n
type(phs_forest_t), intent(in) :: forest
n = size (forest%grove)
end function phs_forest_get_n_groves
@ %def phs_forest_get_n_groves
@ Get the index bounds for a specific grove.
<<PHS forests: public>>=
public :: phs_forest_get_grove_bounds
<<PHS forests: procedures>>=
subroutine phs_forest_get_grove_bounds (forest, g, i0, i1, n)
type(phs_forest_t), intent(in) :: forest
integer, intent(in) :: g
integer, intent(out) :: i0, i1, n
n = size (forest%grove(g)%tree)
i0 = forest%grove(g)%tree_count_offset + 1
i1 = forest%grove(g)%tree_count_offset + n
end subroutine phs_forest_get_grove_bounds
@ %def phs_forest_get_grove_bounds
@ Get the number of equivalences
<<PHS forests: public>>=
public :: phs_forest_get_n_equivalences
<<PHS forests: procedures>>=
function phs_forest_get_n_equivalences (forest) result (n)
integer :: n
type(phs_forest_t), intent(in) :: forest
n = forest%n_equivalences
end function phs_forest_get_n_equivalences
@ %def phs_forest_get_n_equivalences
@ Return true if a particular channel has a global (s-channel)
mapping; also return the resonance mass and width for this mapping.
<<PHS forests: public>>=
public :: phs_forest_get_s_mapping
public :: phs_forest_get_on_shell
<<PHS forests: procedures>>=
subroutine phs_forest_get_s_mapping (forest, channel, flag, mass, width)
type(phs_forest_t), intent(in) :: forest
integer, intent(in) :: channel
logical, intent(out) :: flag
real(default), intent(out) :: mass, width
flag = mapping_is_s_channel (forest%s_mapping(channel))
if (flag) then
mass = mapping_get_mass (forest%s_mapping(channel))
width = mapping_get_width (forest%s_mapping(channel))
else
mass = 0
width = 0
end if
end subroutine phs_forest_get_s_mapping
subroutine phs_forest_get_on_shell (forest, channel, flag, mass)
type(phs_forest_t), intent(in) :: forest
integer, intent(in) :: channel
logical, intent(out) :: flag
real(default), intent(out) :: mass
flag = mapping_is_on_shell (forest%s_mapping(channel))
if (flag) then
mass = mapping_get_mass (forest%s_mapping(channel))
else
mass = 0
end if
end subroutine phs_forest_get_on_shell
@ %def phs_forest_get_s_mapping
@ %def phs_forest_get_on_shell
@
Extract the set of unique resonance histories, in form of an array.
<<PHS forests: phs forest: TBP>>=
procedure :: extract_resonance_history_set &
=> phs_forest_extract_resonance_history_set
<<PHS forests: procedures>>=
subroutine phs_forest_extract_resonance_history_set &
(forest, res_set, include_trivial)
class(phs_forest_t), intent(in) :: forest
type(resonance_history_set_t), intent(out) :: res_set
logical, intent(in), optional :: include_trivial
type(resonance_history_t) :: rh
integer :: g, t
logical :: triv
triv = .false.; if (present (include_trivial)) triv = include_trivial
call res_set%init ()
do g = 1, size (forest%grove)
associate (grove => forest%grove(g))
do t = 1, size (grove%tree)
call grove%tree(t)%extract_resonance_history (rh)
call res_set%enter (rh, include_trivial)
end do
end associate
end do
call res_set%freeze ()
end subroutine phs_forest_extract_resonance_history_set
@ %def phs_forest_extract_resonance_history_set
@
\subsection{Read the phase space setup from file}
The phase space setup is stored in a file. The file may be generated
by the [[cascades]] module below, or by other means. This file has to
be read and parsed to create the PHS forest as the internal
phase-space representation.
Create lexer and syntax:
<<PHS forests: procedures>>=
subroutine define_phs_forest_syntax (ifile)
type(ifile_t) :: ifile
call ifile_append (ifile, "SEQ phase_space_list = process_phase_space*")
call ifile_append (ifile, "SEQ process_phase_space = " &
// "process_def process_header phase_space")
call ifile_append (ifile, "SEQ process_def = process process_list")
call ifile_append (ifile, "KEY process")
call ifile_append (ifile, "LIS process_list = process_tag*")
call ifile_append (ifile, "IDE process_tag")
call ifile_append (ifile, "SEQ process_header = " &
// "md5sum_process = md5sum " &
// "md5sum_model_par = md5sum " &
// "md5sum_phs_config = md5sum " &
// "sqrts = real " &
// "m_threshold_s = real " &
// "m_threshold_t = real " &
// "off_shell = integer " &
// "t_channel = integer " &
// "keep_nonresonant = logical")
call ifile_append (ifile, "KEY '='")
call ifile_append (ifile, "KEY '-'")
call ifile_append (ifile, "KEY md5sum_process")
call ifile_append (ifile, "KEY md5sum_model_par")
call ifile_append (ifile, "KEY md5sum_phs_config")
call ifile_append (ifile, "KEY sqrts")
call ifile_append (ifile, "KEY m_threshold_s")
call ifile_append (ifile, "KEY m_threshold_t")
call ifile_append (ifile, "KEY off_shell")
call ifile_append (ifile, "KEY t_channel")
call ifile_append (ifile, "KEY keep_nonresonant")
call ifile_append (ifile, "QUO md5sum = '""' ... '""'")
call ifile_append (ifile, "REA real")
call ifile_append (ifile, "INT integer")
call ifile_append (ifile, "IDE logical")
call ifile_append (ifile, "SEQ phase_space = grove_def+")
call ifile_append (ifile, "SEQ grove_def = grove tree_def+")
call ifile_append (ifile, "KEY grove")
call ifile_append (ifile, "SEQ tree_def = tree bincodes mapping*")
call ifile_append (ifile, "KEY tree")
call ifile_append (ifile, "SEQ bincodes = bincode*")
call ifile_append (ifile, "INT bincode")
call ifile_append (ifile, "SEQ mapping = map bincode channel signed_pdg")
call ifile_append (ifile, "KEY map")
call ifile_append (ifile, "ALT channel = &
&s_channel | t_channel | u_channel | &
&collinear | infrared | radiation | on_shell")
call ifile_append (ifile, "KEY s_channel")
! call ifile_append (ifile, "KEY t_channel") !!! Key already exists
call ifile_append (ifile, "KEY u_channel")
call ifile_append (ifile, "KEY collinear")
call ifile_append (ifile, "KEY infrared")
call ifile_append (ifile, "KEY radiation")
call ifile_append (ifile, "KEY on_shell")
call ifile_append (ifile, "ALT signed_pdg = &
&pdg | negative_pdg")
call ifile_append (ifile, "SEQ negative_pdg = '-' pdg")
call ifile_append (ifile, "INT pdg")
end subroutine define_phs_forest_syntax
@ %def define_phs_forest_syntax
@ The model-file syntax and lexer are fixed, therefore stored as
module variables:
<<PHS forests: variables>>=
type(syntax_t), target, save :: syntax_phs_forest
@ %def syntax_phs_forest
<<PHS forests: public>>=
public :: syntax_phs_forest_init
<<PHS forests: procedures>>=
subroutine syntax_phs_forest_init ()
type(ifile_t) :: ifile
call define_phs_forest_syntax (ifile)
call syntax_init (syntax_phs_forest, ifile)
call ifile_final (ifile)
end subroutine syntax_phs_forest_init
@ %def syntax_phs_forest_init
<<PHS forests: procedures>>=
subroutine lexer_init_phs_forest (lexer)
type(lexer_t), intent(out) :: lexer
call lexer_init (lexer, &
comment_chars = "#!", &
quote_chars = '"', &
quote_match = '"', &
single_chars = "-", &
special_class = ["="] , &
keyword_list = syntax_get_keyword_list_ptr (syntax_phs_forest))
end subroutine lexer_init_phs_forest
@ %def lexer_init_phs_forest
<<PHS forests: public>>=
public :: syntax_phs_forest_final
<<PHS forests: procedures>>=
subroutine syntax_phs_forest_final ()
call syntax_final (syntax_phs_forest)
end subroutine syntax_phs_forest_final
@ %def syntax_phs_forest_final
<<PHS forests: public>>=
public :: syntax_phs_forest_write
<<PHS forests: procedures>>=
subroutine syntax_phs_forest_write (unit)
integer, intent(in), optional :: unit
call syntax_write (syntax_phs_forest, unit)
end subroutine syntax_phs_forest_write
@ %def syntax_phs_forest_write
@ The concrete parser and interpreter. Generate an input stream for
the external [[unit]], read the parse tree (with given [[syntax]] and
[[lexer]]) from this stream, and transfer the contents of the parse
tree to the PHS [[forest]].
We look for the matching [[process]] tag, count groves and trees for
initializing the [[forest]], and fill the trees.
If the optional parameters are set, compare the parameters stored in
the file to those. Set [[match]] true if everything agrees.
<<PHS forests: public>>=
public :: phs_forest_read
<<PHS forests: interfaces>>=
interface phs_forest_read
module procedure phs_forest_read_file
module procedure phs_forest_read_unit
module procedure phs_forest_read_parse_tree
end interface
<<PHS forests: procedures>>=
subroutine phs_forest_read_file &
(forest, filename, process_id, n_in, n_out, model, found, &
md5sum_process, md5sum_model_par, &
md5sum_phs_config, phs_par, match)
type(phs_forest_t), intent(out) :: forest
type(string_t), intent(in) :: filename
type(string_t), intent(in) :: process_id
integer, intent(in) :: n_in, n_out
class(model_data_t), intent(in), target :: model
logical, intent(out) :: found
character(32), intent(in), optional :: &
md5sum_process, md5sum_model_par, md5sum_phs_config
type(phs_parameters_t), intent(in), optional :: phs_par
logical, intent(out), optional :: match
type(parse_tree_t), target :: parse_tree
type(stream_t), target :: stream
type(lexer_t) :: lexer
call lexer_init_phs_forest (lexer)
call stream_init (stream, char (filename))
call lexer_assign_stream (lexer, stream)
call parse_tree_init (parse_tree, syntax_phs_forest, lexer)
call phs_forest_read (forest, parse_tree, &
process_id, n_in, n_out, model, found, &
md5sum_process, md5sum_model_par, md5sum_phs_config, phs_par, match)
call stream_final (stream)
call lexer_final (lexer)
call parse_tree_final (parse_tree)
end subroutine phs_forest_read_file
subroutine phs_forest_read_unit &
(forest, unit, process_id, n_in, n_out, model, found, &
md5sum_process, md5sum_model_par, md5sum_phs_config, &
phs_par, match)
type(phs_forest_t), intent(out) :: forest
integer, intent(in) :: unit
type(string_t), intent(in) :: process_id
integer, intent(in) :: n_in, n_out
class(model_data_t), intent(in), target :: model
logical, intent(out) :: found
character(32), intent(in), optional :: &
md5sum_process, md5sum_model_par, md5sum_phs_config
type(phs_parameters_t), intent(in), optional :: phs_par
logical, intent(out), optional :: match
type(parse_tree_t), target :: parse_tree
type(stream_t), target :: stream
type(lexer_t) :: lexer
call lexer_init_phs_forest (lexer)
call stream_init (stream, unit)
call lexer_assign_stream (lexer, stream)
call parse_tree_init (parse_tree, syntax_phs_forest, lexer)
call phs_forest_read (forest, parse_tree, &
process_id, n_in, n_out, model, found, &
md5sum_process, md5sum_model_par, md5sum_phs_config, &
phs_par, match)
call stream_final (stream)
call lexer_final (lexer)
call parse_tree_final (parse_tree)
end subroutine phs_forest_read_unit
subroutine phs_forest_read_parse_tree &
(forest, parse_tree, process_id, n_in, n_out, model, found, &
md5sum_process, md5sum_model_par, md5sum_phs_config, &
phs_par, match)
type(phs_forest_t), intent(out) :: forest
type(parse_tree_t), intent(in), target :: parse_tree
type(string_t), intent(in) :: process_id
integer, intent(in) :: n_in, n_out
class(model_data_t), intent(in), target :: model
logical, intent(out) :: found
character(32), intent(in), optional :: &
md5sum_process, md5sum_model_par, md5sum_phs_config
type(phs_parameters_t), intent(in), optional :: phs_par
logical, intent(out), optional :: match
type(parse_node_t), pointer :: node_header, node_phs, node_grove
integer :: n_grove, g
integer, dimension(:), allocatable :: n_tree
integer :: t
node_header => parse_tree_get_process_ptr (parse_tree, process_id)
found = associated (node_header); if (.not. found) return
if (present (match)) then
call phs_forest_check_input (node_header, &
md5sum_process, md5sum_model_par, md5sum_phs_config, phs_par, match)
if (.not. match) return
end if
node_phs => parse_node_get_next_ptr (node_header)
n_grove = parse_node_get_n_sub (node_phs)
allocate (n_tree (n_grove))
do g = 1, n_grove
node_grove => parse_node_get_sub_ptr (node_phs, g)
n_tree(g) = parse_node_get_n_sub (node_grove) - 1
end do
call phs_forest_init (forest, n_tree, n_in, n_out)
do g = 1, n_grove
node_grove => parse_node_get_sub_ptr (node_phs, g)
do t = 1, n_tree(g)
call phs_tree_set (forest%grove(g)%tree(t), &
parse_node_get_sub_ptr (node_grove, t+1), model)
end do
end do
end subroutine phs_forest_read_parse_tree
@ %def phs_forest
@ Check the input for consistency. If any MD5 sum or phase-space
parameter disagrees, the phase-space file cannot be used. The MD5
sum checks are skipped if the stored MD5 sum is empty.
<<PHS forests: procedures>>=
subroutine phs_forest_check_input (pn_header, &
md5sum_process, md5sum_model_par, md5sum_phs_config, phs_par, match)
type(parse_node_t), intent(in), target :: pn_header
character(32), intent(in) :: &
md5sum_process, md5sum_model_par, md5sum_phs_config
type(phs_parameters_t), intent(in), optional :: phs_par
logical, intent(out) :: match
type(parse_node_t), pointer :: pn_md5sum, pn_rval, pn_ival, pn_lval
character(32) :: md5sum
type(phs_parameters_t) :: phs_par_old
character(1) :: lstr
pn_md5sum => parse_node_get_sub_ptr (pn_header, 3)
md5sum = parse_node_get_string (pn_md5sum)
if (md5sum /= "" .and. md5sum /= md5sum_process) then
call msg_message ("Phase space: discarding old configuration &
&(process changed)")
match = .false.; return
end if
pn_md5sum => parse_node_get_next_ptr (pn_md5sum, 3)
md5sum = parse_node_get_string (pn_md5sum)
if (md5sum /= "" .and. md5sum /= md5sum_model_par) then
call msg_message ("Phase space: discarding old configuration &
&(model parameters changed)")
match = .false.; return
end if
pn_md5sum => parse_node_get_next_ptr (pn_md5sum, 3)
md5sum = parse_node_get_string (pn_md5sum)
if (md5sum /= "" .and. md5sum /= md5sum_phs_config) then
call msg_message ("Phase space: discarding old configuration &
&(configuration parameters changed)")
match = .false.; return
end if
if (present (phs_par)) then
pn_rval => parse_node_get_next_ptr (pn_md5sum, 3)
phs_par_old%sqrts = parse_node_get_real (pn_rval)
pn_rval => parse_node_get_next_ptr (pn_rval, 3)
phs_par_old%m_threshold_s = parse_node_get_real (pn_rval)
pn_rval => parse_node_get_next_ptr (pn_rval, 3)
phs_par_old%m_threshold_t = parse_node_get_real (pn_rval)
pn_ival => parse_node_get_next_ptr (pn_rval, 3)
phs_par_old%off_shell = parse_node_get_integer (pn_ival)
pn_ival => parse_node_get_next_ptr (pn_ival, 3)
phs_par_old%t_channel = parse_node_get_integer (pn_ival)
pn_lval => parse_node_get_next_ptr (pn_ival, 3)
lstr = parse_node_get_string (pn_lval)
read (lstr, "(L1)") phs_par_old%keep_nonresonant
if (phs_par_old /= phs_par) then
call msg_message &
("Phase space: discarding old configuration &
&(configuration parameters changed)")
match = .false.; return
end if
end if
match = .true.
end subroutine phs_forest_check_input
@ %def phs_forest_check_input
@ Initialize a specific tree in the forest, using the contents of the
'tree' node. First, count the bincodes, allocate an array and read
them in, and make the tree. Each $t$-channel tree is flipped to
$s$-channel. Then, find mappings and initialize them.
<<PHS forests: procedures>>=
subroutine phs_tree_set (tree, node, model)
type(phs_tree_t), intent(inout) :: tree
type(parse_node_t), intent(in), target :: node
class(model_data_t), intent(in), target :: model
type(parse_node_t), pointer :: node_bincodes, node_mapping, pn_pdg
integer :: n_bincodes, offset
integer(TC), dimension(:), allocatable :: bincode
integer :: b, n_mappings, m
integer(TC) :: k
type(string_t) :: type
integer :: pdg
node_bincodes => parse_node_get_sub_ptr (node, 2)
if (associated (node_bincodes)) then
select case (char (parse_node_get_rule_key (node_bincodes)))
case ("bincodes")
n_bincodes = parse_node_get_n_sub (node_bincodes)
offset = 2
case default
n_bincodes = 0
offset = 1
end select
else
n_bincodes = 0
offset = 2
end if
allocate (bincode (n_bincodes))
do b = 1, n_bincodes
bincode(b) = parse_node_get_integer &
(parse_node_get_sub_ptr (node_bincodes, b))
end do
call phs_tree_from_array (tree, bincode)
call phs_tree_flip_t_to_s_channel (tree)
call phs_tree_canonicalize (tree)
n_mappings = parse_node_get_n_sub (node) - offset
do m = 1, n_mappings
node_mapping => parse_node_get_sub_ptr (node, m + offset)
k = parse_node_get_integer &
(parse_node_get_sub_ptr (node_mapping, 2))
type = parse_node_get_key &
(parse_node_get_sub_ptr (node_mapping, 3))
pn_pdg => parse_node_get_sub_ptr (node_mapping, 4)
select case (char (pn_pdg%get_rule_key ()))
case ("pdg")
pdg = pn_pdg%get_integer ()
case ("negative_pdg")
pdg = - parse_node_get_integer (pn_pdg%get_sub_ptr (2))
end select
call phs_tree_init_mapping (tree, k, type, pdg, model)
end do
end subroutine phs_tree_set
@ %def phs_tree_set
@
\subsection{Preparation}
The trees that we read from file do not carry flavor information.
This is set separately:
The flavor list must be unique for a unique set of masses; if a given
particle can have different flavor, the mass must be degenerate, so we
can choose one of the possible flavor combinations.
<<PHS forests: public>>=
public :: phs_forest_set_flavors
<<PHS forests: procedures>>=
subroutine phs_forest_set_flavors (forest, flv, reshuffle, flv_extra)
type(phs_forest_t), intent(inout) :: forest
type(flavor_t), dimension(:), intent(in) :: flv
integer, intent(in), dimension(:), allocatable, optional :: reshuffle
type(flavor_t), intent(in), optional :: flv_extra
integer :: i, n_flv0
if (present (reshuffle) .and. present (flv_extra)) then
n_flv0 = size (flv)
do i = 1, n_flv0
if (reshuffle(i) <= n_flv0) then
forest%flv(i) = flv (reshuffle(i))
else
forest%flv(i) = flv_extra
end if
end do
else
allocate (forest%flv (size (flv)))
forest%flv = flv
end if
end subroutine phs_forest_set_flavors
@ %def phs_forest_set_flavors
@
<<PHS forests: public>>=
public :: phs_forest_set_momentum_links
<<PHS forests: procedures>>=
subroutine phs_forest_set_momentum_links (forest, list)
type(phs_forest_t), intent(inout) :: forest
integer, intent(in), dimension(:), allocatable :: list
integer :: g, t
do g = 1, size (forest%grove)
do t = 1, size (forest%grove(g)%tree)
associate (tree => forest%grove(g)%tree(t))
call phs_tree_set_momentum_links (tree, list)
!!! call phs_tree_reshuffle_mappings (tree)
end associate
end do
end do
end subroutine phs_forest_set_momentum_links
@ %def phs_forest_set_momentum_links
@ Once the parameter set is fixed, the masses and the widths of the
particles are known and the [[mass_sum]] arrays as well as the mapping
parameters can be computed. Note that order is important: we first
compute the mass sums, then the ordinary mappings. The resonances
obtained here determine the effective masses, which in turn are used
to implement step mappings for resonance decay products that are not
mapped otherwise.
<<PHS forests: public>>=
public :: phs_forest_set_parameters
<<PHS forests: procedures>>=
subroutine phs_forest_set_parameters &
(forest, mapping_defaults, variable_limits)
type(phs_forest_t), intent(inout) :: forest
type(mapping_defaults_t), intent(in) :: mapping_defaults
logical, intent(in) :: variable_limits
integer :: g, t
do g = 1, size (forest%grove)
do t = 1, size (forest%grove(g)%tree)
call phs_tree_set_mass_sum &
(forest%grove(g)%tree(t), forest%flv(forest%n_in+1:))
call phs_tree_set_mapping_parameters (forest%grove(g)%tree(t), &
mapping_defaults, variable_limits)
call phs_tree_set_effective_masses (forest%grove(g)%tree(t))
if (mapping_defaults%step_mapping) then
call phs_tree_set_step_mappings (forest%grove(g)%tree(t), &
mapping_defaults%step_mapping_exp, variable_limits)
end if
end do
end do
end subroutine phs_forest_set_parameters
@ %def phs_forest_set_parameters
@ Generate the particle combination table. Scan all trees and merge
their individual combination tables. At the end, valid entries are
non-zero, and they indicate the indices of a pair of particles to be
combined to a new particle. If a particle is accessible by more than
one tree (this is usual), only keep the first possibility.
<<PHS forests: public>>=
public :: phs_forest_setup_prt_combinations
<<PHS forests: procedures>>=
subroutine phs_forest_setup_prt_combinations (forest)
type(phs_forest_t), intent(inout) :: forest
integer :: g, t
integer, dimension(:,:), allocatable :: tree_prt_combination
forest%prt_combination = 0
allocate (tree_prt_combination (2, size (forest%prt_combination, 2)))
do g = 1, size (forest%grove)
do t = 1, size (forest%grove(g)%tree)
call phs_tree_setup_prt_combinations &
(forest%grove(g)%tree(t), tree_prt_combination)
where (tree_prt_combination /= 0 .and. forest%prt_combination == 0)
forest%prt_combination = tree_prt_combination
end where
end do
end do
end subroutine phs_forest_setup_prt_combinations
@ %def phs_forest_setup_prt_combinations
@
\subsection{Accessing the particle arrays}
Set the incoming particles from the contents of an interaction.
<<PHS forests: public>>=
public :: phs_forest_set_prt_in
<<PHS forests: interfaces>>=
interface phs_forest_set_prt_in
module procedure phs_forest_set_prt_in_int, phs_forest_set_prt_in_mom
end interface phs_forest_set_prt_in
<<PHS forests: procedures>>=
subroutine phs_forest_set_prt_in_int (forest, int, lt_cm_to_lab)
type(phs_forest_t), intent(inout) :: forest
type(interaction_t), intent(in) :: int
type(lorentz_transformation_t), intent(in), optional :: lt_cm_to_lab
if (present (lt_cm_to_lab)) then
call phs_prt_set_momentum (forest%prt_in, &
inverse (lt_cm_to_lab) * &
int%get_momenta (outgoing=.false.))
else
call phs_prt_set_momentum (forest%prt_in, &
int%get_momenta (outgoing=.false.))
end if
associate (m_in => forest%flv(:forest%n_in)%get_mass ())
call phs_prt_set_msq (forest%prt_in, m_in ** 2)
end associate
call phs_prt_set_defined (forest%prt_in)
end subroutine phs_forest_set_prt_in_int
subroutine phs_forest_set_prt_in_mom (forest, mom, lt_cm_to_lab)
type(phs_forest_t), intent(inout) :: forest
type(vector4_t), dimension(size (forest%prt_in)), intent(in) :: mom
type(lorentz_transformation_t), intent(in), optional :: lt_cm_to_lab
if (present (lt_cm_to_lab)) then
call phs_prt_set_momentum (forest%prt_in, &
inverse (lt_cm_to_lab) * mom)
else
call phs_prt_set_momentum (forest%prt_in, mom)
end if
associate (m_in => forest%flv(:forest%n_in)%get_mass ())
call phs_prt_set_msq (forest%prt_in, m_in ** 2)
end associate
call phs_prt_set_defined (forest%prt_in)
end subroutine phs_forest_set_prt_in_mom
@ %def phs_forest_set_prt_in
@ Set the outgoing particles from the contents of an interaction.
<<PHS forests: public>>=
public :: phs_forest_set_prt_out
<<PHS forests: interfaces>>=
interface phs_forest_set_prt_out
module procedure phs_forest_set_prt_out_int, phs_forest_set_prt_out_mom
end interface phs_forest_set_prt_out
<<PHS forests: procedures>>=
subroutine phs_forest_set_prt_out_int (forest, int, lt_cm_to_lab)
type(phs_forest_t), intent(inout) :: forest
type(interaction_t), intent(in) :: int
type(lorentz_transformation_t), intent(in), optional :: lt_cm_to_lab
if (present (lt_cm_to_lab)) then
call phs_prt_set_momentum (forest%prt_out, &
inverse (lt_cm_to_lab) * &
int%get_momenta (outgoing=.true.))
else
call phs_prt_set_momentum (forest%prt_out, &
int%get_momenta (outgoing=.true.))
end if
associate (m_out => forest%flv(forest%n_in+1:)%get_mass ())
call phs_prt_set_msq (forest%prt_out, m_out ** 2)
end associate
call phs_prt_set_defined (forest%prt_out)
end subroutine phs_forest_set_prt_out_int
subroutine phs_forest_set_prt_out_mom (forest, mom, lt_cm_to_lab)
type(phs_forest_t), intent(inout) :: forest
type(vector4_t), dimension(size (forest%prt_out)), intent(in) :: mom
type(lorentz_transformation_t), intent(in), optional :: lt_cm_to_lab
if (present (lt_cm_to_lab)) then
call phs_prt_set_momentum (forest%prt_out, &
inverse (lt_cm_to_lab) * mom)
else
call phs_prt_set_momentum (forest%prt_out, mom)
end if
associate (m_out => forest%flv(forest%n_in+1:)%get_mass ())
call phs_prt_set_msq (forest%prt_out, m_out ** 2)
end associate
call phs_prt_set_defined (forest%prt_out)
end subroutine phs_forest_set_prt_out_mom
@ %def phs_forest_set_prt_out
@ Combine particles as described by the particle combination table.
Particle momentum sums will be calculated only if the resulting
particle is contained in at least one of the trees in the current
forest. The others are kept undefined.
<<PHS forests: public>>=
public :: phs_forest_combine_particles
<<PHS forests: procedures>>=
subroutine phs_forest_combine_particles (forest)
type(phs_forest_t), intent(inout) :: forest
integer :: k
integer, dimension(2) :: kk
do k = 1, size (forest%prt_combination, 2)
kk = forest%prt_combination(:,k)
if (kk(1) /= 0) then
call phs_prt_combine (forest%prt(k), &
forest%prt(kk(1)), forest%prt(kk(2)))
end if
end do
end subroutine phs_forest_combine_particles
@ %def phs_forest_combine_particles
@ Extract the outgoing particles and insert into an interaction.
<<PHS forests: public>>=
public :: phs_forest_get_prt_out
<<PHS forests: procedures>>=
subroutine phs_forest_get_prt_out (forest, int, lt_cm_to_lab)
type(phs_forest_t), intent(in) :: forest
type(interaction_t), intent(inout) :: int
type(lorentz_transformation_t), intent(in), optional :: lt_cm_to_lab
if (present (lt_cm_to_lab)) then
call int%set_momenta (lt_cm_to_lab * &
phs_prt_get_momentum (forest%prt_out), outgoing=.true.)
else
call int%set_momenta (phs_prt_get_momentum (forest%prt_out), &
outgoing=.true.)
end if
end subroutine phs_forest_get_prt_out
@ %def phs_forest_get_prt_out
@ Extract the outgoing particle momenta
<<PHS forests: public>>=
public :: phs_forest_get_momenta_out
<<PHS forests: procedures>>=
function phs_forest_get_momenta_out (forest, lt_cm_to_lab) result (p)
type(phs_forest_t), intent(in) :: forest
type(lorentz_transformation_t), intent(in), optional :: lt_cm_to_lab
type(vector4_t), dimension(size (forest%prt_out)) :: p
p = phs_prt_get_momentum (forest%prt_out)
if (present (lt_cm_to_lab)) p = p * lt_cm_to_lab
end function phs_forest_get_momenta_out
@ %def phs_forest_get_momenta_out
@
\subsection{Find equivalences among phase-space trees}
Scan phase space for equivalences. We generate the complete set of
unique permutations for the given list of outgoing particles, and use
this for scanning equivalences within each grove.
@ We scan all pairs of trees, using all permutations. This implies
that trivial equivalences are included, and equivalences between
different trees are recorded twice. This is intentional.
<<PHS forests: procedures>>=
subroutine phs_grove_set_equivalences (grove, perm_array)
type(phs_grove_t), intent(inout) :: grove
type(permutation_t), dimension(:), intent(in) :: perm_array
type(equivalence_t), pointer :: eq
integer :: t1, t2, i
do t1 = 1, size (grove%tree)
do t2 = 1, size (grove%tree)
SCAN_PERM: do i = 1, size (perm_array)
if (phs_tree_equivalent &
(grove%tree(t1), grove%tree(t2), perm_array(i))) then
call equivalence_list_add &
(grove%equivalence_list, t1, t2, perm_array(i))
eq => grove%equivalence_list%last
call phs_tree_find_msq_permutation &
(grove%tree(t1), grove%tree(t2), eq%perm, &
eq%msq_perm)
call phs_tree_find_angle_permutation &
(grove%tree(t1), grove%tree(t2), eq%perm, &
eq%angle_perm, eq%angle_sig)
end if
end do SCAN_PERM
end do
end do
end subroutine phs_grove_set_equivalences
@ %def phs_grove_set_equivalences
<<PHS forests: public>>=
public :: phs_forest_set_equivalences
<<PHS forests: procedures>>=
subroutine phs_forest_set_equivalences (forest)
type(phs_forest_t), intent(inout) :: forest
type(permutation_t), dimension(:), allocatable :: perm_array
integer :: i
call permutation_array_make &
(perm_array, forest%flv(forest%n_in+1:)%get_pdg ())
do i = 1, size (forest%grove)
call phs_grove_set_equivalences (forest%grove(i), perm_array)
end do
forest%n_equivalences = sum (forest%grove%equivalence_list%length)
end subroutine phs_forest_set_equivalences
@ %def phs_forest_set_equivalences
@
\subsection{Interface for channel equivalences}
Here, we store the equivalence list in the appropriate containers that
the [[phs_base]] module provides. There is one separate list for each
channel.
<<PHS forests: public>>=
public :: phs_forest_get_equivalences
<<PHS forests: procedures>>=
subroutine phs_forest_get_equivalences (forest, channel, azimuthal_dependence)
type(phs_forest_t), intent(in) :: forest
type(phs_channel_t), dimension(:), intent(out) :: channel
logical, intent(in) :: azimuthal_dependence
integer :: n_masses, n_angles
integer :: mode_azimuthal_angle
integer, dimension(:), allocatable :: n_eq
type(equivalence_t), pointer :: eq
integer, dimension(:), allocatable :: perm, mode
integer :: g, c, j, left, right
n_masses = forest%n_masses
n_angles = forest%n_angles
allocate (n_eq (forest%n_trees), source = 0)
allocate (perm (forest%n_dimensions))
allocate (mode (forest%n_dimensions), source = EQ_IDENTITY)
do g = 1, size (forest%grove)
eq => forest%grove(g)%equivalence_list%first
do while (associated (eq))
left = eq%left + forest%grove(g)%tree_count_offset
n_eq(left) = n_eq(left) + 1
eq => eq%next
end do
end do
do c = 1, size (channel)
allocate (channel(c)%eq (n_eq(c)))
do j = 1, n_eq(c)
call channel(c)%eq(j)%init (forest%n_dimensions)
end do
end do
n_eq = 0
if (azimuthal_dependence) then
mode_azimuthal_angle = EQ_IDENTITY
else
mode_azimuthal_angle = EQ_INVARIANT
end if
do g = 1, size (forest%grove)
eq => forest%grove(g)%equivalence_list%first
do while (associated (eq))
left = eq%left + forest%grove(g)%tree_count_offset
right = eq%right + forest%grove(g)%tree_count_offset
do j = 1, n_masses
perm(j) = permute (j, eq%msq_perm)
mode(j) = EQ_IDENTITY
end do
do j = 1, n_angles
perm(n_masses+j) = n_masses + permute (j, eq%angle_perm)
if (j == 1) then
mode(n_masses+j) = mode_azimuthal_angle ! first az. angle
else if (mod(j,2) == 1) then
mode(n_masses+j) = EQ_SYMMETRIC ! other az. angles
else if (eq%angle_sig(j)) then
mode(n_masses+j) = EQ_IDENTITY ! polar angle +
else
mode(n_masses+j) = EQ_INVERT ! polar angle -
end if
end do
n_eq(left) = n_eq(left) + 1
associate (eq_cur => channel(left)%eq(n_eq(left)))
eq_cur%c = right
eq_cur%perm = perm
eq_cur%mode = mode
end associate
eq => eq%next
end do
end do
end subroutine phs_forest_get_equivalences
@ %def phs_forest_get_equivalences
@
\subsection{Phase-space evaluation}
Given one row of the [[x]] parameter array and the corresponding
channel index, compute first all relevant momenta and then recover the
remainder of the [[x]] array, the Jacobians [[phs_factor]], and the
phase-space [[volume]].
The output argument [[ok]] indicates whether this was successful.
<<PHS forests: public>>=
public :: phs_forest_evaluate_selected_channel
<<PHS forests: procedures>>=
subroutine phs_forest_evaluate_selected_channel &
(forest, channel, active, sqrts, x, phs_factor, volume, ok)
type(phs_forest_t), intent(inout) :: forest
integer, intent(in) :: channel
logical, dimension(:), intent(in) :: active
real(default), intent(in) :: sqrts
real(default), dimension(:,:), intent(inout) :: x
real(default), dimension(:), intent(out) :: phs_factor
real(default), intent(out) :: volume
logical, intent(out) :: ok
integer :: g, t
integer(TC) :: k, k_root, k_in
g = forest%grove_lookup (channel)
t = channel - forest%grove(g)%tree_count_offset
call phs_prt_set_undefined (forest%prt)
call phs_prt_set_undefined (forest%prt_out)
k_in = forest%n_tot
do k = 1,forest%n_in
forest%prt(ibset(0,k_in-k)) = forest%prt_in(k)
end do
do k = 1, forest%n_out
call phs_prt_set_msq (forest%prt(ibset(0,k-1)), &
forest%flv(forest%n_in+k)%get_mass () ** 2)
end do
k_root = 2**forest%n_out - 1
select case (forest%n_in)
case (1)
forest%prt(k_root) = forest%prt_in(1)
case (2)
call phs_prt_combine &
(forest%prt(k_root), forest%prt_in(1), forest%prt_in(2))
end select
call phs_tree_compute_momenta_from_x (forest%grove(g)%tree(t), &
forest%prt, phs_factor(channel), volume, sqrts, x(:,channel), ok)
if (ok) then
do k = 1, forest%n_out
forest%prt_out(k) = forest%prt(ibset(0,k-1))
end do
end if
end subroutine phs_forest_evaluate_selected_channel
@ %def phs_forest_evaluate_selected_channel
@ The remainder: recover $x$ values for all channels except for the current
channel.
NOTE: OpenMP not used for the first loop. [[combine_particles]] is not a
channel-local operation.
<<PHS forests: public>>=
public :: phs_forest_evaluate_other_channels
<<PHS forests: procedures>>=
subroutine phs_forest_evaluate_other_channels &
(forest, channel, active, sqrts, x, phs_factor, combine)
type(phs_forest_t), intent(inout) :: forest
integer, intent(in) :: channel
logical, dimension(:), intent(in) :: active
real(default), intent(in) :: sqrts
real(default), dimension(:,:), intent(inout) :: x
real(default), dimension(:), intent(inout) :: phs_factor
logical, intent(in) :: combine
integer :: g, t, ch, n_channel
g = forest%grove_lookup (channel)
t = channel - forest%grove(g)%tree_count_offset
n_channel = forest%n_trees
if (combine) then
do ch = 1, n_channel
if (ch == channel) cycle
if (active(ch)) then
g = forest%grove_lookup(ch)
t = ch - forest%grove(g)%tree_count_offset
call phs_tree_combine_particles &
(forest%grove(g)%tree(t), forest%prt)
end if
end do
end if
!OMP PARALLEL PRIVATE (g,t,ch) SHARED(active,forest,sqrts,x,channel)
!OMP DO SCHEDULE(STATIC)
do ch = 1, n_channel
if (ch == channel) cycle
if (active(ch)) then
g = forest%grove_lookup(ch)
t = ch - forest%grove(g)%tree_count_offset
call phs_tree_compute_x_from_momenta &
(forest%grove(g)%tree(t), &
forest%prt, phs_factor(ch), sqrts, x(:,ch))
end if
end do
!OMP END DO
!OMP END PARALLEL
end subroutine phs_forest_evaluate_other_channels
@ %def phs_forest_evaluate_other_channels
@ The complement: recover one row of the [[x]] array and the
associated Jacobian entry, corresponding to
[[channel]], from incoming and outgoing momenta. Also compute the
phase-space volume.
<<PHS forests: public>>=
public :: phs_forest_recover_channel
<<PHS forests: procedures>>=
subroutine phs_forest_recover_channel &
(forest, channel, sqrts, x, phs_factor, volume)
type(phs_forest_t), intent(inout) :: forest
integer, intent(in) :: channel
real(default), intent(in) :: sqrts
real(default), dimension(:,:), intent(inout) :: x
real(default), dimension(:), intent(inout) :: phs_factor
real(default), intent(out) :: volume
integer :: g, t
integer(TC) :: k, k_in
g = forest%grove_lookup (channel)
t = channel - forest%grove(g)%tree_count_offset
call phs_prt_set_undefined (forest%prt)
k_in = forest%n_tot
forall (k = 1:forest%n_in)
forest%prt(ibset(0,k_in-k)) = forest%prt_in(k)
end forall
forall (k = 1:forest%n_out)
forest%prt(ibset(0,k-1)) = forest%prt_out(k)
end forall
call phs_forest_combine_particles (forest)
call phs_tree_compute_volume &
(forest%grove(g)%tree(t), sqrts, volume)
call phs_tree_compute_x_from_momenta &
(forest%grove(g)%tree(t), &
forest%prt, phs_factor(channel), sqrts, x(:,channel))
end subroutine phs_forest_recover_channel
@ %def phs_forest_recover_channel
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[phs_forests_ut.f90]]>>=
<<File header>>
module phs_forests_ut
use unit_tests
use phs_forests_uti
<<Standard module head>>
<<PHS forests: public test>>
contains
<<PHS forests: test driver>>
end module phs_forests_ut
@ %def phs_forests_ut
@
<<[[phs_forests_uti.f90]]>>=
<<File header>>
module phs_forests_uti
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_12
use lorentz
use flavors
use interactions
use model_data
use mappings
use phs_base
use resonances, only: resonance_history_set_t
use phs_forests
<<Standard module head>>
<<PHS forests: test declarations>>
contains
<<PHS forests: tests>>
end module phs_forests_uti
@ %def phs_forests_ut
@ API: driver for the unit tests below.
<<PHS forests: public test>>=
public :: phs_forests_test
<<PHS forests: test driver>>=
subroutine phs_forests_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<PHS forests: execute tests>>
end subroutine phs_forests_test
@ %def phs_forests_test
@
\subsubsection{Basic universal test}
Write a possible phase-space file for a $2\to 3$ process and make the
corresponding forest, print the forest. Choose some in-particle
momenta and a random-number array and evaluate out-particles and
phase-space factors.
<<PHS forests: execute tests>>=
call test (phs_forest_1, "phs_forest_1", &
"check phs forest setup", &
u, results)
<<PHS forests: test declarations>>=
public :: phs_forest_1
<<PHS forests: tests>>=
subroutine phs_forest_1 (u)
use os_interface
integer, intent(in) :: u
type(phs_forest_t) :: forest
type(phs_channel_t), dimension(:), allocatable :: channel
type(model_data_t), target :: model
type(string_t) :: process_id
type(flavor_t), dimension(5) :: flv
type(string_t) :: filename
type(interaction_t) :: int
integer :: unit_fix
type(mapping_defaults_t) :: mapping_defaults
logical :: found_process, ok
integer :: n_channel, ch, i
logical, dimension(4) :: active = .true.
real(default) :: sqrts = 1000
real(default), dimension(5,4) :: x
real(default), dimension(4) :: factor
real(default) :: volume
write (u, "(A)") "* Test output: PHS forest"
write (u, "(A)") "* Purpose: test PHS forest routines"
write (u, "(A)")
write (u, "(A)") "* Reading model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Create phase-space file 'phs_forest_test.phs'"
write (u, "(A)")
call flv%init ([11, -11, 11, -11, 22], model)
unit_fix = free_unit ()
open (file="phs_forest_test.phs", unit=unit_fix, action="write")
write (unit_fix, *) "process foo"
write (unit_fix, *) 'md5sum_process = "6ABA33BC2927925D0F073B1C1170780A"'
write (unit_fix, *) 'md5sum_model_par = "1A0B151EE6E2DEB92D880320355A3EAB"'
write (unit_fix, *) 'md5sum_phs_config = "B6A8877058809A8BDD54753CDAB83ACE"'
write (unit_fix, *) "sqrts = 100.00000000000000"
write (unit_fix, *) "m_threshold_s = 50.000000000000000"
write (unit_fix, *) "m_threshold_t = 100.00000000000000"
write (unit_fix, *) "off_shell = 2"
write (unit_fix, *) "t_channel = 6"
write (unit_fix, *) "keep_nonresonant = F"
write (unit_fix, *) ""
write (unit_fix, *) " grove"
write (unit_fix, *) " tree 3 7"
write (unit_fix, *) " map 3 s_channel 23"
write (unit_fix, *) " tree 5 7"
write (unit_fix, *) " tree 6 7"
write (unit_fix, *) " grove"
write (unit_fix, *) " tree 9 11"
write (unit_fix, *) " map 9 t_channel 22"
close (unit_fix)
write (u, "(A)")
write (u, "(A)") "* Read phase-space file 'phs_forest_test.phs'"
call syntax_phs_forest_init ()
process_id = "foo"
filename = "phs_forest_test.phs"
call phs_forest_read &
(forest, filename, process_id, 2, 3, model, found_process)
write (u, "(A)")
write (u, "(A)") "* Set parameters, flavors, equiv, momenta"
write (u, "(A)")
call phs_forest_set_flavors (forest, flv)
call phs_forest_set_parameters (forest, mapping_defaults, .false.)
call phs_forest_setup_prt_combinations (forest)
call phs_forest_set_equivalences (forest)
call int%basic_init (2, 0, 3)
call int%set_momentum &
(vector4_moving (500._default, 500._default, 3), 1)
call int%set_momentum &
(vector4_moving (500._default,-500._default, 3), 2)
call phs_forest_set_prt_in (forest, int)
n_channel = 2
x = 0
x(:,n_channel) = [0.3, 0.4, 0.1, 0.9, 0.6]
write (u, "(A)") " Input values:"
write (u, "(3x,5(1x," // FMT_12 // "))") x(:,n_channel)
write (u, "(A)")
write (u, "(A)") "* Evaluating phase space"
call phs_forest_evaluate_selected_channel (forest, &
n_channel, active, sqrts, x, factor, volume, ok)
call phs_forest_evaluate_other_channels (forest, &
n_channel, active, sqrts, x, factor, combine=.true.)
call phs_forest_get_prt_out (forest, int)
write (u, "(A)") " Output values:"
do ch = 1, 4
write (u, "(3x,5(1x," // FMT_12 // "))") x(:,ch)
end do
call int%basic_write (u)
write (u, "(A)") " Factors:"
write (u, "(3x,5(1x," // FMT_12 // "))") factor
write (u, "(A)") " Volume:"
write (u, "(3x,5(1x," // FMT_12 // "))") volume
call phs_forest_write (forest, u)
write (u, "(A)")
write (u, "(A)") "* Compute equivalences"
n_channel = 4
allocate (channel (n_channel))
call phs_forest_get_equivalences (forest, &
channel, .true.)
do i = 1, n_channel
write (u, "(1x,I0,':')", advance = "no") ch
call channel(i)%write (u)
end do
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
call phs_forest_final (forest)
call syntax_phs_forest_final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_forest_1"
end subroutine phs_forest_1
@ %def phs_forest_1
@
\subsubsection{Resonance histories}
Read a suitably nontrivial forest from file and recover the set of
resonance histories.
<<PHS forests: execute tests>>=
call test (phs_forest_2, "phs_forest_2", &
"handle phs forest resonance content", &
u, results)
<<PHS forests: test declarations>>=
public :: phs_forest_2
<<PHS forests: tests>>=
subroutine phs_forest_2 (u)
use os_interface
integer, intent(in) :: u
integer :: unit_fix
type(phs_forest_t) :: forest
type(model_data_t), target :: model
type(string_t) :: process_id
type(string_t) :: filename
logical :: found_process
type(resonance_history_set_t) :: res_set
integer :: i
write (u, "(A)") "* Test output: phs_forest_2"
write (u, "(A)") "* Purpose: test PHS forest routines"
write (u, "(A)")
write (u, "(A)") "* Reading model file"
call model%init_sm_test ()
write (u, "(A)")
write (u, "(A)") "* Create phase-space file 'phs_forest_2.phs'"
write (u, "(A)")
unit_fix = free_unit ()
open (file="phs_forest_2.phs", unit=unit_fix, action="write")
write (unit_fix, *) "process foo"
write (unit_fix, *) 'md5sum_process = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"'
write (unit_fix, *) 'md5sum_model_par = "1A0B151EE6E2DEB92D880320355A3EAB"'
write (unit_fix, *) 'md5sum_phs_config = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"'
write (unit_fix, *) "sqrts = 100.00000000000000"
write (unit_fix, *) "m_threshold_s = 50.000000000000000"
write (unit_fix, *) "m_threshold_t = 100.00000000000000"
write (unit_fix, *) "off_shell = 2"
write (unit_fix, *) "t_channel = 6"
write (unit_fix, *) "keep_nonresonant = F"
write (unit_fix, *) ""
write (unit_fix, *) " grove"
write (unit_fix, *) " tree 3 7"
write (unit_fix, *) " tree 3 7"
write (unit_fix, *) " map 3 s_channel -24"
write (unit_fix, *) " tree 5 7"
write (unit_fix, *) " tree 3 7"
write (unit_fix, *) " map 3 s_channel -24"
write (unit_fix, *) " map 7 s_channel 23"
write (unit_fix, *) " tree 5 7"
write (unit_fix, *) " map 7 s_channel 25"
write (unit_fix, *) " tree 3 11"
write (unit_fix, *) " map 3 s_channel -24"
close (unit_fix)
write (u, "(A)") "* Read phase-space file 'phs_forest_2.phs'"
call syntax_phs_forest_init ()
process_id = "foo"
filename = "phs_forest_2.phs"
call phs_forest_read &
(forest, filename, process_id, 2, 3, model, found_process)
write (u, "(A)")
write (u, "(A)") "* Extract resonance history set"
write (u, "(A)")
call forest%extract_resonance_history_set (res_set)
call res_set%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
call phs_forest_final (forest)
call syntax_phs_forest_final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_forest_2"
end subroutine phs_forest_2
@ %def phs_forest_2
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Finding phase space parameterizations}
If the phase space configuration is not found in the appropriate file,
we should generate one.
The idea is to construct all Feynman diagrams subject to certain
constraints which eliminate everything that is probably irrelevant for
the integration. These Feynman diagrams (cascades) are grouped in
groves by finding equivalence classes related by symmetry and ordered
with respect to their importance (resonances). Finally, the result
(or part of it) is written to file and used for the integration.
This module may eventually disappear and be replaced by CAML code.
In particular, we need here a set of Feynman rules (vertices with
particle codes, but not the factors). Thus, the module works for the
Standard Model only.
Note that this module is stand-alone, it communicates to the main
program only via the generated ASCII phase-space configuration file.
<<[[cascades.f90]]>>=
<<File header>>
module cascades
<<Use kinds>>
use kinds, only: TC, i8, i32
<<Use strings>>
<<Use debug>>
use io_units
use constants, only: one
use format_defs, only: FMT_12, FMT_19
use numeric_utils
use diagnostics
use hashes
use sorting
use physics_defs, only: SCALAR, SPINOR, VECTOR, VECTORSPINOR, TENSOR
use physics_defs, only: UNDEFINED
use model_data
use flavors
use lorentz
use resonances, only: resonance_info_t
use resonances, only: resonance_history_t
use resonances, only: resonance_history_set_t
use phs_forests
<<Standard module head>>
<<Cascades: public>>
<<Cascades: parameters>>
<<Cascades: types>>
<<Cascades: interfaces>>
contains
<<Cascades: procedures>>
end module cascades
@ %def cascades
@
\subsection{The mapping modes}
The valid mapping modes, to be used below. We will make use of the convention
that mappings of internal particles have a positive value. Only for positive
values, the flavor code is propagated when combining cascades.
<<Mapping modes>>=
integer, parameter :: &
& EXTERNAL_PRT = -1, &
& NO_MAPPING = 0, S_CHANNEL = 1, T_CHANNEL = 2, U_CHANNEL = 3, &
& RADIATION = 4, COLLINEAR = 5, INFRARED = 6, &
& STEP_MAPPING_E = 11, STEP_MAPPING_H = 12, &
& ON_SHELL = 99
@ %def EXTERNAL_PRT
@ %def NO_MAPPING S_CHANNEL T_CHANNEL U_CHANNEL
@ %def RADIATION COLLINEAR INFRARED
@ %def STEP_MAPPING_E STEP_MAPPING_H
@ %def ON_SHELL
<<Cascades: parameters>>=
<<Mapping modes>>
@
\subsection{The cascade type}
A cascade is essentially the same as a decay tree (both definitions
may be merged in a later version). It contains a linked tree of
nodes, each of which representing an internal particle. In contrast
to decay trees, each node has a definite particle code. These nodes
need not be modified, therefore we can use pointers and do not have to
copy them. Thus, physically each cascades has only a single node, the
mother particle. However, to be able to compare trees quickly, we
store in addition an array of binary codes which is always sorted in
ascending order. This is accompanied by a corresponding list of
particle codes. The index is the location of the corresponding
cascade in the cascade set, this may be used to access the daughters
directly.
The real mass is the particle mass belonging to the particle code.
The minimal mass is the sum of the real masses of all its daughters;
this is the kinematical cutoff. The effective mass may be zero if the
particle mass is below a certain threshold; it may be the real mass if
the particle is resonant; or it may be some other value.
The logical [[t_channel]] is set if this a $t$-channel line, while
[[initial]] is true only for an initial particle. Note that both
initial particles are also [[t_channel]] by definition, and that they
are distinguished by the direction of the tree: One of them decays
and is the root of the tree, while the other one is one of the leaves.
The cascade is a list of nodes (particles) which are linked via the
[[daughter]] entries. The node is the mother particle of
the decay cascade. Much of the information in the nodes is repeated
in arrays, to be accessible more easily. The arrays will be kept
sorted by binary codes.
The counter [[n_off_shell]] is increased for each internal line that
is neither resonant nor log-enhanced. It is set to zero if the
current line is resonant, since this implies on-shell particle production
and subsequent decay.
The counter [[n_t_channel]] is non-negative once an initial particle
is included in the tree: then, it counts the number of $t$-channel lines.
The [[multiplicity]] is the number of branchings to follow until all
daughters are on-shell. A resonant or non-decaying particle has
multiplicity one. Merging nodes, the multiplicities add unless the
mother is a resonance. An initial or final node has multiplicity
zero.
The arrays correspond to the subnode tree [[tree]] of the current
cascade. PDG codes are stored only for those positions which are
resonant, with the exception of the last entry, i.e., the current node.
Other positions, in particular external legs, are assigned undefined
PDG code.
A cascade is uniquely identified by its tree, the tree of PDG codes,
and the tree of mappings. The tree of resonances is kept only to mask
the PDG tree as described above.
<<Cascades: types>>=
type :: cascade_t
private
! counters
integer :: index = 0
integer :: grove = 0
! status
logical :: active = .false.
logical :: complete = .false.
logical :: incoming = .false.
! this node
integer(TC) :: bincode = 0
type(flavor_t) :: flv
integer :: pdg = UNDEFINED
logical :: is_vector = .false.
real(default) :: m_min = 0
real(default) :: m_rea = 0
real(default) :: m_eff = 0
integer :: mapping = NO_MAPPING
logical :: on_shell = .false.
logical :: resonant = .false.
logical :: log_enhanced = .false.
logical :: t_channel = .false.
! global tree properties
integer :: multiplicity = 0
integer :: internal = 0
integer :: n_off_shell = 0
integer :: n_resonances = 0
integer :: n_log_enhanced = 0
integer :: n_t_channel = 0
integer :: res_hash = 0
! the sub-node tree
integer :: depth = 0
integer(TC), dimension(:), allocatable :: tree
integer, dimension(:), allocatable :: tree_pdg
integer, dimension(:), allocatable :: tree_mapping
logical, dimension(:), allocatable :: tree_resonant
! branch connections
logical :: has_children = .false.
type(cascade_t), pointer :: daughter1 => null ()
type(cascade_t), pointer :: daughter2 => null ()
type(cascade_t), pointer :: mother => null ()
! next in list
type(cascade_t), pointer :: next => null ()
contains
<<Cascades: cascade: TBP>>
end type cascade_t
@ %def cascade_t
<<Cascades: procedures>>=
subroutine cascade_init (cascade, depth)
type(cascade_t), intent(out) :: cascade
integer, intent(in) :: depth
integer, save :: index = 0
index = cascade_index ()
cascade%index = index
cascade%depth = depth
cascade%active = .true.
allocate (cascade%tree (depth))
allocate (cascade%tree_pdg (depth))
allocate (cascade%tree_mapping (depth))
allocate (cascade%tree_resonant (depth))
end subroutine cascade_init
@ %def cascade_init
@ Keep and increment a global index
<<Cascades: procedures>>=
function cascade_index (seed) result (index)
integer :: index
integer, intent(in), optional :: seed
integer, save :: i = 0
if (present (seed)) i = seed
i = i + 1
index = i
end function cascade_index
@ %def cascade_index
@ We need three versions of writing cascades. This goes to the
phase-space file.
For t/u channel mappings, we use the absolute value of the PDG code.
<<Cascades: procedures>>=
subroutine cascade_write_file_format (cascade, model, unit)
type(cascade_t), intent(in) :: cascade
class(model_data_t), intent(in), target :: model
integer, intent(in), optional :: unit
type(flavor_t) :: flv
integer :: u, i
2 format(3x,A,1x,I3,1x,A,1x,I9,1x,'!',1x,A)
u = given_output_unit (unit); if (u < 0) return
call write_reduced (cascade%tree, u)
write (u, "(A)")
do i = 1, cascade%depth
call flv%init (cascade%tree_pdg(i), model)
select case (cascade%tree_mapping(i))
case (NO_MAPPING, EXTERNAL_PRT)
case (S_CHANNEL)
write(u,2) 'map', &
cascade%tree(i), 's_channel', cascade%tree_pdg(i), &
char (flv%get_name ())
case (T_CHANNEL)
write(u,2) 'map', &
cascade%tree(i), 't_channel', abs (cascade%tree_pdg(i)), &
char (flv%get_name ())
case (U_CHANNEL)
write(u,2) 'map', &
cascade%tree(i), 'u_channel', abs (cascade%tree_pdg(i)), &
char (flv%get_name ())
case (RADIATION)
write(u,2) 'map', &
cascade%tree(i), 'radiation', cascade%tree_pdg(i), &
char (flv%get_name ())
case (COLLINEAR)
write(u,2) 'map', &
cascade%tree(i), 'collinear', cascade%tree_pdg(i), &
char (flv%get_name ())
case (INFRARED)
write(u,2) 'map', &
cascade%tree(i), 'infrared ', cascade%tree_pdg(i), &
char (flv%get_name ())
case (ON_SHELL)
write(u,2) 'map', &
cascade%tree(i), 'on_shell ', cascade%tree_pdg(i), &
char (flv%get_name ())
case default
call msg_bug (" Impossible mapping mode encountered")
end select
end do
contains
subroutine write_reduced (array, unit)
integer(TC), dimension(:), intent(in) :: array
integer, intent(in) :: unit
integer :: i
write (u, "(3x,A,1x)", advance="no") "tree"
do i = 1, size (array)
if (decay_level (array(i)) > 1) then
write (u, "(1x,I0)", advance="no") array(i)
end if
end do
end subroutine write_reduced
elemental function decay_level (k) result (l)
integer(TC), intent(in) :: k
integer :: l
integer :: i
l = 0
do i = 0, bit_size(k) - 1
if (btest(k,i)) l = l + 1
end do
end function decay_level
subroutine start_comment (u)
integer, intent(in) :: u
write(u, '(1x,A)', advance='no') '!'
end subroutine start_comment
end subroutine cascade_write_file_format
@ %def cascade_write_file_format
@ This creates metapost source for graphical display:
<<Cascades: procedures>>=
subroutine cascade_write_graph_format (cascade, count, unit)
type(cascade_t), intent(in) :: cascade
integer, intent(in) :: count
integer, intent(in), optional :: unit
integer :: u
integer(TC) :: mask
type(string_t) :: left_str, right_str
u = given_output_unit (unit); if (u < 0) return
mask = 2**((cascade%depth+3)/2) - 1
left_str = ""
right_str = ""
write (u, '(A)') "\begin{minipage}{105pt}"
write (u, '(A)') "\vspace{30pt}"
write (u, '(A)') "\begin{center}"
write (u, '(A)') "\begin{fmfgraph*}(55,55)"
call graph_write (cascade, mask)
write (u, '(A)') "\fmfleft{" // char (extract (left_str, 2)) // "}"
write (u, '(A)') "\fmfright{" // char (extract (right_str, 2)) // "}"
write (u, '(A)') "\end{fmfgraph*}\\"
write (u, '(A,I5,A)') "\fbox{$", count, "$}"
write (u, '(A)') "\end{center}"
write (u, '(A)') "\end{minipage}"
write (u, '(A)') "%"
contains
recursive subroutine graph_write (cascade, mask, reverse)
type(cascade_t), intent(in) :: cascade
integer(TC), intent(in) :: mask
logical, intent(in), optional :: reverse
type(flavor_t) :: anti
logical :: rev
rev = .false.; if (present(reverse)) rev = reverse
if (cascade%has_children) then
if (.not.rev) then
call vertex_write (cascade, cascade%daughter1, mask)
call vertex_write (cascade, cascade%daughter2, mask)
else
call vertex_write (cascade, cascade%daughter2, mask, .true.)
call vertex_write (cascade, cascade%daughter1, mask, .true.)
end if
if (cascade%complete) then
call vertex_write (cascade, cascade%mother, mask, .true.)
write (u, '(A,I0,A)') "\fmfv{d.shape=square}{v0}"
end if
else
if (cascade%incoming) then
anti = cascade%flv%anti ()
call external_write (cascade%bincode, anti%get_tex_name (), &
left_str)
else
call external_write (cascade%bincode, cascade%flv%get_tex_name (), &
right_str)
end if
end if
end subroutine graph_write
recursive subroutine vertex_write (cascade, daughter, mask, reverse)
type(cascade_t), intent(in) :: cascade, daughter
integer(TC), intent(in) :: mask
logical, intent(in), optional :: reverse
integer :: bincode
if (cascade%complete) then
bincode = 0
else
bincode = cascade%bincode
end if
call graph_write (daughter, mask, reverse)
if (daughter%has_children) then
call line_write (bincode, daughter%bincode, daughter%flv, &
mapping=daughter%mapping)
else
call line_write (bincode, daughter%bincode, daughter%flv)
end if
end subroutine vertex_write
subroutine line_write (i1, i2, flv, mapping)
integer(TC), intent(in) :: i1, i2
type(flavor_t), intent(in) :: flv
integer, intent(in), optional :: mapping
integer :: k1, k2
type(string_t) :: prt_type
select case (flv%get_spin_type ())
case (SCALAR); prt_type = "plain"
case (SPINOR); prt_type = "fermion"
case (VECTOR); prt_type = "boson"
case (VECTORSPINOR); prt_type = "fermion"
case (TENSOR); prt_type = "dbl_wiggly"
case default; prt_type = "dashes"
end select
if (flv%is_antiparticle ()) then
k1 = i2; k2 = i1
else
k1 = i1; k2 = i2
end if
if (present (mapping)) then
select case (mapping)
case (S_CHANNEL)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=blue,lab=\sm\blue$" // &
& char (flv%get_tex_name ()) // "$}" // &
& "{v", k1, ",v", k2, "}"
case (T_CHANNEL, U_CHANNEL)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=cyan,lab=\sm\cyan$" // &
& char (flv%get_tex_name ()) // "$}" // &
& "{v", k1, ",v", k2, "}"
case (RADIATION)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=green,lab=\sm\green$" // &
& char (flv%get_tex_name ()) // "$}" // &
& "{v", k1, ",v", k2, "}"
case (COLLINEAR)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=magenta,lab=\sm\magenta$" // &
& char (flv%get_tex_name ()) // "$}" // &
& "{v", k1, ",v", k2, "}"
case (INFRARED)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=red,lab=\sm\red$" // &
& char (flv%get_tex_name ()) // "$}" // &
& "{v", k1, ",v", k2, "}"
case default
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=black}" // &
& "{v", k1, ",v", k2, "}"
end select
else
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& "}" // &
& "{v", k1, ",v", k2, "}"
end if
end subroutine line_write
subroutine external_write (bincode, name, ext_str)
integer(TC), intent(in) :: bincode
type(string_t), intent(in) :: name
type(string_t), intent(inout) :: ext_str
character(len=20) :: str
write (str, '(A2,I0)') ",v", bincode
ext_str = ext_str // trim (str)
write (u, '(A,I0,A,I0,A)') "\fmflabel{\sm$" &
// char (name) &
// "\,(", bincode, ")" &
// "$}{v", bincode, "}"
end subroutine external_write
end subroutine cascade_write_graph_format
@ %def cascade_write_graph_format
@ This is for screen/debugging output:
<<Cascades: procedures>>=
subroutine cascade_write (cascade, unit)
type(cascade_t), intent(in) :: cascade
integer, intent(in), optional :: unit
integer :: u
character(9) :: depth
u = given_output_unit (unit); if (u < 0) return
write (u, "(A,(1x,I7))") 'Cascade #', cascade%index
write (u, "(A,(1x,I7))") ' Grove: #', cascade%grove
write (u, "(A,3(1x,L1))") ' act/cmp/inc: ', &
cascade%active, cascade%complete, cascade%incoming
write (u, "(A,I0)") ' Bincode: ', cascade%bincode
write (u, "(A)", advance="no") ' Flavor: '
call cascade%flv%write (unit)
write (u, "(A,I9)") ' Active flavor:', cascade%pdg
write (u, "(A,L1)") ' Is vector: ', cascade%is_vector
write (u, "(A,3(1x," // FMT_19 // "))") ' Mass (m/r/e): ', &
cascade%m_min, cascade%m_rea, cascade%m_eff
write (u, "(A,I1)") ' Mapping: ', cascade%mapping
write (u, "(A,3(1x,L1))") ' res/log/tch: ', &
cascade%resonant, cascade%log_enhanced, cascade%t_channel
write (u, "(A,(1x,I7))") ' Multiplicity: ', cascade%multiplicity
write (u, "(A,2(1x,I7))") ' n intern/off: ', &
cascade%internal, cascade%n_off_shell
write (u, "(A,3(1x,I7))") ' n res/log/tch:', &
cascade%n_resonances, cascade%n_log_enhanced, cascade%n_t_channel
write (u, "(A,I7)") ' Depth: ', cascade%depth
write (depth, "(I7)") cascade%depth
write (u, "(A," // depth // "(1x,I7))") &
' Tree: ', cascade%tree
write (u, "(A," // depth // "(1x,I7))") &
' Tree(PDG): ', cascade%tree_pdg
write (u, "(A," // depth // "(1x,I7))") &
' Tree(mapping):', cascade%tree_mapping
write (u, "(A," // depth // "(1x,L1))") &
' Tree(res): ', cascade%tree_resonant
if (cascade%has_children) then
write (u, "(A,I7,1x,I7)") ' Daughter1/2: ', &
cascade%daughter1%index, cascade%daughter2%index
end if
if (associated (cascade%mother)) then
write (u, "(A,I7)") ' Mother: ', cascade%mother%index
end if
end subroutine cascade_write
@ %def cascade_write
@
\subsection{Creating new cascades}
This initializes a single-particle cascade (external, final state).
The PDG entry in the tree is set undefined because the cascade is not
resonant. However, the flavor entry is set, so the cascade flavor
is identified nevertheless.
<<Cascades: procedures>>=
subroutine cascade_init_outgoing (cascade, flv, pos, m_thr)
type(cascade_t), intent(out) :: cascade
type(flavor_t), intent(in) :: flv
integer, intent(in) :: pos
real(default), intent(in) :: m_thr
call cascade_init (cascade, 1)
cascade%bincode = ibset (0_TC, pos-1)
cascade%flv = flv
cascade%pdg = cascade%flv%get_pdg ()
cascade%is_vector = flv%get_spin_type () == VECTOR
cascade%m_min = flv%get_mass ()
cascade%m_rea = cascade%m_min
if (cascade%m_rea >= m_thr) then
cascade%m_eff = cascade%m_rea
end if
cascade%on_shell = .true.
cascade%multiplicity = 1
cascade%tree(1) = cascade%bincode
cascade%tree_pdg(1) = cascade%pdg
cascade%tree_mapping(1) = EXTERNAL_PRT
cascade%tree_resonant(1) = .false.
end subroutine cascade_init_outgoing
@ %def cascade_init_outgoing
@ The same for an incoming line:
<<Cascades: procedures>>=
subroutine cascade_init_incoming (cascade, flv, pos, m_thr)
type(cascade_t), intent(out) :: cascade
type(flavor_t), intent(in) :: flv
integer, intent(in) :: pos
real(default), intent(in) :: m_thr
call cascade_init (cascade, 1)
cascade%incoming = .true.
cascade%bincode = ibset (0_TC, pos-1)
cascade%flv = flv%anti ()
cascade%pdg = cascade%flv%get_pdg ()
cascade%is_vector = flv%get_spin_type () == VECTOR
cascade%m_min = flv%get_mass ()
cascade%m_rea = cascade%m_min
if (cascade%m_rea >= m_thr) then
cascade%m_eff = cascade%m_rea
end if
cascade%on_shell = .true.
cascade%n_t_channel = 0
cascade%n_off_shell = 0
cascade%tree(1) = cascade%bincode
cascade%tree_pdg(1) = cascade%pdg
cascade%tree_mapping(1) = EXTERNAL_PRT
cascade%tree_resonant(1) = .false.
end subroutine cascade_init_incoming
@ %def cascade_init_outgoing
@
\subsection{Tools}
This function returns true if the two cascades share no common
external particle. This is a requirement for joining them.
<<Cascades: interfaces>>=
interface operator(.disjunct.)
module procedure cascade_disjunct
end interface
<<Cascades: procedures>>=
function cascade_disjunct (cascade1, cascade2) result (flag)
logical :: flag
type(cascade_t), intent(in) :: cascade1, cascade2
flag = iand (cascade1%bincode, cascade2%bincode) == 0
end function cascade_disjunct
@ %def cascade_disjunct
@ %def .disjunct.
@ Compute a hash code for the resonance pattern of a cascade. We count the
number of times each particle appears as a resonance.
We pack the PDG codes of the resonances in two arrays (s-channel and
t-channel), sort them both, concatenate the results, transfer to
[[i8]] integers, and compute the hash code from this byte stream.
For t/u-channel, we remove the sign for antiparticles since this is not
well-defined.
<<Cascades: procedures>>=
subroutine cascade_assign_resonance_hash (cascade)
type(cascade_t), intent(inout) :: cascade
integer(i8), dimension(1) :: mold
cascade%res_hash = hash (transfer &
([sort (pack (cascade%tree_pdg, &
cascade%tree_resonant)), &
sort (pack (abs (cascade%tree_pdg), &
cascade%tree_mapping == T_CHANNEL .or. &
cascade%tree_mapping == U_CHANNEL))], &
mold))
end subroutine cascade_assign_resonance_hash
@ %def cascade_assign_resonance_hash
@
\subsection{Hash entries for cascades}
We will set up a hash array which contains keys of and pointers to
cascades. We hold a list of cascade (pointers) within each bucket.
This is not for collision resolution, but for keeping similar, but
unequal cascades together.
<<Cascades: types>>=
type :: cascade_p
type(cascade_t), pointer :: cascade => null ()
type(cascade_p), pointer :: next => null ()
end type cascade_p
@ %def cascade_p
@ Here is the bucket or hash entry type:
<<Cascades: types>>=
type :: hash_entry_t
integer(i32) :: hashval = 0
integer(i8), dimension(:), allocatable :: key
type(cascade_p), pointer :: first => null ()
type(cascade_p), pointer :: last => null ()
end type hash_entry_t
@ %def hash_entry_t
<<Cascades: public>>=
public :: hash_entry_init
<<Cascades: procedures>>=
subroutine hash_entry_init (entry, entry_in)
type(hash_entry_t), intent(out) :: entry
type(hash_entry_t), intent(in) :: entry_in
type(cascade_p), pointer :: casc_iter, casc_copy
entry%hashval = entry_in%hashval
entry%key = entry_in%key
casc_iter => entry_in%first
do while (associated (casc_iter))
allocate (casc_copy)
casc_copy = casc_iter
casc_copy%next => null ()
if (associated (entry%first)) then
entry%last%next => casc_copy
else
entry%first => casc_copy
end if
entry%last => casc_copy
casc_iter => casc_iter%next
end do
end subroutine hash_entry_init
@ %def hash_entry_init
@ Finalize: just deallocate the list; the contents are just pointers.
<<Cascades: procedures>>=
subroutine hash_entry_final (hash_entry)
type(hash_entry_t), intent(inout) :: hash_entry
type(cascade_p), pointer :: current
do while (associated (hash_entry%first))
current => hash_entry%first
hash_entry%first => current%next
deallocate (current)
end do
end subroutine hash_entry_final
@ %def hash_entry_final
@ Output: concise format for debugging, just list cascade indices.
<<Cascades: procedures>>=
subroutine hash_entry_write (hash_entry, unit)
type(hash_entry_t), intent(in) :: hash_entry
integer, intent(in), optional :: unit
type(cascade_p), pointer :: current
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)", advance="no") "Entry:"
do i = 1, size (hash_entry%key)
write (u, "(1x,I0)", advance="no") hash_entry%key(i)
end do
write (u, "(1x,A)", advance="no") "->"
current => hash_entry%first
do while (associated (current))
write (u, "(1x,I7)", advance="no") current%cascade%index
current => current%next
end do
write (u, *)
end subroutine hash_entry_write
@ %def hash_entry_write
@ This function adds a cascade pointer to the bucket. If [[ok]] is
present, check first if it is already there and return failure if yes.
If [[cascade_ptr]] is also present, set it to the current cascade if
successful. If not, set it to the cascade that is already there.
<<Cascades: procedures>>=
subroutine hash_entry_add_cascade_ptr (hash_entry, cascade, ok, cascade_ptr)
type(hash_entry_t), intent(inout) :: hash_entry
type(cascade_t), intent(in), target :: cascade
logical, intent(out), optional :: ok
type(cascade_t), optional, pointer :: cascade_ptr
type(cascade_p), pointer :: current
if (present (ok)) then
call hash_entry_check_cascade (hash_entry, cascade, ok, cascade_ptr)
if (.not. ok) return
end if
allocate (current)
current%cascade => cascade
if (associated (hash_entry%last)) then
hash_entry%last%next => current
else
hash_entry%first => current
end if
hash_entry%last => current
end subroutine hash_entry_add_cascade_ptr
@ %def hash_entry_add_cascade_ptr
@ This function checks whether a cascade is already in the bucket.
For incomplete cascades, we look for an exact match. It should suffice
to verify the tree, the PDG codes, and the mapping modes. This is the
information that is written to the phase space file.
For complete cascades, we ignore the PDG code at positions with
mappings infrared, collinear, or t/u-channel. Thus a cascade which is
distinguished only by PDG code at such places, is flagged existent.
If the convention is followed that light particles come before heavier
ones (in the model definition), this ensures that the lightest
particle is kept in the appropriate place, corresponding to the
strongest peak.
For external cascades (incoming/outgoing) we take the PDG code into
account even though it is zeroed in the PDG-code tree.
<<Cascades: procedures>>=
subroutine hash_entry_check_cascade (hash_entry, cascade, ok, cascade_ptr)
type(hash_entry_t), intent(in), target :: hash_entry
type(cascade_t), intent(in), target :: cascade
logical, intent(out) :: ok
type(cascade_t), optional, pointer :: cascade_ptr
type(cascade_p), pointer :: current
integer, dimension(:), allocatable :: tree_pdg
ok = .true.
allocate (tree_pdg (size (cascade%tree_pdg)))
if (cascade%complete) then
where (cascade%tree_mapping == INFRARED .or. &
cascade%tree_mapping == COLLINEAR .or. &
cascade%tree_mapping == T_CHANNEL .or. &
cascade%tree_mapping == U_CHANNEL)
tree_pdg = 0
elsewhere
tree_pdg = cascade%tree_pdg
end where
else
tree_pdg = cascade%tree_pdg
end if
current => hash_entry%first
do while (associated (current))
if (current%cascade%depth == cascade%depth) then
if (all (current%cascade%tree == cascade%tree)) then
if (all (current%cascade%tree_mapping == cascade%tree_mapping)) &
then
if (all (current%cascade%tree_pdg .match. tree_pdg)) then
if (present (cascade_ptr)) cascade_ptr => current%cascade
ok = .false.; return
end if
end if
end if
end if
current => current%next
end do
if (present (cascade_ptr)) cascade_ptr => cascade
end subroutine hash_entry_check_cascade
@ %def hash_entry_check_cascade
@ For PDG codes, we specify that the undefined code matches any code.
This is already defined for flavor objects, but here we need it for
the codes themselves.
<<Cascades: interfaces>>=
interface operator(.match.)
module procedure pdg_match
end interface
<<Cascades: procedures>>=
elemental function pdg_match (pdg1, pdg2) result (flag)
logical :: flag
integer(TC), intent(in) :: pdg1, pdg2
select case (pdg1)
case (0)
flag = .true.
case default
select case (pdg2)
case (0)
flag = .true.
case default
flag = pdg1 == pdg2
end select
end select
end function pdg_match
@ %def .match.
@
\subsection{The cascade set}
The cascade set will later be transformed into the decay forest. It
is set up as a linked list. In addition to the usual [[first]] and
[[last]] pointers, there is a [[first_t]] pointer which points to the
first t-channel cascade (after all s-channel cascades), and a
[[first_k]] pointer which points to the first final cascade (with a
keystone).
As an auxiliary device, the object contains a hash array with
associated parameters where an additional pointer is stored for each
cascade. The keys are made from the relevant cascade data. This hash
is used for fast detection (and thus avoidance) of double entries in
the cascade list.
<<Cascades: public>>=
public :: cascade_set_t
<<Cascades: types>>=
type :: cascade_set_t
private
class(model_data_t), pointer :: model
integer :: n_in, n_out, n_tot
type(flavor_t), dimension(:,:), allocatable :: flv
integer :: depth_out, depth_tot
real(default) :: sqrts = 0
real(default) :: m_threshold_s = 0
real(default) :: m_threshold_t = 0
integer :: off_shell = 0
integer :: t_channel = 0
logical :: keep_nonresonant
integer :: n_groves = 0
! The cascade list
type(cascade_t), pointer :: first => null ()
type(cascade_t), pointer :: last => null ()
type(cascade_t), pointer :: first_t => null ()
type(cascade_t), pointer :: first_k => null ()
! The hashtable
integer :: n_entries = 0
real :: fill_ratio = 0
integer :: n_entries_max = 0
integer(i32) :: mask = 0
logical :: fatal_beam_decay = .true.
type(hash_entry_t), dimension(:), allocatable :: entry
end type cascade_set_t
@ %def cascade_set_t
@
<<Cascades: public>>=
interface cascade_set_init
module procedure cascade_set_init_base
module procedure cascade_set_init_from_cascade
end interface
@ %def cascade_set_init
@ This might be broken. Test before using.
<<Cascades: procedures>>=
subroutine cascade_set_init_from_cascade (cascade_set, cascade_set_in)
type(cascade_set_t), intent(out) :: cascade_set
type(cascade_set_t), intent(in), target :: cascade_set_in
type(cascade_t), pointer :: casc_iter, casc_copy
cascade_set%model => cascade_set_in%model
cascade_set%n_in = cascade_set_in%n_in
cascade_set%n_out = cascade_set_in%n_out
cascade_set%n_tot = cascade_set_in%n_tot
cascade_set%flv = cascade_set_in%flv
cascade_set%depth_out = cascade_set_in%depth_out
cascade_set%depth_tot = cascade_set_in%depth_tot
cascade_set%sqrts = cascade_set_in%sqrts
cascade_set%m_threshold_s = cascade_set_in%m_threshold_s
cascade_set%m_threshold_t = cascade_set_in%m_threshold_t
cascade_set%off_shell = cascade_set_in%off_shell
cascade_set%t_channel = cascade_set_in%t_channel
cascade_set%keep_nonresonant = cascade_set_in%keep_nonresonant
cascade_set%n_groves = cascade_set_in%n_groves
casc_iter => cascade_set_in%first
do while (associated (casc_iter))
allocate (casc_copy)
casc_copy = casc_iter
casc_copy%next => null ()
if (associated (cascade_set%first)) then
cascade_set%last%next => casc_copy
else
cascade_set%first => casc_copy
end if
cascade_set%last => casc_copy
casc_iter => casc_iter%next
end do
cascade_set%n_entries = cascade_set_in%n_entries
cascade_set%fill_ratio = cascade_set_in%fill_ratio
cascade_set%n_entries_max = cascade_set_in%n_entries_max
cascade_set%mask = cascade_set_in%mask
cascade_set%fatal_beam_decay = cascade_set_in%fatal_beam_decay
allocate (cascade_set%entry (0:cascade_set%mask))
cascade_set%entry = cascade_set_in%entry
end subroutine cascade_set_init_from_cascade
@ %def cascade_set_init_from_cascade
@ Return true if there are cascades which are active and complete, so
the phase space file would be nonempty.
<<Cascades: public>>=
public :: cascade_set_is_valid
<<Cascades: procedures>>=
function cascade_set_is_valid (cascade_set) result (flag)
logical :: flag
type(cascade_set_t), intent(in) :: cascade_set
type(cascade_t), pointer :: cascade
flag = .false.
cascade => cascade_set%first_k
do while (associated (cascade))
if (cascade%active .and. cascade%complete) then
flag = .true.
return
end if
cascade => cascade%next
end do
end function cascade_set_is_valid
@ %def cascade_set_is_valid
@ The initializer sets up the hash table with some initial size
guessed by looking at the number of external particles. We choose 256
for 3 external particles and a factor of 4 for each additional
particle, limited at $2^{30}$=1G.
Note: the explicit initialization loop might be avoided (ELEMENTAL),
but a bug in nagfor 5.3.2 prevents this.
<<Cascades: parameters>>=
real, parameter, public :: CASCADE_SET_FILL_RATIO = 0.1
<<Cascades: procedures>>=
subroutine cascade_set_init_base (cascade_set, model, n_in, n_out, phs_par, &
fatal_beam_decay, flv)
type(cascade_set_t), intent(out) :: cascade_set
class(model_data_t), intent(in), target :: model
integer, intent(in) :: n_in, n_out
type(phs_parameters_t), intent(in) :: phs_par
logical, intent(in) :: fatal_beam_decay
type(flavor_t), dimension(:,:), intent(in), optional :: flv
integer :: size_guess
integer :: i, j
cascade_set%model => model
cascade_set%n_in = n_in
cascade_set%n_out = n_out
cascade_set%n_tot = n_in + n_out
if (present (flv)) then
allocate (cascade_set%flv (size (flv, 1), size (flv, 2)))
do i = 1, size (flv, 2)
do j = 1, size (flv, 1)
call cascade_set%flv(j,i)%init (flv(j,i)%get_pdg (), model)
end do
end do
end if
select case (n_in)
case (1); cascade_set%depth_out = 2 * n_out - 3
case (2); cascade_set%depth_out = 2 * n_out - 1
end select
cascade_set%depth_tot = 2 * cascade_set%n_tot - 3
cascade_set%sqrts = phs_par%sqrts
cascade_set%m_threshold_s = phs_par%m_threshold_s
cascade_set%m_threshold_t = phs_par%m_threshold_t
cascade_set%off_shell = phs_par%off_shell
cascade_set%t_channel = phs_par%t_channel
cascade_set%keep_nonresonant = phs_par%keep_nonresonant
cascade_set%fill_ratio = CASCADE_SET_FILL_RATIO
size_guess = ishft (256, min (2 * (cascade_set%n_tot - 3), 22))
cascade_set%n_entries_max = size_guess * cascade_set%fill_ratio
cascade_set%mask = size_guess - 1
allocate (cascade_set%entry (0:cascade_set%mask))
cascade_set%fatal_beam_decay = fatal_beam_decay
end subroutine cascade_set_init_base
@ %def cascade_set_init_base
@ The finalizer has to delete both the hash and the list.
<<Cascades: public>>=
public :: cascade_set_final
<<Cascades: procedures>>=
subroutine cascade_set_final (cascade_set)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), pointer :: current
integer :: i
if (allocated (cascade_set%entry)) then
do i = 0, cascade_set%mask
call hash_entry_final (cascade_set%entry(i))
end do
deallocate (cascade_set%entry)
end if
do while (associated (cascade_set%first))
current => cascade_set%first
cascade_set%first => cascade_set%first%next
deallocate (current)
end do
end subroutine cascade_set_final
@ %def cascade_set_final
@ Write the process in ASCII format, in columns that are headed by the
corresponding bincode.
<<Cascades: public>>=
public :: cascade_set_write_process_bincode_format
<<Cascades: procedures>>=
subroutine cascade_set_write_process_bincode_format (cascade_set, unit)
type(cascade_set_t), intent(in), target :: cascade_set
integer, intent(in), optional :: unit
integer, dimension(:), allocatable :: bincode, field_width
integer :: n_in, n_out, n_tot, n_flv
integer :: u, f, i, bc
character(20) :: str
type(string_t) :: fmt_head
type(string_t), dimension(:), allocatable :: fmt_proc
u = given_output_unit (unit); if (u < 0) return
if (.not. allocated (cascade_set%flv)) return
write (u, "('!',1x,A)") "List of subprocesses with particle bincodes:"
n_in = cascade_set%n_in
n_out = cascade_set%n_out
n_tot = cascade_set%n_tot
n_flv = size (cascade_set%flv, 2)
allocate (bincode (n_tot), field_width (n_tot), fmt_proc (n_tot))
bc = 1
do i = 1, n_out
bincode(n_in + i) = bc
bc = 2 * bc
end do
do i = n_in, 1, -1
bincode(i) = bc
bc = 2 * bc
end do
do i = 1, n_tot
write (str, "(I0)") bincode(i)
field_width(i) = len_trim (str)
do f = 1, n_flv
field_width(i) = max (field_width(i), &
len (cascade_set%flv(i,f)%get_name ()))
end do
end do
fmt_head = "('!'"
do i = 1, n_tot
fmt_head = fmt_head // ",1x,"
fmt_proc(i) = "(1x,"
write (str, "(I0)") field_width(i)
fmt_head = fmt_head // "I" // trim(str)
fmt_proc(i) = fmt_proc(i) // "A" // trim(str)
if (i == n_in) then
fmt_head = fmt_head // ",1x,' '"
end if
end do
do i = 1, n_tot
fmt_proc(i) = fmt_proc(i) // ")"
end do
fmt_head = fmt_head // ")"
write (u, char (fmt_head)) bincode
do f = 1, n_flv
write (u, "('!')", advance="no")
do i = 1, n_tot
write (u, char (fmt_proc(i)), advance="no") &
char (cascade_set%flv(i,f)%get_name ())
if (i == n_in) write (u, "(1x,'=>')", advance="no")
end do
write (u, *)
end do
write (u, char (fmt_head)) bincode
end subroutine cascade_set_write_process_bincode_format
@ %def cascade_set_write_process_tex_format
@ Write the process as a \LaTeX\ expression.
<<Cascades: procedures>>=
subroutine cascade_set_write_process_tex_format (cascade_set, unit)
type(cascade_set_t), intent(in), target :: cascade_set
integer, intent(in), optional :: unit
integer :: u, f, i
u = given_output_unit (unit); if (u < 0) return
if (.not. allocated (cascade_set%flv)) return
write (u, "(A)") "\begin{align*}"
do f = 1, size (cascade_set%flv, 2)
do i = 1, cascade_set%n_in
if (i > 1) write (u, "(A)", advance="no") "\quad "
write (u, "(A)", advance="no") &
char (cascade_set%flv(i,f)%get_tex_name ())
end do
write (u, "(A)", advance="no") "\quad &\to\quad "
do i = cascade_set%n_in + 1, cascade_set%n_tot
if (i > cascade_set%n_in + 1) write (u, "(A)", advance="no") "\quad "
write (u, "(A)", advance="no") &
char (cascade_set%flv(i,f)%get_tex_name ())
end do
if (f < size (cascade_set%flv, 2)) then
write (u, "(A)") "\\"
else
write (u, "(A)") ""
end if
end do
write (u, "(A)") "\end{align*}"
end subroutine cascade_set_write_process_tex_format
@ %def cascade_set_write_process_tex_format
@ Three output routines: phase-space file, graph source code, and
screen output.
This version generates the phase space file. It deals only with
complete cascades.
<<Cascades: public>>=
public :: cascade_set_write_file_format
<<Cascades: procedures>>=
subroutine cascade_set_write_file_format (cascade_set, unit)
type(cascade_set_t), intent(in), target :: cascade_set
integer, intent(in), optional :: unit
type(cascade_t), pointer :: cascade
integer :: u, grove, count
logical :: first_in_grove
u = given_output_unit (unit); if (u < 0) return
count = 0
do grove = 1, cascade_set%n_groves
first_in_grove = .true.
cascade => cascade_set%first_k
do while (associated (cascade))
if (cascade%active .and. cascade%complete) then
if (cascade%grove == grove) then
if (first_in_grove) then
first_in_grove = .false.
write (u, "(A)")
write (u, "(1x,'!',1x,A,1x,I0,A)", advance='no') &
'Multiplicity =', cascade%multiplicity, ","
select case (cascade%n_resonances)
case (0)
write (u, '(1x,A)', advance='no') 'no resonances, '
case (1)
write (u, '(1x,A)', advance='no') '1 resonance, '
case default
write (u, '(1x,I0,1x,A)', advance='no') &
cascade%n_resonances, 'resonances, '
end select
write (u, '(1x,I0,1x,A)', advance='no') &
cascade%n_log_enhanced, 'logs, '
write (u, '(1x,I0,1x,A)', advance='no') &
cascade%n_off_shell, 'off-shell, '
select case (cascade%n_t_channel)
case (0); write (u, '(1x,A)') 's-channel graph'
case (1); write (u, '(1x,A)') '1 t-channel line'
case default
write(u,'(1x,I0,1x,A)') &
cascade%n_t_channel, 't-channel lines'
end select
write (u, '(1x,A,I0)') 'grove #', grove
end if
count = count + 1
write (u, "(1x,'!',1x,A,I0)") "Channel #", count
call cascade_write_file_format (cascade, cascade_set%model, u)
end if
end if
cascade => cascade%next
end do
end do
end subroutine cascade_set_write_file_format
@ %def cascade_set_write_file_format
@ This is the graph output format, the driver-file
<<Cascades: public>>=
public :: cascade_set_write_graph_format
<<Cascades: procedures>>=
subroutine cascade_set_write_graph_format &
(cascade_set, filename, process_id, unit)
type(cascade_set_t), intent(in), target :: cascade_set
type(string_t), intent(in) :: filename, process_id
integer, intent(in), optional :: unit
type(cascade_t), pointer :: cascade
integer :: u, grove, count, pgcount
logical :: first_in_grove
u = given_output_unit (unit); if (u < 0) return
write (u, '(A)') "\documentclass[10pt]{article}"
write (u, '(A)') "\usepackage{amsmath}"
write (u, '(A)') "\usepackage{feynmp}"
write (u, '(A)') "\usepackage{url}"
write (u, '(A)') "\usepackage{color}"
write (u, *)
write (u, '(A)') "\textwidth 18.5cm"
write (u, '(A)') "\evensidemargin -1.5cm"
write (u, '(A)') "\oddsidemargin -1.5cm"
write (u, *)
write (u, '(A)') "\newcommand{\blue}{\color{blue}}"
write (u, '(A)') "\newcommand{\green}{\color{green}}"
write (u, '(A)') "\newcommand{\red}{\color{red}}"
write (u, '(A)') "\newcommand{\magenta}{\color{magenta}}"
write (u, '(A)') "\newcommand{\cyan}{\color{cyan}}"
write (u, '(A)') "\newcommand{\sm}{\footnotesize}"
write (u, '(A)') "\setlength{\parindent}{0pt}"
write (u, '(A)') "\setlength{\parsep}{20pt}"
write (u, *)
write (u, '(A)') "\begin{document}"
write (u, '(A)') "\begin{fmffile}{" // char (filename) // "}"
write (u, '(A)') "\fmfcmd{color magenta; magenta = red + blue;}"
write (u, '(A)') "\fmfcmd{color cyan; cyan = green + blue;}"
write (u, '(A)') "\begin{fmfshrink}{0.5}"
write (u, '(A)') "\begin{flushleft}"
write (u, *)
write (u, '(A)') "\noindent" // &
& "\textbf{\large\texttt{WHIZARD} phase space channels}" // &
& "\hfill\today"
write (u, *)
write (u, '(A)') "\vspace{10pt}"
write (u, '(A)') "\noindent" // &
& "\textbf{Process:} \url{" // char (process_id) // "}"
call cascade_set_write_process_tex_format (cascade_set, u)
write (u, *)
write (u, '(A)') "\noindent" // &
& "\textbf{Note:} These are pseudo Feynman graphs that "
write (u, '(A)') "visualize phase-space parameterizations " // &
& "(``integration channels''). "
write (u, '(A)') "They do \emph{not} indicate Feynman graphs used for the " // &
& "matrix element."
write (u, *)
write (u, '(A)') "\textbf{Color code:} " // &
& "{\blue resonance,} " // &
& "{\cyan t-channel,} " // &
& "{\green radiation,} "
write (u, '(A)') "{\red infrared,} " // &
& "{\magenta collinear,} " // &
& "external/off-shell"
write (u, *)
write (u, '(A)') "\noindent" // &
& "\textbf{Black square:} Keystone, indicates ordering of " // &
& "phase space parameters."
write (u, *)
write (u, '(A)') "\vspace{-20pt}"
count = 0
pgcount = 0
do grove = 1, cascade_set%n_groves
first_in_grove = .true.
cascade => cascade_set%first
do while (associated (cascade))
if (cascade%active .and. cascade%complete) then
if (cascade%grove == grove) then
if (first_in_grove) then
first_in_grove = .false.
write (u, *)
write (u, '(A)') "\vspace{20pt}"
write (u, '(A)') "\begin{tabular}{l}"
write (u, '(A,I5,A)') &
& "\fbox{\bf Grove \boldmath$", grove, "$} \\[10pt]"
write (u, '(A,I1,A)') "Multiplicity: ", &
cascade%multiplicity, "\\"
write (u, '(A,I1,A)') "Resonances: ", &
cascade%n_resonances, "\\"
write (u, '(A,I1,A)') "Log-enhanced: ", &
cascade%n_log_enhanced, "\\"
write (u, '(A,I1,A)') "Off-shell: ", &
cascade%n_off_shell, "\\"
write (u, '(A,I1,A)') "t-channel: ", &
cascade%n_t_channel, ""
write (u, '(A)') "\end{tabular}"
end if
count = count + 1
call cascade_write_graph_format (cascade, count, unit)
if (pgcount >= 250) then
write (u, '(A)') "\clearpage"
pgcount = 0
end if
end if
end if
cascade => cascade%next
end do
end do
write (u, '(A)') "\end{flushleft}"
write (u, '(A)') "\end{fmfshrink}"
write (u, '(A)') "\end{fmffile}"
write (u, '(A)') "\end{document}"
end subroutine cascade_set_write_graph_format
@ %def cascade_set_write_graph_format
@ This is for screen output and debugging:
<<Cascades: public>>=
public :: cascade_set_write
<<Cascades: procedures>>=
subroutine cascade_set_write (cascade_set, unit, active_only, complete_only)
type(cascade_set_t), intent(in), target :: cascade_set
integer, intent(in), optional :: unit
logical, intent(in), optional :: active_only, complete_only
logical :: active, complete
type(cascade_t), pointer :: cascade
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
active = .true.; if (present (active_only)) active = active_only
complete = .false.; if (present (complete_only)) complete = complete_only
write (u, "(A)") "Cascade set:"
write (u, "(3x,A)", advance="no") "Model:"
if (associated (cascade_set%model)) then
write (u, "(1x,A)") char (cascade_set%model%get_name ())
else
write (u, "(1x,A)") "[none]"
end if
write (u, "(3x,A)", advance="no") "n_in/out/tot ="
write (u, "(3(1x,I7))") &
cascade_set%n_in, cascade_set%n_out, cascade_set%n_tot
write (u, "(3x,A)", advance="no") "depth_out/tot ="
write (u, "(2(1x,I7))") cascade_set%depth_out, cascade_set%depth_tot
write (u, "(3x,A)", advance="no") "mass thr(s/t) ="
write (u, "(2(1x," // FMT_19 // "))") &
cascade_set%m_threshold_s, cascade_set%m_threshold_t
write (u, "(3x,A)", advance="no") "off shell ="
write (u, "(1x,I7)") cascade_set%off_shell
write (u, "(3x,A)", advance="no") "keep_nonreson ="
write (u, "(1x,L1)") cascade_set%keep_nonresonant
write (u, "(3x,A)", advance="no") "n_groves ="
write (u, "(1x,I7)") cascade_set%n_groves
write (u, "(A)")
write (u, "(A)") "Cascade list:"
if (associated (cascade_set%first)) then
cascade => cascade_set%first
do while (associated (cascade))
if (active .and. .not. cascade%active) cycle
if (complete .and. .not. cascade%complete) cycle
call cascade_write (cascade, unit)
cascade => cascade%next
end do
else
write (u, "(A)") "[empty]"
end if
write (u, "(A)") "Hash array"
write (u, "(3x,A)", advance="no") "n_entries ="
write (u, "(1x,I7)") cascade_set%n_entries
write (u, "(3x,A)", advance="no") "fill_ratio ="
write (u, "(1x," // FMT_12 // ")") cascade_set%fill_ratio
write (u, "(3x,A)", advance="no") "n_entries_max ="
write (u, "(1x,I7)") cascade_set%n_entries_max
write (u, "(3x,A)", advance="no") "mask ="
write (u, "(1x,I0)") cascade_set%mask
do i = 0, ubound (cascade_set%entry, 1)
if (allocated (cascade_set%entry(i)%key)) then
write (u, "(1x,I7)") i
call hash_entry_write (cascade_set%entry(i), u)
end if
end do
end subroutine cascade_set_write
@ %def cascade_set_write
@
\subsection{Adding cascades}
Add a deep copy of a cascade to the set. The copy has all content of the
original, but the pointers are nullified. We do not care whether insertion
was successful or not. The pointer argument, if present, is assigned to the
input cascade, or to the hash entry if it is already present.
The procedure is recursive: any daughter or mother entries are also
deep-copied and added to the cascade set before the current copy is added.
<<Cascades: procedures>>=
recursive subroutine cascade_set_add_copy &
(cascade_set, cascade_in, cascade_ptr)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in) :: cascade_in
type(cascade_t), optional, pointer :: cascade_ptr
type(cascade_t), pointer :: cascade
logical :: ok
allocate (cascade)
cascade = cascade_in
if (associated (cascade_in%daughter1)) call cascade_set_add_copy &
(cascade_set, cascade_in%daughter1, cascade%daughter1)
if (associated (cascade_in%daughter2)) call cascade_set_add_copy &
(cascade_set, cascade_in%daughter2, cascade%daughter2)
if (associated (cascade_in%mother)) call cascade_set_add_copy &
(cascade_set, cascade_in%mother, cascade%mother)
cascade%next => null ()
call cascade_set_add (cascade_set, cascade, ok, cascade_ptr)
if (.not. ok) deallocate (cascade)
end subroutine cascade_set_add_copy
@ %def cascade_set_add_copy
@ Add a cascade to the set. This does not deep-copy. We first try to insert
it in the hash array. If successful, add it to the list. Failure indicates
that it is already present, and we drop it.
The hash key is built solely from the tree array, so neither particle
codes nor resonances count, just topology.
Technically, hash and list receive only pointers, so the cascade can
be considered as being in either of both. We treat it as part of the
list.
<<Cascades: procedures>>=
subroutine cascade_set_add (cascade_set, cascade, ok, cascade_ptr)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in), target :: cascade
logical, intent(out) :: ok
type(cascade_t), optional, pointer :: cascade_ptr
integer(i8), dimension(1) :: mold
call cascade_set_hash_insert &
(cascade_set, transfer (cascade%tree, mold), cascade, ok, cascade_ptr)
if (ok) call cascade_set_list_add (cascade_set, cascade)
end subroutine cascade_set_add
@ %def cascade_set_add
@ Add a new cascade to the list:
<<Cascades: procedures>>=
subroutine cascade_set_list_add (cascade_set, cascade)
type(cascade_set_t), intent(inout) :: cascade_set
type(cascade_t), intent(in), target :: cascade
if (associated (cascade_set%last)) then
cascade_set%last%next => cascade
else
cascade_set%first => cascade
end if
cascade_set%last => cascade
end subroutine cascade_set_list_add
@ %def cascade_set_list_add
@ Add a cascade entry to the hash array:
<<Cascades: procedures>>=
subroutine cascade_set_hash_insert &
(cascade_set, key, cascade, ok, cascade_ptr)
type(cascade_set_t), intent(inout), target :: cascade_set
integer(i8), dimension(:), intent(in) :: key
type(cascade_t), intent(in), target :: cascade
logical, intent(out) :: ok
type(cascade_t), optional, pointer :: cascade_ptr
integer(i32) :: h
if (cascade_set%n_entries >= cascade_set%n_entries_max) &
call cascade_set_hash_expand (cascade_set)
h = hash (key)
call cascade_set_hash_insert_rec &
(cascade_set, h, h, key, cascade, ok, cascade_ptr)
end subroutine cascade_set_hash_insert
@ %def cascade_set_hash_insert
@ Double the hashtable size when necesssary:
<<Cascades: procedures>>=
subroutine cascade_set_hash_expand (cascade_set)
type(cascade_set_t), intent(inout), target :: cascade_set
type(hash_entry_t), dimension(:), allocatable, target :: table_tmp
type(cascade_p), pointer :: current
integer :: i, s
allocate (table_tmp (0:cascade_set%mask))
table_tmp = cascade_set%entry
deallocate (cascade_set%entry)
s = 2 * size (table_tmp)
cascade_set%n_entries = 0
cascade_set%n_entries_max = s * cascade_set%fill_ratio
cascade_set%mask = s - 1
allocate (cascade_set%entry (0:cascade_set%mask))
do i = 0, ubound (table_tmp, 1)
current => table_tmp(i)%first
do while (associated (current))
call cascade_set_hash_insert_rec &
(cascade_set, table_tmp(i)%hashval, table_tmp(i)%hashval, &
table_tmp(i)%key, current%cascade)
current => current%next
end do
end do
end subroutine cascade_set_hash_expand
@ %def cascade_set_hash_expand
@ Insert the cascade at the bucket determined by the hash value. If
the bucket is filled, check first for a collision (unequal keys). In
that case, choose the following bucket and repeat. Otherwise, add the
cascade to the bucket.
If the bucket is empty, record the hash value, allocate and store the
key, and then add the cascade to the bucket.
If [[ok]] is present, before insertion we check whether the cascade is
already stored, and return failure if yes.
<<Cascades: procedures>>=
recursive subroutine cascade_set_hash_insert_rec &
(cascade_set, h, hashval, key, cascade, ok, cascade_ptr)
type(cascade_set_t), intent(inout) :: cascade_set
integer(i32), intent(in) :: h, hashval
integer(i8), dimension(:), intent(in) :: key
type(cascade_t), intent(in), target :: cascade
logical, intent(out), optional :: ok
type(cascade_t), optional, pointer :: cascade_ptr
integer(i32) :: i
i = iand (h, cascade_set%mask)
if (allocated (cascade_set%entry(i)%key)) then
if (size (cascade_set%entry(i)%key) /= size (key)) then
call cascade_set_hash_insert_rec &
(cascade_set, h + 1, hashval, key, cascade, ok, cascade_ptr)
else if (any (cascade_set%entry(i)%key /= key)) then
call cascade_set_hash_insert_rec &
(cascade_set, h + 1, hashval, key, cascade, ok, cascade_ptr)
else
call hash_entry_add_cascade_ptr &
(cascade_set%entry(i), cascade, ok, cascade_ptr)
end if
else
cascade_set%entry(i)%hashval = hashval
allocate (cascade_set%entry(i)%key (size (key)))
cascade_set%entry(i)%key = key
call hash_entry_add_cascade_ptr &
(cascade_set%entry(i), cascade, ok, cascade_ptr)
cascade_set%n_entries = cascade_set%n_entries + 1
end if
end subroutine cascade_set_hash_insert_rec
@ %def cascade_set_hash_insert_rec
@
\subsection{External particles}
We want to initialize the cascade set with the outgoing particles. In
case of multiple processes, initial cascades are prepared for all of
them. The hash array check ensures that no particle appears more than
once at the same place.
<<Cascades: interfaces>>=
interface cascade_set_add_outgoing
module procedure cascade_set_add_outgoing1
module procedure cascade_set_add_outgoing2
end interface
<<Cascades: procedures>>=
subroutine cascade_set_add_outgoing2 (cascade_set, flv)
type(cascade_set_t), intent(inout), target :: cascade_set
type(flavor_t), dimension(:,:), intent(in) :: flv
integer :: pos, prc, n_out, n_prc
type(cascade_t), pointer :: cascade
logical :: ok
n_out = size (flv, dim=1)
n_prc = size (flv, dim=2)
do prc = 1, n_prc
do pos = 1, n_out
allocate (cascade)
call cascade_init_outgoing &
(cascade, flv(pos,prc), pos, cascade_set%m_threshold_s)
call cascade_set_add (cascade_set, cascade, ok)
if (.not. ok) then
deallocate (cascade)
end if
end do
end do
end subroutine cascade_set_add_outgoing2
subroutine cascade_set_add_outgoing1 (cascade_set, flv)
type(cascade_set_t), intent(inout), target :: cascade_set
type(flavor_t), dimension(:), intent(in) :: flv
integer :: pos, n_out
type(cascade_t), pointer :: cascade
logical :: ok
n_out = size (flv, dim=1)
do pos = 1, n_out
allocate (cascade)
call cascade_init_outgoing &
(cascade, flv(pos), pos, cascade_set%m_threshold_s)
call cascade_set_add (cascade_set, cascade, ok)
if (.not. ok) then
deallocate (cascade)
end if
end do
end subroutine cascade_set_add_outgoing1
@ %def cascade_set_add_outgoing
@ The incoming particles are added one at a time. Nevertheless, we
may have several processes which are looped over. At the first
opportunity, we set the pointer [[first_t]] in the cascade set which
should point to the first t-channel cascade.
Return the indices of the first and last cascade generated.
<<Cascades: interfaces>>=
interface cascade_set_add_incoming
module procedure cascade_set_add_incoming0
module procedure cascade_set_add_incoming1
end interface
<<Cascades: procedures>>=
subroutine cascade_set_add_incoming1 (cascade_set, n1, n2, pos, flv)
type(cascade_set_t), intent(inout), target :: cascade_set
integer, intent(out) :: n1, n2
integer, intent(in) :: pos
type(flavor_t), dimension(:), intent(in) :: flv
integer :: prc, n_prc
type(cascade_t), pointer :: cascade
logical :: ok
n1 = 0
n2 = 0
n_prc = size (flv)
do prc = 1, n_prc
allocate (cascade)
call cascade_init_incoming &
(cascade, flv(prc), pos, cascade_set%m_threshold_t)
call cascade_set_add (cascade_set, cascade, ok)
if (ok) then
if (n1 == 0) n1 = cascade%index
n2 = cascade%index
if (.not. associated (cascade_set%first_t)) then
cascade_set%first_t => cascade
end if
else
deallocate (cascade)
end if
end do
end subroutine cascade_set_add_incoming1
subroutine cascade_set_add_incoming0 (cascade_set, n1, n2, pos, flv)
type(cascade_set_t), intent(inout), target :: cascade_set
integer, intent(out) :: n1, n2
integer, intent(in) :: pos
type(flavor_t), intent(in) :: flv
type(cascade_t), pointer :: cascade
logical :: ok
n1 = 0
n2 = 0
allocate (cascade)
call cascade_init_incoming &
(cascade, flv, pos, cascade_set%m_threshold_t)
call cascade_set_add (cascade_set, cascade, ok)
if (ok) then
if (n1 == 0) n1 = cascade%index
n2 = cascade%index
if (.not. associated (cascade_set%first_t)) then
cascade_set%first_t => cascade
end if
else
deallocate (cascade)
end if
end subroutine cascade_set_add_incoming0
@ %def cascade_set_add_incoming
@
\subsection{Cascade combination I: flavor assignment}
We have two disjunct cascades, now use the vertex table to determine
the possible flavors of the combination cascade. For each
possibility, try to generate a new cascade. The total cascade depth
has to be one less than the limit, because this is reached by setting
the keystone.
<<Cascades: procedures>>=
subroutine cascade_match_pair (cascade_set, cascade1, cascade2, s_channel)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in), target :: cascade1, cascade2
logical, intent(in) :: s_channel
integer, dimension(:), allocatable :: pdg3
integer :: i, depth_max
type(flavor_t) :: flv
if (s_channel) then
depth_max = cascade_set%depth_out
else
depth_max = cascade_set%depth_tot
end if
if (cascade1%depth + cascade2%depth < depth_max) then
call cascade_set%model%match_vertex ( &
cascade1%flv%get_pdg (), &
cascade2%flv%get_pdg (), &
pdg3)
do i = 1, size (pdg3)
call flv%init (pdg3(i), cascade_set%model)
if (s_channel) then
call cascade_combine_s (cascade_set, cascade1, cascade2, flv)
else
call cascade_combine_t (cascade_set, cascade1, cascade2, flv)
end if
end do
deallocate (pdg3)
end if
end subroutine cascade_match_pair
@ %def cascade_match_pair
@ The triplet version takes a third cascade, and we check whether this
triplet has a matching vertex in the database. If yes, we make a
keystone cascade.
<<Cascades: procedures>>=
subroutine cascade_match_triplet &
(cascade_set, cascade1, cascade2, cascade3, s_channel)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in), target :: cascade1, cascade2, cascade3
logical, intent(in) :: s_channel
integer :: depth_max
depth_max = cascade_set%depth_tot
if (cascade1%depth + cascade2%depth + cascade3%depth == depth_max) then
if (cascade_set%model%check_vertex ( &
cascade1%flv%get_pdg (), &
cascade2%flv%get_pdg (), &
cascade3%flv%get_pdg ())) then
call cascade_combine_keystone &
(cascade_set, cascade1, cascade2, cascade3, s_channel)
end if
end if
end subroutine cascade_match_triplet
@ %def cascade_match_triplet
@
\subsection{Cascade combination II: kinematics setup and check}
Having three matching flavors, we start constructing the combination
cascade. We look at the mass hierarchies and determine whether the
cascade is to be kept. In passing we set mapping modes, resonance
properties and such.
If successful, the cascade is finalized. For a resonant cascade, we
prepare in addition a copy without the resonance.
<<Cascades: procedures>>=
subroutine cascade_combine_s (cascade_set, cascade1, cascade2, flv)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in), target :: cascade1, cascade2
type(flavor_t), intent(in) :: flv
type(cascade_t), pointer :: cascade3, cascade4
logical :: keep
keep = .false.
allocate (cascade3)
call cascade_init (cascade3, cascade1%depth + cascade2%depth + 1)
cascade3%bincode = ior (cascade1%bincode, cascade2%bincode)
cascade3%flv = flv%anti ()
cascade3%pdg = cascade3%flv%get_pdg ()
cascade3%is_vector = flv%get_spin_type () == VECTOR
cascade3%m_min = cascade1%m_min + cascade2%m_min
cascade3%m_rea = flv%get_mass ()
if (cascade3%m_rea > cascade_set%m_threshold_s) then
cascade3%m_eff = cascade3%m_rea
end if
! Potentially resonant cases [sqrts = m_rea for on-shell decay]
if (cascade3%m_rea > cascade3%m_min &
.and. cascade3%m_rea <= cascade_set%sqrts) then
if (flv%get_width () /= 0) then
if (cascade1%on_shell .or. cascade2%on_shell) then
keep = .true.
cascade3%mapping = S_CHANNEL
cascade3%resonant = .true.
end if
else
call warn_decay (flv)
end if
! Collinear and IR singular cases
else if (cascade3%m_rea < cascade_set%sqrts) then
! Massless splitting
if (cascade1%m_eff == 0 .and. cascade2%m_eff == 0 &
.and. cascade3%depth <= 3) then
keep = .true.
cascade3%log_enhanced = .true.
if (cascade3%is_vector) then
if (cascade1%is_vector .and. cascade2%is_vector) then
cascade3%mapping = COLLINEAR ! three-vector-vertex
else
cascade3%mapping = INFRARED ! vector splitting into matter
end if
else
if (cascade1%is_vector .or. cascade2%is_vector) then
cascade3%mapping = COLLINEAR ! vector radiation off matter
else
cascade3%mapping = INFRARED ! scalar radiation/splitting
end if
end if
! IR radiation off massive particle
else if (cascade3%m_eff > 0 .and. cascade1%m_eff > 0 &
.and. cascade2%m_eff == 0 &
.and. (cascade1%on_shell .or. cascade1%mapping == RADIATION) &
.and. abs (cascade3%m_eff - cascade1%m_eff) &
< cascade_set%m_threshold_s) &
then
keep = .true.
cascade3%log_enhanced = .true.
cascade3%mapping = RADIATION
else if (cascade3%m_eff > 0 .and. cascade2%m_eff > 0 &
.and. cascade1%m_eff == 0 &
.and. (cascade2%on_shell .or. cascade2%mapping == RADIATION) &
.and. abs (cascade3%m_eff - cascade2%m_eff) &
< cascade_set%m_threshold_s) &
then
keep = .true.
cascade3%log_enhanced = .true.
cascade3%mapping = RADIATION
end if
end if
! Non-singular cases, including failed resonances
if (.not. keep) then
! Two on-shell particles from a virtual mother
if (cascade1%on_shell .or. cascade2%on_shell) then
keep = .true.
cascade3%m_eff = max (cascade3%m_min, &
cascade1%m_eff + cascade2%m_eff)
if (cascade3%m_eff < cascade_set%m_threshold_s) then
cascade3%m_eff = 0
end if
end if
end if
! Complete and register the cascade (two in case of resonance)
if (keep) then
cascade3%on_shell = cascade3%resonant .or. cascade3%log_enhanced
if (cascade3%resonant) then
cascade3%pdg = cascade3%flv%get_pdg ()
if (cascade_set%keep_nonresonant) then
allocate (cascade4)
cascade4 = cascade3
cascade4%index = cascade_index ()
cascade4%pdg = UNDEFINED
cascade4%mapping = NO_MAPPING
cascade4%resonant = .false.
cascade4%on_shell = .false.
end if
cascade3%m_min = cascade3%m_rea
call cascade_fusion (cascade_set, cascade1, cascade2, cascade3)
if (cascade_set%keep_nonresonant) then
call cascade_fusion (cascade_set, cascade1, cascade2, cascade4)
end if
else
call cascade_fusion (cascade_set, cascade1, cascade2, cascade3)
end if
else
deallocate (cascade3)
end if
contains
subroutine warn_decay (flv)
type(flavor_t), intent(in) :: flv
integer :: i
integer, dimension(MAX_WARN_RESONANCE), save :: warned_code = 0
LOOP_WARNED: do i = 1, MAX_WARN_RESONANCE
if (warned_code(i) == 0) then
warned_code(i) = flv%get_pdg ()
write (msg_buffer, "(A)") &
& " Intermediate decay of zero-width particle " &
& // char (flv%get_name ()) &
& // " may be possible."
call msg_warning
exit LOOP_WARNED
else if (warned_code(i) == flv%get_pdg ()) then
exit LOOP_WARNED
end if
end do LOOP_WARNED
end subroutine warn_decay
end subroutine cascade_combine_s
@ %def cascade_combine_s
<<Cascades: parameters>>=
integer, parameter, public :: MAX_WARN_RESONANCE = 50
@ %def MAX_WARN_RESONANCE
@ This is the t-channel version. [[cascade1]] is t-channel and
contains the seed, [[cascade2]] is s-channel. We check for
kinematically allowed beam decay (which is a fatal error), or massless
splitting / soft radiation. The cascade is kept in all remaining
cases and submitted for registration.
<<Cascades: procedures>>=
subroutine cascade_combine_t (cascade_set, cascade1, cascade2, flv)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in), target :: cascade1, cascade2
type(flavor_t), intent(in) :: flv
type(cascade_t), pointer :: cascade3
allocate (cascade3)
call cascade_init (cascade3, cascade1%depth + cascade2%depth + 1)
cascade3%bincode = ior (cascade1%bincode, cascade2%bincode)
cascade3%flv = flv%anti ()
cascade3%pdg = abs (cascade3%flv%get_pdg ())
cascade3%is_vector = flv%get_spin_type () == VECTOR
if (cascade1%incoming) then
cascade3%m_min = cascade2%m_min
else
cascade3%m_min = cascade1%m_min + cascade2%m_min
end if
cascade3%m_rea = flv%get_mass ()
if (cascade3%m_rea > cascade_set%m_threshold_t) then
cascade3%m_eff = max (cascade3%m_rea, cascade2%m_eff)
else if (cascade2%m_eff > cascade_set%m_threshold_t) then
cascade3%m_eff = cascade2%m_eff
else
cascade3%m_eff = 0
end if
! Allowed decay of beam particle
if (cascade1%incoming &
.and. cascade1%m_rea > cascade2%m_rea + cascade3%m_rea) then
call beam_decay (cascade_set%fatal_beam_decay)
! Massless splitting
else if (cascade1%m_eff == 0 &
.and. cascade2%m_eff < cascade_set%m_threshold_t &
.and. cascade3%m_eff == 0) then
cascade3%mapping = U_CHANNEL
cascade3%log_enhanced = .true.
! IR radiation off massive particle
else if (cascade1%m_eff /= 0 .and. cascade2%m_eff == 0 &
.and. cascade3%m_eff /= 0 &
.and. (cascade1%on_shell .or. cascade1%mapping == RADIATION) &
.and. abs (cascade1%m_eff - cascade3%m_eff) &
< cascade_set%m_threshold_t) &
then
cascade3%pdg = flv%get_pdg ()
cascade3%log_enhanced = .true.
cascade3%mapping = RADIATION
end if
cascade3%t_channel = .true.
call cascade_fusion (cascade_set, cascade1, cascade2, cascade3)
contains
subroutine beam_decay (fatal_beam_decay)
logical, intent(in) :: fatal_beam_decay
write (msg_buffer, "(1x,A,1x,'->',1x,A,1x,A)") &
char (cascade1%flv%get_name ()), &
char (cascade3%flv%get_name ()), &
char (cascade2%flv%get_name ())
call msg_message
write (msg_buffer, "(1x,'mass(',A,') =',1x,E17.10)") &
char (cascade1%flv%get_name ()), cascade1%m_rea
call msg_message
write (msg_buffer, "(1x,'mass(',A,') =',1x,E17.10)") &
char (cascade3%flv%get_name ()), cascade3%m_rea
call msg_message
write (msg_buffer, "(1x,'mass(',A,') =',1x,E17.10)") &
char (cascade2%flv%get_name ()), cascade2%m_rea
call msg_message
if (fatal_beam_decay) then
call msg_fatal (" Phase space: Initial beam particle can decay")
else
call msg_warning (" Phase space: Initial beam particle can decay")
end if
end subroutine beam_decay
end subroutine cascade_combine_t
@ %def cascade_combine_t
@ Here we complete a decay cascade. The third input is the
single-particle cascade for the initial particle. There is no
resonance or mapping assignment. The only condition for keeping the
cascade is the mass sum of the final state, which must be less than
the available energy.
Two modifications are necessary for scattering cascades: a pure
s-channel diagram (cascade1 is the incoming particle) do not have a
logarithmic mapping at top-level. And in a t-channel diagram, the
last line exchanged is mapped t-channel, not u-channel. Finally, we
can encounter the case of a $2\to 1$ process, where cascade1 is
incoming, and cascade2 is the outgoing particle. In all three cases
we register a new cascade with the modified mapping.
<<Cascades: procedures>>=
subroutine cascade_combine_keystone &
(cascade_set, cascade1, cascade2, cascade3, s_channel)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in), target :: cascade1, cascade2, cascade3
logical, intent(in) :: s_channel
type(cascade_t), pointer :: cascade4, cascade0
logical :: keep, ok
keep = .false.
allocate (cascade4)
call cascade_init &
(cascade4, cascade1%depth + cascade2%depth + cascade3%depth)
cascade4%complete = .true.
if (s_channel) then
cascade4%bincode = ior (cascade1%bincode, cascade2%bincode)
else
cascade4%bincode = cascade3%bincode
end if
cascade4%flv = cascade3%flv
cascade4%pdg = cascade3%pdg
cascade4%mapping = EXTERNAL_PRT
cascade4%is_vector = cascade3%is_vector
cascade4%m_min = cascade1%m_min + cascade2%m_min
cascade4%m_rea = cascade3%m_rea
cascade4%m_eff = cascade3%m_rea
if (cascade4%m_min < cascade_set%sqrts) then
keep = .true.
end if
if (keep) then
if (cascade1%incoming .and. cascade2%log_enhanced) then
allocate (cascade0)
cascade0 = cascade2
cascade0%next => null ()
cascade0%index = cascade_index ()
cascade0%mapping = NO_MAPPING
cascade0%log_enhanced = .false.
cascade0%n_log_enhanced = cascade0%n_log_enhanced - 1
cascade0%tree_mapping(cascade0%depth) = NO_MAPPING
call cascade_keystone &
(cascade_set, cascade1, cascade0, cascade3, cascade4, ok)
if (ok) then
call cascade_set_add (cascade_set, cascade0, ok)
else
deallocate (cascade0)
end if
else if (cascade1%t_channel .and. cascade1%mapping == U_CHANNEL) then
allocate (cascade0)
cascade0 = cascade1
cascade0%next => null ()
cascade0%index = cascade_index ()
cascade0%mapping = T_CHANNEL
cascade0%tree_mapping(cascade0%depth) = T_CHANNEL
call cascade_keystone &
(cascade_set, cascade0, cascade2, cascade3, cascade4, ok)
if (ok) then
call cascade_set_add (cascade_set, cascade0, ok)
else
deallocate (cascade0)
end if
else if (cascade1%incoming .and. cascade2%depth == 1) then
allocate (cascade0)
cascade0 = cascade2
cascade0%next => null ()
cascade0%index = cascade_index ()
cascade0%mapping = ON_SHELL
cascade0%tree_mapping(cascade0%depth) = ON_SHELL
call cascade_keystone &
(cascade_set, cascade1, cascade0, cascade3, cascade4, ok)
if (ok) then
call cascade_set_add (cascade_set, cascade0, ok)
else
deallocate (cascade0)
end if
else
call cascade_keystone &
(cascade_set, cascade1, cascade2, cascade3, cascade4, ok)
end if
else
deallocate (cascade4)
end if
end subroutine cascade_combine_keystone
@ %def cascade_combine_keystone
@
\subsection{Cascade combination III: node connections and tree fusion}
Here we assign global tree properties. If the allowed number of
off-shell lines is exceeded, discard the new cascade. Otherwise,
assign the trees, sort them, and assign connections. Finally, append
the cascade to the list. This may fail (because in the hash array
there is already an equivalent cascade). On failure, discard the
cascade.
<<Cascades: procedures>>=
subroutine cascade_fusion (cascade_set, cascade1, cascade2, cascade3)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in), target :: cascade1, cascade2
type(cascade_t), pointer :: cascade3
integer :: i1, i2, i3, i4
logical :: ok
cascade3%internal = (cascade3%depth - 3) / 2
if (cascade3%resonant) then
cascade3%multiplicity = 1
cascade3%n_resonances = &
cascade1%n_resonances + cascade2%n_resonances + 1
else
cascade3%multiplicity = cascade1%multiplicity + cascade2%multiplicity
cascade3%n_resonances = cascade1%n_resonances + cascade2%n_resonances
end if
if (cascade3%log_enhanced) then
cascade3%n_log_enhanced = &
cascade1%n_log_enhanced + cascade2%n_log_enhanced + 1
else
cascade3%n_log_enhanced = &
cascade1%n_log_enhanced + cascade2%n_log_enhanced
end if
if (cascade3%resonant) then
cascade3%n_off_shell = 0
else if (cascade3%log_enhanced) then
cascade3%n_off_shell = cascade1%n_off_shell + cascade2%n_off_shell
else
cascade3%n_off_shell = cascade1%n_off_shell + cascade2%n_off_shell + 1
end if
if (cascade3%t_channel) then
cascade3%n_t_channel = cascade1%n_t_channel + 1
end if
if (cascade3%n_off_shell > cascade_set%off_shell) then
deallocate (cascade3)
else if (cascade3%n_t_channel > cascade_set%t_channel) then
deallocate (cascade3)
else
i1 = cascade1%depth
i2 = i1 + 1
i3 = i1 + cascade2%depth
i4 = cascade3%depth
cascade3%tree(:i1) = cascade1%tree
where (cascade1%tree_mapping > NO_MAPPING)
cascade3%tree_pdg(:i1) = cascade1%tree_pdg
elsewhere
cascade3%tree_pdg(:i1) = UNDEFINED
end where
cascade3%tree_mapping(:i1) = cascade1%tree_mapping
cascade3%tree_resonant(:i1) = cascade1%tree_resonant
cascade3%tree(i2:i3) = cascade2%tree
where (cascade2%tree_mapping > NO_MAPPING)
cascade3%tree_pdg(i2:i3) = cascade2%tree_pdg
elsewhere
cascade3%tree_pdg(i2:i3) = UNDEFINED
end where
cascade3%tree_mapping(i2:i3) = cascade2%tree_mapping
cascade3%tree_resonant(i2:i3) = cascade2%tree_resonant
cascade3%tree(i4) = cascade3%bincode
cascade3%tree_pdg(i4) = cascade3%pdg
cascade3%tree_mapping(i4) = cascade3%mapping
cascade3%tree_resonant(i4) = cascade3%resonant
call tree_sort (cascade3%tree, &
cascade3%tree_pdg, cascade3%tree_mapping, cascade3%tree_resonant)
cascade3%has_children = .true.
cascade3%daughter1 => cascade1
cascade3%daughter2 => cascade2
call cascade_set_add (cascade_set, cascade3, ok)
if (.not. ok) deallocate (cascade3)
end if
end subroutine cascade_fusion
@ %def cascade_fusion
@ Here we combine a cascade pair with an incoming particle, i.e., we
set a keystone. Otherwise, this is similar. On the first
opportunity, we set the [[first_k]] pointer in the cascade set.
<<Cascades: procedures>>=
subroutine cascade_keystone &
(cascade_set, cascade1, cascade2, cascade3, cascade4, ok)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), intent(in), target :: cascade1, cascade2, cascade3
type(cascade_t), pointer :: cascade4
logical, intent(out) :: ok
integer :: i1, i2, i3, i4
cascade4%internal = (cascade4%depth - 3) / 2
cascade4%multiplicity = cascade1%multiplicity + cascade2%multiplicity
cascade4%n_resonances = cascade1%n_resonances + cascade2%n_resonances
cascade4%n_off_shell = cascade1%n_off_shell + cascade2%n_off_shell
cascade4%n_log_enhanced = &
cascade1%n_log_enhanced + cascade2%n_log_enhanced
cascade4%n_t_channel = cascade1%n_t_channel + cascade2%n_t_channel
if (cascade4%n_off_shell > cascade_set%off_shell) then
deallocate (cascade4)
ok = .false.
else if (cascade4%n_t_channel > cascade_set%t_channel) then
deallocate (cascade4)
ok = .false.
else
i1 = cascade1%depth
i2 = i1 + 1
i3 = i1 + cascade2%depth
i4 = cascade4%depth
cascade4%tree(:i1) = cascade1%tree
where (cascade1%tree_mapping > NO_MAPPING)
cascade4%tree_pdg(:i1) = cascade1%tree_pdg
elsewhere
cascade4%tree_pdg(:i1) = UNDEFINED
end where
cascade4%tree_mapping(:i1) = cascade1%tree_mapping
cascade4%tree_resonant(:i1) = cascade1%tree_resonant
cascade4%tree(i2:i3) = cascade2%tree
where (cascade2%tree_mapping > NO_MAPPING)
cascade4%tree_pdg(i2:i3) = cascade2%tree_pdg
elsewhere
cascade4%tree_pdg(i2:i3) = UNDEFINED
end where
cascade4%tree_mapping(i2:i3) = cascade2%tree_mapping
cascade4%tree_resonant(i2:i3) = cascade2%tree_resonant
cascade4%tree(i4) = cascade4%bincode
cascade4%tree_pdg(i4) = UNDEFINED
cascade4%tree_mapping(i4) = cascade4%mapping
cascade4%tree_resonant(i4) = .false.
call tree_sort (cascade4%tree, &
cascade4%tree_pdg, cascade4%tree_mapping, cascade4%tree_resonant)
cascade4%has_children = .true.
cascade4%daughter1 => cascade1
cascade4%daughter2 => cascade2
cascade4%mother => cascade3
call cascade_set_add (cascade_set, cascade4, ok)
if (ok) then
if (.not. associated (cascade_set%first_k)) then
cascade_set%first_k => cascade4
end if
else
deallocate (cascade4)
end if
end if
end subroutine cascade_keystone
@ %def cascade_keystone
@
Sort a tree (array of binary codes) and particle code array
simultaneously, by ascending binary codes. A convenient method is to
use the [[maxloc]] function iteratively, to find and remove the
largest entry in the tree array one by one.
<<Cascades: procedures>>=
subroutine tree_sort (tree, pdg, mapping, resonant)
integer(TC), dimension(:), intent(inout) :: tree
integer, dimension(:), intent(inout) :: pdg, mapping
logical, dimension(:), intent(inout) :: resonant
integer(TC), dimension(size(tree)) :: tree_tmp
integer, dimension(size(pdg)) :: pdg_tmp, mapping_tmp
logical, dimension(size(resonant)) :: resonant_tmp
integer, dimension(1) :: pos
integer :: i
tree_tmp = tree
pdg_tmp = pdg
mapping_tmp = mapping
resonant_tmp = resonant
do i = size(tree),1,-1
pos = maxloc (tree_tmp)
tree(i) = tree_tmp (pos(1))
pdg(i) = pdg_tmp (pos(1))
mapping(i) = mapping_tmp (pos(1))
resonant(i) = resonant_tmp (pos(1))
tree_tmp(pos(1)) = 0
end do
end subroutine tree_sort
@ %def tree_sort
@
\subsection{Cascade set generation}
These procedures loop over cascades and build up the cascade set. After each
iteration of the innermost loop, we set a breakpoint.
s-channel: We use a nested scan to combine all cascades with all other
cascades.
<<Cascades: procedures>>=
subroutine cascade_set_generate_s (cascade_set)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), pointer :: cascade1, cascade2
cascade1 => cascade_set%first
LOOP1: do while (associated (cascade1))
cascade2 => cascade_set%first
LOOP2: do while (associated (cascade2))
if (cascade2%index >= cascade1%index) exit LOOP2
if (cascade1 .disjunct. cascade2) then
call cascade_match_pair (cascade_set, cascade1, cascade2, .true.)
end if
call terminate_now_if_signal ()
cascade2 => cascade2%next
end do LOOP2
cascade1 => cascade1%next
end do LOOP1
end subroutine cascade_set_generate_s
@ %def cascade_set_generate_s
@ The t-channel cascades are directed and have a seed (one of the
incoming particles) and a target (the other one). We loop over all
possible seeds and targets. Inside this, we loop over all t-channel
cascades ([[cascade1]]) and s-channel cascades ([[cascade2]]) and try
to combine them.
<<Cascades: procedures>>=
subroutine cascade_set_generate_t (cascade_set, pos_seed, pos_target)
type(cascade_set_t), intent(inout), target :: cascade_set
integer, intent(in) :: pos_seed, pos_target
type(cascade_t), pointer :: cascade_seed, cascade_target
type(cascade_t), pointer :: cascade1, cascade2
integer(TC) :: bc_seed, bc_target
bc_seed = ibset (0_TC, pos_seed-1)
bc_target = ibset (0_TC, pos_target-1)
cascade_seed => cascade_set%first_t
LOOP_SEED: do while (associated (cascade_seed))
if (cascade_seed%bincode == bc_seed) then
cascade_target => cascade_set%first_t
LOOP_TARGET: do while (associated (cascade_target))
if (cascade_target%bincode == bc_target) then
cascade1 => cascade_set%first_t
LOOP_T: do while (associated (cascade1))
if ((cascade1 .disjunct. cascade_target) &
.and. .not. (cascade1 .disjunct. cascade_seed)) then
cascade2 => cascade_set%first
LOOP_S: do while (associated (cascade2))
if ((cascade2 .disjunct. cascade_target) &
.and. (cascade2 .disjunct. cascade1)) then
call cascade_match_pair &
(cascade_set, cascade1, cascade2, .false.)
end if
call terminate_now_if_signal ()
cascade2 => cascade2%next
end do LOOP_S
end if
call terminate_now_if_signal ()
cascade1 => cascade1%next
end do LOOP_T
end if
call terminate_now_if_signal ()
cascade_target => cascade_target%next
end do LOOP_TARGET
end if
call terminate_now_if_signal ()
cascade_seed => cascade_seed%next
end do LOOP_SEED
end subroutine cascade_set_generate_t
@ %def cascade_set_generate_t
@ This part completes the phase space for decay processes. It is
similar to s-channel cascade generation, but combines two cascade with
the particular cascade of the incoming particle. This particular
cascade is expected to be pointed at by [[first_t]].
<<Cascades: procedures>>=
subroutine cascade_set_generate_decay (cascade_set)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), pointer :: cascade1, cascade2
type(cascade_t), pointer :: cascade_in
cascade_in => cascade_set%first_t
cascade1 => cascade_set%first
do while (associated (cascade1))
if (cascade1 .disjunct. cascade_in) then
cascade2 => cascade1%next
do while (associated (cascade2))
if ((cascade2 .disjunct. cascade1) &
.and. (cascade2 .disjunct. cascade_in)) then
call cascade_match_triplet (cascade_set, &
cascade1, cascade2, cascade_in, .true.)
end if
call terminate_now_if_signal ()
cascade2 => cascade2%next
end do
end if
call terminate_now_if_signal ()
cascade1 => cascade1%next
end do
end subroutine cascade_set_generate_decay
@ %def cascade_set_generate_decay
@ This part completes the phase space for scattering processes. We
combine a t-channel cascade (containing the seed) with a s-channel
cascade and the target.
<<Cascades: procedures>>=
subroutine cascade_set_generate_scattering &
(cascade_set, ns1, ns2, nt1, nt2, pos_seed, pos_target)
type(cascade_set_t), intent(inout), target :: cascade_set
integer, intent(in) :: pos_seed, pos_target
integer, intent(in) :: ns1, ns2, nt1, nt2
type(cascade_t), pointer :: cascade_seed, cascade_target
type(cascade_t), pointer :: cascade1, cascade2
integer(TC) :: bc_seed, bc_target
bc_seed = ibset (0_TC, pos_seed-1)
bc_target = ibset (0_TC, pos_target-1)
cascade_seed => cascade_set%first_t
LOOP_SEED: do while (associated (cascade_seed))
if (cascade_seed%index < ns1) then
cascade_seed => cascade_seed%next
cycle LOOP_SEED
else if (cascade_seed%index > ns2) then
exit LOOP_SEED
else if (cascade_seed%bincode == bc_seed) then
cascade_target => cascade_set%first_t
LOOP_TARGET: do while (associated (cascade_target))
if (cascade_target%index < nt1) then
cascade_target => cascade_target%next
cycle LOOP_TARGET
else if (cascade_target%index > nt2) then
exit LOOP_TARGET
else if (cascade_target%bincode == bc_target) then
cascade1 => cascade_set%first_t
LOOP_T: do while (associated (cascade1))
if ((cascade1 .disjunct. cascade_target) &
.and. .not. (cascade1 .disjunct. cascade_seed)) then
cascade2 => cascade_set%first
LOOP_S: do while (associated (cascade2))
if ((cascade2 .disjunct. cascade_target) &
.and. (cascade2 .disjunct. cascade1)) then
call cascade_match_triplet (cascade_set, &
cascade1, cascade2, cascade_target, .false.)
end if
call terminate_now_if_signal ()
cascade2 => cascade2%next
end do LOOP_S
end if
call terminate_now_if_signal ()
cascade1 => cascade1%next
end do LOOP_T
end if
call terminate_now_if_signal ()
cascade_target => cascade_target%next
end do LOOP_TARGET
end if
call terminate_now_if_signal ()
cascade_seed => cascade_seed%next
end do LOOP_SEED
end subroutine cascade_set_generate_scattering
@ %def cascade_set_generate_scattering
@
\subsection{Groves}
Before assigning groves, assign hashcodes to the resonance patterns, so they
can easily be compared.
<<Cascades: procedures>>=
subroutine cascade_set_assign_resonance_hash (cascade_set)
type(cascade_set_t), intent(inout) :: cascade_set
type(cascade_t), pointer :: cascade
cascade => cascade_set%first_k
do while (associated (cascade))
call cascade_assign_resonance_hash (cascade)
cascade => cascade%next
end do
end subroutine cascade_set_assign_resonance_hash
@ %def cascade_assign_resonance_hash
@ After all cascades are recorded, we group the complete cascades in
groves. A grove consists of cascades with identical multiplicity,
number of resonances, log-enhanced, t-channel lines, and resonance flavors.
<<Cascades: procedures>>=
subroutine cascade_set_assign_groves (cascade_set)
type(cascade_set_t), intent(inout), target :: cascade_set
type(cascade_t), pointer :: cascade1, cascade2
integer :: multiplicity
integer :: n_resonances, n_log_enhanced, n_t_channel, n_off_shell
integer :: res_hash
integer :: grove
grove = 0
cascade1 => cascade_set%first_k
do while (associated (cascade1))
if (cascade1%active .and. cascade1%complete &
.and. cascade1%grove == 0) then
grove = grove + 1
cascade1%grove = grove
multiplicity = cascade1%multiplicity
n_resonances = cascade1%n_resonances
n_log_enhanced = cascade1%n_log_enhanced
n_off_shell = cascade1%n_off_shell
n_t_channel = cascade1%n_t_channel
res_hash = cascade1%res_hash
cascade2 => cascade1%next
do while (associated (cascade2))
if (cascade2%grove == 0) then
if (cascade2%multiplicity == multiplicity &
.and. cascade2%n_resonances == n_resonances &
.and. cascade2%n_log_enhanced == n_log_enhanced &
.and. cascade2%n_off_shell == n_off_shell &
.and. cascade2%n_t_channel == n_t_channel &
.and. cascade2%res_hash == res_hash) then
cascade2%grove = grove
end if
end if
call terminate_now_if_signal ()
cascade2 => cascade2%next
end do
end if
call terminate_now_if_signal ()
cascade1 => cascade1%next
end do
cascade_set%n_groves = grove
end subroutine cascade_set_assign_groves
@ %def cascade_set_assign_groves
@
\subsection{Generate the phase space file}
Generate a complete phase space configuration.
For each flavor assignment: First, all s-channel
graphs that can be built up from the outgoing particles. Then we
distinguish (1) decay, where we complete the s-channel graphs by
connecting to the input line, and (2) scattering, where we now
generate t-channel graphs by introducing an incoming particle, and
complete this by connecting to the other incoming particle.
After all cascade sets have been generated, merge them into a common set.
This eliminates redunancies between flavor assignments.
<<Cascades: public>>=
public :: cascade_set_generate
<<Cascades: procedures>>=
subroutine cascade_set_generate &
(cascade_set, model, n_in, n_out, flv, phs_par, fatal_beam_decay)
type(cascade_set_t), intent(out) :: cascade_set
class(model_data_t), intent(in), target :: model
integer, intent(in) :: n_in, n_out
type(flavor_t), dimension(:,:), intent(in) :: flv
type(phs_parameters_t), intent(in) :: phs_par
logical, intent(in) :: fatal_beam_decay
type(cascade_set_t), dimension(:), allocatable :: cset
type(cascade_t), pointer :: cascade
integer :: i
if (phase_space_vanishes (phs_par%sqrts, n_in, flv)) return
call cascade_set_init (cascade_set, model, n_in, n_out, phs_par, &
fatal_beam_decay, flv)
allocate (cset (size (flv, 2)))
do i = 1, size (cset)
call cascade_set_generate_single (cset(i), &
model, n_in, n_out, flv(:,i), phs_par, fatal_beam_decay)
cascade => cset(i)%first_k
do while (associated (cascade))
if (cascade%active .and. cascade%complete) then
call cascade_set_add_copy (cascade_set, cascade)
end if
cascade => cascade%next
end do
call cascade_set_final (cset(i))
end do
cascade_set%first_k => cascade_set%first
call cascade_set_assign_resonance_hash (cascade_set)
call cascade_set_assign_groves (cascade_set)
end subroutine cascade_set_generate
@ %def cascade_set_generate
@ This generates phase space for a single channel, without assigning groves.
<<Cascades: procedures>>=
subroutine cascade_set_generate_single (cascade_set, &
model, n_in, n_out, flv, phs_par, fatal_beam_decay)
type(cascade_set_t), intent(out) :: cascade_set
class(model_data_t), intent(in), target :: model
integer, intent(in) :: n_in, n_out
type(flavor_t), dimension(:), intent(in) :: flv
type(phs_parameters_t), intent(in) :: phs_par
logical, intent(in) :: fatal_beam_decay
integer :: n11, n12, n21, n22
call cascade_set_init (cascade_set, model, n_in, n_out, phs_par, &
fatal_beam_decay)
call cascade_set_add_outgoing (cascade_set, flv(n_in+1:))
call cascade_set_generate_s (cascade_set)
select case (n_in)
case(1)
call cascade_set_add_incoming &
(cascade_set, n11, n12, n_out + 1, flv(1))
call cascade_set_generate_decay (cascade_set)
case(2)
call cascade_set_add_incoming &
(cascade_set, n11, n12, n_out + 1, flv(2))
call cascade_set_add_incoming &
(cascade_set, n21, n22, n_out + 2, flv(1))
call cascade_set_generate_t (cascade_set, n_out + 1, n_out + 2)
call cascade_set_generate_t (cascade_set, n_out + 2, n_out + 1)
call cascade_set_generate_scattering &
(cascade_set, n11, n12, n21, n22, n_out + 1, n_out + 2)
call cascade_set_generate_scattering &
(cascade_set, n21, n22, n11, n12, n_out + 2, n_out + 1)
end select
end subroutine cascade_set_generate_single
@ %def cascade_set_generate_single
@ Sanity check: Before anything else is done, check if there could
possibly be any phase space.
<<Cascades: public>>=
public :: phase_space_vanishes
<<Cascades: procedures>>=
function phase_space_vanishes (sqrts, n_in, flv) result (flag)
logical :: flag
real(default), intent(in) :: sqrts
integer, intent(in) :: n_in
type(flavor_t), dimension(:,:), intent(in) :: flv
real(default), dimension(:,:), allocatable :: mass
real(default), dimension(:), allocatable :: mass_in, mass_out
integer :: n_prt, n_flv, i, j
flag = .false.
if (sqrts <= 0) then
call msg_error ("Phase space vanishes (sqrts must be positive)")
flag = .true.; return
end if
n_prt = size (flv, 1)
n_flv = size (flv, 2)
allocate (mass (n_prt, n_flv), mass_in (n_flv), mass_out (n_flv))
mass = flv%get_mass ()
mass_in = sum (mass(:n_in,:), 1)
mass_out = sum (mass(n_in+1:,:), 1)
if (any (mass_in > sqrts)) then
call msg_error ("Mass sum of incoming particles " &
// "is more than available energy")
flag = .true.; return
end if
if (any (mass_out > sqrts)) then
call msg_error ("Mass sum of outgoing particles " &
// "is more than available energy")
flag = .true.; return
end if
end function phase_space_vanishes
@ %def phase_space_vanishes
@
\subsection{Return the resonance histories for subtraction}
This appears to be essential (re-export of some imported assignment?)!
<<Cascades: public>>=
public :: assignment(=)
@
Extract the resonance set from a complete cascade.
<<Cascades: cascade: TBP>>=
procedure :: extract_resonance_history => cascade_extract_resonance_history
<<Cascades: procedures>>=
subroutine cascade_extract_resonance_history &
(cascade, res_hist, model, n_out)
class(cascade_t), intent(in), target :: cascade
type(resonance_history_t), intent(out) :: res_hist
class(model_data_t), intent(in), target :: model
integer, intent(in) :: n_out
type(resonance_info_t) :: resonance
integer :: i, mom_id, pdg
if (debug_on) call msg_debug2 (D_PHASESPACE, "cascade_extract_resonance_history")
if (cascade%n_resonances > 0) then
if (cascade%has_children) then
if (debug_on) call msg_debug2 (D_PHASESPACE, "cascade has resonances and children")
do i = 1, size(cascade%tree_resonant)
if (cascade%tree_resonant (i)) then
mom_id = cascade%tree (i)
pdg = cascade%tree_pdg (i)
call resonance%init (mom_id, pdg, model, n_out)
if (debug2_active (D_PHASESPACE)) then
print *, 'D: Adding resonance'
call resonance%write ()
end if
call res_hist%add_resonance (resonance)
end if
end do
end if
end if
end subroutine cascade_extract_resonance_history
@ %def cascade_extract_resonance_history
@
<<Cascades: public>>=
public :: cascade_set_get_n_trees
<<Cascades: procedures>>=
function cascade_set_get_n_trees (cascade_set) result (n)
type(cascade_set_t), intent(in), target :: cascade_set
integer :: n
type(cascade_t), pointer :: cascade
integer :: grove
if (debug_on) call msg_debug (D_PHASESPACE, "cascade_set_get_n_trees")
n = 0
do grove = 1, cascade_set%n_groves
cascade => cascade_set%first_k
do while (associated (cascade))
if (cascade%active .and. cascade%complete) then
if (cascade%grove == grove) then
n = n + 1
end if
end if
cascade => cascade%next
end do
end do
if (debug_on) call msg_debug (D_PHASESPACE, "n", n)
end function cascade_set_get_n_trees
@ %def cascade_set_get_n_trees
@ Distill the set of resonance histories from the cascade set. The
result is an array which contains each valid history exactly once.
<<Cascades: public>>=
public :: cascade_set_get_resonance_histories
<<Cascades: procedures>>=
subroutine cascade_set_get_resonance_histories (cascade_set, n_filter, res_hists)
type(cascade_set_t), intent(in), target :: cascade_set
integer, intent(in), optional :: n_filter
type(resonance_history_t), dimension(:), allocatable, intent(out) :: res_hists
type(resonance_history_t), dimension(:), allocatable :: tmp
type(cascade_t), pointer :: cascade
type(resonance_history_t) :: res_hist
type(resonance_history_set_t) :: res_hist_set
integer :: grove, i, n_hists
logical :: included, add_to_list
if (debug_on) call msg_debug (D_PHASESPACE, "cascade_set_get_resonance_histories")
call res_hist_set%init (n_filter = n_filter)
do grove = 1, cascade_set%n_groves
cascade => cascade_set%first_k
do while (associated (cascade))
if (cascade%active .and. cascade%complete) then
if (cascade%grove == grove) then
if (debug_on) call msg_debug2 (D_PHASESPACE, "grove", grove)
call cascade%extract_resonance_history &
(res_hist, cascade_set%model, cascade_set%n_out)
call res_hist_set%enter (res_hist)
end if
end if
cascade => cascade%next
end do
end do
call res_hist_set%freeze ()
call res_hist_set%to_array (res_hists)
end subroutine cascade_set_get_resonance_histories
@ %def cascade_set_get_resonance_histories
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[cascades_ut.f90]]>>=
<<File header>>
module cascades_ut
use unit_tests
use cascades_uti
<<Standard module head>>
<<Cascades: public test>>
contains
<<Cascades: test driver>>
end module cascades_ut
@ %def cascades_ut
@
<<[[cascades_uti.f90]]>>=
<<File header>>
module cascades_uti
<<Use kinds>>
<<Use strings>>
use numeric_utils
use flavors
use model_data
use phs_forests, only: phs_parameters_t
use resonances, only: resonance_history_t
use cascades
<<Standard module head>>
<<Cascades: test declarations>>
contains
<<Cascades: tests>>
end module cascades_uti
@ %def cascades_ut
@ API: driver for the unit tests below.
<<Cascades: public test>>=
public :: cascades_test
<<Cascades: test driver>>=
subroutine cascades_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Cascades: execute tests>>
end subroutine cascades_test
@ %def cascades_test
\subsubsection{Check cascade setup}
@ Checking the basic setup up of the phase space cascade parameterizations.
<<Cascades: execute tests>>=
call test (cascades_1, "cascades_1", &
"check cascade setup", &
u, results)
<<Cascades: test declarations>>=
public :: cascades_1
<<Cascades: tests>>=
subroutine cascades_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t), dimension(5,2) :: flv
type(cascade_set_t) :: cascade_set
type(phs_parameters_t) :: phs_par
write (u, "(A)") "* Test output: cascades_1"
write (u, "(A)") "* Purpose: test cascade phase space functions"
write (u, "(A)")
write (u, "(A)") "* Initializing"
write (u, "(A)")
call model%init_sm_test ()
call flv(1,1)%init ( 2, model)
call flv(2,1)%init (-2, model)
call flv(3,1)%init ( 1, model)
call flv(4,1)%init (-1, model)
call flv(5,1)%init (21, model)
call flv(1,2)%init ( 2, model)
call flv(2,2)%init (-2, model)
call flv(3,2)%init ( 2, model)
call flv(4,2)%init (-2, model)
call flv(5,2)%init (21, model)
phs_par%sqrts = 1000._default
phs_par%off_shell = 2
write (u, "(A)")
write (u, "(A)") "* Generating the cascades"
write (u, "(A)")
call cascade_set_generate (cascade_set, model, 2, 3, flv, phs_par,.true.)
call cascade_set_write (cascade_set, u)
call cascade_set_write_file_format (cascade_set, u)
write (u, "(A)") "* Cleanup"
write (u, "(A)")
call cascade_set_final (cascade_set)
call model%final ()
write (u, *)
write (u, "(A)") "* Test output end: cascades_1"
end subroutine cascades_1
@ %def cascades_1
@
\subsubsection{Check resonance history}
<<Cascades: execute tests>>=
call test(cascades_2, "cascades_2", &
"Check resonance history", u, results)
<<Cascades: test declarations>>=
public :: cascades_2
<<Cascades: tests>>=
subroutine cascades_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t), dimension(5,1) :: flv
type(cascade_set_t) :: cascade_set
type(phs_parameters_t) :: phs_par
type(resonance_history_t), dimension(:), allocatable :: res_hists
integer :: n, i
write (u, "(A)") "* Test output: cascades_2"
write (u, "(A)") "* Purpose: Check resonance history"
write (u, "(A)")
write (u, "(A)") "* Initializing"
write (u, "(A)")
call model%init_sm_test ()
call flv(1,1)%init ( 2, model)
call flv(2,1)%init (-2, model)
call flv(3,1)%init ( 1, model)
call flv(4,1)%init (-1, model)
call flv(5,1)%init (22, model)
phs_par%sqrts = 1000._default
phs_par%off_shell = 2
write (u, "(A)")
write (u, "(A)") "* Generating the cascades"
write (u, "(A)")
call cascade_set_generate (cascade_set, model, 2, 3, flv, phs_par,.true.)
call cascade_set_get_resonance_histories (cascade_set, res_hists = res_hists)
n = cascade_set_get_n_trees (cascade_set)
call assert_equal (u, n, 24, "Number of trees")
do i = 1, size(res_hists)
call res_hists(i)%write (u)
write (u, "(A)")
end do
write (u, "(A)") "* Cleanup"
write (u, "(A)")
call cascade_set_final (cascade_set)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: cascades_2"
end subroutine cascades_2
@ %def cascades_2
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{WOOD phase space}
This is the module that interfaces the [[phs_forests]] phase-space
treatment and the [[cascades]] module for generating phase-space
channels. As an extension of the [[phs_base]] abstract type,
the phase-space configuration and instance implement the standard API.
(Currently, this is the only generic phase-space implementation of
\whizard. For trivial two-particle phase space, there is
[[phs_wood]] as an alternative.)
<<[[phs_wood.f90]]>>=
<<File header>>
module phs_wood
<<Use kinds>>
<<Use strings>>
use io_units
use constants
use numeric_utils
use diagnostics
use os_interface
use md5
use physics_defs
use lorentz
use model_data
use flavors
use process_constants
use sf_mappings
use sf_base
use phs_base
use mappings
use resonances, only: resonance_history_set_t
use phs_forests
use cascades
use cascades2
<<Standard module head>>
<<PHS wood: public>>
<<PHS wood: parameters>>
<<PHS wood: types>>
contains
<<PHS wood: procedures>>
end module phs_wood
@ %def phs_wood
@
\subsection{Configuration}
<<PHS wood: parameters>>=
integer, parameter, public :: EXTENSION_NONE = 0
integer, parameter, public :: EXTENSION_DEFAULT = 1
integer, parameter, public :: EXTENSION_DGLAP = 2
<<PHS wood: public>>=
public :: phs_wood_config_t
<<PHS wood: types>>=
type, extends (phs_config_t) :: phs_wood_config_t
character(32) :: md5sum_forest = ""
type(string_t) :: phs_path
integer :: io_unit = 0
logical :: io_unit_keep_open = .false.
logical :: use_equivalences = .false.
logical :: fatal_beam_decay = .true.
type(mapping_defaults_t) :: mapping_defaults
type(phs_parameters_t) :: par
type(string_t) :: run_id
type(cascade_set_t), allocatable :: cascade_set
logical :: use_cascades2 = .false.
type(feyngraph_set_t), allocatable :: feyngraph_set
type(phs_forest_t) :: forest
type(os_data_t) :: os_data
integer :: extension_mode = EXTENSION_NONE
contains
<<PHS wood: phs wood config: TBP>>
end type phs_wood_config_t
@ %def phs_wood_config_t
@ Finalizer. We should delete the cascade set and the forest subobject.
Also close the I/O unit, just in case. (We assume that [[io_unit]] is
not standard input/output.)
<<PHS wood: phs wood config: TBP>>=
procedure :: final => phs_wood_config_final
<<PHS wood: procedures>>=
subroutine phs_wood_config_final (object)
class(phs_wood_config_t), intent(inout) :: object
logical :: opened
if (object%io_unit /= 0) then
inquire (unit = object%io_unit, opened = opened)
if (opened) close (object%io_unit)
end if
call object%clear_phase_space ()
call phs_forest_final (object%forest)
end subroutine phs_wood_config_final
@ %def phs_wood_config_final
@
<<PHS wood: phs wood config: TBP>>=
procedure :: increase_n_par => phs_wood_config_increase_n_par
<<PHS wood: procedures>>=
subroutine phs_wood_config_increase_n_par (phs_config)
class(phs_wood_config_t), intent(inout) :: phs_config
select case (phs_config%extension_mode)
case (EXTENSION_DEFAULT)
phs_config%n_par = phs_config%n_par + 3
case (EXTENSION_DGLAP)
phs_config%n_par = phs_config%n_par + 4
end select
end subroutine phs_wood_config_increase_n_par
@ %def phs_wood_config_increase_n_par
@
<<PHS wood: phs wood config: TBP>>=
procedure :: set_extension_mode => phs_wood_config_set_extension_mode
<<PHS wood: procedures>>=
subroutine phs_wood_config_set_extension_mode (phs_config, mode)
class(phs_wood_config_t), intent(inout) :: phs_config
integer, intent(in) :: mode
phs_config%extension_mode = mode
end subroutine phs_wood_config_set_extension_mode
@ %def phs_wood_config_set_extension_mode
@ Output. The contents of the PHS forest are not printed explicitly.
<<PHS wood: phs wood config: TBP>>=
procedure :: write => phs_wood_config_write
<<PHS wood: procedures>>=
subroutine phs_wood_config_write (object, unit, include_id)
class(phs_wood_config_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: include_id
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") &
"Partonic phase-space configuration (phase-space forest):"
call object%base_write (unit)
write (u, "(1x,A)") "Phase-space configuration parameters:"
call object%par%write (u)
call object%mapping_defaults%write (u)
write (u, "(3x,A,A,A)") "Run ID: '", char (object%run_id), "'"
end subroutine phs_wood_config_write
@ %def phs_wood_config_write
@ Print the PHS forest contents.
<<PHS wood: phs wood config: TBP>>=
procedure :: write_forest => phs_wood_config_write_forest
<<PHS wood: procedures>>=
subroutine phs_wood_config_write_forest (object, unit)
class(phs_wood_config_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
call phs_forest_write (object%forest, u)
end subroutine phs_wood_config_write_forest
@ %def phs_wood_config_write_forest
@ Set the phase-space parameters that the configuration generator requests.
<<PHS wood: phs wood config: TBP>>=
procedure :: set_parameters => phs_wood_config_set_parameters
<<PHS wood: procedures>>=
subroutine phs_wood_config_set_parameters (phs_config, par)
class(phs_wood_config_t), intent(inout) :: phs_config
type(phs_parameters_t), intent(in) :: par
phs_config%par = par
end subroutine phs_wood_config_set_parameters
@ %def phs_wood_config_set_parameters
@ Enable the generation of channel equivalences (when calling [[configure]]).
<<PHS wood: phs wood config: TBP>>=
procedure :: enable_equivalences => phs_wood_config_enable_equivalences
<<PHS wood: procedures>>=
subroutine phs_wood_config_enable_equivalences (phs_config)
class(phs_wood_config_t), intent(inout) :: phs_config
phs_config%use_equivalences = .true.
end subroutine phs_wood_config_enable_equivalences
@ %def phs_wood_config_enable_equivalences
@ Set the phase-space mapping parameters that the configuration generator
requests.g
<<PHS wood: phs wood config: TBP>>=
procedure :: set_mapping_defaults => phs_wood_config_set_mapping_defaults
<<PHS wood: procedures>>=
subroutine phs_wood_config_set_mapping_defaults (phs_config, mapping_defaults)
class(phs_wood_config_t), intent(inout) :: phs_config
type(mapping_defaults_t), intent(in) :: mapping_defaults
phs_config%mapping_defaults = mapping_defaults
end subroutine phs_wood_config_set_mapping_defaults
@ %def phs_wood_config_set_mapping_defaults
@ Define the input stream for the phase-space file as an open logical unit.
The unit must be connected.
<<PHS wood: phs wood config: TBP>>=
procedure :: set_input => phs_wood_config_set_input
<<PHS wood: procedures>>=
subroutine phs_wood_config_set_input (phs_config, unit)
class(phs_wood_config_t), intent(inout) :: phs_config
integer, intent(in) :: unit
phs_config%io_unit = unit
rewind (unit)
end subroutine phs_wood_config_set_input
@ %def phs_wood_config_set_input
@
\subsection{Phase-space generation}
This subroutine generates a phase space configuration using the
[[cascades]] module. Note that this may take time, and the
[[cascade_set]] subobject may consume a large amount of memory.
<<PHS wood: phs wood config: TBP>>=
procedure :: generate_phase_space => phs_wood_config_generate_phase_space
<<PHS wood: procedures>>=
subroutine phs_wood_config_generate_phase_space (phs_config)
class(phs_wood_config_t), intent(inout) :: phs_config
integer :: off_shell, extra_off_shell
logical :: valid
integer :: unit_fds
type(string_t) :: file_name
logical :: file_exists
call msg_message ("Phase space: generating configuration ...")
off_shell = phs_config%par%off_shell
if (phs_config%use_cascades2) then
file_name = char (phs_config%id) // ".fds"
inquire (file=char (file_name), exist=file_exists)
if (.not. file_exists) call msg_fatal &
("The O'Mega input file " // char (file_name) // &
" does not exist. " // "Please make sure that the " // &
"variable ?omega_write_phs_output has been set correctly.")
unit_fds = free_unit ()
open (unit=unit_fds, file=char(file_name), status='old', action='read')
do extra_off_shell = 0, max (phs_config%n_tot - 3, 0)
phs_config%par%off_shell = off_shell + extra_off_shell
allocate (phs_config%feyngraph_set)
call feyngraph_set_generate (phs_config%feyngraph_set, &
phs_config%model, phs_config%n_in, phs_config%n_out, &
phs_config%flv, &
phs_config%par, phs_config%fatal_beam_decay, unit_fds, &
phs_config%vis_channels)
if (feyngraph_set_is_valid (phs_config%feyngraph_set)) then
exit
else
call msg_message ("Phase space: ... failed. &
&Increasing phs_off_shell ...")
call phs_config%feyngraph_set%final ()
deallocate (phs_config%feyngraph_set)
end if
end do
close (unit_fds)
else
allocate (phs_config%cascade_set)
do extra_off_shell = 0, max (phs_config%n_tot - 3, 0)
phs_config%par%off_shell = off_shell + extra_off_shell
call cascade_set_generate (phs_config%cascade_set, &
phs_config%model, phs_config%n_in, phs_config%n_out, &
phs_config%flv, &
phs_config%par, phs_config%fatal_beam_decay)
if (cascade_set_is_valid (phs_config%cascade_set)) then
exit
else
call msg_message ("Phase space: ... failed. &
&Increasing phs_off_shell ...")
end if
end do
end if
if (phs_config%use_cascades2) then
valid = feyngraph_set_is_valid (phs_config%feyngraph_set)
else
valid = cascade_set_is_valid (phs_config%cascade_set)
end if
if (valid) then
call msg_message ("Phase space: ... success.")
else
call msg_fatal ("Phase-space: generation failed")
end if
end subroutine phs_wood_config_generate_phase_space
@ %def phs_wood_config_generate_phase_space
@ Using the generated phase-space configuration, write an appropriate
phase-space file to the stored (or explicitly specified) I/O unit.
<<PHS wood: phs wood config: TBP>>=
procedure :: write_phase_space => phs_wood_config_write_phase_space
<<PHS wood: procedures>>=
subroutine phs_wood_config_write_phase_space (phs_config, &
filename_vis, unit)
class(phs_wood_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
type(string_t), intent(in), optional :: filename_vis
type(string_t) :: setenv_tex, setenv_mp, pipe, pipe_dvi
integer :: u, unit_tex, unit_dev, status
if (allocated (phs_config%cascade_set) .or. allocated (phs_config%feyngraph_set)) then
if (present (unit)) then
u = unit
else
u = phs_config%io_unit
end if
write (u, "(1x,A,A)") "process ", char (phs_config%id)
write (u, "(A)")
if (phs_config%use_cascades2) then
call feyngraph_set_write_process_bincode_format (phs_config%feyngraph_set, u)
else
call cascade_set_write_process_bincode_format (phs_config%cascade_set, u)
end if
write (u, "(A)")
write (u, "(3x,A,A,A32,A)") "md5sum_process = ", &
'"', phs_config%md5sum_process, '"'
write (u, "(3x,A,A,A32,A)") "md5sum_model_par = ", &
'"', phs_config%md5sum_model_par, '"'
write (u, "(3x,A,A,A32,A)") "md5sum_phs_config = ", &
'"', phs_config%md5sum_phs_config, '"'
call phs_config%par%write (u)
if (phs_config%use_cascades2) then
call feyngraph_set_write_file_format (phs_config%feyngraph_set, u)
else
call cascade_set_write_file_format (phs_config%cascade_set, u)
end if
if (phs_config%vis_channels) then
unit_tex = free_unit ()
open (unit=unit_tex, file=char(filename_vis // ".tex"), &
action="write", status="replace")
if (phs_config%use_cascades2) then
call feyngraph_set_write_graph_format (phs_config%feyngraph_set, &
filename_vis // "-graphs", phs_config%id, unit_tex)
else
call cascade_set_write_graph_format (phs_config%cascade_set, &
filename_vis // "-graphs", phs_config%id, unit_tex)
end if
close (unit_tex)
call msg_message ("Phase space: visualizing channels in file " &
// char(trim(filename_vis)) // "...")
if (phs_config%os_data%event_analysis_ps) then
BLOCK: do
unit_dev = free_unit ()
open (file = "/dev/null", unit = unit_dev, &
action = "write", iostat = status)
if (status /= 0) then
pipe = ""
pipe_dvi = ""
else
pipe = " > /dev/null"
pipe_dvi = " 2>/dev/null 1>/dev/null"
end if
close (unit_dev)
if (phs_config%os_data%whizard_texpath /= "") then
setenv_tex = "TEXINPUTS=" // &
phs_config%os_data%whizard_texpath // ":$TEXINPUTS "
setenv_mp = "MPINPUTS=" // &
phs_config%os_data%whizard_texpath // ":$MPINPUTS "
else
setenv_tex = ""
setenv_mp = ""
end if
call os_system_call (setenv_tex // &
phs_config%os_data%latex // " " // &
filename_vis // ".tex " // pipe, status)
if (status /= 0) exit BLOCK
if (phs_config%os_data%mpost /= "") then
call os_system_call (setenv_mp // &
phs_config%os_data%mpost // " " // &
filename_vis // "-graphs.mp" // pipe, status)
else
call msg_fatal ("Could not use MetaPOST.")
end if
if (status /= 0) exit BLOCK
call os_system_call (setenv_tex // &
phs_config%os_data%latex // " " // &
filename_vis // ".tex" // pipe, status)
if (status /= 0) exit BLOCK
call os_system_call &
(phs_config%os_data%dvips // " -o " // filename_vis &
// ".ps " // filename_vis // ".dvi" // pipe_dvi, status)
if (status /= 0) exit BLOCK
if (phs_config%os_data%event_analysis_pdf) then
call os_system_call (phs_config%os_data%ps2pdf // " " // &
filename_vis // ".ps", status)
if (status /= 0) exit BLOCK
end if
exit BLOCK
end do BLOCK
if (status /= 0) then
call msg_error ("Unable to compile analysis output file")
end if
end if
end if
else
call msg_fatal ("Phase-space configuration: &
&no phase space object generated")
end if
end subroutine phs_wood_config_write_phase_space
@ %def phs_config_write_phase_space
@ Clear the phase-space configuration. This is useful since the
object may become \emph{really} large.
<<PHS wood: phs wood config: TBP>>=
procedure :: clear_phase_space => phs_wood_config_clear_phase_space
<<PHS wood: procedures>>=
subroutine phs_wood_config_clear_phase_space (phs_config)
class(phs_wood_config_t), intent(inout) :: phs_config
if (allocated (phs_config%cascade_set)) then
call cascade_set_final (phs_config%cascade_set)
deallocate (phs_config%cascade_set)
end if
if (allocated (phs_config%feyngraph_set)) then
call phs_config%feyngraph_set%final ()
deallocate (phs_config%feyngraph_set)
end if
end subroutine phs_wood_config_clear_phase_space
@ %def phs_wood_config_clear_phase_space
@
Extract the set of resonance histories
<<PHS wood: phs wood config: TBP>>=
procedure :: extract_resonance_history_set &
=> phs_wood_config_extract_resonance_history_set
<<PHS wood: procedures>>=
subroutine phs_wood_config_extract_resonance_history_set &
(phs_config, res_set, include_trivial)
class(phs_wood_config_t), intent(in) :: phs_config
type(resonance_history_set_t), intent(out) :: res_set
logical, intent(in), optional :: include_trivial
call phs_config%forest%extract_resonance_history_set &
(res_set, include_trivial)
end subroutine phs_wood_config_extract_resonance_history_set
@ %def phs_wood_config_extract_resonance_history_set
@
\subsection{Phase-space configuration}
We read the phase-space configuration from the stored I/O unit. If
this is not set, we assume that we have to generate a phase space
configuration. When done, we open a scratch file and write the
configuration.
If [[rebuild]] is set, we should trash any existing phase space file
and build a new one. Otherwise, we try to use an old one, which we
check for existence and integrity. If [[ignore_mismatch]] is set, we
reuse an existing file even if it does not match the current setup.
<<PHS wood: phs wood config: TBP>>=
procedure :: configure => phs_wood_config_configure
<<PHS wood: procedures>>=
subroutine phs_wood_config_configure (phs_config, sqrts, &
sqrts_fixed, cm_frame, azimuthal_dependence, rebuild, ignore_mismatch, &
nlo_type, subdir)
class(phs_wood_config_t), intent(inout) :: phs_config
real(default), intent(in) :: sqrts
logical, intent(in), optional :: sqrts_fixed
logical, intent(in), optional :: cm_frame
logical, intent(in), optional :: azimuthal_dependence
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
integer, intent(in), optional :: nlo_type
type(string_t), intent(in), optional :: subdir
type(string_t) :: filename, filename_vis
logical :: variable_limits
logical :: ok, exist, found, check, match, rebuild_phs
integer :: g, c0, c1, n
if (present (nlo_type)) then
phs_config%nlo_type = nlo_type
else
phs_config%nlo_type = BORN
end if
phs_config%sqrts = sqrts
phs_config%par%sqrts = sqrts
if (present (sqrts_fixed)) &
phs_config%sqrts_fixed = sqrts_fixed
if (present (cm_frame)) &
phs_config%cm_frame = cm_frame
if (present (azimuthal_dependence)) &
phs_config%azimuthal_dependence = azimuthal_dependence
if (present (rebuild)) then
rebuild_phs = rebuild
else
rebuild_phs = .true.
end if
if (present (ignore_mismatch)) then
check = .not. ignore_mismatch
if (ignore_mismatch) &
call msg_warning ("Reading phs file: MD5 sum check disabled")
else
check = .true.
end if
phs_config%md5sum_forest = ""
call phs_config%compute_md5sum (include_id = .false.)
if (phs_config%io_unit == 0) then
filename = phs_config%make_phs_filename (subdir)
filename_vis = phs_config%make_phs_filename (subdir) // "-vis"
if (.not. rebuild_phs) then
if (check) then
call phs_config%read_phs_file (exist, found, match, subdir=subdir)
rebuild_phs = .not. (exist .and. found .and. match)
else
call phs_config%read_phs_file (exist, found, subdir=subdir)
rebuild_phs = .not. (exist .and. found)
end if
end if
if (.not. mpi_is_comm_master ()) then
rebuild_phs = .false.
call msg_message ("MPI: Workers do not build phase space configuration.")
end if
if (rebuild_phs) then
call phs_config%generate_phase_space ()
phs_config%io_unit = free_unit ()
if (phs_config%id /= "") then
call msg_message ("Phase space: writing configuration file '" &
// char (filename) // "'")
open (phs_config%io_unit, file = char (filename), &
status = "replace", action = "readwrite")
else
open (phs_config%io_unit, status = "scratch", action = "readwrite")
end if
call phs_config%write_phase_space (filename_vis)
rewind (phs_config%io_unit)
else
call msg_message ("Phase space: keeping configuration file '" &
// char (filename) // "'")
end if
end if
if (phs_config%io_unit == 0) then
ok = .true.
else
call phs_forest_read (phs_config%forest, phs_config%io_unit, &
phs_config%id, phs_config%n_in, phs_config%n_out, &
phs_config%model, ok)
if (.not. phs_config%io_unit_keep_open) then
close (phs_config%io_unit)
phs_config%io_unit = 0
end if
end if
if (ok) then
call phs_forest_set_flavors (phs_config%forest, phs_config%flv(:,1))
variable_limits = .not. phs_config%cm_frame
call phs_forest_set_parameters &
(phs_config%forest, phs_config%mapping_defaults, variable_limits)
call phs_forest_setup_prt_combinations (phs_config%forest)
phs_config%n_channel = phs_forest_get_n_channels (phs_config%forest)
phs_config%n_par = phs_forest_get_n_parameters (phs_config%forest)
allocate (phs_config%channel (phs_config%n_channel))
if (phs_config%use_equivalences) then
call phs_forest_set_equivalences (phs_config%forest)
call phs_forest_get_equivalences (phs_config%forest, &
phs_config%channel, phs_config%azimuthal_dependence)
phs_config%provides_equivalences = .true.
end if
call phs_forest_set_s_mappings (phs_config%forest)
call phs_config%record_on_shell ()
if (phs_config%mapping_defaults%enable_s_mapping) then
call phs_config%record_s_mappings ()
end if
allocate (phs_config%chain (phs_config%n_channel), source = 0)
do g = 1, phs_forest_get_n_groves (phs_config%forest)
call phs_forest_get_grove_bounds (phs_config%forest, g, c0, c1, n)
phs_config%chain (c0:c1) = g
end do
phs_config%provides_chains = .true.
call phs_config%compute_md5sum_forest ()
else
write (msg_buffer, "(A,A,A)") &
"Phase space: process '", &
char (phs_config%id), "' not found in configuration file"
call msg_fatal ()
end if
end subroutine phs_wood_config_configure
@ %def phs_wood_config_configure
@ The MD5 sum of the forest is computed in addition to the MD5 sum of
the configuration. The reason is that the forest may depend on a
user-provided external file. On the other hand, this MD5 sum encodes
all information that is relevant for further processing. Therefore,
the [[get_md5sum]] method returns this result, once it is available.
<<PHS wood: phs wood config: TBP>>=
procedure :: compute_md5sum_forest => phs_wood_config_compute_md5sum_forest
<<PHS wood: procedures>>=
subroutine phs_wood_config_compute_md5sum_forest (phs_config)
class(phs_wood_config_t), intent(inout) :: phs_config
integer :: u
u = free_unit ()
open (u, status = "scratch", action = "readwrite")
call phs_config%write_forest (u)
rewind (u)
phs_config%md5sum_forest = md5sum (u)
close (u)
end subroutine phs_wood_config_compute_md5sum_forest
@ %def phs_wood_config_compute_md5sum_forest
@ Create filenames according to standard conventions. The [[id]] is the
process name including the suffix [[_iX]] where [[X]] stands for the component
identifier (an integer). The [[run_id]] may be set or unset.
The convention for file names that include the run ID is to separate prefix, run
ID, and any extensions by dots. We construct the file name by concatenating
the individual elements accordingly. If there is no run ID, we nevertheless
replace [[_iX]] by [[.iX]].
<<PHS wood: phs wood config: TBP>>=
procedure :: make_phs_filename => phs_wood_make_phs_filename
<<PHS wood: procedures>>=
function phs_wood_make_phs_filename (phs_config, subdir) result (filename)
class(phs_wood_config_t), intent(in) :: phs_config
type(string_t), intent(in), optional :: subdir
type(string_t) :: filename
type(string_t) :: basename, suffix, comp_code, comp_index
basename = phs_config%id
call split (basename, suffix, "_", back=.true.)
comp_code = extract (suffix, 1, 1)
comp_index = extract (suffix, 2)
if (comp_code == "i" .and. verify (comp_index, "1234567890") == 0) then
suffix = "." // comp_code // comp_index
else
basename = phs_config%id
suffix = ""
end if
if (phs_config%run_id /= "") then
filename = basename // "." // phs_config%run_id // suffix // ".phs"
else
filename = basename // suffix // ".phs"
end if
if (present (subdir)) then
filename = subdir // "/" // filename
end if
end function phs_wood_make_phs_filename
-
+
@ %def phs_wood_make_phs_filename
@
<<PHS wood: phs wood config: TBP>>=
procedure :: reshuffle_flavors => phs_wood_config_reshuffle_flavors
<<PHS wood: procedures>>=
subroutine phs_wood_config_reshuffle_flavors (phs_config, reshuffle, flv_extra)
class(phs_wood_config_t), intent(inout) :: phs_config
integer, intent(in), dimension(:), allocatable :: reshuffle
type(flavor_t), intent(in) :: flv_extra
call phs_forest_set_flavors (phs_config%forest, phs_config%flv(:,1), reshuffle, flv_extra)
end subroutine phs_wood_config_reshuffle_flavors
@ %def phs_wood_config_reshuffle_flavors
@
<<PHS wood: phs wood config: TBP>>=
procedure :: set_momentum_links => phs_wood_config_set_momentum_links
<<PHS wood: procedures>>=
subroutine phs_wood_config_set_momentum_links (phs_config, reshuffle)
class(phs_wood_config_t), intent(inout) :: phs_config
integer, intent(in), dimension(:), allocatable :: reshuffle
call phs_forest_set_momentum_links (phs_config%forest, reshuffle)
end subroutine phs_wood_config_set_momentum_links
@ %def phs_wood_config_set_momentum_links
@ Identify resonances which are marked by s-channel mappings for the
whole phase space and report them to the channel array.
<<PHS wood: phs wood config: TBP>>=
procedure :: record_s_mappings => phs_wood_config_record_s_mappings
<<PHS wood: procedures>>=
subroutine phs_wood_config_record_s_mappings (phs_config)
class(phs_wood_config_t), intent(inout) :: phs_config
logical :: flag
real(default) :: mass, width
integer :: c
do c = 1, phs_config%n_channel
call phs_forest_get_s_mapping (phs_config%forest, c, flag, mass, width)
if (flag) then
if (mass == 0) then
call msg_fatal ("Phase space: s-channel resonance " &
// " has zero mass")
end if
if (width == 0) then
call msg_fatal ("Phase space: s-channel resonance " &
// " has zero width")
end if
call phs_config%channel(c)%set_resonant (mass, width)
end if
end do
end subroutine phs_wood_config_record_s_mappings
@ %def phs_wood_config_record_s_mappings
@ Identify on-shell mappings for the whole phase space and report them
to the channel array.
<<PHS wood: phs wood config: TBP>>=
procedure :: record_on_shell => phs_wood_config_record_on_shell
<<PHS wood: procedures>>=
subroutine phs_wood_config_record_on_shell (phs_config)
class(phs_wood_config_t), intent(inout) :: phs_config
logical :: flag
real(default) :: mass
integer :: c
do c = 1, phs_config%n_channel
call phs_forest_get_on_shell (phs_config%forest, c, flag, mass)
if (flag) then
call phs_config%channel(c)%set_on_shell (mass)
end if
end do
end subroutine phs_wood_config_record_on_shell
@ %def phs_wood_config_record_on_shell
@ Return the most relevant MD5 sum. This overrides the method of the
base type.
<<PHS wood: phs wood config: TBP>>=
procedure :: get_md5sum => phs_wood_config_get_md5sum
<<PHS wood: procedures>>=
function phs_wood_config_get_md5sum (phs_config) result (md5sum)
class(phs_wood_config_t), intent(in) :: phs_config
character(32) :: md5sum
if (phs_config%md5sum_forest /= "") then
md5sum = phs_config%md5sum_forest
else
md5sum = phs_config%md5sum_phs_config
end if
end function phs_wood_config_get_md5sum
@ %def phs_wood_config_get_md5sum
@ Check whether a phase-space configuration for the current process exists.
We look for the phase-space file that should correspond to the current
process. If we find it, we check the MD5 sums stored in the file against the
MD5 sums in the current configuration (if required).
If successful, read the PHS file.
<<PHS wood: phs wood config: TBP>>=
procedure :: read_phs_file => phs_wood_read_phs_file
<<PHS wood: procedures>>=
subroutine phs_wood_read_phs_file (phs_config, exist, found, match, subdir)
class(phs_wood_config_t), intent(inout) :: phs_config
logical, intent(out) :: exist
logical, intent(out) :: found
logical, intent(out), optional :: match
type(string_t), intent(in), optional :: subdir
type(string_t) :: filename
integer :: u
filename = phs_config%make_phs_filename (subdir)
inquire (file = char (filename), exist = exist)
if (exist) then
u = free_unit ()
open (u, file = char (filename), action = "read", status = "old")
call phs_forest_read (phs_config%forest, u, &
phs_config%id, phs_config%n_in, phs_config%n_out, &
phs_config%model, found, &
phs_config%md5sum_process, &
phs_config%md5sum_model_par, &
phs_config%md5sum_phs_config, &
match = match)
close (u)
else
found = .false.
if (present (match)) match = .false.
end if
end subroutine phs_wood_read_phs_file
@ %def phs_wood_read_phs_file
@ Startup message, after configuration is complete.
<<PHS wood: phs wood config: TBP>>=
procedure :: startup_message => phs_wood_config_startup_message
<<PHS wood: procedures>>=
subroutine phs_wood_config_startup_message (phs_config, unit)
class(phs_wood_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
integer :: n_groves, n_eq
n_groves = phs_forest_get_n_groves (phs_config%forest)
n_eq = phs_forest_get_n_equivalences (phs_config%forest)
call phs_config%base_startup_message (unit)
if (phs_config%n_channel == 1) then
write (msg_buffer, "(A,2(I0,A))") &
"Phase space: found ", phs_config%n_channel, &
" channel, collected in ", n_groves, &
" grove."
else if (n_groves == 1) then
write (msg_buffer, "(A,2(I0,A))") &
"Phase space: found ", phs_config%n_channel, &
" channels, collected in ", n_groves, &
" grove."
else
write (msg_buffer, "(A,2(I0,A))") &
"Phase space: found ", phs_config%n_channel, &
" channels, collected in ", &
phs_forest_get_n_groves (phs_config%forest), &
" groves."
end if
call msg_message (unit = unit)
if (phs_config%use_equivalences) then
if (n_eq == 1) then
write (msg_buffer, "(A,I0,A)") &
"Phase space: Using ", n_eq, &
" equivalence between channels."
else
write (msg_buffer, "(A,I0,A)") &
"Phase space: Using ", n_eq, &
" equivalences between channels."
end if
else
write (msg_buffer, "(A)") &
"Phase space: no equivalences between channels used."
end if
call msg_message (unit = unit)
write (msg_buffer, "(A,2(1x,I0,1x,A))") &
"Phase space: wood"
call msg_message (unit = unit)
end subroutine phs_wood_config_startup_message
@ %def phs_wood_config_startup_message
@ Allocate an instance: the actual phase-space object.
<<PHS wood: phs wood config: TBP>>=
procedure, nopass :: allocate_instance => phs_wood_config_allocate_instance
<<PHS wood: procedures>>=
subroutine phs_wood_config_allocate_instance (phs)
class(phs_t), intent(inout), pointer :: phs
allocate (phs_wood_t :: phs)
end subroutine phs_wood_config_allocate_instance
@ %def phs_wood_config_allocate_instance
@
\subsection{Kinematics implementation}
We generate $\cos\theta$ and $\phi$ uniformly, covering the solid angle.
<<PHS wood: public>>=
public :: phs_wood_t
<<PHS wood: types>>=
type, extends (phs_t) :: phs_wood_t
real(default) :: sqrts = 0
type(phs_forest_t) :: forest
real(default), dimension(3) :: r_real
integer :: n_r_born = 0
contains
<<PHS wood: phs wood: TBP>>
end type phs_wood_t
@ %def phs_wood_t
@ Output. The [[verbose]] setting is irrelevant, we just display the contents
of the base object.
<<PHS wood: phs wood: TBP>>=
procedure :: write => phs_wood_write
<<PHS wood: procedures>>=
subroutine phs_wood_write (object, unit, verbose)
class(phs_wood_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit)
call object%base_write (u)
end subroutine phs_wood_write
@ %def phs_wood_write
@ Write the forest separately.
<<PHS wood: phs wood: TBP>>=
procedure :: write_forest => phs_wood_write_forest
<<PHS wood: procedures>>=
subroutine phs_wood_write_forest (object, unit)
class(phs_wood_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
call phs_forest_write (object%forest, u)
end subroutine phs_wood_write_forest
@ %def phs_wood_write_forest
@ Finalizer.
<<PHS wood: phs wood: TBP>>=
procedure :: final => phs_wood_final
<<PHS wood: procedures>>=
subroutine phs_wood_final (object)
class(phs_wood_t), intent(inout) :: object
call phs_forest_final (object%forest)
end subroutine phs_wood_final
@ %def phs_wood_final
@ Initialization. We allocate arrays ([[base_init]]) and adjust the
phase-space volume. The two-particle phase space volume is
\begin{equation}
\Phi_2 = \frac{1}{4(2\pi)^5} = 2.55294034614 \times 10^{-5}
\end{equation}
independent of the particle masses.
<<PHS wood: phs wood: TBP>>=
procedure :: init => phs_wood_init
<<PHS wood: procedures>>=
subroutine phs_wood_init (phs, phs_config)
class(phs_wood_t), intent(out) :: phs
class(phs_config_t), intent(in), target :: phs_config
call phs%base_init (phs_config)
select type (phs_config)
type is (phs_wood_config_t)
phs%forest = phs_config%forest
select case (phs_config%extension_mode)
case (EXTENSION_DEFAULT)
phs%n_r_born = phs_config%n_par - 3
case (EXTENSION_DGLAP)
phs%n_r_born = phs_config%n_par - 4
end select
end select
end subroutine phs_wood_init
@ %def phs_wood_init
@
\subsection{Evaluation}
We compute the outgoing momenta from the incoming momenta and
the input parameter set [[r_in]] in channel [[r_in]]. We also compute the
[[r]] parameters and Jacobians [[f]] for all other channels.
We do \emph{not} need to a apply a transformation from/to the c.m.\ frame,
because in [[phs_base]] the momenta are already boosted to the c.m.\ frame
before assigning them in the [[phs]] object, and inversely boosted when
extracting them.
<<PHS wood: phs wood: TBP>>=
procedure :: evaluate_selected_channel => phs_wood_evaluate_selected_channel
procedure :: evaluate_other_channels => phs_wood_evaluate_other_channels
<<PHS wood: procedures>>=
subroutine phs_wood_evaluate_selected_channel (phs, c_in, r_in)
class(phs_wood_t), intent(inout) :: phs
integer, intent(in) :: c_in
real(default), intent(in), dimension(:) :: r_in
logical :: ok
phs%q_defined = .false.
if (phs%p_defined) then
call phs_forest_set_prt_in (phs%forest, phs%p)
phs%r(:,c_in) = r_in
call phs_forest_evaluate_selected_channel (phs%forest, &
c_in, phs%active_channel, &
phs%sqrts_hat, phs%r, phs%f, phs%volume, ok)
select type (config => phs%config)
type is (phs_wood_config_t)
if (config%extension_mode > EXTENSION_NONE) then
if (phs%n_r_born > 0) then
phs%r_real = r_in (phs%n_r_born + 1 : phs%n_r_born + 3)
else
call msg_fatal ("n_r_born should be larger than 0!")
end if
end if
end select
if (ok) then
phs%q = phs_forest_get_momenta_out (phs%forest)
phs%q_defined = .true.
end if
end if
end subroutine phs_wood_evaluate_selected_channel
subroutine phs_wood_evaluate_other_channels (phs, c_in)
class(phs_wood_t), intent(inout) :: phs
integer, intent(in) :: c_in
integer :: c
if (phs%q_defined) then
call phs_forest_evaluate_other_channels (phs%forest, &
c_in, phs%active_channel, &
phs%sqrts_hat, phs%r, phs%f, combine=.true.)
select type (config => phs%config)
type is (phs_wood_config_t)
if (config%extension_mode > EXTENSION_NONE) then
if (phs%n_r_born > 0) then
do c = 1, size (phs%r, 2)
phs%r(phs%n_r_born + 1 : phs%n_r_born + 3, c) = phs%r_real
end do
else
phs%r_defined = .false.
end if
end if
end select
phs%r_defined = .true.
end if
end subroutine phs_wood_evaluate_other_channels
@ %def phs_wood_evaluate_selected_channel
@ %def phs_wood_evaluate_other_channels
@ Inverse evaluation.
<<PHS wood: phs wood: TBP>>=
procedure :: inverse => phs_wood_inverse
<<PHS wood: procedures>>=
subroutine phs_wood_inverse (phs)
class(phs_wood_t), intent(inout) :: phs
if (phs%p_defined .and. phs%q_defined) then
call phs_forest_set_prt_in (phs%forest, phs%p)
call phs_forest_set_prt_out (phs%forest, phs%q)
call phs_forest_recover_channel (phs%forest, &
1, &
phs%sqrts_hat, phs%r, phs%f, phs%volume)
call phs_forest_evaluate_other_channels (phs%forest, &
1, phs%active_channel, &
phs%sqrts_hat, phs%r, phs%f, combine=.false.)
phs%r_defined = .true.
end if
end subroutine phs_wood_inverse
@ %def phs_wood_inverse
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[phs_wood_ut.f90]]>>=
<<File header>>
module phs_wood_ut
use unit_tests
use phs_wood_uti
<<Standard module head>>
<<PHS wood: public test>>
<<PHS wood: public test auxiliary>>
contains
<<PHS wood: test driver>>
end module phs_wood_ut
@ %def phs_wood_ut
@
<<[[phs_wood_uti.f90]]>>=
<<File header>>
module phs_wood_uti
<<Use kinds>>
<<Use strings>>
use io_units
use os_interface
use lorentz
use flavors
use model_data
use process_constants
use mappings
use phs_base
use phs_forests
use phs_wood
use phs_base_ut, only: init_test_process_data, init_test_decay_data
<<Standard module head>>
<<PHS wood: public test auxiliary>>
<<PHS wood: test declarations>>
contains
<<PHS wood: tests>>
<<PHS wood: test auxiliary>>
end module phs_wood_uti
@ %def phs_wood_ut
@ API: driver for the unit tests below.
<<PHS wood: public test>>=
public :: phs_wood_test
<<PHS wood: test driver>>=
subroutine phs_wood_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<PHS wood: execute tests>>
end subroutine phs_wood_test
@ %def phs_wood_test
<<PHS wood: public test>>=
public :: phs_wood_vis_test
<<PHS wood: test driver>>=
subroutine phs_wood_vis_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<PHS wood: execute vis tests>>
end subroutine phs_wood_vis_test
@ %def phs_wood_vis_test
@
\subsubsection{Phase-space configuration data}
Construct and display a test phase-space configuration object. Also
check the [[azimuthal_dependence]] flag.
This auxiliary routine writes a phase-space configuration file to unit
[[u_phs]].
<<PHS wood: public test auxiliary>>=
public :: write_test_phs_file
<<PHS wood: test auxiliary>>=
subroutine write_test_phs_file (u_phs, procname)
integer, intent(in) :: u_phs
type(string_t), intent(in), optional :: procname
if (present (procname)) then
write (u_phs, "(A,A)") "process ", char (procname)
else
write (u_phs, "(A)") "process testproc"
end if
write (u_phs, "(A,A)") " md5sum_process = ", '""'
write (u_phs, "(A,A)") " md5sum_model_par = ", '""'
write (u_phs, "(A,A)") " md5sum_phs_config = ", '""'
write (u_phs, "(A)") " sqrts = 1000"
write (u_phs, "(A)") " m_threshold_s = 50"
write (u_phs, "(A)") " m_threshold_t = 100"
write (u_phs, "(A)") " off_shell = 2"
write (u_phs, "(A)") " t_channel = 6"
write (u_phs, "(A)") " keep_nonresonant = T"
write (u_phs, "(A)") " grove #1"
write (u_phs, "(A)") " tree 3"
end subroutine write_test_phs_file
@ %def write_test_phs_file
@
<<PHS wood: execute tests>>=
call test (phs_wood_1, "phs_wood_1", &
"phase-space configuration", &
u, results)
<<PHS wood: test declarations>>=
public :: phs_wood_1
<<PHS wood: tests>>=
subroutine phs_wood_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
class(phs_config_t), allocatable :: phs_data
type(mapping_defaults_t) :: mapping_defaults
real(default) :: sqrts
integer :: u_phs, iostat
character(32) :: buffer
write (u, "(A)") "* Test output: phs_wood_1"
write (u, "(A)") "* Purpose: initialize and display &
&phase-space configuration data"
write (u, "(A)")
call model%init_test ()
call syntax_phs_forest_init ()
write (u, "(A)") "* Initialize a process"
write (u, "(A)")
call init_test_process_data (var_str ("phs_wood_1"), process_data)
write (u, "(A)") "* Create a scratch phase-space file"
write (u, "(A)")
u_phs = free_unit ()
open (u_phs, status = "scratch", action = "readwrite")
call write_test_phs_file (u_phs, var_str ("phs_wood_1"))
rewind (u_phs)
do
read (u_phs, "(A)", iostat = iostat) buffer
if (iostat /= 0) exit
write (u, "(A)") trim (buffer)
end do
write (u, "(A)")
write (u, "(A)") "* Setup phase-space configuration object"
write (u, "(A)")
mapping_defaults%step_mapping = .false.
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_input (u_phs)
call phs_data%set_mapping_defaults (mapping_defaults)
end select
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs_data%write (u)
write (u, "(A)")
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%write_forest (u)
end select
write (u, "(A)")
write (u, "(A)") "* Cleanup"
close (u_phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_wood_1"
end subroutine phs_wood_1
@ %def phs_wood_1
@
\subsubsection{Phase space evaluation}
Compute kinematics for given parameters, also invert the calculation.
<<PHS wood: execute tests>>=
call test (phs_wood_2, "phs_wood_2", &
"phase-space evaluation", &
u, results)
<<PHS wood: test declarations>>=
public :: phs_wood_2
<<PHS wood: tests>>=
subroutine phs_wood_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(process_constants_t) :: process_data
real(default) :: sqrts, E
class(phs_config_t), allocatable, target :: phs_data
class(phs_t), pointer :: phs => null ()
type(vector4_t), dimension(2) :: p, q
integer :: u_phs
write (u, "(A)") "* Test output: phs_wood_2"
write (u, "(A)") "* Purpose: test simple single-channel phase space"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
write (u, "(A)") "* Initialize a process and a matching &
&phase-space configuration"
write (u, "(A)")
call init_test_process_data (var_str ("phs_wood_2"), process_data)
u_phs = free_unit ()
open (u_phs, status = "scratch", action = "readwrite")
call write_test_phs_file (u_phs, var_str ("phs_wood_2"))
rewind (u_phs)
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_input (u_phs)
end select
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs_data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%write (u, verbose=.true.)
write (u, "(A)")
write (u, "(A)") "* Set incoming momenta"
write (u, "(A)")
E = sqrts / 2
p(1) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
p(2) = vector4_moving (E,-sqrt (E**2 - flv%get_mass ()**2), 3)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Compute phase-space point &
&for x = 0.125, 0.5"
write (u, "(A)")
call phs%evaluate_selected_channel (1, [0.125_default, 0.5_default])
call phs%evaluate_other_channels (1)
call phs%write (u)
write (u, "(A)")
select type (phs)
type is (phs_wood_t)
call phs%write_forest (u)
end select
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
call phs%final ()
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call phs%write (u)
write (u, "(A)")
select type (phs)
type is (phs_wood_t)
call phs%write_forest (u)
end select
call phs%final ()
deallocate (phs)
close (u_phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_wood_2"
end subroutine phs_wood_2
@ %def phs_wood_2
@
\subsubsection{Phase-space generation}
Generate phase space for a simple process.
<<PHS wood: execute tests>>=
call test (phs_wood_3, "phs_wood_3", &
"phase-space generation", &
u, results)
<<PHS wood: test declarations>>=
public :: phs_wood_3
<<PHS wood: tests>>=
subroutine phs_wood_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
type(phs_parameters_t) :: phs_par
class(phs_config_t), allocatable :: phs_data
integer :: iostat
character(80) :: buffer
write (u, "(A)") "* Test output: phs_wood_3"
write (u, "(A)") "* Purpose: generate a phase-space configuration"
write (u, "(A)")
call model%init_test ()
call syntax_phs_forest_init ()
write (u, "(A)") "* Initialize a process and phase-space parameters"
write (u, "(A)")
call init_test_process_data (var_str ("phs_wood_3"), process_data)
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
phs_par%sqrts = 1000
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
phs_data%io_unit_keep_open = .true.
end select
write (u, "(A)")
write (u, "(A)") "* Generate a scratch phase-space file"
write (u, "(A)")
call phs_data%configure (phs_par%sqrts)
select type (phs_data)
type is (phs_wood_config_t)
rewind (phs_data%io_unit)
do
read (phs_data%io_unit, "(A)", iostat = iostat) buffer
if (iostat /= 0) exit
write (u, "(A)") trim (buffer)
end do
end select
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_wood_3"
end subroutine phs_wood_3
@ %def phs_wood_3
@
\subsubsection{Nontrivial process}
Generate phase space for a $2\to 3$ process.
<<PHS wood: execute tests>>=
call test (phs_wood_4, "phs_wood_4", &
"nontrivial process", &
u, results)
<<PHS wood: test declarations>>=
public :: phs_wood_4
<<PHS wood: tests>>=
subroutine phs_wood_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
type(phs_parameters_t) :: phs_par
class(phs_config_t), allocatable, target :: phs_data
integer :: iostat
character(80) :: buffer
class(phs_t), pointer :: phs => null ()
real(default) :: E, pL
type(vector4_t), dimension(2) :: p
type(vector4_t), dimension(3) :: q
write (u, "(A)") "* Test output: phs_wood_4"
write (u, "(A)") "* Purpose: generate a phase-space configuration"
write (u, "(A)")
call model%init_test ()
call syntax_phs_forest_init ()
write (u, "(A)") "* Initialize a process and phase-space parameters"
write (u, "(A)")
process_data%id = "phs_wood_4"
process_data%model_name = "Test"
process_data%n_in = 2
process_data%n_out = 3
process_data%n_flv = 1
allocate (process_data%flv_state (process_data%n_in + process_data%n_out, &
process_data%n_flv))
process_data%flv_state(:,1) = [25, 25, 25, 6, -6]
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
phs_par%sqrts = 1000
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
phs_data%io_unit_keep_open = .true.
end select
write (u, "(A)")
write (u, "(A)") "* Generate a scratch phase-space file"
write (u, "(A)")
call phs_data%configure (phs_par%sqrts)
select type (phs_data)
type is (phs_wood_config_t)
rewind (phs_data%io_unit)
do
read (phs_data%io_unit, "(A)", iostat = iostat) buffer
if (iostat /= 0) exit
write (u, "(A)") trim (buffer)
end do
end select
write (u, "(A)")
write (u, "(A)") "* Initialize the phase-space instance"
write (u, "(A)")
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
write (u, "(A)") "* Set incoming momenta"
write (u, "(A)")
select type (phs_data)
type is (phs_wood_config_t)
E = phs_data%sqrts / 2
pL = sqrt (E**2 - phs_data%flv(1,1)%get_mass ()**2)
end select
p(1) = vector4_moving (E, pL, 3)
p(2) = vector4_moving (E, -pL, 3)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
write (u, "(A)") "* Compute phase-space point &
&for x = 0.1, 0.2, 0.3, 0.4, 0.5"
write (u, "(A)")
call phs%evaluate_selected_channel (1, &
[0.1_default, 0.2_default, 0.3_default, 0.4_default, 0.5_default])
call phs%evaluate_other_channels (1)
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
call phs%get_outgoing_momenta (q)
call phs%final ()
deallocate (phs)
call phs_data%allocate_instance (phs)
call phs%init (phs_data)
call phs%set_incoming_momenta (p)
call phs%compute_flux ()
call phs%set_outgoing_momenta (q)
call phs%inverse ()
call phs%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call phs%final ()
deallocate (phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_wood_4"
end subroutine phs_wood_4
@ %def phs_wood_4
@
\subsubsection{Equivalences}
Generate phase space for a simple process, including channel equivalences.
<<PHS wood: execute tests>>=
call test (phs_wood_5, "phs_wood_5", &
"equivalences", &
u, results)
<<PHS wood: test declarations>>=
public :: phs_wood_5
<<PHS wood: tests>>=
subroutine phs_wood_5 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
type(phs_parameters_t) :: phs_par
class(phs_config_t), allocatable :: phs_data
write (u, "(A)") "* Test output: phs_wood_5"
write (u, "(A)") "* Purpose: generate a phase-space configuration"
write (u, "(A)")
call model%init_test ()
call syntax_phs_forest_init ()
write (u, "(A)") "* Initialize a process and phase-space parameters"
write (u, "(A)")
call init_test_process_data (var_str ("phs_wood_5"), process_data)
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
phs_par%sqrts = 1000
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
call phs_data%enable_equivalences ()
end select
write (u, "(A)")
write (u, "(A)") "* Generate a scratch phase-space file"
write (u, "(A)")
call phs_data%configure (phs_par%sqrts)
call phs_data%write (u)
write (u, "(A)")
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%write_forest (u)
end select
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_wood_5"
end subroutine phs_wood_5
@ %def phs_wood_5
@
\subsubsection{MD5 sum checks}
Generate phase space for a simple process. Repeat this with and without
parameter change.
<<PHS wood: execute tests>>=
call test (phs_wood_6, "phs_wood_6", &
"phase-space generation", &
u, results)
<<PHS wood: test declarations>>=
public :: phs_wood_6
<<PHS wood: tests>>=
subroutine phs_wood_6 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(process_constants_t) :: process_data
type(phs_parameters_t) :: phs_par
class(phs_config_t), allocatable :: phs_data
logical :: exist, found, match
integer :: u_phs
character(*), parameter :: filename = "phs_wood_6_p.phs"
write (u, "(A)") "* Test output: phs_wood_6"
write (u, "(A)") "* Purpose: generate and check phase-space file"
write (u, "(A)")
call model%init_test ()
call syntax_phs_forest_init ()
write (u, "(A)") "* Initialize a process and phase-space parameters"
write (u, "(A)")
call init_test_process_data (var_str ("phs_wood_6"), process_data)
process_data%id = "phs_wood_6_p"
process_data%md5sum = "1234567890abcdef1234567890abcdef"
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
phs_par%sqrts = 1000
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
end select
write (u, "(A)") "* Remove previous phs file, if any"
write (u, "(A)")
inquire (file = filename, exist = exist)
if (exist) then
u_phs = free_unit ()
open (u_phs, file = filename, action = "write")
close (u_phs, status = "delete")
end if
write (u, "(A)") "* Check phase-space file (should fail)"
write (u, "(A)")
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%read_phs_file (exist, found, match)
write (u, "(1x,A,L1)") "exist = ", exist
write (u, "(1x,A,L1)") "found = ", found
write (u, "(1x,A,L1)") "match = ", match
end select
write (u, "(A)")
write (u, "(A)") "* Generate a phase-space file"
write (u, "(A)")
call phs_data%configure (phs_par%sqrts)
write (u, "(1x,A,A,A)") "MD5 sum (process) = '", &
phs_data%md5sum_process, "'"
write (u, "(1x,A,A,A)") "MD5 sum (model par) = '", &
phs_data%md5sum_model_par, "'"
write (u, "(1x,A,A,A)") "MD5 sum (phs config) = '", &
phs_data%md5sum_phs_config, "'"
write (u, "(A)")
write (u, "(A)") "* Check MD5 sum"
write (u, "(A)")
call phs_data%final ()
deallocate (phs_data)
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
phs_par%sqrts = 1000
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
phs_data%sqrts = phs_par%sqrts
phs_data%par%sqrts = phs_par%sqrts
end select
call phs_data%compute_md5sum ()
write (u, "(1x,A,A,A)") "MD5 sum (process) = '", &
phs_data%md5sum_process, "'"
write (u, "(1x,A,A,A)") "MD5 sum (model par) = '", &
phs_data%md5sum_model_par, "'"
write (u, "(1x,A,A,A)") "MD5 sum (phs config) = '", &
phs_data%md5sum_phs_config, "'"
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%read_phs_file (exist, found, match)
write (u, "(1x,A,L1)") "exist = ", exist
write (u, "(1x,A,L1)") "found = ", found
write (u, "(1x,A,L1)") "match = ", match
end select
write (u, "(A)")
write (u, "(A)") "* Modify sqrts and check MD5 sum"
write (u, "(A)")
call phs_data%final ()
deallocate (phs_data)
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
phs_par%sqrts = 500
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
phs_data%sqrts = phs_par%sqrts
phs_data%par%sqrts = phs_par%sqrts
end select
call phs_data%compute_md5sum ()
write (u, "(1x,A,A,A)") "MD5 sum (process) = '", &
phs_data%md5sum_process, "'"
write (u, "(1x,A,A,A)") "MD5 sum (model par) = '", &
phs_data%md5sum_model_par, "'"
write (u, "(1x,A,A,A)") "MD5 sum (phs config) = '", &
phs_data%md5sum_phs_config, "'"
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%read_phs_file (exist, found, match)
write (u, "(1x,A,L1)") "exist = ", exist
write (u, "(1x,A,L1)") "found = ", found
write (u, "(1x,A,L1)") "match = ", match
end select
write (u, "(A)")
write (u, "(A)") "* Modify process and check MD5 sum"
write (u, "(A)")
call phs_data%final ()
deallocate (phs_data)
process_data%md5sum = "77777777777777777777777777777777"
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
phs_par%sqrts = 1000
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
phs_data%sqrts = phs_par%sqrts
phs_data%par%sqrts = phs_par%sqrts
end select
call phs_data%compute_md5sum ()
write (u, "(1x,A,A,A)") "MD5 sum (process) = '", &
phs_data%md5sum_process, "'"
write (u, "(1x,A,A,A)") "MD5 sum (model par) = '", &
phs_data%md5sum_model_par, "'"
write (u, "(1x,A,A,A)") "MD5 sum (phs config) = '", &
phs_data%md5sum_phs_config, "'"
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%read_phs_file (exist, found, match)
write (u, "(1x,A,L1)") "exist = ", exist
write (u, "(1x,A,L1)") "found = ", found
write (u, "(1x,A,L1)") "match = ", match
end select
write (u, "(A)")
write (u, "(A)") "* Modify phs parameter and check MD5 sum"
write (u, "(A)")
call phs_data%final ()
deallocate (phs_data)
allocate (phs_wood_config_t :: phs_data)
process_data%md5sum = "1234567890abcdef1234567890abcdef"
call phs_data%init (process_data, model)
phs_par%sqrts = 1000
phs_par%off_shell = 17
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
phs_data%sqrts = phs_par%sqrts
phs_data%par%sqrts = phs_par%sqrts
end select
call phs_data%compute_md5sum ()
write (u, "(1x,A,A,A)") "MD5 sum (process) = '", &
phs_data%md5sum_process, "'"
write (u, "(1x,A,A,A)") "MD5 sum (model par) = '", &
phs_data%md5sum_model_par, "'"
write (u, "(1x,A,A,A)") "MD5 sum (phs config) = '", &
phs_data%md5sum_phs_config, "'"
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%read_phs_file (exist, found, match)
write (u, "(1x,A,L1)") "exist = ", exist
write (u, "(1x,A,L1)") "found = ", found
write (u, "(1x,A,L1)") "match = ", match
end select
write (u, "(A)")
write (u, "(A)") "* Modify model parameter and check MD5 sum"
write (u, "(A)")
call phs_data%final ()
deallocate (phs_data)
allocate (phs_wood_config_t :: phs_data)
call model%set_par (var_str ("ms"), 100._default)
call phs_data%init (process_data, model)
phs_par%sqrts = 1000
phs_par%off_shell = 1
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_parameters (phs_par)
phs_data%sqrts = phs_par%sqrts
phs_data%par%sqrts = phs_par%sqrts
end select
call phs_data%compute_md5sum ()
write (u, "(1x,A,A,A)") "MD5 sum (process) = '", &
phs_data%md5sum_process, "'"
write (u, "(1x,A,A,A)") "MD5 sum (model par) = '", &
phs_data%md5sum_model_par, "'"
write (u, "(1x,A,A,A)") "MD5 sum (phs config) = '", &
phs_data%md5sum_phs_config, "'"
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%read_phs_file (exist, found, match)
write (u, "(1x,A,L1)") "exist = ", exist
write (u, "(1x,A,L1)") "found = ", found
write (u, "(1x,A,L1)") "match = ", match
end select
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_wood_6"
end subroutine phs_wood_6
@ %def phs_wood_6
@
<<PHS wood: execute vis tests>>=
call test (phs_wood_vis_1, "phs_wood_vis_1", &
"visualizing phase space channels", &
u, results)
<<PHS wood: test declarations>>=
public :: phs_wood_vis_1
<<PHS wood: tests>>=
subroutine phs_wood_vis_1 (u)
integer, intent(in) :: u
type(os_data_t) :: os_data
type(model_data_t), target :: model
type(process_constants_t) :: process_data
class(phs_config_t), allocatable :: phs_data
type(mapping_defaults_t) :: mapping_defaults
type(string_t) :: vis_file, pdf_file, ps_file
real(default) :: sqrts
logical :: exist, exist_pdf, exist_ps
integer :: u_phs, iostat, u_vis
character(95) :: buffer
write (u, "(A)") "* Test output: phs_wood_vis_1"
write (u, "(A)") "* Purpose: visualizing the &
&phase-space configuration"
write (u, "(A)")
call os_data%init ()
call model%init_test ()
call syntax_phs_forest_init ()
write (u, "(A)") "* Initialize a process"
write (u, "(A)")
call init_test_process_data (var_str ("phs_wood_vis_1"), process_data)
write (u, "(A)") "* Create a scratch phase-space file"
write (u, "(A)")
u_phs = free_unit ()
open (u_phs, status = "scratch", action = "readwrite")
call write_test_phs_file (u_phs, var_str ("phs_wood_vis_1"))
rewind (u_phs)
do
read (u_phs, "(A)", iostat = iostat) buffer
if (iostat /= 0) exit
write (u, "(A)") trim (buffer)
end do
write (u, "(A)")
write (u, "(A)") "* Setup phase-space configuration object"
write (u, "(A)")
mapping_defaults%step_mapping = .false.
allocate (phs_wood_config_t :: phs_data)
call phs_data%init (process_data, model)
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%set_input (u_phs)
call phs_data%set_mapping_defaults (mapping_defaults)
phs_data%os_data = os_data
phs_data%io_unit = 0
phs_data%io_unit_keep_open = .true.
phs_data%vis_channels = .true.
end select
sqrts = 1000._default
call phs_data%configure (sqrts)
call phs_data%write (u)
write (u, "(A)")
select type (phs_data)
type is (phs_wood_config_t)
call phs_data%write_forest (u)
end select
vis_file = "phs_wood_vis_1.phs-vis.tex"
ps_file = "phs_wood_vis_1.phs-vis.ps"
pdf_file = "phs_wood_vis_1.phs-vis.pdf"
inquire (file = char (vis_file), exist = exist)
if (exist) then
u_vis = free_unit ()
open (u_vis, file = char (vis_file), action = "read", status = "old")
iostat = 0
do while (iostat == 0)
read (u_vis, "(A)", iostat = iostat) buffer
if (iostat == 0) write (u, "(A)") trim (buffer)
end do
close (u_vis)
else
write (u, "(A)") "[Visualize LaTeX file is missing]"
end if
inquire (file = char (ps_file), exist = exist_ps)
if (exist_ps) then
write (u, "(A)") "[Visualize Postscript file exists and is nonempty]"
else
write (u, "(A)") "[Visualize Postscript file is missing/non-regular]"
end if
inquire (file = char (pdf_file), exist = exist_pdf)
if (exist_pdf) then
write (u, "(A)") "[Visualize PDF file exists and is nonempty]"
else
write (u, "(A)") "[Visualize PDF file is missing/non-regular]"
end if
write (u, "(A)")
write (u, "(A)") "* Cleanup"
close (u_phs)
call phs_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_wood_vis_1"
end subroutine phs_wood_vis_1
@ %def phs_wood_vis_1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{The FKS phase space}
<<[[phs_fks.f90]]>>=
<<File header>>
module phs_fks
<<Use kinds>>
<<Use strings>>
<<Use debug>>
use constants
use diagnostics
use io_units, only: given_output_unit, free_unit
use format_utils, only: write_separator
use lorentz
use physics_defs
use flavors
use pdg_arrays, only: is_colored
use models, only: model_t
use sf_mappings
use sf_base
use phs_base
use resonances, only: resonance_contributors_t, resonance_history_t
use phs_forests, only: phs_forest_final
use phs_wood
use cascades
use cascades2
use process_constants
use process_libraries
use ttv_formfactors, only: generate_on_shell_decay_threshold, m1s_to_mpole
use format_defs, only: FMT_17
<<Standard module head>>
<<phs fks: public>>
<<phs fks: parameters>>
<<phs fks: types>>
<<phs fks: interfaces>>
contains
<<phs fks: procedures>>
end module phs_fks
@ %def phs_fks
@
@ A container for the $x_\oplus$- and $x_\ominus$-values for initial-state
phase spaces.
<<phs fks: public>>=
public :: isr_kinematics_t
<<phs fks: types>>=
type :: isr_kinematics_t
integer :: n_in
real(default), dimension(2) :: x = one
real(default), dimension(2) :: z = zero
real(default), dimension(2) :: z_coll = zero
real(default) :: sqrts_born = zero
real(default) :: beam_energy = zero
real(default) :: fac_scale = zero
real(default), dimension(2) :: jacobian = one
integer :: isr_mode = SQRTS_FIXED
end type isr_kinematics_t
@ %def type isr_kinematics_t
@
<<phs fks: public>>=
public :: phs_point_set_t
<<phs fks: types>>=
type :: phs_point_set_t
type(phs_point_t), dimension(:), allocatable :: phs_point
logical :: initialized = .false.
contains
<<phs fks: phs point set: TBP>>
end type phs_point_set_t
@ %def phs_point_set_t
@
<<phs fks: phs point set: TBP>>=
procedure :: init => phs_point_set_init
<<phs fks: procedures>>=
subroutine phs_point_set_init (phs_point_set, n_particles, n_phs)
class(phs_point_set_t), intent(out) :: phs_point_set
integer, intent(in) :: n_particles, n_phs
integer :: i_phs
allocate (phs_point_set%phs_point (n_phs))
do i_phs = 1, n_phs
phs_point_set%phs_point(i_phs) = n_particles
end do
phs_point_set%initialized = .true.
end subroutine phs_point_set_init
@ %def phs_point_set_init
@
<<phs fks: phs point set: TBP>>=
procedure :: write => phs_point_set_write
<<phs fks: procedures>>=
subroutine phs_point_set_write (phs_point_set, i_phs, contributors, unit, show_mass, &
testflag, check_conservation, ultra, n_in)
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in), optional :: i_phs
integer, intent(in), dimension(:), optional :: contributors
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_mass
logical, intent(in), optional :: testflag, ultra
logical, intent(in), optional :: check_conservation
integer, intent(in), optional :: n_in
integer :: i, u
type(vector4_t) :: p_sum
u = given_output_unit (unit); if (u < 0) return
if (present (i_phs)) then
call phs_point_set%phs_point(i_phs)%write &
(unit = u, show_mass = show_mass, testflag = testflag, &
check_conservation = check_conservation, ultra = ultra, n_in = n_in)
else
do i = 1, size(phs_point_set%phs_point)
call phs_point_set%phs_point(i)%write &
(unit = u, show_mass = show_mass, testflag = testflag, &
check_conservation = check_conservation, ultra = ultra, n_in = n_in)
end do
end if
if (present (contributors)) then
p_sum = vector4_null
if (debug_on) call msg_debug (D_SUBTRACTION, "Invariant masses for real emission: ")
associate (p => phs_point_set%phs_point(i_phs)%p)
do i = 1, size (contributors)
p_sum = p_sum + p(contributors(i))
end do
p_sum = p_sum + p(size(p))
end associate
if (debug_active (D_SUBTRACTION)) &
call vector4_write (p_sum, unit = unit, show_mass = show_mass, &
testflag = testflag, ultra = ultra)
end if
end subroutine phs_point_set_write
@ %def phs_point_set_write
@
<<phs fks: phs point set: TBP>>=
procedure :: get_n_momenta => phs_point_set_get_n_momenta
<<phs fks: procedures>>=
elemental function phs_point_set_get_n_momenta (phs_point_set, i_res) result (n)
integer :: n
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in) :: i_res
n = phs_point_set%phs_point(i_res)%n_momenta
end function phs_point_set_get_n_momenta
@ %def phs_point_set_get_n_momenta
@
<<phs fks: phs point set: TBP>>=
procedure :: get_momenta => phs_point_set_get_momenta
<<phs fks: procedures>>=
pure function phs_point_set_get_momenta (phs_point_set, i_phs, n_in) result (p)
type(vector4_t), dimension(:), allocatable :: p
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in) :: i_phs
integer, intent(in), optional :: n_in
if (present (n_in)) then
allocate (p (n_in), source = phs_point_set%phs_point(i_phs)%p(1:n_in))
else
allocate (p (phs_point_set%phs_point(i_phs)%n_momenta), &
source = phs_point_set%phs_point(i_phs)%p)
end if
end function phs_point_set_get_momenta
@ %def phs_point_set_get_momenta
@
<<phs fks: phs point set: TBP>>=
procedure :: get_momentum => phs_point_set_get_momentum
<<phs fks: procedures>>=
pure function phs_point_set_get_momentum (phs_point_set, i_phs, i_mom) result (p)
type(vector4_t) :: p
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in) :: i_phs, i_mom
p = phs_point_set%phs_point(i_phs)%p(i_mom)
end function phs_point_set_get_momentum
@ %def phs_point_set_get_momentum
@
<<phs fks: phs point set: TBP>>=
procedure :: get_energy => phs_point_set_get_energy
<<phs fks: procedures>>=
pure function phs_point_set_get_energy (phs_point_set, i_phs, i_mom) result (E)
real(default) :: E
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in) :: i_phs, i_mom
E = phs_point_set%phs_point(i_phs)%p(i_mom)%p(0)
end function phs_point_set_get_energy
@ %def phs_point_set_get_energy
@
<<phs fks: phs point set: TBP>>=
procedure :: get_sqrts => phs_point_set_get_sqrts
<<phs fks: procedures>>=
function phs_point_set_get_sqrts (phs_point_set, i_phs) result (sqrts)
real(default) :: sqrts
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in) :: i_phs
associate (p => phs_point_set%phs_point(i_phs)%p)
sqrts = (p(1) + p(2))**1
end associate
end function phs_point_set_get_sqrts
@ %def phs_point_set_get_sqrts
@
<<phs fks: phs point set: TBP>>=
generic :: set_momenta => set_momenta_p, set_momenta_phs_point
procedure :: set_momenta_p => phs_point_set_set_momenta_p
<<phs fks: procedures>>=
subroutine phs_point_set_set_momenta_p (phs_point_set, i_phs, p)
class(phs_point_set_t), intent(inout) :: phs_point_set
integer, intent(in) :: i_phs
type(vector4_t), intent(in), dimension(:) :: p
phs_point_set%phs_point(i_phs)%p = p
end subroutine phs_point_set_set_momenta_p
@ %def phs_point_set_set_momenta_p
@
<<phs fks: phs point set: TBP>>=
procedure :: set_momenta_phs_point => phs_point_set_set_momenta_phs_point
<<phs fks: procedures>>=
subroutine phs_point_set_set_momenta_phs_point (phs_point_set, i_phs, p)
class(phs_point_set_t), intent(inout) :: phs_point_set
integer, intent(in) :: i_phs
type(phs_point_t), intent(in) :: p
phs_point_set%phs_point(i_phs) = p
end subroutine phs_point_set_set_momenta_phs_point
@ %def phs_point_set_set_momenta_phs_point
@
<<phs fks: phs point set: TBP>>=
procedure :: get_n_particles => phs_point_set_get_n_particles
<<phs fks: procedures>>=
function phs_point_set_get_n_particles (phs_point_set, i) result (n_particles)
integer :: n_particles
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in), optional :: i
integer :: j
j = 1; if (present (i)) j = i
n_particles = size (phs_point_set%phs_point(j)%p)
end function phs_point_set_get_n_particles
@ %def phs_point_set_get_n_particles
@
<<phs fks: phs point set: TBP>>=
procedure :: get_n_phs => phs_point_set_get_n_phs
<<phs fks: procedures>>=
function phs_point_set_get_n_phs (phs_point_set) result (n_phs)
integer :: n_phs
class(phs_point_set_t), intent(in) :: phs_point_set
n_phs = size (phs_point_set%phs_point)
end function phs_point_set_get_n_phs
@ %def phs_point_set_get_n_phs
@
<<phs fks: phs point set: TBP>>=
procedure :: get_invariant_mass => phs_point_set_get_invariant_mass
<<phs fks: procedures>>=
function phs_point_set_get_invariant_mass (phs_point_set, i_phs, i_part) result (m2)
real(default) :: m2
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in) :: i_phs
integer, intent(in), dimension(:) :: i_part
type(vector4_t) :: p
integer :: i
p = vector4_null
do i = 1, size (i_part)
p = p + phs_point_set%phs_point(i_phs)%p(i_part(i))
end do
m2 = p**2
end function phs_point_set_get_invariant_mass
@ %def phs_point_set_get_invariant_mass
@
<<phs fks: phs point set: TBP>>=
procedure :: write_phs_point => phs_point_set_write_phs_point
<<phs fks: procedures>>=
subroutine phs_point_set_write_phs_point (phs_point_set, i_phs, unit, show_mass, &
testflag, check_conservation, ultra, n_in)
class(phs_point_set_t), intent(in) :: phs_point_set
integer, intent(in) :: i_phs
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_mass
logical, intent(in), optional :: testflag, ultra
logical, intent(in), optional :: check_conservation
integer, intent(in), optional :: n_in
call phs_point_set%phs_point(i_phs)%write (unit, show_mass, testflag, &
check_conservation, ultra, n_in)
end subroutine phs_point_set_write_phs_point
@ %def phs_point_set_write_phs_point
@
<<phs fks: phs point set: TBP>>=
procedure :: final => phs_point_set_final
<<phs fks: procedures>>=
subroutine phs_point_set_final (phs_point_set)
class(phs_point_set_t), intent(inout) :: phs_point_set
integer :: i
do i = 1, size (phs_point_set%phs_point)
call phs_point_set%phs_point(i)%final ()
end do
deallocate (phs_point_set%phs_point)
phs_point_set%initialized = .false.
end subroutine phs_point_set_final
@ %def phs_point_set_final
@
<<phs fks: public>>=
public :: real_jacobian_t
<<phs fks: types>>=
type :: real_jacobian_t
real(default), dimension(4) :: jac = 1._default
end type real_jacobian_t
@ %def real_jacobian_t
@
<<phs fks: public>>=
public :: real_kinematics_t
<<phs fks: types>>=
type :: real_kinematics_t
logical :: supply_xi_max = .true.
real(default) :: xi_tilde
real(default) :: phi
real(default), dimension(:), allocatable :: xi_max, y
real(default) :: xi_mismatch, y_mismatch
type(real_jacobian_t), dimension(:), allocatable :: jac
real(default) :: jac_mismatch
type(phs_point_set_t) :: p_born_cms
type(phs_point_set_t) :: p_born_lab
type(phs_point_set_t) :: p_real_cms
type(phs_point_set_t) :: p_real_lab
type(phs_point_set_t) :: p_born_onshell
type(phs_point_set_t), dimension(2) :: p_real_onshell
integer, dimension(:), allocatable :: alr_to_i_phs
real(default), dimension(3) :: x_rad
real(default), dimension(:), allocatable :: jac_rand
real(default), dimension(:), allocatable :: y_soft
real(default) :: cms_energy2
type(vector4_t), dimension(:), allocatable :: xi_ref_momenta
contains
<<phs fks: real kinematics: TBP>>
end type real_kinematics_t
@ %def real_kinematics_t
@
<<phs fks: real kinematics: TBP>>=
procedure :: init => real_kinematics_init
<<phs fks: procedures>>=
subroutine real_kinematics_init (r, n_tot, n_phs, n_alr, n_contr)
class(real_kinematics_t), intent(inout) :: r
integer, intent(in) :: n_tot, n_phs, n_alr, n_contr
allocate (r%xi_max (n_phs))
allocate (r%y (n_phs))
allocate (r%y_soft (n_phs))
call r%p_born_cms%init (n_tot - 1, 1)
call r%p_born_lab%init (n_tot - 1, 1)
call r%p_real_cms%init (n_tot, n_phs)
call r%p_real_lab%init (n_tot, n_phs)
allocate (r%jac (n_phs), r%jac_rand (n_phs))
allocate (r%alr_to_i_phs (n_alr))
allocate (r%xi_ref_momenta (n_contr))
r%alr_to_i_phs = 0
r%xi_tilde = zero; r%xi_mismatch = zero
r%xi_max = zero
r%y = zero; r%y_mismatch = zero
r%y_soft = zero
r%phi = zero
r%cms_energy2 = zero
r%xi_ref_momenta = vector4_null
r%jac_mismatch = one
r%jac_rand = one
end subroutine real_kinematics_init
@ %def real_kinematics_init
@
<<phs fks: real kinematics: TBP>>=
procedure :: init_onshell => real_kinematics_init_onshell
<<phs fks: procedures>>=
subroutine real_kinematics_init_onshell (r, n_tot, n_phs)
class(real_kinematics_t), intent(inout) :: r
integer, intent(in) :: n_tot, n_phs
call r%p_born_onshell%init (n_tot - 1, 1)
call r%p_real_onshell(1)%init (n_tot, n_phs)
call r%p_real_onshell(2)%init (n_tot, n_phs)
end subroutine real_kinematics_init_onshell
@ %def real_kinematics_init_onshell
@
<<phs fks: real kinematics: TBP>>=
procedure :: write => real_kinematics_write
<<phs fks: procedures>>=
subroutine real_kinematics_write (r, unit)
class(real_kinematics_t), intent(in) :: r
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
write (u,"(A)") "Real kinematics: "
write (u,"(A," // FMT_17 // ",1X)") "xi_tilde: ", r%xi_tilde
write (u,"(A," // FMT_17 // ",1X)") "phi: ", r%phi
do i = 1, size (r%xi_max)
write (u,"(A,I1,1X)") "i_phs: ", i
write (u,"(A," // FMT_17 // ",1X)") "xi_max: ", r%xi_max(i)
write (u,"(A," // FMT_17 // ",1X)") "y: ", r%y(i)
write (u,"(A," // FMT_17 // ",1X)") "jac_rand: ", r%jac_rand(i)
write (u,"(A," // FMT_17 // ",1X)") "y_soft: ", r%y_soft(i)
end do
write (u, "(A)") "Born Momenta: "
write (u, "(A)") "CMS: "
call r%p_born_cms%write (unit = u)
write (u, "(A)") "Lab: "
call r%p_born_lab%write (unit = u)
write (u, "(A)") "Real Momenta: "
write (u, "(A)") "CMS: "
call r%p_real_cms%write (unit = u)
write (u, "(A)") "Lab: "
call r%p_real_lab%write (unit = u)
end subroutine real_kinematics_write
@ %def real_kinematics_write
@ The boost to the center-of-mass system only has a reasonable meaning
above the threshold. Below the threshold, we do not apply boost at all, so
that the top quarks stay in the rest frame. However, with top quarks exactly
at rest, problems arise in the matrix elements (e.g. in the computation
of angles). Therefore, we apply a boost which is not exactly 1, but has a
tiny value differing from that.
<<phs fks: public>>=
public :: get_boost_for_threshold_projection
<<phs fks: procedures>>=
function get_boost_for_threshold_projection (p, sqrts, mtop) result (L)
type(lorentz_transformation_t) :: L
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(in) :: sqrts, mtop
type(vector4_t) :: p_tmp
type(vector3_t) :: dir
real(default) :: scale_factor, arg
p_tmp = p(THR_POS_WP) + p(THR_POS_B)
arg = sqrts**2 - four * mtop**2
if (arg > zero) then
scale_factor = sqrt (arg) / two
else
scale_factor = tiny_07*1000
end if
dir = scale_factor * create_unit_vector (p_tmp)
p_tmp = [sqrts / two, dir%p]
L = boost (p_tmp, mtop)
end function get_boost_for_threshold_projection
@ %def get_boost_for_threshold_projection
@ This routine recomputes the value of $\phi$ used to generate the real phase space.
<<phs fks: procedures>>=
function get_generation_phi (p_born, p_real, emitter, i_gluon) result (phi)
real(default) :: phi
type(vector4_t), intent(in), dimension(:) :: p_born, p_real
integer, intent(in) :: emitter, i_gluon
type(vector4_t) :: p1, p2, pp
type(lorentz_transformation_t) :: rot_to_gluon, rot_to_z
type(vector3_t) :: dir, z
real(default) :: cpsi
pp = p_real(emitter) + p_real(i_gluon)
cpsi = (space_part_norm (pp)**2 - space_part_norm (p_real(emitter))**2 &
+ space_part_norm (p_real(i_gluon))**2) / &
(two * space_part_norm (pp) * space_part_norm (p_real(i_gluon)))
dir = create_orthogonal (space_part (p_born(emitter)))
rot_to_gluon = rotation (cpsi, sqrt (one - cpsi**2), dir)
pp = rot_to_gluon * p_born(emitter)
z%p = [0, 0, 1]
rot_to_z = rotation_to_2nd &
(space_part (p_born(emitter)) / space_part_norm (p_born(emitter)), z)
p1 = rot_to_z * pp / space_part_norm (pp)
p2 = rot_to_z * p_real(i_gluon)
phi = azimuthal_distance (p1, p2)
if (phi < zero) phi = twopi - abs(phi)
end function get_generation_phi
@ %def get_generation_phi
@
<<phs fks: real kinematics: TBP>>=
procedure :: apply_threshold_projection_real => real_kinematics_apply_threshold_projection_real
<<phs fks: procedures>>=
subroutine real_kinematics_apply_threshold_projection_real (r, i_phs, mtop, L_to_cms, invert)
class(real_kinematics_t), intent(inout) :: r
integer, intent(in) :: i_phs
real(default), intent(in) :: mtop
type(lorentz_transformation_t), intent(in), dimension(:) :: L_to_cms
logical, intent(in) :: invert
integer :: leg, other_leg
type(vector4_t), dimension(4) :: k_tmp
type(vector4_t), dimension(4) :: k_decay_onshell_real
type(vector4_t), dimension(3) :: k_decay_onshell_born
do leg = 1, 2
other_leg = 3 - leg
associate (p_real => r%p_real_cms%phs_point(i_phs)%p, &
p_real_onshell => r%p_real_onshell(leg)%phs_point(i_phs)%p)
p_real_onshell(1:2) = p_real(1:2)
k_tmp(1) = p_real(7)
k_tmp(2) = p_real(ass_quark(leg))
k_tmp(3) = p_real(ass_boson(leg))
k_tmp(4) = [mtop, zero, zero, zero]
call generate_on_shell_decay_threshold (k_tmp(1:3), &
k_tmp(4), k_decay_onshell_real (2:4))
k_decay_onshell_real (1) = k_tmp(4)
k_tmp(1) = p_real(ass_quark(other_leg))
k_tmp(2) = p_real(ass_boson(other_leg))
k_decay_onshell_born = create_two_particle_decay (mtop**2, k_tmp(1), k_tmp(2))
p_real_onshell(THR_POS_GLUON) = L_to_cms(leg) * k_decay_onshell_real (2)
p_real_onshell(ass_quark(leg)) = L_to_cms(leg) * k_decay_onshell_real(3)
p_real_onshell(ass_boson(leg)) = L_to_cms(leg) * k_decay_onshell_real(4)
p_real_onshell(ass_quark(other_leg)) = L_to_cms(leg) * k_decay_onshell_born (2)
p_real_onshell(ass_boson(other_leg)) = L_to_cms(leg) * k_decay_onshell_born (3)
if (invert) then
call vector4_invert_direction (p_real_onshell (ass_quark(other_leg)))
call vector4_invert_direction (p_real_onshell (ass_boson(other_leg)))
end if
end associate
end do
end subroutine real_kinematics_apply_threshold_projection_real
@ %def real_kinematics_apply_threshold_projection_real
@
<<phs fks: public>>=
public :: threshold_projection_born
<<phs fks: procedures>>=
subroutine threshold_projection_born (mtop, L_to_cms, p_in, p_onshell)
real(default), intent(in) :: mtop
type(lorentz_transformation_t), intent(in) :: L_to_cms
type(vector4_t), intent(in), dimension(:) :: p_in
type(vector4_t), intent(out), dimension(:) :: p_onshell
type(vector4_t), dimension(3) :: k_decay_onshell
type(vector4_t) :: p_tmp_1, p_tmp_2
type(lorentz_transformation_t) :: L_to_cms_inv
p_onshell(1:2) = p_in(1:2)
L_to_cms_inv = inverse (L_to_cms)
p_tmp_1 = L_to_cms_inv * p_in(THR_POS_B)
p_tmp_2 = L_to_cms_inv * p_in(THR_POS_WP)
k_decay_onshell = create_two_particle_decay (mtop**2, &
p_tmp_1, p_tmp_2)
p_onshell([THR_POS_B, THR_POS_WP]) = k_decay_onshell([2, 3])
p_tmp_1 = L_to_cms * p_in(THR_POS_BBAR)
p_tmp_2 = L_to_cms * p_in(THR_POS_WM)
k_decay_onshell = create_two_particle_decay (mtop**2, &
p_tmp_1, p_tmp_2)
p_onshell([THR_POS_BBAR, THR_POS_WM]) = k_decay_onshell([2, 3])
p_onshell([THR_POS_WP, THR_POS_B]) = L_to_cms * p_onshell([THR_POS_WP, THR_POS_B])
p_onshell([THR_POS_WM, THR_POS_BBAR]) = L_to_cms_inv * p_onshell([THR_POS_WM, THR_POS_BBAR])
end subroutine threshold_projection_born
@ %def threshold_projection_born
@ This routine computes the bounds of the Dalitz region for massive emitters, see below.
It is also used by [[Powheg]], so the routine is public.
-The input parameter [[m2]] corresponds to the squared mass of the emitter and [[p]] is the
-four-momentum of the emitter.
+The input parameter [[m2]] corresponds to the squared mass of the emitter.
<<phs fks: public>>=
public :: compute_dalitz_bounds
<<phs fks: procedures>>=
pure subroutine compute_dalitz_bounds (q0, m2, mrec2, z1, z2, k0_rec_max)
real(default), intent(in) :: q0, m2, mrec2
real(default), intent(out) :: z1, z2, k0_rec_max
k0_rec_max = (q0**2 - m2 + mrec2) / (two * q0)
z1 = (k0_rec_max + sqrt(k0_rec_max**2 - mrec2)) / q0
z2 = (k0_rec_max - sqrt(k0_rec_max**2 - mrec2)) / q0
end subroutine compute_dalitz_bounds
@ %def compute_dalitz_bounds
@ Compute the [[kt2]] of a given emitter
<<phs fks: real kinematics: TBP>>=
procedure :: kt2 => real_kinematics_kt2
<<phs fks: procedures>>=
function real_kinematics_kt2 &
(real_kinematics, i_phs, emitter, kt2_type, xi, y) result (kt2)
real(default) :: kt2
class(real_kinematics_t), intent(in) :: real_kinematics
integer, intent(in) :: emitter, i_phs, kt2_type
real(default), intent(in), optional :: xi, y
real(default) :: xii, yy
real(default) :: q, E_em, z, z1, z2, m2, mrec2, k0_rec_max
type(vector4_t) :: p_emitter
if (present (y)) then
yy = y
else
yy = real_kinematics%y (i_phs)
end if
if (present (xi)) then
xii = xi
else
xii = real_kinematics%xi_tilde * real_kinematics%xi_max (i_phs)
end if
select case (kt2_type)
case (FSR_SIMPLE)
kt2 = real_kinematics%cms_energy2 / two * xii**2 * (1 - yy)
case (FSR_MASSIVE)
q = sqrt (real_kinematics%cms_energy2)
p_emitter = real_kinematics%p_born_cms%phs_point(1)%p(emitter)
mrec2 = (q - p_emitter%p(0))**2 - sum (p_emitter%p(1:3)**2)
m2 = p_emitter**2
E_em = energy (p_emitter)
call compute_dalitz_bounds (q, m2, mrec2, z1, z2, k0_rec_max)
z = z2 - (z2 - z1) * (one + yy) / two
kt2 = xii**2 * q**3 * (one - z) / &
(two * E_em - z * xii * q)
case (FSR_MASSLESS_RECOILER)
kt2 = real_kinematics%cms_energy2 / two * xii**2 * (1 - yy**2) / two
case default
kt2 = zero
call msg_bug ("kt2_type must be set to a known value")
end select
end function real_kinematics_kt2
@ %def real_kinematics_kt2
@
<<phs fks: parameters>>=
integer, parameter, public :: FSR_SIMPLE = 1
integer, parameter, public :: FSR_MASSIVE = 2
integer, parameter, public :: FSR_MASSLESS_RECOILER = 3
@ %def FSR_SIMPLE FSR_MASSIVE FSR_MASSLESS_RECOILER
@
<<phs fks: real kinematics: TBP>>=
procedure :: final => real_kinematics_final
<<phs fks: procedures>>=
subroutine real_kinematics_final (real_kin)
class(real_kinematics_t), intent(inout) :: real_kin
if (allocated (real_kin%xi_max)) deallocate (real_kin%xi_max)
if (allocated (real_kin%y)) deallocate (real_kin%y)
if (allocated (real_kin%alr_to_i_phs)) deallocate (real_kin%alr_to_i_phs)
if (allocated (real_kin%jac_rand)) deallocate (real_kin%jac_rand)
if (allocated (real_kin%y_soft)) deallocate (real_kin%y_soft)
if (allocated (real_kin%xi_ref_momenta)) deallocate (real_kin%xi_ref_momenta)
call real_kin%p_born_cms%final (); call real_kin%p_born_lab%final ()
call real_kin%p_real_cms%final (); call real_kin%p_real_lab%final ()
end subroutine real_kinematics_final
@ %def real_kinematics_final
@
<<phs fks: parameters>>=
integer, parameter, public :: I_XI = 1
integer, parameter, public :: I_Y = 2
integer, parameter, public :: I_PHI = 3
integer, parameter, public :: PHS_MODE_UNDEFINED = 0
integer, parameter, public :: PHS_MODE_ADDITIONAL_PARTICLE = 1
integer, parameter, public :: PHS_MODE_COLLINEAR_REMNANT = 2
@ %def parameters
@
<<phs fks: public>>=
public :: phs_fks_config_t
<<phs fks: types>>=
type, extends (phs_wood_config_t) :: phs_fks_config_t
integer :: mode = PHS_MODE_UNDEFINED
character(32) :: md5sum_born_config
logical :: make_dalitz_plot = .false.
contains
<<phs fks: fks config: TBP>>
end type phs_fks_config_t
@ %def phs_fks_config_t
@
<<phs fks: fks config: TBP>>=
procedure :: clear_phase_space => fks_config_clear_phase_space
<<phs fks: procedures>>=
subroutine fks_config_clear_phase_space (phs_config)
class(phs_fks_config_t), intent(inout) :: phs_config
end subroutine fks_config_clear_phase_space
@ %def fks_config_clear_phase_space
@
<<phs fks: fks config: TBP>>=
procedure :: write => phs_fks_config_write
<<phs fks: procedures>>=
subroutine phs_fks_config_write (object, unit, include_id)
class(phs_fks_config_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: include_id
integer :: u
u = given_output_unit (unit)
call object%phs_wood_config_t%write (u)
write (u, "(A,A)") "Extra Born md5sum: ", object%md5sum_born_config
end subroutine phs_fks_config_write
@ %def phs_fks_config_write
@
<<phs fks: fks config: TBP>>=
procedure :: set_mode => phs_fks_config_set_mode
<<phs fks: procedures>>=
subroutine phs_fks_config_set_mode (phs_config, mode)
class(phs_fks_config_t), intent(inout) :: phs_config
integer, intent(in) :: mode
select case (mode)
case (NLO_REAL, NLO_MISMATCH)
phs_config%mode = PHS_MODE_ADDITIONAL_PARTICLE
case (NLO_DGLAP)
phs_config%mode = PHS_MODE_COLLINEAR_REMNANT
end select
end subroutine phs_fks_config_set_mode
@ %def phs_fks_config_set_mod
@
<<phs fks: fks config: TBP>>=
procedure :: configure => phs_fks_config_configure
<<phs fks: procedures>>=
subroutine phs_fks_config_configure (phs_config, sqrts, &
sqrts_fixed, cm_frame, azimuthal_dependence, rebuild, &
ignore_mismatch, nlo_type, subdir)
class(phs_fks_config_t), intent(inout) :: phs_config
real(default), intent(in) :: sqrts
logical, intent(in), optional :: sqrts_fixed
logical, intent(in), optional :: cm_frame
logical, intent(in), optional :: azimuthal_dependence
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
integer, intent(in), optional :: nlo_type
type(string_t), intent(in), optional :: subdir
if (phs_config%extension_mode == EXTENSION_NONE) then
select case (phs_config%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
phs_config%n_par = phs_config%n_par + 3
case (PHS_MODE_COLLINEAR_REMNANT)
phs_config%n_par = phs_config%n_par + 1
end select
end if
!!! Channel equivalences not accessible yet
phs_config%provides_equivalences = .false.
call phs_config%compute_md5sum ()
end subroutine phs_fks_config_configure
@ %def phs_fks_config_configure
@
<<phs fks: fks config: TBP>>=
procedure :: startup_message => phs_fks_config_startup_message
<<phs fks: procedures>>=
subroutine phs_fks_config_startup_message (phs_config, unit)
class(phs_fks_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
call phs_config%phs_wood_config_t%startup_message (unit)
end subroutine phs_fks_config_startup_message
@ %def phs_fks_config_startup_message
@
<<phs fks: fks config: TBP>>=
procedure, nopass :: allocate_instance => phs_fks_config_allocate_instance
<<phs fks: procedures>>=
subroutine phs_fks_config_allocate_instance (phs)
class(phs_t), intent(inout), pointer :: phs
allocate (phs_fks_t :: phs)
end subroutine phs_fks_config_allocate_instance
@ %def phs_fks_config_allocate_instance
@ If the phase space is generated from file, but we want to have resonance
histories, we must force the cascade sets to be generated. However, it must
be assured that Born flavors are used for this.
<<phs fks: fks config: TBP>>=
procedure :: generate_phase_space_extra => phs_fks_config_generate_phase_space_extra
<<phs fks: procedures>>=
subroutine phs_fks_config_generate_phase_space_extra (phs_config)
class(phs_fks_config_t), intent(inout) :: phs_config
integer :: off_shell, extra_off_shell
type(flavor_t), dimension(:,:), allocatable :: flv_born
integer :: i, j
integer :: n_state, n_flv_born
integer :: unit_fds
logical :: valid
type(string_t) :: file_name
logical :: file_exists
if (phs_config%use_cascades2) then
allocate (phs_config%feyngraph_set)
else
allocate (phs_config%cascade_set)
end if
n_flv_born = size (phs_config%flv, 1) - 1
n_state = size (phs_config%flv, 2)
allocate (flv_born (n_flv_born, n_state))
do i = 1, n_flv_born
do j = 1, n_state
flv_born(i, j) = phs_config%flv(i, j)
end do
end do
if (phs_config%use_cascades2) then
file_name = char (phs_config%id) // ".fds"
inquire (file=char (file_name), exist=file_exists)
if (.not. file_exists) call msg_fatal &
("The O'Mega input file " // char (file_name) // &
" does not exist. " // "Please make sure that the " // &
"variable ?omega_write_phs_output has been set correctly.")
unit_fds = free_unit ()
open (unit=unit_fds, file=char(file_name), status='old', action='read')
end if
off_shell = phs_config%par%off_shell
do extra_off_shell = 0, max (n_flv_born - 2, 0)
phs_config%par%off_shell = off_shell + extra_off_shell
if (phs_config%use_cascades2) then
call feyngraph_set_generate (phs_config%feyngraph_set, &
phs_config%model, phs_config%n_in, phs_config%n_out - 1, &
flv_born, phs_config%par, phs_config%fatal_beam_decay, unit_fds, &
phs_config%vis_channels)
if (feyngraph_set_is_valid (phs_config%feyngraph_set)) exit
else
call cascade_set_generate (phs_config%cascade_set, &
phs_config%model, phs_config%n_in, phs_config%n_out - 1, &
flv_born, phs_config%par, phs_config%fatal_beam_decay)
if (cascade_set_is_valid (phs_config%cascade_set)) exit
end if
end do
if (phs_config%use_cascades2) then
close (unit_fds)
valid = feyngraph_set_is_valid (phs_config%feyngraph_set)
else
valid = cascade_set_is_valid (phs_config%cascade_set)
end if
if (.not. valid) &
call msg_fatal ("Resonance extraction: Phase space generation failed")
end subroutine phs_fks_config_generate_phase_space_extra
@ %def phs_fks_config_generate_phase_space_extra
@
<<phs fks: fks config: TBP>>=
procedure :: set_born_config => phs_fks_config_set_born_config
<<phs fks: procedures>>=
subroutine phs_fks_config_set_born_config (phs_config, phs_cfg_born)
class(phs_fks_config_t), intent(inout) :: phs_config
type(phs_wood_config_t), intent(in), target :: phs_cfg_born
if (debug_on) call msg_debug (D_PHASESPACE, "phs_fks_config_set_born_config")
phs_config%forest = phs_cfg_born%forest
phs_config%n_channel = phs_cfg_born%n_channel
allocate (phs_config%channel (phs_config%n_channel))
phs_config%channel = phs_cfg_born%channel
phs_config%n_par = phs_cfg_born%n_par
phs_config%n_state = phs_cfg_born%n_state
phs_config%sqrts = phs_cfg_born%sqrts
phs_config%par = phs_cfg_born%par
phs_config%sqrts_fixed = phs_cfg_born%sqrts_fixed
phs_config%azimuthal_dependence = phs_cfg_born%azimuthal_dependence
phs_config%provides_chains = phs_cfg_born%provides_chains
phs_config%cm_frame = phs_cfg_born%cm_frame
phs_config%vis_channels = phs_cfg_born%vis_channels
allocate (phs_config%chain (size (phs_cfg_born%chain)))
phs_config%chain = phs_cfg_born%chain
phs_config%model => phs_cfg_born%model
phs_config%use_cascades2 = phs_cfg_born%use_cascades2
if (allocated (phs_cfg_born%cascade_set)) then
allocate (phs_config%cascade_set)
phs_config%cascade_set = phs_cfg_born%cascade_set
end if
if (allocated (phs_cfg_born%feyngraph_set)) then
allocate (phs_config%feyngraph_set)
phs_config%feyngraph_set = phs_cfg_born%feyngraph_set
end if
phs_config%md5sum_born_config = phs_cfg_born%md5sum_phs_config
end subroutine phs_fks_config_set_born_config
@ %def phs_fks_config_set_born_config
@
<<phs fks: fks config: TBP>>=
procedure :: get_resonance_histories => phs_fks_config_get_resonance_histories
<<phs fks: procedures>>=
function phs_fks_config_get_resonance_histories (phs_config) result (resonance_histories)
type(resonance_history_t), dimension(:), allocatable :: resonance_histories
class(phs_fks_config_t), intent(inout) :: phs_config
if (allocated (phs_config%cascade_set)) then
call cascade_set_get_resonance_histories &
(phs_config%cascade_set, n_filter = 2, res_hists = resonance_histories)
else if (allocated (phs_config%feyngraph_set)) then
call feyngraph_set_get_resonance_histories &
(phs_config%feyngraph_set, n_filter = 2, res_hists = resonance_histories)
else
if (debug_on) call msg_debug (D_PHASESPACE, "Have to rebuild phase space for resonance histories")
call phs_config%generate_phase_space_extra ()
if (phs_config%use_cascades2) then
call feyngraph_set_get_resonance_histories &
(phs_config%feyngraph_set, n_filter = 2, res_hists = resonance_histories)
else
call cascade_set_get_resonance_histories &
(phs_config%cascade_set, n_filter = 2, res_hists = resonance_histories)
end if
end if
end function phs_fks_config_get_resonance_histories
@ %def phs_fks_config_get_resonance_histories
@
<<phs fks: public>>=
public :: dalitz_plot_t
<<phs fks: types>>=
type :: dalitz_plot_t
integer :: unit = -1
type(string_t) :: filename
logical :: active = .false.
logical :: inverse = .false.
contains
<<phs fks: dalitz plot: TBP>>
end type dalitz_plot_t
@ %def dalitz_plot_t
@
<<phs fks: dalitz plot: TBP>>=
procedure :: init => dalitz_plot_init
<<phs fks: procedures>>=
subroutine dalitz_plot_init (plot, unit, filename, inverse)
class(dalitz_plot_t), intent(inout) :: plot
integer, intent(in) :: unit
type(string_t), intent(in) :: filename
logical, intent(in) :: inverse
plot%active = .true.
plot%unit = unit
plot%inverse = inverse
open (plot%unit, file = char (filename), action = "write")
end subroutine dalitz_plot_init
@ %def daltiz_plot_init
@
<<phs fks: dalitz plot: TBP>>=
procedure :: write_header => dalitz_plot_write_header
<<phs fks: procedures>>=
subroutine dalitz_plot_write_header (plot)
class(dalitz_plot_t), intent(in) :: plot
write (plot%unit, "(A36)") "### Dalitz plot generated by WHIZARD"
if (plot%inverse) then
write (plot%unit, "(A10,1x,A4)") "### k0_n+1", "k0_n"
else
write (plot%unit, "(A8,1x,A6)") "### k0_n", "k0_n+1"
end if
end subroutine dalitz_plot_write_header
@ %def dalitz_plot_write_header
@
<<phs fks: dalitz plot: TBP>>=
procedure :: register => dalitz_plot_register
<<phs fks: procedures>>=
subroutine dalitz_plot_register (plot, k0_n, k0_np1)
class(dalitz_plot_t), intent(in) :: plot
real(default), intent(in) :: k0_n, k0_np1
if (plot%inverse) then
write (plot%unit, "(F8.4,1X,F8.4)") k0_np1, k0_n
else
write (plot%unit, "(F8.4,1X,F8.4)") k0_np1, k0_n
end if
end subroutine dalitz_plot_register
@ %def dalitz_plot_register
@
<<phs fks: dalitz plot: TBP>>=
procedure :: final => dalitz_plot_final
<<phs fks: procedures>>=
subroutine dalitz_plot_final (plot)
class(dalitz_plot_t), intent(inout) :: plot
logical :: opened
plot%active = .false.
plot%inverse = .false.
if (plot%unit >= 0) then
inquire (unit = plot%unit, opened = opened)
if (opened) close (plot%unit)
end if
plot%filename = var_str ('')
plot%unit = -1
end subroutine dalitz_plot_final
@ %def dalitz_plot_final
@
<<phs fks: parameters>>=
integer, parameter, public :: GEN_REAL_PHASE_SPACE = 1
integer, parameter, public :: GEN_SOFT_MISMATCH = 2
integer, parameter, public :: GEN_SOFT_LIMIT_TEST = 3
integer, parameter, public :: GEN_COLL_LIMIT_TEST = 4
integer, parameter, public :: GEN_ANTI_COLL_LIMIT_TEST = 5
integer, parameter, public :: GEN_SOFT_COLL_LIMIT_TEST = 6
integer, parameter, public :: GEN_SOFT_ANTI_COLL_LIMIT_TEST = 7
integer, parameter, public :: SQRTS_FIXED = 1
integer, parameter, public :: SQRTS_VAR = 2
- real(default), parameter :: xi_tilde_test_soft = 0.0001_default
+ real(default), parameter :: xi_tilde_test_soft = 0.00001_default
real(default), parameter :: xi_tilde_test_coll = 0.5_default
real(default), parameter :: y_test_soft = 0.5_default
- real(default), parameter :: y_test_coll = 0.999999_default
+ real(default), parameter :: y_test_coll = 0.9999999_default
@
@ Very soft or collinear phase-space points can become a problem for
matrix elements providers, as some scalar products cannot be evaluated
properly. Here, a nonsensical result can spoil the whole integration.
We therefore check the scalar products appearing to be below a certain
tolerance.
<<phs fks: public>>=
public :: check_scalar_products
<<phs fks: procedures>>=
function check_scalar_products (p) result (valid)
logical :: valid
type(vector4_t), intent(in), dimension(:) :: p
- real(default), parameter :: tolerance = 1E-6_default
+ real(default), parameter :: tolerance = 1E-7_default
integer :: i, j
valid = .true.
do i = 1, size (p)
do j = i, size (p)
if (i /= j) then
if (abs(p(i) * p(j)) < tolerance) then
valid = .false.
exit
end if
end if
end do
end do
end function check_scalar_products
@ %def check_scalar_products
@ [[xi_min]] should be set to a non-zero value in order to avoid
phase-space points with [[p_real(emitter) = 0]].
<<phs fks: public>>=
public :: phs_fks_generator_t
<<phs fks: types>>=
type :: phs_fks_generator_t
integer, dimension(:), allocatable :: emitters
type(real_kinematics_t), pointer :: real_kinematics => null()
type(isr_kinematics_t), pointer :: isr_kinematics => null()
integer :: n_in
real(default) :: xi_min = tiny_07
real(default) :: y_max = one
real(default) :: sqrts
real(default) :: E_gluon
real(default) :: mrec2
real(default), dimension(:), allocatable :: m2
logical :: massive_phsp = .false.
logical, dimension(:), allocatable :: is_massive
logical :: singular_jacobian = .false.
integer :: i_fsr_first = -1
type(resonance_contributors_t), dimension(:), allocatable :: resonance_contributors !!! Put somewhere else?
integer :: mode = GEN_REAL_PHASE_SPACE
contains
<<phs fks: phs fks generator: TBP>>
end type phs_fks_generator_t
@ %def phs_fks_generator_t
@
<<phs fks: phs fks generator: TBP>>=
procedure :: connect_kinematics => phs_fks_generator_connect_kinematics
<<phs fks: procedures>>=
subroutine phs_fks_generator_connect_kinematics &
(generator, isr_kinematics, real_kinematics, massive_phsp)
class(phs_fks_generator_t), intent(inout) :: generator
type(isr_kinematics_t), intent(in), pointer :: isr_kinematics
type(real_kinematics_t), intent(in), pointer :: real_kinematics
logical, intent(in) :: massive_phsp
generator%real_kinematics => real_kinematics
generator%isr_kinematics => isr_kinematics
generator%massive_phsp = massive_phsp
end subroutine phs_fks_generator_connect_kinematics
@ %def phs_fks_generator_connect_kinematics
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_isr_kinematics => phs_fks_generator_compute_isr_kinematics
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_isr_kinematics (generator, r, p_in)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: r
type(vector4_t), dimension(2), intent(in), optional :: p_in
integer :: em
type(vector4_t), dimension(2) :: p
if (present (p_in)) then
p = p_in
else
p = generator%real_kinematics%p_born_lab%phs_point(1)%p(1:2)
end if
associate (isr => generator%isr_kinematics)
do em = 1, 2
isr%x(em) = p(em)%p(0) / isr%beam_energy
isr%z(em) = one - (one - isr%x(em)) * r
isr%jacobian(em) = one - isr%x(em)
end do
isr%sqrts_born = (p(1) + p(2))**1
end associate
end subroutine phs_fks_generator_compute_isr_kinematics
@ %def phs_fks_generator_compute_isr_kinematics
@
<<phs fks: phs fks generator: TBP>>=
procedure :: final => phs_fks_generator_final
<<phs fks: procedures>>=
subroutine phs_fks_generator_final (generator)
class(phs_fks_generator_t), intent(inout) :: generator
if (allocated (generator%emitters)) deallocate (generator%emitters)
if (associated (generator%real_kinematics)) nullify (generator%real_kinematics)
if (associated (generator%isr_kinematics)) nullify (generator%isr_kinematics)
if (allocated (generator%m2)) deallocate (generator%m2)
generator%massive_phsp = .false.
if (allocated (generator%is_massive)) deallocate (generator%is_massive)
generator%singular_jacobian = .false.
generator%i_fsr_first = -1
if (allocated (generator%resonance_contributors)) &
deallocate (generator%resonance_contributors)
generator%mode = GEN_REAL_PHASE_SPACE
end subroutine phs_fks_generator_final
@ %def phs_fks_generator_final
@ A resonance phase space is uniquely specified via the resonance contributors and the
corresponding emitters. The [[phs_identifier]] type also checks whether
the given contributor-emitter configuration has already been evaluated to
avoid duplicate computations.
<<phs fks: public>>=
public :: phs_identifier_t
<<phs fks: types>>=
type :: phs_identifier_t
integer, dimension(:), allocatable :: contributors
integer :: emitter = -1
logical :: evaluated = .false.
contains
<<phs fks: phs identifier: TBP>>
end type phs_identifier_t
@ %def phs_identifier_t
@
<<phs fks: phs identifier: TBP>>=
generic :: init => init_from_emitter, init_from_emitter_and_contributors
procedure :: init_from_emitter => phs_identifier_init_from_emitter
procedure :: init_from_emitter_and_contributors &
=> phs_identifier_init_from_emitter_and_contributors
<<phs fks: procedures>>=
subroutine phs_identifier_init_from_emitter (phs_id, emitter)
class(phs_identifier_t), intent(out) :: phs_id
integer, intent(in) :: emitter
phs_id%emitter = emitter
end subroutine phs_identifier_init_from_emitter
subroutine phs_identifier_init_from_emitter_and_contributors &
(phs_id, emitter, contributors)
class(phs_identifier_t), intent(out) :: phs_id
integer, intent(in) :: emitter
integer, intent(in), dimension(:) :: contributors
allocate (phs_id%contributors (size (contributors)))
phs_id%contributors = contributors
phs_id%emitter = emitter
end subroutine phs_identifier_init_from_emitter_and_contributors
@ %def phs_identifier_init_from_emitter
@ %def phs_identifier_init_from_emitter_and_contributors
@
<<phs fks: phs identifier: TBP>>=
procedure :: check => phs_identifier_check
<<phs fks: procedures>>=
function phs_identifier_check (phs_id, emitter, contributors) result (check)
logical :: check
class(phs_identifier_t), intent(in) :: phs_id
integer, intent(in) :: emitter
integer, intent(in), dimension(:), optional :: contributors
check = phs_id%emitter == emitter
if (present (contributors)) then
if (.not. allocated (phs_id%contributors)) &
call msg_fatal ("Phs identifier: contributors not allocated!")
check = check .and. all (phs_id%contributors == contributors)
end if
end function phs_identifier_check
@ %def phs_identifier_check
@
<<phs fks: phs identifier: TBP>>=
procedure :: write => phs_identifier_write
<<phs fks: procedures>>=
subroutine phs_identifier_write (phs_id, unit)
class(phs_identifier_t), intent(in) :: phs_id
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
write (u, '(A)') 'phs_identifier: '
write (u, '(A,1X,I1)') 'Emitter: ', phs_id%emitter
if (allocated (phs_id%contributors)) then
write (u, '(A)', advance = 'no') 'Resonance contributors: '
do i = 1, size (phs_id%contributors)
write (u, '(I1,1X)', advance = 'no') phs_id%contributors(i)
end do
else
write (u, '(A)') 'No Contributors allocated'
end if
end subroutine phs_identifier_write
@ %def phs_identifier_write
@
<<phs fks: public>>=
public :: check_for_phs_identifier
<<phs fks: procedures>>=
subroutine check_for_phs_identifier (phs_id, n_in, emitter, contributors, phs_exist, i_phs)
type(phs_identifier_t), intent(in), dimension(:) :: phs_id
integer, intent(in) :: n_in, emitter
integer, intent(in), dimension(:), optional :: contributors
logical, intent(out) :: phs_exist
integer, intent(out) :: i_phs
integer :: i
phs_exist = .false.
i_phs = -1
do i = 1, size (phs_id)
if (phs_id(i)%emitter < 0) then
i_phs = i
exit
end if
phs_exist = phs_id(i)%emitter == emitter
if (present (contributors)) &
phs_exist = phs_exist .and. all (phs_id(i)%contributors == contributors)
if (phs_exist) then
i_phs = i
exit
end if
end do
end subroutine check_for_phs_identifier
@ %def check_for_phs_identifier
@
@ The fks phase space type contains the wood phase space and
separately the in- and outcoming momenta for the real process and the
corresponding Born momenta. Additionally, there are the variables
$\xi$,$\xi_{max}$, $y$ and $\phi$ which are used to create the real
phase space, as well as the jacobian and its corresponding soft and
collinear limit. Lastly, the array \texttt{ch\_to\_em} connects each
channel with an emitter.
<<phs fks: public>>=
public :: phs_fks_t
<<phs fks: types>>=
type, extends (phs_wood_t) :: phs_fks_t
integer :: mode = PHS_MODE_UNDEFINED
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: q_born
type(vector4_t), dimension(:), allocatable :: p_real
type(vector4_t), dimension(:), allocatable :: q_real
type(vector4_t), dimension(:), allocatable :: p_born_tot
type(phs_fks_generator_t) :: generator
logical :: perform_generation = .true.
real(default) :: r_isr
type(phs_identifier_t), dimension(:), allocatable :: phs_identifiers
contains
<<phs fks: phs fks: TBP>>
end type phs_fks_t
@ %def phs_fks_t
@
<<phs fks: interfaces>>=
interface compute_beta
module procedure compute_beta_massless
module procedure compute_beta_massive
end interface
interface get_xi_max_fsr
module procedure get_xi_max_fsr_massless
module procedure get_xi_max_fsr_massive
end interface
@ %def interfaces
@
<<phs fks: phs fks: TBP>>=
procedure :: write => phs_fks_write
<<phs fks: procedures>>=
subroutine phs_fks_write (object, unit, verbose)
class(phs_fks_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u, i, n_id
u = given_output_unit (unit)
call object%base_write ()
n_id = size (object%phs_identifiers)
if (n_id == 0) then
write (u, "(A)") "No phs identifiers allocated! "
else
do i = 1, n_id
call object%phs_identifiers(i)%write (u)
end do
end if
end subroutine phs_fks_write
@ %def phs_fks_write
@ Initializer for the phase space. Calls the initialization of the
corresponding Born phase space, sets up the
channel-emitter-association and allocates space for the momenta.
<<phs fks: phs fks: TBP>>=
procedure :: init => phs_fks_init
<<phs fks: procedures>>=
subroutine phs_fks_init (phs, phs_config)
class(phs_fks_t), intent(out) :: phs
class(phs_config_t), intent(in), target :: phs_config
call phs%base_init (phs_config)
select type (phs_config)
type is (phs_fks_config_t)
phs%config => phs_config
phs%forest = phs_config%forest
end select
select type(phs)
type is (phs_fks_t)
select type (phs_config)
type is (phs_fks_config_t)
phs%mode = phs_config%mode
end select
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
phs%n_r_born = phs%config%n_par - 3
case (PHS_MODE_COLLINEAR_REMNANT)
phs%n_r_born = phs%config%n_par - 1
end select
end select
end subroutine phs_fks_init
@ %def phs_fks_init
@
<<phs fks: phs fks: TBP>>=
procedure :: allocate_momenta => phs_fks_allocate_momenta
<<phs fks: procedures>>=
subroutine phs_fks_allocate_momenta (phs, phs_config, data_is_born)
class(phs_fks_t), intent(inout) :: phs
class(phs_config_t), intent(in) :: phs_config
logical, intent(in) :: data_is_born
integer :: n_out_born
allocate (phs%p_born (phs_config%n_in))
allocate (phs%p_real (phs_config%n_in))
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
if (data_is_born) then
n_out_born = phs_config%n_out
else
n_out_born = phs_config%n_out - 1
end if
allocate (phs%q_born (n_out_born))
allocate (phs%q_real (n_out_born + 1))
allocate (phs%p_born_tot (phs_config%n_in + n_out_born))
end select
end subroutine phs_fks_allocate_momenta
@ %def phs_fks_allocate_momenta
@ Evaluate selected channel. First, the subroutine calls the
evaluation procedure of the underlying Born phase space, using $n_r -
3$ random numbers. Then, the remaining three random numbers are used
to create $\xi$, $y$ and $\phi$, from which the real momenta are
calculated from the Born momenta.
<<phs fks: phs fks: TBP>>=
procedure :: evaluate_selected_channel => phs_fks_evaluate_selected_channel
<<phs fks: procedures>>=
subroutine phs_fks_evaluate_selected_channel (phs, c_in, r_in)
class(phs_fks_t), intent(inout) :: phs
integer, intent(in) :: c_in
real(default), intent(in), dimension(:) :: r_in
integer :: n_in
call phs%phs_wood_t%evaluate_selected_channel (c_in, r_in)
phs%r(:,c_in) = r_in
phs%q_defined = phs%phs_wood_t%q_defined
if (.not. phs%q_defined) return
if (phs%perform_generation) then
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
n_in = phs%config%n_in
phs%p_born = phs%phs_wood_t%p
phs%q_born = phs%phs_wood_t%q
phs%p_born_tot (1: n_in) = phs%p_born
phs%p_born_tot (n_in + 1 :) = phs%q_born
call phs%set_reference_frames (.true.)
call phs%set_isr_kinematics (.true.)
case (PHS_MODE_COLLINEAR_REMNANT)
call phs%compute_isr_kinematics (r_in(phs%n_r_born + 1))
phs%r_isr = r_in(phs%n_r_born + 1)
end select
end if
end subroutine phs_fks_evaluate_selected_channel
@ %def phs_fks_evaluate_selected_channel
@
<<phs fks: phs fks: TBP>>=
procedure :: evaluate_other_channels => phs_fks_evaluate_other_channels
<<phs fks: procedures>>=
subroutine phs_fks_evaluate_other_channels (phs, c_in)
class(phs_fks_t), intent(inout) :: phs
integer, intent(in) :: c_in
call phs%phs_wood_t%evaluate_other_channels (c_in)
phs%r_defined = .true.
end subroutine phs_fks_evaluate_other_channels
@ %def phs_fks_evaluate_other_channels
@
<<phs fks: phs fks: TBP>>=
procedure :: get_mcpar => phs_fks_get_mcpar
<<phs fks: procedures>>=
subroutine phs_fks_get_mcpar (phs, c, r)
class(phs_fks_t), intent(in) :: phs
integer, intent(in) :: c
real(default), dimension(:), intent(out) :: r
r(1 : phs%n_r_born) = phs%r(1 : phs%n_r_born,c)
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
r(phs%n_r_born + 1 :) = phs%r_real
case (PHS_MODE_COLLINEAR_REMNANT)
r(phs%n_r_born + 1 :) = phs%r_isr
end select
end subroutine phs_fks_get_mcpar
@ %def phs_fks_get_mcpar
@
<<phs fks: phs fks: TBP>>=
procedure :: set_beam_energy => phs_fks_set_beam_energy
<<phs fks: procedures>>=
subroutine phs_fks_set_beam_energy (phs)
class(phs_fks_t), intent(inout) :: phs
call phs%generator%set_sqrts_hat (phs%config%sqrts)
end subroutine phs_fks_set_beam_energy
@ %def phs_fks_set_beam_energy
@
<<phs fks: phs fks: TBP>>=
procedure :: set_emitters => phs_fks_set_emitters
<<phs fks: procedures>>=
subroutine phs_fks_set_emitters (phs, emitters)
class(phs_fks_t), intent(inout) :: phs
integer, intent(in), dimension(:), allocatable :: emitters
call phs%generator%set_emitters (emitters)
end subroutine phs_fks_set_emitters
@ %def phs_fks_set_emitters
@
<<phs fks: phs fks: TBP>>=
procedure :: set_momenta => phs_fks_set_momenta
<<phs fks: procedures>>=
subroutine phs_fks_set_momenta (phs, p)
class(phs_fks_t), intent(inout) :: phs
type(vector4_t), intent(in), dimension(:) :: p
integer :: n_in, n_tot_born
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
n_in = phs%config%n_in; n_tot_born = phs%config%n_tot - 1
phs%p_born = p(1 : n_in)
phs%q_born = p(n_in + 1 : n_tot_born)
phs%p_born_tot = p
end select
end subroutine phs_fks_set_momenta
@ %def phs_fks_set_momenta
@
<<phs fks: phs fks: TBP>>=
procedure :: setup_masses => phs_fks_setup_masses
<<phs fks: procedures>>=
subroutine phs_fks_setup_masses (phs, n_tot)
class(phs_fks_t), intent(inout) :: phs
integer, intent(in) :: n_tot
call phs%generator%setup_masses (n_tot)
end subroutine phs_fks_setup_masses
@ %def phs_fks_setup_masses
@
<<phs fks: phs fks: TBP>>=
procedure :: get_born_momenta => phs_fks_get_born_momenta
<<phs fks: procedures>>=
subroutine phs_fks_get_born_momenta (phs, p)
class(phs_fks_t), intent(inout) :: phs
type(vector4_t), intent(out), dimension(:) :: p
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
p(1 : phs%config%n_in) = phs%p_born
p(phs%config%n_in + 1 :) = phs%q_born
case (PHS_MODE_COLLINEAR_REMNANT)
p(1:phs%config%n_in) = phs%phs_wood_t%p
p(phs%config%n_in + 1 : ) = phs%phs_wood_t%q
end select
if (.not. phs%config%cm_frame) p = phs%lt_cm_to_lab * p
end subroutine phs_fks_get_born_momenta
@ %def phs_fks_get_born_momenta
@
<<phs fks: phs fks: TBP>>=
procedure :: get_outgoing_momenta => phs_fks_get_outgoing_momenta
<<phs fks: procedures>>=
subroutine phs_fks_get_outgoing_momenta (phs, q)
class(phs_fks_t), intent(in) :: phs
type(vector4_t), intent(out), dimension(:) :: q
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
q = phs%q_real
case (PHS_MODE_COLLINEAR_REMNANT)
q = phs%phs_wood_t%q
end select
end subroutine phs_fks_get_outgoing_momenta
@ %def phs_fks_get_outgoing_momenta
@
<<phs fks: phs fks: TBP>>=
procedure :: get_incoming_momenta => phs_fks_get_incoming_momenta
<<phs fks: procedures>>=
subroutine phs_fks_get_incoming_momenta (phs, p)
class(phs_fks_t), intent(in) :: phs
type(vector4_t), intent(inout), dimension(:), allocatable :: p
p = phs%p_real
end subroutine phs_fks_get_incoming_momenta
@ %def phs_fks_get_incoming_momenta
@
<<phs fks: phs fks: TBP>>=
procedure :: set_isr_kinematics => phs_fks_set_isr_kinematics
<<phs fks: procedures>>=
subroutine phs_fks_set_isr_kinematics (phs, requires_boost)
class(phs_fks_t), intent(inout) :: phs
logical, intent(in) :: requires_boost
type(vector4_t), dimension(2) :: p
if (phs%generator%isr_kinematics%isr_mode == SQRTS_VAR) then
if (requires_boost) then
p = phs%lt_cm_to_lab * phs%generator%real_kinematics%p_born_cms%phs_point(1)%p(1:2)
else
p = phs%generator%real_kinematics%p_born_lab%phs_point(1)%p(1:2)
end if
call phs%generator%set_isr_kinematics (p)
end if
end subroutine phs_fks_set_isr_kinematics
@ %def phs_fks_set_isr_kinematics
@
<<phs fks: phs fks: TBP>>=
procedure :: generate_radiation_variables => &
phs_fks_generate_radiation_variables
<<phs fks: procedures>>=
subroutine phs_fks_generate_radiation_variables (phs, r_in, threshold)
class(phs_fks_t), intent(inout) :: phs
real(default), intent(in), dimension(:) :: r_in
logical, intent(in) :: threshold
type(vector4_t), dimension(:), allocatable :: p_born
if (size (r_in) /= 3) call msg_fatal &
("Real kinematics need to be generated using three random numbers!")
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
allocate (p_born (size (phs%p_born_tot)))
if (threshold) then
p_born = phs%get_onshell_projected_momenta ()
else
p_born = phs%p_born_tot
if (.not. phs%is_cm_frame ()) &
p_born = inverse (phs%lt_cm_to_lab) * p_born
end if
call phs%generator%generate_radiation_variables &
(r_in, p_born, phs%phs_identifiers, threshold)
phs%r_real = r_in
end select
end subroutine phs_fks_generate_radiation_variables
@ %def phs_fks_generate_radiation_variables
@
<<phs fks: phs fks: TBP>>=
procedure :: compute_xi_ref_momenta => phs_fks_compute_xi_ref_momenta
<<phs fks: procedures>>=
subroutine phs_fks_compute_xi_ref_momenta (phs, p_in, contributors)
class(phs_fks_t), intent(inout) :: phs
type(vector4_t), intent(in), dimension(:), optional :: p_in
type(resonance_contributors_t), intent(in), dimension(:), optional :: contributors
if (phs%mode == PHS_MODE_ADDITIONAL_PARTICLE) then
if (present (p_in)) then
call phs%generator%compute_xi_ref_momenta (p_in, contributors)
else
call phs%generator%compute_xi_ref_momenta (phs%p_born_tot, contributors)
end if
end if
end subroutine phs_fks_compute_xi_ref_momenta
@ %def phs_fks_compute_xi_ref_momenta
@
<<phs fks: phs fks: TBP>>=
procedure :: compute_xi_ref_momenta_threshold => phs_fks_compute_xi_ref_momenta_threshold
<<phs fks: procedures>>=
subroutine phs_fks_compute_xi_ref_momenta_threshold (phs)
class(phs_fks_t), intent(inout) :: phs
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
call phs%generator%compute_xi_ref_momenta_threshold &
(phs%get_onshell_projected_momenta ())
end select
end subroutine phs_fks_compute_xi_ref_momenta_threshold
@ %def phs_fks_compute_xi_ref_momenta
@
<<phs fks: phs fks: TBP>>=
procedure :: compute_cms_energy => phs_fks_compute_cms_energy
<<phs fks: procedures>>=
subroutine phs_fks_compute_cms_energy (phs)
class(phs_fks_t), intent(inout) :: phs
if (phs%mode == PHS_MODE_ADDITIONAL_PARTICLE) &
call phs%generator%compute_cms_energy (phs%p_born_tot)
end subroutine phs_fks_compute_cms_energy
@ %def phs_fks_compute_cms_energy
@ When initial-state radiation is involved, either due to beamnstrahlung or
QCD corrections, it is important to have access to both the phase space points
in the center-of-mass and lab frame.
<<phs fks: phs fks: TBP>>=
procedure :: set_reference_frames => phs_fks_set_reference_frames
<<phs fks: procedures>>=
subroutine phs_fks_set_reference_frames (phs, is_cms)
class(phs_fks_t), intent(inout) :: phs
logical, intent(in) :: is_cms
type(lorentz_transformation_t) :: lt
associate (real_kinematics => phs%generator%real_kinematics)
if (phs%config%cm_frame) then
real_kinematics%p_born_cms%phs_point(1)%p = phs%p_born_tot
real_kinematics%p_born_lab%phs_point(1)%p = phs%p_born_tot
else
if (is_cms) then
real_kinematics%p_born_cms%phs_point(1)%p = phs%p_born_tot
lt = phs%lt_cm_to_lab
real_kinematics%p_born_lab%phs_point(1)%p = &
lt * phs%p_born_tot
else
real_kinematics%p_born_lab%phs_point(1)%p = phs%p_born_tot
lt = inverse (phs%lt_cm_to_lab)
real_kinematics%p_born_cms%phs_point(1)%p = &
lt * phs%p_born_tot
end if
end if
end associate
end subroutine phs_fks_set_reference_frames
@ %def phs_fks_set_reference_frames
@
<<phs fks: phs fks: TBP>>=
procedure :: i_phs_is_isr => phs_fks_i_phs_is_isr
<<phs fks: procedures>>=
function phs_fks_i_phs_is_isr (phs, i_phs) result (is_isr)
logical :: is_isr
class(phs_fks_t), intent(in) :: phs
integer, intent(in) :: i_phs
is_isr = phs%phs_identifiers(i_phs)%emitter <= phs%generator%n_in
end function phs_fks_i_phs_is_isr
@ %def phs_fks_i_phs_is_isr
@
\subsection{Creation of the real phase space - FSR}
At this point, the Born phase space has been generated, as well as the
three random variables $\xi$, $y$ and $\phi$. The question is how the
real phase space is generated for a final-state emission
configuration. We work with two different sets of momenta, the Born
configuration $\Bigl\{ \bar{k}_{\oplus}, \bar{k}_{\ominus}, \bar{k}_{1}, ...,
\bar{k}_{n} \Bigr\}$ and the real configuration $\Bigl\{ k_{\oplus},
k_{\ominus}, k_1,..., k_n, k_{n+1} \Bigr\}$. We define the momentum of
the emitter to be on the $n$-th position and the momentum of the
radiated particle to be at position $n+1$. The magnitude of the
spatial component of k is denoted by $\underline{k}$.
For final-state emissions, it is $\bar{k}_\oplus = k_\oplus$ and
$\bar{k}_\ominus = k_\ominus$. Thus, the center-of-mass systems
coincide and it is
\begin{equation}
q = \sum_{i=1}^n \bar{k}_i = \sum_{i=1}^{n+1} k_i,
\end{equation}
with $\vec{q} = 0$ and $q^2 = \left(q^0\right)^2$.
We want to construct the real phase space from the Born phase space
using three random numbers. They are defined as follows:
\begin{itemize}
\item $\xi = \frac{2k_{n+1}^0}{\sqrt{s}} \in [0, \xi_{max}]$, where
$k_{n+1}$ denotes the four-momentum of the radiated particle.
\item $y = \cos\theta = \frac{\vec{k}_n \cdot
\vec{k}_{n+1}}{\underline{k}_n \underline{k}_{n+1}}$ is the
splitting angle.
\item The angle between tho two splitting particles in the transversal
plane, $phi \in [0,2\pi]$.
\end{itemize}
Further, $k_{rec} = \sum_{i=1}^{n-1} k_i$ denotes the sum of all
recoiling momenta.
<<phs fks: phs fks generator: TBP>>=
generic :: generate_fsr => generate_fsr_default, generate_fsr_resonances
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_fsr_default => phs_fks_generator_generate_fsr_default
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_fsr_default (generator, emitter, i_phs, &
p_born, p_real, xi_y_phi, no_jacobians)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: emitter, i_phs
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
real(default), intent(in), dimension(3), optional :: xi_y_phi
logical, intent(in), optional :: no_jacobians
real(default) :: q0
call generator%generate_fsr_in (p_born, p_real)
q0 = sum (p_born(1:generator%n_in))**1
-
generator%i_fsr_first = generator%n_in + 1
call generator%generate_fsr_out (emitter, i_phs, p_born, p_real, q0, &
xi_y_phi = xi_y_phi, no_jacobians = no_jacobians)
if (debug_active (D_PHASESPACE)) then
call vector4_check_momentum_conservation (p_real, generator%n_in, &
rel_smallness = 1000 * tiny_07, abs_smallness = tiny_07)
end if
end subroutine phs_fks_generator_generate_fsr_default
@ %def phs_fks_generator_generate_fsr
@
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_fsr_resonances => phs_fks_generator_generate_fsr_resonances
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_fsr_resonances (generator, &
emitter, i_phs, i_con, p_born, p_real, xi_y_phi, no_jacobians)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: emitter, i_phs
integer, intent(in) :: i_con
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
real(default), intent(in), dimension(3), optional :: xi_y_phi
logical, intent(in), optional :: no_jacobians
integer, dimension(:), allocatable :: resonance_list
integer, dimension(size(p_born)) :: inv_resonance_list
type(vector4_t), dimension(:), allocatable :: p_tmp_born
type(vector4_t), dimension(:), allocatable :: p_tmp_real
type(vector4_t) :: p_resonance
real(default) :: q0
integer :: i, j, nlegborn, nlegreal
integer :: i_emitter
type(lorentz_transformation_t) :: boost_to_resonance
integer :: n_resonant_particles
if (debug_on) call msg_debug2 (D_PHASESPACE, "phs_fks_generator_generate_fsr_resonances")
nlegborn = size (p_born); nlegreal = nlegborn + 1
allocate (resonance_list (size (generator%resonance_contributors(i_con)%c)))
resonance_list = generator%resonance_contributors(i_con)%c
n_resonant_particles = size (resonance_list)
if (.not. any (resonance_list == emitter)) then
call msg_fatal ("Emitter must be included in the resonance list!")
else
do i = 1, n_resonant_particles
if (resonance_list (i) == emitter) i_emitter = i
end do
end if
inv_resonance_list = &
create_inverse_resonance_list (nlegborn, resonance_list)
allocate (p_tmp_born (n_resonant_particles))
allocate (p_tmp_real (n_resonant_particles + 1))
p_tmp_born = vector4_null
p_tmp_real = vector4_null
j = 1
do i = 1, n_resonant_particles
p_tmp_born(j) = p_born(resonance_list(i))
j = j + 1
end do
call generator%generate_fsr_in (p_born, p_real)
p_resonance = generator%real_kinematics%xi_ref_momenta(i_con)
q0 = p_resonance**1
boost_to_resonance = inverse (boost (p_resonance, q0))
p_tmp_born = boost_to_resonance * p_tmp_born
generator%i_fsr_first = 1
call generator%generate_fsr_out (emitter, i_phs, p_tmp_born, p_tmp_real, &
q0, i_emitter, xi_y_phi)
p_tmp_real = inverse (boost_to_resonance) * p_tmp_real
do i = generator%n_in + 1, nlegborn
if (any (resonance_list == i)) then
p_real(i) = p_tmp_real(inv_resonance_list (i))
else
p_real(i) = p_born (i)
end if
end do
p_real(nlegreal) = p_tmp_real (n_resonant_particles + 1)
if (debug_active (D_PHASESPACE)) then
call vector4_check_momentum_conservation (p_real, generator%n_in, &
rel_smallness = 1000 * tiny_07, abs_smallness = tiny_07)
end if
contains
function create_inverse_resonance_list (nlegborn, resonance_list) &
result (inv_resonance_list)
integer, intent(in) :: nlegborn
integer, intent(in), dimension(:) :: resonance_list
integer, dimension(nlegborn) :: inv_resonance_list
integer :: i, j
inv_resonance_list = 0
j = 1
do i = 1, nlegborn
if (any (i == resonance_list)) then
inv_resonance_list (i) = j
j = j + 1
end if
end do
end function create_inverse_resonance_list
function boosted_energy () result (E)
real(default) :: E
type(vector4_t) :: p_boost
p_boost = boost_to_resonance * p_resonance
E = p_boost%p(0)
end function boosted_energy
end subroutine phs_fks_generator_generate_fsr_resonances
@ %def phs_fks_generator_generate_fsr_resonances
@
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_fsr_threshold => phs_fks_generator_generate_fsr_threshold
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_fsr_threshold (generator, &
emitter, i_phs, p_born, p_real, xi_y_phi)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: emitter, i_phs
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
real(default), intent(in), dimension(3), optional :: xi_y_phi
type(vector4_t), dimension(2) :: p_tmp_born
type(vector4_t), dimension(3) :: p_tmp_real
integer :: nlegborn, nlegreal
type(vector4_t) :: p_top
real(default) :: q0
type(lorentz_transformation_t) :: boost_to_top
integer :: leg, other_leg
real(default) :: sqrts, mtop
if (debug_on) call msg_debug2 (D_PHASESPACE, "phs_fks_generator_generate_fsr_resonances")
nlegborn = size (p_born); nlegreal = nlegborn + 1
leg = thr_leg(emitter); other_leg = 3 - leg
p_tmp_born(1) = p_born (ass_boson(leg))
p_tmp_born(2) = p_born (ass_quark(leg))
call generator%generate_fsr_in (p_born, p_real)
p_top = generator%real_kinematics%xi_ref_momenta(leg)
q0 = p_top**1
sqrts = two * p_born(1)%p(0)
mtop = m1s_to_mpole (sqrts)
if (sqrts**2 - four * mtop**2 > zero) then
boost_to_top = inverse (boost (p_top, q0))
else
boost_to_top = identity
end if
p_tmp_born = boost_to_top * p_tmp_born
generator%i_fsr_first = 1
call generator%generate_fsr_out (emitter, i_phs, p_tmp_born, &
p_tmp_real, q0, 2, xi_y_phi)
p_tmp_real = inverse (boost_to_top) * p_tmp_real
p_real(ass_boson(leg)) = p_tmp_real(1)
p_real(ass_quark(leg)) = p_tmp_real(2)
p_real(ass_boson(other_leg)) = p_born(ass_boson(other_leg))
p_real(ass_quark(other_leg)) = p_born(ass_quark(other_leg))
p_real(THR_POS_GLUON) = p_tmp_real(3)
end subroutine phs_fks_generator_generate_fsr_threshold
@ %def phs_fks_generator_generate_fsr_threshold
@
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_fsr_in => phs_fks_generator_generate_fsr_in
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_fsr_in (generator, p_born, p_real)
class(phs_fks_generator_t), intent(inout) :: generator
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
integer :: i
do i = 1, generator%n_in
p_real(i) = p_born(i)
end do
end subroutine phs_fks_generator_generate_fsr_in
@ %def phs_fks_generator_generate_fsr_in
@
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_fsr_out => phs_fks_generator_generate_fsr_out
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_fsr_out (generator, &
emitter, i_phs, p_born, p_real, q0, p_emitter_index, xi_y_phi, no_jacobians)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: emitter, i_phs
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
real(default), intent(in) :: q0
integer, intent(in), optional :: p_emitter_index
real(default), intent(in), dimension(3), optional :: xi_y_phi
logical, intent(in), optional :: no_jacobians
real(default) :: xi, y, phi
integer :: nlegborn, nlegreal
real(default) :: uk_np1, uk_n
real(default) :: uk_rec, k_rec0
type(vector3_t) :: k_n_born, k
real(default) :: uk_n_born, uk, k2, k0_n
real(default) :: cpsi, beta
type(vector3_t) :: vec, vec_orth
type(lorentz_transformation_t) :: rot
integer :: i, p_em
logical :: compute_jac
p_em = emitter; if (present (p_emitter_index)) p_em = p_emitter_index
compute_jac = .true.
if (present (no_jacobians)) compute_jac = .not. no_jacobians
if (generator%i_fsr_first < 0) &
call msg_fatal ("FSR generator is called for outgoing particles but "&
&"i_fsr_first is not set!")
if (present (xi_y_phi)) then
xi = xi_y_phi(I_XI)
y = xi_y_phi(I_Y)
phi = xi_y_phi(I_PHI)
else
associate (rad_var => generator%real_kinematics)
xi = rad_var%xi_tilde
if (rad_var%supply_xi_max) xi = xi * rad_var%xi_max(i_phs)
y = rad_var%y(i_phs)
phi = rad_var%phi
end associate
end if
nlegborn = size (p_born)
nlegreal = nlegborn + 1
generator%E_gluon = q0 * xi / two
uk_np1 = generator%E_gluon
k_n_born = p_born(p_em)%p(1:3)
uk_n_born = k_n_born**1
generator%mrec2 = (q0 - p_born(p_em)%p(0))**2 &
- space_part_norm(p_born(p_em))**2
if (generator%is_massive(emitter)) then
call generator%compute_emitter_kinematics (y, emitter, &
i_phs, q0, k0_n, uk_n, uk, compute_jac)
else
call generator%compute_emitter_kinematics (y, q0, uk_n, uk)
generator%real_kinematics%y_soft(i_phs) = y
k0_n = uk_n
end if
if (debug_on) call msg_debug2 (D_PHASESPACE, "phs_fks_generator_generate_fsr_out")
call debug_input_values ()
vec = uk_n / uk_n_born * k_n_born
vec_orth = create_orthogonal (vec)
p_real(p_em)%p(0) = k0_n
p_real(p_em)%p(1:3) = vec%p(1:3)
cpsi = (uk_n**2 + uk**2 - uk_np1**2) / (two * uk_n * uk)
!!! This is to catch the case where cpsi = 1, but numerically
!!! turns out to be slightly larger than 1.
call check_cpsi_bound (cpsi)
rot = rotation (cpsi, - sqrt (one - cpsi**2), vec_orth)
p_real(p_em) = rot * p_real(p_em)
vec = uk_np1 / uk_n_born * k_n_born
vec_orth = create_orthogonal (vec)
p_real(nlegreal)%p(0) = uk_np1
p_real(nlegreal)%p(1:3) = vec%p(1:3)
cpsi = (uk_np1**2 + uk**2 - uk_n**2) / (two * uk_np1 * uk)
call check_cpsi_bound (cpsi)
rot = rotation (cpsi, sqrt (one - cpsi**2), vec_orth)
p_real(nlegreal) = rot * p_real(nlegreal)
call construct_recoiling_momenta ()
if (compute_jac) call compute_jacobians ()
contains
<<phs fks: generator generate fsr out procedures>>
end subroutine phs_fks_generator_generate_fsr_out
@ %def phs_fks_generator_generate_fsr_out
@
<<phs fks: generator generate fsr out procedures>>=
subroutine debug_input_values ()
if (debug2_active (D_PHASESPACE)) then
call generator%write ()
print *, 'emitter = ', emitter
print *, 'p_born:'
call vector4_write_set (p_born)
print *, 'p_real:'
call vector4_write_set (p_real)
print *, 'q0 = ', q0
if (present(p_emitter_index)) then
print *, 'p_emitter_index = ', p_emitter_index
else
print *, 'p_emitter_index not given'
end if
end if
end subroutine debug_input_values
<<phs fks: generator generate fsr out procedures>>=
subroutine check_cpsi_bound (cpsi)
real(default), intent(inout) :: cpsi
if (cpsi > one) then
cpsi = one
else if (cpsi < -one) then
cpsi = - one
end if
end subroutine check_cpsi_bound
@ Construction of the recoiling momenta. The reshuffling of momenta
must not change the invariant mass of the recoiling system, which
means $k_{\rm{rec}}^2 = \bar{k_{\rm{rec}}}^2$. Therefore, the momenta
are related by a boost, $\bar{k}_i = \Lambda k_i$. The boost parameter
is
\begin{equation*}
\beta = \frac{q^2 - (k_{\rm{rec}}^0 +
\underline{k}_{\rm{rec}})^2}{q^2 + (k_{\rm{rec}}^0 +
\underline{k}_{\rm{rec}})^2}
\end{equation*}
<<phs fks: generator generate fsr out procedures>>=
subroutine construct_recoiling_momenta ()
type(lorentz_transformation_t) :: lambda
k_rec0 = q0 - p_real(p_em)%p(0) - p_real(nlegreal)%p(0)
- uk_rec = sqrt (k_rec0**2 - generator%mrec2)
+ if (k_rec0**2 > generator%mrec2) then
+ uk_rec = sqrt (k_rec0**2 - generator%mrec2)
+ else
+ uk_rec = 0
+ end if
if (generator%is_massive(emitter)) then
beta = compute_beta (q0**2, k_rec0, uk_rec, &
p_born(p_em)%p(0), uk_n_born)
else
beta = compute_beta (q0**2, k_rec0, uk_rec)
end if
k = p_real(p_em)%p(1:3) + p_real(nlegreal)%p(1:3)
vec%p(1:3) = one / uk * k%p(1:3)
lambda = boost (beta / sqrt(one - beta**2), vec)
do i = generator%i_fsr_first, nlegborn
if (i /= p_em) then
p_real(i) = lambda * p_born(i)
end if
end do
vec%p(1:3) = p_born(p_em)%p(1:3) / uk_n_born
rot = rotation (cos(phi), sin(phi), vec)
p_real(nlegreal) = rot * p_real(nlegreal)
p_real(p_em) = rot * p_real(p_em)
end subroutine construct_recoiling_momenta
@ The factor $\frac{q^2}{(4\pi)^3}$ is not included here since it is
supplied during phase space generation. Also, we already divide by
$\xi$.
<<phs fks: generator generate fsr out procedures>>=
subroutine compute_jacobians ()
associate (jac => generator%real_kinematics%jac(i_phs))
if (generator%is_massive(emitter)) then
jac%jac(1) = jac%jac(1) * four / q0 / uk_n_born / xi
else
k2 = two * uk_n * uk_np1* (one - y)
jac%jac(1) = uk_n**2 / uk_n_born / (uk_n - k2 / (two * q0))
end if
jac%jac(2) = one
jac%jac(3) = one - xi / two * q0 / uk_n_born
end associate
end subroutine compute_jacobians
@ %def compute_jacobians
@
<<phs fks: phs fks: TBP>>=
procedure :: generate_fsr_in => phs_fks_generate_fsr_in
<<phs fks: procedures>>=
subroutine phs_fks_generate_fsr_in (phs)
class(phs_fks_t), intent(inout) :: phs
type(vector4_t), dimension(:), allocatable :: p
p = phs%generator%real_kinematics%p_born_lab%get_momenta (1, phs%generator%n_in)
end subroutine phs_fks_generate_fsr_in
@ %def phs_fks_generate_fsr_in
@
<<phs fks: phs fks: TBP>>=
procedure :: generate_fsr => phs_fks_generate_fsr
<<phs fks: procedures>>=
subroutine phs_fks_generate_fsr (phs, emitter, i_phs, p_real, i_con, &
xi_y_phi, no_jacobians)
class(phs_fks_t), intent(inout) :: phs
integer, intent(in) :: emitter, i_phs
type(vector4_t), intent(inout), dimension(:) :: p_real
integer, intent(in), optional :: i_con
real(default), intent(in), dimension(3), optional :: xi_y_phi
logical, intent(in), optional :: no_jacobians
type(vector4_t), dimension(:), allocatable :: p
associate (generator => phs%generator)
allocate (p (1:generator%real_kinematics%p_born_cms%get_n_particles()), &
source = generator%real_kinematics%p_born_cms%phs_point(1)%p)
generator%real_kinematics%supply_xi_max = .true.
if (present (i_con)) then
call generator%generate_fsr (emitter, i_phs, i_con, p, p_real, &
xi_y_phi, no_jacobians)
else
call generator%generate_fsr (emitter, i_phs, p, p_real, &
xi_y_phi, no_jacobians)
end if
generator%real_kinematics%p_real_cms%phs_point(i_phs)%p = p_real
if (.not. phs%config%cm_frame) p_real = phs%lt_cm_to_lab * p_real
generator%real_kinematics%p_real_lab%phs_point(i_phs)%p = p_real
end associate
end subroutine phs_fks_generate_fsr
@ %def phs_fks_generate_fsr
@
<<phs fks: phs fks: TBP>>=
procedure :: get_onshell_projected_momenta => phs_fks_get_onshell_projected_momenta
<<phs fks: procedures>>=
pure function phs_fks_get_onshell_projected_momenta (phs) result (p)
type(vector4_t), dimension(:), allocatable :: p
class(phs_fks_t), intent(in) :: phs
p = phs%generator%real_kinematics%p_born_onshell%phs_point(1)%p
end function phs_fks_get_onshell_projected_momenta
@ %def phs_fks_get_onshell_projected_momenta
@
<<phs fks: phs fks: TBP>>=
procedure :: generate_fsr_threshold => phs_fks_generate_fsr_threshold
<<phs fks: procedures>>=
subroutine phs_fks_generate_fsr_threshold (phs, emitter, i_phs, p_real)
class(phs_fks_t), intent(inout) :: phs
integer, intent(in) :: emitter, i_phs
type(vector4_t), intent(inout), dimension(:), optional :: p_real
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: pp
integer :: leg
associate (generator => phs%generator)
generator%real_kinematics%supply_xi_max = .true.
allocate (p_born (1 : generator%real_kinematics%p_born_cms%get_n_particles()))
p_born = generator%real_kinematics%p_born_onshell%get_momenta (1)
allocate (pp (size (p_born) + 1))
call generator%generate_fsr_threshold (emitter, i_phs, p_born, pp)
leg = thr_leg (emitter)
call generator%real_kinematics%p_real_onshell(leg)%set_momenta (i_phs, pp)
if (present (p_real)) p_real = pp
end associate
end subroutine phs_fks_generate_fsr_threshold
@ %def phs_fks_generate_fsr_threshold
@
<<phs fks: phs fks: TBP>>=
generic :: compute_xi_max => compute_xi_max_internal, compute_xi_max_with_output
procedure :: compute_xi_max_internal => phs_fks_compute_xi_max_internal
<<phs fks: procedures>>=
subroutine phs_fks_compute_xi_max_internal (phs, p, threshold)
class(phs_fks_t), intent(inout) :: phs
type(vector4_t), intent(in), dimension(:) :: p
logical, intent(in) :: threshold
integer :: i_phs, i_con, emitter
do i_phs = 1, size (phs%phs_identifiers)
associate (phs_id => phs%phs_identifiers(i_phs), generator => phs%generator)
emitter = phs_id%emitter
if (threshold) then
call generator%compute_xi_max (emitter, i_phs, p, &
generator%real_kinematics%xi_max(i_phs), i_con = thr_leg(emitter))
else if (allocated (phs_id%contributors)) then
do i_con = 1, size (phs_id%contributors)
call generator%compute_xi_max (emitter, i_phs, p, &
generator%real_kinematics%xi_max(i_phs), i_con = 1)
end do
else
call generator%compute_xi_max (emitter, i_phs, p, &
generator%real_kinematics%xi_max(i_phs))
end if
end associate
end do
end subroutine phs_fks_compute_xi_max_internal
@ %def phs_fks_compute_xi_max
@
<<phs fks: phs fks: TBP>>=
procedure :: compute_xi_max_with_output => phs_fks_compute_xi_max_with_output
<<phs fks: procedures>>=
subroutine phs_fks_compute_xi_max_with_output (phs, emitter, i_phs, y, p, xi_max)
class(phs_fks_t), intent(inout) :: phs
integer, intent(in) :: i_phs, emitter
real(default), intent(in) :: y
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(out) :: xi_max
call phs%generator%compute_xi_max (emitter, i_phs, p, xi_max, y_in = y)
end subroutine phs_fks_compute_xi_max_with_output
@ %def phs_fks_compute_xi_max_with_output
@
<<phs fks: phs fks generator: TBP>>=
generic :: compute_emitter_kinematics => &
compute_emitter_kinematics_massless, &
compute_emitter_kinematics_massive
procedure :: compute_emitter_kinematics_massless => &
phs_fks_generator_compute_emitter_kinematics_massless
procedure :: compute_emitter_kinematics_massive => &
phs_fks_generator_compute_emitter_kinematics_massive
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_emitter_kinematics_massless &
(generator, y, q0, uk_em, uk)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: y, q0
real(default), intent(out) :: uk_em, uk
real(default) :: k0_np1, q2
k0_np1 = generator%E_gluon
q2 = q0**2
uk_em = (q2 - generator%mrec2 - two * q0 * k0_np1) / (two * (q0 - k0_np1 * (one - y)))
uk = sqrt (uk_em**2 + k0_np1**2 + two * uk_em * k0_np1 * y)
end subroutine phs_fks_generator_compute_emitter_kinematics_massless
subroutine phs_fks_generator_compute_emitter_kinematics_massive &
(generator, y, em, i_phs, q0, k0_em, uk_em, uk, compute_jac)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: y
integer, intent(in) :: em, i_phs
real(default), intent(in) :: q0
real(default), intent(inout) :: k0_em, uk_em, uk
logical, intent(in) :: compute_jac
real(default) :: k0_np1, q2, mrec2, m2
real(default) :: k0_rec_max, k0_em_max, k0_rec, uk_rec
real(default) :: z, z1, z2
k0_np1 = generator%E_gluon
q2 = q0**2
mrec2 = generator%mrec2
m2 = generator%m2(em)
k0_rec_max = (q2 - m2 + mrec2) / (two * q0)
k0_em_max = (q2 + m2 - mrec2) /(two * q0)
z1 = (k0_rec_max + sqrt (k0_rec_max**2 - mrec2)) / q0
z2 = (k0_rec_max - sqrt (k0_rec_max**2 - mrec2)) / q0
z = z2 - (z2 - z1) * (one + y) / two
k0_em = k0_em_max - k0_np1 * z
k0_rec = q0 - k0_np1 - k0_em
uk_em = sqrt(k0_em**2 - m2)
uk_rec = sqrt(k0_rec**2 - mrec2)
uk = uk_rec
if (compute_jac) &
generator%real_kinematics%jac(i_phs)%jac = q0 * (z1 - z2) / four * k0_np1
generator%real_kinematics%y_soft(i_phs) = &
(two * q2 * z - q2 - mrec2 + m2) / (sqrt(k0_em_max**2 - m2) * q0) / two
end subroutine phs_fks_generator_compute_emitter_kinematics_massive
@ %def phs_fks_generator_compute_emitter_kinematics
@
<<phs fks: procedures>>=
function recompute_xi_max (q0, mrec2, m2, y) result (xi_max)
real(default) :: xi_max
real(default), intent(in) :: q0, mrec2, m2, y
real(default) :: q2, k0_np1_max, k0_rec_max
real(default) :: z1, z2, z
q2 = q0**2
k0_rec_max = (q2 - m2 + mrec2) / (two * q0)
z1 = (k0_rec_max + sqrt (k0_rec_max**2 - mrec2)) / q0
z2 = (k0_rec_max - sqrt (k0_rec_max**2 - mrec2)) / q0
z = z2 - (z2 - z1) * (one + y) / 2
k0_np1_max = - (q2 * z**2 - two * q0 * k0_rec_max * z + mrec2) / (two * q0 * z * (one - z))
xi_max = two * k0_np1_max / q0
end function recompute_xi_max
@ %def recompute_xi_max
@
<<phs fks: procedures>>=
function compute_beta_massless (q2, k0_rec, uk_rec) result (beta)
real(default), intent(in) :: q2, k0_rec, uk_rec
real(default) :: beta
beta = (q2 - (k0_rec + uk_rec)**2) / (q2 + (k0_rec + uk_rec)**2)
end function compute_beta_massless
function compute_beta_massive (q2, k0_rec, uk_rec, &
k0_em_born, uk_em_born) result (beta)
real(default), intent(in) :: q2, k0_rec, uk_rec
real(default), intent(in) :: k0_em_born, uk_em_born
real(default) :: beta
real(default) :: k0_rec_born, uk_rec_born, alpha
k0_rec_born = sqrt(q2) - k0_em_born
uk_rec_born = uk_em_born
alpha = (k0_rec + uk_rec) / (k0_rec_born + uk_rec_born)
beta = (one - alpha**2) / (one + alpha**2)
end function compute_beta_massive
@ %def compute_beta
@ The momentum of the radiated particle is computed according to
\begin{equation}
\label{eq:phs fks:compute k_n}
\underline{k}_n = \frac{q^2 - M_{\rm{rec}}^2 -
2q^0\underline{k}_{n+1}}{2(q^0 - \underline{k}_{n+1}(1-y))},
\end{equation}
with $k = k_n + k_{n+1}$ and $M_{\rm{rec}}^2 = k_{\rm{rec}}^2 =
\left(q-k\right)^2$. Because of $\boldsymbol{\bar{k}}_n \parallel
\boldsymbol{k}_n + \boldsymbol{k}_{n+1}$ we find $M_{\rm{rec}}^2 =
\left(q-\bar{k}_n\right)^2$.
Equation \ref{eq:phs fks: compute k_n} follows from the fact that
$\left(\boldsymbol{k} - \boldsymbol{k}_n\right)^2 =
\boldsymbol{k}_{n+1}^2$, which is equivalent to $\boldsymbol{k}_n
\cdot \boldsymbol{k} = \frac{1}{2} \left(\underline{k}_n^2 +
\underline{k}^2 - \underline{k}_{n+1}^2\right)$.\\
$\boldsymbol{k}_n$ and $\boldsymbol{k}_{n+1}$ are obtained by first
setting up vectors parallel to $\boldsymbol{\bar{k}}_n$,
\begin{equation*}
\boldsymbol{k}_n' = \underline{k}_n
\frac{\bar{\pmb{k}}_n}{\underline{\bar{k}}_n}, \quad \pmb{k}_{n+1}'
= \underline{k}_{n+1}\frac{\bar{\pmb{k}}_n}{\underline{\bar{k}}_n},
\end{equation*}
and then rotating these vectors by an amount of $\cos\psi_n =
\frac{\boldsymbol{k}_n\cdot\pmb{k}}{\underline{k}_n \underline{k}}$.
@ The emitted particle cannot have more momentum than the emitter has
in the Born phase space. Thus, there is an upper bound for $\xi$,
determined by the condition $k_{n+1}^0 = \underline{\bar{k}}_n$, which
is equal to
\begin{equation*}
\xi_{\rm{max}} = \frac{2}{\underline{\bar{k}}_n}{q^0}.
\end{equation*}
<<phs fks: procedures>>=
pure function get_xi_max_fsr_massless (p_born, q0, emitter) result (xi_max)
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in) :: q0
integer, intent(in) :: emitter
real(default) :: xi_max
real(default) :: uk_n_born
uk_n_born = space_part_norm (p_born(emitter))
xi_max = two * uk_n_born / q0
end function get_xi_max_fsr_massless
@ %def get_xi_max_fsr_massless
@ The computation of $\xi_{\rm{max}}$ for massive emitters is described
in arXiv:1202.0465. Let's recapitulate it here.
We consider the Dalitz-domain created by $k_{n+1}^0$, $k_n^0$ and
$k_{\rm{rec}}^0$ and introduce the parameterization
\begin{equation*}
k_n^0 = \bar{k}_n^0 - zk_{n+1}^0
\end{equation*}
Then, for each value of $z$, there exists a maximum value of
$\underline{k}_{n+1}$ from which $\xi_{\rm{max}}$ can be extracted via
$\xi_{\rm{max}} = 2k_{n+1}^0/q$. It is determined by the condition
\begin{equation*}
\underline{k}_{n+1} \pm \underline{k}_n \pm \underline{k}_{\rm{rec}} = 0.
\end{equation*}
This can be manipulated to yield
\begin{equation*}
\left(\underline{k}_{n+1}^2 + \underline{k}_n^2 -
\underline{k}_{\rm{rec}}^2\right)^2 =
4\underline{k}^2_{n+1}\underline{k}_n^2.
\end{equation*}
Here we can use $\underline{k}_n^2 = \left(k_n^0\right)^2 - m^2$ and
$\underline{k}_{\rm{rec}}^2 = \left(q - k_n^0 - k_{n+1}^0\right)^2 -
M_{\rm{rec}}^2$, as well as the above parameterization of $k_n^0$, to
obtain
\begin{equation*}
4\underline{k}_{n+1}^2\left(2\underline{k}_{n+1}qz(1-z) +
q^2z^2 - 2q\bar{k}_{\rm{rec}}^0z + M_{\rm{rec}}^2\right) = 0.
\end{equation*}
Solving for $k_{n+1}^0$ gives
\begin{equation}
k_{n+1}^0 = \frac{2q\bar{k}^0_{\rm{rec}}z - q^2z^2 - M_{\rm{rec}}^2}{2qz(1-z)}.
\label{XiMaxMassive}
\end{equation}
It is still open how to compute $z$. For this, consider that the
right-hand-side of equation (\ref{XiMaxMassive}) vanishes for
\begin{equation*}
z_{1,2} = \left(\bar{k}_{\rm{rec}}^0 \pm
\sqrt{\left(\bar{k}_{\rm{rec}}^0\right)^2 - M_{\rm{rec}}^2}\right)/q,
\end{equation*}
which corresponds to the borders of the Dalitz-region where the gluon
momentum vanishes. Thus we define
\begin{equation*}
z = z_2 - \frac{1}{2} (z_2 - z_1)(1+y).
\end{equation*}
<<phs fks: procedures>>=
pure function get_xi_max_fsr_massive (p_born, q0, emitter, m2, y) result (xi_max)
real(default) :: xi_max
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in) :: q0
integer, intent(in) :: emitter
real(default), intent(in) :: m2, y
real(default) :: mrec2
real(default) :: k0_rec_max
real(default) :: z, z1, z2
real(default) :: k0_np1_max
associate (p => p_born(emitter)%p)
mrec2 = (q0 - p(0))**2 - p(1)**2 - p(2)**2 - p(3)**2
end associate
call compute_dalitz_bounds (q0, m2, mrec2, z1, z2, k0_rec_max)
z = z2 - (z2 - z1) * (one + y) / two
k0_np1_max = - (q0**2 * z**2 - two * q0 * k0_rec_max * z + mrec2) &
/ (two * q0 * z * (one - z))
xi_max = two * k0_np1_max / q0
end function get_xi_max_fsr_massive
@ %def get_xi_max_fsr_massive
@
<<phs fks: parameters>>=
integer, parameter, public :: I_PLUS = 1
integer, parameter, public :: I_MINUS = 2
@ %def parameters
@
<<phs fks: procedures>>=
function get_xi_max_isr (xb, y) result (xi_max)
real(default) :: xi_max
real(default), dimension(2), intent(in) :: xb
real(default), intent(in) :: y
xi_max = one - max (xi_max_isr_plus (xb(I_PLUS), y), xi_max_isr_minus (xb(I_MINUS), y))
end function get_xi_max_isr
@ %def get_xi_max_isr
@
<<phs fks: procedures>>=
function xi_max_isr_plus (x, y)
real(default) :: xi_max_isr_plus
real(default), intent(in) :: x, y
real(default) :: deno
deno = sqrt ((one + x**2)**2 * (one - y)**2 + 16 * y * x**2) + (one - y) * (1 - x**2)
xi_max_isr_plus = two * (one + y) * x**2 / deno
end function xi_max_isr_plus
function xi_max_isr_minus (x, y)
real(default) :: xi_max_isr_minus
real(default), intent(in) :: x, y
real(default) :: deno
deno = sqrt ((one + x**2)**2 * (one + y)**2 - 16 * y * x**2) + (one + y) * (1 - x**2)
xi_max_isr_minus = two * (one - y) * x**2 / deno
end function xi_max_isr_minus
@ %def xi_max_isr_plus, xi_max_isr_minus
@
<<phs fks: procedures>>=
recursive function get_xi_max_isr_decay (p) result (xi_max)
real(default) :: xi_max
type(vector4_t), dimension(:), intent(in) :: p
integer :: n_tot
type(vector4_t), dimension(:), allocatable :: p_dec_new
n_tot = size (p)
if (n_tot == 3) then
xi_max = xi_max_one_to_two (p(1), p(2), p(3))
else
allocate (p_dec_new (n_tot - 1))
p_dec_new(1) = sum (p (3 : ))
p_dec_new(2 : n_tot - 1) = p (3 : n_tot)
xi_max = min (xi_max_one_to_two (p(1), p(2), sum(p(3 : ))), &
get_xi_max_isr_decay (p_dec_new))
end if
contains
function xi_max_one_to_two (p_in, p_out1, p_out2) result (xi_max)
real(default) :: xi_max
type(vector4_t), intent(in) :: p_in, p_out1, p_out2
real(default) :: m_in, m_out1, m_out2
m_in = p_in**1
m_out1 = p_out1**1; m_out2 = p_out2**1
xi_max = one - (m_out1 + m_out2)**2 / m_in**2
end function xi_max_one_to_two
end function get_xi_max_isr_decay
@ %def get_xi_max_isr_decay
@
\subsection{Creation of the real phase space - ISR}
<<phs fks: phs fks: TBP>>=
procedure :: generate_isr => phs_fks_generate_isr
<<phs fks: procedures>>=
subroutine phs_fks_generate_isr (phs, i_phs, p_real)
class(phs_fks_t), intent(inout) :: phs
integer, intent(in) :: i_phs
type(vector4_t), intent(inout), dimension(:) :: p_real
type(vector4_t) :: p0, p1
type(lorentz_transformation_t) :: lt
real(default) :: sqrts_hat
type(vector4_t), dimension(:), allocatable :: p_work
associate (generator => phs%generator)
select case (generator%n_in)
case (1)
allocate (p_work (1:generator%real_kinematics%p_born_cms%get_n_particles()), &
source = generator%real_kinematics%p_born_cms%phs_point(1)%p)
call generator%generate_isr_fixed_beam_energy (i_phs, p_work, p_real)
phs%config%cm_frame = .true.
case (2)
select case (generator%isr_kinematics%isr_mode)
case (SQRTS_FIXED)
allocate (p_work (1:generator%real_kinematics%p_born_cms%get_n_particles()), &
source = generator%real_kinematics%p_born_cms%phs_point(1)%p)
call generator%generate_isr_fixed_beam_energy (i_phs, p_work, p_real)
case (SQRTS_VAR)
allocate (p_work (1:generator%real_kinematics%p_born_lab%get_n_particles()), &
source = generator%real_kinematics%p_born_lab%phs_point(1)%p)
call generator%generate_isr (i_phs, p_work, p_real)
end select
end select
generator%real_kinematics%p_real_lab%phs_point(i_phs)%p = p_real
if (.not. phs%config%cm_frame) then
sqrts_hat = (p_real(1) + p_real(2))**1
p0 = p_real(1) + p_real(2)
lt = boost (p0, sqrts_hat)
p1 = inverse(lt) * p_real(1)
lt = lt * rotation_to_2nd (3, space_part (p1))
phs%generator%real_kinematics%p_real_cms%phs_point(i_phs)%p = &
inverse (lt) * p_real
else
phs%generator%real_kinematics%p_real_cms%phs_point(i_phs)%p = p_real
end if
end associate
end subroutine phs_fks_generate_isr
@ %def phs_fks_generate_isr
@ The real phase space for an inital-state emission involved in a decay
process is generated by first setting the gluon momentum like in the
scattering case by using its angular coordinates $y$ and $\phi$ and then
adjusting the gluon energy with $\xi$. The emitter momentum is kept
identical to the Born case, i.e. $p_{\rm{in}} = \bar{p}_{\rm{in}}$, so
that after the emission it has momentum $p_{\rm{virt}} = p_{\rm{in}} -
p_{\rm{g}}$ and invariant mass $m^2 = p_{\rm{virt}}^2$. Note that the
final state momenta have to remain on-shell, so that $p_1^2 =
\bar{p}_1^2 = m_1^2$ and $p_2^2 = \bar{p}_2^2 = m_2^2$. Let $\Lambda$ be
the boost from into the rest frame of the emitter after emission, i.e.
$\Lambda p_{\rm{virt}} = \left(m, 0, 0, 0\right)$. In this reference
frame, the spatial components of the final-state momenta sum up to zero,
and their magnitude is
\begin{equation*}
p = \frac{\sqrt {\lambda (m^2, m_1^2, m_2^2)}}{2m},
\end{equation*}
a fact already used in the evaluation of the phase space trees of
[[phs_forest]]. Obviously, from this, the final-state energies can be
deferred via $E_i^2 = m_i^2 - p^2$. In the next step, the $p_{1,2}$ are
set up as vectors $(E,0,0,\pm p)$ along the z-axis and then rotated
about the same azimuthal and polar angles as in the Born system.
Finally, the momenta are boosted out of the rest frame by multiplying
with $\Lambda$.
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_isr_fixed_beam_energy => phs_fks_generator_generate_isr_fixed_beam_energy
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_isr_fixed_beam_energy (generator, i_phs, p_born, p_real)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: i_phs
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
real(default) :: xi_max, xi, y, phi
integer :: nlegborn, nlegreal, i
real(default) :: k0_np1
real(default) :: msq_in
type(vector4_t) :: p_virt
real(default) :: jac_real
associate (rad_var => generator%real_kinematics)
xi_max = rad_var%xi_max(i_phs)
xi = rad_var%xi_tilde * xi_max
y = rad_var%y(i_phs)
phi = rad_var%phi
rad_var%y_soft(i_phs) = y
end associate
nlegborn = size (p_born)
nlegreal = nlegborn + 1
msq_in = sum (p_born(1:generator%n_in))**2
generator%real_kinematics%jac(i_phs)%jac = one
p_real(1) = p_born(1)
if (generator%n_in > 1) p_real(2) = p_born(2)
k0_np1 = zero
do i = 1, generator%n_in
k0_np1 = k0_np1 + p_real(i)%p(0) * xi / two
end do
p_real(nlegreal)%p(0) = k0_np1
p_real(nlegreal)%p(1) = k0_np1 * sqrt(one - y**2) * sin(phi)
p_real(nlegreal)%p(2) = k0_np1 * sqrt(one - y**2) * cos(phi)
p_real(nlegreal)%p(3) = k0_np1 * y
p_virt = sum (p_real(1:generator%n_in)) - p_real(nlegreal)
jac_real = one
call generate_on_shell_decay (p_virt, &
p_born(generator%n_in + 1 : nlegborn), p_real(generator%n_in + 1 : nlegreal - 1), &
1, msq_in, jac_real)
associate (jac => generator%real_kinematics%jac(i_phs))
jac%jac(1) = jac_real
jac%jac(2) = one
end associate
end subroutine phs_fks_generator_generate_isr_fixed_beam_energy
@ %def phs_fks_generator_generate_isr_fixed_beam_energy
@
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_isr_factorized => phs_fks_generator_generate_isr_factorized
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_isr_factorized (generator, i_phs, emitter, p_born, p_real)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: i_phs, emitter
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
type(vector4_t), dimension(3) :: p_tmp_born
type(vector4_t), dimension(4) :: p_tmp_real
type(vector4_t) :: p_top
type(lorentz_transformation_t) :: boost_to_rest_frame
integer, parameter :: nlegreal = 7 !!! Factorized phase space so far only required for ee -> bwbw
p_tmp_born = vector4_null; p_tmp_real = vector4_null
p_real(1:2) = p_born(1:2)
if (emitter == THR_POS_B) then
p_top = p_born (THR_POS_WP) + p_born (THR_POS_B)
p_tmp_born(2) = p_born (THR_POS_WP)
p_tmp_born(3) = p_born (THR_POS_B)
else if (emitter == THR_POS_BBAR) then
p_top = p_born (THR_POS_WM) + p_born (THR_POS_BBAR)
p_tmp_born(2) = p_born (THR_POS_WM)
p_tmp_born(3) = p_born (THR_POS_BBAR)
else
call msg_fatal ("Threshold computation requires emitters to be at position 5 and 6 " // &
"Please check if your process specification fulfills this requirement.")
end if
p_tmp_born (1) = p_top
boost_to_rest_frame = inverse (boost (p_top, p_top**1))
p_tmp_born = boost_to_rest_frame * p_tmp_born
call generator%compute_xi_max_isr_factorized (i_phs, p_tmp_born)
call generator%generate_isr_fixed_beam_energy (i_phs, p_tmp_born, p_tmp_real)
p_tmp_real = inverse (boost_to_rest_frame) * p_tmp_real
if (emitter == THR_POS_B) then
p_real(THR_POS_WP) = p_tmp_real(2)
p_real(THR_POS_B) = p_tmp_real(3)
p_real(THR_POS_WM) = p_born(THR_POS_WM)
p_real(THR_POS_BBAR) = p_born(THR_POS_BBAR)
!!! Exception has been handled above
else
p_real(THR_POS_WM) = p_tmp_real(2)
p_real(THR_POS_BBAR) = p_tmp_real(3)
p_real(THR_POS_WP) = p_born(THR_POS_WP)
p_real(THR_POS_B) = p_born(THR_POS_B)
end if
p_real(nlegreal) = p_tmp_real(4)
end subroutine phs_fks_generator_generate_isr_factorized
@ %def phs_fks_generator_generate_isr_factorized
@
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_isr => phs_fks_generator_generate_isr
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_isr (generator, i_phs, p_born, p_real)
!!! Important: Import momenta in the lab frame
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: i_phs
type(vector4_t), intent(in) , dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
real(default) :: xi_max, xi_tilde, xi, y, phi
integer :: nlegborn, nlegreal
real(default) :: sqrts_real
real(default) :: k0_np1
type(lorentz_transformation_t) :: lambda_transv, lambda_longit, lambda_longit_inv
real(default) :: x_plus, x_minus, xb_plus, xb_minus
real(default) :: onemy, onepy
integer :: i
real(default) :: xi_plus, xi_minus
real(default) :: beta_gamma
type(vector3_t) :: beta_vec
associate (rad_var => generator%real_kinematics)
xi_max = rad_var%xi_max(i_phs)
xi_tilde = rad_var%xi_tilde
xi = xi_tilde * xi_max
y = rad_var%y(i_phs)
onemy = one - y; onepy = one + y
phi = rad_var%phi
rad_var%y_soft(i_phs) = y
end associate
nlegborn = size (p_born)
nlegreal = nlegborn + 1
generator%isr_kinematics%sqrts_born = (p_born(1) + p_born(2))**1
!!! Initial state real momenta
xb_plus = generator%isr_kinematics%x(I_PLUS)
xb_minus = generator%isr_kinematics%x(I_MINUS)
x_plus = xb_plus / sqrt(one - xi) * sqrt ((two - xi * onemy) / (two - xi * onepy))
x_minus = xb_minus / sqrt(one - xi) * sqrt ((two - xi * onepy) / (two - xi * onemy))
xi_plus = xi_tilde * (one - xb_plus)
xi_minus = xi_tilde * (one - xb_minus)
p_real(I_PLUS) = x_plus / xb_plus * p_born(I_PLUS)
p_real(I_MINUS) = x_minus / xb_minus * p_born(I_MINUS)
generator%isr_kinematics%z(I_PLUS) = x_plus / xb_plus
generator%isr_kinematics%z(I_MINUS) = x_minus / xb_minus
generator%isr_kinematics%z_coll(I_PLUS) = one / (one - xi_plus)
generator%isr_kinematics%z_coll(I_MINUS) = one / (one - xi_minus)
!!! Create radiation momentum
sqrts_real = generator%isr_kinematics%sqrts_born / sqrt (one - xi)
k0_np1 = sqrts_real * xi / two
p_real(nlegreal)%p(0) = k0_np1
p_real(nlegreal)%p(1) = k0_np1 * sqrt (one - y**2) * sin(phi)
p_real(nlegreal)%p(2) = k0_np1 * sqrt (one - y**2) * cos(phi)
p_real(nlegreal)%p(3) = k0_np1 * y
call get_boost_parameters (p_real, beta_gamma, beta_vec)
lambda_longit = create_longitudinal_boost (beta_gamma, beta_vec, inverse = .true.)
p_real(nlegreal) = lambda_longit * p_real(nlegreal)
call get_boost_parameters (p_born, beta_gamma, beta_vec)
lambda_longit = create_longitudinal_boost (beta_gamma, beta_vec, inverse = .false.)
forall (i = 3 : nlegborn) p_real(i) = lambda_longit * p_born(i)
lambda_transv = create_transversal_boost (p_real(nlegreal), xi, sqrts_real)
forall (i = 3 : nlegborn) p_real(i) = lambda_transv * p_real(i)
lambda_longit_inv = create_longitudinal_boost (beta_gamma, beta_vec, inverse = .true.)
forall (i = 3 : nlegborn) p_real(i) = lambda_longit_inv * p_real(i)
!!! Compute jacobians
associate (jac => generator%real_kinematics%jac(i_phs))
!!! Additional 1 / (1 - xi) factor because in the real jacobian,
!!! there is s_real in the numerator
!!! We also have to adapt the flux factor, which is 1/2s_real for the real component
!!! The reweighting factor is s_born / s_real, cancelling the (1-x) factor from above
jac%jac(1) = one / (one - xi)
jac%jac(2) = one
jac%jac(3) = one / (one - xi_plus)**2
jac%jac(4) = one / (one - xi_minus)**2
end associate
contains
subroutine get_boost_parameters (p, beta_gamma, beta_vec)
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(out) :: beta_gamma
type(vector3_t), intent(out) :: beta_vec
beta_vec = (p(1)%p(1:3) + p(2)%p(1:3)) / (p(1)%p(0) + p(2)%p(0))
beta_gamma = beta_vec**1 / sqrt (one - beta_vec**2)
beta_vec = beta_vec / beta_vec**1
end subroutine get_boost_parameters
function create_longitudinal_boost (beta_gamma, beta_vec, inverse) result (lambda)
real(default), intent(in) :: beta_gamma
type(vector3_t), intent(in) :: beta_vec
logical, intent(in) :: inverse
type(lorentz_transformation_t) :: lambda
if (inverse) then
lambda = boost (beta_gamma, beta_vec)
else
lambda = boost (-beta_gamma, beta_vec)
end if
end function create_longitudinal_boost
function create_transversal_boost (p_rad, xi, sqrts_real) result (lambda)
type(vector4_t), intent(in) :: p_rad
real(default), intent(in) :: xi, sqrts_real
type(lorentz_transformation_t) :: lambda
type(vector3_t) :: vec_transverse
real(default) :: pt2, beta, beta_gamma
pt2 = transverse_part (p_rad)**2
beta = one / sqrt (one + sqrts_real**2 * (one - xi) / pt2)
beta_gamma = beta / sqrt (one - beta**2)
vec_transverse%p(1:2) = p_rad%p(1:2)
vec_transverse%p(3) = zero
vec_transverse = normalize (vec_transverse)
lambda = boost (-beta_gamma, vec_transverse)
end function create_transversal_boost
end subroutine phs_fks_generator_generate_isr
@ %def phs_fks_generator_generate_isr
@
<<phs fks: phs fks generator: TBP>>=
procedure :: set_sqrts_hat => phs_fks_generator_set_sqrts_hat
<<phs fks: procedures>>=
subroutine phs_fks_generator_set_sqrts_hat (generator, sqrts)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: sqrts
generator%sqrts = sqrts
end subroutine phs_fks_generator_set_sqrts_hat
@ %def phs_fks_generator_set_sqrts_hat
@
<<phs fks: phs fks generator: TBP>>=
procedure :: set_emitters => phs_fks_generator_set_emitters
<<phs fks: procedures>>=
subroutine phs_fks_generator_set_emitters (generator, emitters)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in), dimension(:), allocatable :: emitters
allocate (generator%emitters (size (emitters)))
generator%emitters = emitters
end subroutine phs_fks_generator_set_emitters
@ %def phs_fks_generator_set_emitters
@
<<phs fks: phs fks generator: TBP>>=
procedure :: setup_masses => phs_fks_generator_setup_masses
<<phs fks: procedures>>=
subroutine phs_fks_generator_setup_masses (generator, n_tot)
class (phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: n_tot
if (.not. allocated (generator%m2)) then
allocate (generator%is_massive (n_tot))
allocate (generator%m2 (n_tot))
generator%is_massive = .false.
generator%m2 = zero
end if
end subroutine phs_fks_generator_setup_masses
@ %def phs_fks_generator_setup_masses
@
<<phs fks: phs fks generator: TBP>>=
procedure :: set_xi_and_y_bounds => phs_fks_generator_set_xi_and_y_bounds
<<phs fks: procedures>>=
subroutine phs_fks_generator_set_xi_and_y_bounds (generator, xi_min, y_max)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: xi_min, y_max
generator%xi_min = xi_min
generator%y_max = y_max
end subroutine phs_fks_generator_set_xi_and_y_bounds
@ %def phs_fks_generator_set_xi_and_y_bounds
@
<<phs fks: phs fks generator: TBP>>=
procedure :: set_isr_kinematics => phs_fks_generator_set_isr_kinematics
<<phs fks: procedures>>=
subroutine phs_fks_generator_set_isr_kinematics (generator, p)
class(phs_fks_generator_t), intent(inout) :: generator
type(vector4_t), dimension(2), intent(in) :: p
generator%isr_kinematics%x = p%p(0) / generator%isr_kinematics%beam_energy
end subroutine phs_fks_generator_set_isr_kinematics
@ %def phs_fks_generator_set_isr_kinematics
@
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_radiation_variables => &
phs_fks_generator_generate_radiation_variables
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_radiation_variables &
(generator, r_in, p_born, phs_identifiers, threshold)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in), dimension(:) :: r_in
type(vector4_t), intent(in), dimension(:) :: p_born
type(phs_identifier_t), intent(in), dimension(:) :: phs_identifiers
logical, intent(in), optional :: threshold
associate (rad_var => generator%real_kinematics)
rad_var%phi = r_in (I_PHI) * twopi
select case (generator%mode)
case (GEN_REAL_PHASE_SPACE)
rad_var%jac_rand = twopi
call generator%compute_y_real_phs (r_in(I_Y), p_born, phs_identifiers, &
rad_var%jac_rand, rad_var%y, threshold)
case (GEN_SOFT_MISMATCH)
rad_var%jac_mismatch = twopi
call generator%compute_y_mismatch (r_in(I_Y), rad_var%jac_mismatch, &
rad_var%y_mismatch, rad_var%y_soft)
case default
call generator%compute_y_test (rad_var%y)
end select
call generator%compute_xi_tilde (r_in(I_XI))
call generator%set_masses (p_born, phs_identifiers)
end associate
end subroutine phs_fks_generator_generate_radiation_variables
@ %def phs_fks_generator_generate_radiation_variables
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_xi_ref_momenta => phs_fks_generator_compute_xi_ref_momenta
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_xi_ref_momenta &
(generator, p_born, resonance_contributors)
class(phs_fks_generator_t), intent(inout) :: generator
type(vector4_t), intent(in), dimension(:) :: p_born
type(resonance_contributors_t), intent(in), dimension(:), optional &
:: resonance_contributors
integer :: i_con, n_contributors
if (present (resonance_contributors)) then
n_contributors = size (resonance_contributors)
if (.not. allocated (generator%resonance_contributors)) &
allocate (generator%resonance_contributors (n_contributors))
do i_con = 1, n_contributors
generator%real_kinematics%xi_ref_momenta(i_con) = &
get_resonance_momentum (p_born, resonance_contributors(i_con)%c)
generator%resonance_contributors(i_con) = resonance_contributors(i_con)
end do
else
generator%real_kinematics%xi_ref_momenta(1) = sum (p_born(1:generator%n_in))
end if
end subroutine phs_fks_generator_compute_xi_ref_momenta
@ %def phs_fks_generator_compute_xi_ref_momenta
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_xi_ref_momenta_threshold &
=> phs_fks_generator_compute_xi_ref_momenta_threshold
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_xi_ref_momenta_threshold (generator, p_born)
class(phs_fks_generator_t), intent(inout) :: generator
type(vector4_t), intent(in), dimension(:) :: p_born
generator%real_kinematics%xi_ref_momenta(1) = p_born(THR_POS_WP) + p_born(THR_POS_B)
generator%real_kinematics%xi_ref_momenta(2) = p_born(THR_POS_WM) + p_born(THR_POS_BBAR)
end subroutine phs_fks_generator_compute_xi_ref_momenta_threshold
@ %def phs_fks_generator_compute_xi_ref_momenta
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_cms_energy => phs_fks_generator_compute_cms_energy
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_cms_energy (generator, p_born)
class(phs_fks_generator_t), intent(inout) :: generator
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t) :: p_sum
p_sum = sum (p_born (1 : generator%n_in))
generator%real_kinematics%cms_energy2 = p_sum**2
end subroutine phs_fks_generator_compute_cms_energy
@ %def phs_fks_generator_compute_cms_energy
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_xi_max => phs_fks_generator_compute_xi_max
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_xi_max (generator, emitter, &
i_phs, p, xi_max, i_con, y_in)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: i_phs, emitter
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(out) :: xi_max
integer, intent(in), optional :: i_con
real(default), intent(in), optional :: y_in
real(default) :: q0
type(vector4_t), dimension(:), allocatable :: pp, pp_decay
type(vector4_t) :: p_res
type(lorentz_transformation_t) :: L_to_resonance
real(default) :: y
if (.not. any (generator%emitters == emitter)) return
allocate (pp (size (p)))
associate (rad_var => generator%real_kinematics)
if (present (i_con)) then
q0 = rad_var%xi_ref_momenta(i_con)**1
else
q0 = energy (sum (p(1:generator%n_in)))
end if
if (present (y_in)) then
y = y_in
else
y = rad_var%y(i_phs)
end if
if (present (i_con)) then
p_res = rad_var%xi_ref_momenta(i_con)
L_to_resonance = inverse (boost (p_res, q0))
pp = L_to_resonance * p
else
pp = p
end if
if (emitter <= generator%n_in) then
select case (generator%isr_kinematics%isr_mode)
case (SQRTS_FIXED)
if (generator%n_in > 1) then
allocate (pp_decay (size (pp) - 1))
else
allocate (pp_decay (size (pp)))
end if
pp_decay (1) = sum (pp(1:generator%n_in))
pp_decay (2 : ) = pp (generator%n_in + 1 : )
xi_max = get_xi_max_isr_decay (pp_decay)
deallocate (pp_decay)
case (SQRTS_VAR)
xi_max = get_xi_max_isr (generator%isr_kinematics%x, y)
end select
else
if (generator%is_massive(emitter)) then
xi_max = get_xi_max_fsr (pp, q0, emitter, generator%m2(emitter), y)
else
xi_max = get_xi_max_fsr (pp, q0, emitter)
end if
end if
deallocate (pp)
end associate
end subroutine phs_fks_generator_compute_xi_max
@ %def phs_fks_generator_compute_xi_max
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_xi_max_isr_factorized &
=> phs_fks_generator_compute_xi_max_isr_factorized
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_xi_max_isr_factorized &
(generator, i_phs, p)
class(phs_fks_generator_t), intent(inout) :: generator
integer, intent(in) :: i_phs
type(vector4_t), intent(in), dimension(:) :: p
generator%real_kinematics%xi_max(i_phs) = get_xi_max_isr_decay (p)
end subroutine phs_fks_generator_compute_xi_max_isr_factorized
@ %def phs_fks_generator_compute_xi_max_isr_factorized
@
<<phs fks: phs fks generator: TBP>>=
procedure :: set_masses => phs_fks_generator_set_masses
<<phs fks: procedures>>=
subroutine phs_fks_generator_set_masses (generator, p, phs_identifiers)
class(phs_fks_generator_t), intent(inout) :: generator
type(phs_identifier_t), intent(in), dimension(:) :: phs_identifiers
type(vector4_t), intent(in), dimension(:) :: p
integer :: emitter, i_phs
do i_phs = 1, size (phs_identifiers)
emitter = phs_identifiers(i_phs)%emitter
if (any (generator%emitters == emitter) .and. emitter > 0) then
if (generator%is_massive (emitter) .and. emitter > generator%n_in) &
generator%m2(emitter) = p(emitter)**2
end if
end do
end subroutine phs_fks_generator_set_masses
@ %def phs_fhs_generator_set_masses
@
<<phs fks: public>>=
public :: compute_y_from_emitter
<<phs fks: procedures>>=
subroutine compute_y_from_emitter (r_y, p, n_in, emitter, massive, &
y_max, jac_rand, y, contributors, threshold)
real(default), intent(in) :: r_y
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: n_in
integer, intent(in) :: emitter
logical, intent(in) :: massive
real(default), intent(in) :: y_max
real(default), intent(inout) :: jac_rand
real(default), intent(out) :: y
integer, intent(in), dimension(:), allocatable, optional :: contributors
logical, intent(in), optional :: threshold
logical :: thr, resonance
type(vector4_t) :: p_res, p_em
real(default) :: q0
type(lorentz_transformation_t) :: boost_to_resonance
integer :: i
real(default) :: beta, one_m_beta, one_p_beta
thr = .false.; if (present (threshold)) thr = threshold
p_res = vector4_null
if (present (contributors)) then
resonance = allocated (contributors)
else
resonance = .false.
end if
if (massive) then
if (resonance) then
do i = 1, size (contributors)
p_res = p_res + p(contributors(i))
end do
else if (thr) then
p_res = p(ass_boson(thr_leg(emitter))) + p(ass_quark(thr_leg(emitter)))
else
p_res = sum (p(1:n_in))
end if
q0 = p_res**1
boost_to_resonance = inverse (boost (p_res, q0))
p_em = boost_to_resonance * p(emitter)
beta = beta_emitter (q0, p_em)
one_m_beta = one - beta
one_p_beta = one + beta
y = one / beta * (one - one_p_beta * &
exp ( - r_y * log(one_p_beta / one_m_beta)))
jac_rand = jac_rand * &
(one - beta * y) * log(one_p_beta / one_m_beta) / beta
else
y = (one - two * r_y) * y_max
jac_rand = jac_rand * 3 * (one - y**2)
y = 1.5_default * (y - y**3 / 3)
end if
end subroutine compute_y_from_emitter
@ %def compute_y_from_emitter
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_y_real_phs => phs_fks_generator_compute_y_real_phs
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_y_real_phs (generator, r_y, p, phs_identifiers, &
jac_rand, y, threshold)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: r_y
type(vector4_t), intent(in), dimension(:) :: p
type(phs_identifier_t), intent(in), dimension(:) :: phs_identifiers
real(default), intent(inout), dimension(:) :: jac_rand
real(default), intent(out), dimension(:) :: y
logical, intent(in), optional :: threshold
real(default) :: beta, one_p_beta, one_m_beta
type(lorentz_transformation_t) :: boost_to_resonance
real(default) :: q0
type(vector4_t) :: p_res, p_em
integer :: i, i_phs, emitter
logical :: thr
logical :: construct_massive_fsr
construct_massive_fsr = .false.
thr = .false.; if (present (threshold)) thr = threshold
do i_phs = 1, size (phs_identifiers)
emitter = phs_identifiers(i_phs)%emitter
!!! We need this additional check because of decay phase spaces
!!! t -> bW has a massive emitter at position 1, which should
!!! not be treated here.
construct_massive_fsr = emitter > generator%n_in
if (construct_massive_fsr) construct_massive_fsr = &
construct_massive_fsr .and. generator%is_massive (emitter)
call compute_y_from_emitter (r_y, p, generator%n_in, emitter, construct_massive_fsr, &
generator%y_max, jac_rand(i_phs), y(i_phs), &
phs_identifiers(i_phs)%contributors, threshold)
end do
end subroutine phs_fks_generator_compute_y_real_phs
@ %def phs_fks_generator_compute_y_real_phs
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_y_mismatch => phs_fks_generator_compute_y_mismatch
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_y_mismatch (generator, r_y, jac_rand, y, y_soft)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: r_y
real(default), intent(inout) :: jac_rand
real(default), intent(out) :: y
real(default), intent(out), dimension(:) :: y_soft
y = (one - two * r_y) * generator%y_max
jac_rand = jac_rand * 3 * (one - y**2)
y = 1.5_default * (y - y**3 / 3)
y_soft = y
end subroutine phs_fks_generator_compute_y_mismatch
@ %def phs_fks_generator_compute_y_mismatch
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_y_test => phs_fks_generator_compute_y_test
<<phs fks: procedures>>=
subroutine phs_fks_generator_compute_y_test (generator, y)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(out), dimension(:):: y
select case (generator%mode)
case (GEN_SOFT_LIMIT_TEST)
y = y_test_soft
case (GEN_COLL_LIMIT_TEST)
y = y_test_coll
case (GEN_ANTI_COLL_LIMIT_TEST)
y = - y_test_coll
case (GEN_SOFT_COLL_LIMIT_TEST)
y = y_test_coll
case (GEN_SOFT_ANTI_COLL_LIMIT_TEST)
y = - y_test_coll
end select
end subroutine phs_fks_generator_compute_y_test
@ %def phs_fks_generator_compute_y_test
@
<<phs fks: public>>=
public :: beta_emitter
<<phs fks: procedures>>=
pure function beta_emitter (q0, p) result (beta)
real(default), intent(in) :: q0
type(vector4_t), intent(in) :: p
real(default) :: beta
real(default) :: m2, mrec2, k0_max
m2 = p**2
mrec2 = (q0 - p%p(0))**2 - p%p(1)**2 - p%p(2)**2 - p%p(3)**2
k0_max = (q0**2 - mrec2 + m2) / (two * q0)
beta = sqrt(one - m2 / k0_max**2)
end function beta_emitter
@ %def beta_emitter
@
<<phs fks: phs fks generator: TBP>>=
procedure :: compute_xi_tilde => phs_fks_generator_compute_xi_tilde
<<phs fks: procedures>>=
pure subroutine phs_fks_generator_compute_xi_tilde (generator, r)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: r
real(default) :: deno
associate (rad_var => generator%real_kinematics)
select case (generator%mode)
case (GEN_REAL_PHASE_SPACE)
if (generator%singular_jacobian) then
rad_var%xi_tilde = (one - generator%xi_min) - (one - r)**2 * &
(one - two * generator%xi_min)
rad_var%jac_rand = rad_var%jac_rand * two * (one - r) * &
(one - two * generator%xi_min)
else
rad_var%xi_tilde = generator%xi_min + r * (one - generator%xi_min)
rad_var%jac_rand = rad_var%jac_rand * (one - generator%xi_min)
end if
case (GEN_SOFT_MISMATCH)
deno = one - r
if (deno < tiny_13) deno = tiny_13
rad_var%xi_mismatch = generator%xi_min + r / deno
rad_var%jac_mismatch = rad_var%jac_mismatch / deno**2
case (GEN_SOFT_LIMIT_TEST)
rad_var%xi_tilde = r * two * xi_tilde_test_soft
rad_var%jac_rand = two * xi_tilde_test_soft
case (GEN_COLL_LIMIT_TEST)
rad_var%xi_tilde = xi_tilde_test_coll
rad_var%jac_rand = xi_tilde_test_coll
case (GEN_ANTI_COLL_LIMIT_TEST)
rad_var%xi_tilde = xi_tilde_test_coll
rad_var%jac_rand = xi_tilde_test_coll
case (GEN_SOFT_COLL_LIMIT_TEST)
rad_var%xi_tilde = r * two * xi_tilde_test_soft
rad_var%jac_rand = two * xi_tilde_test_soft
case (GEN_SOFT_ANTI_COLL_LIMIT_TEST)
rad_var%xi_tilde = r * two * xi_tilde_test_soft
rad_var%jac_rand = two * xi_tilde_test_soft
end select
end associate
end subroutine phs_fks_generator_compute_xi_tilde
@ %def phs_fks_generator_compute_xi_tilde
@
<<phs fks: phs fks generator: TBP>>=
procedure :: prepare_generation => phs_fks_generator_prepare_generation
<<phs fks: procedures>>=
subroutine phs_fks_generator_prepare_generation (generator, r_in, i_phs, &
emitter, p_born, phs_identifiers, contributors, i_con)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), dimension(3), intent(in) :: r_in
integer, intent(in) :: i_phs, emitter
type(vector4_t), intent(in), dimension(:) :: p_born
type(phs_identifier_t), intent(in), dimension(:) :: phs_identifiers
type(resonance_contributors_t), intent(in), dimension(:), optional :: contributors
integer, intent(in), optional :: i_con
call generator%generate_radiation_variables (r_in, p_born, phs_identifiers)
call generator%compute_xi_ref_momenta (p_born, contributors)
call generator%compute_xi_max (emitter, i_phs, p_born, &
generator%real_kinematics%xi_max(i_phs), i_con = i_con)
end subroutine phs_fks_generator_prepare_generation
@ %def phs_fks_generator_prepare_generation
@ Get [[xi]] and [[y]] from an external routine (e.g. [[powheg]]) and
generate an FSR phase space. Note that the flag [[supply\_xi\_max]] is
set to [[.false.]] because it is assumed that the upper bound on [[xi]]
has already been taken into account during its generation.
<<phs fks: phs fks generator: TBP>>=
procedure :: generate_fsr_from_xi_and_y => &
phs_fks_generator_generate_fsr_from_xi_and_y
<<phs fks: procedures>>=
subroutine phs_fks_generator_generate_fsr_from_xi_and_y (generator, xi, y, &
phi, emitter, i_phs, p_born, p_real)
class(phs_fks_generator_t), intent(inout) :: generator
real(default), intent(in) :: xi, y, phi
integer, intent(in) :: emitter, i_phs
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(inout), dimension(:) :: p_real
associate (rad_var => generator%real_kinematics)
rad_var%supply_xi_max = .false.
rad_var%xi_tilde = xi
rad_var%y(i_phs) = y
rad_var%phi = phi
end associate
call generator%set_sqrts_hat (p_born(1)%p(0) + p_born(2)%p(0))
call generator%generate_fsr (emitter, i_phs, p_born, p_real)
end subroutine phs_fks_generator_generate_fsr_from_xi_and_y
@ %def phs_fks_generator_generate_fsr_from_xi_and_y
@
<<phs fks: phs fks generator: TBP>>=
procedure :: get_radiation_variables => &
phs_fks_generator_get_radiation_variables
<<phs fks: procedures>>=
pure subroutine phs_fks_generator_get_radiation_variables (generator, &
i_phs, xi, y, phi)
class(phs_fks_generator_t), intent(in) :: generator
integer, intent(in) :: i_phs
real(default), intent(out) :: xi, y
real(default), intent(out), optional :: phi
associate (rad_var => generator%real_kinematics)
xi = rad_var%xi_max(i_phs) * rad_var%xi_tilde
y = rad_var%y(i_phs)
if (present (phi)) phi = rad_var%phi
end associate
end subroutine phs_fks_generator_get_radiation_variables
@ %def phs_fks_generator_get_radiation_variables
@
<<phs fks: phs fks generator: TBP>>=
procedure :: write => phs_fks_generator_write
<<phs fks: procedures>>=
subroutine phs_fks_generator_write (generator, unit)
class(phs_fks_generator_t), intent(in) :: generator
integer, intent(in), optional :: unit
integer :: u
type(string_t) :: massive_phsp
u = given_output_unit (unit); if (u < 0) return
if (generator%massive_phsp) then
massive_phsp = " massive "
else
massive_phsp = " massless "
end if
write (u, "(A)") char ("This is a generator for a" &
// massive_phsp // "phase space")
if (associated (generator%real_kinematics)) then
call generator%real_kinematics%write ()
else
write (u, "(A)") "Warning: There are no real " // &
"kinematics associated with this generator"
end if
call write_separator (u)
write (u, "(A," // FMT_17 // ",1X)") "sqrts: ", generator%sqrts
write (u, "(A," // FMT_17 // ",1X)") "E_gluon: ", generator%E_gluon
write (u, "(A," // FMT_17 // ",1X)") "mrec2: ", generator%mrec2
end subroutine phs_fks_generator_write
@ %def phs_fks_generator_write
@
<<phs fks: phs fks: TBP>>=
procedure :: compute_isr_kinematics => phs_fks_compute_isr_kinematics
<<phs fks: procedures>>=
subroutine phs_fks_compute_isr_kinematics (phs, r)
class(phs_fks_t), intent(inout) :: phs
real(default), intent(in) :: r
if (.not. phs%config%cm_frame) then
call phs%generator%compute_isr_kinematics (r, phs%lt_cm_to_lab * phs%phs_wood_t%p)
else
call phs%generator%compute_isr_kinematics (r, phs%phs_wood_t%p)
end if
end subroutine phs_fks_compute_isr_kinematics
@ %def phs_fks_compute_isr_kinematics
@
<<phs fks: phs fks: TBP>>=
procedure :: final => phs_fks_final
<<phs fks: procedures>>=
subroutine phs_fks_final (object)
class(phs_fks_t), intent(inout) :: object
call phs_forest_final (object%forest)
call object%generator%final ()
end subroutine phs_fks_final
@ %def phs_fks_final
@
<<phs fks: public>>=
public :: get_filtered_resonance_histories
<<phs fks: procedures>>=
subroutine filter_particles_from_resonances (res_hist, exclusion_list, &
model, res_hist_filtered)
type(resonance_history_t), intent(in), dimension(:) :: res_hist
type(string_t), intent(in), dimension(:) :: exclusion_list
type(model_t), intent(in) :: model
type(resonance_history_t), intent(out), dimension(:), allocatable :: res_hist_filtered
integer :: i_hist, i_flv, i_new, n_orig
logical, dimension(size (res_hist)) :: to_filter
type(flavor_t) :: flv
to_filter = .false.
n_orig = size (res_hist)
do i_flv = 1, size (exclusion_list)
call flv%init (exclusion_list (i_flv), model)
do i_hist = 1, size (res_hist)
if (res_hist(i_hist)%has_flavor (flv)) to_filter (i_hist) = .true.
end do
end do
allocate (res_hist_filtered (n_orig - count (to_filter)))
i_new = 1
do i_hist = 1, size (res_hist)
if (.not. to_filter (i_hist)) then
res_hist_filtered (i_new) = res_hist (i_hist)
i_new = i_new + 1
end if
end do
end subroutine filter_particles_from_resonances
@ %def filter_particles_from_resonances
@
<<phs fks: procedures>>=
subroutine clean_resonance_histories (res_hist, n_in, flv, res_hist_clean, success)
type(resonance_history_t), intent(in), dimension(:) :: res_hist
integer, intent(in) :: n_in
integer, intent(in), dimension(:) :: flv
type(resonance_history_t), intent(out), dimension(:), allocatable :: res_hist_clean
logical, intent(out) :: success
integer :: i_hist
type(resonance_history_t), dimension(:), allocatable :: res_hist_colored, res_hist_contracted
if (debug_on) call msg_debug (D_SUBTRACTION, "resonance_mapping_init")
if (debug_active (D_SUBTRACTION)) then
call msg_debug (D_SUBTRACTION, "Original resonances:")
do i_hist = 1, size(res_hist)
call res_hist(i_hist)%write ()
end do
end if
call remove_uncolored_resonances ()
call contract_resonances (res_hist_colored, res_hist_contracted)
call remove_subresonances (res_hist_contracted, res_hist_clean)
!!! Here, we are still not sure whether we actually would rather use
!!! call remove_multiple_resonances (res_hist_contracted, res_hist_clean)
if (debug_active (D_SUBTRACTION)) then
call msg_debug (D_SUBTRACTION, "Resonances after removing uncolored and duplicates: ")
do i_hist = 1, size (res_hist_clean)
call res_hist_clean(i_hist)%write ()
end do
end if
if (size (res_hist_clean) == 0) then
call msg_warning ("No resonances found. Proceed in usual FKS mode.")
success = .false.
else
success = .true.
end if
contains
subroutine remove_uncolored_resonances ()
type(resonance_history_t), dimension(:), allocatable :: res_hist_tmp
integer :: n_hist, nleg_out, n_removed
integer :: i_res, i_hist
n_hist = size (res_hist)
nleg_out = size (flv) - n_in
allocate (res_hist_tmp (n_hist))
allocate (res_hist_colored (n_hist))
do i_hist = 1, n_hist
res_hist_tmp(i_hist) = res_hist(i_hist)
call res_hist_tmp(i_hist)%add_offset (n_in)
n_removed = 0
do i_res = 1, res_hist_tmp(i_hist)%n_resonances
associate (resonance => res_hist_tmp(i_hist)%resonances(i_res - n_removed))
if (.not. any (is_colored (flv (resonance%contributors%c))) &
.or. size (resonance%contributors%c) == nleg_out) then
call res_hist_tmp(i_hist)%remove_resonance (i_res - n_removed)
n_removed = n_removed + 1
end if
end associate
end do
if (allocated (res_hist_tmp(i_hist)%resonances)) then
if (any (res_hist_colored == res_hist_tmp(i_hist))) then
cycle
else
do i_res = 1, res_hist_tmp(i_hist)%n_resonances
associate (resonance => res_hist_tmp(i_hist)%resonances(i_res))
call res_hist_colored(i_hist)%add_resonance (resonance)
end associate
end do
end if
end if
end do
end subroutine remove_uncolored_resonances
subroutine contract_resonances (res_history_in, res_history_out)
type(resonance_history_t), intent(in), dimension(:) :: res_history_in
type(resonance_history_t), intent(out), dimension(:), allocatable :: res_history_out
logical, dimension(:), allocatable :: i_non_zero
integer :: n_hist_non_zero, n_hist
integer :: i_hist_new
n_hist = size (res_history_in); n_hist_non_zero = 0
allocate (i_non_zero (n_hist))
i_non_zero = .false.
do i_hist = 1, n_hist
if (res_history_in(i_hist)%n_resonances /= 0) then
n_hist_non_zero = n_hist_non_zero + 1
i_non_zero(i_hist) = .true.
end if
end do
allocate (res_history_out (n_hist_non_zero))
i_hist_new = 1
do i_hist = 1, n_hist
if (i_non_zero (i_hist)) then
res_history_out (i_hist_new) = res_history_in (i_hist)
i_hist_new = i_hist_new + 1
end if
end do
end subroutine contract_resonances
subroutine remove_subresonances (res_history_in, res_history_out)
type(resonance_history_t), intent(in), dimension(:) :: res_history_in
type(resonance_history_t), intent(out), dimension(:), allocatable :: res_history_out
logical, dimension(:), allocatable :: i_non_sub_res
integer :: n_hist, n_hist_non_sub_res
integer :: i_hist1, i_hist2
logical :: is_not_subres
n_hist = size (res_history_in); n_hist_non_sub_res = 0
allocate (i_non_sub_res (n_hist)); i_non_sub_res = .false.
do i_hist1 = 1, n_hist
is_not_subres = .true.
do i_hist2 = 1, n_hist
if (i_hist1 == i_hist2) cycle
is_not_subres = is_not_subres .and. &
.not.(res_history_in(i_hist2) .contains. res_history_in(i_hist1))
end do
if (is_not_subres) then
n_hist_non_sub_res = n_hist_non_sub_res + 1
i_non_sub_res (i_hist1) = .true.
end if
end do
allocate (res_history_out (n_hist_non_sub_res))
i_hist2 = 1
do i_hist1 = 1, n_hist
if (i_non_sub_res (i_hist1)) then
res_history_out (i_hist2) = res_history_in (i_hist1)
i_hist2 = i_hist2 + 1
end if
end do
end subroutine remove_subresonances
subroutine remove_multiple_resonances (res_history_in, res_history_out)
type(resonance_history_t), intent(in), dimension(:) :: res_history_in
type(resonance_history_t), intent(out), dimension(:), allocatable :: res_history_out
integer :: n_hist, n_hist_single
logical, dimension(:), allocatable :: i_hist_single
integer :: i_hist, j
n_hist = size (res_history_in)
n_hist_single = 0
allocate (i_hist_single (n_hist)); i_hist_single = .false.
do i_hist = 1, n_hist
if (res_history_in(i_hist)%n_resonances == 1) then
n_hist_single = n_hist_single + 1
i_hist_single(i_hist) = .true.
end if
end do
allocate (res_history_out (n_hist_single))
j = 1
do i_hist = 1, n_hist
if (i_hist_single(i_hist)) then
res_history_out(j) = res_history_in(i_hist)
j = j + 1
end if
end do
end subroutine remove_multiple_resonances
end subroutine clean_resonance_histories
@ %def clean_resonance_histories
@
<<phs fks: procedures>>=
subroutine get_filtered_resonance_histories (phs_config, n_in, flv_state, model, &
excluded_resonances, resonance_histories_filtered, success)
type(phs_fks_config_t), intent(inout) :: phs_config
integer, intent(in) :: n_in
integer, intent(in), dimension(:,:), allocatable :: flv_state
type(model_t), intent(in) :: model
type(string_t), intent(in), dimension(:), allocatable :: excluded_resonances
type(resonance_history_t), intent(out), dimension(:), &
allocatable :: resonance_histories_filtered
logical, intent(out) :: success
type(resonance_history_t), dimension(:), allocatable :: resonance_histories
type(resonance_history_t), dimension(:), allocatable :: &
resonance_histories_clean!, resonance_histories_filtered
allocate (resonance_histories (size (phs_config%get_resonance_histories ())))
resonance_histories = phs_config%get_resonance_histories ()
call clean_resonance_histories (resonance_histories, &
n_in, flv_state (:,1), resonance_histories_clean, success)
if (success .and. allocated (excluded_resonances)) then
call filter_particles_from_resonances (resonance_histories_clean, &
excluded_resonances, model, resonance_histories_filtered)
else
allocate (resonance_histories_filtered (size (resonance_histories_clean)))
resonance_histories_filtered = resonance_histories_clean
end if
end subroutine get_filtered_resonance_histories
@ %def get_filtered_resonance_histories
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Unit tests}
Test module for FKS phase space, followed by the corresponding implementation module.
<<[[phs_fks_ut.f90]]>>=
<<File header>>
module phs_fks_ut
use unit_tests
use phs_fks_uti
<<Standard module head>>
<<phs fks: public test>>
contains
<<phs fks: test driver>>
end module phs_fks_ut
@ %def phs_fks_ut
@
<<[[phs_fks_uti.f90]]>>=
<<File header>>
module phs_fks_uti
<<Use kinds>>
use format_utils, only: write_separator, pac_fmt
use format_defs, only: FMT_15, FMT_19
use numeric_utils, only: nearly_equal
use constants, only: tiny_07, zero, one, two
use lorentz
use physics_defs, only: THR_POS_B, THR_POS_BBAR, THR_POS_WP, THR_POS_WM, THR_POS_GLUON
use physics_defs, only: thr_leg
use resonances, only: resonance_contributors_t
use phs_fks
<<Standard module head>>
<<phs fks: test declarations>>
contains
<<phs fks: tests>>
end module phs_fks_uti
@ %def phs_fks_uti
@ API: driver for the unit tests below.
<<phs fks: public test>>=
public :: phs_fks_generator_test
<<phs fks: test driver>>=
subroutine phs_fks_generator_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
call test(phs_fks_generator_1, "phs_fks_generator_1", &
"Test the generation of FKS phase spaces", u, results)
call test(phs_fks_generator_2, "phs_fks_generator_2", &
"Test the generation of an ISR FKS phase space", u, results)
call test(phs_fks_generator_3, "phs_fks_generator_3", &
"Test the generation of a real phase space for decays", &
u, results)
call test(phs_fks_generator_4, "phs_fks_generator_4", &
"Test the generation of an FSR phase space with "&
&"conserved invariant resonance masses", u, results)
call test(phs_fks_generator_5, "phs_fks_generator_5", &
"Test on-shell projection of a Born phase space and the generation"&
&" of a real phase-space from that", u, results)
call test(phs_fks_generator_6, "phs_fks_generator_6", &
"Test the generation of a real phase space for 1 -> 3 decays", &
u, results)
call test(phs_fks_generator_7, "phs_fks_generator_7", &
"Test the generation of an ISR FKS phase space for fixed beam energy", &
u, results)
end subroutine phs_fks_generator_test
@ %def phs_fks_generator_test
@
<<phs fks: test declarations>>=
public :: phs_fks_generator_1
<<phs fks: tests>>=
subroutine phs_fks_generator_1 (u)
integer, intent(in) :: u
type(phs_fks_generator_t) :: generator
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: p_real
integer :: emitter, i_phs
real(default) :: x1, x2, x3
real(default), parameter :: sqrts = 250.0_default
type(phs_identifier_t), dimension(2) :: phs_identifiers
write (u, "(A)") "* Test output: phs_fks_generator_1"
write (u, "(A)") "* Purpose: Create massless fsr phase space"
write (u, "(A)")
allocate (p_born (4))
p_born(1)%p(0) = 125.0_default
p_born(1)%p(1:2) = 0.0_default
p_born(1)%p(3) = 125.0_default
p_born(2)%p(0) = 125.0_default
p_born(2)%p(1:2) = 0.0_default
p_born(2)%p(3) = -125.0_default
p_born(3)%p(0) = 125.0_default
p_born(3)%p(1) = -39.5618_default
p_born(3)%p(2) = -20.0791_default
p_born(3)%p(3) = -114.6957_default
p_born(4)%p(0) = 125.0_default
p_born(4)%p(1:3) = -p_born(3)%p(1:3)
allocate (generator%isr_kinematics)
generator%n_in = 2
generator%isr_kinematics%isr_mode = SQRTS_FIXED
call generator%set_sqrts_hat (sqrts)
write (u, "(A)") "* Use four-particle phase space containing: "
call vector4_write_set (p_born, u, testflag = .true., ultra = .true.)
write (u, "(A)") "***********************"
write (u, "(A)")
x1 = 0.5_default; x2 = 0.25_default; x3 = 0.75_default
write (u, "(A)" ) "* Use random numbers: "
write (u, "(A,F3.2,1X,A,F3.2,1X,A,F3.2)") &
"x1: ", x1, "x2: ", x2, "x3: ", x3
allocate (generator%real_kinematics)
call generator%real_kinematics%init (4, 2, 2, 1)
allocate (generator%emitters (2))
generator%emitters(1) = 3; generator%emitters(2) = 4
allocate (generator%m2 (4))
generator%m2 = zero
allocate (generator%is_massive (4))
generator%is_massive(1:2) = .false.
generator%is_massive(3:4) = .true.
phs_identifiers(1)%emitter = 3
phs_identifiers(2)%emitter = 4
call generator%compute_xi_ref_momenta (p_born)
call generator%generate_radiation_variables ([x1,x2,x3], p_born, phs_identifiers)
do i_phs = 1, 2
emitter = phs_identifiers(i_phs)%emitter
call generator%compute_xi_max (emitter, i_phs, p_born, &
generator%real_kinematics%xi_max(i_phs))
end do
write (u, "(A)") &
"* With these, the following radiation variables have been produced:"
associate (rad_var => generator%real_kinematics)
write (u, "(A,F3.2)") "xi_tilde: ", rad_var%xi_tilde
write (u, "(A,F3.2)") "y: " , rad_var%y(1)
write (u, "(A,F3.2)") "phi: ", rad_var%phi
end associate
call write_separator (u)
write (u, "(A)") "Produce real momenta: "
i_phs = 1; emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
allocate (p_real (5))
call generator%generate_fsr (emitter, i_phs, p_born, p_real)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_fks_generator_1"
end subroutine phs_fks_generator_1
@ %def phs_fks_generator_1
@
<<phs fks: test declarations>>=
public :: phs_fks_generator_2
<<phs fks: tests>>=
subroutine phs_fks_generator_2 (u)
integer, intent(in) :: u
type(phs_fks_generator_t) :: generator
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: p_real
integer :: emitter, i_phs
real(default) :: x1, x2, x3
real(default), parameter :: sqrts_hadronic = 250.0_default
type(phs_identifier_t), dimension(2) :: phs_identifiers
write (u, "(A)") "* Test output: phs_fks_generator_2"
write (u, "(A)") "* Purpose: Create massless ISR phase space"
write (u, "(A)")
allocate (p_born (4))
p_born(1)%p(0) = 114.661_default
p_born(1)%p(1:2) = 0.0_default
p_born(1)%p(3) = 114.661_default
p_born(2)%p(0) = 121.784_default
p_born(2)%p(1:2) = 0.0_default
p_born(2)%p(3) = -121.784_default
p_born(3)%p(0) = 115.148_default
p_born(3)%p(1) = -46.250_default
p_born(3)%p(2) = -37.711_default
p_born(3)%p(3) = 98.478_default
p_born(4)%p(0) = 121.296_default
p_born(4)%p(1:2) = -p_born(3)%p(1:2)
p_born(4)%p(3) = -105.601_default
phs_identifiers(1)%emitter = 1
phs_identifiers(2)%emitter = 2
allocate (generator%emitters (2))
allocate (generator%isr_kinematics)
generator%emitters(1) = 1; generator%emitters(2) = 2
generator%sqrts = sqrts_hadronic
generator%isr_kinematics%beam_energy = sqrts_hadronic / two
call generator%set_sqrts_hat (sqrts_hadronic)
call generator%set_isr_kinematics (p_born)
generator%n_in = 2
generator%isr_kinematics%isr_mode = SQRTS_VAR
write (u, "(A)") "* Use four-particle phase space containing: "
call vector4_write_set (p_born, u, testflag = .true., ultra = .true.)
write (u, "(A)") "***********************"
write (u, "(A)")
x1=0.5_default; x2=0.25_default; x3=0.65_default
write (u, "(A)" ) "* Use random numbers: "
write (u, "(A,F3.2,1X,A,F3.2,1X,A,F3.2)") &
"x1: ", x1, "x2: ", x2, "x3: ", x3
allocate (generator%real_kinematics)
call generator%real_kinematics%init (4, 2, 2, 1)
call generator%real_kinematics%p_born_lab%set_momenta (1, p_born)
allocate (generator%m2 (2))
generator%m2(1) = 0._default; generator%m2(2) = 0._default
allocate (generator%is_massive (4))
generator%is_massive = .false.
call generator%generate_radiation_variables ([x1,x2,x3], p_born, phs_identifiers)
call generator%compute_xi_ref_momenta (p_born)
do i_phs = 1, 2
emitter = phs_identifiers(i_phs)%emitter
call generator%compute_xi_max (emitter, i_phs, p_born, &
generator%real_kinematics%xi_max(i_phs))
end do
write (u, "(A)") &
"* With these, the following radiation variables have been produced:"
associate (rad_var => generator%real_kinematics)
write (u, "(A,F3.2)") "xi_tilde: ", rad_var%xi_tilde
write (u, "(A,F3.2)") "y: " , rad_var%y(1)
write (u, "(A,F3.2)") "phi: ", rad_var%phi
end associate
write (u, "(A)") "Initial-state momentum fractions: "
associate (xb => generator%isr_kinematics%x)
write (u, "(A,F3.2)") "x_born_plus: ", xb(1)
write (u, "(A,F3.2)") "x_born_minus: ", xb(2)
end associate
call write_separator (u)
write (u, "(A)") "Produce real momenta: "
i_phs = 1; emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
allocate (p_real(5))
call generator%generate_isr (i_phs, p_born, p_real)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_fks_generator_2"
end subroutine phs_fks_generator_2
@ %def phs_fks_generator_2
@
<<phs fks: test declarations>>=
public :: phs_fks_generator_3
<<phs fks: tests>>=
subroutine phs_fks_generator_3 (u)
integer, intent(in) :: u
type(phs_fks_generator_t) :: generator
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: p_real
real(default) :: x1, x2, x3
real(default) :: mB, mW, mT
integer :: i, emitter, i_phs
type(phs_identifier_t), dimension(2) :: phs_identifiers
write (u, "(A)") "* Test output: phs_fks_generator_3"
write (u, "(A)") "* Puropse: Create real phase space for particle decays"
write (u, "(A)")
allocate (p_born(3))
p_born(1)%p(0) = 172._default
p_born(1)%p(1) = 0._default
p_born(1)%p(2) = 0._default
p_born(1)%p(3) = 0._default
p_born(2)%p(0) = 104.72866679_default
p_born(2)%p(1) = 45.028053213_default
p_born(2)%p(2) = 29.450337581_default
p_born(2)%p(3) = -5.910229156_default
p_born(3)%p(0) = 67.271333209_default
p_born(3)%p(1:3) = -p_born(2)%p(1:3)
generator%n_in = 1
allocate (generator%isr_kinematics)
generator%isr_kinematics%isr_mode = SQRTS_FIXED
mB = 4.2_default
mW = 80.376_default
mT = 172._default
generator%sqrts = mT
write (u, "(A)") "* Use three-particle phase space containing: "
call vector4_write_set (p_born, u, testflag = .true., ultra = .true.)
write (u, "(A)") "**********************"
write (u, "(A)")
x1 = 0.5_default; x2 = 0.25_default; x3 = 0.6_default
write (u, "(A)") "* Use random numbers: "
write (u, "(A,F3.2,1X,A,F3.2,A,1X,F3.2)") &
"x1: ", x1, "x2: ", x2, "x3: ", x3
allocate (generator%real_kinematics)
call generator%real_kinematics%init (3, 2, 2, 1)
call generator%real_kinematics%p_born_lab%set_momenta (1, p_born)
allocate (generator%emitters(2))
generator%emitters(1) = 1
generator%emitters(2) = 3
allocate (generator%m2 (3), generator%is_massive(3))
generator%m2(1) = mT**2
generator%m2(2) = mW**2
generator%m2(3) = mB**2
generator%is_massive = .true.
phs_identifiers(1)%emitter = 1
phs_identifiers(2)%emitter = 3
call generator%generate_radiation_variables ([x1,x2,x3], p_born, phs_identifiers)
call generator%compute_xi_ref_momenta (p_born)
do i_phs = 1, 2
emitter = phs_identifiers(i_phs)%emitter
call generator%compute_xi_max (emitter, i_phs, p_born, &
generator%real_kinematics%xi_max(i_phs))
end do
write (u, "(A)") &
"* With these, the following radiation variables have been produced: "
associate (rad_var => generator%real_kinematics)
write (u, "(A,F4.2)") "xi_tilde: ", rad_var%xi_tilde
do i = 1, 2
write (u, "(A,I1,A,F5.2)") "i: ", i, "y: " , rad_var%y(i)
end do
write (u, "(A,F4.2)") "phi: ", rad_var%phi
end associate
call write_separator (u)
write (u, "(A)") "Produce real momenta via initial-state emission: "
i_phs = 1; emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
allocate (p_real (4))
call generator%generate_isr_fixed_beam_energy (i_phs, p_born, p_real)
call pacify (p_real, 1E-6_default)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
call write_separator(u)
write (u, "(A)") "Produce real momenta via final-state emisson: "
i_phs = 2; emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
call generator%generate_fsr (emitter, i_phs, p_born, p_real)
call pacify (p_real, 1E-6_default)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_fks_generator_3"
end subroutine phs_fks_generator_3
@ %def phs_fks_generator_3
@
<<phs fks: test declarations>>=
public :: phs_fks_generator_4
<<phs fks: tests>>=
subroutine phs_fks_generator_4 (u)
integer, intent(in) :: u
type(phs_fks_generator_t) :: generator
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: p_real
integer, dimension(:), allocatable :: emitters
integer, dimension(:,:), allocatable :: resonance_lists
type(resonance_contributors_t), dimension(2) :: alr_contributors
real(default) :: x1, x2, x3
real(default), parameter :: sqrts = 250.0_default
integer, parameter :: nlegborn = 6
integer :: i_phs, i_con, emitter
real(default) :: m_inv_born, m_inv_real
character(len=7) :: fmt
type(phs_identifier_t), dimension(2) :: phs_identifiers
call pac_fmt (fmt, FMT_19, FMT_15, .true.)
write (u, "(A)") "* Test output: phs_fks_generator_4"
write (u, "(A)") "* Purpose: Create FSR phase space with fixed resonances"
write (u, "(A)")
allocate (p_born (nlegborn))
p_born(1)%p(0) = 250._default
p_born(1)%p(1) = 0._default
p_born(1)%p(2) = 0._default
p_born(1)%p(3) = 250._default
p_born(2)%p(0) = 250._default
p_born(2)%p(1) = 0._default
p_born(2)%p(2) = 0._default
p_born(2)%p(3) = -250._default
p_born(3)%p(0) = 145.91184486_default
p_born(3)%p(1) = 50.39727589_default
p_born(3)%p(2) = 86.74156041_default
p_born(3)%p(3) = -69.03608748_default
p_born(4)%p(0) = 208.1064784_default
p_born(4)%p(1) = -44.07610020_default
p_born(4)%p(2) = -186.34264578_default
p_born(4)%p(3) = 13.48038407_default
p_born(5)%p(0) = 26.25614471_default
p_born(5)%p(1) = -25.12258068_default
p_born(5)%p(2) = -1.09540228_default
p_born(5)%p(3) = -6.27703505_default
p_born(6)%p(0) = 119.72553196_default
p_born(6)%p(1) = 18.80140499_default
p_born(6)%p(2) = 100.69648766_default
p_born(6)%p(3) = 61.83273846_default
allocate (generator%isr_kinematics)
generator%n_in = 2
generator%isr_kinematics%isr_mode = SQRTS_FIXED
call generator%set_sqrts_hat (sqrts)
write (u, "(A)") "* Test process: e+ e- -> W+ W- b b~"
write (u, "(A)") "* Resonance pairs: (3,5) and (4,6)"
write (u, "(A)") "* Use four-particle phase space containing: "
call vector4_write_set (p_born, u, testflag = .true., ultra = .true.)
write (u, "(A)") "******************************"
write (u, "(A)")
x1 = 0.5_default; x2 = 0.25_default; x3 = 0.75_default
write (u, "(A)") "* Use random numbers: "
write (u, "(A,F3.2,1X,A,F3.2,1X,A,F3.2)") &
"x1: ", x1, "x2: ", x2, "x3: ", x3
allocate (generator%real_kinematics)
call generator%real_kinematics%init (nlegborn, 2, 2, 2)
allocate (generator%emitters (2))
generator%emitters(1) = 5; generator%emitters(2) = 6
allocate (generator%m2 (nlegborn))
generator%m2 = p_born**2
allocate (generator%is_massive (nlegborn))
generator%is_massive (1:2) = .false.
generator%is_massive (3:6) = .true.
phs_identifiers(1)%emitter = 5
phs_identifiers(2)%emitter = 6
do i_phs = 1, 2
allocate (phs_identifiers(i_phs)%contributors (2))
end do
allocate (resonance_lists (2, 2))
resonance_lists (1,:) = [3,5]
resonance_lists (2,:) = [4,6]
!!! Here is obviously some redundance. Surely we can improve on this.
do i_phs = 1, 2
phs_identifiers(i_phs)%contributors = resonance_lists(i_phs,:)
end do
do i_con = 1, 2
allocate (alr_contributors(i_con)%c (size (resonance_lists(i_con,:))))
alr_contributors(i_con)%c = resonance_lists(i_con,:)
end do
call generator%generate_radiation_variables &
([x1, x2, x3], p_born, phs_identifiers)
allocate (p_real(nlegborn + 1))
call generator%compute_xi_ref_momenta (p_born, alr_contributors)
!!! Keep the distinction between i_phs and i_con because in general,
!!! they are not the same.
do i_phs = 1, 2
i_con = i_phs
emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1,1X,A,I1,A,I1,A)") &
"* Generate FSR phase space for emitter ", emitter, &
"and resonance pair (", resonance_lists (i_con, 1), ",", &
resonance_lists (i_con, 2), ")"
call generator%compute_xi_max (emitter, i_phs, p_born, &
generator%real_kinematics%xi_max(i_phs), i_con = i_con)
call generator%generate_fsr (emitter, i_phs, i_con, p_born, p_real)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
call write_separator(u)
write (u, "(A)") "* Check if resonance masses are conserved: "
m_inv_born = compute_resonance_mass (p_born, resonance_lists (i_con,:))
m_inv_real = compute_resonance_mass (p_real, resonance_lists (i_con,:), 7)
write (u, "(A,1X, " // fmt // ")") "m_inv_born = ", m_inv_born
write (u, "(A,1X, " // fmt // ")") "m_inv_real = ", m_inv_real
if (abs (m_inv_born - m_inv_real) < tiny_07) then
write (u, "(A)") " Success! "
else
write (u, "(A)") " Failure! "
end if
call write_separator(u)
call write_separator(u)
end do
deallocate (p_real)
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_fks_generator_4"
end subroutine phs_fks_generator_4
@ %def phs_fks_generator_4
@
<<phs fks: test declarations>>=
public :: phs_fks_generator_5
<<phs fks: tests>>=
subroutine phs_fks_generator_5 (u)
use ttv_formfactors, only: init_parameters
integer, intent(in) :: u
type(phs_fks_generator_t) :: generator
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: p_born_onshell
type(vector4_t), dimension(:), allocatable :: p_real
real(default) :: x1, x2, x3
real(default) :: mB, mW, mtop, mcheck
integer :: i, emitter, i_phs
type(phs_identifier_t), dimension(2) :: phs_identifiers
type(lorentz_transformation_t) :: L_to_cms
real(default), parameter :: sqrts = 360._default
real(default), parameter :: momentum_tolerance = 1E-10_default
real(default) :: mpole, gam_out
write (u, "(A)") "* Test output: phs_fks_generator_5"
write (u, "(A)") "* Puropse: Perform threshold on-shell projection of "
write (u, "(A)") "* Born momenta and create a real phase-space "
write (u, "(A)") "* point from those. "
write (u, "(A)")
allocate (p_born(6), p_born_onshell(6))
p_born(1)%p(0) = sqrts / two
p_born(1)%p(1:2) = zero
p_born(1)%p(3) = sqrts / two
p_born(2)%p(0) = sqrts / two
p_born(2)%p(1:2) = zero
p_born(2)%p(3) = -sqrts / two
p_born(3)%p(0) = 117.1179139230_default
p_born(3)%p(1) = 56.91215483880_default
p_born(3)%p(2) = -40.02386013017_default
p_born(3)%p(3) = -49.07634310496_default
p_born(4)%p(0) = 98.91904548743_default
p_born(4)%p(1) = 56.02241403836_default
p_born(4)%p(2) = -8.302977504723_default
p_born(4)%p(3) = -10.50293716131_default
p_born(5)%p(0) = 62.25884689208_default
p_born(5)%p(1) = -60.00786540278_default
p_born(5)%p(2) = 4.753602375910_default
p_born(5)%p(3) = 15.32916731546_default
p_born(6)%p(0) = 81.70419369751_default
p_born(6)%p(1) = -52.92670347439_default
p_born(6)%p(2) = 43.57323525898_default
p_born(6)%p(3) = 44.25011295081_default
generator%n_in = 2
allocate (generator%isr_kinematics)
generator%isr_kinematics%isr_mode = SQRTS_FIXED
mB = 4.2_default
mW = 80.376_default
mtop = 172._default
generator%sqrts = sqrts
!!! Dummy-initialization of the threshold model because generate_fsr_threshold
!!! uses m1s_to_mpole to determine if it is above or below threshold.
call init_parameters (mpole, gam_out, mtop, one, one / 1.5_default, 125._default, &
0.47_default, 0.118_default, 91._default, 80._default, 4.2_default, &
one, one, one, one, zero, zero, zero, zero, zero, zero, .false., zero)
write (u, "(A)") "* Use four-particle phase space containing: "
call vector4_write_set (p_born, u, testflag = .true., ultra = .true.)
call vector4_check_momentum_conservation &
(p_born, 2, unit = u, abs_smallness = momentum_tolerance, verbose = .true.)
write (u, "(A)") "**********************"
write (u, "(A)")
allocate (generator%real_kinematics)
call generator%real_kinematics%init (7, 2, 2, 2)
call generator%real_kinematics%init_onshell (7, 2)
generator%real_kinematics%p_born_cms%phs_point(1)%p = p_born
write (u, "(A)") "Get boost projection system -> CMS: "
L_to_cms = get_boost_for_threshold_projection (p_born, sqrts, mtop)
call L_to_cms%write (u, testflag = .true., ultra = .true.)
write (u, "(A)") "**********************"
write (u, "(A)")
write (u, "(A)") "* Perform onshell-projection:"
associate (p_born => generator%real_kinematics%p_born_cms%phs_point(1)%p, &
p_born_onshell => generator%real_kinematics%p_born_onshell%phs_point(1)%p)
call threshold_projection_born (mtop, L_to_cms, p_born, p_born_onshell)
end associate
call generator%real_kinematics%p_born_onshell%write (1, unit = u, testflag = .true., &
ultra = .true.)
associate (p => generator%real_kinematics%p_born_onshell%phs_point(1)%p)
p_born_onshell = p
call check_phsp (p, 0)
end associate
allocate (generator%emitters (2))
generator%emitters(1) = THR_POS_B; generator%emitters(2) = THR_POS_BBAR
allocate (generator%m2 (6), generator%is_massive(6))
generator%m2 = p_born**2
generator%is_massive (1:2) = .false.
generator%is_massive (3:6) = .true.
phs_identifiers(1)%emitter = THR_POS_B
phs_identifiers(2)%emitter = THR_POS_BBAR
x1 = 0.5_default; x2 = 0.25_default; x3 = 0.6_default
write (u, "(A)") "* Use random numbers: "
write (u, "(A,F3.2,1X,A,F3.2,A,1X,F3.2)") &
"x1: ", x1, "x2: ", x2, "x3: ", x3
call generator%generate_radiation_variables ([x1,x2,x3], p_born_onshell, phs_identifiers)
do i_phs = 1, 2
emitter = phs_identifiers(i_phs)%emitter
call generator%compute_xi_ref_momenta_threshold (p_born_onshell)
call generator%compute_xi_max (emitter, i_phs, p_born_onshell, &
generator%real_kinematics%xi_max(i_phs), i_con = thr_leg(emitter))
end do
write (u, "(A)") &
"* With these, the following radiation variables have been produced: "
associate (rad_var => generator%real_kinematics)
write (u, "(A,F4.2)") "xi_tilde: ", rad_var%xi_tilde
write (u, "(A)") "xi_max: "
write (u, "(2F5.2)") rad_var%xi_max(1), rad_var%xi_max(2)
write (u, "(A)") "y: "
write (u, "(2F5.2)") rad_var%y(1), rad_var%y(2)
write (u, "(A,F4.2)") "phi: ", rad_var%phi
end associate
call write_separator (u)
write (u, "(A)") "* Produce real momenta from on-shell phase space: "
allocate (p_real(7))
do i_phs = 1, 2
emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
call generator%generate_fsr_threshold (emitter, i_phs, p_born_onshell, p_real)
call check_phsp (p_real, emitter)
end do
call write_separator(u)
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_fks_generator_5"
contains
subroutine check_phsp (p, emitter)
type(vector4_t), intent(inout), dimension(:) :: p
integer, intent(in) :: emitter
type(vector4_t) :: pp
real(default) :: E_tot
logical :: check
write (u, "(A)") "* Check momentum conservation: "
call vector4_check_momentum_conservation &
(p, 2, unit = u, abs_smallness = momentum_tolerance, verbose = .true.)
write (u, "(A)") "* Check invariant masses: "
write (u, "(A)", advance = "no") "inv(W+, b, gl): "
pp = p(THR_POS_WP) + p(THR_POS_B)
if (emitter == THR_POS_B) pp = pp + p(THR_POS_GLUON)
if (nearly_equal (pp**1, mtop)) then
write (u, "(A)") "CHECK"
else
write (u, "(A,F7.3)") "FAIL: ", pp**1
end if
write (u, "(A)", advance = "no") "inv(W-, bbar): "
pp = p(THR_POS_WM) + p(THR_POS_BBAR)
if (emitter == THR_POS_BBAR) pp = pp + p(THR_POS_GLUON)
if (nearly_equal (pp**1, mtop)) then
write (u, "(A)") "CHECK"
else
write (u, "(A,F7.3)") "FAIL: ", pp**1
end if
write (u, "(A)") "* Sum of energies equal to sqrts?"
E_tot = sum(p(1:2)%p(0)); check = nearly_equal (E_tot, sqrts)
write (u, "(A,L1)") "Initial state: ", check
if (.not. check) write (u, "(A,F7.3)") "E_tot: ", E_tot
if (emitter > 0) then
E_tot = sum(p(3:7)%p(0))
else
E_tot = sum(p(3:6)%p(0))
end if
check = nearly_equal (E_tot, sqrts)
write (u, "(A,L1)") "Final state : ", check
if (.not. check) write (u, "(A,F7.3)") "E_tot: ", E_tot
call pacify (p, 1E-6_default)
call vector4_write_set (p, u, testflag = .true., ultra = .true.)
end subroutine check_phsp
end subroutine phs_fks_generator_5
@ %def phs_fks_generator_5
@
<<phs fks: test declarations>>=
public :: phs_fks_generator_6
<<phs fks: tests>>=
subroutine phs_fks_generator_6 (u)
integer, intent(in) :: u
type(phs_fks_generator_t) :: generator
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: p_real
real(default) :: x1, x2, x3
real(default) :: mB, mW, mT
integer :: i, emitter, i_phs
type(phs_identifier_t), dimension(2) :: phs_identifiers
write (u, "(A)") "* Test output: phs_fks_generator_6"
write (u, "(A)") "* Puropse: Create real phase space for particle decays"
write (u, "(A)")
allocate (p_born(4))
p_born(1)%p(0) = 173.1_default
p_born(1)%p(1) = zero
p_born(1)%p(2) = zero
p_born(1)%p(3) = zero
p_born(2)%p(0) = 68.17074462929_default
p_born(2)%p(1) = -37.32578717617_default
p_born(2)%p(2) = 30.99675959336_default
p_born(2)%p(3) = -47.70321718398_default
p_born(3)%p(0) = 65.26639312326_default
p_born(3)%p(1) = -1.362927648502_default
p_born(3)%p(2) = -33.25327150840_default
p_born(3)%p(3) = 56.14324922494_default
p_born(4)%p(0) = 39.66286224745_default
p_born(4)%p(1) = 38.68871482467_default
p_born(4)%p(2) = 2.256511915049_default
p_born(4)%p(3) = -8.440032040958_default
generator%n_in = 1
allocate (generator%isr_kinematics)
generator%isr_kinematics%isr_mode = SQRTS_FIXED
mB = 4.2_default
mW = 80.376_default
mT = 173.1_default
generator%sqrts = mT
write (u, "(A)") "* Use four-particle phase space containing: "
call vector4_write_set (p_born, u, testflag = .true., ultra = .true.)
write (u, "(A)") "**********************"
write (u, "(A)")
x1=0.5_default; x2=0.25_default; x3=0.6_default
write (u, "(A)") "* Use random numbers: "
write (u, "(A,F3.2,1X,A,F3.2,A,1X,F3.2)") &
"x1: ", x1, "x2: ", x2, "x3: ", x3
allocate (generator%real_kinematics)
call generator%real_kinematics%init (3, 2, 2, 1)
call generator%real_kinematics%p_born_lab%set_momenta (1, p_born)
allocate (generator%emitters(2))
generator%emitters(1) = 1
generator%emitters(2) = 2
allocate (generator%m2 (4), generator%is_massive(4))
generator%m2(1) = mT**2
generator%m2(2) = mB**2
generator%m2(3) = zero
generator%m2(4) = zero
generator%is_massive(1:2) = .true.
generator%is_massive(3:4) = .false.
phs_identifiers(1)%emitter = 1
phs_identifiers(2)%emitter = 2
call generator%generate_radiation_variables ([x1,x2,x3], p_born, phs_identifiers)
call generator%compute_xi_ref_momenta (p_born)
do i_phs = 1, 2
emitter = phs_identifiers(i_phs)%emitter
call generator%compute_xi_max (emitter, i_phs, p_born, &
generator%real_kinematics%xi_max(i_phs))
end do
write (u, "(A)") &
"* With these, the following radiation variables have been produced: "
associate (rad_var => generator%real_kinematics)
write (u, "(A,F4.2)") "xi_tilde: ", rad_var%xi_tilde
do i = 1, 2
write (u, "(A,I1,A,F5.2)") "i: ", i, "y: " , rad_var%y(i)
end do
write (u, "(A,F4.2)") "phi: ", rad_var%phi
end associate
call write_separator (u)
write (u, "(A)") "Produce real momenta via initial-state emission: "
i_phs = 1; emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
allocate (p_real(5))
call generator%generate_isr_fixed_beam_energy (i_phs, p_born, p_real)
call pacify (p_real, 1E-6_default)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
call write_separator(u)
write (u, "(A)") "Produce real momenta via final-state emisson: "
i_phs = 2; emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
call generator%generate_fsr (emitter, i_phs, p_born, p_real)
call pacify (p_real, 1E-6_default)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_fks_generator_6"
end subroutine phs_fks_generator_6
@ %def phs_fks_generator_6
@
<<phs fks: test declarations>>=
public :: phs_fks_generator_7
<<phs fks: tests>>=
subroutine phs_fks_generator_7 (u)
integer, intent(in) :: u
type(phs_fks_generator_t) :: generator
type(vector4_t), dimension(:), allocatable :: p_born
type(vector4_t), dimension(:), allocatable :: p_real
real(default) :: x1, x2, x3
integer :: i, emitter, i_phs
type(phs_identifier_t), dimension(2) :: phs_identifiers
real(default), parameter :: sqrts = 1000.0_default
write (u, "(A)") "* Test output: phs_fks_generator_7"
write (u, "(A)") "* Puropse: Create real phase space for scattering ISR"
write (u, "(A)") "* keeping the beam energy fixed."
write (u, "(A)")
allocate (p_born(4))
p_born(1)%p(0) = 500._default
p_born(1)%p(1) = 0._default
p_born(1)%p(2) = 0._default
p_born(1)%p(3) = 500._default
p_born(2)%p(0) = 500._default
p_born(2)%p(1) = 0._default
p_born(2)%p(2) = 0._default
p_born(2)%p(3) = -500._default
p_born(3)%p(0) = 500._default
p_born(3)%p(1) = 11.275563070_default
p_born(3)%p(2) = -13.588797663_default
p_born(3)%p(3) = 486.93070588_default
p_born(4)%p(0) = 500._default
p_born(4)%p(1:3) = -p_born(3)%p(1:3)
phs_identifiers(1)%emitter = 1
phs_identifiers(2)%emitter = 2
allocate (generator%emitters(2))
generator%n_in = 2
allocate (generator%isr_kinematics)
generator%isr_kinematics%isr_mode = SQRTS_FIXED
generator%emitters(1) = 1; generator%emitters(2) = 2
generator%sqrts = sqrts
write (u, "(A)") "* Use 2 -> 2 phase space containing: "
call vector4_write_set (p_born, u, testflag = .true., ultra = .true.)
write (u, "(A)") "**********************"
write (u, "(A)")
x1 = 0.5_default; x2 = 0.25_default; x3 = 0.6_default
write (u, "(A)") "* Use random numbers: "
write (u, "(A,F3.2,1X,A,F3.2,A,1X,F3.2)") &
"x1: ", x1, "x2: ", x2, "x3: ", x3
allocate (generator%real_kinematics)
call generator%real_kinematics%init (4, 2, 2, 1)
call generator%real_kinematics%p_born_lab%set_momenta (1, p_born)
allocate (generator%m2 (4))
generator%m2 = 0._default
allocate (generator%is_massive(4))
generator%is_massive = .false.
call generator%generate_radiation_variables ([x1,x2,x3], p_born, phs_identifiers)
call generator%compute_xi_ref_momenta (p_born)
do i_phs = 1, 2
emitter = phs_identifiers(i_phs)%emitter
call generator%compute_xi_max (emitter, i_phs, p_born, &
generator%real_kinematics%xi_max(i_phs))
end do
write (u, "(A)") &
"* With these, the following radiation variables have been produced: "
associate (rad_var => generator%real_kinematics)
write (u, "(A,F4.2)") "xi_tilde: ", rad_var%xi_tilde
do i = 1, 2
write (u, "(A,I1,A,F5.2)") "i: ", i, "y: " , rad_var%y(i)
end do
write (u, "(A,F4.2)") "phi: ", rad_var%phi
end associate
call write_separator (u)
write (u, "(A)") "Produce real momenta via initial-state emission: "
i_phs = 1; emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
allocate (p_real(5))
call generator%generate_isr_fixed_beam_energy (i_phs, p_born, p_real)
call pacify (p_real, 1E-6_default)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
call write_separator(u)
i_phs = 2; emitter = phs_identifiers(i_phs)%emitter
write (u, "(A,I1)") "emitter: ", emitter
call generator%generate_isr_fixed_beam_energy (i_phs, p_born, p_real)
call pacify (p_real, 1E-6_default)
call vector4_write_set (p_real, u, testflag = .true., ultra = .true.)
write (u, "(A)")
write (u, "(A)") "* Test output end: phs_fks_generator_7"
end subroutine phs_fks_generator_7
@ %def phs_fks_generator_3
@
\section{Dispatch}
<<[[dispatch_phase_space.f90]]>>=
<<File header>>
module dispatch_phase_space
<<Use kinds>>
<<Use strings>>
use io_units, only: free_unit
use variables, only: var_list_t
use os_interface, only: os_data_t
use diagnostics
use sf_mappings, only: sf_channel_t
use beam_structures, only: beam_structure_t
use dispatch_beams, only: sf_prop_t, strfun_mode
use mappings
use phs_forests, only: phs_parameters_t
use phs_base
use phs_none
use phs_single
use phs_rambo
use phs_wood
use phs_fks
<<Standard module head>>
<<Dispatch phs: public>>
contains
<<Dispatch phs: procedures>>
end module dispatch_phase_space
@ %def dispatch_phase_space
Allocate a phase-space object according to the variable [[$phs_method]].
<<Dispatch phs: public>>=
public :: dispatch_phs
<<Dispatch phs: procedures>>=
subroutine dispatch_phs (phs, var_list, os_data, process_id, &
mapping_defaults, phs_par, phs_method_in)
class(phs_config_t), allocatable, intent(inout) :: phs
type(var_list_t), intent(in) :: var_list
type(os_data_t), intent(in) :: os_data
type(string_t), intent(in) :: process_id
type(mapping_defaults_t), intent(in), optional :: mapping_defaults
type(phs_parameters_t), intent(in), optional :: phs_par
type(string_t), intent(in), optional :: phs_method_in
type(string_t) :: phs_method, phs_file, run_id
logical :: use_equivalences, vis_channels, fatal_beam_decay
integer :: u_phs
logical :: exist
if (present (phs_method_in)) then
phs_method = phs_method_in
else
phs_method = &
var_list%get_sval (var_str ("$phs_method"))
end if
phs_file = &
var_list%get_sval (var_str ("$phs_file"))
use_equivalences = &
var_list%get_lval (var_str ("?use_vamp_equivalences"))
vis_channels = &
var_list%get_lval (var_str ("?vis_channels"))
fatal_beam_decay = &
var_list%get_lval (var_str ("?fatal_beam_decay"))
run_id = &
var_list%get_sval (var_str ("$run_id"))
select case (char (phs_method))
case ("none")
allocate (phs_none_config_t :: phs)
case ("single")
allocate (phs_single_config_t :: phs)
if (vis_channels) then
call msg_warning ("Visualizing phase space channels not " // &
"available for method 'single'.")
end if
case ("rambo")
allocate (phs_rambo_config_t :: phs)
if (vis_channels) &
call msg_warning ("Visualizing phase space channels not " // &
"available for method 'rambo'.")
case ("fks")
allocate (phs_fks_config_t :: phs)
case ("wood", "default", "fast_wood")
call dispatch_wood ()
case default
call msg_fatal ("Phase space: parameterization method '" &
// char (phs_method) // "' not implemented")
end select
contains
<<Dispatch phs: dispatch phs: procedures>>
end subroutine dispatch_phs
@ %def dispatch_phs
@
<<Dispatch phs: dispatch phs: procedures>>=
subroutine dispatch_wood ()
allocate (phs_wood_config_t :: phs)
select type (phs)
type is (phs_wood_config_t)
if (phs_file /= "") then
inquire (file = char (phs_file), exist = exist)
if (exist) then
call msg_message ("Phase space: reading configuration from '" &
// char (phs_file) // "'")
u_phs = free_unit ()
open (u_phs, file = char (phs_file), &
action = "read", status = "old")
call phs%set_input (u_phs)
else
call msg_fatal ("Phase space: configuration file '" &
// char (phs_file) // "' not found")
end if
end if
if (present (phs_par)) &
call phs%set_parameters (phs_par)
if (use_equivalences) &
call phs%enable_equivalences ()
if (present (mapping_defaults)) &
call phs%set_mapping_defaults (mapping_defaults)
if (phs_method == "fast_wood") phs%use_cascades2 = .true.
phs%vis_channels = vis_channels
phs%fatal_beam_decay = fatal_beam_decay
phs%os_data = os_data
phs%run_id = run_id
end select
end subroutine dispatch_wood
@
@ Configure channel mappings, using some conditions
from the phase space configuration. If there are no structure
functions, we enable a default setup with a single (dummy)
structure-function channel. Otherwise, we look at the channel
collection that we got from the phase-space configuration step. Each
entry should be translated into an independent structure-function
channel, where typically there is one default entry, which could be
mapped using a standard s-channel mapping if the structure function
setup recommends this, and other entries with s-channel resonances.
The latter need to be translated into global mappings from the
structure-function chain.
<<Dispatch phs: public>>=
public :: dispatch_sf_channels
<<Dispatch phs: procedures>>=
subroutine dispatch_sf_channels (sf_channel, sf_string, sf_prop, coll, &
var_list, sqrts, beam_structure)
type(sf_channel_t), dimension(:), allocatable, intent(out) :: sf_channel
type(string_t), intent(out) :: sf_string
type(sf_prop_t), intent(in) :: sf_prop
type(phs_channel_collection_t), intent(in) :: coll
type(var_list_t), intent(in) :: var_list
real(default), intent(in) :: sqrts
type(beam_structure_t), intent(in) :: beam_structure
type(beam_structure_t) :: beam_structure_tmp
class(channel_prop_t), allocatable :: prop
integer :: n_strfun, n_sf_channel, i
logical :: sf_allow_s_mapping, circe1_map, circe1_generate
logical :: s_mapping_enable, endpoint_mapping, power_mapping
logical :: single_parameter
integer, dimension(:), allocatable :: s_mapping, single_mapping
real(default) :: s_mapping_power
real(default) :: circe1_mapping_slope, endpoint_mapping_slope
real(default) :: power_mapping_eps
beam_structure_tmp = beam_structure
call beam_structure_tmp%expand (strfun_mode)
n_strfun = beam_structure_tmp%get_n_record ()
sf_string = beam_structure_tmp%to_string (sf_only = .true.)
sf_allow_s_mapping = &
var_list%get_lval (var_str ("?sf_allow_s_mapping"))
circe1_generate = &
var_list%get_lval (var_str ("?circe1_generate"))
circe1_map = &
var_list%get_lval (var_str ("?circe1_map"))
circe1_mapping_slope = &
var_list%get_rval (var_str ("circe1_mapping_slope"))
s_mapping_enable = .false.
s_mapping_power = 1
endpoint_mapping = .false.
endpoint_mapping_slope = 1
power_mapping = .false.
single_parameter = .false.
select case (char (sf_string))
case ("", "[any particles]")
case ("pdf_builtin, none", &
"pdf_builtin_photon, none", &
"none, pdf_builtin", &
"none, pdf_builtin_photon", &
"lhapdf, none", &
"lhapdf_photon, none", &
"none, lhapdf", &
"none, lhapdf_photon")
single_parameter = .true.
case ("pdf_builtin, none => none, pdf_builtin", &
"pdf_builtin, none => none, pdf_builtin_photon", &
"pdf_builtin_photon, none => none, pdf_builtin", &
"pdf_builtin_photon, none => none, pdf_builtin_photon", &
"lhapdf, none => none, lhapdf", &
"lhapdf, none => none, lhapdf_photon", &
"lhapdf_photon, none => none, lhapdf", &
"lhapdf_photon, none => none, lhapdf_photon")
allocate (s_mapping (2), source = [1, 2])
s_mapping_enable = .true.
s_mapping_power = 2
case ("pdf_builtin, none => none, pdf_builtin => epa, none => none, epa", &
"pdf_builtin, none => none, pdf_builtin => ewa, none => none, ewa", &
"pdf_builtin, none => none, pdf_builtin => ewa, none => none, epa", &
"pdf_builtin, none => none, pdf_builtin => epa, none => none, ewa")
allocate (s_mapping (2), source = [1, 2])
s_mapping_enable = .true.
s_mapping_power = 2
case ("isr, none", &
"none, isr")
allocate (single_mapping (1), source = [1])
single_parameter = .true.
case ("isr, none => none, isr")
allocate (s_mapping (2), source = [1, 2])
power_mapping = .true.
power_mapping_eps = minval (sf_prop%isr_eps)
case ("isr, none => none, isr => epa, none => none, epa", &
"isr, none => none, isr => ewa, none => none, ewa", &
"isr, none => none, isr => ewa, none => none, epa", &
"isr, none => none, isr => epa, none => none, ewa")
allocate (s_mapping (2), source = [1, 2])
power_mapping = .true.
power_mapping_eps = minval (sf_prop%isr_eps)
case ("circe1 => isr, none => none, isr => epa, none => none, epa", &
"circe1 => isr, none => none, isr => ewa, none => none, ewa", &
"circe1 => isr, none => none, isr => ewa, none => none, epa", &
"circe1 => isr, none => none, isr => epa, none => none, ewa")
if (circe1_generate) then
allocate (s_mapping (2), source = [2, 3])
else
allocate (s_mapping (3), source = [1, 2, 3])
endpoint_mapping = .true.
endpoint_mapping_slope = circe1_mapping_slope
end if
power_mapping = .true.
power_mapping_eps = minval (sf_prop%isr_eps)
case ("pdf_builtin, none => none, isr", &
"pdf_builtin_photon, none => none, isr", &
"lhapdf, none => none, isr", &
"lhapdf_photon, none => none, isr")
allocate (single_mapping (1), source = [2])
case ("isr, none => none, pdf_builtin", &
"isr, none => none, pdf_builtin_photon", &
"isr, none => none, lhapdf", &
"isr, none => none, lhapdf_photon")
allocate (single_mapping (1), source = [1])
case ("epa, none", &
"none, epa")
allocate (single_mapping (1), source = [1])
single_parameter = .true.
case ("epa, none => none, epa")
allocate (single_mapping (2), source = [1, 2])
case ("epa, none => none, isr", &
"isr, none => none, epa", &
"ewa, none => none, isr", &
"isr, none => none, ewa")
allocate (single_mapping (2), source = [1, 2])
case ("pdf_builtin, none => none, epa", &
"pdf_builtin_photon, none => none, epa", &
"lhapdf, none => none, epa", &
"lhapdf_photon, none => none, epa")
allocate (single_mapping (1), source = [2])
case ("pdf_builtin, none => none, ewa", &
"pdf_builtin_photon, none => none, ewa", &
"lhapdf, none => none, ewa", &
"lhapdf_photon, none => none, ewa")
allocate (single_mapping (1), source = [2])
case ("epa, none => none, pdf_builtin", &
"epa, none => none, pdf_builtin_photon", &
"epa, none => none, lhapdf", &
"epa, none => none, lhapdf_photon")
allocate (single_mapping (1), source = [1])
case ("ewa, none => none, pdf_builtin", &
"ewa, none => none, pdf_builtin_photon", &
"ewa, none => none, lhapdf", &
"ewa, none => none, lhapdf_photon")
allocate (single_mapping (1), source = [1])
case ("ewa, none", &
"none, ewa")
allocate (single_mapping (1), source = [1])
single_parameter = .true.
case ("ewa, none => none, ewa")
allocate (single_mapping (2), source = [1, 2])
case ("energy_scan, none => none, energy_scan")
allocate (s_mapping (2), source = [1, 2])
case ("sf_test_1, none => none, sf_test_1")
allocate (s_mapping (2), source = [1, 2])
case ("circe1")
if (circe1_generate) then
!!! no mapping
else if (circe1_map) then
allocate (s_mapping (1), source = [1])
endpoint_mapping = .true.
endpoint_mapping_slope = circe1_mapping_slope
else
allocate (s_mapping (1), source = [1])
s_mapping_enable = .true.
end if
case ("circe1 => isr, none => none, isr")
if (circe1_generate) then
allocate (s_mapping (2), source = [2, 3])
else
allocate (s_mapping (3), source = [1, 2, 3])
endpoint_mapping = .true.
endpoint_mapping_slope = circe1_mapping_slope
end if
power_mapping = .true.
power_mapping_eps = minval (sf_prop%isr_eps)
case ("circe1 => isr, none", &
"circe1 => none, isr")
allocate (single_mapping (1), source = [2])
case ("circe1 => epa, none => none, epa")
if (circe1_generate) then
allocate (single_mapping (2), source = [2, 3])
else
call msg_fatal ("CIRCE/EPA: supported with ?circe1_generate=true &
&only")
end if
case ("circe1 => ewa, none => none, ewa")
if (circe1_generate) then
allocate (single_mapping (2), source = [2, 3])
else
call msg_fatal ("CIRCE/EWA: supported with ?circe1_generate=true &
&only")
end if
case ("circe1 => epa, none", &
"circe1 => none, epa")
if (circe1_generate) then
allocate (single_mapping (1), source = [2])
else
call msg_fatal ("CIRCE/EPA: supported with ?circe1_generate=true &
&only")
end if
case ("circe1 => epa, none => none, isr", &
"circe1 => isr, none => none, epa", &
"circe1 => ewa, none => none, isr", &
"circe1 => isr, none => none, ewa")
if (circe1_generate) then
allocate (single_mapping (2), source = [2, 3])
else
call msg_fatal ("CIRCE/EPA: supported with ?circe1_generate=true &
&only")
end if
case ("circe2", &
"gaussian", &
"beam_events")
!!! no mapping
case ("circe2 => isr, none => none, isr", &
"gaussian => isr, none => none, isr", &
"beam_events => isr, none => none, isr")
allocate (s_mapping (2), source = [2, 3])
power_mapping = .true.
power_mapping_eps = minval (sf_prop%isr_eps)
case ("circe2 => isr, none", &
"circe2 => none, isr", &
"gaussian => isr, none", &
"gaussian => none, isr", &
"beam_events => isr, none", &
"beam_events => none, isr")
allocate (single_mapping (1), source = [2])
case ("circe2 => epa, none => none, epa", &
"gaussian => epa, none => none, epa", &
"beam_events => epa, none => none, epa")
allocate (single_mapping (2), source = [2, 3])
case ("circe2 => epa, none", &
"circe2 => none, epa", &
"circe2 => ewa, none", &
"circe2 => none, ewa", &
"gaussian => epa, none", &
"gaussian => none, epa", &
"gaussian => ewa, none", &
"gaussian => none, ewa", &
"beam_events => epa, none", &
"beam_events => none, epa", &
"beam_events => ewa, none", &
"beam_events => none, ewa")
allocate (single_mapping (1), source = [2])
case ("circe2 => epa, none => none, isr", &
"circe2 => isr, none => none, epa", &
"circe2 => ewa, none => none, isr", &
"circe2 => isr, none => none, ewa", &
"gaussian => epa, none => none, isr", &
"gaussian => isr, none => none, epa", &
"gaussian => ewa, none => none, isr", &
"gaussian => isr, none => none, ewa", &
"beam_events => epa, none => none, isr", &
"beam_events => isr, none => none, epa", &
"beam_events => ewa, none => none, isr", &
"beam_events => isr, none => none, ewa")
allocate (single_mapping (2), source = [2, 3])
case ("energy_scan")
case default
call msg_fatal ("Beam structure: " &
// char (sf_string) // " not supported")
end select
if (sf_allow_s_mapping .and. coll%n > 0) then
n_sf_channel = coll%n
allocate (sf_channel (n_sf_channel))
do i = 1, n_sf_channel
call sf_channel(i)%init (n_strfun)
if (allocated (single_mapping)) then
call sf_channel(i)%activate_mapping (single_mapping)
end if
if (allocated (prop)) deallocate (prop)
call coll%get_entry (i, prop)
if (allocated (prop)) then
if (endpoint_mapping .and. power_mapping) then
select type (prop)
type is (resonance_t)
call sf_channel(i)%set_eir_mapping (s_mapping, &
a = endpoint_mapping_slope, eps = power_mapping_eps, &
m = prop%mass / sqrts, w = prop%width / sqrts)
type is (on_shell_t)
call sf_channel(i)%set_eio_mapping (s_mapping, &
a = endpoint_mapping_slope, eps = power_mapping_eps, &
m = prop%mass / sqrts)
end select
else if (endpoint_mapping) then
select type (prop)
type is (resonance_t)
call sf_channel(i)%set_epr_mapping (s_mapping, &
a = endpoint_mapping_slope, &
m = prop%mass / sqrts, w = prop%width / sqrts)
type is (on_shell_t)
call sf_channel(i)%set_epo_mapping (s_mapping, &
a = endpoint_mapping_slope, &
m = prop%mass / sqrts)
end select
else if (power_mapping) then
select type (prop)
type is (resonance_t)
call sf_channel(i)%set_ipr_mapping (s_mapping, &
eps = power_mapping_eps, &
m = prop%mass / sqrts, w = prop%width / sqrts)
type is (on_shell_t)
call sf_channel(i)%set_ipo_mapping (s_mapping, &
eps = power_mapping_eps, &
m = prop%mass / sqrts)
end select
else if (allocated (s_mapping)) then
select type (prop)
type is (resonance_t)
call sf_channel(i)%set_res_mapping (s_mapping, &
m = prop%mass / sqrts, w = prop%width / sqrts, &
single = single_parameter)
type is (on_shell_t)
call sf_channel(i)%set_os_mapping (s_mapping, &
m = prop%mass / sqrts, &
single = single_parameter)
end select
else if (allocated (single_mapping)) then
select type (prop)
type is (resonance_t)
call sf_channel(i)%set_res_mapping (single_mapping, &
m = prop%mass / sqrts, w = prop%width / sqrts, &
single = single_parameter)
type is (on_shell_t)
call sf_channel(i)%set_os_mapping (single_mapping, &
m = prop%mass / sqrts, &
single = single_parameter)
end select
end if
else if (endpoint_mapping .and. power_mapping) then
call sf_channel(i)%set_ei_mapping (s_mapping, &
a = endpoint_mapping_slope, eps = power_mapping_eps)
else if (endpoint_mapping .and. .not. allocated (single_mapping)) then
call sf_channel(i)%set_ep_mapping (s_mapping, &
a = endpoint_mapping_slope)
else if (power_mapping .and. .not. allocated (single_mapping)) then
call sf_channel(i)%set_ip_mapping (s_mapping, &
eps = power_mapping_eps)
else if (s_mapping_enable .and. .not. allocated (single_mapping)) then
call sf_channel(i)%set_s_mapping (s_mapping, &
power = s_mapping_power)
end if
end do
else if (sf_allow_s_mapping) then
allocate (sf_channel (1))
call sf_channel(1)%init (n_strfun)
if (allocated (single_mapping)) then
call sf_channel(1)%activate_mapping (single_mapping)
else if (endpoint_mapping .and. power_mapping) then
call sf_channel(i)%set_ei_mapping (s_mapping, &
a = endpoint_mapping_slope, eps = power_mapping_eps)
else if (endpoint_mapping) then
call sf_channel(1)%set_ep_mapping (s_mapping, &
a = endpoint_mapping_slope)
else if (power_mapping) then
call sf_channel(1)%set_ip_mapping (s_mapping, &
eps = power_mapping_eps)
else if (s_mapping_enable) then
call sf_channel(1)%set_s_mapping (s_mapping, &
power = s_mapping_power)
end if
else
allocate (sf_channel (1))
call sf_channel(1)%init (n_strfun)
if (allocated (single_mapping)) then
call sf_channel(1)%activate_mapping (single_mapping)
end if
end if
end subroutine dispatch_sf_channels
@ %def dispatch_sf_channels
@
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[dispatch_phs_ut.f90]]>>=
<<File header>>
module dispatch_phs_ut
use unit_tests
use dispatch_phs_uti
<<Standard module head>>
<<Dispatch phs: public test>>
contains
<<Dispatch phs: test driver>>
end module dispatch_phs_ut
@ %def dispatch_phs_ut
@
<<[[dispatch_phs_uti.f90]]>>=
<<File header>>
module dispatch_phs_uti
<<Use kinds>>
<<Use strings>>
use variables
use io_units, only: free_unit
use os_interface, only: os_data_t
use process_constants
use model_data
use models
use phs_base
use phs_none
use phs_forests
use phs_wood
use mappings
use dispatch_phase_space
<<Standard module head>>
<<Dispatch phs: test declarations>>
contains
<<Dispatch phs: tests>>
end module dispatch_phs_uti
@ %def dispatch_phs_ut
@ API: driver for the unit tests below.
<<Dispatch phs: public test>>=
public ::dispatch_phs_test
<<Dispatch phs: test driver>>=
subroutine dispatch_phs_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Dispatch phs: execute tests>>
end subroutine dispatch_phs_test
@ %def dispatch_phs_test
@
\subsubsection{Select type: phase-space configuration object}
<<Dispatch phs: execute tests>>=
call test (dispatch_phs_1, "dispatch_phs_1", &
"phase-space configuration", &
u, results)
<<Dispatch phs: test declarations>>=
public :: dispatch_phs_1
<<Dispatch phs: tests>>=
subroutine dispatch_phs_1 (u)
integer, intent(in) :: u
type(var_list_t) :: var_list
class(phs_config_t), allocatable :: phs
type(phs_parameters_t) :: phs_par
type(os_data_t) :: os_data
type(mapping_defaults_t) :: mapping_defs
write (u, "(A)") "* Test output: dispatch_phs_1"
write (u, "(A)") "* Purpose: select phase-space configuration method"
write (u, "(A)")
call var_list%init_defaults (0)
write (u, "(A)") "* Allocate PHS as phs_none_t"
write (u, "(A)")
call var_list%set_string (&
var_str ("$phs_method"), &
var_str ("none"), is_known = .true.)
call dispatch_phs (phs, var_list, os_data, var_str ("dispatch_phs_1"))
call phs%write (u)
call phs%final ()
deallocate (phs)
write (u, "(A)")
write (u, "(A)") "* Allocate PHS as phs_single_t"
write (u, "(A)")
call var_list%set_string (&
var_str ("$phs_method"), &
var_str ("single"), is_known = .true.)
call dispatch_phs (phs, var_list, os_data, var_str ("dispatch_phs_1"))
call phs%write (u)
call phs%final ()
deallocate (phs)
write (u, "(A)")
write (u, "(A)") "* Allocate PHS as phs_wood_t"
write (u, "(A)")
call var_list%set_string (&
var_str ("$phs_method"), &
var_str ("wood"), is_known = .true.)
call dispatch_phs (phs, var_list, os_data, var_str ("dispatch_phs_1"))
call phs%write (u)
call phs%final ()
deallocate (phs)
write (u, "(A)")
write (u, "(A)") "* Setting parameters for phs_wood_t"
write (u, "(A)")
phs_par%m_threshold_s = 123
phs_par%m_threshold_t = 456
phs_par%t_channel = 42
phs_par%off_shell = 17
phs_par%keep_nonresonant = .false.
mapping_defs%energy_scale = 987
mapping_defs%invariant_mass_scale = 654
mapping_defs%momentum_transfer_scale = 321
mapping_defs%step_mapping = .false.
mapping_defs%step_mapping_exp = .false.
mapping_defs%enable_s_mapping = .true.
call dispatch_phs (phs, var_list, os_data, var_str ("dispatch_phs_1"), &
mapping_defs, phs_par)
call phs%write (u)
call phs%final ()
call var_list%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: dispatch_phs_1"
end subroutine dispatch_phs_1
@ %def dispatch_phs_1
@
\subsubsection{Phase-space configuration with file}
<<Dispatch phs: execute tests>>=
call test (dispatch_phs_2, "dispatch_phs_2", &
"configure phase space using file", &
u, results)
<<Dispatch phs: test declarations>>=
public :: dispatch_phs_2
<<Dispatch phs: tests>>=
subroutine dispatch_phs_2 (u)
use phs_base_ut, only: init_test_process_data
use phs_wood_ut, only: write_test_phs_file
use phs_forests
integer, intent(in) :: u
type(var_list_t) :: var_list
type(os_data_t) :: os_data
type(process_constants_t) :: process_data
type(model_list_t) :: model_list
type(model_t), pointer :: model
class(phs_config_t), allocatable :: phs
integer :: u_phs
write (u, "(A)") "* Test output: dispatch_phs_2"
write (u, "(A)") "* Purpose: select 'wood' phase-space &
&for a test process"
write (u, "(A)") "* and read phs configuration from file"
write (u, "(A)")
write (u, "(A)") "* Initialize a process"
write (u, "(A)")
call var_list%init_defaults (0)
- call os_data%init ()
+ call os_data%init ()
call syntax_model_file_init ()
call model_list%read_model &
(var_str ("Test"), var_str ("Test.mdl"), os_data, model)
call syntax_phs_forest_init ()
call init_test_process_data (var_str ("dispatch_phs_2"), process_data)
write (u, "(A)") "* Write phase-space file"
u_phs = free_unit ()
open (u_phs, file = "dispatch_phs_2.phs", action = "write", status = "replace")
call write_test_phs_file (u_phs, var_str ("dispatch_phs_2"))
close (u_phs)
write (u, "(A)")
write (u, "(A)") "* Allocate PHS as phs_wood_t"
write (u, "(A)")
call var_list%set_string (&
var_str ("$phs_method"), &
var_str ("wood"), is_known = .true.)
call var_list%set_string (&
var_str ("$phs_file"), &
var_str ("dispatch_phs_2.phs"), is_known = .true.)
call dispatch_phs (phs, var_list, os_data, var_str ("dispatch_phs_2"))
call phs%init (process_data, model)
call phs%configure (sqrts = 1000._default)
call phs%write (u)
write (u, "(A)")
select type (phs)
type is (phs_wood_config_t)
call phs%write_forest (u)
end select
call phs%final ()
call var_list%final ()
call syntax_model_file_final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: dispatch_phs_2"
end subroutine dispatch_phs_2
@ %def dispatch_phs_2
@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{A lexer for O'Mega's phase-space output}
This module provides three data types. One of them is the type
[[dag_string_t]] which should contain the information of all Feynman
diagrams in the factorized form which is provided by O'Mega in its
phase-space outout. This output is translated into a string of tokens (in
the form of an a array of the type [[dag_token_t]]) which have a certain
meaning. The purpose of this module is only to identify these tokens
correctly and to provide some procedures and interfaces which allow us to
use these strings in a similar way as variables of the basic character
type or the type [[iso_varying_string]]. Both [[character]] and
[[iso_varying_string]] have some disadvantages at least if one wants to
keep support for some older compiler versions. These can be circumvented
by the [[dag_string_t]] type. Finally the [[dag_chain_t]] type is used
to create a larger string in several steps without always recreating the
string, which is done in the form of a simple linked list. In the end
one can create a single [[dag_string]] out of this list, which is more
useful.
<<[[cascades2_lexer.f90]]>>=
<<File header>>
module cascades2_lexer
<<Use kinds>>
use kinds, only: TC, i8
<<Standard module head>>
<<Cascades2 lexer: public>>
<<Cascades2 lexer: parameters>>
<<Cascades2 lexer: types>>
<<Cascades2 lexer: interfaces>>
contains
<<Cascades2 lexer: procedures>>
end module cascades2_lexer
@ %def cascades2_lexer
@ This is the token type. By default the variable [[type]] is [[EMPTY_TK]]
but can obtain other values corresponding to the parameters defined below.
The type of the token corresponds to a particular sequence of characters.
When the token corresponds to a node of a tree, i.e. some particle in the
Feynman diagram, the type is [[NODE_TK]] and the [[particle_name]] variable
is holding the name of the particle. O'Megas output contains in
addition to the particle name some numbers which indicate the external
momenta that are flowing through this line. These numbers are translated
into a binary code and saved in the variable [[bincode]]. In this case
the number 1 corresponds to a bit set at position 0, 2 corresponds to a
bit set at position 1, etc. Instead of numbers which are composed out of
several digits, letters are used, i.e. A instead of 10 (bit at position 9),
B instead of 11 (bit at position 10), etc.\\
When the DAG is reconstructed from a [[dag_string]] which was built from
O'Mega's output, this string is modified such that a substring (a set of
tokens) is replaced by a single token where the type variable is one of
the three parameters [[DAG_NODE_TK]], [[DAG_OPTIONS_TK]] and
[[DAG_COMBINATION_TK]]. These parameters correspond to the three types
[[dag_node_t]], [[dag_options_t]] and [[dag_combination_t]] (see [[cascades2]]
for more information. In this case, since these objects are organized
in arrays, the [[index]] variable holds the corresponding position in
the array.\\
In any case, we want to be able to reproduce the character string from
which a token (or a string) has been created. The variable [[char_len]]
is the length of this string. For tokens with the type [[DAG_NODE_TK]],
[[DAG_OPTIONS_TK]] and [[DAG_COMBINATION_TK]] we use output of the form
[[<N23>]], [[<O23>]] or [[<C23>]] which is useful for debugging the parser.
Here 23 is the [[index]] and [[N]], [[O]] or [[C]] obviously corresponds
to the [[type]].
<<Cascades2 lexer: parameters>>=
integer, parameter :: PRT_NAME_LEN = 20
@ %def PRT_NAME_LEN
<<Cascades2 lexer: public>>=
public :: dag_token_t
<<Cascades2 lexer: types>>=
type :: dag_token_t
integer :: type = EMPTY_TK
integer :: char_len = 0
integer(TC) :: bincode = 0
character (PRT_NAME_LEN) :: particle_name=""
integer :: index = 0
contains
<<Cascades2 lexer: dag token: TBP>>
end type dag_token_t
@ %def dag_token_t
@ This is the string type. It also holds the number of characters in the
corresponding character string. It contains an array of tokens. If the
[[dag_string]] is constructed using the type [[dag_chain_t]], which creates
a linked list, we also need the pointer [[next]].
<<Cascades2 lexer: public>>=
public :: dag_string_t
<<Cascades2 lexer: types>>=
type :: dag_string_t
integer :: char_len = 0
type (dag_token_t), dimension(:), allocatable :: t
type (dag_string_t), pointer :: next => null ()
contains
<<Cascades2 lexer: dag string: TBP>>
end type dag_string_t
@ %def dag_string_t
@ This is the chain of [[dag_strings]]. It allows us to construct a large
string by appending new strings to the linked list, which can later be
merged to a single string. This is very useful because the file written
by O'Mega contains large strings where each string contains all Feynman
diagrams in a factorized form, but these large strings are cut into
several pieces and distributed over many lines. As the file can become
large, rewriting a new [[dag_string]] (or [[iso_varying_string]]) would
consume more and more time with each additional line. For recreating a
single [[dag_string]] out of this chain, we need the total character
length and the sum of all sizes of the [[dag_token]] arrays [[t]].
<<Cascades2 lexer: public>>=
public :: dag_chain_t
<<Cascades2 lexer: types>>=
type :: dag_chain_t
integer :: char_len = 0
integer :: t_size = 0
type (dag_string_t), pointer :: first => null ()
type (dag_string_t), pointer :: last => null ()
contains
<<Cascades2 lexer: dag chain: TBP>>
end type dag_chain_t
@ %def dag_chain_t
@ We define two parameters holding the characters corresponding to a
backslash and a blanc space.
<<Cascades2 lexer: parameters>>=
character(len=1), parameter, public :: BACKSLASH_CHAR = "\\"
character(len=1), parameter :: BLANC_CHAR = " "
@ %def BACKSLASH_CHAR BLANC_CHAR
@ These are the parameters which correspond to meaningful types
of [[token]].
<<Cascades2 lexer: parameters>>=
integer, parameter, public :: NEW_LINE_TK = -2
integer, parameter :: BLANC_SPACE_TK = -1
integer, parameter :: EMPTY_TK = 0
integer, parameter, public :: NODE_TK = 1
integer, parameter, public :: DAG_NODE_TK = 2
integer, parameter, public :: DAG_OPTIONS_TK = 3
integer, parameter, public :: DAG_COMBINATION_TK = 4
integer, parameter, public :: COLON_TK = 11
integer, parameter, public :: COMMA_TK = 12
integer, parameter, public :: VERTICAL_BAR_TK = 13
integer, parameter, public :: OPEN_PAR_TK = 21
integer, parameter, public :: CLOSED_PAR_TK = 22
integer, parameter, public :: OPEN_CURLY_TK = 31
integer, parameter, public :: CLOSED_CURLY_TK = 32
@ %def NEW_LINE_TK BLANC_SPACE_TK EMPTY_TK NODE_TK
@ %def COLON_TK COMMA_TK VERTICAL_LINE_TK OPEN_PAR_TK
@ %def CLOSED_PAR_TK OPEN_CURLY_TK CLOSED_CURLY_TK
@ Different sorts of assignment. This contains the conversion
of a [[character]] variable into a [[dag_token]] or [[dag_string]].
<<Cascades2 lexer: public>>=
public :: assignment (=)
<<Cascades2 lexer: interfaces>>=
interface assignment (=)
module procedure dag_token_assign_from_char_string
module procedure dag_token_assign_from_dag_token
module procedure dag_string_assign_from_dag_token
module procedure dag_string_assign_from_char_string
module procedure dag_string_assign_from_dag_string
module procedure dag_string_assign_from_dag_token_array
end interface assignment (=)
@ %def interfaces
<<Cascades2 lexer: dag token: TBP>>=
procedure :: init_dag_object_token => dag_token_init_dag_object_token
<<Cascades2 lexer: procedures>>=
subroutine dag_token_init_dag_object_token (dag_token, type, index)
class (dag_token_t), intent (out) :: dag_token
integer, intent (in) :: index
integer :: type
dag_token%type = type
dag_token%char_len = integer_n_dec_digits (index) + 3
dag_token%index = index
contains
function integer_n_dec_digits (number) result (n_digits)
integer, intent (in) :: number
integer :: n_digits
integer :: div_number
n_digits = 0
div_number = number
do
div_number = div_number / 10
n_digits = n_digits + 1
if (div_number == 0) exit
enddo
end function integer_n_dec_digits
end subroutine dag_token_init_dag_object_token
@ %def dag_token_init_dag_object_token
<<Cascades2 lexer: procedures>>=
elemental subroutine dag_token_assign_from_char_string (dag_token, char_string)
type (dag_token_t), intent (out) :: dag_token
character (len=*), intent (in) :: char_string
integer :: i, j
logical :: set_bincode
integer :: bit_pos
character (len=10) :: index_char
dag_token%char_len = len (char_string)
if (dag_token%char_len == 1) then
select case (char_string(1:1))
case (BACKSLASH_CHAR)
dag_token%type = NEW_LINE_TK
case (" ")
dag_token%type = BLANC_SPACE_TK
case (":")
dag_token%type = COLON_TK
case (",")
dag_token%type = COMMA_TK
case ("|")
dag_token%type = VERTICAL_BAR_TK
case ("(")
dag_token%type = OPEN_PAR_TK
case (")")
dag_token%type = CLOSED_PAR_TK
case ("{")
dag_token%type = OPEN_CURLY_TK
case ("}")
dag_token%type = CLOSED_CURLY_TK
end select
else if (char_string(1:1) == "<") then
select case (char_string(2:2))
case ("N")
dag_token%type = DAG_NODE_TK
case ("O")
dag_token%type = DAG_OPTIONS_TK
case ("C")
dag_token%type = DAG_COMBINATION_TK
end select
read(char_string(3:dag_token%char_len-1), fmt="(I10)") dag_token%index
else
dag_token%bincode = 0
set_bincode = .false.
do i=1, dag_token%char_len
select case (char_string(i:i))
case ("[")
dag_token%type = NODE_TK
if (i > 1) then
do j = 1, i - 1
dag_token%particle_name(j:j) = char_string(j:j)
enddo
end if
set_bincode = .true.
case ("]")
set_bincode = .false.
case default
dag_token%type = NODE_TK
if (set_bincode) then
select case (char_string(i:i))
case ("1", "2", "3", "4", "5", "6", "7", "8", "9")
read (char_string(i:i), fmt="(I1)") bit_pos
case ("A")
bit_pos = 10
case ("B")
bit_pos = 11
case ("C")
bit_pos = 12
end select
dag_token%bincode = ibset(dag_token%bincode, bit_pos - 1)
end if
end select
if (dag_token%type /= NODE_TK) exit
enddo
end if
end subroutine dag_token_assign_from_char_string
@ %def dag_token_assign_from_char_string
<<Cascades2 lexer: procedures>>=
elemental subroutine dag_token_assign_from_dag_token (token_out, token_in)
type (dag_token_t), intent (out) :: token_out
type (dag_token_t), intent (in) :: token_in
token_out%type = token_in%type
token_out%char_len = token_in%char_len
token_out%bincode = token_in%bincode
token_out%particle_name = token_in%particle_name
token_out%index = token_in%index
end subroutine dag_token_assign_from_dag_token
@ %def dag_token_assign_from_dag_token
<<Cascades2 lexer: procedures>>=
elemental subroutine dag_string_assign_from_dag_token (dag_string, dag_token)
type (dag_string_t), intent (out) :: dag_string
type (dag_token_t), intent (in) :: dag_token
allocate (dag_string%t(1))
dag_string%t(1) = dag_token
dag_string%char_len = dag_token%char_len
end subroutine dag_string_assign_from_dag_token
@ %def dag_string_assign_from_dag_token
<<Cascades2 lexer: procedures>>=
subroutine dag_string_assign_from_dag_token_array (dag_string, dag_token)
type (dag_string_t), intent (out) :: dag_string
type (dag_token_t), dimension(:), intent (in) :: dag_token
allocate (dag_string%t(size(dag_token)))
dag_string%t = dag_token
dag_string%char_len = sum(dag_token%char_len)
end subroutine dag_string_assign_from_dag_token_array
@ %def dag_string_assign_from_dag_token_array
<<Cascades2 lexer: procedures>>=
elemental subroutine dag_string_assign_from_char_string (dag_string, char_string)
type (dag_string_t), intent (out) :: dag_string
character (len=*), intent (in) :: char_string
type (dag_token_t), dimension(:), allocatable :: token
integer :: token_pos
integer :: i
character (len=len(char_string)) :: node_char
integer :: node_char_len
node_char = ""
dag_string%char_len = len (char_string)
if (dag_string%char_len > 0) then
allocate (token(dag_string%char_len))
token_pos = 0
node_char_len = 0
do i=1, dag_string%char_len
select case (char_string(i:i))
case (BACKSLASH_CHAR, " ", ":", ",", "|", "(", ")", "{", "}")
if (node_char_len > 0) then
token_pos = token_pos + 1
token(token_pos) = node_char(:node_char_len)
node_char_len = 0
end if
token_pos = token_pos + 1
token(token_pos) = char_string(i:i)
case default
node_char_len = node_char_len + 1
node_char(node_char_len:node_char_len) = char_string(i:i)
end select
enddo
if (node_char_len > 0) then
token_pos = token_pos + 1
token(token_pos) = node_char(:node_char_len)
end if
if (token_pos > 0) then
allocate (dag_string%t(token_pos))
dag_string%t = token(:token_pos)
deallocate (token)
end if
end if
end subroutine dag_string_assign_from_char_string
@ %def dag_string_assign_from_char_string
<<Cascades2 lexer: procedures>>=
elemental subroutine dag_string_assign_from_dag_string (string_out, string_in)
type (dag_string_t), intent (out) :: string_out
type (dag_string_t), intent (in) :: string_in
if (allocated (string_in%t)) then
allocate (string_out%t (size(string_in%t)))
string_out%t = string_in%t
end if
string_out%char_len = string_in%char_len
end subroutine dag_string_assign_from_dag_string
@ %def dag_string_assign_from_dag_string
@ Concatenate strings/tokens. The result is always a [[dag_string]].
<<Cascades2 lexer: public>>=
public :: operator (//)
<<Cascades2 lexer: interfaces>>=
interface operator (//)
module procedure concat_dag_token_dag_token
module procedure concat_dag_string_dag_token
module procedure concat_dag_token_dag_string
module procedure concat_dag_string_dag_string
end interface operator (//)
@ %def interfaces
<<Cascades2 lexer: procedures>>=
function concat_dag_token_dag_token (token1, token2) result (res_string)
type (dag_token_t), intent (in) :: token1, token2
type (dag_string_t) :: res_string
if (token1%type == EMPTY_TK) then
res_string = token2
else if (token2%type == EMPTY_TK) then
res_string = token1
else
allocate (res_string%t(2))
res_string%t(1) = token1
res_string%t(2) = token2
res_string%char_len = token1%char_len + token2%char_len
end if
end function concat_dag_token_dag_token
@ %def concat_dag_token_dag_token
<<Cascades2 lexer: procedures>>=
function concat_dag_string_dag_token (dag_string, dag_token) result (res_string)
type (dag_string_t), intent (in) :: dag_string
type (dag_token_t), intent (in) :: dag_token
type (dag_string_t) :: res_string
integer :: t_size
if (dag_string%char_len == 0) then
res_string = dag_token
else if (dag_token%type == EMPTY_TK) then
res_string = dag_string
else
t_size = size (dag_string%t)
allocate (res_string%t(t_size+1))
res_string%t(:t_size) = dag_string%t
res_string%t(t_size+1) = dag_token
res_string%char_len = dag_string%char_len + dag_token%char_len
end if
end function concat_dag_string_dag_token
@ %def concat_dag_string_dag_token
<<Cascades2 lexer: procedures>>=
function concat_dag_token_dag_string (dag_token, dag_string) result (res_string)
type (dag_token_t), intent (in) :: dag_token
type (dag_string_t), intent (in) :: dag_string
type (dag_string_t) :: res_string
integer :: t_size
if (dag_token%type == EMPTY_TK) then
res_string = dag_string
else if (dag_string%char_len == 0) then
res_string = dag_token
else
t_size = size (dag_string%t)
allocate (res_string%t(t_size+1))
res_string%t(2:t_size+1) = dag_string%t
res_string%t(1) = dag_token
res_string%char_len = dag_token%char_len + dag_string%char_len
end if
end function concat_dag_token_dag_string
@ %def concat_dag_token_dag_string
<<Cascades2 lexer: procedures>>=
function concat_dag_string_dag_string (string1, string2) result (res_string)
type (dag_string_t), intent (in) :: string1, string2
type (dag_string_t) :: res_string
integer :: t1_size, t2_size, t_size
if (string1%char_len == 0) then
res_string = string2
else if (string2%char_len == 0) then
res_string = string1
else
t1_size = size (string1%t)
t2_size = size (string2%t)
t_size = t1_size + t2_size
if (t_size > 0) then
allocate (res_string%t(t_size))
res_string%t(:t1_size) = string1%t
res_string%t(t1_size+1:) = string2%t
res_string%char_len = string1%char_len + string2%char_len
end if
end if
end function concat_dag_string_dag_string
@ %def concat_dag_string_dag_string
@ Compare strings/tokens/characters. Each character is relevant, including
all blanc spaces. An exception is the [[newline]] character which is not
treated by the types used in this module (not to confused with the type
parameter [[NEW_LINE_TK]] which corresponds to the backslash character
and simply tells us that the string continues on the next line in the file).
<<Cascades2 lexer: public>>=
public :: operator (==)
<<Cascades2 lexer: interfaces>>=
interface operator (==)
module procedure dag_token_eq_dag_token
module procedure dag_string_eq_dag_string
module procedure dag_token_eq_dag_string
module procedure dag_string_eq_dag_token
module procedure dag_token_eq_char_string
module procedure char_string_eq_dag_token
module procedure dag_string_eq_char_string
module procedure char_string_eq_dag_string
end interface operator (==)
@ %def interfaces
<<Cascades2 lexer: procedures>>=
elemental function dag_token_eq_dag_token (token1, token2) result (flag)
type (dag_token_t), intent (in) :: token1, token2
logical :: flag
flag = (token1%type == token2%type) .and. &
(token1%char_len == token2%char_len) .and. &
(token1%bincode == token2%bincode) .and. &
(token1%index == token2%index) .and. &
(token1%particle_name == token2%particle_name)
end function dag_token_eq_dag_token
@ %def dag_token_eq_dag_token
<<Cascades2 lexer: procedures>>=
elemental function dag_string_eq_dag_string (string1, string2) result (flag)
type (dag_string_t), intent (in) :: string1, string2
logical :: flag
flag = (string1%char_len == string2%char_len) .and. &
(allocated (string1%t) .eqv. allocated (string2%t))
if (flag) then
if (allocated (string1%t)) flag = all (string1%t == string2%t)
end if
end function dag_string_eq_dag_string
@ %def dag_string_eq_dag_string
<<Cascades2 lexer: procedures>>=
elemental function dag_token_eq_dag_string (dag_token, dag_string) result (flag)
type (dag_token_t), intent (in) :: dag_token
type (dag_string_t), intent (in) :: dag_string
logical :: flag
flag = size (dag_string%t) == 1 .and. &
dag_string%char_len == dag_token%char_len
if (flag) flag = (dag_string%t(1) == dag_token)
end function dag_token_eq_dag_string
@ %def dag_token_eq_dag_string
<<Cascades2 lexer: procedures>>=
elemental function dag_string_eq_dag_token (dag_string, dag_token) result (flag)
type (dag_token_t), intent (in) :: dag_token
type (dag_string_t), intent (in) :: dag_string
logical :: flag
flag = (dag_token == dag_string)
end function dag_string_eq_dag_token
@ %def dag_string_eq_dag_token
<<Cascades2 lexer: procedures>>=
elemental function dag_token_eq_char_string (dag_token, char_string) result (flag)
type (dag_token_t), intent (in) :: dag_token
character (len=*), intent (in) :: char_string
logical :: flag
flag = (char (dag_token) == char_string)
end function dag_token_eq_char_string
@ %def dag_token_eq_char_string
<<Cascades2 lexer: procedures>>=
elemental function char_string_eq_dag_token (char_string, dag_token) result (flag)
type (dag_token_t), intent (in) :: dag_token
character (len=*), intent (in) :: char_string
logical :: flag
flag = (char (dag_token) == char_string)
end function char_string_eq_dag_token
@ %def char_string_eq_dag_token
<<Cascades2 lexer: procedures>>=
elemental function dag_string_eq_char_string (dag_string, char_string) result (flag)
type (dag_string_t), intent (in) :: dag_string
character (len=*), intent (in) :: char_string
logical :: flag
flag = (char (dag_string) == char_string)
end function dag_string_eq_char_string
@ %def dag_string_eq_char_string
<<Cascades2 lexer: procedures>>=
elemental function char_string_eq_dag_string (char_string, dag_string) result (flag)
type (dag_string_t), intent (in) :: dag_string
character (len=*), intent (in) :: char_string
logical :: flag
flag = (char (dag_string) == char_string)
end function char_string_eq_dag_string
@ %def char_string_eq_dag_string
<<Cascades2 lexer: public>>=
public :: operator (/=)
<<Cascades2 lexer: interfaces>>=
interface operator (/=)
module procedure dag_token_ne_dag_token
module procedure dag_string_ne_dag_string
module procedure dag_token_ne_dag_string
module procedure dag_string_ne_dag_token
module procedure dag_token_ne_char_string
module procedure char_string_ne_dag_token
module procedure dag_string_ne_char_string
module procedure char_string_ne_dag_string
end interface operator (/=)
@ %def interfaces
<<Cascades2 lexer: procedures>>=
elemental function dag_token_ne_dag_token (token1, token2) result (flag)
type (dag_token_t), intent (in) :: token1, token2
logical :: flag
flag = .not. (token1 == token2)
end function dag_token_ne_dag_token
@ %def dag_token_ne_dag_token
<<Cascades2 lexer: procedures>>=
elemental function dag_string_ne_dag_string (string1, string2) result (flag)
type (dag_string_t), intent (in) :: string1, string2
logical :: flag
flag = .not. (string1 == string2)
end function dag_string_ne_dag_string
@ %def dag_string_ne_dag_string
<<Cascades2 lexer: procedures>>=
elemental function dag_token_ne_dag_string (dag_token, dag_string) result (flag)
type (dag_token_t), intent (in) :: dag_token
type (dag_string_t), intent (in) :: dag_string
logical :: flag
flag = .not. (dag_token == dag_string)
end function dag_token_ne_dag_string
@ %def dag_token_ne_dag_string
<<Cascades2 lexer: procedures>>=
elemental function dag_string_ne_dag_token (dag_string, dag_token) result (flag)
type (dag_token_t), intent (in) :: dag_token
type (dag_string_t), intent (in) :: dag_string
logical :: flag
flag = .not. (dag_string == dag_token)
end function dag_string_ne_dag_token
@ %def dag_string_ne_dag_token
<<Cascades2 lexer: procedures>>=
elemental function dag_token_ne_char_string (dag_token, char_string) result (flag)
type (dag_token_t), intent (in) :: dag_token
character (len=*), intent (in) :: char_string
logical :: flag
flag = .not. (dag_token == char_string)
end function dag_token_ne_char_string
@ %def dag_token_ne_char_string
<<Cascades2 lexer: procedures>>=
elemental function char_string_ne_dag_token (char_string, dag_token) result (flag)
type (dag_token_t), intent (in) :: dag_token
character (len=*), intent (in) :: char_string
logical :: flag
flag = .not. (char_string == dag_token)
end function char_string_ne_dag_token
@ %def char_string_ne_dag_token
<<Cascades2 lexer: procedures>>=
elemental function dag_string_ne_char_string (dag_string, char_string) result (flag)
type (dag_string_t), intent (in) :: dag_string
character (len=*), intent (in) :: char_string
logical :: flag
flag = .not. (dag_string == char_string)
end function dag_string_ne_char_string
@ %def dag_string_ne_char_string
<<Cascades2 lexer: procedures>>=
elemental function char_string_ne_dag_string (char_string, dag_string) result (flag)
type (dag_string_t), intent (in) :: dag_string
character (len=*), intent (in) :: char_string
logical :: flag
flag = .not. (char_string == dag_string)
end function char_string_ne_dag_string
@ %def char_string_ne_dag_string
@ Convert a [[dag_token]] or [[dag_string]] to character.
<<Cascades2 lexer: public>>=
public :: char
<<Cascades2 lexer: interfaces>>=
interface char
module procedure char_dag_token
module procedure char_dag_string
end interface char
@ %def interfaces
<<Cascades2 lexer: procedures>>=
pure function char_dag_token (dag_token) result (char_string)
type (dag_token_t), intent (in) :: dag_token
character (dag_token%char_len) :: char_string
integer :: i
integer :: name_len
integer :: bc_pos
integer :: n_digits
character (len=9) :: fmt_spec
select case (dag_token%type)
case (EMPTY_TK)
char_string = ""
case (NEW_LINE_TK)
char_string = BACKSLASH_CHAR
case (BLANC_SPACE_TK)
char_string = " "
case (COLON_TK)
char_string = ":"
case (COMMA_TK)
char_string = ","
case (VERTICAL_BAR_TK)
char_string = "|"
case (OPEN_PAR_TK)
char_string = "("
case (CLOSED_PAR_TK)
char_string = ")"
case (OPEN_CURLY_TK)
char_string = "{"
case (CLOSED_CURLY_TK)
char_string = "}"
case (DAG_NODE_TK, DAG_OPTIONS_TK, DAG_COMBINATION_TK)
n_digits = dag_token%char_len - 3
fmt_spec = ""
if (n_digits > 9) then
write (fmt_spec, fmt="(A,I2,A)") "(A,I", n_digits, ",A)"
else
write (fmt_spec, fmt="(A,I1,A)") "(A,I", n_digits, ",A)"
end if
select case (dag_token%type)
case (DAG_NODE_TK)
write (char_string, fmt=fmt_spec) "<N", dag_token%index, ">"
case (DAG_OPTIONS_TK)
write (char_string, fmt=fmt_spec) "<O", dag_token%index, ">"
case (DAG_COMBINATION_TK)
write (char_string, fmt=fmt_spec) "<C", dag_token%index, ">"
end select
case (NODE_TK)
name_len = len_trim (dag_token%particle_name)
char_string = dag_token%particle_name
bc_pos = name_len + 1
char_string(bc_pos:bc_pos) = "["
do i=0, bit_size (dag_token%bincode) - 1
if (btest (dag_token%bincode, i)) then
bc_pos = bc_pos + 1
select case (i)
case (0, 1, 2, 3, 4, 5, 6, 7, 8)
write (char_string(bc_pos:bc_pos), fmt="(I1)") i + 1
case (9)
write (char_string(bc_pos:bc_pos), fmt="(A1)") "A"
case (10)
write (char_string(bc_pos:bc_pos), fmt="(A1)") "B"
case (11)
write (char_string(bc_pos:bc_pos), fmt="(A1)") "C"
end select
bc_pos = bc_pos + 1
if (bc_pos == dag_token%char_len) then
write (char_string(bc_pos:bc_pos), fmt="(A1)") "]"
return
else
write (char_string(bc_pos:bc_pos), fmt="(A1)") "/"
end if
end if
enddo
end select
end function char_dag_token
@ %def char_dag_token
<<Cascades2 lexer: procedures>>=
pure function char_dag_string (dag_string) result (char_string)
type (dag_string_t), intent (in) :: dag_string
character (dag_string%char_len) :: char_string
integer :: pos
integer :: i
char_string = ""
pos = 0
do i=1, size(dag_string%t)
char_string(pos+1:pos+dag_string%t(i)%char_len) = char (dag_string%t(i))
pos = pos + dag_string%t(i)%char_len
enddo
end function char_dag_string
@ %def char_dag_string
@ Remove all tokens which are irrelevant for parsing. These are of type
[[NEW_LINE_TK]], [[BLANC_SPACE_TK]] and [[EMTPY_TK]].
<<Cascades2 lexer: dag string: TBP>>=
procedure :: clean => dag_string_clean
<<Cascades2 lexer: procedures>>=
subroutine dag_string_clean (dag_string)
class (dag_string_t), intent (inout) :: dag_string
type (dag_token_t), dimension(:), allocatable :: tmp_token
integer :: n_keep
integer :: i
n_keep = 0
dag_string%char_len = 0
allocate (tmp_token (size(dag_string%t)))
do i=1, size (dag_string%t)
select case (dag_string%t(i)%type)
case(NEW_LINE_TK, BLANC_SPACE_TK, EMPTY_TK)
case default
n_keep = n_keep + 1
tmp_token(n_keep) = dag_string%t(i)
dag_string%char_len = dag_string%char_len + dag_string%t(i)%char_len
end select
enddo
deallocate (dag_string%t)
allocate (dag_string%t(n_keep))
dag_string%t = tmp_token(:n_keep)
end subroutine dag_string_clean
@ %def dag_string_clean
@ If we operate explicitly on the [[token]] array [[t]] of a [[dag_string]],
the variable [[char_len]] is not automatically modified. It can however be
determined afterwards using the following subroutine.
<<Cascades2 lexer: dag string: TBP>>=
procedure :: update_char_len => dag_string_update_char_len
<<Cascades2 lexer: procedures>>=
subroutine dag_string_update_char_len (dag_string)
class (dag_string_t), intent (inout) :: dag_string
integer :: char_len
integer :: i
char_len = 0
if (allocated (dag_string%t)) then
do i=1, size (dag_string%t)
char_len = char_len + dag_string%t(i)%char_len
enddo
end if
dag_string%char_len = char_len
end subroutine dag_string_update_char_len
@ %def dag_string_update_char_len
@ Append a [[dag_string]] to a [[dag_chain]]. The argument [[char_string]]
is of type [[character]] because the subroutine is used for reading from
the file produced by O'Mega which is first read line by line to a character
variable.
<<Cascades2 lexer: dag chain: TBP>>=
procedure :: append => dag_chain_append_string
<<Cascades2 lexer: procedures>>=
subroutine dag_chain_append_string (dag_chain, char_string)
class (dag_chain_t), intent (inout) :: dag_chain
character (len=*), intent (in) :: char_string
if (.not. associated (dag_chain%first)) then
allocate (dag_chain%first)
dag_chain%last => dag_chain%first
else
allocate (dag_chain%last%next)
dag_chain%last => dag_chain%last%next
end if
dag_chain%last = char_string
dag_chain%char_len = dag_chain%char_len + dag_chain%last%char_len
dag_chain%t_size = dag_chain%t_size + size (dag_chain%last%t)
end subroutine dag_chain_append_string
@ %def dag_chain_append_string
@ Reduce the linked list of [[dag_string]] objects which are attached
to a given [[dag_chain]] object to a single [[dag_string]].
<<Cascades2 lexer: dag chain: TBP>>=
procedure :: compress => dag_chain_compress
<<Cascades2 lexer: procedures>>=
subroutine dag_chain_compress (dag_chain)
class (dag_chain_t), intent (inout) :: dag_chain
type (dag_string_t), pointer :: current
type (dag_string_t), pointer :: remove
integer :: filled_t
current => dag_chain%first
dag_chain%first => null ()
allocate (dag_chain%first)
dag_chain%last => dag_chain%first
dag_chain%first%char_len = dag_chain%char_len
allocate (dag_chain%first%t (dag_chain%t_size))
filled_t = 0
do while (associated (current))
dag_chain%first%t(filled_t+1:filled_t+size(current%t)) = current%t
filled_t = filled_t + size (current%t)
remove => current
current => current%next
deallocate (remove)
enddo
end subroutine dag_chain_compress
@ %def dag_chain_compress
@ Finalizer for [[dag_string_t]].
<<Cascades2 lexer: dag string: TBP>>=
procedure :: final => dag_string_final
<<Cascades2 lexer: procedures>>=
subroutine dag_string_final (dag_string)
class (dag_string_t), intent (inout) :: dag_string
if (allocated (dag_string%t)) deallocate (dag_string%t)
dag_string%next => null ()
end subroutine dag_string_final
@ %def dag_string_final
@ Finalizer for [[dag_chain_t]].
<<Cascades2 lexer: dag chain: TBP>>=
procedure :: final => dag_chain_final
<<Cascades2 lexer: procedures>>=
subroutine dag_chain_final (dag_chain)
class (dag_chain_t), intent (inout) :: dag_chain
type (dag_string_t), pointer :: current
current => dag_chain%first
do while (associated (current))
dag_chain%first => dag_chain%first%next
call current%final ()
deallocate (current)
current => dag_chain%first
enddo
dag_chain%last => null ()
end subroutine dag_chain_final
@ %def dag_chain_final
<<[[cascades2_lexer_ut.f90]]>>=
<<File header>>
module cascades2_lexer_ut
use unit_tests
use cascades2_lexer_uti
<<Standard module head>>
<<Cascades2 lexer: public test>>
contains
<<Cascades2 lexer: test driver>>
end module cascades2_lexer_ut
@ %def cascades2_lexer_ut
@
<<[[cascades2_lexer_uti.f90]]>>=
<<File header>>
module cascades2_lexer_uti
<<Use kinds>>
<<Use strings>>
use numeric_utils
use cascades2_lexer
<<Standard module head>>
<<Cascades2 lexer: test declarations>>
contains
<<Cascades2 lexer: tests>>
end module cascades2_lexer_uti
@ %def cascades2_lexer_uti
@ API: driver for the unit tests below.
<<Cascades2 lexer: public test>>=
public :: cascades2_lexer_test
<<Cascades2 lexer: test driver>>=
subroutine cascades2_lexer_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Cascades2 lexer: execute tests>>
end subroutine cascades2_lexer_test
@ %def cascades2_lexer_test
@
<<Cascades2 lexer: execute tests>>=
call test (cascades2_lexer_1, "cascades2_lexer_1", &
"make phase-space", u, results)
<<Cascades2 lexer: test declarations>>=
public :: cascades2_lexer_1
<<Cascades2 lexer: tests>>=
subroutine cascades2_lexer_1 (u)
integer, intent(in) :: u
integer :: u_in = 8
character (len=300) :: line
integer :: stat
logical :: fail
type (dag_string_t) :: dag_string
write (u, "(A)") "* Test output: cascades2_lexer_1"
write (u, "(A)") "* Purpose: read lines of O'Mega's phase space output, translate"
write (u, "(A)") "* to dag_string, retranslate to character string and"
write (u, "(A)") "* compare"
write (u, "(A)")
open (unit=u_in, file="cascades2_lexer_1.fds", status='old', action='read')
stat = 0
fail = .false.
read (unit=u_in, fmt="(A)", iostat=stat) line
do while (stat == 0 .and. .not. fail)
read (unit=u_in, fmt="(A)", iostat=stat) line
if (stat /= 0) exit
dag_string = line
fail = (char(dag_string) /= line)
enddo
if (fail) then
write (u, "(A)") "* Test result: Test failed!"
else
write (u, "(A)") "* Test result: Test passed"
end if
close (u_in)
write (u, *)
write (u, "(A)") "* Test output end: cascades2_lexer_1"
end subroutine cascades2_lexer_1
@ %def cascades2_lexer_1
@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{An alternative cascades module}
This module might replace the module [[cascades]], which generates
suitable phase space parametrizations and generates the phase space file.
The mappings, as well as the criteria to determine these, do not change.
The advantage of this module is that it makes use of the [[O'Mega]] matrix
element generator which provides the relevant Feynman diagrams (the ones
which can be constructed only from 3-vertices). In principle, the
construction of these diagrams is also one of the tasks of the existing
[[cascades]] module, in which the diagrams would correspond to a set of
cascades. It starts by creating cascades which correspond to the
outgoing particles. These are combined to a new cascade using the
vertices of the model. In this way, since each cascade knows the
daughter cascades from which it is built, complete Feynman diagrams are
represented by sets of cascades, as soon as the existing cascades can be
recombined with the incoming particle(s).
In this module, the Feynman diagrams are represented by the type
[[feyngraph_t]], which represents the Feynman diagrams as a tree of
nodes. The object which contains the necessary kinematical information
to determine mappings, and hence sensible phase space parametrizations
is of another type, called [[kingraph_t]], which is built from a
corresponding [[feyngraph]] object.
There are two types of output which can be produced by [[O'Mega]] and
are potentially relevant here. The first type contains all tree
diagrams for the process under consideration, where each line of the
output corresponds to one Feynman diagram. This output is easy to read,
but can be very large, depending on the number of particles involved in
the process. Moreover, it repeats substructures of the diagrams which
are part of more than one diagram. One could in principle work with
this output and construct a [[feyngraph]] from each line, if allowed,
i.e. if there are only 3-vertices.
The other output contains also all of these Feynman diagrams, but in
a factorized form. This means that the substructures which appear in
several Feynman diagrams, are written only once, if possible. This
leads to a much shorter input file, which speeds up the parsing
process. Furthermore it makes it possible to reconstruct the
[[feyngraphs]] in such a way that the calculations concerning
subdiagrams which reappear in other [[feyngraphs]] have to be
performed only once. This is already the case in the existing
[[cascades]] module but can be exploited more efficiently here
because the possible graphs are well known from the input file, whereas
the [[cascades]] module would create a large number of [[cascades]]
which do not lead to a complete Feynman diagram of the given process.
<<[[cascades2.f90]]>>=
<<File header>>
module cascades2
<<Use kinds>>
use kinds, only: TC, i8
<<Use debug>>
use cascades2_lexer
use sorting
use flavors
use model_data
use iso_varying_string, string_t => varying_string
use io_units
use physics_defs, only: SCALAR, SPINOR, VECTOR, VECTORSPINOR, TENSOR
use phs_forests, only: phs_parameters_t
use diagnostics
use hashes
use cascades, only: phase_space_vanishes, MAX_WARN_RESONANCE
use, intrinsic :: iso_fortran_env, only : input_unit, output_unit, error_unit
use resonances, only: resonance_info_t
use resonances, only: resonance_history_t
use resonances, only: resonance_history_set_t
<<Standard module head>>
<<Cascades2: public>>
<<Cascades2: parameters>>
<<Cascades2: types>>
<<Cascades2: interfaces>>
contains
<<Cascades2: procedures>>
end module cascades2
@ %def cascades2
@
\subsection{Particle properties}
We define a type holding the properties of the particles which are needed
for parsing and finding the phase space parametrizations and mappings.
The properties of all particles which appear in the parsed
Feynman diagrams for the given process will be stored in a central place,
and only pointers to these objects are used.
<<Cascades2: types>>=
type :: part_prop_t
character (len=LABEL_LEN) :: particle_label
integer :: pdg = 0
real(default) :: mass = 0.
real :: width = 0.
integer :: spin_type = 0
logical :: is_vector = .false.
logical :: empty = .true.
type (part_prop_t), pointer :: anti => null ()
type (string_t) :: tex_name
contains
<<Cascades2: part prop: TBP>>
end type part_prop_t
@ %def part_prop_t
@ The [[particle_label]] in [[part_prop_t]] is simply the particle name
(e.g. 'W+'). The corresponding variable in the type [[f_node_t]] contains
some additional information related to the external momenta, see below.
The length of the [[character]] variable is fixed as:
<<Cascades2: parameters>>=
integer, parameter :: LABEL_LEN=30
@ %def LABEL_LEN
<<Cascades2: part prop: TBP>>=
procedure :: final => part_prop_final
<<Cascades2: procedures>>=
subroutine part_prop_final (part)
class(part_prop_t), intent(inout) :: part
part%anti => null ()
end subroutine part_prop_final
@ %def part_prop_final
@
\subsection{The mapping modes}
The possible mappings are essentially the same as in [[cascades]], but we
introduce in addition the mapping constant [[NON_RESONANT]], which does
not refer to a new mapping; it corresponds to the nonresonant version of
a potentially resonant particle (or [[k_node]]). This becomes relevant
when we compare [[k_nodes]] to eliminate equivalences.
<<Cascades2: parameters>>=
integer, parameter :: &
& NONRESONANT = -2, EXTERNAL_PRT = -1, &
& NO_MAPPING = 0, S_CHANNEL = 1, T_CHANNEL = 2, U_CHANNEL = 3, &
& RADIATION = 4, COLLINEAR = 5, INFRARED = 6, &
& STEP_MAPPING_E = 11, STEP_MAPPING_H = 12, &
& ON_SHELL = 99
@ %def NONRESONANT EXTERNAL_PRT
@ %def NO_MAPPING S_CHANNEL T_CHANNEL U_CHANNEL
@ %def RADIATION COLLINEAR INFRARED
@ %def STEP_MAPPING_E STEP_MAPPING_H
@ %def ON_SHELL
@
\subsection{Grove properties}
The channels or [[kingraphs]] will be grouped in groves, i.e. sets of
channels, which share some characteristic numbers. These numbers are
stored in the following type:
<<Cascades2: types>>=
type :: grove_prop_t
integer :: multiplicity = 0
integer :: n_resonances = 0
integer :: n_log_enhanced = 0
integer :: n_off_shell = 0
integer :: n_t_channel = 0
integer :: res_hash = 0
end type grove_prop_t
@ %def grove_prop_t
@
\subsection{The tree type}
This type contains all the information which is needed to
reconstruct a [[feyngraph]] or [[kingraph]]. We store bincodes, pdg codes
and mappings for all nodes of a valid [[kingraph]]. If we label the
external particles as given in the process definition with integer
numbers representing their position in the process definition, the bincode
would be the number that one obtains by setting the bit at the position
that is given by this number. If we combine two particles/nodes to a third
one (using a three-vertex of the given model), the bincode is the number which
one obtains by setting all the bits which are set for the two particles.
The [[pdg]] and [[mapping]] are simply the pdg-code and mapping at the
position (i.e. propagator or external particle) which is specified by the
corresponding bincode. We use [[tree_t]] not only for completed [[kingraphs]],
but also for all [[k_nodes]], which are a subtree of a [[kingraph]].
<<Cascades2: types>>=
type :: tree_t
integer(TC), dimension(:), allocatable :: bc
integer, dimension(:), allocatable :: pdg
integer, dimension(:), allocatable :: mapping
integer :: n_entries = 0
logical :: keep = .true.
logical :: empty = .true.
contains
<<Cascades2: tree: TBP>>
end type tree_t
@ %def tree_t
<<Cascades2: tree: TBP>>=
procedure :: final => tree_final
<<Cascades2: procedures>>=
subroutine tree_final (tree)
class (tree_t), intent (inout) :: tree
if (allocated (tree%bc)) deallocate (tree%bc)
if (allocated (tree%pdg)) deallocate (tree%pdg)
if (allocated (tree%mapping)) deallocate (tree%mapping)
end subroutine tree_final
@ %def tree_final
<<Cascades2: interfaces>>=
interface assignment (=)
module procedure tree_assign
end interface assignment (=)
<<Cascades2: procedures>>=
subroutine tree_assign (tree1, tree2)
type (tree_t), intent (inout) :: tree1
type (tree_t), intent (in) :: tree2
if (allocated (tree2%bc)) then
allocate (tree1%bc(size(tree2%bc)))
tree1%bc = tree2%bc
end if
if (allocated (tree2%pdg)) then
allocate (tree1%pdg(size(tree2%pdg)))
tree1%pdg = tree2%pdg
end if
if (allocated (tree2%mapping)) then
allocate (tree1%mapping(size(tree2%mapping)))
tree1%mapping = tree2%mapping
end if
tree1%n_entries = tree2%n_entries
tree1%keep = tree2%keep
tree1%empty = tree2%empty
end subroutine tree_assign
@ %def tree_assign
@
\subsection{Add entries to the tree}
The following procedures fill the arrays in [[tree_t]] with entries
resulting from the bincode and mapping assignment.
<<Cascades2: tree: TBP>>=
procedure :: add_entry_from_numbers => tree_add_entry_from_numbers
procedure :: add_entry_from_node => tree_add_entry_from_node
generic :: add_entry => add_entry_from_numbers, add_entry_from_node
@ Here we add a single entry to each of the arrays. This will exclusively
be used for external particles.
<<Cascades2: procedures>>=
subroutine tree_add_entry_from_numbers (tree, bincode, pdg, mapping)
class (tree_t), intent (inout) :: tree
integer(TC), intent (in) :: bincode
integer, intent (in) :: pdg
integer, intent (in) :: mapping
integer :: pos
if (tree%empty) then
allocate (tree%bc(1))
allocate (tree%pdg(1))
allocate (tree%mapping(1))
pos = tree%n_entries + 1
tree%bc(pos) = bincode
tree%pdg(pos) = pdg
tree%mapping(pos) = mapping
tree%n_entries = pos
tree%empty = .false.
end if
end subroutine tree_add_entry_from_numbers
@ %def tree_add_entry_from_numbers
@ Here we merge two existing subtrees and a single entry (bc, pdg and
mapping).
<<Cascades2: procedures>>=
subroutine tree_merge (tree, tree1, tree2, bc, pdg, mapping)
class (tree_t), intent (inout) :: tree
type (tree_t), intent (in) :: tree1, tree2
integer(TC), intent (in) :: bc
integer, intent (in) :: pdg, mapping
integer :: tree_size
integer :: i1, i2
if (tree%empty) then
i1 = tree1%n_entries
i2 = tree1%n_entries + tree2%n_entries
tree_size = tree1%n_entries + tree2%n_entries + 1
allocate (tree%bc (tree_size))
allocate (tree%pdg (tree_size))
allocate (tree%mapping (tree_size))
tree%bc(:i1) = tree1%bc
tree%pdg(:i1) = tree1%pdg
tree%mapping(:i1) = tree1%mapping
tree%bc(i1+1:i2) = tree2%bc
tree%pdg(i1+1:i2) = tree2%pdg
tree%mapping(i1+1:i2) = tree2%mapping
tree%bc(tree_size) = bc
tree%pdg(tree_size) = pdg
tree%mapping(tree_size) = mapping
tree%n_entries = tree_size
tree%empty = .false.
end if
end subroutine tree_merge
@ %def tree_merge
@ Here we add entries to a tree for a given [[k_node]], which means that
we first have to determine whether the node is external or internal.
The arrays are sorted after the entries have been added (see below for
details).
<<Cascades2: procedures>>=
subroutine tree_add_entry_from_node (tree, node)
class (tree_t), intent (inout) :: tree
type (k_node_t), intent (in) :: node
integer :: pdg
if (node%t_line) then
pdg = abs (node%particle%pdg)
else
pdg = node%particle%pdg
end if
if (associated (node%daughter1) .and. &
associated (node%daughter2)) then
call tree_merge (tree, node%daughter1%subtree, &
node%daughter2%subtree, node%bincode, &
node%particle%pdg, node%mapping)
else
call tree_add_entry_from_numbers (tree, node%bincode, &
node%particle%pdg, node%mapping)
end if
call tree%sort ()
end subroutine tree_add_entry_from_node
@ %def tree_add_entry_from_node
@ For a well-defined order of the elements of the arrays in [[tree_t]],
the elements can be sorted. The bincodes (entries of [[bc]]) are
simply ordered by size, the [[pdg]] and [[mapping]] entries go to the
positions of the corresponding [[bc]] values.
<<Cascades2: tree: TBP>>=
procedure :: sort => tree_sort
<<Cascades2: procedures>>=
subroutine tree_sort (tree)
class (tree_t), intent (inout) :: tree
integer(TC), dimension(size(tree%bc)) :: bc_tmp
integer, dimension(size(tree%pdg)) :: pdg_tmp, mapping_tmp
integer, dimension(1) :: pos
integer :: i
bc_tmp = tree%bc
pdg_tmp = tree%pdg
mapping_tmp = tree%mapping
do i = size(tree%bc),1,-1
pos = maxloc (bc_tmp)
tree%bc(i) = bc_tmp (pos(1))
tree%pdg(i) = pdg_tmp (pos(1))
tree%mapping(i) = mapping_tmp (pos(1))
bc_tmp(pos(1)) = 0
end do
end subroutine tree_sort
@ %def tree_sort
@
\subsection{Graph types}
We define an abstract type which will give rise to two different types:
The type [[feyngraph_t]] contains the pure information of the
corresponding Feynman diagram, but also a list of objects of the
[[kingraph]] type which contain the kinematically relevant data for the
mapping calculation as well as the mappings themselves. Every graph
should have an index which is unique. Graphs which are not needed any
more can be disabled by setting the [[keep]] variable to [[false]].
<<Cascades2: types>>=
type, abstract :: graph_t
integer :: index = 0
integer :: n_nodes = 0
logical :: keep = .true.
end type graph_t
@ %def graph_t
@ This is the type representing the Feynman diagrams which are read from
an input file created by O'Mega. It is a tree of nodes, which we call
[[f_nodes]], so that [[feyngraph_t]] contains a pointer to the root of
this tree, and each node can have two daughter nodes. The case of only
one associated daughter should never appear, because in the method of
phase space parametrization which is used here, we combine always two
particle momenta to a third one. The [[feyngraphs]] will be arranged in
a linked list. This is why we have a pointer to the next graph. The
[[kingraphs]] on the other hand are arranged in linked lists which are
attached to the corresponding [[feyngraph]]. In general, a [[feyngraph]]
can give rise to more than one [[kingraph]] because we make a copy
every time a particle can be resonant, so that in the copy we keep
the particle nonresonant.
<<Cascades2: types>>=
type, extends (graph_t) :: feyngraph_t
type (string_t) :: omega_feyngraph_output
type (f_node_t), pointer :: root => null ()
type (feyngraph_t), pointer :: next => null()
type (kingraph_t), pointer :: kin_first => null ()
type (kingraph_t), pointer :: kin_last => null ()
contains
<<Cascades2: feyngraph: TBP>>
end type feyngraph_t
@ %def feyngraph_t
@ A container for a pointer of type [[feyngraph_t]]. This is used to
realize arrays of these pointers.
<<Cascades2: types>>=
type :: feyngraph_ptr_t
type (feyngraph_t), pointer :: graph => null ()
end type feyngraph_ptr_t
@ %def feyngraph_ptr_t
@
The length of a string describing a Feynman diagram which is produced by
O'Mega is fixed by the parameter
<<Cascades2: parameters>>=
integer, parameter :: FEYNGRAPH_LEN=300
@ %def feyngraph_len
<<Cascades2: feyngraph: TBP>>=
procedure :: final => feyngraph_final
<<Cascades2: procedures>>=
subroutine feyngraph_final (graph)
class(feyngraph_t), intent(inout) :: graph
type (kingraph_t), pointer :: current
graph%root => null ()
graph%kin_last => null ()
do while (associated (graph%kin_first))
current => graph%kin_first
graph%kin_first => graph%kin_first%next
call current%final ()
deallocate (current)
enddo
end subroutine feyngraph_final
@ %def feyngraph_final
This is the type of graph which is used to find the phase space channels,
or in other words, each kingraph could correspond to a channel, if it is
not eliminated for kinematical reasons or due to an equivalence. For the
linked list which is attached to the corresponding [[feyngraph]], we
need the [[next]] pointer, whereas [[grove_next]] points to the next
[[kingraph]] within a grove. The information which is relevant for the
specification of a channel is stored in [[tree]]. We use [[grove_prop]]
to sort the [[kingraph]] in a grove in which all [[kingraphs]] are
characterized by the numbers contained in [[grove_prop]]. Later these
groves are further subdevided using the resonance hash. A [[kingraph]]
which is constructed directly from the output of O'Mega, is not
[[inverse]]. In this case the first incoming particle is the root ofthe
tree. In a scattering process, we can also construct a [[kingraph]]
where the root of the tree is the second incoming particle. In this
case the value of [[inverse]] is [[.true.]].
<<Cascades2: types>>=
type, extends (graph_t) :: kingraph_t
type (k_node_t), pointer :: root => null ()
type (kingraph_t), pointer :: next => null()
type (kingraph_t), pointer :: grove_next => null ()
type (tree_t) :: tree
type (grove_prop_t) :: grove_prop
logical :: inverse = .false.
integer :: prc_component = 0
contains
<<Cascades2: kingraph: TBP>>
end type kingraph_t
@ %def kingraph_t
@ Another container for a pointer to emulate arrays of pointers:
<<Cascades2: types>>=
type :: kingraph_ptr_t
type (kingraph_t), pointer :: graph => null ()
end type kingraph_ptr_t
@ %def kingraph_ptr_t
@
<<Cascades2: kingraph: TBP>>=
procedure :: final => kingraph_final
<<Cascades2: procedures>>=
subroutine kingraph_final (graph)
class(kingraph_t), intent(inout) :: graph
graph%root => null ()
graph%next => null ()
graph%grove_next => null ()
call graph%tree%final ()
end subroutine kingraph_final
@ %def kingraph_final
@
\subsection{The node types}
We define an abstract type containing variables which are needed for
[[f_node_t]] as well as [[k_node_t]]. We say that a node is on the
t-line if it lies between the two nodes which correspond to the two
incoming particles. [[incoming]] and [[tline]] are used only for
scattering processes and remain [[.false.]] in decay processes. The
variable [[n_subtree_nodes]] holds the number of nodes (including the
node itself) of the subtree of which the node is the root.
<<Cascades2: types>>=
type, abstract :: node_t
type (part_prop_t), pointer :: particle => null ()
logical :: incoming = .false.
logical :: t_line = .false.
integer :: index = 0
logical :: keep = .true.
integer :: n_subtree_nodes = 1
end type node_t
@ %def node_t
@ We use two different list types for the different kinds of nodes. We
therefore start with an abstract type:
<<Cascades2: types>>=
type, abstract :: list_t
integer :: n_entries = 0
end type list_t
@ %def list_t
@ Since the contents of the lists are different, we introduce two
different entry types. Since the trees of nodes use pointers, the nodes
should only be allocated by a type-bound procedure of the corresponding
list type, such that we can keep track of all nodes, eventually reuse
and in the end deallocate nodes correctly, without forgetting any nodes.
Here is the type for the [[k_nodes]]. The list is a linked list. We want
to reuse (recycle) the [[k_nodes]] which are neither [[incoming]] nore
[[t_line]].
<<Cascades2: types>>=
type :: k_node_entry_t
type (k_node_t), pointer :: node => null ()
type (k_node_entry_t), pointer :: next => null ()
logical :: recycle = .false.
contains
<<Cascades2: k node entry: TBP>>
end type k_node_entry_t
@ %def k_node_entry_t
<<Cascades2: k node entry: TBP>>=
procedure :: final => k_node_entry_final
<<Cascades2: procedures>>=
subroutine k_node_entry_final (entry)
class(k_node_entry_t), intent(inout) :: entry
if (associated (entry%node)) then
call entry%node%final
deallocate (entry%node)
end if
entry%next => null ()
end subroutine k_node_entry_final
@ %def k_node_entry_final
<<Cascades2: k node entry: TBP>>=
procedure :: write => k_node_entry_write
<<Cascades2: procedures>>=
subroutine k_node_entry_write (k_node_entry, u)
class (k_node_entry_t), intent (in) :: k_node_entry
integer, intent (in) :: u
end subroutine k_node_entry_write
@ %def k_node_entry_write
@ Here is the list type for [[k_nodes]]. A [[k_node_list]] can be
declared to be an observer. In this case it does not create any nodes by
itself, but the entries set their pointers to existing nodes. In this
way we can use the list structure and the type bound procedures for
existing nodes.
<<Cascades2: types>>=
type, extends (list_t) :: k_node_list_t
type (k_node_entry_t), pointer :: first => null ()
type (k_node_entry_t), pointer :: last => null ()
integer :: n_recycle
logical :: observer = .false.
contains
<<Cascades2: k node list: TBP>>
end type k_node_list_t
@ %def k_node_list_t
<<Cascades2: k node list: TBP>>=
procedure :: final => k_node_list_final
<<Cascades2: procedures>>=
subroutine k_node_list_final (list)
class(k_node_list_t), intent(inout) :: list
type (k_node_entry_t), pointer :: current
do while (associated (list%first))
current => list%first
list%first => list%first%next
if (list%observer) current%node => null ()
call current%final ()
deallocate (current)
enddo
end subroutine k_node_list_final
@ %def k_node_list_final
@ The [[f_node_t]] type contains the [[particle_label]] variable which is
extracted from the input file. It consists not only of the particle
name, but also of some numbers in brackets. These numbers indicate which
external particles are part of the subtree of this node. The [[f_node]]
contains also a list of [[k_nodes]]. Therefore, if the nodes are not
[[incoming]] or [[t_line]], the mapping calculations for these
[[k_nodes]] which can appear in several [[kingraphs]] have to be
performed only once.
<<Cascades2: types>>=
type, extends (node_t) :: f_node_t
type (f_node_t), pointer :: daughter1 => null ()
type (f_node_t), pointer :: daughter2 => null ()
character (len=LABEL_LEN) :: particle_label
type (k_node_list_t) :: k_node_list
contains
<<Cascades2: f node: TBP>>
end type f_node_t
@ %def f_node_t
@ The finalizer nullifies the daughter pointers, since they are
deallocated, like the [[f_node]] itself, with the finalizer of the
[[f_node_list]].
<<Cascades2: f node: TBP>>=
procedure :: final => f_node_final
<<Cascades2: procedures>>=
recursive subroutine f_node_final (node)
class(f_node_t), intent(inout) :: node
call node%k_node_list%final ()
node%daughter1 => null ()
node%daughter2 => null ()
end subroutine f_node_final
@ %def f_node_final
@ Finaliser for [[f_node_entry]].
<<Cascades2: f node entry: TBP>>=
procedure :: final => f_node_entry_final
<<Cascades2: procedures>>=
subroutine f_node_entry_final (entry)
class(f_node_entry_t), intent(inout) :: entry
if (associated (entry%node)) then
call entry%node%final ()
deallocate (entry%node)
end if
entry%next => null ()
end subroutine f_node_entry_final
@ %def f_node_entry_final
@ Set index if not yet done, i.e. if it is zero.
<<Cascades2: f node: TBP>>=
procedure :: set_index => f_node_set_index
<<Cascades2: procedures>>=
subroutine f_node_set_index (f_node)
class (f_node_t), intent (inout) :: f_node
integer, save :: counter = 0
if (f_node%index == 0) then
counter = counter + 1
f_node%index = counter
end if
end subroutine f_node_set_index
@ %def f_node_set_index
@
Type for the nodes of the tree (lines of the Feynman diagrams). We also need a type containing a
pointer to a node, which is needed for creating arrays of pointers. This will be used for scattering
processes where we can take either the first or the second particle to be the root of the tree. Since
we need both cases for the calculations and O'Mega only gives us one of these, we have to perform a
transformation of the graph in which some nodes (on the line which we hereafter call t-line) need
to know their mother and sister nodes, which become their daughters within this transformation.
<<Cascades2: types>>=
type :: f_node_ptr_t
type (f_node_t), pointer :: node => null ()
contains
<<Cascades2: f node ptr: TBP>>
end type f_node_ptr_t
@ %def f_node_ptr_t
<<Cascades2: f node ptr: TBP>>=
procedure :: final => f_node_ptr_final
<<Cascades2: procedures>>=
subroutine f_node_ptr_final (f_node_ptr)
class (f_node_ptr_t), intent (inout) :: f_node_ptr
f_node_ptr%node => null ()
end subroutine f_node_ptr_final
@ %def f_node_ptr_final
<<Cascades2: interfaces>>=
interface assignment (=)
module procedure f_node_ptr_assign
end interface assignment (=)
<<Cascades2: procedures>>=
subroutine f_node_ptr_assign (ptr1, ptr2)
type (f_node_ptr_t), intent (out) :: ptr1
type (f_node_ptr_t), intent (in) :: ptr2
ptr1%node => ptr2%node
end subroutine f_node_ptr_assign
@ %def f_node_ptr_assign
@
<<Cascades2: types>>=
type :: k_node_ptr_t
type (k_node_t), pointer :: node => null ()
end type k_node_ptr_t
@ %def k_node_ptr_t
@
<<Cascades2: types>>=
type, extends (node_t) :: k_node_t
type (k_node_t), pointer :: daughter1 => null ()
type (k_node_t), pointer :: daughter2 => null ()
type (k_node_t), pointer :: inverse_daughter1 => null ()
type (k_node_t), pointer :: inverse_daughter2 => null ()
type (f_node_t), pointer :: f_node => null ()
type (tree_t) :: subtree
real (default) :: ext_mass_sum = 0.
real (default) :: effective_mass = 0.
logical :: resonant = .false.
logical :: on_shell = .false.
logical :: log_enhanced = .false.
integer :: mapping = NO_MAPPING
integer(TC) :: bincode = 0
logical :: mapping_assigned = .false.
logical :: is_nonresonant_copy = .false.
logical :: subtree_checked = .false.
integer :: n_off_shell = 0
integer :: n_log_enhanced = 0
integer :: n_resonances = 0
integer :: multiplicity = 0
integer :: n_t_channel = 0
integer :: f_node_index = 0
contains
<<Cascades2: k node: TBP>>
end type k_node_t
@ %def k_node_t
@
Subroutine for [[k_node]] assignment.
<<Cascades2: interfaces>>=
interface assignment (=)
module procedure k_node_assign
end interface assignment (=)
<<Cascades2: procedures>>=
subroutine k_node_assign (k_node1, k_node2)
type (k_node_t), intent (inout) :: k_node1
type (k_node_t), intent (in) :: k_node2
k_node1%f_node => k_node2%f_node
k_node1%particle => k_node2%particle
k_node1%incoming = k_node2%incoming
k_node1%t_line = k_node2%t_line
k_node1%keep = k_node2%keep
k_node1%n_subtree_nodes = k_node2%n_subtree_nodes
k_node1%ext_mass_sum = k_node2%ext_mass_sum
k_node1%effective_mass = k_node2%effective_mass
k_node1%resonant = k_node2%resonant
k_node1%on_shell = k_node2%on_shell
k_node1%log_enhanced = k_node2%log_enhanced
k_node1%mapping = k_node2%mapping
k_node1%bincode = k_node2%bincode
k_node1%mapping_assigned = k_node2%mapping_assigned
k_node1%is_nonresonant_copy = k_node2%is_nonresonant_copy
k_node1%n_off_shell = k_node2%n_off_shell
k_node1%n_log_enhanced = k_node2%n_log_enhanced
k_node1%n_resonances = k_node2%n_resonances
k_node1%multiplicity = k_node2%multiplicity
k_node1%n_t_channel = k_node2%n_t_channel
k_node1%f_node_index = k_node2%f_node_index
end subroutine k_node_assign
@ %def k_node_assign
@ The finalizer of [[k_node_t]] nullifies all pointers to nodes, since the
deallocation of these nodes takes place in the finalizer of the list by which
they were created.
<<Cascades2: k node: TBP>>=
procedure :: final => k_node_final
<<Cascades2: procedures>>=
recursive subroutine k_node_final (k_node)
class(k_node_t), intent(inout) :: k_node
k_node%daughter1 => null ()
k_node%daughter2 => null ()
k_node%inverse_daughter1 => null ()
k_node%inverse_daughter2 => null ()
k_node%f_node => null ()
end subroutine k_node_final
@ %def k_node_final
@ Set an index to a [[k_node]], if not yet done, i.e. if it is zero. The
indices are simply positive integer numbers starting from 1.
<<Cascades2: k node: TBP>>=
procedure :: set_index => k_node_set_index
<<Cascades2: procedures>>=
subroutine k_node_set_index (k_node)
class (k_node_t), intent (inout) :: k_node
integer, save :: counter = 0
if (k_node%index == 0) then
counter = counter + 1
k_node%index = counter
end if
end subroutine k_node_set_index
@ %def k_node_set_index
@ The process type (decay or scattering) is given by an integer which is
equal to the number of incoming particles.
<<Cascades2: public>>=
public :: DECAY, SCATTERING
<<Cascades2: parameters>>=
integer, parameter :: DECAY=1, SCATTERING=2
@ %def decay scattering
@ The entries of the [[f_node_list]] contain the substring of the input
file from which the node's subtree will be constructed (or a modified
string containing placeholders for substrings). We use the
length of this string for fast comparison to find the nodes in the
[[f_node_list]] which we want to reuse.
<<Cascades2: types>>=
type :: f_node_entry_t
character (len=FEYNGRAPH_LEN) :: subtree_string
integer :: string_len = 0
type (f_node_t), pointer :: node => null ()
type (f_node_entry_t), pointer :: next => null ()
integer :: subtree_size = 0
contains
<<Cascades2: f node entry: TBP>>
end type f_node_entry_t
@ %def f_node_entry_t
@ A write method for [[f_node_entry]].
<<Cascades2: f node entry: TBP>>=
procedure :: write => f_node_entry_write
<<Cascades2: procedures>>=
subroutine f_node_entry_write (f_node_entry, u)
class (f_node_entry_t), intent (in) :: f_node_entry
integer, intent (in) :: u
write (unit=u, fmt='(A)') trim(f_node_entry%subtree_string)
end subroutine f_node_entry_write
@ %def f_node_entry_write
<<Cascades2: interfaces>>=
interface assignment (=)
module procedure f_node_entry_assign
end interface assignment (=)
<<Cascades2: procedures>>=
subroutine f_node_entry_assign (entry1, entry2)
type (f_node_entry_t), intent (out) :: entry1
type (f_node_entry_t), intent (in) :: entry2
entry1%node => entry2%node
entry1%subtree_string = entry2%subtree_string
entry1%string_len = entry2%string_len
entry1%subtree_size = entry2%subtree_size
end subroutine f_node_entry_assign
@ %def f_node_entry_assign
@ This is the list type for [[f_nodes]]. The variable [[max_tree_size]]
is the number of nodes which appear in a complete graph.
<<Cascades2: types>>=
type, extends (list_t) :: f_node_list_t
type (f_node_entry_t), pointer :: first => null ()
type (f_node_entry_t), pointer :: last => null ()
type (k_node_list_t), pointer :: k_node_list => null ()
integer :: max_tree_size = 0
contains
<<Cascades2: f node list: TBP>>
end type f_node_list_t
@ %def f_node_list_t
@ Add an entry to the [[f_node_list]]. If the node might be reused, we check first
using the [[subtree_string]] if there is already a node in the list which
is the root of exactly the same subtree. Otherwise we add an entry to the
list and allocate the node. In both cases we return a pointer to the node
which allows to access the node.
<<Cascades2: f node list: TBP>>=
procedure :: add_entry => f_node_list_add_entry
<<Cascades2: procedures>>=
subroutine f_node_list_add_entry (list, subtree_string, ptr_to_node, &
recycle, subtree_size)
class (f_node_list_t), intent (inout) :: list
character (len=*), intent (in) :: subtree_string
type (f_node_t), pointer, intent (out) :: ptr_to_node
logical, intent (in) :: recycle
integer, intent (in), optional :: subtree_size
type (f_node_entry_t), pointer :: current
type (f_node_entry_t), pointer :: second
integer :: subtree_len
ptr_to_node => null ()
if (recycle) then
subtree_len = len_trim (subtree_string)
current => list%first
do while (associated (current))
if (present (subtree_size)) then
if (current%subtree_size /= subtree_size) exit
end if
if (current%string_len == subtree_len) then
if (trim (current%subtree_string) == trim (subtree_string)) then
ptr_to_node => current%node
exit
end if
end if
current => current%next
enddo
end if
if (.not. associated (ptr_to_node)) then
if (list%n_entries == 0) then
allocate (list%first)
list%last => list%first
else
second => list%first
list%first => null ()
allocate (list%first)
list%first%next => second
end if
list%n_entries = list%n_entries + 1
list%first%subtree_string = trim(subtree_string)
list%first%string_len = subtree_len
if (present (subtree_size)) list%first%subtree_size = subtree_size
allocate (list%first%node)
call list%first%node%set_index ()
ptr_to_node => list%first%node
end if
end subroutine f_node_list_add_entry
@ %def f_node_list_add_entry
@ A write method for debugging.
<<Cascades2: f node list: TBP>>=
procedure :: write => f_node_list_write
<<Cascades2: procedures>>=
subroutine f_node_list_write (f_node_list, u)
class (f_node_list_t), intent (in) :: f_node_list
integer, intent (in) :: u
type (f_node_entry_t), pointer :: current
integer :: pos = 0
current => f_node_list%first
do while (associated (current))
pos = pos + 1
write (unit=u, fmt='(A,I10)') 'entry #: ', pos
call current%write (u)
write (unit=u, fmt=*)
current => current%next
enddo
end subroutine f_node_list_write
@ %def f_node_list_write
<<Cascades2: interfaces>>=
interface assignment (=)
module procedure k_node_entry_assign
end interface assignment (=)
<<Cascades2: procedures>>=
subroutine k_node_entry_assign (entry1, entry2)
type (k_node_entry_t), intent (out) :: entry1
type (k_node_entry_t), intent (in) :: entry2
entry1%node => entry2%node
entry1%recycle = entry2%recycle
end subroutine k_node_entry_assign
@ %def k_node_entry_assign
@ Add an entry to the [[k_node_list]]. We have to specify if the
node can be reused. The check for existing reusable nodes happens with
[[k_node_list_get_nodes]] (see below).
<<Cascades2: k node list: TBP>>=
procedure :: add_entry => k_node_list_add_entry
<<Cascades2: procedures>>=
recursive subroutine k_node_list_add_entry (list, ptr_to_node, recycle)
class (k_node_list_t), intent (inout) :: list
type (k_node_t), pointer, intent (out) :: ptr_to_node
logical, intent (in) :: recycle
if (list%n_entries == 0) then
allocate (list%first)
list%last => list%first
else
allocate (list%last%next)
list%last => list%last%next
end if
list%n_entries = list%n_entries + 1
list%last%recycle = recycle
allocate (list%last%node)
call list%last%node%set_index ()
ptr_to_node => list%last%node
end subroutine k_node_list_add_entry
@ %def k_node_list_add_entry
@ We need a similar subroutine for adding only a pointer to a list. This
is needed for a [[k_node_list]] which is only an observer, i.e. it does
not create any nodes by itself.
<<Cascades2: k node list: TBP>>=
procedure :: add_pointer => k_node_list_add_pointer
<<Cascades2: procedures>>=
subroutine k_node_list_add_pointer (list, ptr_to_node, recycle)
class (k_node_list_t), intent (inout) :: list
type (k_node_t), pointer, intent (in) :: ptr_to_node
logical, optional, intent (in) :: recycle
logical :: rec
if (present (recycle)) then
rec = recycle
else
rec = .false.
end if
if (list%n_entries == 0) then
allocate (list%first)
list%last => list%first
else
allocate (list%last%next)
list%last => list%last%next
end if
list%n_entries = list%n_entries + 1
list%last%recycle = rec
list%last%node => ptr_to_node
end subroutine k_node_list_add_pointer
@ %def k_node_list_add_pointer
@ The [[k_node_list]] can also be used to collect [[k_nodes]] which belong to
different [[f_nodes]] in order to compare these. This is done only for nodes
which have the same number of subtree nodes. We compare all nodes of the
list with each other (as long as the node is not deactivated, i.e. if
the [[keep]] variable is set to [[.true.]]) using the subroutine
[[subtree_select]]. If it turns out that two nodes are equivalent, we
keep only one of them. The term equivalent in this module refers to trees
or subtrees which differ in the pdg codes at positions where
the trivial mapping is used ([[NO_MAPPING]] or [[NON_RESONANT]]) so that
the mass of the particle does not matter. Depending on the available
couplings, two equivalent subtrees could eventually lead to the same phase
space channels, which is why only one of them is kept.
<<Cascades2: k node list: TBP>>=
procedure :: check_subtree_equivalences => k_node_list_check_subtree_equivalences
<<Cascades2: procedures>>=
subroutine k_node_list_check_subtree_equivalences (list, model)
class (k_node_list_t), intent (inout) :: list
type (model_data_t), intent (in) :: model
type (k_node_ptr_t), dimension (:), allocatable :: set
type (k_node_entry_t), pointer :: current
integer :: pos
integer :: i,j
if (list%n_entries == 0) return
allocate (set (list%n_entries))
current => list%first
pos = 0
do while (associated (current))
pos = pos + 1
set(pos)%node => current%node
current => current%next
enddo
do i=1, list%n_entries
if (set(i)%node%keep) then
do j=i+1, list%n_entries
if (set(j)%node%keep) then
if (set(i)%node%bincode == set(j)%node%bincode) then
call subtree_select (set(i)%node%subtree,set(j)%node%subtree, model)
if (.not. set(i)%node%subtree%keep) then
set(i)%node%keep = .false.
exit
else if (.not. set(j)%node%subtree%keep) then
set(j)%node%keep = .false.
end if
end if
end if
enddo
end if
enddo
deallocate (set)
end subroutine k_node_list_check_subtree_equivalences
@ %def k_node_list_check_subtree_equivalences
@ This subroutine is used to obtain all [[k_nodes]] of a [[k_node_list]]
which can be recycled and are not disabled for some reason. We pass an
allocatable array of the type [[k_node_ptr_t]] which will be allocated
if there are any such nodes in the list and the pointers will be
associated with these nodes.
<<Cascades2: k node list: TBP>>=
procedure :: get_nodes => k_node_list_get_nodes
<<Cascades2: procedures>>=
subroutine k_node_list_get_nodes (list, nodes)
class (k_node_list_t), intent (inout) :: list
type (k_node_ptr_t), dimension(:), allocatable, intent (out) :: nodes
integer :: n_nodes
integer :: pos
type (k_node_entry_t), pointer :: current, garbage
n_nodes = 0
current => list%first
do while (associated (current))
if (current%recycle .and. current%node%keep) n_nodes = n_nodes + 1
current => current%next
enddo
if (n_nodes /= 0) then
pos = 1
allocate (nodes (n_nodes))
do while (associated (list%first) .and. .not. list%first%node%keep)
garbage => list%first
list%first => list%first%next
call garbage%final ()
deallocate (garbage)
enddo
current => list%first
do while (associated (current))
do while (associated (current%next))
if (.not. current%next%node%keep) then
garbage => current%next
current%next => current%next%next
call garbage%final
deallocate (garbage)
else
exit
end if
enddo
if (current%recycle .and. current%node%keep) then
nodes(pos)%node => current%node
pos = pos + 1
end if
current => current%next
enddo
end if
end subroutine k_node_list_get_nodes
@ %def k_node_list_get_nodes
<<Cascades2: f node list: TBP>>=
procedure :: final => f_node_list_final
<<Cascades2: procedures>>=
subroutine f_node_list_final (list)
class (f_node_list_t) :: list
type (f_node_entry_t), pointer :: current
list%k_node_list => null ()
do while (associated (list%first))
current => list%first
list%first => list%first%next
call current%final ()
deallocate (current)
enddo
end subroutine f_node_list_final
@ %def f_node_list_final
@
\subsection{The grove list}
First a type is introduced in order to speed up the comparison of kingraphs
with the purpose to quickly find the graphs that might be equivalent.
This is done solely on the basis of a number (which is given
by the value of [[depth]] in [[compare_tree_t]]) of bincodes, which are
the highest ones that do not belong to external particles.
The highest such value determines the index of the element in the [[entry]]
array of the [[compare_tree]]. The next lower such value determines
the index of the element in the [[entry]] array of this [[entry]], and so
on and so forth. This results in a tree structure where the number of
levels is given by [[depth]] and should not be too large for reasons of
memory.
This is the entry type.
<<Cascades2: types>>=
type :: compare_tree_entry_t
type (compare_tree_entry_t), dimension(:), pointer :: entry => null ()
type (kingraph_ptr_t), dimension(:), allocatable :: graph_entry
contains
<<Cascades2: compare tree entry: TBP>>
end type compare_tree_entry_t
@ %def compare_tree_entry_t
@ This is the tree type.
<<Cascades2: types>>=
type :: compare_tree_t
integer :: depth = 3
type (compare_tree_entry_t), dimension(:), pointer :: entry => null ()
contains
<<Cascades2: compare tree: TBP>>
end type compare_tree_t
@ %def compare_tree_t
@ Finalizers for both types. The one for the entry type has to be recursive.
<<Cascades2: compare tree: TBP>>=
procedure :: final => compare_tree_final
<<Cascades2: procedures>>=
subroutine compare_tree_final (ctree)
class (compare_tree_t), intent (inout) :: ctree
integer :: i
if (associated (ctree%entry)) then
do i=1, size (ctree%entry)
call ctree%entry(i)%final ()
deallocate (ctree%entry)
end do
end if
end subroutine compare_tree_final
@ %def compare_tree_final
<<Cascades2: compare tree entry: TBP>>=
procedure :: final => compare_tree_entry_final
<<Cascades2: procedures>>=
recursive subroutine compare_tree_entry_final (ct_entry)
class (compare_tree_entry_t), intent (inout) :: ct_entry
integer :: i
if (associated (ct_entry%entry)) then
do i=1, size (ct_entry%entry)
call ct_entry%entry(i)%final ()
enddo
deallocate (ct_entry%entry)
else
deallocate (ct_entry%graph_entry)
end if
end subroutine compare_tree_entry_final
@ %def compare_tree_entry_final
@ Check the presence of a graph which is considered as equivalent and
select between the two. If there is no such graph, the current one
is added to the list. First the entry has to be found:
<<Cascades2: compare tree: TBP>>=
procedure :: check_kingraph => compare_tree_check_kingraph
<<Cascades2: procedures>>=
subroutine compare_tree_check_kingraph (ctree, kingraph, model, preliminary)
class (compare_tree_t), intent (inout) :: ctree
type (kingraph_t), intent (inout), pointer :: kingraph
type (model_data_t), intent (in) :: model
logical, intent (in) :: preliminary
integer :: i
integer :: pos
integer(TC) :: sz
integer(TC), dimension(:), allocatable :: identifier
if (.not. associated (ctree%entry)) then
sz = 0_TC
do i = size(kingraph%tree%bc), 1, -1
sz = ior (sz, kingraph%tree%bc(i))
enddo
if (sz > 0) then
allocate (ctree%entry (sz))
else
call msg_bug ("Compare tree could not be created")
end if
end if
allocate (identifier (ctree%depth))
pos = 0
do i = size(kingraph%tree%bc), 1, -1
if (popcnt (kingraph%tree%bc(i)) /= 1) then
pos = pos + 1
identifier(pos) = kingraph%tree%bc(i)
if (pos == ctree%depth) exit
end if
enddo
if (size (identifier) > 1) then
call ctree%entry(identifier(1))%check_kingraph (kingraph, model, &
preliminary, identifier(1), identifier(2:))
else if (size (identifier) == 1) then
call ctree%entry(identifier(1))%check_kingraph (kingraph, model, preliminary)
end if
deallocate (identifier)
end subroutine compare_tree_check_kingraph
@ %def compare_tree_check_kingraph
@ Then the graphs of the entry are checked.
<<Cascades2: compare tree entry: TBP>>=
procedure :: check_kingraph => compare_tree_entry_check_kingraph
<<Cascades2: procedures>>=
recursive subroutine compare_tree_entry_check_kingraph (ct_entry, kingraph, &
model, preliminary, subtree_size, identifier)
class (compare_tree_entry_t), intent (inout) :: ct_entry
type (kingraph_t), pointer, intent (inout) :: kingraph
type (model_data_t), intent (in) :: model
logical, intent (in) :: preliminary
integer, intent (in), optional :: subtree_size
integer, dimension (:), intent (in), optional :: identifier
if (present (identifier)) then
if (.not. associated (ct_entry%entry)) &
allocate (ct_entry%entry(subtree_size))
if (size (identifier) > 1) then
call ct_entry%entry(identifier(1))%check_kingraph (kingraph, &
model, preliminary, identifier(1), identifier(2:))
else if (size (identifier) == 1) then
call ct_entry%entry(identifier(1))%check_kingraph (kingraph, &
model, preliminary)
end if
else
if (allocated (ct_entry%graph_entry)) then
call perform_check
else
allocate (ct_entry%graph_entry(1))
ct_entry%graph_entry(1)%graph => kingraph
end if
end if
contains
subroutine perform_check
integer :: i
logical :: rebuild
rebuild = .true.
do i=1, size(ct_entry%graph_entry)
if (ct_entry%graph_entry(i)%graph%keep) then
if (preliminary .or. &
ct_entry%graph_entry(i)%graph%prc_component /= kingraph%prc_component) then
call kingraph_select (ct_entry%graph_entry(i)%graph, kingraph, model, preliminary)
if (.not. kingraph%keep) then
return
else if (rebuild .and. .not. ct_entry%graph_entry(i)%graph%keep) then
ct_entry%graph_entry(i)%graph => kingraph
rebuild = .false.
end if
end if
end if
enddo
if (rebuild) call rebuild_graph_entry
end subroutine perform_check
subroutine rebuild_graph_entry
type (kingraph_ptr_t), dimension(:), allocatable :: tmp_ptr
integer :: i
integer :: pos
allocate (tmp_ptr(size(ct_entry%graph_entry)+1))
pos = 0
do i=1, size(ct_entry%graph_entry)
pos = pos + 1
tmp_ptr(pos)%graph => ct_entry%graph_entry(i)%graph
enddo
pos = pos + 1
tmp_ptr(pos)%graph => kingraph
deallocate (ct_entry%graph_entry)
allocate (ct_entry%graph_entry (pos))
do i=1, pos
ct_entry%graph_entry(i)%graph => tmp_ptr(i)%graph
enddo
deallocate (tmp_ptr)
end subroutine rebuild_graph_entry
end subroutine compare_tree_entry_check_kingraph
@ %def compare_tree_entry_check_kingraph
@ The grove to which a completed [[kingraph]] will be added is determined by the
entries of [[grove_prop]]. We use another list type (linked list) to
arrange the groves. Each [[grove]] contains again a linked list of
[[kingraphs]].
<<Cascades2: types>>=
type :: grove_t
type (grove_prop_t) :: grove_prop
type (grove_t), pointer :: next => null ()
type (kingraph_t), pointer :: first => null ()
type (kingraph_t), pointer :: last => null ()
type (compare_tree_t) :: compare_tree
contains
<<Cascades2: grove: TBP>>
end type grove_t
@ %def grove_t
@ Container for a pointer of type [[grove_t]]:
<<Cascades2: types>>=
type :: grove_ptr_t
type (grove_t), pointer :: grove => null ()
end type grove_ptr_t
@ %def grove_ptr_t
<<Cascades2: grove: TBP>>=
procedure :: final => grove_final
<<Cascades2: procedures>>=
subroutine grove_final (grove)
class(grove_t), intent(inout) :: grove
grove%first => null ()
grove%last => null ()
grove%next => null ()
end subroutine grove_final
@ %def grove_final
@ This is the list type:
<<Cascades2: types>>=
type :: grove_list_t
type (grove_t), pointer :: first => null ()
contains
<<Cascades2: grove list: TBP>>
end type grove_list_t
@ %def grove_list_t
<<Cascades2: grove list: TBP>>=
procedure :: final => grove_list_final
<<Cascades2: procedures>>=
subroutine grove_list_final (list)
class(grove_list_t), intent(inout) :: list
class(grove_t), pointer :: current
do while (associated (list%first))
current => list%first
list%first => list%first%next
call current%final ()
deallocate (current)
end do
end subroutine grove_list_final
@ %def grove_list_final
@
\subsection{The feyngraph set}
The fundament of the module is the public type [[feyngraph_set_t]]. It
is not only a linked list of all [[feyngraphs]] but contains an array
of all particle properties ([[particle]]), an [[f_node_list]] and a
pointer of the type [[grove_list_t]], since several [[feyngraph_sets]]
can share a common [[grove_list]]. In addition it keeps the data which
unambiguously specifies the process, as well as the model which
provides information which allows us to choose between equivalent
subtrees or complete [[kingraphs]].
<<Cascades2: public>>=
public :: feyngraph_set_t
<<Cascades2: types>>=
type :: feyngraph_set_t
type (model_data_t), pointer :: model => null ()
type(flavor_t), dimension(:,:), allocatable :: flv
integer :: n_in = 0
integer :: n_out = 0
integer :: process_type = DECAY
type (phs_parameters_t) :: phs_par
logical :: fatal_beam_decay = .true.
type (part_prop_t), dimension (:), pointer :: particle => null ()
type (f_node_list_t) :: f_node_list
type (feyngraph_t), pointer :: first => null ()
type (feyngraph_t), pointer :: last => null ()
integer :: n_graphs = 0
type (grove_list_t), pointer :: grove_list => null ()
logical :: use_dag = .true.
type (dag_t), pointer :: dag => null ()
type (feyngraph_set_t), dimension (:), pointer :: fset => null ()
contains
<<Cascades2: feyngraph set: TBP>>
end type feyngraph_set_t
@ %def feyngraph_set_t
@ This final procedure contains calls to all other necessary final
procedures.
<<Cascades2: feyngraph set: TBP>>=
procedure :: final => feyngraph_set_final
<<Cascades2: procedures>>=
recursive subroutine feyngraph_set_final (set)
class(feyngraph_set_t), intent(inout) :: set
class(feyngraph_t), pointer :: current
integer :: i
if (associated (set%fset)) then
do i=1, size (set%fset)
call set%fset(i)%final ()
enddo
deallocate (set%fset)
else
set%particle => null ()
set%grove_list => null ()
end if
set%model => null ()
if (allocated (set%flv)) deallocate (set%flv)
set%last => null ()
do while (associated (set%first))
current => set%first
set%first => set%first%next
call current%final ()
deallocate (current)
end do
if (associated (set%particle)) then
do i = 1, size (set%particle)
call set%particle(i)%final ()
end do
deallocate (set%particle)
end if
if (associated (set%grove_list)) then
if (debug_on) call msg_debug (D_PHASESPACE, "grove_list: final")
call set%grove_list%final ()
deallocate (set%grove_list)
end if
if (debug_on) call msg_debug (D_PHASESPACE, "f_node_list: final")
call set%f_node_list%final ()
if (associated (set%dag)) then
if (debug_on) call msg_debug (D_PHASESPACE, "dag: final")
if (associated (set%dag)) then
call set%dag%final ()
deallocate (set%dag)
end if
end if
end subroutine feyngraph_set_final
@ %def feyngraph_set_final
@
\subsection{Construct the feyngraph set}
We construct the [[feyngraph_set]] from an input file. Therefore we pass
a unit to [[feyngraph_set_build]]. The parsing subroutines are chosen
depending on the value of [[use_dag]]. In the DAG output, which is the one
that is produced by default, we have to work on a string of one line,
where the lenght of this string becomes larger the more particles are
involved in the process. The other output (which is now only used in a
unit test) contains one Feynman diagram per line and each line starts with an open
parenthesis so that we read the file line per line and create a
[[feyngraph]] for every line. Only after this, nodes are created. In both
decay and scattering processes the diagrams are represented like in a decay
process, i.e. in a scattering process one of the incoming particles appears
as an outgoing particle.
<<Cascades2: feyngraph set: TBP>>=
procedure :: build => feyngraph_set_build
<<Cascades2: procedures>>=
subroutine feyngraph_set_build (feyngraph_set, u_in)
class (feyngraph_set_t), intent (inout) :: feyngraph_set
integer, intent (in) :: u_in
integer :: stat = 0
character (len=FEYNGRAPH_LEN) :: omega_feyngraph_output
type (feyngraph_t), pointer :: current_graph
type (feyngraph_t), pointer :: compare_graph
logical :: present
if (feyngraph_set%use_dag) then
allocate (feyngraph_set%dag)
if (.not. associated (feyngraph_set%first)) then
call feyngraph_set%dag%read_string (u_in, feyngraph_set%flv(:,1))
call feyngraph_set%dag%construct (feyngraph_set)
call feyngraph_set%dag%make_feyngraphs (feyngraph_set)
end if
else
if (.not. associated (feyngraph_set%first)) then
read (unit=u_in, fmt='(A)', iostat=stat, advance='yes') omega_feyngraph_output
if (omega_feyngraph_output(1:1) == '(') then
allocate (feyngraph_set%first)
feyngraph_set%first%omega_feyngraph_output = trim(omega_feyngraph_output)
feyngraph_set%last => feyngraph_set%first
feyngraph_set%n_graphs = feyngraph_set%n_graphs + 1
else
call msg_fatal ("Invalid input file")
end if
read (unit=u_in, fmt='(A)', iostat=stat, advance='yes') omega_feyngraph_output
do while (stat == 0)
if (omega_feyngraph_output(1:1) == '(') then
compare_graph => feyngraph_set%first
present = .false.
do while (associated (compare_graph))
if (len_trim(compare_graph%omega_feyngraph_output) &
== len_trim(omega_feyngraph_output)) then
if (compare_graph%omega_feyngraph_output == omega_feyngraph_output) then
present = .true.
exit
end if
end if
compare_graph => compare_graph%next
enddo
if (.not. present) then
allocate (feyngraph_set%last%next)
feyngraph_set%last => feyngraph_set%last%next
feyngraph_set%last%omega_feyngraph_output = trim(omega_feyngraph_output)
feyngraph_set%n_graphs = feyngraph_set%n_graphs + 1
end if
read (unit=u_in, fmt='(A)', iostat=stat, advance='yes') omega_feyngraph_output
else
exit
end if
enddo
current_graph => feyngraph_set%first
do while (associated (current_graph))
call feyngraph_construct (feyngraph_set, current_graph)
current_graph => current_graph%next
enddo
feyngraph_set%f_node_list%max_tree_size = feyngraph_set%first%n_nodes
end if
end if
end subroutine feyngraph_set_build
@ %def feyngraph_set_build
@ Read the string from the file. The output which is produced by O'Mega
contains the DAG in a factorised form as a long string, distributed over
several lines (in addition, in the case of a scattering process, it
contains a similar string for the same process, but with the other
incoming particle as the root of the tree structure). In general, such a
file can contain many of these strings, belonging to different process
components. Therefore we first have to find the correct position of the
string for the process in question. Therefore we look for a line
containing a pair of colons, in which case the line contains a process
string. Then we check if the process string describes the correct
process, which is done by checking for all the incoming and outgoing
particle names. If the process is correct, the dag output should start
in the following line. As long as we do not find the correct process
string, we continue searching. If we reach the end of the file, we
rewind the unit once, and repeat searching. If the process is still not
found, there must be some sort of error.
<<Cascades2: dag: TBP>>=
procedure :: read_string => dag_read_string
<<Cascades2: procedures>>=
subroutine dag_read_string (dag, u_in, flv)
class (dag_t), intent (inout) :: dag
integer, intent (in) :: u_in
type(flavor_t), dimension(:), intent(in) :: flv
character (len=BUFFER_LEN) :: process_string
logical :: process_found
logical :: rewound
!!! find process string in file
process_found = .false.
rewound = .false.
do while (.not. process_found)
process_string = ""
read (unit=u_in, fmt='(A)') process_string
if (len_trim(process_string) /= 0) then
if (index (process_string, "::") > 0) then
process_found = process_string_match (trim (process_string), flv)
end if
else if (.not. rewound) then
rewind (u_in)
rewound = .true.
else
call msg_bug ("Process string not found in O'Mega input file.")
end if
enddo
call fds_file_get_line (u_in, dag%string)
call dag%string%clean ()
if (.not. allocated (dag%string%t) .or. dag%string%char_len == 0) &
call msg_bug ("Process string not found in O'Mega input file.")
end subroutine dag_read_string
@ %def dag_read_string
@ The output of factorized Feynman diagrams which is created by O'Mega
for a given process could in principle be written to a single line in
the file. This can however lead to different problems with different
compilers as soon as such lines become too long. This is the reason why
the line is cut into smaller pieces. This means that a new line starts
after each vertical bar. For this long string the type [[dag_string_t]]
has been introduced. In order to read the file quickly into such a
[[dag_string]] we use another type, [[dag_chain_t]] which is a linked
list of such [[dag_strings]]. This has the advantage that we do not
have to recreate a new [[dag_string]] for every line which has been
read from file. Only in the end of this operation we compress the
list of strings to a single string, removing useless [[dag_tokens]],
such as blanc space tokens. This subroutine reads all lines starting
from the position in the file the unit is connected to, until no
backslash character is found at the end of a line (the backslash
means that the next line also belongs to the current string).
<<Cascades2: parameters>>=
integer, parameter :: BUFFER_LEN = 1000
integer, parameter :: STACK_SIZE = 100
@ %def BUFFER_LEN STACK_SIZE
<<Cascades2: procedures>>=
subroutine fds_file_get_line (u, string)
integer, intent (in) :: u
type (dag_string_t), intent (out) :: string
type (dag_chain_t) :: chain
integer :: string_size, current_len
character (len=BUFFER_LEN) :: buffer
integer :: fragment_len
integer :: stat
current_len = 0
stat = 0
string_size = 0
do while (stat == 0)
read (unit=u, fmt='(A)', iostat=stat) buffer
if (stat /= 0) exit
fragment_len = len_trim (buffer)
if (fragment_len == 0) then
exit
else if (buffer (fragment_len:fragment_len) == BACKSLASH_CHAR) then
fragment_len = fragment_len - 1
end if
call chain%append (buffer(:fragment_len))
if (buffer(fragment_len+1:fragment_len+1) /= BACKSLASH_CHAR) exit
enddo
if (associated (chain%first)) then
call chain%compress ()
string = chain%first
call chain%final ()
end if
end subroutine fds_file_get_line
@ %def fds_file_get_line
@ We check, if the process string which has been read from file
corresponds to the process for which we want to extract the Feynman
diagrams.
<<Cascades2: procedures>>=
function process_string_match (string, flv) result (match)
character (len=*), intent(in) :: string
type(flavor_t), dimension(:), intent(in) :: flv
logical :: match
integer :: pos
integer :: occurence
integer :: i
pos = 1
match = .false.
do i=1, size (flv)
occurence = index (string(pos:), char(flv(i)%get_name()))
if (occurence > 0) then
pos = pos + occurence
match = .true.
else
match = .false.
exit
end if
enddo
end function process_string_match
@ %def process_string_match
@
\subsection{Particle properties}
This subroutine initializes a model instance with the Standard Model
data. It is only relevant for a unit test.
We do not have to care about the model initialization in this module
because the [[model]] is passed to [[feyngraph_set_generate]] when
it is called.
<<Cascades2: public>>=
public :: init_sm_full_test
<<Cascades2: procedures>>=
subroutine init_sm_full_test (model)
class(model_data_t), intent(out) :: model
type(field_data_t), pointer :: field
integer, parameter :: n_real = 17
integer, parameter :: n_field = 21
integer, parameter :: n_vtx = 56
integer :: i
call model%init (var_str ("SM_vertex_test"), &
n_real, 0, n_field, n_vtx)
call model%init_par (1, var_str ("mZ"), 91.1882_default)
call model%init_par (2, var_str ("mW"), 80.419_default)
call model%init_par (3, var_str ("mH"), 125._default)
call model%init_par (4, var_str ("me"), 0.000510997_default)
call model%init_par (5, var_str ("mmu"), 0.105658389_default)
call model%init_par (6, var_str ("mtau"), 1.77705_default)
call model%init_par (7, var_str ("ms"), 0.095_default)
call model%init_par (8, var_str ("mc"), 1.2_default)
call model%init_par (9, var_str ("mb"), 4.2_default)
call model%init_par (10, var_str ("mtop"), 173.1_default)
call model%init_par (11, var_str ("wtop"), 1.523_default)
call model%init_par (12, var_str ("wZ"), 2.443_default)
call model%init_par (13, var_str ("wW"), 2.049_default)
call model%init_par (14, var_str ("wH"), 0.004143_default)
call model%init_par (15, var_str ("ee"), 0.3079561542961_default)
call model%init_par (16, var_str ("cw"), 8.819013863636E-01_default)
call model%init_par (17, var_str ("sw"), 4.714339240339E-01_default)
i = 0
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("D_QUARK"), 1)
call field%set (spin_type=2, color_type=3, charge_type=-2, isospin_type=-2)
call field%set (name = [var_str ("d")], anti = [var_str ("dbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("U_QUARK"), 2)
call field%set (spin_type=2, color_type=3, charge_type=3, isospin_type=2)
call field%set (name = [var_str ("u")], anti = [var_str ("ubar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("S_QUARK"), 3)
call field%set (spin_type=2, color_type=3, charge_type=-2, isospin_type=-2)
call field%set (mass_data=model%get_par_real_ptr (7))
call field%set (name = [var_str ("s")], anti = [var_str ("sbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("C_QUARK"), 4)
call field%set (spin_type=2, color_type=3, charge_type=3, isospin_type=2)
call field%set (mass_data=model%get_par_real_ptr (8))
call field%set (name = [var_str ("c")], anti = [var_str ("cbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("B_QUARK"), 5)
call field%set (spin_type=2, color_type=3, charge_type=-2, isospin_type=-2)
call field%set (mass_data=model%get_par_real_ptr (9))
call field%set (name = [var_str ("b")], anti = [var_str ("bbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("T_QUARK"), 6)
call field%set (spin_type=2, color_type=3, charge_type=3, isospin_type=2)
call field%set (mass_data=model%get_par_real_ptr (10))
call field%set (width_data=model%get_par_real_ptr (11))
call field%set (name = [var_str ("t")], anti = [var_str ("tbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("E_LEPTON"), 11)
call field%set (spin_type=2)
call field%set (mass_data=model%get_par_real_ptr (4))
call field%set (name = [var_str ("e-")], anti = [var_str ("e+")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("E_NEUTRINO"), 12)
call field%set (spin_type=2, is_left_handed=.true.)
call field%set (name = [var_str ("nue")], anti = [var_str ("nuebar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("MU_LEPTON"), 13)
call field%set (spin_type=2)
call field%set (mass_data=model%get_par_real_ptr (5))
call field%set (name = [var_str ("mu-")], anti = [var_str ("mu+")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("MU_NEUTRINO"), 14)
call field%set (spin_type=2, is_left_handed=.true.)
call field%set (name = [var_str ("numu")], anti = [var_str ("numubar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("TAU_LEPTON"), 15)
call field%set (spin_type=2)
call field%set (mass_data=model%get_par_real_ptr (6))
call field%set (name = [var_str ("tau-")], anti = [var_str ("tau+")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("TAU_NEUTRINO"), 16)
call field%set (spin_type=2, is_left_handed=.true.)
call field%set (name = [var_str ("nutau")], anti = [var_str ("nutaubar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("GLUON"), 21)
call field%set (spin_type=3, color_type=8)
call field%set (name = [var_str ("gl")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("PHOTON"), 22)
call field%set (spin_type=3)
call field%set (name = [var_str ("A")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("Z_BOSON"), 23)
call field%set (spin_type=3)
call field%set (mass_data=model%get_par_real_ptr (1))
call field%set (width_data=model%get_par_real_ptr (12))
call field%set (name = [var_str ("Z")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("W_BOSON"), 24)
call field%set (spin_type=3)
call field%set (mass_data=model%get_par_real_ptr (2))
call field%set (width_data=model%get_par_real_ptr (13))
call field%set (name = [var_str ("W+")], anti = [var_str ("W-")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("HIGGS"), 25)
call field%set (spin_type=1)
call field%set (mass_data=model%get_par_real_ptr (3))
call field%set (width_data=model%get_par_real_ptr (14))
call field%set (name = [var_str ("H")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("PROTON"), 2212)
call field%set (spin_type=2)
call field%set (name = [var_str ("p")], anti = [var_str ("pbar")])
! call field%set (mass_data=model%get_par_real_ptr (12))
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("HADRON_REMNANT_SINGLET"), 91)
call field%set (color_type=1)
call field%set (name = [var_str ("hr1")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("HADRON_REMNANT_TRIPLET"), 92)
call field%set (color_type=3)
call field%set (name = [var_str ("hr3")], anti = [var_str ("hr3bar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("HADRON_REMNANT_OCTET"), 93)
call field%set (color_type=8)
call field%set (name = [var_str ("hr8")])
call model%freeze_fields ()
i = 0
i = i + 1
!!! QED
call model%set_vertex (i, [var_str ("dbar"), var_str ("d"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("ubar"), var_str ("u"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("sbar"), var_str ("s"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("cbar"), var_str ("c"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("bbar"), var_str ("b"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("tbar"), var_str ("t"), var_str ("A")])
i = i + 1
!!!
call model%set_vertex (i, [var_str ("e+"), var_str ("e-"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("mu+"), var_str ("mu-"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("tau+"), var_str ("tau-"), var_str ("A")])
i = i + 1
!!! QCD
call model%set_vertex (i, [var_str ("gl"), var_str ("gl"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("gl"), var_str ("gl"), &
var_str ("gl"), var_str ("gl")])
i = i + 1
!!!
call model%set_vertex (i, [var_str ("dbar"), var_str ("d"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("ubar"), var_str ("u"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("sbar"), var_str ("s"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("cbar"), var_str ("c"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("bbar"), var_str ("b"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("tbar"), var_str ("t"), var_str ("gl")])
i = i + 1
!!! Neutral currents
call model%set_vertex (i, [var_str ("dbar"), var_str ("d"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("ubar"), var_str ("u"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("sbar"), var_str ("s"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("cbar"), var_str ("c"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("bbar"), var_str ("b"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("tbar"), var_str ("t"), var_str ("Z")])
i = i + 1
!!!
call model%set_vertex (i, [var_str ("e+"), var_str ("e-"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("mu+"), var_str ("muu-"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("tau+"), var_str ("tau-"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("nuebar"), var_str ("nue"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("numubar"), var_str ("numu"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("nutaubar"), var_str ("nutau"), &
var_str ("Z")])
i = i + 1
!!! Charged currents
call model%set_vertex (i, [var_str ("ubar"), var_str ("d"), var_str ("W+")])
i = i + 1
call model%set_vertex (i, [var_str ("cbar"), var_str ("s"), var_str ("W+")])
i = i + 1
call model%set_vertex (i, [var_str ("tbar"), var_str ("b"), var_str ("W+")])
i = i + 1
call model%set_vertex (i, [var_str ("dbar"), var_str ("u"), var_str ("W-")])
i = i + 1
call model%set_vertex (i, [var_str ("sbar"), var_str ("c"), var_str ("W-")])
i = i + 1
call model%set_vertex (i, [var_str ("bbar"), var_str ("t"), var_str ("W-")])
i = i + 1
!!!
call model%set_vertex (i, [var_str ("nuebar"), var_str ("e-"), var_str ("W+")])
i = i + 1
call model%set_vertex (i, [var_str ("numubar"), var_str ("mu-"), var_str ("W+")])
i = i + 1
call model%set_vertex (i, [var_str ("nutaubar"), var_str ("tau-"), var_str ("W+")])
i = i + 1
call model%set_vertex (i, [var_str ("e+"), var_str ("nue"), var_str ("W-")])
i = i + 1
call model%set_vertex (i, [var_str ("mu+"), var_str ("numu"), var_str ("W-")])
i = i + 1
call model%set_vertex (i, [var_str ("tau+"), var_str ("nutau"), var_str ("W-")])
i = i + 1
!!! Yukawa
!!! keeping only 3rd generation for the moment
! call model%set_vertex (i, [var_str ("sbar"), var_str ("s"), var_str ("H")])
! i = i + 1
! call model%set_vertex (i, [var_str ("cbar"), var_str ("c"), var_str ("H")])
! i = i + 1
call model%set_vertex (i, [var_str ("bbar"), var_str ("b"), var_str ("H")])
i = i + 1
call model%set_vertex (i, [var_str ("tbar"), var_str ("t"), var_str ("H")])
i = i + 1
! call model%set_vertex (i, [var_str ("mubar"), var_str ("mu"), var_str ("H")])
! i = i + 1
call model%set_vertex (i, [var_str ("taubar"), var_str ("tau"), var_str ("H")])
i = i + 1
!!! Vector-boson self-interactions
call model%set_vertex (i, [var_str ("W+"), var_str ("W-"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("W+"), var_str ("W-"), var_str ("Z")])
i = i + 1
!!!
call model%set_vertex (i, [var_str ("W+"), var_str ("W-"), var_str ("Z"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("W+"), var_str ("W+"), var_str ("W-"), var_str ("W-")])
i = i + 1
call model%set_vertex (i, [var_str ("W+"), var_str ("W-"), var_str ("Z"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("W+"), var_str ("W-"), var_str ("A"), var_str ("A")])
i = i + 1
!!! Higgs - vector boson
! call model%set_vertex (i, [var_str ("H"), var_str ("Z"), var_str ("A")])
! i = i + 1
! call model%set_vertex (i, [var_str ("H"), var_str ("A"), var_str ("A")])
! i = i + 1
! call model%set_vertex (i, [var_str ("H"), var_str ("gl"), var_str ("gl")])
! i = i + 1
!!!
call model%set_vertex (i, [var_str ("H"), var_str ("W+"), var_str ("W-")])
i = i + 1
call model%set_vertex (i, [var_str ("H"), var_str ("Z"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("H"), var_str ("H"), var_str ("W+"), var_str ("W-")])
i = i + 1
call model%set_vertex (i, [var_str ("H"), var_str ("H"), var_str ("Z"), var_str ("Z")])
i = i + 1
!!! Higgs self-interactions
call model%set_vertex (i, [var_str ("H"), var_str ("H"), var_str ("H")])
i = i + 1
call model%set_vertex (i, [var_str ("H"), var_str ("H"), var_str ("H"), var_str ("H")])
i = i + 1
call model%freeze_vertices ()
end subroutine init_sm_full_test
@ %def init_sm_full_test
@ Initialize a [[part_prop]] object by passing a [[particle_label]],
which is simply the particle name. [[part_prop]] should be part of the
[[particle]] array of [[feyngraph_set]]. We use the [[model]] of
[[feyngraph_set]] to obtain the relevant data of the particle which is
needed to find [[phase_space]] parametrizations. When a [[part_prop]]
is initialized, we add and initialize also the corresponding anti-
particle [[part_prop]] if it is not yet in the array.
<<Cascades2: part prop: TBP>>=
procedure :: init => part_prop_init
<<Cascades2: procedures>>=
recursive subroutine part_prop_init (part_prop, feyngraph_set, particle_label)
class (part_prop_t), intent (out), target :: part_prop
type (feyngraph_set_t), intent (inout) :: feyngraph_set
character (len=*), intent (in) :: particle_label
type (flavor_t) :: flv, anti
type (string_t) :: name
integer :: i
name = particle_label
call flv%init (name, feyngraph_set%model)
part_prop%particle_label = particle_label
part_prop%pdg = flv%get_pdg ()
part_prop%mass = flv%get_mass ()
part_prop%width = flv%get_width()
part_prop%spin_type = flv%get_spin_type ()
part_prop%is_vector = flv%get_spin_type () == VECTOR
part_prop%empty = .false.
part_prop%tex_name = flv%get_tex_name ()
anti = flv%anti ()
if (flv%get_pdg() == anti%get_pdg()) then
select type (part_prop)
type is (part_prop_t)
part_prop%anti => part_prop
end select
else
do i=1, size (feyngraph_set%particle)
if (feyngraph_set%particle(i)%pdg == (- part_prop%pdg)) then
part_prop%anti => feyngraph_set%particle(i)
exit
else if (feyngraph_set%particle(i)%empty) then
part_prop%anti => feyngraph_set%particle(i)
call feyngraph_set%particle(i)%init (feyngraph_set, char(anti%get_name()))
exit
end if
enddo
end if
end subroutine part_prop_init
@ %def part_prop_init
@ This subroutine assigns to a node the particle properties. Since these
properties do not change and are simply read from the model file, we
use pointers to the elements of the [[particle]] array of the
[[feyngraph_set]]. If there is no corresponding array element, we
have to initialize the first empty element of the array.
<<Cascades2: parameters>>=
integer, parameter :: PRT_ARRAY_SIZE = 200
<<Cascades2: f node: TBP>>=
procedure :: assign_particle_properties => f_node_assign_particle_properties
<<Cascades2: procedures>>=
subroutine f_node_assign_particle_properties (node, feyngraph_set)
class (f_node_t), intent (inout ) :: node
type (feyngraph_set_t), intent (inout) :: feyngraph_set
character (len=LABEL_LEN) :: particle_label
integer :: i
particle_label = node%particle_label(1:index (node%particle_label, '[')-1)
if (.not. associated (feyngraph_set%particle)) then
allocate (feyngraph_set%particle (PRT_ARRAY_SIZE))
end if
do i = 1, size (feyngraph_set%particle)
if (particle_label == feyngraph_set%particle(i)%particle_label) then
node%particle => feyngraph_set%particle(i)
exit
else if (feyngraph_set%particle(i)%empty) then
call feyngraph_set%particle(i)%init (feyngraph_set, particle_label)
node%particle => feyngraph_set%particle(i)
exit
end if
enddo
!!! Since the O'Mega output uses the anti-particles instead of the particles specified
!!! in the process definition, we revert this here. An exception is the first particle
!!! in the parsable DAG output
node%particle => node%particle%anti
end subroutine f_node_assign_particle_properties
@ %def f_node_assign_particle_properties
@ From the output of a Feynman diagram (in the non-factorized output)
we need to find out how many daughter nodes would be required to
reconstruct it correctly, to make sure that we keep
only those [[feyngraphs]] which are constructed solely on the basis of
the 3-vertices which are provided by the model. The number of daughter
particles can easily be determined from the syntax of O'Mega's output:
The particle which appears before the colon ':' is the mother particle.
The particles or subtrees (i.e. whole parentheses) follow after the
colon and are separated by commas.
<<Cascades2: procedures>>=
function get_n_daughters (subtree_string, pos_first_colon) &
result (n_daughters)
character (len=*), intent (in) :: subtree_string
integer, intent (in) :: pos_first_colon
integer :: n_daughters
integer :: n_open_par
integer :: i
n_open_par = 1
n_daughters = 0
if (len_trim(subtree_string) > 0) then
if (pos_first_colon > 0) then
do i=pos_first_colon, len_trim(subtree_string)
if (subtree_string(i:i) == ',') then
if (n_open_par == 1) n_daughters = n_daughters + 1
else if (subtree_string(i:i) == '(') then
n_open_par = n_open_par + 1
else if (subtree_string(i:i) == ')') then
n_open_par = n_open_par - 1
end if
end do
if (n_open_par == 0) then
n_daughters = n_daughters + 1
end if
end if
end if
end function get_n_daughters
@ %def get_n_daughters
@
\subsection{Reconstruction of trees}
The reconstruction of a tree or subtree with the non-factorized input can
be done recursively, i.e. we first find the root of the tree in the
string and create an [[f_node]]. Then we look for daughters, which in the
string appear either as single particles or subtrees (which are of the
same form as the tree which we want to reconstruct. Therefore the
subroutine can simply be called again and again until there are no more
daughter nodes to create. When we meet a vertex which requires more than
two daughter particles, we stop the recursion and disable the node using
its [[keep]] variable. Whenever a daughter node is not kept, we do not
keep the mother node as well.
<<Cascades2: procedures>>=
recursive subroutine node_construct_subtree_rec (feyngraph_set, &
feyngraph, subtree_string, mother_node)
type (feyngraph_set_t), intent (inout) :: feyngraph_set
type (feyngraph_t), intent (inout) :: feyngraph
character (len=*), intent (in) :: subtree_string
type (f_node_t), pointer, intent (inout) :: mother_node
integer :: n_daughters
integer :: pos_first_colon
integer :: current_daughter
integer :: pos_subtree_begin, pos_subtree_end
integer :: i
integer :: n_open_par
if (.not. associated (mother_node)) then
call feyngraph_set%f_node_list%add_entry (subtree_string, mother_node, .true.)
current_daughter = 1
n_open_par = 1
pos_first_colon = index (subtree_string, ':')
n_daughters = get_n_daughters (subtree_string, pos_first_colon)
if (pos_first_colon == 0) then
mother_node%particle_label = subtree_string
else
mother_node%particle_label = subtree_string(2:pos_first_colon-1)
end if
if (.not. associated (mother_node%particle)) then
call mother_node%assign_particle_properties (feyngraph_set)
end if
if (n_daughters /= 2 .and. n_daughters /= 0) then
mother_node%keep = .false.
feyngraph%keep = .false.
return
end if
pos_subtree_begin = pos_first_colon + 1
do i = pos_first_colon + 1, len(trim(subtree_string))
if (current_daughter == 2) then
pos_subtree_end = len(trim(subtree_string)) - 1
call node_construct_subtree_rec (feyngraph_set, feyngraph, &
subtree_string(pos_subtree_begin:pos_subtree_end), &
mother_node%daughter2)
exit
else if (subtree_string(i:i) == ',') then
if (n_open_par == 1) then
pos_subtree_end = i - 1
call node_construct_subtree_rec (feyngraph_set, feyngraph, &
subtree_string(pos_subtree_begin:pos_subtree_end), &
mother_node%daughter1)
current_daughter = 2
pos_subtree_begin = i + 1
end if
else if (subtree_string(i:i) == '(') then
n_open_par = n_open_par + 1
else if (subtree_string(i:i) == ')') then
n_open_par = n_open_par - 1
end if
end do
end if
if (associated (mother_node%daughter1)) then
if (.not. mother_node%daughter1%keep) then
mother_node%keep = .false.
end if
end if
if (associated (mother_node%daughter2)) then
if (.not. mother_node%daughter2%keep) then
mother_node%keep = .false.
end if
end if
if (associated (mother_node%daughter1) .and. &
associated (mother_node%daughter2)) then
mother_node%n_subtree_nodes = &
mother_node%daughter1%n_subtree_nodes &
+ mother_node%daughter2%n_subtree_nodes + 1
end if
if (.not. mother_node%keep) then
feyngraph%keep = .false.
end if
end subroutine node_construct_subtree_rec
@ %def node_construct_subtree_rec
@ When the non-factorized version of the O'Mega output is used, the
[[feyngraph]] is reconstructed from the contents of its [[string_t]]
variable [[omega_feyngraph_output]]. This can be used for the recursive
reconstruction of the tree of [[k_nodes]] with
[[node_construct_subtree_rec]].
<<Cascades2: procedures>>=
subroutine feyngraph_construct (feyngraph_set, feyngraph)
type (feyngraph_set_t), intent (inout) :: feyngraph_set
type (feyngraph_t), pointer, intent (inout) :: feyngraph
call node_construct_subtree_rec (feyngraph_set, feyngraph, &
char(feyngraph%omega_feyngraph_output), feyngraph%root)
feyngraph%n_nodes = feyngraph%root%n_subtree_nodes
end subroutine feyngraph_construct
@ %def feyngraph_construct
@ We introduce another node type, which is called [[dag_node_t]] and
is used to reproduce the dag structure which is represented by the input.
The [[dag_nodes]] can have several combinations of daughters 1 and 2.
The [[dag]] type contains an array of [[dag_nodes]] and is only used
for the reconstruction of [[feyngraphs]] which are factorized as well, but
in the other direction as the original output. This means in particular
that the outgoing particles in the output file (which there can appear
many times) exist only once as [[f_nodes]]. To represent combinations of
daughters and alternatives (options), we further use the types
[[dag_options_t]] and [[dag_combination_t]]. The [[dag_nodes]],
[[dag_options]] and [[dag_combinations]] correspond to a substring of
the string which has been read from file (and transformed into an object
of type [[dag_string_t]], which is simply another compact representation
of this string), or a modified version of this substring. The aim is to
create only one object for a given substring, even if it appears several
times in the original string and then create trees of [[f_nodes]], which
build up the [[feyngraph]], such that as many [[f_nodes]] as possible can be reused.
An outgoing particle (always interpreting the input as a decay) is
called a [[leaf]] in the context of a [[dag]].
<<Cascades2: types>>=
type :: dag_node_t
integer :: string_len
type (dag_string_t) :: string
logical :: leaf = .false.
type (f_node_ptr_t), dimension (:), allocatable :: f_node
integer :: subtree_size = 0
contains
<<Cascades2: dag node: TBP>>
end type dag_node_t
@ %def dag_node_t
<<Cascades2: dag node: TBP>>=
procedure :: final => dag_node_final
<<Cascades2: procedures>>=
subroutine dag_node_final (dag_node)
class (dag_node_t), intent (inout) :: dag_node
integer :: i
call dag_node%string%final ()
if (allocated (dag_node%f_node)) then
do i=1, size (dag_node%f_node)
if (associated (dag_node%f_node(i)%node)) then
call dag_node%f_node(i)%node%final ()
deallocate (dag_node%f_node(i)%node)
end if
enddo
deallocate (dag_node%f_node)
end if
end subroutine dag_node_final
@ %def dag_node_final
@ Whenever there are more than one possible subtrees (represented by
a [[dag_node]]) or combinations of subtrees to daughters (represented
by [[dag_combination_t]]), we use the type [[dag_options_t]]. In the
syntax of the factorized output, options are listed within curly
braces, separated by horizontal bars.
<<Cascades2: types>>=
type :: dag_options_t
integer :: string_len
type (dag_string_t) :: string
type (f_node_ptr_t), dimension (:), allocatable :: f_node_ptr1
type (f_node_ptr_t), dimension (:), allocatable :: f_node_ptr2
contains
<<Cascades2: dag options: TBP>>
end type dag_options_t
@ %def dag_node_options_t
<<Cascades2: dag options: TBP>>=
procedure :: final => dag_options_final
<<Cascades2: procedures>>=
subroutine dag_options_final (dag_options)
class (dag_options_t), intent (inout) :: dag_options
integer :: i
call dag_options%string%final ()
if (allocated (dag_options%f_node_ptr1)) then
do i=1, size (dag_options%f_node_ptr1)
dag_options%f_node_ptr1(i)%node => null ()
enddo
deallocate (dag_options%f_node_ptr1)
end if
if (allocated (dag_options%f_node_ptr2)) then
do i=1, size (dag_options%f_node_ptr2)
dag_options%f_node_ptr2(i)%node => null ()
enddo
deallocate (dag_options%f_node_ptr2)
end if
end subroutine dag_options_final
@ %def dag_options_final
@ A pair of two daughters (which can be [[dag_nodes]] or [[dag_options]])
is represented by the type [[dag_combination_t]]. In the original string,
a [[dag_combination]] appears between parentheses, which contain a comma,
but not a colon. If we find a colon between these parentheses, it is a
a [[dag_node]] instead.
<<Cascades2: types>>=
type :: dag_combination_t
integer :: string_len
type (dag_string_t) :: string
integer, dimension (2) :: combination
type (f_node_ptr_t), dimension (:), allocatable :: f_node_ptr1
type (f_node_ptr_t), dimension (:), allocatable :: f_node_ptr2
contains
<<Cascades2: dag combination: TBP>>
end type dag_combination_t
@ %def dag_combination_t
<<Cascades2: dag combination: TBP>>=
procedure :: final => dag_combination_final
<<Cascades2: procedures>>=
subroutine dag_combination_final (dag_combination)
class (dag_combination_t), intent (inout) :: dag_combination
integer :: i
call dag_combination%string%final ()
if (allocated (dag_combination%f_node_ptr1)) then
do i=1, size (dag_combination%f_node_ptr1)
dag_combination%f_node_ptr1(i)%node => null ()
enddo
deallocate (dag_combination%f_node_ptr1)
end if
if (allocated (dag_combination%f_node_ptr2)) then
do i=1, size (dag_combination%f_node_ptr2)
dag_combination%f_node_ptr2(i)%node => null ()
enddo
deallocate (dag_combination%f_node_ptr2)
end if
end subroutine dag_combination_final
@ %def dag_combination_final
@ Here is the type representing the DAG, i.e. it holds arrays of the
[[dag_nodes]], [[dag_options]] and [[dag_combinations]]. The root node
of the [[dag]] is the last filled element of the [[node]] array.
<<Cascades2: types>>=
type :: dag_t
type (dag_string_t) :: string
type (dag_node_t), dimension (:), allocatable :: node
type (dag_options_t), dimension (:), allocatable :: options
type (dag_combination_t), dimension (:), allocatable :: combination
integer :: n_nodes = 0
integer :: n_options = 0
integer :: n_combinations = 0
contains
<<Cascades2: dag: TBP>>
end type dag_t
@ %def dag_t
<<Cascades2: dag: TBP>>=
procedure :: final => dag_final
<<Cascades2: procedures>>=
subroutine dag_final (dag)
class (dag_t), intent (inout) :: dag
integer :: i
call dag%string%final ()
if (allocated (dag%node)) then
do i=1, size (dag%node)
call dag%node(i)%final ()
enddo
deallocate (dag%node)
end if
if (allocated (dag%options)) then
do i=1, size (dag%options)
call dag%options(i)%final ()
enddo
deallocate (dag%options)
end if
if (allocated (dag%combination)) then
do i=1, size (dag%combination)
call dag%combination(i)%final ()
enddo
deallocate (dag%combination)
end if
end subroutine dag_final
@ %def dag_final
@ We construct the DAG from the given [[dag_string]] which is modified
several times so that in the end the remaining string corresponds to a
simple [[dag_node]], the root of the factorized tree. This means that
we first identify the leaves, i.e. outgoing particles. Then we identify
[[dag_nodes]], [[dag_combinations]] and [[options]] until the number of
these objects does not change any more. Identifying means that we add
a corresponding object to the array (if not yet present), which can be identified
with the corresponding substring, and replace the substring in the
original [[dag_string]] by a [[dag_token]] of the corresponding type
(in the char output of this token, this corresponds to a place holder
like e.g. '<O23>' which in this particular case corresponds to an option
and can be found at the position 23 in the array). The character output
of the substrings turns out to be very useful for debugging.
<<Cascades2: dag: TBP>>=
procedure :: construct => dag_construct
<<Cascades2: procedures>>=
subroutine dag_construct (dag, feyngraph_set)
class (dag_t), intent (inout) :: dag
type (feyngraph_set_t), intent (inout) :: feyngraph_set
integer :: n_nodes
integer :: n_options
integer :: n_combinations
logical :: continue_loop
integer :: subtree_size
integer :: i,j
subtree_size = 1
call dag%get_nodes_and_combinations (leaves = .true.)
do i=1, dag%n_nodes
call dag%node(i)%make_f_nodes (feyngraph_set, dag)
enddo
continue_loop = .true.
subtree_size = subtree_size + 2
do while (continue_loop)
n_nodes = dag%n_nodes
n_options = dag%n_options
n_combinations = dag%n_combinations
call dag%get_nodes_and_combinations (leaves = .false.)
if (n_nodes /= dag%n_nodes) then
dag%node(n_nodes+1:dag%n_nodes)%subtree_size = subtree_size
do i = n_nodes+1, dag%n_nodes
call dag%node(i)%make_f_nodes (feyngraph_set, dag)
enddo
subtree_size = subtree_size + 2
end if
if (n_combinations /= dag%n_combinations) then
!$OMP PARALLEL DO
do i = n_combinations+1, dag%n_combinations
call dag%combination(i)%make_f_nodes (feyngraph_set, dag)
enddo
!$OMP END PARALLEL DO
end if
call dag%get_options ()
if (n_options /= dag%n_options) then
!$OMP PARALLEL DO
do i = n_options+1, dag%n_options
call dag%options(i)%make_f_nodes (feyngraph_set, dag)
enddo
!$OMP END PARALLEL DO
end if
if (n_nodes == dag%n_nodes .and. n_options == dag%n_options &
.and. n_combinations == dag%n_combinations) then
continue_loop = .false.
end if
enddo
!!! add root node to dag
call dag%add_node (dag%string%t, leaf = .false.)
dag%node(dag%n_nodes)%subtree_size = subtree_size
call dag%node(dag%n_nodes)%make_f_nodes (feyngraph_set, dag)
if (debug2_active (D_PHASESPACE)) then
call dag%write (output_unit)
end if
!!! set indices for all f_nodes
do i=1, dag%n_nodes
if (allocated (dag%node(i)%f_node)) then
do j=1, size (dag%node(i)%f_node)
if (associated (dag%node(i)%f_node(j)%node)) &
call dag%node(i)%f_node(j)%node%set_index ()
enddo
end if
enddo
end subroutine dag_construct
@ %def dag_construct
@ Identify [[dag_nodes]] and [[dag_combinations]]. Leaves are simply
nodes (i.e. of type [[NODE_TK]]) where only one bit in the bincode is
set. The [[dag_nodes]] and [[dag_combinations]] have in common that they
are surrounded by parentheses. There is however a way to distinguish
between them because the corresponding substring contains a colon (or
[[dag_token]] with type [[COLON_TK]]) if it is a [[dag_node]]. Otherwise
it is a [[dag_combination]]. The string of the [[dag_node]] or
[[dag_combination]] should not contain curly braces, because these
correspond to [[dag_options]] and should be identified before.
<<Cascades2: dag: TBP>>=
procedure :: get_nodes_and_combinations => dag_get_nodes_and_combinations
<<Cascades2: procedures>>=
subroutine dag_get_nodes_and_combinations (dag, leaves)
class (dag_t), intent (inout) :: dag
logical, intent (in) :: leaves
type (dag_string_t) :: new_string
integer :: i, j, k
integer :: i_node
integer :: new_size
integer :: first_colon
logical :: combination
!!! Create nodes also for external particles, except for the incoming one which
!!! appears as the root of the tree. These can easily be identified by their
!!! bincodes, since they should contain only one bit which is set.
if (leaves) then
first_colon = minloc (dag%string%t%type, 1, dag%string%t%type == COLON_TK)
do i = first_colon + 1, size (dag%string%t)
if (dag%string%t(i)%type == NODE_TK) then
if (popcnt(dag%string%t(i)%bincode) == 1) then
call dag%add_node (dag%string%t(i:i), .true., i_node)
call dag%string%t(i)%init_dag_object_token (DAG_NODE_TK, i_node)
end if
end if
enddo
call dag%string%update_char_len ()
else
!!! Create a node or combination for every closed pair of parentheses
!!! which do not contain any other parentheses or curly braces.
!!! A node (not outgoing) contains a colon. This is not the case
!!! for combinations, which we use as the criteria to distinguish
!!! between both.
allocate (new_string%t (size (dag%string%t)))
i = 1
new_size = 0
do while (i <= size(dag%string%t))
if (dag%string%t(i)%type == OPEN_PAR_TK) then
combination = .true.
do j = i+1, size (dag%string%t)
select case (dag%string%t(j)%type)
case (CLOSED_PAR_TK)
new_size = new_size + 1
if (combination) then
call dag%add_combination (dag%string%t(i:j), i_node)
call new_string%t(new_size)%init_dag_object_token (DAG_COMBINATION_TK, i_node)
else
call dag%add_node (dag%string%t(i:j), leaves, i_node)
call new_string%t(new_size)%init_dag_object_token (DAG_NODE_TK, i_node)
end if
i = j + 1
exit
case (OPEN_PAR_TK, OPEN_CURLY_TK, CLOSED_CURLY_TK)
new_size = new_size + 1
new_string%t(new_size) = dag%string%t(i)
i = i + 1
exit
case (COLON_TK)
combination = .false.
end select
enddo
else
new_size = new_size + 1
new_string%t(new_size) = dag%string%t(i)
i = i + 1
end if
enddo
dag%string = new_string%t(:new_size)
call dag%string%update_char_len ()
end if
end subroutine dag_get_nodes_and_combinations
@ %def dag_get_nodes_and_combinations
@ Identify [[dag_options]], i.e. lists of rival nodes or combinations
of nodes. These are identified by the surrounding curly braces. They
should not contain any parentheses any more, because these correspond
either to nodes or to combinations and should be identified before.
<<Cascades2: dag: TBP>>=
procedure :: get_options => dag_get_options
<<Cascades2: procedures>>=
subroutine dag_get_options (dag)
class (dag_t), intent (inout) :: dag
type (dag_string_t) :: new_string
integer :: i, j, k
integer :: new_size
integer :: i_options
character (len=10) :: index_char
integer :: index_start, index_end
!!! Create a node or combination for every closed pair of parentheses
!!! which do not contain any other parentheses or curly braces.
!!! A node (not outgoing) contains a colon. This is not the case
!!! for combinations, which we use as the criteria to distinguish
!!! between both.
allocate (new_string%t (size (dag%string%t)))
i = 1
new_size = 0
do while (i <= size(dag%string%t))
if (dag%string%t(i)%type == OPEN_CURLY_TK) then
do j = i+1, size (dag%string%t)
select case (dag%string%t(j)%type)
case (CLOSED_CURLY_TK)
new_size = new_size + 1
call dag%add_options (dag%string%t(i:j), i_options)
call new_string%t(new_size)%init_dag_object_token (DAG_OPTIONS_TK, i_options)
i = j + 1
exit
case (OPEN_PAR_TK, CLOSED_PAR_TK, OPEN_CURLY_TK)
new_size = new_size + 1
new_string%t(new_size) = dag%string%t(i)
i = i + 1
exit
end select
enddo
else
new_size = new_size + 1
new_string%t(new_size) = dag%string%t(i)
i = i + 1
end if
enddo
dag%string = new_string%t(:new_size)
call dag%string%update_char_len ()
end subroutine dag_get_options
@ %def dag_get_options
@ Add a [[dag_node]] to the list. The optional argument returns the index
of the node. The node might already exist. In this case we only return
the index.
<<Cascades2: dag: TBP>>=
procedure :: add_node => dag_add_node
<<Cascades2: parameters>>=
integer, parameter :: DAG_STACK_SIZE = 1000
<<Cascades2: procedures>>=
subroutine dag_add_node (dag, string, leaf, i_node)
class (dag_t), intent (inout) :: dag
type (dag_token_t), dimension (:), intent (in) :: string
logical, intent (in) :: leaf
integer, intent (out), optional :: i_node
type (dag_node_t), dimension (:), allocatable :: tmp_node
integer :: string_len
integer :: i
string_len = sum (string%char_len)
if (.not. allocated (dag%node)) then
allocate (dag%node (DAG_STACK_SIZE))
else if (dag%n_nodes == size (dag%node)) then
allocate (tmp_node (dag%n_nodes))
tmp_node = dag%node
deallocate (dag%node)
allocate (dag%node (dag%n_nodes+DAG_STACK_SIZE))
dag%node(:dag%n_nodes) = tmp_node
deallocate (tmp_node)
end if
do i = 1, dag%n_nodes
if (dag%node(i)%string_len == string_len) then
if (size (dag%node(i)%string%t) == size (string)) then
if (all(dag%node(i)%string%t == string)) then
if (present (i_node)) i_node = i
return
end if
end if
end if
enddo
dag%n_nodes = dag%n_nodes + 1
dag%node(dag%n_nodes)%string = string
dag%node(dag%n_nodes)%string_len = string_len
if (present (i_node)) i_node = dag%n_nodes
dag%node(dag%n_nodes)%leaf = leaf
end subroutine dag_add_node
@ %def dag_add_node
@ A similar subroutine for options.
<<Cascades2: dag: TBP>>=
procedure :: add_options => dag_add_options
<<Cascades2: procedures>>=
subroutine dag_add_options (dag, string, i_options)
class (dag_t), intent (inout) :: dag
type (dag_token_t), dimension (:), intent (in) :: string
integer, intent (out), optional :: i_options
type (dag_options_t), dimension (:), allocatable :: tmp_options
integer :: string_len
integer :: i
string_len = sum (string%char_len)
if (.not. allocated (dag%options)) then
allocate (dag%options (DAG_STACK_SIZE))
else if (dag%n_options == size (dag%options)) then
allocate (tmp_options (dag%n_options))
tmp_options = dag%options
deallocate (dag%options)
allocate (dag%options (dag%n_options+DAG_STACK_SIZE))
dag%options(:dag%n_options) = tmp_options
deallocate (tmp_options)
end if
do i = 1, dag%n_options
if (dag%options(i)%string_len == string_len) then
if (size (dag%options(i)%string%t) == size (string)) then
if (all(dag%options(i)%string%t == string)) then
if (present (i_options)) i_options = i
return
end if
end if
end if
enddo
dag%n_options = dag%n_options + 1
dag%options(dag%n_options)%string = string
dag%options(dag%n_options)%string_len = string_len
if (present (i_options)) i_options = dag%n_options
end subroutine dag_add_options
@ %def dag_add_options
@ A similar subroutine for combinations.
<<Cascades2: dag: TBP>>=
procedure :: add_combination => dag_add_combination
<<Cascades2: procedures>>=
subroutine dag_add_combination (dag, string, i_combination)
class (dag_t), intent (inout) :: dag
type (dag_token_t), dimension (:), intent (in) :: string
integer, intent (out), optional :: i_combination
type (dag_combination_t), dimension (:), allocatable :: tmp_combination
integer :: string_len
integer :: i
string_len = sum (string%char_len)
if (.not. allocated (dag%combination)) then
allocate (dag%combination (DAG_STACK_SIZE))
else if (dag%n_combinations == size (dag%combination)) then
allocate (tmp_combination (dag%n_combinations))
tmp_combination = dag%combination
deallocate (dag%combination)
allocate (dag%combination (dag%n_combinations+DAG_STACK_SIZE))
dag%combination(:dag%n_combinations) = tmp_combination
deallocate (tmp_combination)
end if
do i = 1, dag%n_combinations
if (dag%combination(i)%string_len == string_len) then
if (size (dag%combination(i)%string%t) == size (string)) then
if (all(dag%combination(i)%string%t == string)) then
i_combination = i
return
end if
end if
end if
enddo
dag%n_combinations = dag%n_combinations + 1
dag%combination(dag%n_combinations)%string = string
dag%combination(dag%n_combinations)%string_len = string_len
if (present (i_combination)) i_combination = dag%n_combinations
end subroutine dag_add_combination
@ %def dag_add_combination
@ For a given [[dag_node]] we want to create all [[f_nodes]]. If the node
is not a leaf, it contains in its string placeholders for options or
combinations. For these objects there are similar subroutines which are
needed here to obtain the sets of daughter nodes. If the [[dag_node]] is
a leaf, it corresponds to an external particle and the token contains the
particle name.
<<Cascades2: dag node: TBP>>=
procedure :: make_f_nodes => dag_node_make_f_nodes
<<Cascades2: procedures>>=
subroutine dag_node_make_f_nodes (dag_node, feyngraph_set, dag)
class (dag_node_t), intent (inout) :: dag_node
type (feyngraph_set_t), intent (inout) :: feyngraph_set
type (dag_t), intent (inout) :: dag
character (len=LABEL_LEN) :: particle_label
integer :: i, j
integer, dimension (2) :: obj
integer, dimension (2) :: i_obj
integer :: n_obj
integer :: pos
integer :: new_size, size1, size2
integer, dimension(:), allocatable :: match
if (allocated (dag_node%f_node)) return
pos = minloc (dag_node%string%t%type, 1,dag_node%string%t%type == NODE_TK)
particle_label = char (dag_node%string%t(pos))
if (dag_node%leaf) then
!!! construct subtree with procedure similar to the one for the old output
allocate (dag_node%f_node(1))
allocate (dag_node%f_node(1)%node)
dag_node%f_node(1)%node%particle_label = particle_label
call dag_node%f_node(1)%node%assign_particle_properties (feyngraph_set)
if (.not. dag_node%f_node(1)%node%keep) then
deallocate (dag_node%f_node)
return
end if
else
n_obj = 0
do i = 1, size (dag_node%string%t)
select case (dag_node%string%t(i)%type)
case (DAG_NODE_TK, DAG_OPTIONS_TK, DAG_COMBINATION_TK)
n_obj = n_obj + 1
if (n_obj > 2) return
obj(n_obj) = dag_node%string%t(i)%type
i_obj(n_obj) = dag_node%string%t(i)%index
end select
enddo
if (n_obj == 1) then
if (obj(1) == DAG_OPTIONS_TK) then
if (allocated (dag%options(i_obj(1))%f_node_ptr1)) then
size1 = size(dag%options(i_obj(1))%f_node_ptr1)
allocate (dag_node%f_node(size1))
do i=1, size1
allocate (dag_node%f_node(i)%node)
dag_node%f_node(i)%node%particle_label = particle_label
call dag_node%f_node(i)%node%assign_particle_properties (feyngraph_set)
dag_node%f_node(i)%node%daughter1 => dag%options(i_obj(1))%f_node_ptr1(i)%node
dag_node%f_node(i)%node%daughter2 => dag%options(i_obj(1))%f_node_ptr2(i)%node
dag_node%f_node(i)%node%n_subtree_nodes = &
dag%options(i_obj(1))%f_node_ptr1(i)%node%n_subtree_nodes &
+ dag%options(i_obj(1))%f_node_ptr2(i)%node%n_subtree_nodes + 1
enddo
end if
else if (obj(1) == DAG_COMBINATION_TK) then
if (allocated (dag%combination(i_obj(1))%f_node_ptr1)) then
size1 = size(dag%combination(i_obj(1))%f_node_ptr1)
allocate (dag_node%f_node(size1))
do i=1, size1
allocate (dag_node%f_node(i)%node)
dag_node%f_node(i)%node%particle_label = particle_label
call dag_node%f_node(i)%node%assign_particle_properties (feyngraph_set)
dag_node%f_node(i)%node%daughter1 => dag%combination(i_obj(1))%f_node_ptr1(i)%node
dag_node%f_node(i)%node%daughter2 => dag%combination(i_obj(1))%f_node_ptr2(i)%node
dag_node%f_node(i)%node%n_subtree_nodes = &
dag%combination(i_obj(1))%f_node_ptr1(i)%node%n_subtree_nodes &
+ dag%combination(i_obj(1))%f_node_ptr2(i)%node%n_subtree_nodes + 1
enddo
end if
end if
!!! simply set daughter pointers, daughters are already combined correctly
else if (n_obj == 2) then
size1 = 0
size2 = 0
if (obj(1) == DAG_NODE_TK) then
if (allocated (dag%node(i_obj(1))%f_node)) then
do i=1, size (dag%node(i_obj(1))%f_node)
if (dag%node(i_obj(1))%f_node(i)%node%keep) size1 = size1 + 1
enddo
end if
else if (obj(1) == DAG_OPTIONS_TK) then
if (allocated (dag%options(i_obj(1))%f_node_ptr1)) then
do i=1, size (dag%options(i_obj(1))%f_node_ptr1)
if (dag%options(i_obj(1))%f_node_ptr1(i)%node%keep) size1 = size1 + 1
enddo
end if
end if
if (obj(2) == DAG_NODE_TK) then
if (allocated (dag%node(i_obj(2))%f_node)) then
do i=1, size (dag%node(i_obj(2))%f_node)
if (dag%node(i_obj(2))%f_node(i)%node%keep) size2 = size2 + 1
enddo
end if
else if (obj(2) == DAG_OPTIONS_TK) then
if (allocated (dag%options(i_obj(2))%f_node_ptr1)) then
do i=1, size (dag%options(i_obj(2))%f_node_ptr1)
if (dag%options(i_obj(2))%f_node_ptr1(i)%node%keep) size2 = size2 + 1
enddo
end if
end if
!!! make all combinations of daughters
select case (obj(1))
case (DAG_NODE_TK)
select case (obj(2))
case (DAG_NODE_TK)
call combine_all_daughters(dag%node(i_obj(1))%f_node, &
dag%node(i_obj(2))%f_node)
case (DAG_OPTIONS_TK)
call combine_all_daughters(dag%node(i_obj(1))%f_node, &
dag%options(i_obj(2))%f_node_ptr1)
end select
case (DAG_OPTIONS_TK)
select case (obj(2))
case (DAG_NODE_TK)
call combine_all_daughters(dag%options(i_obj(1))%f_node_ptr1, &
dag%node(i_obj(2))%f_node)
case (DAG_OPTIONS_TK)
call combine_all_daughters(dag%options(i_obj(1))%f_node_ptr1, &
dag%options(i_obj(2))%f_node_ptr1)
end select
end select
end if
end if
contains
subroutine combine_all_daughters (daughter1_ptr, daughter2_ptr)
type (f_node_ptr_t), dimension (:), intent (in) :: daughter1_ptr
type (f_node_ptr_t), dimension (:), intent (in) :: daughter2_ptr
integer :: i, j
integer :: pos
new_size = size1*size2
allocate (dag_node%f_node(new_size))
pos = 0
do i = 1, size (daughter1_ptr)
if (daughter1_ptr(i)%node%keep) then
do j = 1, size (daughter2_ptr)
if (daughter2_ptr(j)%node%keep) then
pos = pos + 1
allocate (dag_node%f_node(pos)%node)
dag_node%f_node(pos)%node%particle_label = particle_label
call dag_node%f_node(pos)%node%assign_particle_properties (feyngraph_set)
dag_node%f_node(pos)%node%daughter1 => daughter1_ptr(i)%node
dag_node%f_node(pos)%node%daughter2 => daughter2_ptr(j)%node
dag_node%f_node(pos)%node%n_subtree_nodes = daughter1_ptr(i)%node%n_subtree_nodes &
+ daughter2_ptr(j)%node%n_subtree_nodes + 1
call feyngraph_set%model%match_vertex (daughter1_ptr(i)%node%particle%pdg, &
daughter2_ptr(j)%node%particle%pdg, match)
if (allocated (match)) then
if (any (abs(match) == abs(dag_node%f_node(pos)%node%particle%pdg))) then
dag_node%f_node(pos)%node%keep = .true.
else
dag_node%f_node(pos)%node%keep = .false.
end if
deallocate (match)
else
dag_node%f_node(pos)%node%keep = .false.
end if
end if
enddo
end if
enddo
end subroutine combine_all_daughters
end subroutine dag_node_make_f_nodes
@ %def dag_node_make_f_nodes
@ In [[dag_options_make_f_nodes_single]]
we obtain all [[f_nodes]] for [[dag_nodes]] which correspond to a
set of rival subtrees or nodes, which is the first possibility for
which [[dag_options]] can appear.
In [[dag_options_make_f_nodes_pair]]
the options are rival pairs ([[daughter1]], [[daughter2]]).
Therefore we have to pass two allocatable arrays of type [[f_node_ptr_t]]
to the subroutine.
<<Cascades2: dag options: TBP>>=
procedure :: make_f_nodes => dag_options_make_f_nodes
<<Cascades2: procedures>>=
subroutine dag_options_make_f_nodes (dag_options, &
feyngraph_set, dag)
class (dag_options_t), intent (inout) :: dag_options
type (feyngraph_set_t), intent (inout) :: feyngraph_set
type (dag_t), intent (inout) :: dag
integer, dimension (:), allocatable :: obj, i_obj
integer :: n_obj
integer :: i
integer :: pos
!!! read options
if (allocated (dag_options%f_node_ptr1)) return
n_obj = count ((dag_options%string%t%type == DAG_NODE_TK) .or. &
(dag_options%string%t%type == DAG_OPTIONS_TK) .or. &
(dag_options%string%t%type == DAG_COMBINATION_TK), 1)
allocate (obj(n_obj)); allocate (i_obj(n_obj))
pos = 0
do i = 1, size (dag_options%string%t)
select case (dag_options%string%t(i)%type)
case (DAG_NODE_TK, DAG_OPTIONS_TK, DAG_COMBINATION_TK)
pos = pos + 1
obj(pos) = dag_options%string%t(i)%type
i_obj(pos) = dag_options%string%t(i)%index
end select
enddo
if (any (dag_options%string%t%type == DAG_NODE_TK)) then
call dag_options_make_f_nodes_single
else if (any (dag_options%string%t%type == DAG_COMBINATION_TK)) then
call dag_options_make_f_nodes_pair
end if
deallocate (obj, i_obj)
contains
subroutine dag_options_make_f_nodes_single
integer :: i_start, i_end
integer :: n_nodes
n_nodes = 0
do i=1, n_obj
if (allocated (dag%node(i_obj(i))%f_node)) then
n_nodes = n_nodes + size (dag%node(i_obj(i))%f_node)
end if
enddo
if (n_nodes /= 0) then
allocate (dag_options%f_node_ptr1 (n_nodes))
i_end = 0
do i = 1, n_obj
if (allocated (dag%node(i_obj(i))%f_node)) then
i_start = i_end + 1
i_end = i_end + size (dag%node(i_obj(i))%f_node)
dag_options%f_node_ptr1(i_start:i_end) = dag%node(i_obj(i))%f_node
end if
enddo
end if
end subroutine dag_options_make_f_nodes_single
subroutine dag_options_make_f_nodes_pair
integer :: i_start, i_end
integer :: n_nodes
!!! get f_nodes from each combination
n_nodes = 0
do i=1, n_obj
if (allocated (dag%combination(i_obj(i))%f_node_ptr1)) then
n_nodes = n_nodes + size (dag%combination(i_obj(i))%f_node_ptr1)
end if
enddo
if (n_nodes /= 0) then
allocate (dag_options%f_node_ptr1 (n_nodes))
allocate (dag_options%f_node_ptr2 (n_nodes))
i_end = 0
do i=1, n_obj
if (allocated (dag%combination(i_obj(i))%f_node_ptr1)) then
i_start = i_end + 1
i_end = i_end + size (dag%combination(i_obj(i))%f_node_ptr1)
dag_options%f_node_ptr1(i_start:i_end) = dag%combination(i_obj(i))%f_node_ptr1
dag_options%f_node_ptr2(i_start:i_end) = dag%combination(i_obj(i))%f_node_ptr2
end if
enddo
end if
end subroutine dag_options_make_f_nodes_pair
end subroutine dag_options_make_f_nodes
@ %def dag_options_make_f_nodes
@ We create all combinations of daughter [[f_nodes]] for a combination.
In the combination each daughter can be either a single [[dag_node]] or
[[dag_options]] which are a set of single [[dag_nodes]]. Therefore, we
first create all possible [[f_nodes]] for daughter1, then all possible
[[f_nodes]] for daughter2. In the end we combine all [[daughter1]] nodes
with all [[daughter2]] nodes.
<<Cascades2: dag combination: TBP>>=
procedure :: make_f_nodes => dag_combination_make_f_nodes
<<Cascades2: procedures>>=
subroutine dag_combination_make_f_nodes (dag_combination, &
feyngraph_set, dag)
class (dag_combination_t), intent (inout) :: dag_combination
type (feyngraph_set_t), intent (inout) :: feyngraph_set
type (dag_t), intent (inout) :: dag
integer, dimension (2) :: obj, i_obj
integer :: n_obj
integer :: new_size, size1, size2
integer :: i, j, pos
if (allocated (dag_combination%f_node_ptr1)) return
n_obj = 0
do i = 1, size (dag_combination%string%t)
select case (dag_combination%string%t(i)%type)
case (DAG_NODE_TK, DAG_OPTIONS_TK, DAG_COMBINATION_TK)
n_obj = n_obj + 1
if (n_obj > 2) return
obj(n_obj) = dag_combination%string%t(i)%type
i_obj(n_obj) = dag_combination%string%t(i)%index
end select
enddo
size1 = 0
size2 = 0
if (obj(1) == DAG_NODE_TK) then
if (allocated (dag%node(i_obj(1))%f_node)) &
size1 = size (dag%node(i_obj(1))%f_node)
else if (obj(1) == DAG_OPTIONS_TK) then
if (allocated (dag%options(i_obj(1))%f_node_ptr1)) &
size1 = size (dag%options(i_obj(1))%f_node_ptr1)
end if
if (obj(2) == DAG_NODE_TK) then
if (allocated (dag%node(i_obj(2))%f_node)) &
size2 = size (dag%node(i_obj(2))%f_node)
else if (obj(2) == DAG_OPTIONS_TK) then
if (allocated (dag%options(i_obj(2))%f_node_ptr1)) &
size2 = size (dag%options(i_obj(2))%f_node_ptr1)
end if
!!! combine the 2 arrays of f_nodes
new_size = size1*size2
if (new_size /= 0) then
allocate (dag_combination%f_node_ptr1 (new_size))
allocate (dag_combination%f_node_ptr2 (new_size))
pos = 0
select case (obj(1))
case (DAG_NODE_TK)
select case (obj(2))
case (DAG_NODE_TK)
do i = 1, size1
do j = 1, size2
pos = pos + 1
dag_combination%f_node_ptr1(pos) = dag%node(i_obj(1))%f_node(i)
dag_combination%f_node_ptr2(pos) = dag%node(i_obj(2))%f_node(j)
enddo
enddo
case (DAG_OPTIONS_TK)
do i = 1, size1
do j = 1, size2
pos = pos + 1
dag_combination%f_node_ptr1(pos) = dag%node(i_obj(1))%f_node(i)
dag_combination%f_node_ptr2(pos) = dag%options(i_obj(2))%f_node_ptr1(j)
enddo
enddo
end select
case (DAG_OPTIONS_TK)
select case (obj(2))
case (DAG_NODE_TK)
do i = 1, size1
do j = 1, size2
pos = pos + 1
dag_combination%f_node_ptr1(pos) = dag%options(i_obj(1))%f_node_ptr1(i)
dag_combination%f_node_ptr2(pos) = dag%node(i_obj(2))%f_node(j)
enddo
enddo
case (DAG_OPTIONS_TK)
do i = 1, size1
do j = 1, size2
pos = pos + 1
dag_combination%f_node_ptr1(pos) = dag%options(i_obj(1))%f_node_ptr1(i)
dag_combination%f_node_ptr2(pos) = dag%options(i_obj(2))%f_node_ptr1(j)
enddo
enddo
end select
end select
end if
end subroutine dag_combination_make_f_nodes
@ %def dag_combination_make_f_nodes
@ Here we create the [[feyngraphs]]. After the construction of the
[[dag]] the remaining [[dag_string]] should contain a token for a
single [[dag_node]] which corresponds to the roots of the
[[feyngraphs]]. Therefore we make all [[f_nodes]] for this [[dag_node]]
and create a [[feyngraph]] for each [[f_node]]. Note that only
3-vertices are accepted. All other vertices are rejected. The
starting point is the last dag node which has been added to the list,
since this corresponds to the root of the tree.
Is is important to understand that the structure of feyngraphs is not
the same as the structure of the dag which is read from file, because
for the calculations which are performed in this module we want to
reuse the nodes for the outgoing particles, which means that they
appear only once. In O'Mega's output, it is the first incoming particle
which appears only once and the outgoing particles appear many times. This
transition is incorporated in the subroutines which create [[f_nodes]]
from the different dag objects.
<<Cascades2: dag: TBP>>=
procedure :: make_feyngraphs => dag_make_feyngraphs
<<Cascades2: procedures>>=
subroutine dag_make_feyngraphs (dag, feyngraph_set)
class (dag_t), intent (inout) :: dag
type (feyngraph_set_t), intent (inout) :: feyngraph_set
integer :: i
integer :: max_subtree_size
max_subtree_size = dag%node(dag%n_nodes)%subtree_size
if (allocated (dag%node(dag%n_nodes)%f_node)) then
do i = 1, size (dag%node(dag%n_nodes)%f_node)
if (.not. associated (feyngraph_set%first)) then
allocate (feyngraph_set%last)
feyngraph_set%first => feyngraph_set%last
else
allocate (feyngraph_set%last%next)
feyngraph_set%last => feyngraph_set%last%next
end if
feyngraph_set%last%root => dag%node(dag%n_nodes)%f_node(i)%node
!!! The first particle was correct in the O'Mega parsable DAG output. It was however
!!! changed to its anti-particle in f_node_assign_particle_properties, which we revert here.
feyngraph_set%last%root%particle => feyngraph_set%last%root%particle%anti
feyngraph_set%last%n_nodes = feyngraph_set%last%root%n_subtree_nodes
feyngraph_set%n_graphs = feyngraph_set%n_graphs + 1
enddo
feyngraph_set%f_node_list%max_tree_size = feyngraph_set%first%n_nodes
end if
end subroutine dag_make_feyngraphs
@ %def dag_make_feyngraphs
@ A write procedure of the [[dag]] for debugging.
<<Cascades2: dag: TBP>>=
procedure :: write => dag_write
<<Cascades2: procedures>>=
subroutine dag_write (dag, u)
class (dag_t), intent (in) :: dag
integer, intent(in) :: u
integer :: i
write (u,fmt='(A)') 'nodes'
do i=1, dag%n_nodes
write (u,fmt='(I5,3X,A)') i, char (dag%node(i)%string)
enddo
write (u,fmt='(A)') 'options'
do i=1, dag%n_options
write (u,fmt='(I5,3X,A)') i, char (dag%options(i)%string)
enddo
write (u,fmt='(A)') 'combination'
do i=1, dag%n_combinations
write (u,fmt='(I5,3X,A)') i, char (dag%combination(i)%string)
enddo
end subroutine dag_write
@ %def dag_write
@ Make a copy of a resonant [[k_node]], where the copy is kept
nonresonant.
<<Cascades2: procedures>>=
subroutine k_node_make_nonresonant_copy (k_node)
type (k_node_t), intent (in) :: k_node
type (k_node_t), pointer :: copy
call k_node%f_node%k_node_list%add_entry (copy, recycle=.true.)
copy%daughter1 => k_node%daughter1
copy%daughter2 => k_node%daughter2
copy = k_node
copy%mapping = NONRESONANT
copy%resonant = .false.
copy%on_shell = .false.
copy%mapping_assigned = .true.
copy%is_nonresonant_copy = .true.
end subroutine k_node_make_nonresonant_copy
@ %def k_node_make_nonresonant_copy
@ For a given [[feyngraph]] we create all possible [[kingraphs]]. Here
we use existing [[k_nodes]] which have already been created when the
mapping calculations of the pure s-channel subgraphs are performed. The
nodes for the incoming particles or the nodes on the t-line will have
to be created in all cases because they are not used in several graphs.
To obtain the existing [[k_nodes]], we use the subroutine
[[k_node_init_from_f_node]] which itself uses [[f_node_list_get_nodes]]
to obtain all active [[k_nodes]] in the [[k_node_list]] of the [[f_node]].
The created [[kingraphs]] are attached to the linked list
of the [[feyngraph]]. For scattering processes we have to split up the
t-line, because since all graphs are represented as a decay, different
nodes can share daughter nodes. This happens also for the t-line or
the incoming particle which appears as an outgoing particle. For the
[[t_line]] or [[incoming]] nodes we do not want to recycle nodes but
rather create a copy of this line for each [[kingraph]].
<<Cascades2: feyngraph: TBP>>=
procedure :: make_kingraphs => feyngraph_make_kingraphs
<<Cascades2: procedures>>=
subroutine feyngraph_make_kingraphs (feyngraph, feyngraph_set)
class (feyngraph_t), intent (inout) :: feyngraph
type (feyngraph_set_t), intent (in) :: feyngraph_set
type (k_node_ptr_t), dimension (:), allocatable :: kingraph_root
integer :: i
if (.not. associated (feyngraph%kin_first)) then
call k_node_init_from_f_node (feyngraph%root, &
kingraph_root, feyngraph_set)
if (.not. feyngraph%root%keep) return
if (feyngraph_set%process_type == SCATTERING) then
call split_up_t_lines (kingraph_root)
end if
do i=1, size (kingraph_root)
if (associated (feyngraph%kin_last)) then
allocate (feyngraph%kin_last%next)
feyngraph%kin_last => feyngraph%kin_last%next
else
allocate (feyngraph%kin_last)
feyngraph%kin_first => feyngraph%kin_last
end if
feyngraph%kin_last%root => kingraph_root(i)%node
feyngraph%kin_last%n_nodes = feyngraph%n_nodes
feyngraph%kin_last%keep = feyngraph%keep
if (feyngraph_set%process_type == SCATTERING) then
feyngraph%kin_last%root%bincode = &
f_node_get_external_bincode (feyngraph_set, feyngraph%root)
end if
enddo
deallocate (kingraph_root)
end if
end subroutine feyngraph_make_kingraphs
@ %def feyngraph_make_kingraphs
@ Create all [[k_nodes]] for a given [[f_node]]. We return these nodes
using [[k_node_ptr]]. If the node is external, we assign also the bincode
to the [[k_nodes]] because this is determined from substrings of the
input file which belong to the [[feyngraphs]] and [[f_nodes]].
<<Cascades2: procedures>>=
recursive subroutine k_node_init_from_f_node (f_node, k_node_ptr, feyngraph_set)
type (f_node_t), target, intent (inout) :: f_node
type (k_node_ptr_t), allocatable, dimension (:), intent (out) :: k_node_ptr
type (feyngraph_set_t), intent (in) :: feyngraph_set
type (k_node_ptr_t), allocatable, dimension(:) :: daughter_ptr1, daughter_ptr2
integer :: n_nodes
integer :: i, j
integer :: pos
integer, save :: counter = 0
if (.not. (f_node%incoming .or. f_node%t_line)) then
call f_node%k_node_list%get_nodes (k_node_ptr)
if (.not. allocated (k_node_ptr) .and. f_node%k_node_list%n_entries > 0) then
f_node%keep = .false.
return
end if
end if
if (.not. allocated (k_node_ptr)) then
if (associated (f_node%daughter1) .and. associated (f_node%daughter2)) then
call k_node_init_from_f_node (f_node%daughter1, daughter_ptr1, &
feyngraph_set)
call k_node_init_from_f_node (f_node%daughter2, daughter_ptr2, &
feyngraph_set)
if (.not. (f_node%daughter1%keep .and. f_node%daughter2%keep)) then
f_node%keep = .false.
return
end if
n_nodes = size (daughter_ptr1) * size (daughter_ptr2)
allocate (k_node_ptr (n_nodes))
pos = 1
do i=1, size (daughter_ptr1)
do j=1, size (daughter_ptr2)
if (f_node%incoming .or. f_node%t_line) then
call f_node%k_node_list%add_entry (k_node_ptr(pos)%node, recycle = .false.)
else
call f_node%k_node_list%add_entry (k_node_ptr(pos)%node, recycle = .true.)
end if
k_node_ptr(pos)%node%f_node => f_node
k_node_ptr(pos)%node%daughter1 => daughter_ptr1(i)%node
k_node_ptr(pos)%node%daughter2 => daughter_ptr2(j)%node
k_node_ptr(pos)%node%f_node_index = f_node%index
k_node_ptr(pos)%node%incoming = f_node%incoming
k_node_ptr(pos)%node%t_line = f_node%t_line
k_node_ptr(pos)%node%particle => f_node%particle
pos = pos + 1
enddo
enddo
deallocate (daughter_ptr1, daughter_ptr2)
else
allocate (k_node_ptr(1))
if (f_node%incoming .or. f_node%t_line) then
call f_node%k_node_list%add_entry (k_node_ptr(1)%node, recycle=.false.)
else
call f_node%k_node_list%add_entry (k_node_ptr(1)%node, recycle=.true.)
end if
k_node_ptr(1)%node%f_node => f_node
k_node_ptr(1)%node%f_node_index = f_node%index
k_node_ptr(1)%node%incoming = f_node%incoming
k_node_ptr(1)%node%t_line = f_node%t_line
k_node_ptr(1)%node%particle => f_node%particle
k_node_ptr(1)%node%bincode = f_node_get_external_bincode (feyngraph_set, &
f_node)
end if
end if
end subroutine k_node_init_from_f_node
@ %def k_node_init_from_f_node
@ The graphs resulting from [[k_node_init_from_f_node]] are fine if they
are used only in one direction. This is however not the case when one
wants to invert the graphs, i.e. take the other incoming particle of a
scattering process as the decaying particle, because the outgoing
[[f_nodes]] (and hence also the [[k_nodes]]) exist only once. This
problem is solved here by creating a distinct t-line for each of the
graphs. The following subroutine disentangles the data structure by
creating new nodes such that the different t-lines are not connected
any more.
<<Cascades2: procedures>>=
recursive subroutine split_up_t_lines (t_node)
type (k_node_ptr_t), dimension(:), intent (inout) :: t_node
type (k_node_t), pointer :: ref_node => null ()
type (k_node_t), pointer :: ref_daughter => null ()
type (k_node_t), pointer :: new_daughter => null ()
type (k_node_ptr_t), dimension(:), allocatable :: t_daughter
integer :: ref_daughter_index
integer :: i, j
allocate (t_daughter (size (t_node)))
do i=1, size (t_node)
ref_node => t_node(i)%node
if (associated (ref_node%daughter1) .and. associated (ref_node%daughter2)) then
ref_daughter => null ()
if (ref_node%daughter1%incoming .or. ref_node%daughter1%t_line) then
ref_daughter => ref_node%daughter1
ref_daughter_index = 1
else if (ref_node%daughter2%incoming .or. ref_node%daughter2%t_line) then
ref_daughter => ref_node%daughter2
ref_daughter_index = 2
end if
do j=1, size (t_daughter)
if (.not. associated (t_daughter(j)%node)) then
t_daughter(j)%node => ref_daughter
exit
else if (t_daughter(j)%node%index == ref_daughter%index) then
new_daughter => null ()
call ref_daughter%f_node%k_node_list%add_entry (new_daughter, recycle=.false.)
new_daughter = ref_daughter
new_daughter%daughter1 => ref_daughter%daughter1
new_daughter%daughter2 => ref_daughter%daughter2
if (ref_daughter_index == 1) then
ref_node%daughter1 => new_daughter
else if (ref_daughter_index == 2) then
ref_node%daughter2 => new_daughter
end if
ref_daughter => new_daughter
end if
enddo
else
return
end if
enddo
call split_up_t_lines (t_daughter)
deallocate (t_daughter)
end subroutine split_up_t_lines
@ %def split_up_t_lines
@ This subroutine sets the [[inverse_daughters]] of a [[k_node]]. If we
invert a [[kingraph]] such that not the first but the second incoming
particle appears as the root of the tree, the [[incoming]] and [[t_line]]
particles obtain other daughters. These are the former mother node and
the sister node [[s_daughter]]. Here we set only the pointers for
the [[inverse_daughters]]. The inversion happens in [[kingraph_make_inverse_copy]]
and [[node_inverse_deep_copy]].
<<Cascades2: procedures>>=
subroutine kingraph_set_inverse_daughters (kingraph)
type (kingraph_t), intent (inout) :: kingraph
type (k_node_t), pointer :: mother
type (k_node_t), pointer :: t_daughter
type (k_node_t), pointer :: s_daughter
mother => kingraph%root
do while (associated (mother))
if (associated (mother%daughter1) .and. &
associated (mother%daughter2)) then
if (mother%daughter1%t_line .or. mother%daughter1%incoming) then
t_daughter => mother%daughter1; s_daughter => mother%daughter2
else if (mother%daughter2%t_line .or. mother%daughter2%incoming) then
t_daughter => mother%daughter2; s_daughter => mother%daughter1
else
exit
end if
t_daughter%inverse_daughter1 => mother
t_daughter%inverse_daughter2 => s_daughter
mother => t_daughter
else
exit
end if
enddo
end subroutine kingraph_set_inverse_daughters
@ %def kingraph_set_inverse_daughters
@ Set the bincode of an [[f_node]] which corresponds to an external
particle. This is done on the basis of the [[particle_label]] which is a
substring of the input file. Here it is not the particle name which is
important, but the number(s) in brackets which in general indicate the
external particles which are connected to the current node. This function
is however only used for external particles, so there can either be
one or [[n_out + 1]] particles in the brackets (in the DAG input file
always one, because also for the root there is only a single number).
In all cases we check the number of particles (in the DAG input the
numbers are separated by a slash).
<<Cascades2: procedures>>=
function f_node_get_external_bincode (feyngraph_set, f_node) result (bincode)
type (feyngraph_set_t), intent (in) :: feyngraph_set
type (f_node_t), intent (in) :: f_node
integer (TC) :: bincode
character (len=LABEL_LEN) :: particle_label
integer :: start_pos, end_pos, n_out_decay
integer :: n_prt ! for DAG
integer :: i
bincode = 0
if (feyngraph_set%process_type == DECAY) then
n_out_decay = feyngraph_set%n_out
else
n_out_decay = feyngraph_set%n_out + 1
end if
particle_label = f_node%particle_label
start_pos = index (particle_label, '[') + 1
end_pos = index (particle_label, ']') - 1
particle_label = particle_label(start_pos:end_pos)
!!! n_out_decay is the number of outgoing particles in the
!!! O'Mega output, which is always represented as a decay
if (feyngraph_set%use_dag) then
n_prt = 1
do i=1, len(particle_label)
if (particle_label(i:i) == '/') n_prt = n_prt + 1
enddo
else
n_prt = end_pos - start_pos + 1
end if
if (n_prt == 1) then
bincode = calculate_external_bincode (particle_label, &
feyngraph_set%process_type, n_out_decay)
else if (n_prt == n_out_decay) then
bincode = ibset (0, n_out_decay)
end if
end function f_node_get_external_bincode
@ %def f_node_get_external_bincode
@ Assign a bincode to an internal node, which is calculated from
the bincodes of [[daughter1]] and [[daughter2]].
<<Cascades2: procedures>>=
subroutine node_assign_bincode (node)
type (k_node_t), intent (inout) :: node
if (associated (node%daughter1) .and. associated (node%daughter2) &
.and. .not. node%incoming) then
node%bincode = ior(node%daughter1%bincode, node%daughter2%bincode)
end if
end subroutine node_assign_bincode
@ %def node_assign_bincode
@ Calculate the [[bincode]] from the number in the brackets of the
[[particle_label]], if the node is external. For the root in the
non-factorized output, this is calculated directly in
[[f_node_get_external_bincode]] because in this case all the other
external particle numbers appear between the brackets.
<<Cascades2: procedures>>=
function calculate_external_bincode (label_number_string, process_type, n_out_decay) result (bincode)
character (len=*), intent (in) :: label_number_string
integer, intent (in) :: process_type
integer, intent (in) :: n_out_decay
character :: number_char
integer :: number_int
integer (kind=TC) :: bincode
bincode = 0
read (label_number_string, fmt='(A)') number_char
!!! check if the character is a letter (A,B,C,...) or a number (1...9)
!!! numbers 1 and 2 are special cases
select case (number_char)
case ('1')
if (process_type == SCATTERING) then
number_int = n_out_decay + 3
else
number_int = n_out_decay + 2
end if
case ('2')
if (process_type == SCATTERING) then
number_int = n_out_decay + 2
else
number_int = 2
end if
case ('A')
number_int = 10
case ('B')
number_int = 11
case ('C')
number_int = 12
case ('D')
number_int = 13
case default
read (number_char, fmt='(I1)') number_int
end select
bincode = ibset (bincode, number_int - process_type - 1)
end function calculate_external_bincode
@ %def calculate_external_bincode
@
\subsection{Mapping calculations}
Once a [[k_node]] and its subtree nodes have been created, we can
perform the kinematical calculations and assign mappings, depending on
the particle properties and the results for the subtree nodes. This
could in principle be done recursively, calling the procedure first
for the daughter nodes and then perform the calculations for the actual
node. But for parallization and comparing the nodes, this will be done
simultaneously for all nodes with the same number of subtree nodes, and the number of
subtree nodes increases, starting from one, in steps of two. The
actual mapping calculations are done in complete analogy to cascades.
<<Cascades2: procedures>>=
subroutine node_assign_mapping_s (feyngraph, node, feyngraph_set)
type (feyngraph_t), intent (inout) :: feyngraph
type (k_node_t), intent (inout) :: node
type (feyngraph_set_t), intent (inout) :: feyngraph_set
real(default) :: eff_mass_sum
logical :: keep
if (.not. node%mapping_assigned) then
if (node%particle%mass > feyngraph_set%phs_par%m_threshold_s) then
node%effective_mass = node%particle%mass
end if
if (associated (node%daughter1) .and. associated (node%daughter2)) then
if (.not. (node%daughter1%keep .and. node%daughter2%keep)) then
node%keep = .false.; return
end if
node%ext_mass_sum = node%daughter1%ext_mass_sum &
+ node%daughter2%ext_mass_sum
keep = .false.
!!! Potentially resonant cases [sqrts = m_rea for on-shell decay]
if (node%particle%mass > node%ext_mass_sum &
.and. node%particle%mass <= feyngraph_set%phs_par%sqrts) then
if (node%particle%width /= 0) then
if (node%daughter1%on_shell .or. node%daughter2%on_shell) then
keep = .true.
node%mapping = S_CHANNEL
node%resonant = .true.
end if
else
call warn_decay (node%particle)
end if
!!! Collinear and IR singular cases
else if (node%particle%mass < feyngraph_set%phs_par%sqrts) then
!!! Massless splitting
if (node%daughter1%effective_mass == 0 &
.and. node%daughter2%effective_mass == 0 &
.and. .not. associated (node%daughter1%daughter1) &
.and. .not. associated (node%daughter1%daughter2) &
.and. .not. associated (node%daughter2%daughter1) &
.and. .not. associated (node%daughter2%daughter2)) then
keep = .true.
node%log_enhanced = .true.
if (node%particle%is_vector) then
if (node%daughter1%particle%is_vector &
.and. node%daughter2%particle%is_vector) then
node%mapping = COLLINEAR !!! three-vector-splitting
else
node%mapping = INFRARED !!! vector spliiting into matter
end if
else
if (node%daughter1%particle%is_vector &
.or. node%daughter2%particle%is_vector) then
node%mapping = COLLINEAR !!! vector radiation off matter
else
node%mapping = INFRARED !!! scalar radiation/splitting
end if
end if
!!! IR radiation off massive particle [cascades]
else if (node%effective_mass > 0 .and. &
node%daughter1%effective_mass > 0 .and. &
node%daughter2%effective_mass == 0 .and. &
(node%daughter1%on_shell .or. &
node%daughter1%mapping == RADIATION) .and. &
abs (node%effective_mass - &
node%daughter1%effective_mass) < feyngraph_set%phs_par%m_threshold_s) &
then
keep = .true.
node%log_enhanced = .true.
node%mapping = RADIATION
else if (node%effective_mass > 0 .and. &
node%daughter2%effective_mass > 0 .and. &
node%daughter1%effective_mass == 0 .and. &
(node%daughter2%on_shell .or. &
node%daughter2%mapping == RADIATION) .and. &
abs (node%effective_mass - &
node%daughter2%effective_mass) < feyngraph_set%phs_par%m_threshold_s) &
then
keep = .true.
node%log_enhanced = .true.
node%mapping = RADIATION
end if
end if
!!! Non-singular cases, including failed resonances [from cascades]
if (.not. keep) then
!!! Two on-shell particles from a virtual mother [from cascades, here eventually more than 2]
if (node%daughter1%on_shell .or. node%daughter2%on_shell) then
keep = .true.
eff_mass_sum = node%daughter1%effective_mass &
+ node%daughter2%effective_mass
node%effective_mass = max (node%ext_mass_sum, eff_mass_sum)
if (node%effective_mass < feyngraph_set%phs_par%m_threshold_s) then
node%effective_mass = 0
end if
end if
end if
!!! Complete and register feyngraph (make copy in case of resonance)
if (keep) then
node%on_shell = node%resonant .or. node%log_enhanced
if (node%resonant) then
if (feyngraph_set%phs_par%keep_nonresonant) then
call k_node_make_nonresonant_copy (node)
end if
node%ext_mass_sum = node%particle%mass
end if
end if
node%mapping_assigned = .true.
call node_assign_bincode (node)
call node%subtree%add_entry (node)
else !!! external (outgoing) particle
node%ext_mass_sum = node%particle%mass
node%mapping = EXTERNAL_PRT
node%multiplicity = 1
node%mapping_assigned = .true.
call node%subtree%add_entry (node)
node%on_shell = .true.
if (node%particle%mass >= feyngraph_set%phs_par%m_threshold_s) then
node%effective_mass = node%particle%mass
end if
end if
else if (node%is_nonresonant_copy) then
call node_assign_bincode (node)
call node%subtree%add_entry (node)
node%is_nonresonant_copy = .false.
end if
call node_count_specific_properties (node)
if (node%n_off_shell > feyngraph_set%phs_par%off_shell) then
node%keep = .false.
end if
contains
subroutine warn_decay (particle)
type(part_prop_t), intent(in) :: particle
integer :: i
integer, dimension(MAX_WARN_RESONANCE), save :: warned_code = 0
LOOP_WARNED: do i = 1, MAX_WARN_RESONANCE
if (warned_code(i) == 0) then
warned_code(i) = particle%pdg
write (msg_buffer, "(A)") &
& " Intermediate decay of zero-width particle " &
& // trim(particle%particle_label) &
& // " may be possible."
call msg_warning
exit LOOP_WARNED
else if (warned_code(i) == particle%pdg) then
exit LOOP_WARNED
end if
end do LOOP_WARNED
end subroutine warn_decay
end subroutine node_assign_mapping_s
@ %def node_assign_mapping_s
@ We determine the numbers [[n_resonances]], [[multiplicity]],
[[n_off_shell]] and [[n_log_enhanced]] for a given node.
<<Cascades2: procedures>>=
subroutine node_count_specific_properties (node)
type (k_node_t), intent (inout) :: node
if (associated (node%daughter1) .and. associated(node%daughter2)) then
if (node%resonant) then
node%multiplicity = 1
node%n_resonances &
= node%daughter1%n_resonances &
+ node%daughter2%n_resonances + 1
else
node%multiplicity &
= node%daughter1%multiplicity &
+ node%daughter2%multiplicity
node%n_resonances &
= node%daughter1%n_resonances &
+ node%daughter2%n_resonances
end if
if (node%log_enhanced) then
node%n_log_enhanced &
= node%daughter1%n_log_enhanced &
+ node%daughter2%n_log_enhanced + 1
else
node%n_log_enhanced &
= node%daughter1%n_log_enhanced &
+ node%daughter2%n_log_enhanced
end if
if (node%resonant) then
node%n_off_shell = 0
else if (node%log_enhanced) then
node%n_off_shell &
= node%daughter1%n_off_shell &
+ node%daughter2%n_off_shell
else
node%n_off_shell &
= node%daughter1%n_off_shell &
+ node%daughter2%n_off_shell + 1
end if
if (node%t_line) then
if (node%daughter1%t_line .or. node%daughter1%incoming) then
node%n_t_channel = node%daughter1%n_t_channel + 1
else if (node%daughter2%t_line .or. node%daughter2%incoming) then
node%n_t_channel = node%daughter2%n_t_channel + 1
end if
end if
end if
end subroutine node_count_specific_properties
@ %def node_count_specific_properties
@ The subroutine [[kingraph_assign_mappings_s]] completes kinematical
calculations for a decay process, considering the [[root]] node.
<<Cascades2: procedures>>=
subroutine kingraph_assign_mappings_s (feyngraph, kingraph, feyngraph_set)
type (feyngraph_t), intent (inout) :: feyngraph
type (kingraph_t), pointer, intent (inout) :: kingraph
type (feyngraph_set_t), intent (inout) :: feyngraph_set
if (.not. (kingraph%root%daughter1%keep .and. kingraph%root%daughter2%keep)) then
kingraph%keep = .false.
call kingraph%tree%final ()
end if
if (kingraph%keep) then
kingraph%root%on_shell = .true.
kingraph%root%mapping = EXTERNAL_PRT
kingraph%root%mapping_assigned = .true.
call node_assign_bincode (kingraph%root)
kingraph%root%ext_mass_sum = &
kingraph%root%daughter1%ext_mass_sum + &
kingraph%root%daughter2%ext_mass_sum
if (kingraph%root%ext_mass_sum >= feyngraph_set%phs_par%sqrts) then
kingraph%root%keep = .false.
kingraph%keep = .false.; call kingraph%tree%final (); return
end if
call kingraph%root%subtree%add_entry (kingraph%root)
kingraph%root%multiplicity &
= kingraph%root%daughter1%multiplicity &
+ kingraph%root%daughter2%multiplicity
kingraph%root%n_resonances &
= kingraph%root%daughter1%n_resonances &
+ kingraph%root%daughter2%n_resonances
kingraph%root%n_off_shell &
= kingraph%root%daughter1%n_off_shell &
+ kingraph%root%daughter2%n_off_shell
kingraph%root%n_log_enhanced &
= kingraph%root%daughter1%n_log_enhanced &
+ kingraph%root%daughter2%n_log_enhanced
if (kingraph%root%n_off_shell > feyngraph_set%phs_par%off_shell) then
kingraph%root%keep = .false.
kingraph%keep = .false.; call kingraph%tree%final (); return
else
kingraph%grove_prop%multiplicity = &
kingraph%root%multiplicity
kingraph%grove_prop%n_resonances = &
kingraph%root%n_resonances
kingraph%grove_prop%n_off_shell = &
kingraph%root%n_off_shell
kingraph%grove_prop%n_log_enhanced = &
kingraph%root%n_log_enhanced
end if
kingraph%tree = kingraph%root%subtree
end if
end subroutine kingraph_assign_mappings_s
@ %def kingraph_assign_mappings_s
@ Compute mappings for the [[t_line]] and [[incoming]] nodes. This is
done recursively using [[node_compute_t_line]].
<<Cascades2: procedures>>=
subroutine kingraph_compute_mappings_t_line (feyngraph, kingraph, feyngraph_set)
type (feyngraph_t), intent (inout) :: feyngraph
type (kingraph_t), pointer, intent (inout) :: kingraph
type (feyngraph_set_t), intent (inout) :: feyngraph_set
call node_compute_t_line (feyngraph, kingraph, kingraph%root, feyngraph_set)
if (.not. kingraph%root%keep) then
kingraph%keep = .false.
call kingraph%tree%final ()
end if
if (kingraph%keep) kingraph%tree = kingraph%root%subtree
end subroutine kingraph_compute_mappings_t_line
@ %def kingraph_compute_mappings_t_line
@ Perform the kinematical calculations and mapping assignment for a node
which is either [[incoming]] or [[t_line]]. This is done recursively,
going first to the daughter node which has this property. Therefore we
first set the pointer [[t_node]] to this daughter node and [[s_node]] to
the other one. The mapping determination happens again in the same way as
in [[cascades]].
<<Cascades2: procedures>>=
recursive subroutine node_compute_t_line (feyngraph, kingraph, node, feyngraph_set)
type (feyngraph_t), intent (inout) :: feyngraph
type (kingraph_t), intent (inout) :: kingraph
type (k_node_t), intent (inout) :: node
type (feyngraph_set_t), intent (inout) :: feyngraph_set
type (k_node_t), pointer :: s_node
type (k_node_t), pointer :: t_node
type (k_node_t), pointer :: new_s_node
if (.not. (node%daughter1%keep .and. node%daughter2%keep)) then
node%keep = .false.
return
end if
s_node => null ()
t_node => null ()
new_s_node => null ()
if (associated (node%daughter1) .and. associated (node%daughter2)) then
if (node%daughter1%t_line .or. node%daughter1%incoming) then
t_node => node%daughter1; s_node => node%daughter2
else if (node%daughter2%t_line .or. node%daughter2%incoming) then
t_node => node%daughter2; s_node => node%daughter1
end if
if (t_node%t_line) then
call node_compute_t_line (feyngraph, kingraph, t_node, feyngraph_set)
if (.not. t_node%keep) then
node%keep = .false.
return
end if
else if (t_node%incoming) then
t_node%mapping = EXTERNAL_PRT
t_node%on_shell = .true.
t_node%ext_mass_sum = t_node%particle%mass
if (t_node%particle%mass >= feyngraph_set%phs_par%m_threshold_t) then
t_node%effective_mass = t_node%particle%mass
end if
call t_node%subtree%add_entry (t_node)
end if
!!! root:
if (.not. node%incoming) then
if (t_node%incoming) then
node%ext_mass_sum = s_node%ext_mass_sum
else
node%ext_mass_sum &
= node%daughter1%ext_mass_sum &
+ node%daughter2%ext_mass_sum
end if
if (node%particle%mass > feyngraph_set%phs_par%m_threshold_t) then
node%effective_mass = max (node%particle%mass, &
s_node%effective_mass)
else if (s_node%effective_mass > feyngraph_set%phs_par%m_threshold_t) then
node%effective_mass = s_node%effective_mass
else
node%effective_mass = 0
end if
!!! Allowed decay of beam particle
if (t_node%incoming &
.and. t_node%particle%mass > s_node%particle%mass &
+ node%particle%mass) then
call beam_decay (feyngraph_set%fatal_beam_decay)
!!! Massless splitting
else if (t_node%effective_mass == 0 &
.and. s_node%effective_mass < feyngraph_set%phs_par%m_threshold_t &
.and. node%effective_mass == 0) then
node%mapping = U_CHANNEL
node%log_enhanced = .true.
!!! IR radiation off massive particle
else if (t_node%effective_mass /= 0 &
.and. s_node%effective_mass == 0 &
.and. node%effective_mass /= 0 &
.and. (t_node%on_shell &
.or. t_node%mapping == RADIATION) &
.and. abs (t_node%effective_mass - node%effective_mass) &
< feyngraph_set%phs_par%m_threshold_t) then
node%log_enhanced = .true.
node%mapping = RADIATION
end if
node%mapping_assigned = .true.
call node_assign_bincode (node)
call node%subtree%add_entry (node)
call node_count_specific_properties (node)
if (node%n_off_shell > feyngraph_set%phs_par%off_shell) then
node%keep = .false.
kingraph%keep = .false.; call kingraph%tree%final (); return
else if (node%n_t_channel > feyngraph_set%phs_par%t_channel) then
node%keep = .false.;
kingraph%keep = .false.; call kingraph%tree%final (); return
end if
else
node%mapping = EXTERNAL_PRT
node%on_shell = .true.
node%ext_mass_sum &
= t_node%ext_mass_sum &
+ s_node%ext_mass_sum
node%effective_mass = node%particle%mass
if (.not. (node%ext_mass_sum < feyngraph_set%phs_par%sqrts)) then
node%keep = .false.
kingraph%keep = .false.; call kingraph%tree%final (); return
end if
if (kingraph%keep) then
if (t_node%incoming .and. s_node%log_enhanced) then
call s_node%f_node%k_node_list%add_entry (new_s_node, recycle=.false.)
new_s_node = s_node
new_s_node%daughter1 => s_node%daughter1
new_s_node%daughter2 => s_node%daughter2
if (s_node%index == node%daughter1%index) then
node%daughter1 => new_s_node
else if (s_node%index == node%daughter2%index) then
node%daughter2 => new_s_node
end if
new_s_node%subtree = s_node%subtree
new_s_node%mapping = NO_MAPPING
new_s_node%log_enhanced = .false.
new_s_node%n_log_enhanced &
= new_s_node%n_log_enhanced - 1
new_s_node%log_enhanced = .false.
where (new_s_node%subtree%bc == new_s_node%bincode)
new_s_node%subtree%mapping = NO_MAPPING
endwhere
else if ((t_node%t_line .or. t_node%incoming) .and. &
t_node%mapping == U_CHANNEL) then
t_node%mapping = T_CHANNEL
where (t_node%subtree%bc == t_node%bincode)
t_node%subtree%mapping = T_CHANNEL
endwhere
else if (t_node%incoming .and. &
.not. associated (s_node%daughter1) .and. &
.not. associated (s_node%daughter2)) then
call s_node%f_node%k_node_list%add_entry (new_s_node, recycle=.false.)
new_s_node = s_node
new_s_node%mapping = ON_SHELL
new_s_node%daughter1 => s_node%daughter1
new_s_node%daughter2 => s_node%daughter2
new_s_node%subtree = s_node%subtree
if (s_node%index == node%daughter1%index) then
node%daughter1 => new_s_node
else if (s_node%index == node%daughter2%index) then
node%daughter2 => new_s_node
end if
where (new_s_node%subtree%bc == new_s_node%bincode)
new_s_node%subtree%mapping = ON_SHELL
endwhere
end if
end if
call node%subtree%add_entry (node)
node%multiplicity &
= node%daughter1%multiplicity &
+ node%daughter2%multiplicity
node%n_resonances &
= node%daughter1%n_resonances &
+ node%daughter2%n_resonances
node%n_off_shell &
= node%daughter1%n_off_shell &
+ node%daughter2%n_off_shell
node%n_log_enhanced &
= node%daughter1%n_log_enhanced &
+ node%daughter2%n_log_enhanced
node%n_t_channel &
= node%daughter1%n_t_channel &
+ node%daughter2%n_t_channel
if (node%n_off_shell > feyngraph_set%phs_par%off_shell) then
node%keep = .false.
kingraph%keep = .false.; call kingraph%tree%final (); return
else if (node%n_t_channel > feyngraph_set%phs_par%t_channel) then
node%keep = .false.
kingraph%keep = .false.; call kingraph%tree%final (); return
else
kingraph%grove_prop%multiplicity = node%multiplicity
kingraph%grove_prop%n_resonances = node%n_resonances
kingraph%grove_prop%n_off_shell = node%n_off_shell
kingraph%grove_prop%n_log_enhanced = node%n_log_enhanced
kingraph%grove_prop%n_t_channel = node%n_t_channel
end if
end if
end if
contains
subroutine beam_decay (fatal_beam_decay)
logical, intent(in) :: fatal_beam_decay
write (msg_buffer, "(1x,A,1x,'->',1x,A,1x,A)") &
t_node%particle%particle_label, &
node%particle%particle_label, &
s_node%particle%particle_label
call msg_message
write (msg_buffer, "(1x,'mass(',A,') =',1x,E17.10)") &
t_node%particle%particle_label, t_node%particle%mass
call msg_message
write (msg_buffer, "(1x,'mass(',A,') =',1x,E17.10)") &
node%particle%particle_label, node%particle%mass
call msg_message
write (msg_buffer, "(1x,'mass(',A,') =',1x,E17.10)") &
s_node%particle%particle_label, s_node%particle%mass
call msg_message
if (fatal_beam_decay) then
call msg_fatal (" Phase space: Initial beam particle can decay")
else
call msg_warning (" Phase space: Initial beam particle can decay")
end if
end subroutine beam_decay
end subroutine node_compute_t_line
@ %def node_compute_t_line
@ After all pure s-channel subdiagrams have already been created from the
corresponding [[f_nodes]] and mappings have been determined for their
nodes, we complete the calculations here. In a first step, the
[[kingraphs]] have to be created on the basis of the existing
[[k_nodes]], which means in particular that a [[feyngraph]] can give
rise to several [[kingraphs]] which will all be attached to the linked
list of the [[feyngraph]]. The calculations which remain are of different
kinds for decay and scattering processes. In a decay process the
kinematical calculations have to be done for the [[root]] node. In a
scattering process, after the creation of [[kingraphs]] in the first
step, there will be only [[kingraphs]] with the first incoming particle
as the [[root]] of the tree. For these graphs the [[inverse]] variable
has the value [[.false.]]. Before performing any calculations on these
graphs we make a so-called inverse copy of the graph (see below), which
will also be attached to the linked list. Since the s-channel subgraph
calculations have already been completed, only the t-line computations
remain.
<<Cascades2: feyngraph: TBP>>=
procedure :: make_inverse_kingraphs => feyngraph_make_inverse_kingraphs
<<Cascades2: procedures>>=
subroutine feyngraph_make_inverse_kingraphs (feyngraph)
class (feyngraph_t), intent (inout) :: feyngraph
type (kingraph_t), pointer :: current
current => feyngraph%kin_first
do while (associated (current))
if (current%inverse) exit
call current%make_inverse_copy (feyngraph)
current => current%next
enddo
end subroutine feyngraph_make_inverse_kingraphs
@ %def feyngraph_make_inverse_kingraphs
<<Cascades2: feyngraph: TBP>>=
procedure :: compute_mappings => feyngraph_compute_mappings
<<Cascades2: procedures>>=
subroutine feyngraph_compute_mappings (feyngraph, feyngraph_set)
class (feyngraph_t), intent (inout) :: feyngraph
type (feyngraph_set_t), intent (inout) :: feyngraph_set
type (kingraph_t), pointer :: current
current => feyngraph%kin_first
do while (associated (current))
if (feyngraph_set%process_type == DECAY) then
call kingraph_assign_mappings_s (feyngraph, current, feyngraph_set)
else if (feyngraph_set%process_type == SCATTERING) then
call kingraph_compute_mappings_t_line (feyngraph, current, feyngraph_set)
end if
current => current%next
enddo
end subroutine feyngraph_compute_mappings
@ %def feyngraph_compute_mappings
@ Here we control the mapping calculations for the nodes of s-channel
subgraphs. We start with the nodes with the smallest number of subtree
nodes and always increase this number by two because nodes have exactly
zero or two daughter nodes. We create the [[k_nodes]] using the
[[k_node_list]] of each [[f_node]]. The number of nodes which have to
be created depends of the number of existing daughter nodes, which means
that we have to create a node for each combination of existing and
valid (the ones which we [[keep]]) daughter nodes. If the node
corresponds to an external particle, we create only one node, since
there are no daughter nodes. If the particle is not external and
the daughter [[f_nodes]] do not contain any valid [[k_nodes]], we do
not create a new [[k_nodes]] either. When the calculations for all nodes
with the same number of subtree nodes have been completed, we compare
the valid nodes to eliminate equivalences (see below).
<<Cascades2: procedures>>=
subroutine f_node_list_compute_mappings_s (feyngraph_set)
type (feyngraph_set_t), intent (inout) :: feyngraph_set
type (f_node_ptr_t), dimension(:), allocatable :: set
type (k_node_ptr_t), dimension(:), allocatable :: k_set
type (k_node_entry_t), pointer :: k_entry
type (f_node_entry_t), pointer :: current
type (k_node_list_t), allocatable :: compare_list
integer :: n_entries
integer :: pos
integer :: i, j, k
do i = 1, feyngraph_set%f_node_list%max_tree_size - 2, 2
!!! Counter number of f_nodes with subtree size i for s channel calculations
n_entries = 0
if (feyngraph_set%use_dag) then
do j=1, feyngraph_set%dag%n_nodes
if (allocated (feyngraph_set%dag%node(j)%f_node)) then
do k=1, size(feyngraph_set%dag%node(j)%f_node)
if (associated (feyngraph_set%dag%node(j)%f_node(k)%node)) then
if (.not. (feyngraph_set%dag%node(j)%f_node(k)%node%incoming &
.or. feyngraph_set%dag%node(j)%f_node(k)%node%t_line) &
.and. feyngraph_set%dag%node(j)%f_node(k)%node%n_subtree_nodes == i) then
n_entries = n_entries + 1
end if
end if
enddo
end if
enddo
else
current => feyngraph_set%f_node_list%first
do while (associated (current))
if (.not. (current%node%incoming .or. current%node%t_line) &
.and. current%node%n_subtree_nodes == i) then
n_entries = n_entries + 1
end if
current => current%next
enddo
end if
if (n_entries == 0) exit
!!! Create a temporary k node list for comparison
allocate (set(n_entries))
pos = 0
if (feyngraph_set%use_dag) then
do j=1, feyngraph_set%dag%n_nodes
if (allocated (feyngraph_set%dag%node(j)%f_node)) then
do k=1, size(feyngraph_set%dag%node(j)%f_node)
if (associated (feyngraph_set%dag%node(j)%f_node(k)%node)) then
if (.not. (feyngraph_set%dag%node(j)%f_node(k)%node%incoming &
.or. feyngraph_set%dag%node(j)%f_node(k)%node%t_line) &
.and. feyngraph_set%dag%node(j)%f_node(k)%node%n_subtree_nodes == i) then
pos = pos + 1
set(pos)%node => feyngraph_set%dag%node(j)%f_node(k)%node
end if
end if
enddo
end if
enddo
else
current => feyngraph_set%f_node_list%first
do while (associated (current))
if (.not. (current%node%incoming .or. current%node%t_line) &
.and. current%node%n_subtree_nodes == i) then
pos = pos + 1
set(pos)%node => current%node
end if
current => current%next
enddo
end if
allocate (compare_list)
compare_list%observer = .true.
do j = 1, n_entries
call k_node_init_from_f_node (set(j)%node, k_set, &
feyngraph_set)
if (allocated (k_set)) deallocate (k_set)
enddo
!$OMP PARALLEL DO PRIVATE (k_entry)
do j = 1, n_entries
k_entry => set(j)%node%k_node_list%first
do while (associated (k_entry))
call node_assign_mapping_s(feyngraph_set%first, k_entry%node, feyngraph_set)
k_entry => k_entry%next
enddo
enddo
!$OMP END PARALLEL DO
do j = 1, size (set)
k_entry => set(j)%node%k_node_list%first
do while (associated (k_entry))
if (k_entry%node%keep) then
if (k_entry%node%mapping == NO_MAPPING .or. k_entry%node%mapping == NONRESONANT) then
call compare_list%add_pointer (k_entry%node)
end if
end if
k_entry => k_entry%next
enddo
enddo
deallocate (set)
call compare_list%check_subtree_equivalences(feyngraph_set%model)
call compare_list%final
deallocate (compare_list)
enddo
end subroutine f_node_list_compute_mappings_s
@ %def f_node_list_compute_mappings_s
@
\subsection{Fill the grove list}
Find the [[grove]] within the [[grove_list]] for a [[kingraph]] for
which the kinematical calculations and mapping assignments have been completed. The [[groves]]
are defined by the [[grove_prop]] entries and the value of the resonance
hash ([[res_hash]]). Whenever a matching grove does not exist, we
create one. In a first step we consider only part of the grove properties
(see [[grove_prop_match]]) and the resonance hash is ignored, which leads
to a preliminary grove list. In the end all numbers in [[grove_prop]] as
well as the resonance hash are compared, i.e. we create a new
[[grove_list]].
<<Cascades2: grove list: TBP>>=
procedure :: get_grove => grove_list_get_grove
<<Cascades2: procedures>>=
subroutine grove_list_get_grove (grove_list, kingraph, return_grove, preliminary)
class (grove_list_t), intent (inout) :: grove_list
type (kingraph_t), intent (in), pointer :: kingraph
type (grove_t), intent (inout), pointer :: return_grove
logical, intent (in) :: preliminary
type (grove_t), pointer :: current_grove
return_grove => null ()
if (.not. associated(grove_list%first)) then
allocate (grove_list%first)
grove_list%first%grove_prop = kingraph%grove_prop
return_grove => grove_list%first
return
end if
current_grove => grove_list%first
do while (associated (current_grove))
if ((preliminary .and. (current_grove%grove_prop .match. kingraph%grove_prop)) .or. &
(.not. preliminary .and. current_grove%grove_prop == kingraph%grove_prop)) then
return_grove => current_grove
exit
else if (.not. associated (current_grove%next)) then
allocate (current_grove%next)
current_grove%next%grove_prop = kingraph%grove_prop
if (size (kingraph%tree%bc) < 9) &
current_grove%compare_tree%depth = 1
return_grove => current_grove%next
exit
end if
if (associated (current_grove%next)) then
current_grove => current_grove%next
end if
enddo
end subroutine grove_list_get_grove
@ %def grove_list_get_grove
@ Add a valid [[kingraph]] to a [[grove_list]]. We first look for the
[[grove]] which has the grove properties of the [[kingraph]]. If no such
[[grove]] exists so far, it is created.
<<Cascades2: grove list: TBP>>=
procedure :: add_kingraph => grove_list_add_kingraph
<<Cascades2: procedures>>=
subroutine grove_list_add_kingraph (grove_list, kingraph, preliminary, check, model)
class (grove_list_t), intent (inout) :: grove_list
type (kingraph_t), pointer, intent (inout) :: kingraph
logical, intent (in) :: preliminary
logical, intent (in) :: check
type (model_data_t), optional, intent (in) :: model
type (grove_t), pointer :: grove
type (kingraph_t), pointer :: current
integer, save :: index = 0
grove => null ()
current => null ()
if (preliminary) then
if (kingraph%index == 0) then
index = index + 1
kingraph%index = index
end if
end if
call grove_list%get_grove (kingraph, grove, preliminary)
if (check) then
call grove%compare_tree%check_kingraph (kingraph, model, preliminary)
end if
if (kingraph%keep) then
if (associated (grove%first)) then
grove%last%grove_next => kingraph
grove%last => kingraph
else
grove%first => kingraph
grove%last => kingraph
end if
end if
end subroutine grove_list_add_kingraph
@ %ref grove_list_add_kingraph
@ For a given [[feyngraph]] we store all valid [[kingraphs]] in the
[[grove_list]].
<<Cascades2: grove list: TBP>>=
procedure :: add_feyngraph => grove_list_add_feyngraph
<<Cascades2: procedures>>=
subroutine grove_list_add_feyngraph (grove_list, feyngraph, model)
class (grove_list_t), intent (inout) :: grove_list
type (feyngraph_t), intent (inout) :: feyngraph
type (model_data_t), intent (in) :: model
type (kingraph_t), pointer :: current_kingraph, add_kingraph
do while (associated (feyngraph%kin_first))
if (feyngraph%kin_first%keep) then
add_kingraph => feyngraph%kin_first
feyngraph%kin_first => feyngraph%kin_first%next
add_kingraph%next => null ()
call grove_list%add_kingraph (kingraph=add_kingraph, &
preliminary=.true., check=.true., model=model)
else
exit
end if
enddo
if (associated (feyngraph%kin_first)) then
current_kingraph => feyngraph%kin_first
do while (associated (current_kingraph%next))
if (current_kingraph%next%keep) then
add_kingraph => current_kingraph%next
current_kingraph%next => current_kingraph%next%next
add_kingraph%next => null ()
call grove_list%add_kingraph (kingraph=add_kingraph, &
preliminary=.true., check=.true., model=model)
else
current_kingraph => current_kingraph%next
end if
enddo
end if
end subroutine grove_list_add_feyngraph
@ %def grove_list_add_feyngraph
@ Compare two [[grove_prop]] objects. The [[.match.]] operator is used
for preliminary groves in which the [[kingraphs]] share only the 3
numbers [[n_resonances]], [[n_log_enhanced]] and [[n_t_channel]]. These
groves are only used for comparing the kingraphs, because only graphs
within these preliminary groves can be equivalent (the numbers which are
compared here are unambigously fixed by the combination of mappings in
these channels).
<<Cascades2: interfaces>>=
interface operator (.match.)
module procedure grove_prop_match
end interface operator (.match.)
<<Cascades2: procedures>>=
function grove_prop_match (grove_prop1, grove_prop2) result (gp_match)
type (grove_prop_t), intent (in) :: grove_prop1
type (grove_prop_t), intent (in) :: grove_prop2
logical :: gp_match
gp_match = (grove_prop1%n_resonances == grove_prop2%n_resonances) &
.and. (grove_prop1%n_log_enhanced == grove_prop2%n_log_enhanced) &
.and. (grove_prop1%n_t_channel == grove_prop2%n_t_channel)
end function grove_prop_match
@ %def grove_prop_match
@ The equal operator on the other hand will be used when all valid
[[kingraphs]] have been created and mappings have been determined, to
split up the existing (preliminary) grove list, i.e. to create new
groves which are determined by all entries in [[grove_prop_t]].
<<Cascades2: interfaces>>=
interface operator (==)
module procedure grove_prop_equal
end interface operator (==)
<<Cascades2: procedures>>=
function grove_prop_equal (grove_prop1, grove_prop2) result (gp_equal)
type (grove_prop_t), intent (in) :: grove_prop1
type (grove_prop_t), intent (in) :: grove_prop2
logical :: gp_equal
gp_equal = (grove_prop1%res_hash == grove_prop2%res_hash) &
.and. (grove_prop1%n_resonances == grove_prop2%n_resonances) &
.and. (grove_prop1%n_log_enhanced == grove_prop2%n_log_enhanced) &
.and. (grove_prop1%n_off_shell == grove_prop2%n_off_shell) &
.and. (grove_prop1%multiplicity == grove_prop2%multiplicity) &
.and. (grove_prop1%n_t_channel == grove_prop2%n_t_channel)
end function grove_prop_equal
@ %def grove_prop_equal
@
\subsection{Remove equivalent channels}
Here we define the equivalence condition for completed [[kingraphs]].
The aim is to keep those [[kingraphs]] which describe the strongest
peaks of the amplitude. The [[bincodes]] and [[mappings]] have to be
the same for an equivalence, but the [[pdgs]] can be different. At
the same time we check if the trees are exacly the same (up to the
sign of pdg codes) in which case we do not keep both of them. This
can be the case when the incoming particles are the same or their
mutual anti-particles and there are no t-channel lines in the
Feynman diagram to which the kingraph belongs.
<<Cascades2: parameters>>=
integer, parameter :: EMPTY = -999
<<Cascades2: procedures>>=
function kingraph_eqv (kingraph1, kingraph2) result (eqv)
type (kingraph_t), intent (in) :: kingraph1
type (kingraph_t), intent (inout) :: kingraph2
logical :: eqv
integer :: i
logical :: equal
eqv = .false.
do i = kingraph1%tree%n_entries, 1, -1
if (kingraph1%tree%bc(i) /= kingraph2%tree%bc(i)) return
enddo
do i = kingraph1%tree%n_entries, 1, -1
if ( .not. (kingraph1%tree%mapping(i) == kingraph2%tree%mapping(i) &
.or. ((kingraph1%tree%mapping(i) == NO_MAPPING .or. &
kingraph1%tree%mapping(i) == NONRESONANT) .and. &
(kingraph2%tree%mapping(i) == NO_MAPPING .or. &
kingraph2%tree%mapping(i) == NONRESONANT)))) return
enddo
equal = .true.
do i = kingraph1%tree%n_entries, 1, -1
if (abs(kingraph1%tree%pdg(i)) /= abs(kingraph2%tree%pdg(i))) then
equal = .false.;
select case (kingraph1%tree%mapping(i))
case (S_CHANNEL, RADIATION)
select case (kingraph2%tree%mapping(i))
case (S_CHANNEL, RADIATION)
return
end select
end select
end if
enddo
if (equal) then
kingraph2%keep = .false.
call kingraph2%tree%final ()
else
eqv = .true.
end if
end function kingraph_eqv
@ %def kingraph_eqv
@ Select between two [[kingraphs]] which fulfill the equivalence
condition above. This is done by comparing the [[pdg]] values of the
[[tree]] for increasing bincode. If the particles are different at
some place, we usually choose the one which would be returned first by the
subroutine [[match_vertex]] of the model for the daughter [[pdg]] codes.
Since we work here only on the basis of the the [[trees]] of the
completed [[kingraphs]], we have to use the [[bc]] array to determine
the positions of the daughter nodes' entries in the array. The graph
which has to be kept should correspond to the stronger peak at the place
which is compared.
<<Cascades2: procedures>>=
subroutine kingraph_select (kingraph1, kingraph2, model, preliminary)
type (kingraph_t), intent (inout) :: kingraph1
type (kingraph_t), intent (inout) :: kingraph2
type (model_data_t), intent (in) :: model
logical, intent (in) :: preliminary
integer(TC), dimension(:), allocatable :: tmp_bc, daughter_bc
integer, dimension(:), allocatable :: tmp_pdg, daughter_pdg
integer, dimension (:), allocatable :: pdg_match
integer :: i, j
integer :: n_ext1, n_ext2
if (kingraph_eqv (kingraph1, kingraph2)) then
if (.not. preliminary) then
kingraph2%keep = .false.; call kingraph2%tree%final ()
return
end if
do i=1, size (kingraph1%tree%bc)
if (abs(kingraph1%tree%pdg(i)) /= abs(kingraph2%tree%pdg(i))) then
if (kingraph1%tree%mapping(i) /= EXTERNAL_PRT) then
n_ext1 = popcnt (kingraph1%tree%bc(i))
n_ext2 = n_ext1
do j=i+1, size (kingraph1%tree%bc)
if (abs(kingraph1%tree%pdg(j)) /= abs(kingraph2%tree%pdg(j))) then
n_ext2 = popcnt (kingraph1%tree%bc(j))
if (n_ext2 < n_ext1) exit
end if
enddo
if (n_ext2 < n_ext1) cycle
allocate (tmp_bc(i-1))
tmp_bc = kingraph1%tree%bc(:i-1)
allocate (tmp_pdg(i-1))
tmp_pdg = kingraph1%tree%pdg(:i-1)
do j=i-1, 1, - 1
where (iand (tmp_bc(:j-1),tmp_bc(j)) /= 0 &
.or. iand(tmp_bc(:j-1),kingraph1%tree%bc(i)) == 0)
tmp_bc(:j-1) = 0
tmp_pdg(:j-1) = 0
endwhere
enddo
allocate (daughter_bc(size(pack(tmp_bc, tmp_bc /= 0))))
daughter_bc = pack (tmp_bc, tmp_bc /= 0)
allocate (daughter_pdg(size(pack(tmp_pdg, tmp_pdg /= 0))))
daughter_pdg = pack (tmp_pdg, tmp_pdg /= 0)
if (size (daughter_pdg) == 2) then
call model%match_vertex(daughter_pdg(1), daughter_pdg(2), pdg_match)
end if
do j=1, size (pdg_match)
if (abs(pdg_match(j)) == abs(kingraph1%tree%pdg(i))) then
kingraph2%keep = .false.; call kingraph2%tree%final ()
exit
else if (abs(pdg_match(j)) == abs(kingraph2%tree%pdg(i))) then
kingraph1%keep = .false.; call kingraph1%tree%final ()
exit
end if
enddo
deallocate (tmp_bc, tmp_pdg, daughter_bc, daughter_pdg, pdg_match)
if (.not. (kingraph1%keep .and. kingraph2%keep)) exit
end if
end if
enddo
end if
end subroutine kingraph_select
@ %def kingraph_select
@ At the beginning we do not care about the resonance hash, but only
about part of the grove properties, which is defined in
[[grove_prop_match]]. In these resulting preliminary groves the kingraphs
can be equivalent, i.e. we do not have to compare all graphs with each
other but only all graphs within each of these preliminary groves. In the
end we create a new grove list where the grove properties of the
[[kingraphs]] within a [[grove]] have to be exactly the same and in
addition the groves are distinguished by the resonance hash values. Here
the kingraphs are not compared any more, which means that the number of
channels is not reduced any more.
<<Cascades2: grove list: TBP>>=
procedure :: merge => grove_list_merge
<<Cascades2: procedures>>=
subroutine grove_list_merge (target_list, grove_list, model, prc_component)
class (grove_list_t), intent (inout) :: target_list
type (grove_list_t), intent (inout) :: grove_list
type (model_data_t), intent (in) :: model
integer, intent (in) :: prc_component
type (grove_t), pointer :: current_grove
type (kingraph_t), pointer :: current_graph
current_grove => grove_list%first
do while (associated (current_grove))
do while (associated (current_grove%first))
current_graph => current_grove%first
current_grove%first => current_grove%first%grove_next
current_graph%grove_next => null ()
if (current_graph%keep) then
current_graph%prc_component = prc_component
call target_list%add_kingraph(kingraph=current_graph, &
preliminary=.false., check=.true., model=model)
else
call current_graph%final ()
deallocate (current_graph)
end if
enddo
current_grove => current_grove%next
enddo
end subroutine grove_list_merge
@ %def grove_list_merge
@ Recreate a grove list where we have different groves for different
resonance hashes.
<<Cascades2: grove list: TBP>>=
procedure :: rebuild => grove_list_rebuild
<<Cascades2: procedures>>=
subroutine grove_list_rebuild (grove_list)
class (grove_list_t), intent (inout) :: grove_list
type (grove_list_t) :: tmp_list
type (grove_t), pointer :: current_grove
type (grove_t), pointer :: remove_grove
type (kingraph_t), pointer :: current_graph
type (kingraph_t), pointer :: next_graph
tmp_list%first => grove_list%first
grove_list%first => null ()
current_grove => tmp_list%first
do while (associated (current_grove))
current_graph => current_grove%first
do while (associated (current_graph))
call current_graph%assign_resonance_hash ()
next_graph => current_graph%grove_next
current_graph%grove_next => null ()
if (current_graph%keep) then
call grove_list%add_kingraph (kingraph=current_graph, &
preliminary=.false., check=.false.)
end if
current_graph => next_graph
enddo
current_grove => current_grove%next
enddo
call tmp_list%final
end subroutine grove_list_rebuild
@ %def grove_list_rebuild
@
\subsection{Write the phase-space file}
The phase-space file is written from the graphs which survive the
calculations and equivalence checks and are in the grove list. It is
written grove by grove. The output should be the same as in the
corresponding procedure [[cascade_set_write_file_format]] of
[[cascades]], up to the order of groves and channels.
<<Cascades2: public>>=
public :: feyngraph_set_write_file_format
<<Cascades2: procedures>>=
subroutine feyngraph_set_write_file_format (feyngraph_set, u)
type (feyngraph_set_t), intent (in) :: feyngraph_set
integer, intent (in) :: u
type (grove_t), pointer :: grove
integer :: channel_number
integer :: grove_number
channel_number = 0
grove_number = 0
grove => feyngraph_set%grove_list%first
do while (associated (grove))
grove_number = grove_number + 1
call grove%write_file_format (feyngraph_set, grove_number, channel_number, u)
grove => grove%next
enddo
end subroutine feyngraph_set_write_file_format
@ %def feyngraph_set_write_file_format
@ Write the relevant information of the [[kingraphs]] of a [[grove]] and
the grove properties in the file format.
<<Cascades2: grove: TBP>>=
procedure :: write_file_format => grove_write_file_format
<<Cascades2: procedures>>=
recursive subroutine grove_write_file_format (grove, feyngraph_set, gr_number, ch_number, u)
class (grove_t), intent (in) :: grove
type (feyngraph_set_t), intent (in) :: feyngraph_set
integer, intent (in) :: u
integer, intent (inout) :: gr_number
integer, intent (inout) :: ch_number
type (kingraph_t), pointer :: current
1 format(3x,A,1x,40(1x,I4))
write (u, "(A)")
write (u, "(1x,'!',1x,A,1x,I0,A)", advance='no') &
'Multiplicity =', grove%grove_prop%multiplicity, ","
select case (grove%grove_prop%n_resonances)
case (0)
write (u, '(1x,A)', advance='no') 'no resonances, '
case (1)
write (u, '(1x,A)', advance='no') '1 resonance, '
case default
write (u, '(1x,I0,1x,A)', advance='no') &
grove%grove_prop%n_resonances, 'resonances, '
end select
write (u, '(1x,I0,1x,A)', advance='no') &
grove%grove_prop%n_log_enhanced, 'logs, '
write (u, '(1x,I0,1x,A)', advance='no') &
grove%grove_prop%n_off_shell, 'off-shell, '
select case (grove%grove_prop%n_t_channel)
case (0); write (u, '(1x,A)') 's-channel graph'
case (1); write (u, '(1x,A)') '1 t-channel line'
case default
write(u,'(1x,I0,1x,A)') &
grove%grove_prop%n_t_channel, 't-channel lines'
end select
write (u, '(1x,A,I0)') 'grove #', gr_number
current => grove%first
do while (associated (current))
if (current%keep) then
ch_number = ch_number + 1
call current%write_file_format (feyngraph_set, ch_number, u)
end if
current => current%grove_next
enddo
end subroutine grove_write_file_format
@ %def grove_write_file_format
@ Write the relevant information of a valid [[kingraph]] in the file
format. The information is extracted from the [[tree]].
<<Cascades2: kingraph: TBP>>=
procedure :: write_file_format => kingraph_write_file_format
<<Cascades2: procedures>>=
subroutine kingraph_write_file_format (kingraph, feyngraph_set, ch_number, u)
class (kingraph_t), intent (in) :: kingraph
type (feyngraph_set_t), intent (in) :: feyngraph_set
integer, intent (in) :: ch_number
integer, intent (in) :: u
integer :: i
integer(TC) :: bincode_incoming
2 format(3X,'map',1X,I3,1X,A,1X,I9,1X,'!',1X,A)
!!! determine bincode of incoming particle from tree
bincode_incoming = maxval (kingraph%tree%bc)
write (unit=u, fmt='(1X,A,I0)') '! Channel #', ch_number
write (unit=u, fmt='(3X,A,1X)', advance='no') 'tree'
do i=1, size (kingraph%tree%bc)
if (kingraph%tree%mapping(i) >=0 .or. kingraph%tree%mapping(i) == NONRESONANT &
.or. (kingraph%tree%bc(i) == bincode_incoming &
.and. feyngraph_set%process_type == DECAY)) then
write (unit=u, fmt='(1X,I0)', advance='no') kingraph%tree%bc(i)
end if
enddo
write (unit=u, fmt='(A)', advance='yes')
do i=1, size(kingraph%tree%bc)
select case (kingraph%tree%mapping(i))
case (NO_MAPPING, NONRESONANT, EXTERNAL_PRT)
case (S_CHANNEL)
write (unit=u, fmt=2) kingraph%tree%bc(i), 's_channel', &
kingraph%tree%pdg(i), &
trim(get_particle_name (feyngraph_set, kingraph%tree%pdg(i)))
case (T_CHANNEL)
write (unit=u, fmt=2) kingraph%tree%bc(i), 't_channel', &
abs (kingraph%tree%pdg(i)), &
trim(get_particle_name (feyngraph_set, abs(kingraph%tree%pdg(i))))
case (U_CHANNEL)
write (unit=u, fmt=2) kingraph%tree%bc(i), 'u_channel', &
abs (kingraph%tree%pdg(i)), &
trim(get_particle_name (feyngraph_set, abs(kingraph%tree%pdg(i))))
case (RADIATION)
write (unit=u, fmt=2) kingraph%tree%bc(i), 'radiation', &
kingraph%tree%pdg(i), &
trim(get_particle_name (feyngraph_set, kingraph%tree%pdg(i)))
case (COLLINEAR)
write (unit=u, fmt=2) kingraph%tree%bc(i), 'collinear', &
kingraph%tree%pdg(i), &
trim(get_particle_name (feyngraph_set, kingraph%tree%pdg(i)))
case (INFRARED)
write (unit=u, fmt=2) kingraph%tree%bc(i), 'infrared ', &
kingraph%tree%pdg(i), &
trim(get_particle_name (feyngraph_set, kingraph%tree%pdg(i)))
case (ON_SHELL)
write (unit=u, fmt=2) kingraph%tree%bc(i), 'on_shell ', &
kingraph%tree%pdg(i), &
trim(get_particle_name (feyngraph_set, kingraph%tree%pdg(i)))
case default
call msg_bug (" Impossible mapping mode encountered")
end select
enddo
end subroutine kingraph_write_file_format
@ %def kingraph_write_file_format
@ Get the particle name from the [[particle]] array of the
[[feyngraph_set]]. This is needed for the phs file creation.
<<Cascades2: procedures>>=
function get_particle_name (feyngraph_set, pdg) result (particle_name)
type (feyngraph_set_t), intent (in) :: feyngraph_set
integer, intent (in) :: pdg
character (len=LABEL_LEN) :: particle_name
integer :: i
do i=1, size (feyngraph_set%particle)
if (feyngraph_set%particle(i)%pdg == pdg) then
particle_name = feyngraph_set%particle(i)%particle_label
exit
end if
enddo
end function get_particle_name
@ %def get_particle_name
@
\subsection{Invert a graph}
All Feynman diagrams given by O'Mega look like a decay. The [[feyngraph]]
which is constructed from this output also looks like a decay, where one
of the incoming particles is the decaying particle (or the root of the
tree). The calculations can in principle be done on this data structure.
However, it is also performed with the other incoming particle as
the root. The first part of the calculation is the same for both cases.
For the second part we need to transform/turn the graphs such that the
other incoming particle becomes the root. This is done by identifying
the incoming particles from the O'Mega output (the first one is simply
the root of the existing tree, the second contains [2] in the
[[particle_label]]) and the nodes/particles which connect both incoming
particles (here we set [[t_line = .true.]]). At the same time we set the
pointers [[inverse_daughter1]] and [[inverse_daughter2]] for the
corresponding node, which point to the mother node and the other daughter
of the mother node; these will be the daughters of the node in the
inverted [[feyngraph]].
<<Cascades2: feyngraph: TBP>>=
procedure :: make_invertible => feyngraph_make_invertible
<<Cascades2: procedures>>=
subroutine feyngraph_make_invertible (feyngraph)
class (feyngraph_t), intent (inout) :: feyngraph
logical :: t_line_found
feyngraph%root%incoming = .true.
t_line_found = .false.
if (associated (feyngraph%root%daughter1)) then
call f_node_t_line_check (feyngraph%root%daughter1, t_line_found)
if (.not. t_line_found) then
if (associated (feyngraph%root%daughter2)) then
call f_node_t_line_check (feyngraph%root%daughter2, t_line_found)
end if
end if
end if
contains
<<k node t line check>>
end subroutine feyngraph_make_invertible
@ %def feyngraph_make_invertible
@ Check if a node has to be [[t_line]] or [[incoming]] and assign
inverse daughter pointers.
<<k node t line check>>=
recursive subroutine f_node_t_line_check (node, t_line_found)
type (f_node_t), target, intent (inout) :: node
integer :: pos
logical, intent (inout) :: t_line_found
if (associated (node%daughter1)) then
call f_node_t_line_check (node%daughter1, t_line_found)
if (node%daughter1%incoming .or. node%daughter1%t_line) then
node%t_line = .true.
else if (associated (node%daughter2)) then
call f_node_t_line_check (node%daughter2, t_line_found)
if (node%daughter2%incoming .or. node%daughter2%t_line) then
node%t_line = .true.
end if
end if
else
pos = index (node%particle_label, '[') + 1
if (node%particle_label(pos:pos) == '2') then
node%incoming = .true.
t_line_found = .true.
end if
end if
end subroutine f_node_t_line_check
@ %def k_node_t_line_check
@ Make an inverted copy of a [[kingraph]] using the inverse daughter
pointers.
<<Cascades2: kingraph: TBP>>=
procedure :: make_inverse_copy => kingraph_make_inverse_copy
<<Cascades2: procedures>>=
subroutine kingraph_make_inverse_copy (original_kingraph, feyngraph)
class (kingraph_t), intent (inout) :: original_kingraph
type (feyngraph_t), intent (inout) :: feyngraph
type (kingraph_t), pointer :: kingraph_copy
type (k_node_t), pointer :: potential_root
allocate (kingraph_copy)
if (associated (feyngraph%kin_last)) then
allocate (feyngraph%kin_last%next)
feyngraph%kin_last => feyngraph%kin_last%next
else
allocate(feyngraph%kin_first)
feyngraph%kin_last => feyngraph%kin_first
end if
kingraph_copy => feyngraph%kin_last
call kingraph_set_inverse_daughters (original_kingraph)
kingraph_copy%inverse = .true.
kingraph_copy%n_nodes = original_kingraph%n_nodes
kingraph_copy%keep = original_kingraph%keep
potential_root => original_kingraph%root
do while (.not. potential_root%incoming .or. &
(associated (potential_root%daughter1) .and. associated (potential_root%daughter2)))
if (potential_root%daughter1%incoming .or. potential_root%daughter1%t_line) then
potential_root => potential_root%daughter1
else if (potential_root%daughter2%incoming .or. potential_root%daughter2%t_line) then
potential_root => potential_root%daughter2
end if
enddo
call node_inverse_deep_copy (potential_root, kingraph_copy%root)
end subroutine kingraph_make_inverse_copy
@ %def kingraph_make_inverse_copy
@ Recursively deep-copy nodes, but along the t-line the inverse daughters
become the new daughters. We need a deep copy only for the [[incoming]]
or [[t_line]] nodes. For the other nodes (of s-channel subgraphs) we set
only pointers to the existing nodes of the non-inverted graph.
<<Cascades2: procedures>>=
recursive subroutine node_inverse_deep_copy (original_node, node_copy)
type (k_node_t), intent (in) :: original_node
type (k_node_t), pointer, intent (out) :: node_copy
call original_node%f_node%k_node_list%add_entry(node_copy, recycle=.false.)
node_copy = original_node
if (node_copy%t_line .or. node_copy%incoming) then
node_copy%particle => original_node%particle%anti
else
node_copy%particle => original_node%particle
end if
if (associated (original_node%inverse_daughter1) .and. associated (original_node%inverse_daughter2)) then
if (original_node%inverse_daughter1%incoming .or. original_node%inverse_daughter1%t_line) then
node_copy%daughter2 => original_node%inverse_daughter2
call node_inverse_deep_copy (original_node%inverse_daughter1, &
node_copy%daughter1)
else if (original_node%inverse_daughter2%incoming .or. original_node%inverse_daughter2%t_line) then
node_copy%daughter1 => original_node%inverse_daughter1
call node_inverse_deep_copy (original_node%inverse_daughter2, &
node_copy%daughter2)
end if
end if
end subroutine node_inverse_deep_copy
@ %def node_inverse_deep_copy
@
\subsection{Find phase-space parametrizations}
Perform all mapping calculations for a single process and store valid
[[kingraphs]] (channels) into the grove list, without caring for instance
about the resonance hash values.
<<Cascades2: public>>=
public :: feyngraph_set_generate_single
<<Cascades2: procedures>>=
subroutine feyngraph_set_generate_single (feyngraph_set, model, n_in, n_out, &
phs_par, fatal_beam_decay, u_in)
type(feyngraph_set_t), intent(inout) :: feyngraph_set
type(model_data_t), target, intent(in) :: model
integer, intent(in) :: n_in, n_out
type(phs_parameters_t), intent(in) :: phs_par
logical, intent(in) :: fatal_beam_decay
integer, intent(in) :: u_in
feyngraph_set%n_in = n_in
feyngraph_set%n_out = n_out
feyngraph_set%process_type = n_in
feyngraph_set%phs_par = phs_par
feyngraph_set%model => model
if (debug_on) call msg_debug (D_PHASESPACE, "Construct relevant Feynman diagrams from Omega output")
call feyngraph_set%build (u_in)
if (debug_on) call msg_debug (D_PHASESPACE, "Find phase-space parametrizations")
call feyngraph_set_find_phs_parametrizations(feyngraph_set)
end subroutine feyngraph_set_generate_single
@ %def feyngraph_set_generate_single
@ Find the phase space parametrizations. We start with the computation
of pure s-channel subtrees, i.e. we determine mappings and compare
subtrees in order to reduce the number of channels. This can be
parallelized easily. When all s-channel [[k_nodes]] exist, the possible
[[kingraphs]] are created using these nodes and we determine mappings for
t-channel nodes.
<<Cascades2: procedures>>=
subroutine feyngraph_set_find_phs_parametrizations (feyngraph_set)
class (feyngraph_set_t), intent (inout) :: feyngraph_set
type (feyngraph_t), pointer :: current => null ()
type (feyngraph_ptr_t), dimension (:), allocatable :: set
integer :: pos
integer :: i
allocate (set (feyngraph_set%n_graphs))
pos = 0
current => feyngraph_set%first
do while (associated (current))
pos = pos + 1
set(pos)%graph => current
current => current%next
enddo
if (feyngraph_set%process_type == SCATTERING) then
!$OMP PARALLEL DO
do i=1, feyngraph_set%n_graphs
if (set(i)%graph%keep) then
call set(i)%graph%make_invertible ()
end if
enddo
!$OMP END PARALLEL DO
end if
call f_node_list_compute_mappings_s (feyngraph_set)
do i=1, feyngraph_set%n_graphs
if (set(i)%graph%keep) then
call set(i)%graph%make_kingraphs (feyngraph_set)
end if
enddo
if (feyngraph_set%process_type == SCATTERING) then
do i=1, feyngraph_set%n_graphs
if (set(i)%graph%keep) then
call set(i)%graph%make_inverse_kingraphs ()
end if
enddo
end if
do i=1, feyngraph_set%n_graphs
if (set(i)%graph%keep) then
call set(i)%graph%compute_mappings (feyngraph_set)
end if
enddo
do i=1, feyngraph_set%n_graphs
if (set(i)%graph%keep) then
call feyngraph_set%grove_list%add_feyngraph (set(i)%graph, &
feyngraph_set%model)
end if
enddo
end subroutine feyngraph_set_find_phs_parametrizations
@ %def feyngraph_set_find_phs_parametrizations
@ Compare objects of type [[tree_t]].
<<Cascades2: interfaces>>=
interface operator (==)
module procedure tree_equal
end interface operator (==)
<<Cascades2: procedures>>=
elemental function tree_equal (tree1, tree2) result (flag)
type (tree_t), intent (in) :: tree1, tree2
logical :: flag
if (tree1%n_entries == tree2%n_entries) then
if (tree1%bc(size(tree1%bc)) == tree2%bc(size(tree2%bc))) then
flag = all (tree1%mapping == tree2%mapping) .and. &
all (tree1%bc == tree2%bc) .and. &
all (abs(tree1%pdg) == abs(tree2%pdg))
else
flag = .false.
end if
else
flag = .false.
end if
end function tree_equal
@ %def tree_equal
@ Select between equivalent subtrees (type [[tree_t]]). This is similar
to [[kingraph_select]], but we compare only positions with mappings
[[NONRESONANT]] and [[NO_MAPPING]].
<<Cascades2: interfaces>>=
interface operator (.eqv.)
module procedure subtree_eqv
end interface operator (.eqv.)
<<Cascades2: procedures>>=
pure function subtree_eqv (subtree1, subtree2) result (eqv)
type (tree_t), intent (in) :: subtree1, subtree2
logical :: eqv
integer :: root_pos
integer :: i
logical :: equal
eqv = .false.
if (subtree1%n_entries /= subtree2%n_entries) return
root_pos = subtree1%n_entries
if (subtree1%mapping(root_pos) == NONRESONANT .or. &
subtree2%mapping(root_pos) == NONRESONANT .or. &
(subtree1%mapping(root_pos) == NO_MAPPING .and. &
subtree2%mapping(root_pos) == NO_MAPPING .and. &
abs(subtree1%pdg(root_pos)) == abs(subtree2%pdg(root_pos)))) then
do i = subtree1%n_entries, 1, -1
if (subtree1%bc(i) /= subtree2%bc(i)) return
enddo
equal = .true.
do i = subtree1%n_entries, 1, -1
if (abs(subtree1%pdg(i)) /= abs (subtree2%pdg(i))) then
select case (subtree1%mapping(i))
case (NO_MAPPING, NONRESONANT)
select case (subtree2%mapping(i))
case (NO_MAPPING, NONRESONANT)
equal = .false.
case default
return
end select
case default
return
end select
end if
enddo
do i = subtree1%n_entries, 1, -1
if (subtree1%mapping(i) /= subtree2%mapping(i)) then
select case (subtree1%mapping(i))
case (NO_MAPPING, NONRESONANT)
select case (subtree2%mapping(i))
case (NO_MAPPING, NONRESONANT)
case default
return
end select
case default
return
end select
end if
enddo
if (.not. equal) eqv = .true.
end if
end function subtree_eqv
@ %def subtree_eqv
<<Cascades2: procedures>>=
subroutine subtree_select (subtree1, subtree2, model)
type (tree_t), intent (inout) :: subtree1, subtree2
type (model_data_t), intent (in) :: model
integer :: j, k
integer(TC), dimension(:), allocatable :: tmp_bc, daughter_bc
integer, dimension(:), allocatable :: tmp_pdg, daughter_pdg
integer, dimension (:), allocatable :: pdg_match
if (subtree1 .eqv. subtree2) then
do j=1, subtree1%n_entries
if (abs(subtree1%pdg(j)) /= abs(subtree2%pdg(j))) then
tmp_bc = subtree1%bc(:j-1); tmp_pdg = subtree1%pdg(:j-1)
do k=j-1, 1, - 1
where (iand (tmp_bc(:k-1),tmp_bc(k)) /= 0 &
.or. iand(tmp_bc(:k-1),subtree1%bc(j)) == 0)
tmp_bc(:k-1) = 0
tmp_pdg(:k-1) = 0
endwhere
enddo
daughter_bc = pack (tmp_bc, tmp_bc /= 0)
daughter_pdg = pack (tmp_pdg, tmp_pdg /= 0)
if (size (daughter_pdg) == 2) then
call model%match_vertex(daughter_pdg(1), daughter_pdg(2), pdg_match)
if (.not. allocated (pdg_match)) then
!!! Relevant if tree contains only abs (pdg). In this case, changing the
!!! sign of one of the pdg codes should give a result.
call model%match_vertex(-daughter_pdg(1), daughter_pdg(2), pdg_match)
end if
end if
do k=1, size (pdg_match)
if (abs(pdg_match(k)) == abs(subtree1%pdg(j))) then
if (subtree1%keep) subtree2%keep = .false.
exit
else if (abs(pdg_match(k)) == abs(subtree2%pdg(j))) then
if (subtree2%keep) subtree1%keep = .false.
exit
end if
enddo
deallocate (tmp_bc, tmp_pdg, daughter_bc, daughter_pdg, pdg_match)
if (.not. (subtree1%keep .and. subtree2%keep)) exit
end if
enddo
end if
end subroutine subtree_select
@ %def subtree_select
@ Assign a resonance hash value to a [[kingraph]], like in [[cascades]],
but here without the array [[tree_resonant]].
<<Cascades2: kingraph: TBP>>=
procedure :: assign_resonance_hash => kingraph_assign_resonance_hash
<<Cascades2: procedures>>=
subroutine kingraph_assign_resonance_hash (kingraph)
class (kingraph_t), intent (inout) :: kingraph
logical, dimension (:), allocatable :: tree_resonant
integer(i8), dimension(1) :: mold
allocate (tree_resonant (kingraph%tree%n_entries))
tree_resonant = (kingraph%tree%mapping == S_CHANNEL)
kingraph%grove_prop%res_hash = hash (transfer &
([sort (pack (kingraph%tree%pdg, tree_resonant)), &
sort (pack (abs (kingraph%tree%pdg), &
kingraph%tree%mapping == T_CHANNEL .or. &
kingraph%tree%mapping == U_CHANNEL))], mold))
deallocate (tree_resonant)
end subroutine kingraph_assign_resonance_hash
@ %def kingraph_assign_resonance_hash
@ Write the process in the bincode format. This is again a copy of the
corresponding procedure in [[cascades]], using [[feyngraph_set]] instead
of [[cascade_set]] as an argument.
<<Cascades2: public>>=
public :: feyngraph_set_write_process_bincode_format
<<Cascades2: procedures>>=
subroutine feyngraph_set_write_process_bincode_format (feyngraph_set, unit)
type(feyngraph_set_t), intent(in), target :: feyngraph_set
integer, intent(in), optional :: unit
integer, dimension(:), allocatable :: bincode, field_width
integer :: n_in, n_out, n_tot, n_flv
integer :: u, f, i, bc
character(20) :: str
type(string_t) :: fmt_head
type(string_t), dimension(:), allocatable :: fmt_proc
u = given_output_unit (unit); if (u < 0) return
if (.not. allocated (feyngraph_set%flv)) return
write (u, "('!',1x,A)") "List of subprocesses with particle bincodes:"
n_in = feyngraph_set%n_in
n_out = feyngraph_set%n_out
n_tot = n_in + n_out
n_flv = size (feyngraph_set%flv, 2)
allocate (bincode (n_tot), field_width (n_tot), fmt_proc (n_tot))
bc = 1
do i = 1, n_out
bincode(n_in + i) = bc
bc = 2 * bc
end do
do i = n_in, 1, -1
bincode(i) = bc
bc = 2 * bc
end do
do i = 1, n_tot
write (str, "(I0)") bincode(i)
field_width(i) = len_trim (str)
do f = 1, n_flv
field_width(i) = max (field_width(i), &
len (feyngraph_set%flv(i,f)%get_name ()))
end do
end do
fmt_head = "('!'"
do i = 1, n_tot
fmt_head = fmt_head // ",1x,"
fmt_proc(i) = "(1x,"
write (str, "(I0)") field_width(i)
fmt_head = fmt_head // "I" // trim(str)
fmt_proc(i) = fmt_proc(i) // "A" // trim(str)
if (i == n_in) then
fmt_head = fmt_head // ",1x,' '"
end if
end do
do i = 1, n_tot
fmt_proc(i) = fmt_proc(i) // ")"
end do
fmt_head = fmt_head // ")"
write (u, char (fmt_head)) bincode
do f = 1, n_flv
write (u, "('!')", advance="no")
do i = 1, n_tot
write (u, char (fmt_proc(i)), advance="no") &
char (feyngraph_set%flv(i,f)%get_name ())
if (i == n_in) write (u, "(1x,'=>')", advance="no")
end do
write (u, *)
end do
write (u, char (fmt_head)) bincode
end subroutine feyngraph_set_write_process_bincode_format
@ %def feyngraph_set_write_process_bincode_format
@ Write tex file for graphical display of channels.
<<Cascades2: public>>=
public :: feyngraph_set_write_graph_format
<<Cascades2: procedures>>=
subroutine feyngraph_set_write_graph_format (feyngraph_set, filename, process_id, unit)
type(feyngraph_set_t), intent(in), target :: feyngraph_set
type(string_t), intent(in) :: filename, process_id
integer, intent(in), optional :: unit
type(kingraph_t), pointer :: kingraph
type(grove_t), pointer :: grove
integer :: u, n_grove, count, pgcount
logical :: first_in_grove
u = given_output_unit (unit); if (u < 0) return
write (u, '(A)') "\documentclass[10pt]{article}"
write (u, '(A)') "\usepackage{amsmath}"
write (u, '(A)') "\usepackage{feynmp}"
write (u, '(A)') "\usepackage{url}"
write (u, '(A)') "\usepackage{color}"
write (u, *)
write (u, '(A)') "\textwidth 18.5cm"
write (u, '(A)') "\evensidemargin -1.5cm"
write (u, '(A)') "\oddsidemargin -1.5cm"
write (u, *)
write (u, '(A)') "\newcommand{\blue}{\color{blue}}"
write (u, '(A)') "\newcommand{\green}{\color{green}}"
write (u, '(A)') "\newcommand{\red}{\color{red}}"
write (u, '(A)') "\newcommand{\magenta}{\color{magenta}}"
write (u, '(A)') "\newcommand{\cyan}{\color{cyan}}"
write (u, '(A)') "\newcommand{\sm}{\footnotesize}"
write (u, '(A)') "\setlength{\parindent}{0pt}"
write (u, '(A)') "\setlength{\parsep}{20pt}"
write (u, *)
write (u, '(A)') "\begin{document}"
write (u, '(A)') "\begin{fmffile}{" // char (filename) // "}"
write (u, '(A)') "\fmfcmd{color magenta; magenta = red + blue;}"
write (u, '(A)') "\fmfcmd{color cyan; cyan = green + blue;}"
write (u, '(A)') "\begin{fmfshrink}{0.5}"
write (u, '(A)') "\begin{flushleft}"
write (u, *)
write (u, '(A)') "\noindent" // &
& "\textbf{\large\texttt{WHIZARD} phase space channels}" // &
& "\hfill\today"
write (u, *)
write (u, '(A)') "\vspace{10pt}"
write (u, '(A)') "\noindent" // &
& "\textbf{Process:} \url{" // char (process_id) // "}"
call feyngraph_set_write_process_tex_format (feyngraph_set, u)
write (u, *)
write (u, '(A)') "\noindent" // &
& "\textbf{Note:} These are pseudo Feynman graphs that "
write (u, '(A)') "visualize phase-space parameterizations " // &
& "(``integration channels''). "
write (u, '(A)') "They do \emph{not} indicate Feynman graphs used for the " // &
& "matrix element."
write (u, *)
write (u, '(A)') "\textbf{Color code:} " // &
& "{\blue resonance,} " // &
& "{\cyan t-channel,} " // &
& "{\green radiation,} "
write (u, '(A)') "{\red infrared,} " // &
& "{\magenta collinear,} " // &
& "external/off-shell"
write (u, *)
write (u, '(A)') "\noindent" // &
& "\textbf{Black square:} Keystone, indicates ordering of " // &
& "phase space parameters."
write (u, *)
write (u, '(A)') "\vspace{-20pt}"
count = 0
pgcount = 0
n_grove = 0
grove => feyngraph_set%grove_list%first
do while (associated (grove))
n_grove = n_grove + 1
write (u, *)
write (u, '(A)') "\vspace{20pt}"
write (u, '(A)') "\begin{tabular}{l}"
write (u, '(A,I5,A)') &
& "\fbox{\bf Grove \boldmath$", n_grove, "$} \\[10pt]"
write (u, '(A,I1,A)') "Multiplicity: ", &
grove%grove_prop%multiplicity, "\\"
write (u, '(A,I1,A)') "Resonances: ", &
grove%grove_prop%n_resonances, "\\"
write (u, '(A,I1,A)') "Log-enhanced: ", &
grove%grove_prop%n_log_enhanced, "\\"
write (u, '(A,I1,A)') "Off-shell: ", &
grove%grove_prop%n_off_shell, "\\"
write (u, '(A,I1,A)') "t-channel: ", &
grove%grove_prop%n_t_channel, ""
write (u, '(A)') "\end{tabular}"
kingraph => grove%first
do while (associated (kingraph))
count = count + 1
call kingraph_write_graph_format (kingraph, count, unit)
kingraph => kingraph%grove_next
enddo
grove => grove%next
enddo
write (u, '(A)') "\end{flushleft}"
write (u, '(A)') "\end{fmfshrink}"
write (u, '(A)') "\end{fmffile}"
write (u, '(A)') "\end{document}"
end subroutine feyngraph_set_write_graph_format
@ %def feyngraph_set_write_graph_format
@ Write the process as a \LaTeX\ expression. This is a slightly modified
copy of [[cascade_set_write_process_tex_format]] which has only been
adapted to the types which are used here.
<<Cascades2: procedures>>=
subroutine feyngraph_set_write_process_tex_format (feyngraph_set, unit)
type(feyngraph_set_t), intent(in), target :: feyngraph_set
integer, intent(in), optional :: unit
integer :: n_tot
integer :: u, f, i
n_tot = feyngraph_set%n_in + feyngraph_set%n_out
u = given_output_unit (unit); if (u < 0) return
if (.not. allocated (feyngraph_set%flv)) return
write (u, "(A)") "\begin{align*}"
do f = 1, size (feyngraph_set%flv, 2)
do i = 1, feyngraph_set%n_in
if (i > 1) write (u, "(A)", advance="no") "\quad "
write (u, "(A)", advance="no") &
char (feyngraph_set%flv(i,f)%get_tex_name ())
end do
write (u, "(A)", advance="no") "\quad &\to\quad "
do i = feyngraph_set%n_in + 1, n_tot
if (i > feyngraph_set%n_in + 1) write (u, "(A)", advance="no") "\quad "
write (u, "(A)", advance="no") &
char (feyngraph_set%flv(i,f)%get_tex_name ())
end do
if (f < size (feyngraph_set%flv, 2)) then
write (u, "(A)") "\\"
else
write (u, "(A)") ""
end if
end do
write (u, "(A)") "\end{align*}"
end subroutine feyngraph_set_write_process_tex_format
@ %def feyngraph_set_write_process_tex_format
@ This creates metapost source for graphical display for a given [[kingraph]].
It is the analogon to [[cascade_write_graph_format]] (a modified copy).
<<Cascades2: procedures>>=
subroutine kingraph_write_graph_format (kingraph, count, unit)
type(kingraph_t), intent(in) :: kingraph
integer, intent(in) :: count
integer, intent(in), optional :: unit
integer :: u
type(string_t) :: left_str, right_str
u = given_output_unit (unit); if (u < 0) return
left_str = ""
right_str = ""
write (u, '(A)') "\begin{minipage}{105pt}"
write (u, '(A)') "\vspace{30pt}"
write (u, '(A)') "\begin{center}"
write (u, '(A)') "\begin{fmfgraph*}(55,55)"
call graph_write_node (kingraph%root)
write (u, '(A)') "\fmfleft{" // char (extract (left_str, 2)) // "}"
write (u, '(A)') "\fmfright{" // char (extract (right_str, 2)) // "}"
write (u, '(A)') "\end{fmfgraph*}\\"
write (u, '(A,I5,A)') "\fbox{$", count, "$}"
write (u, '(A)') "\end{center}"
write (u, '(A)') "\end{minipage}"
write (u, '(A)') "%"
contains
recursive subroutine graph_write_node (node)
type(k_node_t), intent(in) :: node
if (associated (node%daughter1) .or. associated (node%daughter2)) then
if (node%daughter2%t_line .or. node%daughter2%incoming) then
call vertex_write (node, node%daughter2)
call vertex_write (node, node%daughter1)
else
call vertex_write (node, node%daughter1)
call vertex_write (node, node%daughter2)
end if
if (node%mapping == EXTERNAL_PRT) then
call line_write (node%bincode, 0, node%particle)
call external_write (node%bincode, node%particle%tex_name, &
left_str)
write (u, '(A,I0,A)') "\fmfv{d.shape=square}{v0}"
end if
else
if (node%incoming) then
call external_write (node%bincode, node%particle%anti%tex_name, &
left_str)
else
call external_write (node%bincode, node%particle%tex_name, &
right_str)
end if
end if
end subroutine graph_write_node
recursive subroutine vertex_write (node, daughter)
type(k_node_t), intent(in) :: node, daughter
integer :: bincode
if (associated (node%daughter1) .and. associated (node%daughter2) &
.and. node%mapping == EXTERNAL_PRT) then
bincode = 0
else
bincode = node%bincode
end if
call graph_write_node (daughter)
if (associated (node%daughter1) .or. associated (node%daughter2)) then
call line_write (bincode, daughter%bincode, daughter%particle, &
mapping=daughter%mapping)
else
call line_write (bincode, daughter%bincode, daughter%particle)
end if
end subroutine vertex_write
subroutine line_write (i1, i2, particle, mapping)
integer(TC), intent(in) :: i1, i2
type(part_prop_t), intent(in) :: particle
integer, intent(in), optional :: mapping
integer :: k1, k2
type(string_t) :: prt_type
select case (particle%spin_type)
case (SCALAR); prt_type = "plain"
case (SPINOR); prt_type = "fermion"
case (VECTOR); prt_type = "boson"
case (VECTORSPINOR); prt_type = "fermion"
case (TENSOR); prt_type = "dbl_wiggly"
case default; prt_type = "dashes"
end select
if (particle%pdg < 0) then
!!! anti-particle
k1 = i2; k2 = i1
else
k1 = i1; k2 = i2
end if
if (present (mapping)) then
select case (mapping)
case (S_CHANNEL)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=blue,lab=\sm\blue$" // &
& char (particle%tex_name) // "$}" // &
& "{v", k1, ",v", k2, "}"
case (T_CHANNEL, U_CHANNEL)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=cyan,lab=\sm\cyan$" // &
& char (particle%tex_name) // "$}" // &
& "{v", k1, ",v", k2, "}"
case (RADIATION)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=green,lab=\sm\green$" // &
& char (particle%tex_name) // "$}" // &
& "{v", k1, ",v", k2, "}"
case (COLLINEAR)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=magenta,lab=\sm\magenta$" // &
& char (particle%tex_name) // "$}" // &
& "{v", k1, ",v", k2, "}"
case (INFRARED)
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=red,lab=\sm\red$" // &
& char (particle%tex_name) // "$}" // &
& "{v", k1, ",v", k2, "}"
case default
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& ",f=black}" // &
& "{v", k1, ",v", k2, "}"
end select
else
write (u, '(A,I0,A,I0,A)') "\fmf{" // char (prt_type) // &
& "}" // &
& "{v", k1, ",v", k2, "}"
end if
end subroutine line_write
subroutine external_write (bincode, name, ext_str)
integer(TC), intent(in) :: bincode
type(string_t), intent(in) :: name
type(string_t), intent(inout) :: ext_str
character(len=20) :: str
write (str, '(A2,I0)') ",v", bincode
ext_str = ext_str // trim (str)
write (u, '(A,I0,A,I0,A)') "\fmflabel{\sm$" &
// char (name) &
// "\,(", bincode, ")" &
// "$}{v", bincode, "}"
end subroutine external_write
end subroutine kingraph_write_graph_format
@ %def kingraph_write_graph_format
@ Generate a [[feyngraph_set]] for several subprocesses. Mapping
calculations are performed separately, but the final grove list is shared
between the subsets [[fset]] of the [[feyngraph_set]].
<<Cascades2: public>>=
public :: feyngraph_set_generate
<<Cascades2: procedures>>=
subroutine feyngraph_set_generate &
(feyngraph_set, model, n_in, n_out, flv, phs_par, fatal_beam_decay, &
u_in, vis_channels, use_dag)
type(feyngraph_set_t), intent(out) :: feyngraph_set
class(model_data_t), intent(in), target :: model
integer, intent(in) :: n_in, n_out
type(flavor_t), dimension(:,:), intent(in) :: flv
type(phs_parameters_t), intent(in) :: phs_par
logical, intent(in) :: fatal_beam_decay
integer, intent(in) :: u_in
logical, intent(in) :: vis_channels
logical, optional, intent(in) :: use_dag
type(grove_t), pointer :: grove
integer :: i, j
type(kingraph_t), pointer :: kingraph
if (phase_space_vanishes (phs_par%sqrts, n_in, flv)) return
if (present (use_dag)) feyngraph_set%use_dag = use_dag
feyngraph_set%process_type = n_in
feyngraph_set%n_in = n_in
feyngraph_set%n_out = n_out
allocate (feyngraph_set%flv (size (flv, 1), size (flv, 2)))
do i = 1, size (flv, 2)
do j = 1, size (flv, 1)
call feyngraph_set%flv(j,i)%init (flv(j,i)%get_pdg (), model)
end do
end do
allocate (feyngraph_set%particle (PRT_ARRAY_SIZE))
allocate (feyngraph_set%grove_list)
allocate (feyngraph_set%fset (size (flv, 2)))
do i = 1, size (feyngraph_set%fset)
feyngraph_set%fset(i)%use_dag = feyngraph_set%use_dag
allocate (feyngraph_set%fset(i)%flv(size (flv,1),1))
feyngraph_set%fset(i)%flv(:,1) = flv(:,i)
feyngraph_set%fset(i)%particle => feyngraph_set%particle
allocate (feyngraph_set%fset(i)%grove_list)
call feyngraph_set_generate_single (feyngraph_set%fset(i), &
model, n_in, n_out, phs_par, fatal_beam_decay, u_in)
call feyngraph_set%grove_list%merge (feyngraph_set%fset(i)%grove_list, model, i)
if (.not. vis_channels) call feyngraph_set%fset(i)%final()
enddo
call feyngraph_set%grove_list%rebuild ()
end subroutine feyngraph_set_generate
@ %def feyngraph_set_generate
@ Check whether the [[grove_list]] of the [[feyngraph_set]] contains any
[[kingraphs]] which are valid, i.e. where the [[keep]] variable has the
value [[.true.]]. This is necessary to write a non-empty phase-space
file. The function is the pendant to [[cascade_set_is_valid]].
<<Cascades2: public>>=
public :: feyngraph_set_is_valid
<<Cascades2: procedures>>=
function feyngraph_set_is_valid (feyngraph_set) result (flag)
class (feyngraph_set_t), intent(in) :: feyngraph_set
type (kingraph_t), pointer :: kingraph
type (grove_t), pointer :: grove
logical :: flag
flag = .false.
if (associated (feyngraph_set%grove_list)) then
grove => feyngraph_set%grove_list%first
do while (associated (grove))
kingraph => grove%first
do while (associated (kingraph))
if (kingraph%keep) then
flag = .true.
return
end if
kingraph => kingraph%next
enddo
grove => grove%next
enddo
end if
end function feyngraph_set_is_valid
@ %def feyngraph_set_is_valid
@
\subsection{Return the resonance histories for subtraction}
The following procedures are copies of corresponding procedures in
[[cascades]], which only have been adapted to the new types used in
this module.\\
Extract the resonance set from a valid [[kingraph]] which is kept in the
final grove list.
<<Cascades2: kingraph: TBP>>=
procedure :: extract_resonance_history => kingraph_extract_resonance_history
<<Cascades2: procedures>>=
subroutine kingraph_extract_resonance_history &
(kingraph, res_hist, model, n_out)
class(kingraph_t), intent(in), target :: kingraph
type(resonance_history_t), intent(out) :: res_hist
class(model_data_t), intent(in), target :: model
integer, intent(in) :: n_out
type(resonance_info_t) :: resonance
integer :: i, mom_id, pdg
if (debug_on) call msg_debug2 (D_PHASESPACE, "kingraph_extract_resonance_history")
if (kingraph%grove_prop%n_resonances > 0) then
if (associated (kingraph%root%daughter1) .or. &
associated (kingraph%root%daughter2)) then
if (debug_on) call msg_debug2 (D_PHASESPACE, "kingraph has resonances, root has children")
do i = 1, kingraph%tree%n_entries
if (kingraph%tree%mapping(i) == S_CHANNEL) then
mom_id = kingraph%tree%bc (i)
pdg = kingraph%tree%pdg (i)
call resonance%init (mom_id, pdg, model, n_out)
if (debug2_active (D_PHASESPACE)) then
print *, 'D: Adding resonance'
call resonance%write ()
end if
call res_hist%add_resonance (resonance)
end if
end do
end if
end if
end subroutine kingraph_extract_resonance_history
@ %def kingraph_extract_resonance_history
@ Determine the number of valid [[kingraphs]] in [[grove_list]].
<<Cascades2: public>>=
public :: grove_list_get_n_trees
<<Cascades2: procedures>>=
function grove_list_get_n_trees (grove_list) result (n)
class (grove_list_t), intent (in) :: grove_list
integer :: n
type(kingraph_t), pointer :: kingraph
type(grove_t), pointer :: grove
if (debug_on) call msg_debug (D_PHASESPACE, "grove_list_get_n_trees")
n = 0
grove => grove_list%first
do while (associated (grove))
kingraph => grove%first
do while (associated (kingraph))
if (kingraph%keep) n = n + 1
kingraph => kingraph%grove_next
enddo
grove => grove%next
enddo
if (debug_on) call msg_debug (D_PHASESPACE, "n", n)
end function grove_list_get_n_trees
@ %def grove_list_get_n_trees
@ Extract the resonance histories from the [[feyngraph_set]], in complete
analogy to [[cascade_set_get_resonance_histories]]
<<Cascades2: public>>=
public :: feyngraph_set_get_resonance_histories
<<Cascades2: procedures>>=
subroutine feyngraph_set_get_resonance_histories (feyngraph_set, n_filter, res_hists)
type(feyngraph_set_t), intent(in), target :: feyngraph_set
integer, intent(in), optional :: n_filter
type(resonance_history_t), dimension(:), allocatable, intent(out) :: res_hists
type(kingraph_t), pointer :: kingraph
type(grove_t), pointer :: grove
type(resonance_history_t) :: res_hist
type(resonance_history_set_t) :: res_hist_set
integer :: i_grove
if (debug_on) call msg_debug (D_PHASESPACE, "grove_list_get_resonance_histories")
call res_hist_set%init (n_filter = n_filter)
grove => feyngraph_set%grove_list%first
i_grove = 0
do while (associated (grove))
i_grove = i_grove + 1
kingraph => grove%first
do while (associated (kingraph))
if (kingraph%keep) then
if (debug_on) call msg_debug2 (D_PHASESPACE, "grove", i_grove)
call kingraph%extract_resonance_history &
(res_hist, feyngraph_set%model, feyngraph_set%n_out)
call res_hist_set%enter (res_hist)
end if
kingraph => kingraph%grove_next
end do
end do
call res_hist_set%freeze ()
call res_hist_set%to_array (res_hists)
end subroutine feyngraph_set_get_resonance_histories
@ %def feyngraph_set_get_resonance_histories
<<[[cascades2_ut.f90]]>>=
<<File header>>
module cascades2_ut
use unit_tests
use cascades2_uti
<<Standard module head>>
<<Cascades2: public test>>
contains
<<Cascades2: test driver>>
end module cascades2_ut
@ %def cascades2_ut
@
<<[[cascades2_uti.f90]]>>=
<<File header>>
module cascades2_uti
<<Use kinds>>
<<Use strings>>
use numeric_utils
use cascades2
use flavors
use phs_forests, only: phs_parameters_t
use model_data
<<Standard module head>>
<<Cascades2: test declarations>>
contains
<<Cascades2: tests>>
end module cascades2_uti
@ %def cascades2_uti
@ API: driver for the unit tests below.
<<Cascades2: public test>>=
public :: cascades2_test
<<Cascades2: test driver>>=
subroutine cascades2_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Cascades2: execute tests>>
end subroutine cascades2_test
@ %def cascades2_test
@
<<Cascades2: execute tests>>=
call test (cascades2_1, "cascades2_1", &
"make phase-space", u, results)
call test (cascades2_2, "cascades2_2", &
"make phase-space (scattering)", u, results)
<<Cascades2: test declarations>>=
public :: cascades2_1
<<Cascades2: tests>>=
subroutine cascades2_1 (u)
integer, intent(in) :: u
type (feyngraph_set_t) :: feyngraph_set
type (model_data_t) :: model
integer :: n_in = 1
integer :: n_out = 6
type(flavor_t), dimension(7,1) :: flv
type (phs_parameters_t) :: phs_par
logical :: fatal_beam_decay = .true.
integer :: u_in = 8
write (u, "(A)") "* Test output: cascades2_1"
write (u, "(A)") "* Purpose: create a test phs file (decay) with the forest"
write (u, "(A)") "* output of O'Mega"
write (u, "(A)")
write (u, "(A)") "* Initializing"
write (u, "(A)")
call init_sm_full_test (model)
call flv(1,1)%init (6, model)
call flv(2,1)%init (5, model)
call flv(3,1)%init (-11, model)
call flv(4,1)%init (12, model)
call flv(5,1)%init (21, model)
call flv(6,1)%init (22, model)
call flv(7,1)%init (21, model)
phs_par%sqrts = 173.1_default
phs_par%m_threshold_s = 50._default
phs_par%m_threshold_t = 100._default
phs_par%keep_nonresonant = .true.
phs_par%off_shell = 2
open (unit=u_in, file="cascades2_1.fds", status='old', action='read')
write (u, "(A)")
write (u, "(A)") "* Generating phase-space parametrizations"
write (u, "(A)")
call feyngraph_set_generate (feyngraph_set, model, n_in, n_out, &
flv, phs_par, fatal_beam_decay, u_in, use_dag = .false., &
vis_channels = .false.)
call feyngraph_set_write_process_bincode_format (feyngraph_set, u)
call feyngraph_set_write_file_format (feyngraph_set, u)
write (u, "(A)") "* Cleanup"
write (u, "(A)")
close (u_in)
call feyngraph_set%final ()
call model%final ()
write (u, *)
write (u, "(A)") "* Test output end: cascades2_1"
end subroutine cascades2_1
@ %def cascades2_1
@
<<Cascades2: test declarations>>=
public :: cascades2_2
<<Cascades2: tests>>=
subroutine cascades2_2 (u)
integer, intent(in) :: u
type (feyngraph_set_t) :: feyngraph_set
type (model_data_t) :: model
integer :: n_in = 2
integer :: n_out = 5
type(flavor_t), dimension(7,1) :: flv
type (phs_parameters_t) :: phs_par
logical :: fatal_beam_decay = .true.
integer :: u_in = 8
write (u, "(A)") "* Test output: cascades2_2"
write (u, "(A)") "* Purpose: create a test phs file (scattering) with the"
write (u, "(A)") "* parsable DAG output of O'Mega"
write (u, "(A)")
write (u, "(A)") "* Initializing"
write (u, "(A)")
call init_sm_full_test (model)
call flv(1,1)%init (-11, model)
call flv(2,1)%init (11, model)
call flv(3,1)%init (-11, model)
call flv(4,1)%init (12, model)
call flv(5,1)%init (1, model)
call flv(6,1)%init (-2, model)
call flv(7,1)%init (22, model)
phs_par%sqrts = 500._default
phs_par%m_threshold_s = 50._default
phs_par%m_threshold_t = 100._default
phs_par%keep_nonresonant = .true.
phs_par%off_shell = 2
phs_par%t_channel = 6
open (unit=u_in, file="cascades2_2.fds", &
status='old', action='read')
write (u, "(A)")
write (u, "(A)") "* Generating phase-space parametrizations"
write (u, "(A)")
call feyngraph_set_generate (feyngraph_set, model, n_in, n_out, &
flv, phs_par, fatal_beam_decay, u_in, use_dag = .true., &
vis_channels = .false.)
call feyngraph_set_write_process_bincode_format (feyngraph_set, u)
call feyngraph_set_write_file_format (feyngraph_set, u)
write (u, "(A)") "* Cleanup"
write (u, "(A)")
close (u_in)
call feyngraph_set%final ()
call model%final ()
write (u, *)
write (u, "(A)") "* Test output end: cascades2_2"
end subroutine cascades2_2
@ %def cascades2_2
Index: trunk/src/process_integration/process_integration.nw
===================================================================
--- trunk/src/process_integration/process_integration.nw (revision 8293)
+++ trunk/src/process_integration/process_integration.nw (revision 8294)
@@ -1,19165 +1,19186 @@
% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: integration and process objects and such
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Integration and Process Objects}
\includemodulegraph{process_integration}
This is the central part of the \whizard\ package. It provides the
functionality for evaluating structure functions, kinematics and matrix
elements, integration and event generation. It combines the various
parts that deal with those tasks individually and organizes the data
transfer between them.
\begin{description}
\item[subevt\_expr]
This enables process observables as (abstract) expressions, to be
evaluated for each process call.
\item[parton\_states]
A [[parton_state_t]] object represents an elementary partonic
interaction. There are two versions: one for the isolated
elementary process, one for the elementary process convoluted with
the structure-function chain. The parton state is an effective
state. It needs not coincide with the seed-kinematics state which is
used in evaluating phase space.
\item[process]
Here, all pieces are combined for the purpose of evaluating the
elementary processes. The whole algorithm is coded in terms of
abstract data types as defined in the appropriate modules: [[prc_core]]
for matrix-element evaluation, [[prc_core_def]] for the associated
configuration and driver, [[sf_base]] for beams and structure-functions,
[[phs_base]] for phase space, and [[mci_base]] for integration and event
generation.
\item[process\_config]
\item[process\_counter]
Very simple object for statistics
\item[process\_mci]
\item[pcm]
\item[kinematics]
\item[instances]
While the above modules set up all static information, the instances
have the changing event data. There are term and process instances but
no component instances.
\item[process\_stacks]
Process stacks collect process objects.
\end{description}
We combine here hard interactions, phase space, and (for scatterings)
structure functions and interfaces them to the integration module.
The process object implements the combination of a fixed beam and
structure-function setup with a number of elementary processes. The
latter are called process components. The process object
represents an entity which is supposedly observable. It should
be meaningful to talk about the cross section of a process.
The individual components of a process are, technically, processes
themselves, but they may have unphysical cross sections which have to
be added for a physical result. Process components may be exclusive
tree-level elementary processes, dipole subtraction term, loop
corrections, etc.
The beam and structure function setup is common to all process
components. Thus, there is only one instance of this part.
The process may be a scattering process or a decay process. In the
latter case, there are no structure functions, and the beam setup
consists of a single particle. Otherwise, the two classes are treated
on the same footing.
Once a sampling point has been chosen, a process determines a set of
partons with a correlated density matrix of quantum numbers. In
general, each sampling point will generate, for each process component,
one or more distinct parton configurations. This is the [[computed]]
state. The computed state is the subject of the multi-channel
integration algorithm.
For NLO computations, it is necessary to project the computed states
onto another set of parton configurations (e.g., by recombining
certain pairs). This is the [[observed]] state. When computing
partonic observables, the information is taken from the observed
state.
For the purpose of event generation, we will later select one parton
configuration from the observed state and collapse the correlated
quantum state. This configuration is then dressed by applying parton
shower, decays and hadronization. The decay chain, in particular,
combines a scattering process with possible subsequent decay processes
on the parton level, which are full-fledged process objects themselves.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Process observables}
We define an abstract [[subevt_expr_t]] object as an extension of the
[[subevt_t]] type. The object contains a local variable list, variable
instances (as targets for pointers in the variable list), and evaluation
trees. The evaluation trees reference both the variables and the [[subevt]].
There are two instances of the abstract type: one for process instances, one
for physical events. Both have a common logical expression [[selection]]
which determines whether the object passes user-defined cuts.
The intention is that we fill the [[subevt_t]] base object and compute the
variables once we have evaluated a kinematical phase space point (or a
complete event). We then evaluate the expressions and can use the results in
further calculations.
The [[process_expr_t]] extension contains furthermore scale and weight
expressions. The [[event_expr_t]] extension contains a reweighting-factor
expression and a logical expression for event analysis. In practice, we will
link the variable list of the [[event_obs]] object to the variable list of the
currently active [[process_obs]] object, such that the process variables are
available to both objects. Event variables are meaningful only for physical
events.
Note that there are unit tests, but they are deferred to the
[[expr_tests]] module.
<<[[subevt_expr.f90]]>>=
<<File header>>
module subevt_expr
<<Use kinds>>
<<Use strings>>
use constants, only: zero, one
use io_units
use format_utils, only: write_separator
use diagnostics
use lorentz
use subevents
use variables
use flavors
use quantum_numbers
use interactions
use particles
use expr_base
<<Standard module head>>
<<Subevt expr: public>>
<<Subevt expr: types>>
<<Subevt expr: interfaces>>
contains
<<Subevt expr: procedures>>
end module subevt_expr
@ %def subevt_expr
@
\subsection{Abstract base type}
<<Subevt expr: types>>=
type, extends (subevt_t), abstract :: subevt_expr_t
logical :: subevt_filled = .false.
type(var_list_t) :: var_list
real(default) :: sqrts_hat = 0
integer :: n_in = 0
integer :: n_out = 0
integer :: n_tot = 0
logical :: has_selection = .false.
class(expr_t), allocatable :: selection
logical :: colorize_subevt = .false.
contains
<<Subevt expr: subevt expr: TBP>>
end type subevt_expr_t
@ %def subevt_expr_t
@ Output: Base and extended version. We already have a [[write]] routine for
the [[subevt_t]] parent type.
<<Subevt expr: subevt expr: TBP>>=
procedure :: base_write => subevt_expr_write
<<Subevt expr: procedures>>=
subroutine subevt_expr_write (object, unit, pacified)
class(subevt_expr_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: pacified
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") "Local variables:"
call write_separator (u)
call var_list_write (object%var_list, u, follow_link=.false., &
pacified = pacified)
call write_separator (u)
if (object%subevt_filled) then
call object%subevt_t%write (u, pacified = pacified)
if (object%has_selection) then
call write_separator (u)
write (u, "(1x,A)") "Selection expression:"
call write_separator (u)
call object%selection%write (u)
end if
else
write (u, "(1x,A)") "subevt: [undefined]"
end if
end subroutine subevt_expr_write
@ %def subevt_expr_write
@ Finalizer.
<<Subevt expr: subevt expr: TBP>>=
procedure (subevt_expr_final), deferred :: final
procedure :: base_final => subevt_expr_final
<<Subevt expr: procedures>>=
subroutine subevt_expr_final (object)
class(subevt_expr_t), intent(inout) :: object
call object%var_list%final ()
if (object%has_selection) then
call object%selection%final ()
end if
end subroutine subevt_expr_final
@ %def subevt_expr_final
@
\subsection{Initialization}
Initialization: define local variables and establish pointers.
The common variables are [[sqrts]] (the nominal beam energy, fixed),
[[sqrts_hat]] (the actual energy), [[n_in]], [[n_out]], and [[n_tot]] for
the [[subevt]]. With the exception of [[sqrts]], all are implemented as
pointers to subobjects.
<<Subevt expr: subevt expr: TBP>>=
procedure (subevt_expr_setup_vars), deferred :: setup_vars
procedure :: base_setup_vars => subevt_expr_setup_vars
<<Subevt expr: procedures>>=
subroutine subevt_expr_setup_vars (expr, sqrts)
class(subevt_expr_t), intent(inout), target :: expr
real(default), intent(in) :: sqrts
call expr%var_list%final ()
call var_list_append_real (expr%var_list, &
var_str ("sqrts"), sqrts, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_real_ptr (expr%var_list, &
var_str ("sqrts_hat"), expr%sqrts_hat, &
is_known = expr%subevt_filled, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_int_ptr (expr%var_list, &
var_str ("n_in"), expr%n_in, &
is_known = expr%subevt_filled, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_int_ptr (expr%var_list, &
var_str ("n_out"), expr%n_out, &
is_known = expr%subevt_filled, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_int_ptr (expr%var_list, &
var_str ("n_tot"), expr%n_tot, &
is_known = expr%subevt_filled, &
locked = .true., verbose = .false., intrinsic = .true.)
end subroutine subevt_expr_setup_vars
@ %def subevt_expr_setup_vars
@ Append the subevent expr (its base-type core) itself to the variable
list, if it is not yet present.
<<Subevt expr: subevt expr: TBP>>=
procedure :: setup_var_self => subevt_expr_setup_var_self
<<Subevt expr: procedures>>=
subroutine subevt_expr_setup_var_self (expr)
class(subevt_expr_t), intent(inout), target :: expr
if (.not. expr%var_list%contains (var_str ("@evt"))) then
call var_list_append_subevt_ptr &
(expr%var_list, &
var_str ("@evt"), expr%subevt_t, &
is_known = expr%subevt_filled, &
locked = .true., verbose = .false., intrinsic=.true.)
end if
end subroutine subevt_expr_setup_var_self
@ %def subevt_expr_setup_var_self
@ Link a variable list to the local one. This could be done event by event,
but before evaluating expressions.
<<Subevt expr: subevt expr: TBP>>=
procedure :: link_var_list => subevt_expr_link_var_list
<<Subevt expr: procedures>>=
subroutine subevt_expr_link_var_list (expr, var_list)
class(subevt_expr_t), intent(inout) :: expr
type(var_list_t), intent(in), target :: var_list
call expr%var_list%link (var_list)
end subroutine subevt_expr_link_var_list
@ %def subevt_expr_link_var_list
@ Compile the selection expression. If there is no expression, the build
method won't allocate the expression object.
<<Subevt expr: subevt expr: TBP>>=
procedure :: setup_selection => subevt_expr_setup_selection
<<Subevt expr: procedures>>=
subroutine subevt_expr_setup_selection (expr, ef_cuts)
class(subevt_expr_t), intent(inout), target :: expr
class(expr_factory_t), intent(in) :: ef_cuts
call ef_cuts%build (expr%selection)
if (allocated (expr%selection)) then
call expr%setup_var_self ()
call expr%selection%setup_lexpr (expr%var_list)
expr%has_selection = .true.
end if
end subroutine subevt_expr_setup_selection
@ %def subevt_expr_setup_selection
@ (De)activate color storage and evaluation for the expression. The subevent
particles will have color information.
<<Subevt expr: subevt expr: TBP>>=
procedure :: colorize => subevt_expr_colorize
<<Subevt expr: procedures>>=
subroutine subevt_expr_colorize (expr, colorize_subevt)
class(subevt_expr_t), intent(inout), target :: expr
logical, intent(in) :: colorize_subevt
expr%colorize_subevt = colorize_subevt
end subroutine subevt_expr_colorize
-
+
@ %def subevt_expr_colorize
@
\subsection{Evaluation}
Reset to initial state, i.e., mark the [[subevt]] as invalid.
<<Subevt expr: subevt expr: TBP>>=
procedure :: reset_contents => subevt_expr_reset_contents
procedure :: base_reset_contents => subevt_expr_reset_contents
<<Subevt expr: procedures>>=
subroutine subevt_expr_reset_contents (expr)
class(subevt_expr_t), intent(inout) :: expr
expr%subevt_filled = .false.
end subroutine subevt_expr_reset_contents
@ %def subevt_expr_reset_contents
@ Evaluate the selection expression and return the result. There is also a
deferred version: this should evaluate the remaining expressions if the event
has passed.
<<Subevt expr: subevt expr: TBP>>=
procedure :: base_evaluate => subevt_expr_evaluate
<<Subevt expr: procedures>>=
subroutine subevt_expr_evaluate (expr, passed)
class(subevt_expr_t), intent(inout) :: expr
logical, intent(out) :: passed
if (expr%has_selection) then
call expr%selection%evaluate ()
if (expr%selection%is_known ()) then
passed = expr%selection%get_log ()
else
call msg_error ("Evaluate selection expression: result undefined")
passed = .false.
end if
else
passed = .true.
end if
end subroutine subevt_expr_evaluate
@ %def subevt_expr_evaluate
@
\subsection{Implementation for partonic events}
This implementation contains the expressions that we can evaluate for the
partonic process during integration.
<<Subevt expr: public>>=
public :: parton_expr_t
<<Subevt expr: types>>=
type, extends (subevt_expr_t) :: parton_expr_t
integer, dimension(:), allocatable :: i_beam
integer, dimension(:), allocatable :: i_in
integer, dimension(:), allocatable :: i_out
logical :: has_scale = .false.
logical :: has_fac_scale = .false.
logical :: has_ren_scale = .false.
logical :: has_weight = .false.
class(expr_t), allocatable :: scale
class(expr_t), allocatable :: fac_scale
class(expr_t), allocatable :: ren_scale
class(expr_t), allocatable :: weight
contains
<<Subevt expr: parton expr: TBP>>
end type parton_expr_t
@ %def parton_expr_t
@ Finalizer.
<<Subevt expr: parton expr: TBP>>=
procedure :: final => parton_expr_final
<<Subevt expr: procedures>>=
subroutine parton_expr_final (object)
class(parton_expr_t), intent(inout) :: object
call object%base_final ()
if (object%has_scale) then
call object%scale%final ()
end if
if (object%has_fac_scale) then
call object%fac_scale%final ()
end if
if (object%has_ren_scale) then
call object%ren_scale%final ()
end if
if (object%has_weight) then
call object%weight%final ()
end if
end subroutine parton_expr_final
@ %def parton_expr_final
@ Output: continue writing the active expressions, after the common selection
expression.
Note: the [[prefix]] argument is declared in the [[write]] method of the
[[subevt_t]] base type. Here, it is unused.
<<Subevt expr: parton expr: TBP>>=
procedure :: write => parton_expr_write
<<Subevt expr: procedures>>=
subroutine parton_expr_write (object, unit, prefix, pacified)
class(parton_expr_t), intent(in) :: object
integer, intent(in), optional :: unit
character(*), intent(in), optional :: prefix
logical, intent(in), optional :: pacified
integer :: u
u = given_output_unit (unit)
call object%base_write (u, pacified = pacified)
if (object%subevt_filled) then
if (object%has_scale) then
call write_separator (u)
write (u, "(1x,A)") "Scale expression:"
call write_separator (u)
call object%scale%write (u)
end if
if (object%has_fac_scale) then
call write_separator (u)
write (u, "(1x,A)") "Factorization scale expression:"
call write_separator (u)
call object%fac_scale%write (u)
end if
if (object%has_ren_scale) then
call write_separator (u)
write (u, "(1x,A)") "Renormalization scale expression:"
call write_separator (u)
call object%ren_scale%write (u)
end if
if (object%has_weight) then
call write_separator (u)
write (u, "(1x,A)") "Weight expression:"
call write_separator (u)
call object%weight%write (u)
end if
end if
end subroutine parton_expr_write
@ %def parton_expr_write
@ Define variables.
<<Subevt expr: parton expr: TBP>>=
procedure :: setup_vars => parton_expr_setup_vars
<<Subevt expr: procedures>>=
subroutine parton_expr_setup_vars (expr, sqrts)
class(parton_expr_t), intent(inout), target :: expr
real(default), intent(in) :: sqrts
call expr%base_setup_vars (sqrts)
end subroutine parton_expr_setup_vars
@ %def parton_expr_setup_vars
@ Compile the scale expressions. If a pointer is disassociated, there is
no expression.
<<Subevt expr: parton expr: TBP>>=
procedure :: setup_scale => parton_expr_setup_scale
procedure :: setup_fac_scale => parton_expr_setup_fac_scale
procedure :: setup_ren_scale => parton_expr_setup_ren_scale
<<Subevt expr: procedures>>=
subroutine parton_expr_setup_scale (expr, ef_scale)
class(parton_expr_t), intent(inout), target :: expr
class(expr_factory_t), intent(in) :: ef_scale
call ef_scale%build (expr%scale)
if (allocated (expr%scale)) then
call expr%setup_var_self ()
call expr%scale%setup_expr (expr%var_list)
expr%has_scale = .true.
end if
end subroutine parton_expr_setup_scale
subroutine parton_expr_setup_fac_scale (expr, ef_fac_scale)
class(parton_expr_t), intent(inout), target :: expr
class(expr_factory_t), intent(in) :: ef_fac_scale
call ef_fac_scale%build (expr%fac_scale)
if (allocated (expr%fac_scale)) then
call expr%setup_var_self ()
call expr%fac_scale%setup_expr (expr%var_list)
expr%has_fac_scale = .true.
end if
end subroutine parton_expr_setup_fac_scale
subroutine parton_expr_setup_ren_scale (expr, ef_ren_scale)
class(parton_expr_t), intent(inout), target :: expr
class(expr_factory_t), intent(in) :: ef_ren_scale
call ef_ren_scale%build (expr%ren_scale)
if (allocated (expr%ren_scale)) then
call expr%setup_var_self ()
call expr%ren_scale%setup_expr (expr%var_list)
expr%has_ren_scale = .true.
end if
end subroutine parton_expr_setup_ren_scale
@ %def parton_expr_setup_scale
@ %def parton_expr_setup_fac_scale
@ %def parton_expr_setup_ren_scale
@ Compile the weight expression.
<<Subevt expr: parton expr: TBP>>=
procedure :: setup_weight => parton_expr_setup_weight
<<Subevt expr: procedures>>=
subroutine parton_expr_setup_weight (expr, ef_weight)
class(parton_expr_t), intent(inout), target :: expr
class(expr_factory_t), intent(in) :: ef_weight
call ef_weight%build (expr%weight)
if (allocated (expr%weight)) then
call expr%setup_var_self ()
call expr%weight%setup_expr (expr%var_list)
expr%has_weight = .true.
end if
end subroutine parton_expr_setup_weight
@ %def parton_expr_setup_weight
@ Filling the partonic state consists of two parts. The first routine
prepares the subevt without assigning momenta. It takes the particles from an
[[interaction_t]]. It needs the indices and flavors for the beam,
incoming, and outgoing particles.
We can assume that the particle content of the subevt does not change.
Therefore, we set the event variables [[n_in]], [[n_out]], [[n_tot]] already
in this initialization step.
<<Subevt expr: parton expr: TBP>>=
procedure :: setup_subevt => parton_expr_setup_subevt
<<Subevt expr: procedures>>=
subroutine parton_expr_setup_subevt (expr, int, &
i_beam, i_in, i_out, f_beam, f_in, f_out)
class(parton_expr_t), intent(inout) :: expr
type(interaction_t), intent(in), target :: int
integer, dimension(:), intent(in) :: i_beam, i_in, i_out
type(flavor_t), dimension(:), intent(in) :: f_beam, f_in, f_out
allocate (expr%i_beam (size (i_beam)))
allocate (expr%i_in (size (i_in)))
allocate (expr%i_out (size (i_out)))
expr%i_beam = i_beam
expr%i_in = i_in
expr%i_out = i_out
call interaction_to_subevt (int, &
expr%i_beam, expr%i_in, expr%i_out, expr%subevt_t)
call subevt_set_pdg_beam (expr%subevt_t, f_beam%get_pdg ())
call subevt_set_pdg_incoming (expr%subevt_t, f_in%get_pdg ())
call subevt_set_pdg_outgoing (expr%subevt_t, f_out%get_pdg ())
call subevt_set_p2_beam (expr%subevt_t, f_beam%get_mass () ** 2)
call subevt_set_p2_incoming (expr%subevt_t, f_in%get_mass () ** 2)
call subevt_set_p2_outgoing (expr%subevt_t, f_out%get_mass () ** 2)
expr%n_in = size (i_in)
expr%n_out = size (i_out)
expr%n_tot = expr%n_in + expr%n_out
end subroutine parton_expr_setup_subevt
@ %def parton_expr_setup_subevt
@ Transfer PDG codes, masses (initalization) and momenta to a
predefined subevent. We use the flavor assignment of the first
branch in the interaction state matrix. Only incoming and outgoing
particles are transferred. Switch momentum sign for incoming
particles.
<<Subevt expr: interfaces>>=
interface interaction_momenta_to_subevt
module procedure interaction_momenta_to_subevt_id
module procedure interaction_momenta_to_subevt_tr
end interface
<<Subevt expr: procedures>>=
subroutine interaction_to_subevt (int, j_beam, j_in, j_out, subevt)
type(interaction_t), intent(in), target :: int
integer, dimension(:), intent(in) :: j_beam, j_in, j_out
type(subevt_t), intent(out) :: subevt
type(flavor_t), dimension(:), allocatable :: flv
integer :: n_beam, n_in, n_out, i, j
allocate (flv (int%get_n_tot ()))
flv = quantum_numbers_get_flavor (int%get_quantum_numbers (1))
n_beam = size (j_beam)
n_in = size (j_in)
n_out = size (j_out)
call subevt_init (subevt, n_beam + n_in + n_out)
do i = 1, n_beam
j = j_beam(i)
call subevt_set_beam (subevt, i, &
flv(j)%get_pdg (), &
vector4_null, &
flv(j)%get_mass () ** 2)
end do
do i = 1, n_in
j = j_in(i)
call subevt_set_incoming (subevt, n_beam + i, &
flv(j)%get_pdg (), &
vector4_null, &
flv(j)%get_mass () ** 2)
end do
do i = 1, n_out
j = j_out(i)
call subevt_set_outgoing (subevt, n_beam + n_in + i, &
flv(j)%get_pdg (), &
vector4_null, &
flv(j)%get_mass () ** 2)
end do
end subroutine interaction_to_subevt
subroutine interaction_momenta_to_subevt_id (int, j_beam, j_in, j_out, subevt)
type(interaction_t), intent(in) :: int
integer, dimension(:), intent(in) :: j_beam, j_in, j_out
type(subevt_t), intent(inout) :: subevt
call subevt_set_p_beam (subevt, - int%get_momenta (j_beam))
call subevt_set_p_incoming (subevt, - int%get_momenta (j_in))
call subevt_set_p_outgoing (subevt, int%get_momenta (j_out))
end subroutine interaction_momenta_to_subevt_id
subroutine interaction_momenta_to_subevt_tr &
(int, j_beam, j_in, j_out, lt, subevt)
type(interaction_t), intent(in) :: int
integer, dimension(:), intent(in) :: j_beam, j_in, j_out
type(subevt_t), intent(inout) :: subevt
type(lorentz_transformation_t), intent(in) :: lt
call subevt_set_p_beam &
(subevt, - lt * int%get_momenta (j_beam))
call subevt_set_p_incoming &
(subevt, - lt * int%get_momenta (j_in))
call subevt_set_p_outgoing &
(subevt, lt * int%get_momenta (j_out))
end subroutine interaction_momenta_to_subevt_tr
@ %def interaction_momenta_to_subevt
@ The second part takes the momenta from the interaction object and thus
completes the subevt. The partonic energy can then be computed.
<<Subevt expr: parton expr: TBP>>=
procedure :: fill_subevt => parton_expr_fill_subevt
<<Subevt expr: procedures>>=
subroutine parton_expr_fill_subevt (expr, int)
class(parton_expr_t), intent(inout) :: expr
type(interaction_t), intent(in), target :: int
call interaction_momenta_to_subevt (int, &
expr%i_beam, expr%i_in, expr%i_out, expr%subevt_t)
expr%sqrts_hat = subevt_get_sqrts_hat (expr%subevt_t)
expr%subevt_filled = .true.
end subroutine parton_expr_fill_subevt
@ %def parton_expr_fill_subevt
@ Evaluate, if the event passes the selection. For absent expressions we take
default values.
<<Subevt expr: parton expr: TBP>>=
procedure :: evaluate => parton_expr_evaluate
<<Subevt expr: procedures>>=
subroutine parton_expr_evaluate &
(expr, passed, scale, fac_scale, ren_scale, weight, scale_forced, force_evaluation)
class(parton_expr_t), intent(inout) :: expr
logical, intent(out) :: passed
real(default), intent(out) :: scale
real(default), intent(out) :: fac_scale
real(default), intent(out) :: ren_scale
real(default), intent(out) :: weight
real(default), intent(in), allocatable, optional :: scale_forced
logical, intent(in), optional :: force_evaluation
logical :: force_scale, force_eval
force_scale = .false.; force_eval = .false.
if (present (scale_forced)) force_scale = allocated (scale_forced)
if (present (force_evaluation)) force_eval = force_evaluation
call expr%base_evaluate (passed)
if (passed .or. force_eval) then
if (force_scale) then
scale = scale_forced
else if (expr%has_scale) then
call expr%scale%evaluate ()
if (expr%scale%is_known ()) then
scale = expr%scale%get_real ()
else
call msg_error ("Evaluate scale expression: result undefined")
scale = zero
end if
else
scale = expr%sqrts_hat
end if
if (force_scale) then
fac_scale = scale_forced
else if (expr%has_fac_scale) then
call expr%fac_scale%evaluate ()
if (expr%fac_scale%is_known ()) then
fac_scale = expr%fac_scale%get_real ()
else
call msg_error ("Evaluate factorization scale expression: &
&result undefined")
fac_scale = zero
end if
else
fac_scale = scale
end if
if (force_scale) then
ren_scale = scale_forced
else if (expr%has_ren_scale) then
call expr%ren_scale%evaluate ()
if (expr%ren_scale%is_known ()) then
ren_scale = expr%ren_scale%get_real ()
else
call msg_error ("Evaluate renormalization scale expression: &
&result undefined")
ren_scale = zero
end if
else
ren_scale = scale
end if
if (expr%has_weight) then
call expr%weight%evaluate ()
if (expr%weight%is_known ()) then
weight = expr%weight%get_real ()
else
call msg_error ("Evaluate weight expression: result undefined")
weight = zero
end if
else
weight = one
end if
else
weight = zero
end if
end subroutine parton_expr_evaluate
@ %def parton_expr_evaluate
@ Return the beam/incoming parton indices.
<<Subevt expr: parton expr: TBP>>=
procedure :: get_beam_index => parton_expr_get_beam_index
procedure :: get_in_index => parton_expr_get_in_index
<<Subevt expr: procedures>>=
subroutine parton_expr_get_beam_index (expr, i_beam)
class(parton_expr_t), intent(in) :: expr
integer, dimension(:), intent(out) :: i_beam
i_beam = expr%i_beam
end subroutine parton_expr_get_beam_index
subroutine parton_expr_get_in_index (expr, i_in)
class(parton_expr_t), intent(in) :: expr
integer, dimension(:), intent(out) :: i_in
i_in = expr%i_in
end subroutine parton_expr_get_in_index
@ %def parton_expr_get_beam_index
@ %def parton_expr_get_in_index
@
\subsection{Implementation for full events}
This implementation contains the expressions that we can evaluate for the
full event. It also contains data that pertain to the event, suitable
for communication with external event formats. These data
simultaneously serve as pointer targets for the variable lists hidden
in the expressions (eval trees).
Squared matrix element and weight values: when reading events from
file, the [[ref]] value is the number in the file, while the [[prc]]
value is the number that we calculate from the momenta in the file,
possibly with different parameters. When generating events the first
time, or if we do not recalculate, the numbers should coincide.
Furthermore, the array of [[alt]] values is copied from an array of
alternative event records. These values should represent calculated
values.
<<Subevt expr: public>>=
public :: event_expr_t
<<Subevt expr: types>>=
type, extends (subevt_expr_t) :: event_expr_t
logical :: has_reweight = .false.
logical :: has_analysis = .false.
class(expr_t), allocatable :: reweight
class(expr_t), allocatable :: analysis
logical :: has_id = .false.
type(string_t) :: id
logical :: has_num_id = .false.
integer :: num_id = 0
logical :: has_index = .false.
integer :: index = 0
logical :: has_sqme_ref = .false.
real(default) :: sqme_ref = 0
logical :: has_sqme_prc = .false.
real(default) :: sqme_prc = 0
logical :: has_weight_ref = .false.
real(default) :: weight_ref = 0
logical :: has_weight_prc = .false.
real(default) :: weight_prc = 0
logical :: has_excess_prc = .false.
real(default) :: excess_prc = 0
integer :: n_alt = 0
logical :: has_sqme_alt = .false.
real(default), dimension(:), allocatable :: sqme_alt
logical :: has_weight_alt = .false.
real(default), dimension(:), allocatable :: weight_alt
contains
<<Subevt expr: event expr: TBP>>
end type event_expr_t
@ %def event_expr_t
@ Finalizer for the expressions.
<<Subevt expr: event expr: TBP>>=
procedure :: final => event_expr_final
<<Subevt expr: procedures>>=
subroutine event_expr_final (object)
class(event_expr_t), intent(inout) :: object
call object%base_final ()
if (object%has_reweight) then
call object%reweight%final ()
end if
if (object%has_analysis) then
call object%analysis%final ()
end if
end subroutine event_expr_final
@ %def event_expr_final
@ Output: continue writing the active expressions, after the common selection
expression.
Note: the [[prefix]] argument is declared in the [[write]] method of the
[[subevt_t]] base type. Here, it is unused.
<<Subevt expr: event expr: TBP>>=
procedure :: write => event_expr_write
<<Subevt expr: procedures>>=
subroutine event_expr_write (object, unit, prefix, pacified)
class(event_expr_t), intent(in) :: object
integer, intent(in), optional :: unit
character(*), intent(in), optional :: prefix
logical, intent(in), optional :: pacified
integer :: u
u = given_output_unit (unit)
call object%base_write (u, pacified = pacified)
if (object%subevt_filled) then
if (object%has_reweight) then
call write_separator (u)
write (u, "(1x,A)") "Reweighting expression:"
call write_separator (u)
call object%reweight%write (u)
end if
if (object%has_analysis) then
call write_separator (u)
write (u, "(1x,A)") "Analysis expression:"
call write_separator (u)
call object%analysis%write (u)
end if
end if
end subroutine event_expr_write
@ %def event_expr_write
@ Initializer. This is required only for the [[sqme_alt]] and
[[weight_alt]] arrays.
<<Subevt expr: event expr: TBP>>=
procedure :: init => event_expr_init
<<Subevt expr: procedures>>=
subroutine event_expr_init (expr, n_alt)
class(event_expr_t), intent(out) :: expr
integer, intent(in), optional :: n_alt
if (present (n_alt)) then
expr%n_alt = n_alt
allocate (expr%sqme_alt (n_alt), source = 0._default)
allocate (expr%weight_alt (n_alt), source = 0._default)
end if
end subroutine event_expr_init
@ %def event_expr_init
@ Define variables. We have the variables of the base type plus
specific variables for full events. There is the event index.
<<Subevt expr: event expr: TBP>>=
procedure :: setup_vars => event_expr_setup_vars
<<Subevt expr: procedures>>=
subroutine event_expr_setup_vars (expr, sqrts)
class(event_expr_t), intent(inout), target :: expr
real(default), intent(in) :: sqrts
call expr%base_setup_vars (sqrts)
call var_list_append_string_ptr (expr%var_list, &
var_str ("$process_id"), expr%id, &
is_known = expr%has_id, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_int_ptr (expr%var_list, &
var_str ("process_num_id"), expr%num_id, &
is_known = expr%has_num_id, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_real_ptr (expr%var_list, &
var_str ("sqme"), expr%sqme_prc, &
is_known = expr%has_sqme_prc, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_real_ptr (expr%var_list, &
var_str ("sqme_ref"), expr%sqme_ref, &
is_known = expr%has_sqme_ref, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_int_ptr (expr%var_list, &
var_str ("event_index"), expr%index, &
is_known = expr%has_index, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_real_ptr (expr%var_list, &
var_str ("event_weight"), expr%weight_prc, &
is_known = expr%has_weight_prc, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_real_ptr (expr%var_list, &
var_str ("event_weight_ref"), expr%weight_ref, &
is_known = expr%has_weight_ref, &
locked = .true., verbose = .false., intrinsic = .true.)
call var_list_append_real_ptr (expr%var_list, &
var_str ("event_excess"), expr%excess_prc, &
is_known = expr%has_excess_prc, &
locked = .true., verbose = .false., intrinsic = .true.)
end subroutine event_expr_setup_vars
@ %def event_expr_setup_vars
@ Compile the analysis expression. If the pointer is disassociated, there is
no expression.
<<Subevt expr: event expr: TBP>>=
procedure :: setup_analysis => event_expr_setup_analysis
<<Subevt expr: procedures>>=
subroutine event_expr_setup_analysis (expr, ef_analysis)
class(event_expr_t), intent(inout), target :: expr
class(expr_factory_t), intent(in) :: ef_analysis
call ef_analysis%build (expr%analysis)
if (allocated (expr%analysis)) then
call expr%setup_var_self ()
call expr%analysis%setup_lexpr (expr%var_list)
expr%has_analysis = .true.
end if
end subroutine event_expr_setup_analysis
@ %def event_expr_setup_analysis
@ Compile the reweight expression.
<<Subevt expr: event expr: TBP>>=
procedure :: setup_reweight => event_expr_setup_reweight
<<Subevt expr: procedures>>=
subroutine event_expr_setup_reweight (expr, ef_reweight)
class(event_expr_t), intent(inout), target :: expr
class(expr_factory_t), intent(in) :: ef_reweight
call ef_reweight%build (expr%reweight)
if (allocated (expr%reweight)) then
call expr%setup_var_self ()
call expr%reweight%setup_expr (expr%var_list)
expr%has_reweight = .true.
end if
end subroutine event_expr_setup_reweight
@ %def event_expr_setup_reweight
@ Store the string or numeric process ID. This should be done during
initialization.
<<Subevt expr: event expr: TBP>>=
procedure :: set_process_id => event_expr_set_process_id
procedure :: set_process_num_id => event_expr_set_process_num_id
<<Subevt expr: procedures>>=
subroutine event_expr_set_process_id (expr, id)
class(event_expr_t), intent(inout) :: expr
type(string_t), intent(in) :: id
expr%id = id
expr%has_id = .true.
end subroutine event_expr_set_process_id
subroutine event_expr_set_process_num_id (expr, num_id)
class(event_expr_t), intent(inout) :: expr
integer, intent(in) :: num_id
expr%num_id = num_id
expr%has_num_id = .true.
end subroutine event_expr_set_process_num_id
@ %def event_expr_set_process_id
@ %def event_expr_set_process_num_id
@ Reset / set the data that pertain to a particular event. The event
index is reset unless explicitly told to keep it.
<<Subevt expr: event expr: TBP>>=
procedure :: reset_contents => event_expr_reset_contents
procedure :: set => event_expr_set
<<Subevt expr: procedures>>=
subroutine event_expr_reset_contents (expr)
class(event_expr_t), intent(inout) :: expr
call expr%base_reset_contents ()
expr%has_sqme_ref = .false.
expr%has_sqme_prc = .false.
expr%has_sqme_alt = .false.
expr%has_weight_ref = .false.
expr%has_weight_prc = .false.
expr%has_weight_alt = .false.
expr%has_excess_prc = .false.
end subroutine event_expr_reset_contents
subroutine event_expr_set (expr, &
weight_ref, weight_prc, weight_alt, &
excess_prc, &
sqme_ref, sqme_prc, sqme_alt)
class(event_expr_t), intent(inout) :: expr
real(default), intent(in), optional :: weight_ref, weight_prc
real(default), intent(in), optional :: excess_prc
real(default), intent(in), optional :: sqme_ref, sqme_prc
real(default), dimension(:), intent(in), optional :: sqme_alt, weight_alt
if (present (sqme_ref)) then
expr%has_sqme_ref = .true.
expr%sqme_ref = sqme_ref
end if
if (present (sqme_prc)) then
expr%has_sqme_prc = .true.
expr%sqme_prc = sqme_prc
end if
if (present (sqme_alt)) then
expr%has_sqme_alt = .true.
expr%sqme_alt = sqme_alt
end if
if (present (weight_ref)) then
expr%has_weight_ref = .true.
expr%weight_ref = weight_ref
end if
if (present (weight_prc)) then
expr%has_weight_prc = .true.
expr%weight_prc = weight_prc
end if
if (present (weight_alt)) then
expr%has_weight_alt = .true.
expr%weight_alt = weight_alt
end if
if (present (excess_prc)) then
expr%has_excess_prc = .true.
expr%excess_prc = excess_prc
end if
end subroutine event_expr_set
@ %def event_expr_reset_contents event_expr_set
@ Access the subevent index.
<<Subevt expr: event expr: TBP>>=
procedure :: has_event_index => event_expr_has_event_index
procedure :: get_event_index => event_expr_get_event_index
<<Subevt expr: procedures>>=
function event_expr_has_event_index (expr) result (flag)
class(event_expr_t), intent(in) :: expr
logical :: flag
flag = expr%has_index
end function event_expr_has_event_index
function event_expr_get_event_index (expr) result (index)
class(event_expr_t), intent(in) :: expr
integer :: index
if (expr%has_index) then
index = expr%index
else
index = 0
end if
end function event_expr_get_event_index
-
+
@ %def event_expr_has_event_index
@ %def event_expr_get_event_index
@ Set/increment the subevent index. Initialize it if necessary.
<<Subevt expr: event expr: TBP>>=
procedure :: set_event_index => event_expr_set_event_index
procedure :: reset_event_index => event_expr_reset_event_index
procedure :: increment_event_index => event_expr_increment_event_index
<<Subevt expr: procedures>>=
subroutine event_expr_set_event_index (expr, index)
class(event_expr_t), intent(inout) :: expr
integer, intent(in) :: index
expr%index = index
expr%has_index = .true.
end subroutine event_expr_set_event_index
subroutine event_expr_reset_event_index (expr)
class(event_expr_t), intent(inout) :: expr
expr%has_index = .false.
end subroutine event_expr_reset_event_index
subroutine event_expr_increment_event_index (expr, offset)
class(event_expr_t), intent(inout) :: expr
integer, intent(in), optional :: offset
if (expr%has_index) then
expr%index = expr%index + 1
else if (present (offset)) then
call expr%set_event_index (offset + 1)
else
call expr%set_event_index (1)
end if
end subroutine event_expr_increment_event_index
@ %def event_expr_set_event_index
@ %def event_expr_increment_event_index
@ Fill the event expression: take the particle data and kinematics
from a [[particle_set]] object.
We allow the particle content to change for each event. Therefore, we set the
event variables each time.
Also increment the event index; initialize it if necessary.
<<Subevt expr: event expr: TBP>>=
procedure :: fill_subevt => event_expr_fill_subevt
<<Subevt expr: procedures>>=
subroutine event_expr_fill_subevt (expr, particle_set)
class(event_expr_t), intent(inout) :: expr
type(particle_set_t), intent(in) :: particle_set
call particle_set%to_subevt (expr%subevt_t, expr%colorize_subevt)
expr%sqrts_hat = subevt_get_sqrts_hat (expr%subevt_t)
expr%n_in = subevt_get_n_in (expr%subevt_t)
expr%n_out = subevt_get_n_out (expr%subevt_t)
expr%n_tot = expr%n_in + expr%n_out
expr%subevt_filled = .true.
end subroutine event_expr_fill_subevt
@ %def event_expr_fill_subevt
@ Evaluate, if the event passes the selection. For absent expressions we take
default values.
<<Subevt expr: event expr: TBP>>=
procedure :: evaluate => event_expr_evaluate
<<Subevt expr: procedures>>=
subroutine event_expr_evaluate (expr, passed, reweight, analysis_flag)
class(event_expr_t), intent(inout) :: expr
logical, intent(out) :: passed
real(default), intent(out) :: reweight
logical, intent(out) :: analysis_flag
call expr%base_evaluate (passed)
if (passed) then
if (expr%has_reweight) then
call expr%reweight%evaluate ()
if (expr%reweight%is_known ()) then
reweight = expr%reweight%get_real ()
else
call msg_error ("Evaluate reweight expression: &
&result undefined")
reweight = 0
end if
else
reweight = 1
end if
if (expr%has_analysis) then
call expr%analysis%evaluate ()
if (expr%analysis%is_known ()) then
analysis_flag = expr%analysis%get_log ()
else
call msg_error ("Evaluate analysis expression: &
&result undefined")
analysis_flag = .false.
end if
else
analysis_flag = .true.
end if
end if
end subroutine event_expr_evaluate
@ %def event_expr_evaluate
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Parton states}
A [[parton_state_t]] object contains the effective kinematics and
dynamics of an elementary partonic interaction, with or without the
beam/structure function state included. The type is abstract and has
two distinct extensions. The [[isolated_state_t]] extension describes
the isolated elementary interaction where the [[int_eff]] subobject
contains the complex transition amplitude, exclusive in all quantum
numbers. The particle content and kinematics describe the effective
partonic state. The [[connected_state_t]] extension contains the
partonic [[subevt]] and the expressions for cuts and scales which use
it.
In the isolated state, the effective partonic interaction may either
be identical to the hard interaction, in which case it is just a
pointer to the latter. Or it may involve a rearrangement of partons,
in which case we allocate it explicitly and flag this by
[[int_is_allocated]].
The [[trace]] evaluator contains the absolute square of the effective
transition amplitude matrix, summed over final states. It is also summed over
initial states, depending on the the beam setup allows. The result is used for
integration.
The [[matrix]] evaluator is the counterpart of [[trace]] which is kept
exclusive in all observable quantum numbers. The [[flows]] evaluator is
furthermore exclusive in colors, but neglecting all color interference. The
[[matrix]] and [[flows]] evaluators are filled only for sampling points that
become part of physical events.
Note: It would be natural to make the evaluators allocatable.
However, this causes memory corruption in gfortran 4.6.3. The extra
[[has_XXX]] flags indicate whether evaluators are active, instead.
This module contains no unit tests. The tests are covered by the
[[processes]] module below.
<<[[parton_states.f90]]>>=
<<File header>>
module parton_states
<<Use kinds>>
<<Use debug>>
use io_units
use format_utils, only: write_separator
use diagnostics
use lorentz
use subevents
use variables
use expr_base
use model_data
use flavors
use helicities
use colors
use quantum_numbers
use state_matrices
use polarizations
use interactions
use evaluators
use beams
use sf_base
use process_constants
use prc_core
use subevt_expr
<<Standard module head>>
<<Parton states: public>>
<<Parton states: types>>
contains
<<Parton states: procedures>>
end module parton_states
@ %def parton_states
@
\subsection{Abstract base type}
The common part are the evaluators, one for the trace (summed over all
quantum numbers), one for the transition matrix (summed only over
unobservable quantum numbers), and one for the flow distribution
(transition matrix without interferences, exclusive in color flow).
<<Parton states: types>>=
type, abstract :: parton_state_t
logical :: has_trace = .false.
logical :: has_matrix = .false.
logical :: has_flows = .false.
type(evaluator_t) :: trace
type(evaluator_t) :: matrix
type(evaluator_t) :: flows
contains
<<Parton states: parton state: TBP>>
end type parton_state_t
@ %def parton_state_t
@ The [[isolated_state_t]] extension contains the [[sf_chain_eff]] object
-and the (hard) effective interaction [[int_eff]], separately, both
+and the (hard) effective interaction [[int_eff]], separately, both are
implemented as a pointer. The evaluators (trace, matrix, flows) apply
to the hard interaction only.
If the effective interaction differs from the hard interaction, the
pointer is allocated explicitly. Analogously for [[sf_chain_eff]].
<<Parton states: public>>=
public :: isolated_state_t
<<Parton states: types>>=
type, extends (parton_state_t) :: isolated_state_t
logical :: sf_chain_is_allocated = .false.
type(sf_chain_instance_t), pointer :: sf_chain_eff => null ()
logical :: int_is_allocated = .false.
type(interaction_t), pointer :: int_eff => null ()
contains
<<Parton states: isolated state: TBP>>
end type isolated_state_t
@ %def isolated_state_t
@ The [[connected_state_t]] extension contains all data that enable
the evaluation of observables for the effective connected state. The
evaluators connect the (effective) structure-function chain and hard
interaction that were kept separate in the [[isolated_state_t]].
The [[flows_sf]] evaluator is an extended copy of the
structure-function
The [[expr]] subobject consists of the [[subevt]], a simple event record,
expressions for cuts etc.\ which refer to this record, and a [[var_list]]
which contains event-specific variables, linked to the process variable
list. Variables used within the expressions are looked up in [[var_list]].
<<Parton states: public>>=
public :: connected_state_t
<<Parton states: types>>=
type, extends (parton_state_t) :: connected_state_t
type(state_flv_content_t) :: state_flv
logical :: has_flows_sf = .false.
type(evaluator_t) :: flows_sf
logical :: has_expr = .false.
type(parton_expr_t) :: expr
contains
<<Parton states: connected state: TBP>>
end type connected_state_t
@ %def connected_state_t
@ Output: each evaluator is written only when it is active. The
[[sf_chain]] is only written if it is explicitly allocated.
<<Parton states: parton state: TBP>>=
procedure :: write => parton_state_write
<<Parton states: procedures>>=
subroutine parton_state_write (state, unit, testflag)
class(parton_state_t), intent(in) :: state
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
select type (state)
class is (isolated_state_t)
if (state%sf_chain_is_allocated) then
call write_separator (u)
call state%sf_chain_eff%write (u)
end if
if (state%int_is_allocated) then
call write_separator (u)
write (u, "(1x,A)") &
"Effective interaction:"
call write_separator (u)
call state%int_eff%basic_write (u, testflag = testflag)
end if
class is (connected_state_t)
if (state%has_flows_sf) then
call write_separator (u)
write (u, "(1x,A)") &
"Evaluator (extension of the beam evaluator &
&with color contractions):"
call write_separator (u)
call state%flows_sf%write (u, testflag = testflag)
end if
end select
if (state%has_trace) then
call write_separator (u)
write (u, "(1x,A)") &
"Evaluator (trace of the squared transition matrix):"
call write_separator (u)
call state%trace%write (u, testflag = testflag)
end if
if (state%has_matrix) then
call write_separator (u)
write (u, "(1x,A)") &
"Evaluator (squared transition matrix):"
call write_separator (u)
call state%matrix%write (u, testflag = testflag)
end if
if (state%has_flows) then
call write_separator (u)
write (u, "(1x,A)") &
"Evaluator (squared color-flow matrix):"
call write_separator (u)
call state%flows%write (u, testflag = testflag)
end if
select type (state)
class is (connected_state_t)
if (state%has_expr) then
call write_separator (u)
call state%expr%write (u)
end if
end select
end subroutine parton_state_write
@ %def parton_state_write
@ Finalize interaction and evaluators, but only if allocated.
<<Parton states: parton state: TBP>>=
procedure :: final => parton_state_final
<<Parton states: procedures>>=
subroutine parton_state_final (state)
class(parton_state_t), intent(inout) :: state
if (state%has_flows) then
call state%flows%final ()
state%has_flows = .false.
end if
if (state%has_matrix) then
call state%matrix%final ()
state%has_matrix = .false.
end if
if (state%has_trace) then
call state%trace%final ()
state%has_trace = .false.
end if
select type (state)
class is (connected_state_t)
if (state%has_flows_sf) then
call state%flows_sf%final ()
state%has_flows_sf = .false.
end if
call state%expr%final ()
class is (isolated_state_t)
if (state%int_is_allocated) then
call state%int_eff%final ()
deallocate (state%int_eff)
state%int_is_allocated = .false.
end if
if (state%sf_chain_is_allocated) then
call state%sf_chain_eff%final ()
end if
end select
end subroutine parton_state_final
@ %def parton_state_final
@
\subsection{Common Initialization}
Initialize the isolated parton state. In this version, the
effective structure-function chain [[sf_chain_eff]] and the effective
interaction [[int_eff]] both are trivial pointers to the seed
structure-function chain and to the hard interaction, respectively.
<<Parton states: isolated state: TBP>>=
procedure :: init => isolated_state_init
<<Parton states: procedures>>=
subroutine isolated_state_init (state, sf_chain, int)
class(isolated_state_t), intent(out) :: state
type(sf_chain_instance_t), intent(in), target :: sf_chain
type(interaction_t), intent(in), target :: int
state%sf_chain_eff => sf_chain
state%int_eff => int
end subroutine isolated_state_init
@ %def isolated_state_init
@
\subsection{Evaluator initialization: isolated state}
Create an evaluator for the trace of the squared transition matrix.
The trace goes over all outgoing quantum numbers. Whether we trace
over incoming quantum numbers other than color, depends on the given
[[qn_mask_in]].
There are two options: explicitly computing the color factor table
([[use_cf]] false; [[nc]] defined), or taking the color factor
table from the hard matrix element data.
<<Parton states: isolated state: TBP>>=
procedure :: setup_square_trace => isolated_state_setup_square_trace
<<Parton states: procedures>>=
subroutine isolated_state_setup_square_trace (state, core, &
qn_mask_in, col, keep_fs_flavor)
class(isolated_state_t), intent(inout), target :: state
class(prc_core_t), intent(in) :: core
type(quantum_numbers_mask_t), intent(in), dimension(:) :: qn_mask_in
!!! Actually need allocatable attribute here fore once because col might
!!! enter the subroutine non-allocated.
integer, intent(in), dimension(:), allocatable :: col
logical, intent(in) :: keep_fs_flavor
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask
associate (data => core%data)
allocate (qn_mask (data%n_in + data%n_out))
qn_mask( : data%n_in) = &
quantum_numbers_mask (.false., .true., .false.) &
.or. qn_mask_in
qn_mask(data%n_in + 1 : ) = &
quantum_numbers_mask (.not. keep_fs_flavor, .true., .true.)
if (core%use_color_factors) then
call state%trace%init_square (state%int_eff, qn_mask, &
col_flow_index = data%cf_index, &
col_factor = data%color_factors, &
col_index_hi = col, &
nc = core%nc)
else
call state%trace%init_square (state%int_eff, qn_mask, nc = core%nc)
end if
end associate
state%has_trace = .true.
end subroutine isolated_state_setup_square_trace
@ %def isolated_state_setup_square_trace
@ Setup an identity-evaluator for the trace. This implies that [[me]]
is considered to be a squared amplitude, as for example for BLHA matrix
elements.
<<Parton states: isolated state: TBP>>=
procedure :: setup_identity_trace => isolated_state_setup_identity_trace
<<Parton states: procedures>>=
subroutine isolated_state_setup_identity_trace (state, core, qn_mask_in, &
keep_fs_flavors, keep_colors)
class(isolated_state_t), intent(inout), target :: state
class(prc_core_t), intent(in) :: core
type(quantum_numbers_mask_t), intent(in), dimension(:) :: qn_mask_in
logical, intent(in), optional :: keep_fs_flavors, keep_colors
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask
logical :: fs_flv_flag, col_flag
fs_flv_flag = .true.; col_flag = .true.
if (present(keep_fs_flavors)) fs_flv_flag = .not. keep_fs_flavors
if (present(keep_colors)) col_flag = .not. keep_colors
associate (data => core%data)
allocate (qn_mask (data%n_in + data%n_out))
qn_mask( : data%n_in) = &
quantum_numbers_mask (.false., col_flag, .false.) .or. qn_mask_in
qn_mask(data%n_in + 1 : ) = &
quantum_numbers_mask (fs_flv_flag, col_flag, .true.)
end associate
call state%int_eff%set_mask (qn_mask)
call state%trace%init_identity (state%int_eff)
state%has_trace = .true.
end subroutine isolated_state_setup_identity_trace
@ %def isolated_state_setup_identity_trace
@ Setup the evaluator for the transition matrix, exclusive in
helicities where this is requested.
For all unstable final-state particles we keep polarization according to the
applicable decay options. If the process is a decay itself, this applies also
to the initial state.
For all polarized final-state particles, we keep polarization including
off-diagonal entries. We drop helicity completely for unpolarized final-state
particles.
For the initial state, if the particle has not been handled yet, we
apply the provided [[qn_mask_in]] which communicates the beam properties.
<<Parton states: isolated state: TBP>>=
procedure :: setup_square_matrix => isolated_state_setup_square_matrix
<<Parton states: procedures>>=
subroutine isolated_state_setup_square_matrix &
(state, core, model, qn_mask_in, col)
class(isolated_state_t), intent(inout), target :: state
class(prc_core_t), intent(in) :: core
class(model_data_t), intent(in), target :: model
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask_in
integer, dimension(:), intent(in) :: col
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask
type(flavor_t), dimension(:), allocatable :: flv
integer :: i
logical :: helmask, helmask_hd
associate (data => core%data)
allocate (qn_mask (data%n_in + data%n_out))
allocate (flv (data%n_flv))
do i = 1, data%n_in + data%n_out
call flv%init (data%flv_state(i,:), model)
if ((data%n_in == 1 .or. i > data%n_in) &
.and. any (.not. flv%is_stable ())) then
helmask = all (flv%decays_isotropically ())
helmask_hd = all (flv%decays_diagonal ())
qn_mask(i) = quantum_numbers_mask (.false., .true., helmask, &
mask_hd = helmask_hd)
else if (i > data%n_in) then
helmask = all (.not. flv%is_polarized ())
qn_mask(i) = quantum_numbers_mask (.false., .true., helmask)
else
qn_mask(i) = quantum_numbers_mask (.false., .true., .false.) &
.or. qn_mask_in(i)
end if
end do
if (core%use_color_factors) then
call state%matrix%init_square (state%int_eff, qn_mask, &
col_flow_index = data%cf_index, &
col_factor = data%color_factors, &
col_index_hi = col, &
nc = core%nc)
else
call state%matrix%init_square (state%int_eff, &
qn_mask, &
nc = core%nc)
end if
end associate
state%has_matrix = .true.
end subroutine isolated_state_setup_square_matrix
@ %def isolated_state_setup_square_matrix
@ This procedure initializes the evaluator that computes the
contributions to color flows, neglecting color interference.
The incoming-particle mask can be used to sum over incoming flavor.
Helicity handling: see above.
<<Parton states: isolated state: TBP>>=
procedure :: setup_square_flows => isolated_state_setup_square_flows
<<Parton states: procedures>>=
subroutine isolated_state_setup_square_flows (state, core, model, qn_mask_in)
class(isolated_state_t), intent(inout), target :: state
class(prc_core_t), intent(in) :: core
class(model_data_t), intent(in), target :: model
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask_in
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask
type(flavor_t), dimension(:), allocatable :: flv
integer :: i
logical :: helmask, helmask_hd
associate (data => core%data)
allocate (qn_mask (data%n_in + data%n_out))
allocate (flv (data%n_flv))
do i = 1, data%n_in + data%n_out
call flv%init (data%flv_state(i,:), model)
if ((data%n_in == 1 .or. i > data%n_in) &
.and. any (.not. flv%is_stable ())) then
helmask = all (flv%decays_isotropically ())
helmask_hd = all (flv%decays_diagonal ())
qn_mask(i) = quantum_numbers_mask (.false., .false., helmask, &
mask_hd = helmask_hd)
else if (i > data%n_in) then
helmask = all (.not. flv%is_polarized ())
qn_mask(i) = quantum_numbers_mask (.false., .false., helmask)
else
qn_mask(i) = quantum_numbers_mask (.false., .false., .false.) &
.or. qn_mask_in(i)
end if
end do
call state%flows%init_square (state%int_eff, qn_mask, &
expand_color_flows = .true.)
end associate
state%has_flows = .true.
end subroutine isolated_state_setup_square_flows
@ %def isolated_state_setup_square_flows
@
\subsection{Evaluator initialization: connected state}
Setup a trace evaluator as a product of two evaluators (incoming state,
effective interaction). In the result, all quantum numbers are summed over.
If the optional [[int]] interaction is provided, use this for the
first factor in the convolution. Otherwise, use the final interaction
of the stored [[sf_chain]].
The [[resonant]] flag applies if we want to construct
a decay chain. The resonance property can propagate to the final
event output.
+
+If an extended structure function is required [[requires_extended_sf]],
+we have to not consider [[sub]] as a quantum number.
<<Parton states: connected state: TBP>>=
procedure :: setup_connected_trace => connected_state_setup_connected_trace
<<Parton states: procedures>>=
subroutine connected_state_setup_connected_trace &
(state, isolated, int, resonant, undo_helicities, &
- keep_fs_flavors, extended_sf)
+ keep_fs_flavors, requires_extended_sf)
class(connected_state_t), intent(inout), target :: state
type(isolated_state_t), intent(in), target :: isolated
type(interaction_t), intent(in), optional, target :: int
logical, intent(in), optional :: resonant
logical, intent(in), optional :: undo_helicities
logical, intent(in), optional :: keep_fs_flavors
- logical, intent(in), optional :: extended_sf
+ logical, intent(in), optional :: requires_extended_sf
type(quantum_numbers_mask_t) :: mask
type(interaction_t), pointer :: src_int, beam_int
logical :: reduce, fs_flv_flag
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, &
"connected_state_setup_connected_trace")
reduce = .false.; fs_flv_flag = .true.
if (present (undo_helicities)) reduce = undo_helicities
if (present (keep_fs_flavors)) fs_flv_flag = .not. keep_fs_flavors
mask = quantum_numbers_mask (fs_flv_flag, .true., .true.)
if (present (int)) then
src_int => int
else
src_int => isolated%sf_chain_eff%get_out_int_ptr ()
end if
if (debug2_active (D_PROCESS_INTEGRATION)) then
call src_int%basic_write ()
end if
call state%trace%init_product (src_int, isolated%trace, &
qn_mask_conn = mask, &
qn_mask_rest = mask, &
connections_are_resonant = resonant, &
- ignore_sub = extended_sf)
+ ignore_sub_for_qn = requires_extended_sf)
if (reduce) then
beam_int => isolated%sf_chain_eff%get_beam_int_ptr ()
call undo_qn_hel (beam_int, mask, beam_int%get_n_tot ())
call undo_qn_hel (src_int, mask, src_int%get_n_tot ())
call beam_int%set_matrix_element (cmplx (1, 0, default))
call src_int%set_matrix_element (cmplx (1, 0, default))
end if
state%has_trace = .true.
contains
subroutine undo_qn_hel (int_in, mask, n_tot)
type(interaction_t), intent(inout) :: int_in
type(quantum_numbers_mask_t), intent(in) :: mask
integer, intent(in) :: n_tot
type(quantum_numbers_mask_t), dimension(n_tot) :: mask_in
mask_in = mask
call int_in%set_mask (mask_in)
end subroutine undo_qn_hel
end subroutine connected_state_setup_connected_trace
@ %def connected_state_setup_connected_trace
@ Setup a matrix evaluator as a product of two evaluators (incoming
state, effective interation). In the intermediate state, color and
helicity is summed over. In the final state, we keep the quantum
numbers which are present in the original evaluators.
<<Parton states: connected state: TBP>>=
procedure :: setup_connected_matrix => connected_state_setup_connected_matrix
<<Parton states: procedures>>=
subroutine connected_state_setup_connected_matrix &
(state, isolated, int, resonant, qn_filter_conn)
class(connected_state_t), intent(inout), target :: state
type(isolated_state_t), intent(in), target :: isolated
type(interaction_t), intent(in), optional, target :: int
logical, intent(in), optional :: resonant
type(quantum_numbers_t), intent(in), optional :: qn_filter_conn
type(quantum_numbers_mask_t) :: mask
type(interaction_t), pointer :: src_int
mask = quantum_numbers_mask (.false., .true., .true.)
if (present (int)) then
src_int => int
else
src_int => isolated%sf_chain_eff%get_out_int_ptr ()
end if
call state%matrix%init_product &
(src_int, isolated%matrix, mask, &
qn_filter_conn = qn_filter_conn, &
connections_are_resonant = resonant)
state%has_matrix = .true.
end subroutine connected_state_setup_connected_matrix
@ %def connected_state_setup_connected_matrix
@ Setup a matrix evaluator as a product of two evaluators (incoming
state, effective interation). In the intermediate state, only
helicity is summed over. In the final state, we keep the quantum
numbers which are present in the original evaluators.
If the optional [[int]] interaction is provided, use this for the
first factor in the convolution. Otherwise, use the final interaction
of the stored [[sf_chain]], after creating an intermediate interaction
that includes a correlated color state. We assume that for a
caller-provided [[int]], this is not necessary.
<<Parton states: connected state: TBP>>=
procedure :: setup_connected_flows => connected_state_setup_connected_flows
<<Parton states: procedures>>=
subroutine connected_state_setup_connected_flows &
(state, isolated, int, resonant, qn_filter_conn)
class(connected_state_t), intent(inout), target :: state
type(isolated_state_t), intent(in), target :: isolated
type(interaction_t), intent(in), optional, target :: int
logical, intent(in), optional :: resonant
type(quantum_numbers_t), intent(in), optional :: qn_filter_conn
type(quantum_numbers_mask_t) :: mask
type(interaction_t), pointer :: src_int
mask = quantum_numbers_mask (.false., .false., .true.)
if (present (int)) then
src_int => int
else
src_int => isolated%sf_chain_eff%get_out_int_ptr ()
call state%flows_sf%init_color_contractions (src_int)
state%has_flows_sf = .true.
src_int => state%flows_sf%interaction_t
end if
call state%flows%init_product (src_int, isolated%flows, mask, &
qn_filter_conn = qn_filter_conn, &
connections_are_resonant = resonant)
state%has_flows = .true.
end subroutine connected_state_setup_connected_flows
@ %def connected_state_setup_connected_flows
@ Determine and store the flavor content for the connected state.
This queries the [[matrix]] evaluator component, which should hold the
requested flavor information.
<<Parton states: connected state: TBP>>=
procedure :: setup_state_flv => connected_state_setup_state_flv
<<Parton states: procedures>>=
subroutine connected_state_setup_state_flv (state, n_out_hard)
class(connected_state_t), intent(inout), target :: state
integer, intent(in) :: n_out_hard
call interaction_get_flv_content &
(state%matrix%interaction_t, state%state_flv, n_out_hard)
end subroutine connected_state_setup_state_flv
@ %def connected_state_setup_state_flv
@ Return the current flavor state object.
<<Parton states: connected state: TBP>>=
procedure :: get_state_flv => connected_state_get_state_flv
<<Parton states: procedures>>=
function connected_state_get_state_flv (state) result (state_flv)
class(connected_state_t), intent(in) :: state
type(state_flv_content_t) :: state_flv
state_flv = state%state_flv
end function connected_state_get_state_flv
@ %def connected_state_get_state_flv
@
\subsection{Cuts and expressions}
Set up the [[subevt]] that corresponds to the connected interaction.
The index arrays refer to the interaction.
We assign the particles as follows: the beam particles are the first
two (decay process: one) entries in the trace evaluator. The incoming
partons are identified by their link to the outgoing partons of the
structure-function chain. The outgoing partons are those of the trace
evaluator, which include radiated partons during the
structure-function chain.
<<Parton states: connected state: TBP>>=
procedure :: setup_subevt => connected_state_setup_subevt
<<Parton states: procedures>>=
subroutine connected_state_setup_subevt (state, sf_chain, f_beam, f_in, f_out)
class(connected_state_t), intent(inout), target :: state
type(sf_chain_instance_t), intent(in), target :: sf_chain
type(flavor_t), dimension(:), intent(in) :: f_beam, f_in, f_out
integer :: n_beam, n_in, n_out, n_vir, n_tot, i, j
integer, dimension(:), allocatable :: i_beam, i_in, i_out
integer :: sf_out_i
type(interaction_t), pointer :: sf_int
sf_int => sf_chain%get_out_int_ptr ()
n_beam = size (f_beam)
n_in = size (f_in)
n_out = size (f_out)
n_vir = state%trace%get_n_vir ()
n_tot = state%trace%get_n_tot ()
allocate (i_beam (n_beam), i_in (n_in), i_out (n_out))
i_beam = [(i, i = 1, n_beam)]
do j = 1, n_in
sf_out_i = sf_chain%get_out_i (j)
i_in(j) = interaction_find_link &
(state%trace%interaction_t, sf_int, sf_out_i)
end do
i_out = [(i, i = n_vir + 1, n_tot)]
call state%expr%setup_subevt (state%trace%interaction_t, &
i_beam, i_in, i_out, f_beam, f_in, f_out)
state%has_expr = .true.
end subroutine connected_state_setup_subevt
@ %def connected_state_setup_subevt
@ Initialize the variable list specific for this state/term. We insert event
variables ([[sqrts_hat]]) and link the process variable list. The variable
list acquires pointers to subobjects of [[state]], which must therefore have a
[[target]] attribute.
<<Parton states: connected state: TBP>>=
procedure :: setup_var_list => connected_state_setup_var_list
<<Parton states: procedures>>=
subroutine connected_state_setup_var_list (state, process_var_list, beam_data)
class(connected_state_t), intent(inout), target :: state
type(var_list_t), intent(in), target :: process_var_list
type(beam_data_t), intent(in) :: beam_data
call state%expr%setup_vars (beam_data%get_sqrts ())
call state%expr%link_var_list (process_var_list)
end subroutine connected_state_setup_var_list
@ %def connected_state_setup_var_list
@ Allocate the cut expression etc.
<<Parton states: connected state: TBP>>=
procedure :: setup_cuts => connected_state_setup_cuts
procedure :: setup_scale => connected_state_setup_scale
procedure :: setup_fac_scale => connected_state_setup_fac_scale
procedure :: setup_ren_scale => connected_state_setup_ren_scale
procedure :: setup_weight => connected_state_setup_weight
<<Parton states: procedures>>=
subroutine connected_state_setup_cuts (state, ef_cuts)
class(connected_state_t), intent(inout), target :: state
class(expr_factory_t), intent(in) :: ef_cuts
call state%expr%setup_selection (ef_cuts)
end subroutine connected_state_setup_cuts
subroutine connected_state_setup_scale (state, ef_scale)
class(connected_state_t), intent(inout), target :: state
class(expr_factory_t), intent(in) :: ef_scale
call state%expr%setup_scale (ef_scale)
end subroutine connected_state_setup_scale
subroutine connected_state_setup_fac_scale (state, ef_fac_scale)
class(connected_state_t), intent(inout), target :: state
class(expr_factory_t), intent(in) :: ef_fac_scale
call state%expr%setup_fac_scale (ef_fac_scale)
end subroutine connected_state_setup_fac_scale
subroutine connected_state_setup_ren_scale (state, ef_ren_scale)
class(connected_state_t), intent(inout), target :: state
class(expr_factory_t), intent(in) :: ef_ren_scale
call state%expr%setup_ren_scale (ef_ren_scale)
end subroutine connected_state_setup_ren_scale
subroutine connected_state_setup_weight (state, ef_weight)
class(connected_state_t), intent(inout), target :: state
class(expr_factory_t), intent(in) :: ef_weight
call state%expr%setup_weight (ef_weight)
end subroutine connected_state_setup_weight
@ %def connected_state_setup_expressions
@ Reset the expression object: invalidate the subevt.
<<Parton states: connected state: TBP>>=
procedure :: reset_expressions => connected_state_reset_expressions
<<Parton states: procedures>>=
subroutine connected_state_reset_expressions (state)
class(connected_state_t), intent(inout) :: state
if (state%has_expr) call state%expr%reset_contents ()
end subroutine connected_state_reset_expressions
@ %def connected_state_reset_expressions
@
\subsection{Evaluation}
Transfer momenta to the trace evaluator and fill the [[subevt]] with
this effective kinematics, if applicable.
Note: we may want to apply a boost for the [[subevt]].
<<Parton states: parton state: TBP>>=
procedure :: receive_kinematics => parton_state_receive_kinematics
<<Parton states: procedures>>=
subroutine parton_state_receive_kinematics (state)
class(parton_state_t), intent(inout), target :: state
if (state%has_trace) then
call state%trace%receive_momenta ()
select type (state)
class is (connected_state_t)
if (state%has_expr) then
call state%expr%fill_subevt (state%trace%interaction_t)
end if
end select
end if
end subroutine parton_state_receive_kinematics
@ %def parton_state_receive_kinematics
@ Recover kinematics: We assume that the trace evaluator is filled
with momenta. Send those momenta back to the sources, then fill the
variables and subevent as above.
The incoming momenta of the connected state are not connected to the
isolated state but to the beam interaction. Therefore, the incoming
momenta within the isolated state do not become defined, yet.
Instead, we reconstruct the beam (and ISR) momentum configuration.
<<Parton states: parton state: TBP>>=
procedure :: send_kinematics => parton_state_send_kinematics
<<Parton states: procedures>>=
subroutine parton_state_send_kinematics (state)
class(parton_state_t), intent(inout), target :: state
if (state%has_trace) then
call interaction_send_momenta (state%trace%interaction_t)
select type (state)
class is (connected_state_t)
call state%expr%fill_subevt (state%trace%interaction_t)
end select
end if
end subroutine parton_state_send_kinematics
@ %def parton_state_send_kinematics
@ Evaluate the expressions. The routine evaluates first the cut expression.
If the event passes, it evaluates the other expressions. Where no expressions
are defined, default values are inserted.
<<Parton states: connected state: TBP>>=
procedure :: evaluate_expressions => connected_state_evaluate_expressions
<<Parton states: procedures>>=
subroutine connected_state_evaluate_expressions (state, passed, &
scale, fac_scale, ren_scale, weight, scale_forced, force_evaluation)
class(connected_state_t), intent(inout) :: state
logical, intent(out) :: passed
real(default), intent(out) :: scale, fac_scale, ren_scale, weight
real(default), intent(in), allocatable, optional :: scale_forced
logical, intent(in), optional :: force_evaluation
if (state%has_expr) then
call state%expr%evaluate (passed, scale, fac_scale, ren_scale, weight, &
scale_forced, force_evaluation)
end if
end subroutine connected_state_evaluate_expressions
@ %def connected_state_evaluate_expressions
@ Evaluate the structure-function chain, if it is allocated
explicitly. The argument is the factorization scale.
If the chain is merely a pointer, the chain should already be
evaluated at this point.
<<Parton states: isolated state: TBP>>=
procedure :: evaluate_sf_chain => isolated_state_evaluate_sf_chain
<<Parton states: procedures>>=
subroutine isolated_state_evaluate_sf_chain (state, fac_scale)
class(isolated_state_t), intent(inout) :: state
real(default), intent(in) :: fac_scale
if (state%sf_chain_is_allocated) call state%sf_chain_eff%evaluate (fac_scale)
end subroutine isolated_state_evaluate_sf_chain
@ %def isolated_state_evaluate_sf_chain
@ Evaluate the trace.
<<Parton states: parton state: TBP>>=
procedure :: evaluate_trace => parton_state_evaluate_trace
<<Parton states: procedures>>=
subroutine parton_state_evaluate_trace (state)
class(parton_state_t), intent(inout) :: state
if (state%has_trace) call state%trace%evaluate ()
end subroutine parton_state_evaluate_trace
@ %def parton_state_evaluate_trace
<<Parton states: parton state: TBP>>=
procedure :: evaluate_matrix => parton_state_evaluate_matrix
<<Parton states: procedures>>=
subroutine parton_state_evaluate_matrix (state)
class(parton_state_t), intent(inout) :: state
if (state%has_matrix) call state%matrix%evaluate ()
end subroutine parton_state_evaluate_matrix
@ %def parton_state_evaluate_matrix
@ Evaluate the extra evaluators that we need for physical events.
<<Parton states: parton state: TBP>>=
procedure :: evaluate_event_data => parton_state_evaluate_event_data
<<Parton states: procedures>>=
subroutine parton_state_evaluate_event_data (state, only_momenta)
class(parton_state_t), intent(inout) :: state
logical, intent(in), optional :: only_momenta
logical :: only_mom
only_mom = .false.; if (present (only_momenta)) only_mom = only_momenta
select type (state)
type is (connected_state_t)
if (state%has_flows_sf) then
call state%flows_sf%receive_momenta ()
if (.not. only_mom) call state%flows_sf%evaluate ()
end if
end select
if (state%has_matrix) then
call state%matrix%receive_momenta ()
if (.not. only_mom) call state%matrix%evaluate ()
end if
if (state%has_flows) then
call state%flows%receive_momenta ()
if (.not. only_mom) call state%flows%evaluate ()
end if
end subroutine parton_state_evaluate_event_data
@ %def parton_state_evaluate_event_data
@ Normalize the helicity density matrix by its trace, i.e., factor out
the trace and put it into an overall normalization factor. The trace
and flow evaluators are unchanged.
<<Parton states: parton state: TBP>>=
procedure :: normalize_matrix_by_trace => &
parton_state_normalize_matrix_by_trace
<<Parton states: procedures>>=
subroutine parton_state_normalize_matrix_by_trace (state)
class(parton_state_t), intent(inout) :: state
if (state%has_matrix) call state%matrix%normalize_by_trace ()
end subroutine parton_state_normalize_matrix_by_trace
@ %def parton_state_normalize_matrix_by_trace
@
\subsection{Accessing the state}
Three functions return a pointer to the event-relevant interactions.
<<Parton states: parton state: TBP>>=
procedure :: get_trace_int_ptr => parton_state_get_trace_int_ptr
procedure :: get_matrix_int_ptr => parton_state_get_matrix_int_ptr
procedure :: get_flows_int_ptr => parton_state_get_flows_int_ptr
<<Parton states: procedures>>=
function parton_state_get_trace_int_ptr (state) result (ptr)
class(parton_state_t), intent(in), target :: state
type(interaction_t), pointer :: ptr
if (state%has_trace) then
ptr => state%trace%interaction_t
else
ptr => null ()
end if
end function parton_state_get_trace_int_ptr
function parton_state_get_matrix_int_ptr (state) result (ptr)
class(parton_state_t), intent(in), target :: state
type(interaction_t), pointer :: ptr
if (state%has_matrix) then
ptr => state%matrix%interaction_t
else
ptr => null ()
end if
end function parton_state_get_matrix_int_ptr
function parton_state_get_flows_int_ptr (state) result (ptr)
class(parton_state_t), intent(in), target :: state
type(interaction_t), pointer :: ptr
if (state%has_flows) then
ptr => state%flows%interaction_t
else
ptr => null ()
end if
end function parton_state_get_flows_int_ptr
@ %def parton_state_get_trace_int_ptr
@ %def parton_state_get_matrix_int_ptr
@ %def parton_state_get_flows_int_ptr
@ Return the indices of the beam particles and the outgoing particles within
the trace (and thus, matrix and flows) evaluator, respectively.
<<Parton states: connected state: TBP>>=
procedure :: get_beam_index => connected_state_get_beam_index
procedure :: get_in_index => connected_state_get_in_index
<<Parton states: procedures>>=
subroutine connected_state_get_beam_index (state, i_beam)
class(connected_state_t), intent(in) :: state
integer, dimension(:), intent(out) :: i_beam
call state%expr%get_beam_index (i_beam)
end subroutine connected_state_get_beam_index
subroutine connected_state_get_in_index (state, i_in)
class(connected_state_t), intent(in) :: state
integer, dimension(:), intent(out) :: i_in
call state%expr%get_in_index (i_in)
end subroutine connected_state_get_in_index
@ %def connected_state_get_beam_index
@ %def connected_state_get_in_index
@
<<Parton states: public>>=
public :: refill_evaluator
<<Parton states: procedures>>=
subroutine refill_evaluator (sqme, qn, flv_index, evaluator)
complex(default), intent(in), dimension(:) :: sqme
type(quantum_numbers_t), intent(in), dimension(:,:) :: qn
integer, intent(in), dimension(:), optional :: flv_index
type(evaluator_t), intent(inout) :: evaluator
integer :: i, i_flv
do i = 1, size (sqme)
if (present (flv_index)) then
i_flv = flv_index(i)
else
i_flv = i
end if
call evaluator%add_to_matrix_element (qn(:,i_flv), sqme(i), &
match_only_flavor = .true.)
end do
end subroutine refill_evaluator
@ %def refill_evaluator
@ Return the number of outgoing (hard) particles for the state.
<<Parton states: parton state: TBP>>=
procedure :: get_n_out => parton_state_get_n_out
<<Parton states: procedures>>=
function parton_state_get_n_out (state) result (n)
class(parton_state_t), intent(in), target :: state
integer :: n
n = state%trace%get_n_out ()
end function parton_state_get_n_out
@ %def parton_state_get_n_out
@
\subsection{Unit tests}
<<[[parton_states_ut.f90]]>>=
<<File header>>
module parton_states_ut
use unit_tests
use parton_states_uti
<<Standard module head>>
<<Parton states: public test>>
contains
<<Parton states: test driver>>
end module parton_states_ut
@ %def parton_states_ut
<<[[parton_states_uti.f90]]>>=
<<File header>>
module parton_states_uti
<<Use kinds>>
<<Use strings>>
use constants, only: zero
use numeric_utils
use flavors
use colors
use helicities
use quantum_numbers
use sf_base, only: sf_chain_instance_t
use state_matrices, only: state_matrix_t
use prc_template_me, only: prc_template_me_t
use interactions, only: interaction_t
use models, only: model_t, create_test_model
use parton_states
<<Standard module head>>
<<Parton states: test declarations>>
contains
<<Parton states: tests>>
end module parton_states_uti
@ %def parton_states_uti
@
<<Parton states: public test>>=
public :: parton_states_test
<<Parton states: test driver>>=
subroutine parton_states_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Parton states: execute tests>>
end subroutine parton_states_test
@ %def parton_states_test
@
\subsubsection{Test a simple isolated state}
<<Parton states: execute tests>>=
call test (parton_states_1, "parton_states_1", &
"Create a 2 -> 2 isolated state and compute trace", &
u, results)
<<Parton states: test declarations>>=
public :: parton_states_1
<<Parton states: tests>>=
subroutine parton_states_1 (u)
integer, intent(in) :: u
type(state_matrix_t), allocatable :: state
type(flavor_t), dimension(2) :: flv_in
type(flavor_t), dimension(2) :: flv_out1, flv_out2
type(flavor_t), dimension(4) :: flv_tot
type(helicity_t), dimension(4) :: hel
type(color_t), dimension(4) :: col
integer :: h1, h2, h3, h4
integer :: f
integer :: i
type(quantum_numbers_t), dimension(4) :: qn
type(prc_template_me_t) :: core
type(sf_chain_instance_t), target :: sf_chain
type(interaction_t), target :: int
type(isolated_state_t) :: isolated_state
integer :: n_states = 0
integer, dimension(:), allocatable :: col_flow_index
type(quantum_numbers_mask_t), dimension(2) :: qn_mask
integer, dimension(8) :: i_allowed_states
complex(default), dimension(8) :: me
complex(default) :: me_check_tot, me_check_1, me_check_2, me2
logical :: tmp1, tmp2
type(model_t), pointer :: test_model => null ()
write (u, "(A)") "* Test output: parton_states_1"
write (u, "(A)") "* Purpose: Test the standard parton states"
write (u, "(A)")
call flv_in%init ([11, -11])
call flv_out1%init ([1, -1])
call flv_out2%init ([2, -2])
write (u, "(A)") "* Using incoming flavors: "
call flavor_write_array (flv_in, u)
write (u, "(A)") "* Two outgoing flavor structures: "
call flavor_write_array (flv_out1, u)
call flavor_write_array (flv_out2, u)
write (u, "(A)") "* Initialize state matrix"
allocate (state)
call state%init ()
write (u, "(A)") "* Fill state matrix"
call col(3)%init ([1])
call col(4)%init ([-1])
do f = 1, 2
do h1 = -1, 1, 2
do h2 = -1, 1, 2
do h3 = -1, 1, 2
do h4 = -1, 1, 2
n_states = n_states + 1
call hel%init ([h1, h2, h3, h4], [h1, h2, h3, h4])
if (f == 1) then
flv_tot = [flv_in, flv_out1]
else
flv_tot = [flv_in, flv_out2]
end if
call qn%init (flv_tot, col, hel)
call state%add_state (qn)
end do
end do
end do
end do
end do
!!! Two flavors, one color flow, 2 x 2 x 2 x 2 helicity configurations
!!! -> 32 states.
write (u, "(A)")
write (u, "(A,I2)") "* Generated number of states: ", n_states
call state%freeze ()
!!! Indices of the helicity configurations which are non-zero
i_allowed_states = [6, 7, 10, 11, 22, 23, 26, 27]
me = [cmplx (-1.89448E-5_default, 9.94456E-7_default, default), &
cmplx (-8.37887E-2_default, 4.30842E-3_default, default), &
cmplx (-1.99997E-1_default, -1.01985E-2_default, default), &
cmplx ( 1.79717E-5_default, 9.27038E-7_default, default), &
cmplx (-1.74859E-5_default, 8.78819E-7_default, default), &
cmplx ( 1.67577E-1_default, -8.61683E-3_default, default), &
cmplx ( 2.41331E-1_default, 1.23306E-2_default, default), &
cmplx (-3.59435E-5_default, -1.85407E-6_default, default)]
me_check_tot = cmplx (zero, zero, default)
me_check_1 = cmplx (zero, zero, default)
me_check_2 = cmplx (zero, zero, default)
do i = 1, 8
me2 = me(i) * conjg (me(i))
me_check_tot = me_check_tot + me2
if (i < 5) then
me_check_1 = me_check_1 + me2
else
me_check_2 = me_check_2 + me2
end if
call state%set_matrix_element (i_allowed_states(i), me(i))
end do
!!! Don't forget the color factor
me_check_tot = 3._default * me_check_tot
me_check_1 = 3._default * me_check_1
me_check_2 = 3._default * me_check_2
write (u, "(A)")
write (u, "(A)") "* Setup interaction"
call int%basic_init (2, 0, 2, set_relations = .true.)
call int%set_state_matrix (state)
core%data%n_in = 2; core%data%n_out = 2
core%data%n_flv = 2
allocate (core%data%flv_state (4, 2))
core%data%flv_state (1, :) = [11, 11]
core%data%flv_state (2, :) = [-11, -11]
core%data%flv_state (3, :) = [1, 2]
core%data%flv_state (4, :) = [-1, -2]
core%use_color_factors = .false.
core%nc = 3
write (u, "(A)") "* Init isolated state"
call isolated_state%init (sf_chain, int)
!!! There is only one color flow.
allocate (col_flow_index (n_states)); col_flow_index = 1
call qn_mask%init (.false., .false., .true., mask_cg = .false.)
write (u, "(A)") "* Give a trace to the isolated state"
call isolated_state%setup_square_trace (core, qn_mask, col_flow_index, .false.)
call isolated_state%evaluate_trace ()
write (u, "(A)")
write (u, "(A)", advance = "no") "* Squared matrix element correct: "
write (u, "(L1)") nearly_equal (me_check_tot, &
isolated_state%trace%get_matrix_element (1), rel_smallness = 0.00001_default)
write (u, "(A)") "* Give a matrix to the isolated state"
call create_test_model (var_str ("SM"), test_model)
call isolated_state%setup_square_matrix (core, test_model, qn_mask, col_flow_index)
call isolated_state%evaluate_matrix ()
write (u, "(A)") "* Sub-matrixelements correct: "
tmp1 = nearly_equal (me_check_1, &
isolated_state%matrix%get_matrix_element (1), rel_smallness = 0.00001_default)
tmp2 = nearly_equal (me_check_2, &
isolated_state%matrix%get_matrix_element (2), rel_smallness = 0.00001_default)
write (u, "(A,L1,A,L1)") "* 1: ", tmp1, ", 2: ", tmp2
write (u, "(A)") "* Test output end: parton_states_1"
end subroutine parton_states_1
@ %def parton_states_1
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Process component management}
This module contains tools for managing and combining process
components and matrix-element code and values, acting at a level below
the actual process definition.
\subsection{Abstract base type}
The types introduced here are abstract base types.
<<[[pcm_base.f90]]>>=
<<File header>>
module pcm_base
<<Use kinds>>
use io_units
use diagnostics
use format_utils, only: write_integer_array
use format_utils, only: write_separator
use physics_defs, only: BORN, NLO_REAL
<<Use strings>>
use os_interface, only: os_data_t
use process_libraries, only: process_component_def_t
use process_libraries, only: process_library_t
use prc_core_def
use prc_core
use variables, only: var_list_t
use mappings, only: mapping_defaults_t
use phs_base, only: phs_config_t
use phs_forests, only: phs_parameters_t
use mci_base, only: mci_t
use model_data, only: model_data_t
use models, only: model_t
use blha_config, only: blha_master_t
use blha_olp_interfaces, only: blha_template_t
use process_config
use process_mci, only: process_mci_entry_t
<<Standard module head>>
<<PCM base: public>>
<<PCM base: parameters>>
<<PCM base: types>>
<<PCM base: interfaces>>
contains
<<PCM base: procedures>>
end module pcm_base
@ %def pcm_base
@
\subsection{Core management}
This object holds information about the cores used by the components
-and allocates the corresponding manager instance.
+and allocates the corresponding manager instance.
[[i_component]] is the index of the process component which this core belongs
to. The pointer to the core definition is a convenient help in configuring
the core itself.
We allow for a [[blha_config]] configuration object that covers BLHA cores.
The BLHA standard is suitable generic to warrant support outside of specific
type extension (i.e., applies to LO and NLO if requested). The BLHA
configuration is allocated only if the core requires it.
<<PCM base: public>>=
public :: core_entry_t
<<PCM base: types>>=
type :: core_entry_t
integer :: i_component = 0
logical :: active = .false.
class(prc_core_def_t), pointer :: core_def => null ()
type(blha_template_t), allocatable :: blha_config
class(prc_core_t), allocatable :: core
contains
<<PCM base: core entry: TBP>>
- end type core_entry_t
+ end type core_entry_t
@ %def core_entry_t
@
<<PCM base: core entry: TBP>>=
procedure :: get_core_ptr => core_entry_get_core_ptr
<<PCM base: procedures>>=
function core_entry_get_core_ptr (core_entry) result (core)
class(core_entry_t), intent(in), target :: core_entry
class(prc_core_t), pointer :: core
if (allocated (core_entry%core)) then
core => core_entry%core
else
core => null ()
end if
end function core_entry_get_core_ptr
@ %def core_entry_get_core_ptr
@ Configure the core object after allocation with correct type. The
[[core_def]] object pointer and the index [[i_component]] of the associated
process component are already there.
<<PCM base: core entry: TBP>>=
procedure :: configure => core_entry_configure
<<PCM base: procedures>>=
subroutine core_entry_configure (core_entry, lib, id)
class(core_entry_t), intent(inout) :: core_entry
type(process_library_t), intent(in), target :: lib
type(string_t), intent(in) :: id
call core_entry%core%init &
(core_entry%core_def, lib, id, core_entry%i_component)
end subroutine core_entry_configure
-
+
@ %def core_entry_configure
@
\subsection{Process component manager}
This object may hold process and method-specific data, and it should
allocate the corresponding manager instance.
The number of components determines the [[component_selected]] array.
[[i_phs_config]] is a lookup table that returns the PHS configuration index
for a given component index.
[[i_core]] is a lookup table that returns the core-entry index for a given
component index.
<<PCM base: public>>=
public :: pcm_t
<<PCM base: types>>=
type, abstract :: pcm_t
logical :: initialized = .false.
logical :: has_pdfs = .false.
integer :: n_components = 0
integer :: n_cores = 0
integer :: n_mci = 0
logical, dimension(:), allocatable :: component_selected
logical, dimension(:), allocatable :: component_active
integer, dimension(:), allocatable :: i_phs_config
integer, dimension(:), allocatable :: i_core
integer, dimension(:), allocatable :: i_mci
type(blha_template_t) :: blha_defaults
logical :: uses_blha = .false.
type(os_data_t) :: os_data
contains
<<PCM base: pcm: TBP>>
end type pcm_t
@ %def pcm_t
@ The factory method. We use the [[inout]] intent, so calling this
again is an error.
<<PCM base: pcm: TBP>>=
procedure(pcm_allocate_instance), deferred :: allocate_instance
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_allocate_instance (pcm, instance)
import
class(pcm_t), intent(in) :: pcm
class(pcm_instance_t), intent(inout), allocatable :: instance
end subroutine pcm_allocate_instance
end interface
@ %def pcm_allocate_instance
@
<<PCM base: pcm: TBP>>=
procedure(pcm_is_nlo), deferred :: is_nlo
<<PCM base: interfaces>>=
abstract interface
function pcm_is_nlo (pcm) result (is_nlo)
import
logical :: is_nlo
class(pcm_t), intent(in) :: pcm
end function pcm_is_nlo
end interface
@ %def pcm_is_nlo
@
<<PCM base: pcm: TBP>>=
procedure(pcm_final), deferred :: final
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_final (pcm)
import
class(pcm_t), intent(inout) :: pcm
end subroutine pcm_final
end interface
@ %def pcm_final
@
\subsection{Initialization methods}
The PCM has the duty to coordinate and configure the process-object
-components.
+components.
Initialize the PCM configuration itself, using environment data.
<<PCM base: pcm: TBP>>=
procedure(pcm_init), deferred :: init
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_init (pcm, env, meta)
import
class(pcm_t), intent(out) :: pcm
type(process_environment_t), intent(in) :: env
type(process_metadata_t), intent(in) :: meta
end subroutine pcm_init
end interface
-
-@ %def pcm_init
+
+@ %def pcm_init
@
Initialize the BLHA configuration block, the component-independent default
settings. This is to be called by [[pcm_init]]. We use the provided variable
list.
This block is filled regardless of whether BLHA is actually used, because why
not? We use a default value for the scheme (not set in unit tests).
<<PCM base: pcm: TBP>>=
procedure :: set_blha_defaults => pcm_set_blha_defaults
<<PCM base: procedures>>=
subroutine pcm_set_blha_defaults (pcm, polarized_beams, var_list)
class(pcm_t), intent(inout) :: pcm
type(var_list_t), intent(in) :: var_list
logical, intent(in) :: polarized_beams
logical :: muon_yukawa_off
real(default) :: top_yukawa
type(string_t) :: ew_scheme
muon_yukawa_off = &
var_list%get_lval (var_str ("?openloops_switch_off_muon_yukawa"))
top_yukawa = &
var_list%get_rval (var_str ("blha_top_yukawa"))
ew_scheme = &
var_list%get_sval (var_str ("$blha_ew_scheme"))
if (ew_scheme == "") ew_scheme = "Gmu"
call pcm%blha_defaults%init &
(polarized_beams, muon_yukawa_off, top_yukawa, ew_scheme)
end subroutine pcm_set_blha_defaults
-
+
@ %def pcm_set_blha_defaults
@ Read the method settings from the variable list and store them in the BLHA
master. The details depend on the [[pcm]] concrete type.
<<PCM base: pcm: TBP>>=
procedure(pcm_set_blha_methods), deferred :: set_blha_methods
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_set_blha_methods (pcm, blha_master, var_list)
import
class(pcm_t), intent(in) :: pcm
type(blha_master_t), intent(inout) :: blha_master
type(var_list_t), intent(in) :: var_list
end subroutine pcm_set_blha_methods
end interface
-
+
@ %def pcm_set_blha_methods
@ Produce the LO and NLO flavor-state tables (as far as available), as
appropriate for BLHA configuration. We may inspect either the PCM itself or
the array of process cores.
<<PCM base: pcm: TBP>>=
procedure(pcm_get_blha_flv_states), deferred :: get_blha_flv_states
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_get_blha_flv_states (pcm, core_entry, flv_born, flv_real)
import
class(pcm_t), intent(in) :: pcm
type(core_entry_t), dimension(:), intent(in) :: core_entry
integer, dimension(:,:), allocatable, intent(out) :: flv_born
integer, dimension(:,:), allocatable, intent(out) :: flv_real
end subroutine pcm_get_blha_flv_states
end interface
-
+
@ %def pcm_get_blha_flv_states
@
Allocate the right number of process components. The number is also stored in
the process meta. Initially, all components are active but none are
selected.
<<PCM base: pcm: TBP>>=
procedure :: allocate_components => pcm_allocate_components
<<PCM base: procedures>>=
subroutine pcm_allocate_components (pcm, comp, meta)
class(pcm_t), intent(inout) :: pcm
type(process_component_t), dimension(:), allocatable, intent(out) :: comp
type(process_metadata_t), intent(in) :: meta
pcm%n_components = meta%n_components
allocate (comp (pcm%n_components))
allocate (pcm%component_selected (pcm%n_components), source = .false.)
allocate (pcm%component_active (pcm%n_components), source = .true.)
end subroutine pcm_allocate_components
-
+
@ %def pcm_allocate_components
@ Each process component belongs to a category/type, which we identify by a
universal integer constant. The categories can be taken from the process
definition. For easy lookup, we store the categories in an array.
<<PCM base: pcm: TBP>>=
procedure(pcm_categorize_components), deferred :: categorize_components
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_categorize_components (pcm, config)
import
class(pcm_t), intent(inout) :: pcm
type(process_config_data_t), intent(in) :: config
end subroutine pcm_categorize_components
end interface
@ %def pcm_categorize_components
@
Allocate the right number and type(s) of process-core
objects, i.e., the interface object between the process and matrix-element
-code.
+code.
Within the [[pcm]] block, also associate cores with components and store
relevant configuration data, including the [[i_core]] lookup table.
<<PCM base: pcm: TBP>>=
procedure(pcm_allocate_cores), deferred :: allocate_cores
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_allocate_cores (pcm, config, core_entry)
import
class(pcm_t), intent(inout) :: pcm
type(process_config_data_t), intent(in) :: config
type(core_entry_t), dimension(:), allocatable, intent(out) :: core_entry
end subroutine pcm_allocate_cores
end interface
-
+
@ %def pcm_allocate_cores
@ Generate and interface external code for a single core, if this is
required.
<<PCM base: pcm: TBP>>=
procedure(pcm_prepare_any_external_code), deferred :: &
prepare_any_external_code
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_prepare_any_external_code &
(pcm, core_entry, i_core, libname, model, var_list)
import
class(pcm_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
integer, intent(in) :: i_core
type(string_t), intent(in) :: libname
type(model_data_t), intent(in), target :: model
type(var_list_t), intent(in) :: var_list
end subroutine pcm_prepare_any_external_code
end interface
-
+
@ %def pcm_prepare_any_external_code
@ Prepare the BLHA configuration for a core object that requires it. This
does not affect the core object, which may not yet be allocated.
<<PCM base: pcm: TBP>>=
procedure(pcm_setup_blha), deferred :: setup_blha
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_setup_blha (pcm, core_entry)
import
class(pcm_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
end subroutine pcm_setup_blha
end interface
-
+
@ %def pcm_setup_blha
@ Configure the BLHA interface for a core object that requires it. This is
separate from the previous method, assuming that the [[pcm]] has to allocate
the actual cores and acquire some data in-between.
<<PCM base: pcm: TBP>>=
procedure(pcm_prepare_blha_core), deferred :: prepare_blha_core
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_prepare_blha_core (pcm, core_entry, model)
import
class(pcm_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
class(model_data_t), intent(in), target :: model
end subroutine pcm_prepare_blha_core
end interface
-
+
@ %def pcm_prepare_blha_core
@ Allocate and configure the MCI (multi-channel integrator) records and their
relation to process components, appropriate for the algorithm implemented by
-[[pcm]].
+[[pcm]].
Create a [[mci_t]] template: the procedure [[dispatch_mci]] is called as a
factory method for allocating the [[mci_t]] object with a specific concrete
type. The call may depend on the concrete [[pcm]] type.
<<PCM base: public>>=
public :: dispatch_mci_proc
<<PCM base: interfaces>>=
abstract interface
subroutine dispatch_mci_proc (mci, var_list, process_id, is_nlo)
import
class(mci_t), allocatable, intent(out) :: mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
logical, intent(in), optional :: is_nlo
end subroutine dispatch_mci_proc
end interface
-
+
@ %def dispatch_mci_proc
<<PCM base: pcm: TBP>>=
procedure(pcm_setup_mci), deferred :: setup_mci
procedure(pcm_call_dispatch_mci), deferred :: call_dispatch_mci
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_setup_mci (pcm, mci_entry)
import
class(pcm_t), intent(inout) :: pcm
type(process_mci_entry_t), &
dimension(:), allocatable, intent(out) :: mci_entry
end subroutine pcm_setup_mci
end interface
-
+
abstract interface
subroutine pcm_call_dispatch_mci (pcm, &
dispatch_mci, var_list, process_id, mci_template)
import
class(pcm_t), intent(inout) :: pcm
procedure(dispatch_mci_proc) :: dispatch_mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
class(mci_t), intent(out), allocatable :: mci_template
end subroutine pcm_call_dispatch_mci
end interface
-
+
@ %def pcm_setup_mci
@ %def pcm_call_dispatch_mci
@ Proceed with PCM configuration based on the core and component
configuration data. Base version is empty.
<<PCM base: pcm: TBP>>=
procedure(pcm_complete_setup), deferred :: complete_setup
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_complete_setup (pcm, core_entry, component, model)
import
class(pcm_t), intent(inout) :: pcm
type(core_entry_t), dimension(:), intent(in) :: core_entry
type(process_component_t), dimension(:), intent(inout) :: component
type(model_t), intent(in), target :: model
end subroutine pcm_complete_setup
end interface
@ %def pcm_complete_setup
@
\subsubsection{Retrieve information}
Return the core index that belongs to a particular component.
<<PCM base: pcm: TBP>>=
procedure :: get_i_core => pcm_get_i_core
<<PCM base: procedures>>=
function pcm_get_i_core (pcm, i_component) result (i_core)
class(pcm_t), intent(in) :: pcm
integer, intent(in) :: i_component
integer :: i_core
if (allocated (pcm%i_core)) then
i_core = pcm%i_core(i_component)
else
i_core = 0
end if
end function pcm_get_i_core
-
+
@ %def pcm_get_i_core
-@
+@
\subsubsection{Phase-space configuration}
Allocate and initialize the right number and type(s) of phase-space
configuration entries. The [[i_phs_config]] lookup table must be set
accordingly.
<<PCM base: pcm: TBP>>=
procedure(pcm_init_phs_config), deferred :: init_phs_config
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_init_phs_config &
(pcm, phs_entry, meta, env, phs_par, mapping_defs)
import
class(pcm_t), intent(inout) :: pcm
type(process_phs_config_t), &
dimension(:), allocatable, intent(out) :: phs_entry
type(process_metadata_t), intent(in) :: meta
type(process_environment_t), intent(in) :: env
type(mapping_defaults_t), intent(in) :: mapping_defs
type(phs_parameters_t), intent(in) :: phs_par
end subroutine pcm_init_phs_config
end interface
@ %def pcm_init_phs_config
@
Initialize a single component. We require all process-configuration blocks,
and specific templates for the phase-space and integrator configuration.
We also provide the current component index [[i]] and the [[active]] flag.
<<PCM base: pcm: TBP>>=
procedure(pcm_init_component), deferred :: init_component
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_init_component &
(pcm, component, i, active, phs_config, env, meta, config)
import
class(pcm_t), intent(in) :: pcm
type(process_component_t), intent(out) :: component
integer, intent(in) :: i
logical, intent(in) :: active
class(phs_config_t), allocatable, intent(in) :: phs_config
type(process_environment_t), intent(in) :: env
type(process_metadata_t), intent(in) :: meta
type(process_config_data_t), intent(in) :: config
end subroutine pcm_init_component
end interface
-
+
@ %def pcm_init_component
@
Record components in the process [[meta]] data if they have turned
out to be inactive.
<<PCM base: pcm: TBP>>=
procedure :: record_inactive_components => pcm_record_inactive_components
<<PCM base: procedures>>=
subroutine pcm_record_inactive_components (pcm, component, meta)
class(pcm_t), intent(inout) :: pcm
type(process_component_t), dimension(:), intent(in) :: component
type(process_metadata_t), intent(inout) :: meta
integer :: i
pcm%component_active = component%active
do i = 1, pcm%n_components
if (.not. component(i)%active) call meta%deactivate_component (i)
end do
end subroutine pcm_record_inactive_components
-
+
@ %def pcm_record_inactive_components
@
\subsection{Manager instance}
This object deals with the actual (squared) matrix element values.
<<PCM base: public>>=
public :: pcm_instance_t
<<PCM base: types>>=
type, abstract :: pcm_instance_t
class(pcm_t), pointer :: config => null ()
logical :: bad_point = .false.
contains
<<PCM base: pcm instance: TBP>>
end type pcm_instance_t
@ %def pcm_instance_t
@
<<PCM base: pcm instance: TBP>>=
procedure(pcm_instance_final), deferred :: final
<<PCM base: interfaces>>=
abstract interface
subroutine pcm_instance_final (pcm_instance)
import
class(pcm_instance_t), intent(inout) :: pcm_instance
end subroutine pcm_instance_final
end interface
@ %def pcm_instance_final
@
<<PCM base: pcm instance: TBP>>=
procedure :: link_config => pcm_instance_link_config
<<PCM base: procedures>>=
subroutine pcm_instance_link_config (pcm_instance, config)
class(pcm_instance_t), intent(inout) :: pcm_instance
class(pcm_t), intent(in), target :: config
pcm_instance%config => config
end subroutine pcm_instance_link_config
@ %def pcm_instance_link_config
@
<<PCM base: pcm instance: TBP>>=
procedure :: is_valid => pcm_instance_is_valid
<<PCM base: procedures>>=
function pcm_instance_is_valid (pcm_instance) result (valid)
logical :: valid
class(pcm_instance_t), intent(in) :: pcm_instance
valid = .not. pcm_instance%bad_point
end function pcm_instance_is_valid
@ %def pcm_instance_is_valid
@
<<PCM base: pcm instance: TBP>>=
procedure :: set_bad_point => pcm_instance_set_bad_point
<<PCM base: procedures>>=
pure subroutine pcm_instance_set_bad_point (pcm_instance, bad_point)
class(pcm_instance_t), intent(inout) :: pcm_instance
logical, intent(in) :: bad_point
pcm_instance%bad_point = pcm_instance%bad_point .or. bad_point
end subroutine pcm_instance_set_bad_point
@ %def pcm_instance_set_bad_point
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{The process object}
<<[[process.f90]]>>=
<<File header>>
module process
<<Use kinds>>
<<Use strings>>
<<Use debug>>
use io_units
use format_utils, only: write_separator
use constants
use diagnostics
use numeric_utils
use lorentz
use cputime
use md5
use rng_base
use dispatch_rng, only: dispatch_rng_factory
use dispatch_rng, only: update_rng_seed_in_var_list
use os_interface
use sm_qcd
use integration_results
use mci_base
use flavors
use model_data
use models
use physics_defs
use process_libraries
use process_constants
use particles
use variables
use beam_structures
use beams
use interactions
use pdg_arrays
use expr_base
use sf_base
use sf_mappings
use resonances, only: resonance_history_t, resonance_history_set_t
use prc_test_core, only: test_t
use prc_core_def, only: prc_core_def_t
use prc_core, only: prc_core_t, helicity_selection_t
use prc_external, only: prc_external_t
use prc_recola, only: prc_recola_t
use blha_olp_interfaces, only: prc_blha_t, blha_template_t
use prc_threshold, only: prc_threshold_t
use phs_fks, only: phs_fks_config_t
use phs_base
use mappings, only: mapping_defaults_t
use phs_forests, only: phs_parameters_t
use phs_wood, only: phs_wood_config_t
use phs_wood, only: EXTENSION_DEFAULT, EXTENSION_DGLAP
use dispatch_phase_space, only: dispatch_phs
use blha_config, only: blha_master_t
use nlo_data, only: FKS_DEFAULT, FKS_RESONANCES
use parton_states, only: connected_state_t
use pcm_base
use pcm
use process_counter
use process_config
use process_mci
<<Standard module head>>
<<Process: public>>
<<Process: public parameters>>
<<Process: types>>
<<Process: interfaces>>
contains
<<Process: procedures>>
end module process
@ %def process
@
\subsection{Process status}
Store counter and status information in a process object.
<<Process: types>>=
type :: process_status_t
private
end type process_status_t
-
+
@ %def process_status_t
@
\subsection{Process status}
Store integration results in a process object.
<<Process: types>>=
type :: process_results_t
private
end type process_results_t
-
+
@ %def process_results_t
@
\subsection{The process type}
A process object is the workspace for the process instance.
After initialization, its contents are filled by
integration passes which shape the integration grids and compute cross
sections. Processes are set up initially from user-level
configuration data. After calculating integrals and thus developing
integration grid data, the program may use a process
object or a copy of it for the purpose of generating events.
The process object consists of several subobjects with their specific
purposes. The corresponding types are defined below. (Technically,
the subobject type definitions have to come before the process type
definition, but with NOWEB magic we reverse this order here.)
The [[type]] determines whether we are considering a decay or a
scattering process.
The [[meta]] object describes the process and its environment. All
contents become fixed when the object is initialized.
The [[config]] object holds physical and technical configuration data
that have been obtained during process initialization, and which are
common to all process components.
The individual process components are configured in the [[component]]
objects. These objects contain more configuration parameters and
workspace, as needed for the specific process variant.
The [[term]] objects describe parton configurations which are
technically used as phase-space points. Each process component may
split into several terms with distinct kinematics and particle
content. Furthermore, each term may project on a different physical
state, e.g., by particle recombination. The [[term]] object provides
the framework for this projection, for applying cuts, weight, and thus
completing the process calculation.
The [[beam_config]] object describes the incoming particles, either the
decay mother or the scattering beams. It also contains the structure-function
information.
The [[mci_entry]] objects configure a MC input parameter set and integrator,
each. The number of parameters depends on the process component and on the
beam and structure-function setup.
The [[pcm]] component is the process-component manager. This
polymorphic object manages and hides the details of dealing with NLO
processes where several components have to be combined in a
non-trivial way. It also acts as an abstract factory for the
corresponding object in [[process_instance]], which does the actual
work for this matter.
<<Process: public>>=
public :: process_t
<<Process: types>>=
type :: process_t
private
type(process_metadata_t) :: &
meta
type(process_environment_t) :: &
env
type(process_config_data_t) :: &
config
class(pcm_t), allocatable :: &
pcm
type(process_component_t), dimension(:), allocatable :: &
component
type(process_phs_config_t), dimension(:), allocatable :: &
phs_entry
type(core_entry_t), dimension(:), allocatable :: &
core_entry
type(process_mci_entry_t), dimension(:), allocatable :: &
mci_entry
class(rng_factory_t), allocatable :: &
rng_factory
type(process_beam_config_t) :: &
beam_config
type(process_term_t), dimension(:), allocatable :: &
term
type(process_status_t) :: &
status
type(process_results_t) :: &
result
contains
<<Process: process: TBP>>
end type process_t
@ %def process_t
@
\subsection{Process pointer}
Wrapper type for storing pointers to process objects in arrays.
<<Process: public>>=
public :: process_ptr_t
<<Process: types>>=
type :: process_ptr_t
type(process_t), pointer :: p => null ()
end type process_ptr_t
@ %def process_ptr_t
@
\subsection{Output}
This procedure is an important debugging and inspection tool; it is
not used during normal operation. The process object is written
to a file (identified by unit, which may also be standard output).
Optional flags determine whether we show everything or just the
interesting parts.
-The shorthand as a traditional TBP.
+The shorthand as a traditional TBP.
<<Process: process: TBP>>=
procedure :: write => process_write
<<Process: procedures>>=
subroutine process_write (process, screen, unit, &
show_os_data, show_var_list, show_rng, show_expressions, pacify)
class(process_t), intent(in) :: process
logical, intent(in) :: screen
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_os_data
logical, intent(in), optional :: show_var_list
logical, intent(in), optional :: show_rng
logical, intent(in), optional :: show_expressions
logical, intent(in), optional :: pacify
integer :: u, iostat
character(0) :: iomsg
integer, dimension(:), allocatable :: v_list
u = given_output_unit (unit)
allocate (v_list (0))
call set_flag (v_list, F_SHOW_OS_DATA, show_os_data)
call set_flag (v_list, F_SHOW_VAR_LIST, show_var_list)
call set_flag (v_list, F_SHOW_RNG, show_rng)
call set_flag (v_list, F_SHOW_EXPRESSIONS, show_expressions)
call set_flag (v_list, F_PACIFY, pacify)
if (screen) then
call process%write_formatted (u, "LISTDIRECTED", v_list, iostat, iomsg)
else
call process%write_formatted (u, "DT", v_list, iostat, iomsg)
end if
end subroutine process_write
@ %def process_write
-@ Standard DTIO procedure with binding.
+@ Standard DTIO procedure with binding.
For the particular application, the screen format is triggered by the
[[LISTDIRECTED]] option for the [[iotype]] format editor string. The
other options activate when the particular parameter value is found in
[[v_list]].
NOTE: The DTIO [[generic]] binding is supported by gfortran since 7.0.
TODO wk 2018: The default could be to show everything, and we should have separate
switches for all major parts. Currently, there are only a few.
<<Process: process: TBP>>=
! generic :: write (formatted) => write_formatted
procedure :: write_formatted => process_write_formatted
<<Process: procedures>>=
subroutine process_write_formatted (dtv, unit, iotype, v_list, iostat, iomsg)
class(process_t), intent(in) :: dtv
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, dimension(:), intent(in) :: v_list
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
integer :: u
logical :: screen
logical :: var_list
logical :: rng_factory
logical :: expressions
logical :: counters
logical :: os_data
logical :: model
logical :: pacify
integer :: i
u = unit
select case (iotype)
case ("LISTDIRECTED")
screen = .true.
case default
screen = .false.
end select
var_list = flagged (v_list, F_SHOW_VAR_LIST)
rng_factory = flagged (v_list, F_SHOW_RNG, .true.)
expressions = flagged (v_list, F_SHOW_EXPRESSIONS)
counters = .true.
os_data = flagged (v_list, F_SHOW_OS_DATA)
model = .false.
pacify = flagged (v_list, F_PACIFY)
associate (process => dtv)
if (screen) then
write (msg_buffer, "(A)") repeat ("-", 72)
call msg_message ()
else
call write_separator (u, 2)
end if
call process%meta%write (u, screen)
if (var_list) then
call process%env%write (u, show_var_list=var_list, &
show_model=.false., show_lib=.false., &
show_os_data=os_data)
else if (.not. screen) then
write (u, "(1x,A)") "Variable list: [not shown]"
end if
if (process%meta%type == PRC_UNKNOWN) then
call write_separator (u, 2)
return
else if (screen) then
return
end if
call write_separator (u)
call process%config%write (u, counters, model, expressions)
if (rng_factory) then
if (allocated (process%rng_factory)) then
call write_separator (u)
call process%rng_factory%write (u)
end if
end if
call write_separator (u, 2)
if (allocated (process%component)) then
write (u, "(1x,A)") "Process component configuration:"
do i = 1, size (process%component)
call write_separator (u)
call process%component(i)%write (u)
end do
else
write (u, "(1x,A)") "Process component configuration: [undefined]"
end if
call write_separator (u, 2)
if (allocated (process%term)) then
write (u, "(1x,A)") "Process term configuration:"
do i = 1, size (process%term)
call write_separator (u)
call process%term(i)%write (u)
end do
else
write (u, "(1x,A)") "Process term configuration: [undefined]"
end if
call write_separator (u, 2)
call process%beam_config%write (u)
call write_separator (u, 2)
if (allocated (process%mci_entry)) then
write (u, "(1x,A)") "Multi-channel integrator configurations:"
do i = 1, size (process%mci_entry)
call write_separator (u)
write (u, "(1x,A,I0,A)") "MCI #", i, ":"
call process%mci_entry(i)%write (u, pacify)
end do
end if
call write_separator (u, 2)
end associate
iostat = 0
iomsg = ""
end subroutine process_write_formatted
-
+
@ %def process_write_formatted
@
<<Process: process: TBP>>=
procedure :: write_meta => process_write_meta
<<Process: procedures>>=
subroutine process_write_meta (process, unit, testflag)
class(process_t), intent(in) :: process
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u, i
u = given_output_unit (unit)
select case (process%meta%type)
case (PRC_UNKNOWN)
write (u, "(1x,A)") "Process instance [undefined]"
return
case (PRC_DECAY)
write (u, "(1x,A)", advance="no") "Process instance [decay]:"
case (PRC_SCATTERING)
write (u, "(1x,A)", advance="no") "Process instance [scattering]:"
case default
call msg_bug ("process_instance_write: undefined process type")
end select
write (u, "(1x,A,A,A)") "'", char (process%meta%id), "'"
write (u, "(3x,A,A,A)") "Run ID = '", char (process%meta%run_id), "'"
if (allocated (process%meta%component_id)) then
write (u, "(3x,A)") "Process components:"
do i = 1, size (process%meta%component_id)
if (process%pcm%component_selected(i)) then
write (u, "(3x,'*')", advance="no")
else
write (u, "(4x)", advance="no")
end if
write (u, "(1x,I0,9A)") i, ": '", &
char (process%meta%component_id (i)), "': ", &
char (process%meta%component_description (i))
end do
end if
end subroutine process_write_meta
@ %def process_write_meta
@ Screen output. Write a short account of the process configuration
and the current results. The verbose version lists the components,
the short version just the results.
<<Process: process: TBP>>=
procedure :: show => process_show
<<Process: procedures>>=
subroutine process_show (object, unit, verbose)
class(process_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
logical :: verb
real(default) :: err_percent
u = given_output_unit (unit)
verb = .true.; if (present (verbose)) verb = verbose
if (verb) then
call object%meta%show (u, object%config%model%get_name ())
select case (object%meta%type)
case (PRC_DECAY)
write (u, "(2x,A)", advance="no") "Computed width ="
case (PRC_SCATTERING)
write (u, "(2x,A)", advance="no") "Computed cross section ="
case default; return
end select
else
if (object%meta%run_id /= "") then
write (u, "('Run',1x,A,':',1x)", advance="no") &
char (object%meta%run_id)
end if
write (u, "(A)", advance="no") char (object%meta%id)
select case (object%meta%num_id)
case (0)
write (u, "(':')")
case default
write (u, "(1x,'(',I0,')',':')") object%meta%num_id
end select
write (u, "(2x)", advance="no")
end if
if (object%has_integral_tot ()) then
write (u, "(ES14.7,1x,'+-',ES9.2)", advance="no") &
object%get_integral_tot (), object%get_error_tot ()
select case (object%meta%type)
case (PRC_DECAY)
write (u, "(1x,A)", advance="no") "GeV"
case (PRC_SCATTERING)
write (u, "(1x,A)", advance="no") "fb "
case default
write (u, "(1x,A)", advance="no") " "
end select
if (object%get_integral_tot () /= 0) then
err_percent = abs (100 &
* object%get_error_tot () / object%get_integral_tot ())
else
err_percent = 0
end if
if (err_percent == 0) then
write (u, "(1x,'(',F4.0,4x,'%)')") err_percent
else if (err_percent < 0.1) then
write (u, "(1x,'(',F7.3,1x,'%)')") err_percent
else if (err_percent < 1) then
write (u, "(1x,'(',F6.2,2x,'%)')") err_percent
else if (err_percent < 10) then
write (u, "(1x,'(',F5.1,3x,'%)')") err_percent
else
write (u, "(1x,'(',F4.0,4x,'%)')") err_percent
end if
else
write (u, "(A)") "[integral undefined]"
end if
end subroutine process_show
@ %def process_show
@ Finalizer. Explicitly iterate over all subobjects that may contain
allocated pointers.
TODO wk 2018 (workaround): The finalizer for the [[config_data]] component is not
called. The reason is that this deletes model data local to the process,
but these could be referenced by pointers (flavor objects) from some
persistent event record. Obviously, such side effects should be avoided, but
this requires refactoring the event-handling procedures.
<<Process: process: TBP>>=
procedure :: final => process_final
<<Process: procedures>>=
subroutine process_final (process)
class(process_t), intent(inout) :: process
integer :: i
! call process%meta%final ()
call process%env%final ()
! call process%config%final ()
if (allocated (process%component)) then
do i = 1, size (process%component)
call process%component(i)%final ()
end do
end if
if (allocated (process%term)) then
do i = 1, size (process%term)
call process%term(i)%final ()
end do
end if
call process%beam_config%final ()
if (allocated (process%mci_entry)) then
do i = 1, size (process%mci_entry)
call process%mci_entry(i)%final ()
end do
end if
if (allocated (process%pcm)) then
call process%pcm%final ()
deallocate (process%pcm)
end if
end subroutine process_final
@ %def process_final
@
\subsubsection{Process setup}
Initialize a process. We need a process library [[lib]] and the process
identifier [[proc_id]] (string). We will fetch the current run ID from the
variable list [[var_list]].
We collect all important data from the environment and store them in the
appropriate places. OS data, model, and variable list are copied
-into [[env]] (true snapshot), also the process library (pointer only).
+into [[env]] (true snapshot), also the process library (pointer only).
The [[meta]] subobject is initialized with process ID and attributes taken
from the process library.
We initialize the [[config]] subobject with all data that are relevant for
this run, using the settings from [[env]]. These data determine the MD5 sum
for this run, which allows us to identify the setup and possibly skips in a
later re-run.
We also allocate and initialize the embedded RNG factory. We take the seed
from the [[var_list]], and we should return the [[var_list]] to the caller
with a new seed.
Finally, we allocate the process component manager [[pcm]], which implements
the chosen algorithm for process integration. The first task of the manager
is to allocate the component array and to determine the component categories
(e.g., Born/Virtual etc.).
TODO wk 2018: The [[pcm]] dispatcher should be provided by the caller, if we
eventually want to eliminate dependencies on concrete [[pcm_t]] extensions.
<<Process: process: TBP>>=
procedure :: init => process_init
<<Process: procedures>>=
subroutine process_init &
(process, proc_id, lib, os_data, model, var_list, beam_structure)
class(process_t), intent(out) :: process
type(string_t), intent(in) :: proc_id
type(process_library_t), intent(in), target :: lib
type(os_data_t), intent(in) :: os_data
class(model_t), intent(in), target :: model
type(var_list_t), intent(inout), target, optional :: var_list
type(beam_structure_t), intent(in), optional :: beam_structure
integer :: next_rng_seed
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, "process_init")
associate &
(meta => process%meta, env => process%env, config => process%config)
call env%init &
(model, lib, os_data, var_list, beam_structure)
call meta%init &
(proc_id, lib, env%get_var_list_ptr ())
call config%init &
(meta, env)
call dispatch_rng_factory &
(process%rng_factory, env%get_var_list_ptr (), next_rng_seed)
call update_rng_seed_in_var_list (var_list, next_rng_seed)
call dispatch_pcm &
(process%pcm, config%process_def%is_nlo ())
associate (pcm => process%pcm)
call pcm%init (env, meta)
call pcm%allocate_components (process%component, meta)
call pcm%categorize_components (config)
end associate
end associate
end subroutine process_init
@ %def process_init
-@
+@
\subsection{Process component manager}
The [[pcm]] (read: process-component manager) takes the responsibility of
steering the actual algorithm of configuration and integration. Depending on
the concrete type, different algorithms can be implemented.
The first version of this supports just two implementations: leading-order
(tree-level) integration and event generation, and NLO (QCD/FKS subtraction).
We thus can start with a single logical for steering the dispatcher.
TODO wk 2018: Eventually, we may eliminate all references to the extensions of
[[pcm_t]] from this module and therefore move this outside the module as well.
<<Process: procedures>>=
subroutine dispatch_pcm (pcm, is_nlo)
class(pcm_t), allocatable, intent(out) :: pcm
logical, intent(in) :: is_nlo
if (.not. is_nlo) then
allocate (pcm_default_t :: pcm)
else
allocate (pcm_nlo_t :: pcm)
end if
end subroutine dispatch_pcm
@ %def dispatch_pcm
@ This step is performed after phase-space and core objects are done: collect
all missing information and prepare the process component manager for the
appropriate integration algorithm.
<<Process: process: TBP>>=
procedure :: complete_pcm_setup => process_complete_pcm_setup
<<Process: procedures>>=
subroutine process_complete_pcm_setup (process)
class(process_t), intent(inout) :: process
call process%pcm%complete_setup &
(process%core_entry, process%component, process%env%get_model_ptr ())
end subroutine process_complete_pcm_setup
-
+
@ %def process_complete_pcm_setup
-@
+@
\subsection{Core management}
Allocate cores (interface objects to matrix-element code).
The [[dispatch_core]] procedure is taken as an argument, so we do not depend on
the implementation, and thus on the specific core types.
The [[helicity_selection]] object collects data that the matrix-element
code needs for configuring the appropriate behavior.
After the cores have been allocated, and assuming the phs initial
configuration has been done before, we proceed with computing the [[pcm]]
internal data.
<<Process: process: TBP>>=
procedure :: setup_cores => process_setup_cores
<<Process: procedures>>=
subroutine process_setup_cores (process, dispatch_core, &
helicity_selection, use_color_factors, has_beam_pol)
class(process_t), intent(inout) :: process
procedure(dispatch_core_proc) :: dispatch_core
type(helicity_selection_t), intent(in), optional :: helicity_selection
logical, intent(in), optional :: use_color_factors
logical, intent(in), optional :: has_beam_pol
integer :: i
associate (pcm => process%pcm)
call pcm%allocate_cores (process%config, process%core_entry)
do i = 1, size (process%core_entry)
call dispatch_core (process%core_entry(i)%core, &
process%core_entry(i)%core_def, &
process%config%model, &
helicity_selection, &
process%config%qcd, &
use_color_factors, &
has_beam_pol)
call process%core_entry(i)%configure &
(process%env%get_lib_ptr (), process%meta%id)
if (process%core_entry(i)%core%uses_blha ()) then
call pcm%setup_blha (process%core_entry(i))
end if
end do
end associate
end subroutine process_setup_cores
-
+
@ %def process_setup_cores
<<Process: interfaces>>=
abstract interface
subroutine dispatch_core_proc (core, core_def, model, &
helicity_selection, qcd, use_color_factors, has_beam_pol)
import
class(prc_core_t), allocatable, intent(inout) :: core
class(prc_core_def_t), intent(in) :: core_def
class(model_data_t), intent(in), target, optional :: model
type(helicity_selection_t), intent(in), optional :: helicity_selection
type(qcd_t), intent(in), optional :: qcd
logical, intent(in), optional :: use_color_factors
logical, intent(in), optional :: has_beam_pol
end subroutine dispatch_core_proc
end interface
-
+
@ %def dispatch_core_proc
@ Use the [[pcm]] to initialize the BLHA interface for each core which
requires it.
<<Process: process: TBP>>=
procedure :: prepare_blha_cores => process_prepare_blha_cores
<<Process: procedures>>=
subroutine process_prepare_blha_cores (process)
class(process_t), intent(inout), target :: process
integer :: i
associate (pcm => process%pcm)
do i = 1, size (process%core_entry)
associate (core_entry => process%core_entry(i))
if (core_entry%core%uses_blha ()) then
pcm%uses_blha = .true.
call pcm%prepare_blha_core (core_entry, process%config%model)
end if
end associate
end do
end associate
end subroutine process_prepare_blha_cores
-
+
@ %def process_prepare_blha_cores
@ Create the BLHA interface data, using PCM for specific data, and write the
BLHA contract file(s).
We take various configuration data and copy them to the [[blha_master]]
record, which then creates and writes the contracts.
For assigning the QCD/QED coupling powers, we inspect the first process
component only. The other parameters are taken as-is from the process
environment variables.
<<Process: process: TBP>>=
procedure :: create_blha_interface => process_create_blha_interface
<<Process: procedures>>=
subroutine process_create_blha_interface (process)
class(process_t), intent(in) :: process
integer :: alpha_power, alphas_power
integer :: openloops_phs_tolerance, openloops_stability_log
logical :: use_cms, use_collier
type(string_t) :: ew_scheme, correction_type
type(string_t) :: openloops_extra_cmd
type(blha_master_t) :: blha_master
integer, dimension(:,:), allocatable :: flv_born, flv_real
if (process%pcm%uses_blha) then
call collect_configuration_parameters (process%get_var_list_ptr ())
call process%component(1)%config%get_coupling_powers &
(alpha_power, alphas_power)
associate (pcm => process%pcm)
call pcm%set_blha_methods (blha_master, process%get_var_list_ptr ())
call blha_master%set_ew_scheme (ew_scheme)
call blha_master%allocate_config_files ()
call blha_master%set_correction_type (correction_type)
call blha_master%setup_additional_features ( &
openloops_phs_tolerance, &
use_cms, &
openloops_stability_log, &
use_collier, &
extra_cmd = openloops_extra_cmd, &
beam_structure = process%env%get_beam_structure ())
call pcm%get_blha_flv_states (process%core_entry, flv_born, flv_real)
call blha_master%generate (process%meta%id, &
process%config%model, process%config%n_in, &
alpha_power, alphas_power, &
flv_born, flv_real)
call blha_master%write_olp (process%meta%id)
end associate
end if
contains
subroutine collect_configuration_parameters (var_list)
type(var_list_t), intent(in) :: var_list
openloops_phs_tolerance = &
var_list%get_ival (var_str ("openloops_phs_tolerance"))
openloops_stability_log = &
var_list%get_ival (var_str ("openloops_stability_log"))
use_cms = &
var_list%get_lval (var_str ("?openloops_use_cms"))
use_collier = &
var_list%get_lval (var_str ("?openloops_use_collier"))
ew_scheme = &
var_list%get_sval (var_str ("$blha_ew_scheme"))
correction_type = &
var_list%get_sval (var_str ("$nlo_correction_type"))
openloops_extra_cmd = &
var_list%get_sval (var_str ("$openloops_extra_cmd"))
end subroutine collect_configuration_parameters
end subroutine process_create_blha_interface
-
+
@ %def process_create_blha_interface
@ Initialize the process components, one by one. We require templates for the
[[mci]] (integrator) and [[phs_config]] (phase-space) configuration data.
The [[active]] flag is set if the component has an associated matrix
element, so we can compute it. The case of no core is a unit-test case.
The specifics depend on the algorithm and are delegated to the [[pcm]]
process-component manager.
The optional [[phs_config]] overrides a pre-generated config array (for unit
test).
<<Process: process: TBP>>=
procedure :: init_components => process_init_components
<<Process: procedures>>=
subroutine process_init_components (process, phs_config)
class(process_t), intent(inout), target :: process
class(phs_config_t), allocatable, intent(in), optional :: phs_config
integer :: i, i_core
class(prc_core_t), pointer :: core
logical :: active
associate (pcm => process%pcm)
do i = 1, pcm%n_components
i_core = pcm%get_i_core(i)
if (i_core > 0) then
core => process%get_core_ptr (i_core)
active = core%has_matrix_element ()
else
active = .true.
end if
if (present (phs_config)) then
call pcm%init_component (process%component(i), &
i, &
active, &
phs_config, &
process%env, process%meta, process%config)
else
call pcm%init_component (process%component(i), &
i, &
active, &
process%phs_entry(pcm%i_phs_config(i))%phs_config, &
process%env, process%meta, process%config)
end if
end do
end associate
end subroutine process_init_components
@ %def process_init_components
@ If process components have turned out to be inactive, this has to be
recorded in the [[meta]] block. Delegate to the [[pcm]].
<<Process: process: TBP>>=
procedure :: record_inactive_components => process_record_inactive_components
<<Process: procedures>>=
subroutine process_record_inactive_components (process)
class(process_t), intent(inout) :: process
associate (pcm => process%pcm)
call pcm%record_inactive_components (process%component, process%meta)
end associate
end subroutine process_record_inactive_components
@ %def process_record_inactive_components
@ Determine the process terms for each process component.
<<Process: process: TBP>>=
procedure :: setup_terms => process_setup_terms
<<Process: procedures>>=
subroutine process_setup_terms (process, with_beams)
class(process_t), intent(inout), target :: process
logical, intent(in), optional :: with_beams
class(model_data_t), pointer :: model
integer :: i, j, k, i_term
integer, dimension(:), allocatable :: n_entry
integer :: n_components, n_tot
integer :: i_sub
type(string_t) :: subtraction_method
class(prc_core_t), pointer :: core => null ()
logical :: setup_subtraction_component, singular_real
logical :: requires_spin_correlations
integer :: nlo_type_to_fetch, n_emitters
i_sub = 0
model => process%config%model
n_components = process%meta%n_components
allocate (n_entry (n_components), source = 0)
do i = 1, n_components
associate (component => process%component(i))
if (component%active) then
n_entry(i) = 1
if (component%get_nlo_type () == NLO_REAL) then
select type (pcm => process%pcm)
type is (pcm_nlo_t)
if (component%component_type /= COMP_REAL_FIN) &
n_entry(i) = n_entry(i) + pcm%region_data%get_n_phs ()
end select
end if
end if
end associate
end do
n_tot = sum (n_entry)
allocate (process%term (n_tot))
k = 0
if (process%is_nlo_calculation ()) then
i_sub = process%component(1)%config%get_associated_subtraction ()
subtraction_method = process%component(i_sub)%config%get_me_method ()
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "process_setup_terms: ", &
subtraction_method)
end if
do i = 1, n_components
associate (component => process%component(i))
if (.not. component%active) cycle
allocate (component%i_term (n_entry(i)))
do j = 1, n_entry(i)
singular_real = component%get_nlo_type () == NLO_REAL &
.and. component%component_type /= COMP_REAL_FIN
setup_subtraction_component = singular_real .and. j == n_entry(i)
i_term = k + j
component%i_term(j) = i_term
if (singular_real) then
process%term(i_term)%i_sub = k + n_entry(i)
else
process%term(i_term)%i_sub = 0
end if
if (setup_subtraction_component) then
select type (pcm => process%pcm)
class is (pcm_nlo_t)
process%term(i_term)%i_core = pcm%i_core(pcm%i_sub)
end select
else
process%term(i_term)%i_core = process%pcm%get_i_core(i)
end if
-
+
if (process%term(i_term)%i_core == 0) then
call msg_bug ("Process '" // char (process%get_id ()) &
// "': core not found!")
end if
core => process%get_core_term (i_term)
if (i_sub > 0) then
select type (pcm => process%pcm)
type is (pcm_nlo_t)
requires_spin_correlations = &
pcm%region_data%requires_spin_correlations ()
n_emitters = pcm%region_data%get_n_emitters_sc ()
class default
requires_spin_correlations = .false.
n_emitters = 0
end select
if (requires_spin_correlations) then
call process%term(i_term)%init ( &
i_term, i, j, core, model, &
nlo_type = component%config%get_nlo_type (), &
use_beam_pol = with_beams, &
subtraction_method = subtraction_method, &
has_pdfs = process%pcm%has_pdfs, &
n_emitters = n_emitters)
else
call process%term(i_term)%init ( &
i_term, i, j, core, model, &
nlo_type = component%config%get_nlo_type (), &
use_beam_pol = with_beams, &
subtraction_method = subtraction_method, &
has_pdfs = process%pcm%has_pdfs)
end if
else
call process%term(i_term)%init ( &
i_term, i, j, core, model, &
nlo_type = component%config%get_nlo_type (), &
use_beam_pol = with_beams, &
has_pdfs = process%pcm%has_pdfs)
end if
end do
end associate
k = k + n_entry(i)
end do
process%config%n_terms = n_tot
end subroutine process_setup_terms
@ %def process_setup_terms
@ Initialize the beam setup. This is the trivial version where the
incoming state of the matrix element coincides with the initial state
of the process. For a scattering process, we need the c.m. energy,
all other variables are set to their default values (no polarization,
lab frame and c.m.\ frame coincide, etc.)
We assume that all components consistently describe a scattering
process, i.e., two incoming particles.
Note: The current layout of the [[beam_data_t]] record requires that the
flavor for each beam is unique. For processes with multiple
flavors in the initial state, one has to set up beams explicitly.
This restriction could be removed by extending the code in the
[[beams]] module.
<<Process: process: TBP>>=
procedure :: setup_beams_sqrts => process_setup_beams_sqrts
<<Process: procedures>>=
subroutine process_setup_beams_sqrts (process, sqrts, beam_structure, i_core)
class(process_t), intent(inout) :: process
real(default), intent(in) :: sqrts
type(beam_structure_t), intent(in), optional :: beam_structure
integer, intent(in), optional :: i_core
type(pdg_array_t), dimension(:,:), allocatable :: pdg_in
integer, dimension(2) :: pdg_scattering
type(flavor_t), dimension(2) :: flv_in
integer :: i, i0, ic
allocate (pdg_in (2, process%meta%n_components))
i0 = 0
do i = 1, process%meta%n_components
if (process%component(i)%active) then
if (present (i_core)) then
ic = i_core
else
ic = process%pcm%get_i_core (i)
end if
associate (core => process%core_entry(ic)%core)
pdg_in(:,i) = core%data%get_pdg_in ()
end associate
if (i0 == 0) i0 = i
end if
end do
do i = 1, process%meta%n_components
if (.not. process%component(i)%active) then
pdg_in(:,i) = pdg_in(:,i0)
end if
end do
if (all (pdg_array_get_length (pdg_in) == 1) .and. &
all (pdg_in(1,:) == pdg_in(1,i0)) .and. &
all (pdg_in(2,:) == pdg_in(2,i0))) then
pdg_scattering = pdg_array_get (pdg_in(:,i0), 1)
call flv_in%init (pdg_scattering, process%config%model)
call process%beam_config%init_scattering (flv_in, sqrts, beam_structure)
else
call msg_fatal ("Setting up process '" // char (process%meta%id) // "':", &
[var_str (" --------------------------------------------"), &
var_str ("Inconsistent initial state. This happens if either "), &
var_str ("several processes with non-matching initial states "), &
var_str ("have been added, or for a single process with an "), &
var_str ("initial state flavor sum. In that case, please set beams "), &
var_str ("explicitly [singling out a flavor / structure function.]")])
end if
end subroutine process_setup_beams_sqrts
@ %def process_setup_beams_sqrts
@ This is the version that applies to decay processes. The energy is the
particle mass, hence no extra argument.
<<Process: process: TBP>>=
procedure :: setup_beams_decay => process_setup_beams_decay
<<Process: procedures>>=
subroutine process_setup_beams_decay (process, rest_frame, beam_structure, i_core)
class(process_t), intent(inout), target :: process
logical, intent(in), optional :: rest_frame
type(beam_structure_t), intent(in), optional :: beam_structure
integer, intent(in), optional :: i_core
type(pdg_array_t), dimension(:,:), allocatable :: pdg_in
integer, dimension(1) :: pdg_decay
type(flavor_t), dimension(1) :: flv_in
integer :: i, i0, ic
allocate (pdg_in (1, process%meta%n_components))
i0 = 0
do i = 1, process%meta%n_components
if (process%component(i)%active) then
if (present (i_core)) then
ic = i_core
else
ic = process%pcm%get_i_core (i)
end if
associate (core => process%core_entry(ic)%core)
pdg_in(:,i) = core%data%get_pdg_in ()
end associate
if (i0 == 0) i0 = i
end if
end do
do i = 1, process%meta%n_components
if (.not. process%component(i)%active) then
pdg_in(:,i) = pdg_in(:,i0)
end if
end do
if (all (pdg_array_get_length (pdg_in) == 1) &
.and. all (pdg_in(1,:) == pdg_in(1,i0))) then
pdg_decay = pdg_array_get (pdg_in(:,i0), 1)
call flv_in%init (pdg_decay, process%config%model)
call process%beam_config%init_decay (flv_in, rest_frame, beam_structure)
else
call msg_fatal ("Setting up decay '" &
// char (process%meta%id) // "': decaying particle not unique")
end if
end subroutine process_setup_beams_decay
@ %def process_setup_beams_decay
@ We have to make sure that the masses of the various flavors
in a given position in the particle string coincide.
<<Process: process: TBP>>=
procedure :: check_masses => process_check_masses
<<Process: procedures>>=
subroutine process_check_masses (process)
class(process_t), intent(in) :: process
type(flavor_t), dimension(:), allocatable :: flv
real(default), dimension(:), allocatable :: mass
integer :: i, j
integer :: i_component
class(prc_core_t), pointer :: core
do i = 1, process%get_n_terms ()
i_component = process%term(i)%i_component
if (.not. process%component(i_component)%active) cycle
core => process%get_core_term (i)
associate (data => core%data)
allocate (flv (data%n_flv), mass (data%n_flv))
do j = 1, data%n_in + data%n_out
call flv%init (data%flv_state(j,:), process%config%model)
mass = flv%get_mass ()
if (any (.not. nearly_equal(mass, mass(1)))) then
call msg_fatal ("Process '" // char (process%meta%id) // "': " &
// "mass values in flavor combination do not coincide. ")
end if
end do
deallocate (flv, mass)
end associate
end do
end subroutine process_check_masses
@ %def process_check_masses
@ For some structure functions we need to get the list of initial
state flavors. This is a two-dimensional array. The first index is
the beam index, the second index is the component index. Each array
element is itself a PDG array object, which consists of the list of
incoming PDG values for this beam and component.
<<Process: process: TBP>>=
procedure :: get_pdg_in => process_get_pdg_in
<<Process: procedures>>=
subroutine process_get_pdg_in (process, pdg_in)
class(process_t), intent(in), target :: process
type(pdg_array_t), dimension(:,:), allocatable, intent(out) :: pdg_in
integer :: i, i_core
allocate (pdg_in (process%config%n_in, process%meta%n_components))
do i = 1, process%meta%n_components
if (process%component(i)%active) then
i_core = process%pcm%get_i_core (i)
associate (core => process%core_entry(i_core)%core)
pdg_in(:,i) = core%data%get_pdg_in ()
end associate
end if
end do
end subroutine process_get_pdg_in
@ %def process_get_pdg_in
@ The phase-space configuration object, in case we need it separately.
<<Process: process: TBP>>=
procedure :: get_phs_config => process_get_phs_config
<<Process: procedures>>=
function process_get_phs_config (process, i_component) result (phs_config)
class(phs_config_t), pointer :: phs_config
class(process_t), intent(in), target :: process
integer, intent(in) :: i_component
if (allocated (process%component)) then
phs_config => process%component(i_component)%phs_config
else
phs_config => null ()
end if
end function process_get_phs_config
@ %def process_get_phs_config
@ The resonance history set can be extracted from the phase-space
configuration. However, this is only possible if the default phase-space
method (wood) has been chosen. If [[include_trivial]] is set, we include the
resonance history with no resonances in the set.
<<Process: process: TBP>>=
procedure :: extract_resonance_history_set &
=> process_extract_resonance_history_set
<<Process: procedures>>=
subroutine process_extract_resonance_history_set &
(process, res_set, include_trivial, i_component)
class(process_t), intent(in), target :: process
type(resonance_history_set_t), intent(out) :: res_set
logical, intent(in), optional :: include_trivial
integer, intent(in), optional :: i_component
integer :: i
i = 1; if (present (i_component)) i = i_component
select type (phs_config => process%get_phs_config (i))
class is (phs_wood_config_t)
call phs_config%extract_resonance_history_set (res_set, include_trivial)
class default
call msg_error ("process '" // char (process%get_id ()) &
// "': extract resonance histories: phase-space method must be &
&'wood'. No resonances can be determined.")
end select
end subroutine process_extract_resonance_history_set
@ %def process_extract_resonance_history_set
@ Initialize from a complete beam setup. If the beam setup does not
apply directly to the process, choose a fallback option as a straight
scattering or decay process.
<<Process: process: TBP>>=
procedure :: setup_beams_beam_structure => process_setup_beams_beam_structure
<<Process: procedures>>=
subroutine process_setup_beams_beam_structure &
(process, beam_structure, sqrts, decay_rest_frame)
class(process_t), intent(inout) :: process
type(beam_structure_t), intent(in) :: beam_structure
real(default), intent(in) :: sqrts
logical, intent(in), optional :: decay_rest_frame
integer :: n_in
logical :: applies
n_in = process%get_n_in ()
call beam_structure%check_against_n_in (process%get_n_in (), applies)
if (applies) then
call process%beam_config%init_beam_structure &
(beam_structure, sqrts, process%get_model_ptr (), decay_rest_frame)
else if (n_in == 2) then
call process%setup_beams_sqrts (sqrts, beam_structure)
else
call process%setup_beams_decay (decay_rest_frame, beam_structure)
end if
end subroutine process_setup_beams_beam_structure
@ %def process_setup_beams_beam_structure
@ Notify the user about beam setup.
<<Process: process: TBP>>=
procedure :: beams_startup_message => process_beams_startup_message
<<Process: procedures>>=
subroutine process_beams_startup_message (process, unit, beam_structure)
class(process_t), intent(in) :: process
integer, intent(in), optional :: unit
type(beam_structure_t), intent(in), optional :: beam_structure
call process%beam_config%startup_message (unit, beam_structure)
end subroutine process_beams_startup_message
@ %def process_beams_startup_message
@ Initialize phase-space configuration by reading out the environment
variables. We return the rebuild flags and store parameters in the blocks
[[phs_par]] and [[mapping_defs]].
The phase-space configuration object(s) are allocated by [[pcm]].
<<Process: process: TBP>>=
procedure :: init_phs_config => process_init_phs_config
<<Process: procedures>>=
subroutine process_init_phs_config (process)
class(process_t), intent(inout) :: process
type(var_list_t), pointer :: var_list
type(phs_parameters_t) :: phs_par
type(mapping_defaults_t) :: mapping_defs
var_list => process%env%get_var_list_ptr ()
phs_par%m_threshold_s = &
var_list%get_rval (var_str ("phs_threshold_s"))
phs_par%m_threshold_t = &
var_list%get_rval (var_str ("phs_threshold_t"))
phs_par%off_shell = &
var_list%get_ival (var_str ("phs_off_shell"))
phs_par%keep_nonresonant = &
var_list%get_lval (var_str ("?phs_keep_nonresonant"))
phs_par%t_channel = &
var_list%get_ival (var_str ("phs_t_channel"))
mapping_defs%energy_scale = &
var_list%get_rval (var_str ("phs_e_scale"))
mapping_defs%invariant_mass_scale = &
var_list%get_rval (var_str ("phs_m_scale"))
mapping_defs%momentum_transfer_scale = &
var_list%get_rval (var_str ("phs_q_scale"))
mapping_defs%step_mapping = &
var_list%get_lval (var_str ("?phs_step_mapping"))
mapping_defs%step_mapping_exp = &
var_list%get_lval (var_str ("?phs_step_mapping_exp"))
mapping_defs%enable_s_mapping = &
var_list%get_lval (var_str ("?phs_s_mapping"))
associate (pcm => process%pcm)
call pcm%init_phs_config (process%phs_entry, &
process%meta, process%env, phs_par, mapping_defs)
end associate
end subroutine process_init_phs_config
-
+
@ %def process_init_phs_config
@ We complete the kinematics configuration after the beam setup, but before we
configure the chain of structure functions. The reason is that we need the
total energy [[sqrts]] for the kinematics, but the structure-function setup
requires the number of channels, which depends on the kinematics
configuration. For instance, the kinematics module may return the need for
parameterizing an s-channel resonance.
<<Process: process: TBP>>=
procedure :: configure_phs => process_configure_phs
<<Process: procedures>>=
subroutine process_configure_phs (process, rebuild, ignore_mismatch, &
combined_integration, subdir)
class(process_t), intent(inout) :: process
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
logical, intent(in), optional :: combined_integration
type(string_t), intent(in), optional :: subdir
real(default) :: sqrts
integer :: i, i_born
class(phs_config_t), pointer :: phs_config_born
sqrts = process%get_sqrts ()
do i = 1, process%meta%n_components
associate (component => process%component(i))
if (component%active) then
select type (pcm => process%pcm)
type is (pcm_default_t)
call component%configure_phs (sqrts, process%beam_config, &
rebuild, ignore_mismatch, subdir)
class is (pcm_nlo_t)
select case (component%config%get_nlo_type ())
case (BORN, NLO_VIRTUAL, NLO_SUBTRACTION)
call component%configure_phs (sqrts, process%beam_config, &
rebuild, ignore_mismatch, subdir)
call check_and_extend_phs (component)
case (NLO_REAL, NLO_MISMATCH, NLO_DGLAP)
i_born = component%config%get_associated_born ()
if (component%component_type /= COMP_REAL_FIN) &
call check_and_extend_phs (component)
call process%component(i_born)%get_phs_config (phs_config_born)
select type (config => component%phs_config)
type is (phs_fks_config_t)
select type (phs_config_born)
type is (phs_wood_config_t)
config%md5sum_born_config = phs_config_born%md5sum_phs_config
call config%set_born_config (phs_config_born)
call config%set_mode (component%config%get_nlo_type ())
end select
end select
call component%configure_phs (sqrts, &
process%beam_config, rebuild, ignore_mismatch, subdir)
end select
class default
call msg_bug ("process_configure_phs: unsupported PCM type")
end select
end if
end associate
end do
contains
subroutine check_and_extend_phs (component)
type(process_component_t), intent(inout) :: component
logical :: requires_dglap_random_number
if (combined_integration) then
requires_dglap_random_number = any (process%component%get_nlo_type () == NLO_DGLAP)
select type (phs_config => component%phs_config)
class is (phs_wood_config_t)
if (requires_dglap_random_number) then
call phs_config%set_extension_mode (EXTENSION_DGLAP)
else
call phs_config%set_extension_mode (EXTENSION_DEFAULT)
end if
call phs_config%increase_n_par ()
end select
end if
end subroutine check_and_extend_phs
end subroutine process_configure_phs
@ %def process_configure_phs
@
<<Process: process: TBP>>=
procedure :: print_phs_startup_message => process_print_phs_startup_message
<<Process: procedures>>=
subroutine process_print_phs_startup_message (process)
class(process_t), intent(in) :: process
integer :: i_component
do i_component = 1, process%meta%n_components
associate (component => process%component(i_component))
if (component%active) then
call component%phs_config%startup_message ()
end if
end associate
end do
end subroutine process_print_phs_startup_message
@ %def process_print_phs_startup_message
@ Insert the structure-function configuration data. First allocate the
storage, then insert data one by one. The third procedure declares a
mapping (of the MC input parameters) for a specific channel and
structure-function combination.
We take the number of channels from the corresponding entry in the
[[config_data]] section.
Otherwise, these a simple wrapper routines. The extra level in the
call tree may allow for simple addressing of multiple concurrent beam
configurations, not implemented currently.
If we do not want structure functions, we simply do not call those procedures.
<<Process: process: TBP>>=
procedure :: init_sf_chain => process_init_sf_chain
generic :: set_sf_channel => set_sf_channel_single
procedure :: set_sf_channel_single => process_set_sf_channel
generic :: set_sf_channel => set_sf_channel_array
procedure :: set_sf_channel_array => process_set_sf_channel_array
<<Process: procedures>>=
subroutine process_init_sf_chain (process, sf_config, sf_trace_file)
class(process_t), intent(inout) :: process
type(sf_config_t), dimension(:), intent(in) :: sf_config
type(string_t), intent(in), optional :: sf_trace_file
type(string_t) :: file
if (present (sf_trace_file)) then
if (sf_trace_file /= "") then
file = sf_trace_file
else
file = process%get_id () // "_sftrace.dat"
end if
call process%beam_config%init_sf_chain (sf_config, file)
else
call process%beam_config%init_sf_chain (sf_config)
end if
end subroutine process_init_sf_chain
subroutine process_set_sf_channel (process, c, sf_channel)
class(process_t), intent(inout) :: process
integer, intent(in) :: c
type(sf_channel_t), intent(in) :: sf_channel
call process%beam_config%set_sf_channel (c, sf_channel)
end subroutine process_set_sf_channel
subroutine process_set_sf_channel_array (process, sf_channel)
class(process_t), intent(inout) :: process
type(sf_channel_t), dimension(:), intent(in) :: sf_channel
integer :: c
call process%beam_config%allocate_sf_channels (size (sf_channel))
do c = 1, size (sf_channel)
call process%beam_config%set_sf_channel (c, sf_channel(c))
end do
end subroutine process_set_sf_channel_array
@ %def process_init_sf_chain
@ %def process_set_sf_channel
@ Notify about the structure-function setup.
<<Process: process: TBP>>=
procedure :: sf_startup_message => process_sf_startup_message
<<Process: procedures>>=
subroutine process_sf_startup_message (process, sf_string, unit)
class(process_t), intent(in) :: process
type(string_t), intent(in) :: sf_string
integer, intent(in), optional :: unit
call process%beam_config%sf_startup_message (sf_string, unit)
end subroutine process_sf_startup_message
@ %def process_sf_startup_message
@ As soon as both the kinematics configuration and the
structure-function setup are complete, we match parameterizations
(channels) for both. The matching entries are (re)set in the
[[component]] phase-space configuration, while the structure-function
configuration is left intact.
<<Process: process: TBP>>=
procedure :: collect_channels => process_collect_channels
<<Process: procedures>>=
subroutine process_collect_channels (process, coll)
class(process_t), intent(inout) :: process
type(phs_channel_collection_t), intent(inout) :: coll
integer :: i
do i = 1, process%meta%n_components
associate (component => process%component(i))
if (component%active) &
call component%collect_channels (coll)
end associate
end do
end subroutine process_collect_channels
@ %def process_collect_channels
@ Independently, we should be able to check if any component does not
contain phase-space parameters. Such a process can only be integrated
if there are structure functions.
<<Process: process: TBP>>=
procedure :: contains_trivial_component => process_contains_trivial_component
<<Process: procedures>>=
function process_contains_trivial_component (process) result (flag)
class(process_t), intent(in) :: process
logical :: flag
integer :: i
flag = .true.
do i = 1, process%meta%n_components
associate (component => process%component(i))
if (component%active) then
if (component%get_n_phs_par () == 0) return
end if
end associate
end do
flag = .false.
end function process_contains_trivial_component
@ %def process_contains_trivial_component
@
<<Process: process: TBP>>=
procedure :: get_master_component => process_get_master_component
<<Process: procedures>>=
function process_get_master_component (process, i_mci) result (i_component)
integer :: i_component
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
integer :: i
i_component = 0
do i = 1, size (process%component)
if (process%component(i)%i_mci == i_mci) then
i_component = i
return
end if
end do
end function process_get_master_component
@ %def process_get_master_component
@ Determine the MC parameter set structure and the MCI configuration for each
process component. We need data from the structure-function and phase-space
setup, so those should be complete before this is called. We also
make a random-number generator instance for each MCI group.
<<Process: process: TBP>>=
procedure :: setup_mci => process_setup_mci
<<Process: procedures>>=
subroutine process_setup_mci (process, dispatch_mci)
class(process_t), intent(inout) :: process
procedure(dispatch_mci_proc) :: dispatch_mci
class(mci_t), allocatable :: mci_template
integer :: i, i_mci
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, "process_setup_mci")
associate (pcm => process%pcm)
call pcm%call_dispatch_mci (dispatch_mci, &
process%get_var_list_ptr (), process%meta%id, mci_template)
call pcm%setup_mci (process%mci_entry)
process%config%n_mci = pcm%n_mci
process%component(:)%i_mci = pcm%i_mci(:)
do i = 1, pcm%n_components
i_mci = process%pcm%i_mci(i)
if (i_mci > 0) then
associate (component => process%component(i), &
mci_entry => process%mci_entry(i_mci))
call mci_entry%configure (mci_template, &
process%meta%type, &
i_mci, i, component, process%beam_config%n_sfpar, &
process%rng_factory)
call mci_entry%set_parameters (process%get_var_list_ptr ())
end associate
end if
end do
end associate
end subroutine process_setup_mci
@ %def process_setup_mci
@ Set cuts. This is a parse node, namely the right-hand side of the [[cut]]
assignment. When creating an instance, we compile this into an evaluation
tree. The parse node may be null.
<<Process: process: TBP>>=
procedure :: set_cuts => process_set_cuts
<<Process: procedures>>=
subroutine process_set_cuts (process, ef_cuts)
class(process_t), intent(inout) :: process
class(expr_factory_t), intent(in) :: ef_cuts
allocate (process%config%ef_cuts, source = ef_cuts)
end subroutine process_set_cuts
@ %def process_set_cuts
@ Analogously for the other expressions.
<<Process: process: TBP>>=
procedure :: set_scale => process_set_scale
procedure :: set_fac_scale => process_set_fac_scale
procedure :: set_ren_scale => process_set_ren_scale
procedure :: set_weight => process_set_weight
<<Process: procedures>>=
subroutine process_set_scale (process, ef_scale)
class(process_t), intent(inout) :: process
class(expr_factory_t), intent(in) :: ef_scale
allocate (process%config%ef_scale, source = ef_scale)
end subroutine process_set_scale
subroutine process_set_fac_scale (process, ef_fac_scale)
class(process_t), intent(inout) :: process
class(expr_factory_t), intent(in) :: ef_fac_scale
allocate (process%config%ef_fac_scale, source = ef_fac_scale)
end subroutine process_set_fac_scale
subroutine process_set_ren_scale (process, ef_ren_scale)
class(process_t), intent(inout) :: process
class(expr_factory_t), intent(in) :: ef_ren_scale
allocate (process%config%ef_ren_scale, source = ef_ren_scale)
end subroutine process_set_ren_scale
subroutine process_set_weight (process, ef_weight)
class(process_t), intent(inout) :: process
class(expr_factory_t), intent(in) :: ef_weight
allocate (process%config%ef_weight, source = ef_weight)
end subroutine process_set_weight
@ %def process_set_scale
@ %def process_set_fac_scale
@ %def process_set_ren_scale
@ %def process_set_weight
@
\subsubsection{MD5 sum}
The MD5 sum of the process object should reflect the state completely,
including integration results. It is used for checking the integrity
of event files. This global checksum includes checksums for the
various parts. In particular, the MCI object receives a checksum that
includes the configuration of all configuration parts relevant for an
individual integration. This checksum is used for checking the
integrity of integration grids.
We do not need MD5 sums for the process terms, since these are
generated from the component definitions.
<<Process: process: TBP>>=
procedure :: compute_md5sum => process_compute_md5sum
<<Process: procedures>>=
subroutine process_compute_md5sum (process)
class(process_t), intent(inout) :: process
integer :: i
call process%config%compute_md5sum ()
do i = 1, process%config%n_components
associate (component => process%component(i))
if (component%active) then
call component%compute_md5sum ()
end if
end associate
end do
call process%beam_config%compute_md5sum ()
do i = 1, process%config%n_mci
call process%mci_entry(i)%compute_md5sum &
(process%config, process%component, process%beam_config)
end do
end subroutine process_compute_md5sum
@ %def process_compute_md5sum
@
<<Process: process: TBP>>=
procedure :: sampler_test => process_sampler_test
<<Process: procedures>>=
subroutine process_sampler_test (process, sampler, n_calls, i_mci)
class(process_t), intent(inout) :: process
class(mci_sampler_t), intent(inout) :: sampler
integer, intent(in) :: n_calls, i_mci
call process%mci_entry(i_mci)%sampler_test (sampler, n_calls)
end subroutine process_sampler_test
@ %def process_sampler_test
@ The finalizer should be called after all integration passes have been
completed. It will, for instance, write a summary of the integration
results.
[[integrate_dummy]] does a ``dummy'' integration in the sense that
nothing is done but just empty integration results appended.
<<Process: process: TBP>>=
procedure :: final_integration => process_final_integration
procedure :: integrate_dummy => process_integrate_dummy
<<Process: procedures>>=
subroutine process_final_integration (process, i_mci)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
call process%mci_entry(i_mci)%final_integration ()
end subroutine process_final_integration
subroutine process_integrate_dummy (process)
class(process_t), intent(inout) :: process
type(integration_results_t) :: results
integer :: u_log
u_log = logfile_unit ()
call results%init (process%meta%type)
call results%display_init (screen = .true., unit = u_log)
call results%new_pass ()
call results%record (1, 0, 0._default, 0._default, 0._default)
call results%display_final ()
end subroutine process_integrate_dummy
@ %def process_final_integration
@ %def process_integrate_dummy
@
<<Process: process: TBP>>=
procedure :: integrate => process_integrate
<<Process: procedures>>=
subroutine process_integrate (process, i_mci, mci_work, &
mci_sampler, n_it, n_calls, adapt_grids, adapt_weights, final, &
pacify, nlo_type)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
type(mci_work_t), intent(inout) :: mci_work
class(mci_sampler_t), intent(inout) :: mci_sampler
integer, intent(in) :: n_it, n_calls
logical, intent(in), optional :: adapt_grids, adapt_weights
logical, intent(in), optional :: final
logical, intent(in), optional :: pacify
integer, intent(in), optional :: nlo_type
associate (mci_entry => process%mci_entry(i_mci))
call mci_entry%integrate (mci_work%mci, mci_sampler, n_it, n_calls, &
adapt_grids, adapt_weights, final, pacify, &
nlo_type = nlo_type)
call mci_entry%results%display_pass (pacify)
end associate
end subroutine process_integrate
@ %def process_integrate
@
<<Process: process: TBP>>=
procedure :: generate_weighted_event => process_generate_weighted_event
<<Process: procedures>>=
subroutine process_generate_weighted_event (process, i_mci, mci_work, &
mci_sampler, keep_failed_events)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
type(mci_work_t), intent(inout) :: mci_work
class(mci_sampler_t), intent(inout) :: mci_sampler
logical, intent(in) :: keep_failed_events
associate (mci_entry => process%mci_entry(i_mci))
call mci_entry%generate_weighted_event (mci_work%mci, &
mci_sampler, keep_failed_events)
end associate
end subroutine process_generate_weighted_event
@ %def process_generate_weighted_event
<<Process: process: TBP>>=
procedure :: generate_unweighted_event => process_generate_unweighted_event
<<Process: procedures>>=
subroutine process_generate_unweighted_event (process, i_mci, &
mci_work, mci_sampler)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
type(mci_work_t), intent(inout) :: mci_work
class(mci_sampler_t), intent(inout) :: mci_sampler
associate (mci_entry => process%mci_entry(i_mci))
call mci_entry%generate_unweighted_event &
(mci_work%mci, mci_sampler)
end associate
end subroutine process_generate_unweighted_event
@ %def process_generate_unweighted_event
@ Display the final results for the sum of all components. (This is useful,
obviously, only if there is more than one component.)
<<Process: process: TBP>>=
procedure :: display_summed_results => process_display_summed_results
<<Process: procedures>>=
subroutine process_display_summed_results (process, pacify)
class(process_t), intent(inout) :: process
logical, intent(in) :: pacify
type(integration_results_t) :: results
integer :: u_log
u_log = logfile_unit ()
call results%init (process%meta%type)
call results%display_init (screen = .true., unit = u_log)
call results%new_pass ()
call results%record (1, 0, &
process%get_integral (), &
process%get_error (), &
process%get_efficiency (), suppress = pacify)
select type (pcm => process%pcm)
class is (pcm_nlo_t)
!!! Check that Born integral is there
if (process%component_can_be_integrated (1)) then
call results%record_correction (process%get_correction (), &
process%get_correction_error ())
end if
end select
call results%display_final ()
end subroutine process_display_summed_results
@ %def process_display_summed_results
@ Run LaTeX/Metapost to generate a ps/pdf file for the integration
history. We (re)write the driver file -- just in case it has been
missed before -- then we compile it.
<<Process: process: TBP>>=
procedure :: display_integration_history => &
process_display_integration_history
<<Process: procedures>>=
subroutine process_display_integration_history &
(process, i_mci, filename, os_data, eff_reset)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
type(string_t), intent(in) :: filename
type(os_data_t), intent(in) :: os_data
logical, intent(in), optional :: eff_reset
call integration_results_write_driver &
(process%mci_entry(i_mci)%results, filename, eff_reset)
call integration_results_compile_driver &
(process%mci_entry(i_mci)%results, filename, os_data)
end subroutine process_display_integration_history
@ %def subroutine process_display_integration_history
@ Write a complete logfile (with hardcoded name based on the process ID).
We do not write internal data.
<<Process: process: TBP>>=
procedure :: write_logfile => process_write_logfile
<<Process: procedures>>=
subroutine process_write_logfile (process, i_mci, filename)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
type(string_t), intent(in) :: filename
type(time_t) :: time
integer :: unit, u
unit = free_unit ()
open (unit = unit, file = char (filename), action = "write", &
status = "replace")
u = given_output_unit (unit)
write (u, "(A)") repeat ("#", 79)
call process%meta%write (u, .false.)
write (u, "(A)") repeat ("#", 79)
write (u, "(3x,A,ES17.10)") "Integral = ", &
process%mci_entry(i_mci)%get_integral ()
write (u, "(3x,A,ES17.10)") "Error = ", &
process%mci_entry(i_mci)%get_error ()
write (u, "(3x,A,ES17.10)") "Accuracy = ", &
process%mci_entry(i_mci)%get_accuracy ()
write (u, "(3x,A,ES17.10)") "Chi2 = ", &
process%mci_entry(i_mci)%get_chi2 ()
write (u, "(3x,A,ES17.10)") "Efficiency = ", &
process%mci_entry(i_mci)%get_efficiency ()
call process%mci_entry(i_mci)%get_time (time, 10000)
if (time%is_known ()) then
write (u, "(3x,A,1x,A)") "T(10k evt) = ", char (time%to_string_dhms ())
else
write (u, "(3x,A)") "T(10k evt) = [undefined]"
end if
call process%mci_entry(i_mci)%results%write (u)
write (u, "(A)") repeat ("#", 79)
call process%mci_entry(i_mci)%results%write_chain_weights (u)
write (u, "(A)") repeat ("#", 79)
call process%mci_entry(i_mci)%counter%write (u)
write (u, "(A)") repeat ("#", 79)
call process%mci_entry(i_mci)%mci%write_log_entry (u)
write (u, "(A)") repeat ("#", 79)
call process%beam_config%data%write (u)
write (u, "(A)") repeat ("#", 79)
if (allocated (process%config%ef_cuts)) then
write (u, "(3x,A)") "Cut expression:"
call process%config%ef_cuts%write (u)
else
write (u, "(3x,A)") "No cuts used."
end if
call write_separator (u)
if (allocated (process%config%ef_scale)) then
write (u, "(3x,A)") "Scale expression:"
call process%config%ef_scale%write (u)
else
write (u, "(3x,A)") "No scale expression was given."
end if
call write_separator (u)
if (allocated (process%config%ef_fac_scale)) then
write (u, "(3x,A)") "Factorization scale expression:"
call process%config%ef_fac_scale%write (u)
else
write (u, "(3x,A)") "No factorization scale expression was given."
end if
call write_separator (u)
if (allocated (process%config%ef_ren_scale)) then
write (u, "(3x,A)") "Renormalization scale expression:"
call process%config%ef_ren_scale%write (u)
else
write (u, "(3x,A)") "No renormalization scale expression was given."
end if
call write_separator (u)
if (allocated (process%config%ef_weight)) then
call write_separator (u)
write (u, "(3x,A)") "Weight expression:"
call process%config%ef_weight%write (u)
else
write (u, "(3x,A)") "No weight expression was given."
end if
write (u, "(A)") repeat ("#", 79)
write (u, "(1x,A)") "Summary of quantum-number states:"
write (u, "(1x,A)") " + sign: allowed and contributing"
write (u, "(1x,A)") " no + : switched off at runtime"
call process%write_state_summary (u)
write (u, "(A)") repeat ("#", 79)
call process%env%write (u, show_var_list=.true., &
show_model=.false., show_lib=.false., show_os_data=.false.)
write (u, "(A)") repeat ("#", 79)
close (u)
end subroutine process_write_logfile
@ %def process_write_logfile
@ Display the quantum-number combinations of the process components, and their
current status (allowed or switched off).
<<Process: process: TBP>>=
procedure :: write_state_summary => process_write_state_summary
<<Process: procedures>>=
subroutine process_write_state_summary (process, unit)
class(process_t), intent(in) :: process
integer, intent(in), optional :: unit
integer :: i, i_component, u
u = given_output_unit (unit)
do i = 1, size (process%term)
call write_separator (u)
i_component = process%term(i)%i_component
if (i_component /= 0) then
call process%term(i)%write_state_summary &
(process%get_core_term(i), unit)
end if
end do
end subroutine process_write_state_summary
@ %def process_write_state_summary
@ Prepare event generation for the specified MCI entry. This implies, in
particular, checking the phase-space file.
<<Process: process: TBP>>=
procedure :: prepare_simulation => process_prepare_simulation
<<Process: procedures>>=
subroutine process_prepare_simulation (process, i_mci)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
call process%mci_entry(i_mci)%prepare_simulation ()
end subroutine process_prepare_simulation
@ %def process_prepare_simulation
@
\subsubsection{Retrieve process data}
Tell whether integral (and error) are known.
<<Process: process: TBP>>=
generic :: has_integral => has_integral_tot, has_integral_mci
procedure :: has_integral_tot => process_has_integral_tot
procedure :: has_integral_mci => process_has_integral_mci
<<Process: procedures>>=
function process_has_integral_mci (process, i_mci) result (flag)
logical :: flag
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
if (allocated (process%mci_entry)) then
flag = process%mci_entry(i_mci)%has_integral ()
else
flag = .false.
end if
end function process_has_integral_mci
function process_has_integral_tot (process) result (flag)
logical :: flag
class(process_t), intent(in) :: process
integer :: i, j, i_component
if (allocated (process%mci_entry)) then
flag = .true.
do i = 1, size (process%mci_entry)
do j = 1, size (process%mci_entry(i)%i_component)
i_component = process%mci_entry(i)%i_component(j)
if (process%component_can_be_integrated (i_component)) &
flag = flag .and. process%mci_entry(i)%has_integral ()
end do
end do
else
flag = .false.
end if
end function process_has_integral_tot
@ %def process_has_integral
@
Return the current integral and error obtained by the integrator [[i_mci]].
<<Process: process: TBP>>=
generic :: get_integral => get_integral_tot, get_integral_mci
generic :: get_error => get_error_tot, get_error_mci
generic :: get_efficiency => get_efficiency_tot, get_efficiency_mci
procedure :: get_integral_tot => process_get_integral_tot
procedure :: get_integral_mci => process_get_integral_mci
procedure :: get_error_tot => process_get_error_tot
procedure :: get_error_mci => process_get_error_mci
procedure :: get_efficiency_tot => process_get_efficiency_tot
procedure :: get_efficiency_mci => process_get_efficiency_mci
<<Process: procedures>>=
function process_get_integral_mci (process, i_mci) result (integral)
real(default) :: integral
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
integral = process%mci_entry(i_mci)%get_integral ()
end function process_get_integral_mci
function process_get_error_mci (process, i_mci) result (error)
real(default) :: error
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
error = process%mci_entry(i_mci)%get_error ()
end function process_get_error_mci
function process_get_efficiency_mci (process, i_mci) result (efficiency)
real(default) :: efficiency
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
efficiency = process%mci_entry(i_mci)%get_efficiency ()
end function process_get_efficiency_mci
function process_get_integral_tot (process) result (integral)
real(default) :: integral
class(process_t), intent(in) :: process
integer :: i, j, i_component
integral = zero
if (allocated (process%mci_entry)) then
do i = 1, size (process%mci_entry)
do j = 1, size (process%mci_entry(i)%i_component)
i_component = process%mci_entry(i)%i_component(j)
if (process%component_can_be_integrated(i_component)) &
integral = integral + process%mci_entry(i)%get_integral ()
end do
end do
end if
end function process_get_integral_tot
function process_get_error_tot (process) result (error)
real(default) :: variance
class(process_t), intent(in) :: process
real(default) :: error
integer :: i, j, i_component
variance = zero
if (allocated (process%mci_entry)) then
do i = 1, size (process%mci_entry)
do j = 1, size (process%mci_entry(i)%i_component)
i_component = process%mci_entry(i)%i_component(j)
if (process%component_can_be_integrated(i_component)) &
variance = variance + process%mci_entry(i)%get_error () ** 2
end do
end do
end if
error = sqrt (variance)
end function process_get_error_tot
function process_get_efficiency_tot (process) result (efficiency)
real(default) :: efficiency
class(process_t), intent(in) :: process
real(default) :: den, eff, int
integer :: i, j, i_component
den = zero
if (allocated (process%mci_entry)) then
do i = 1, size (process%mci_entry)
do j = 1, size (process%mci_entry(i)%i_component)
i_component = process%mci_entry(i)%i_component(j)
if (process%component_can_be_integrated(i_component)) then
int = process%get_integral (i)
if (int > 0) then
eff = process%mci_entry(i)%get_efficiency ()
if (eff > 0) then
den = den + int / eff
else
efficiency = 0
return
end if
end if
end if
end do
end do
end if
if (den > 0) then
efficiency = process%get_integral () / den
else
efficiency = 0
end if
end function process_get_efficiency_tot
@ %def process_get_integral process_get_efficiency
@ Let us call the ratio of the LO and the NLO result $\iota = I_{LO} / I_{NLO}$. Then
usual error propagation gives
\begin{equation*}
\sigma_{\iota}^2 = \left(\frac{\partial \iota}{\partial I_{LO}}\right)^2 \sigma_{I_{LO}}^2
+ \left(\frac{\partial \iota}{\partial I_{NLO}}\right)^2 \sigma_{I_{NLO}}^2
= \frac{I_{NLO}^2\sigma_{I_{LO}}^2}{I_{LO}^4} + \frac{\sigma_{I_{NLO}}^2}{I_{LO}^2}.
\end{equation*}
<<Process: process: TBP>>=
procedure :: get_correction => process_get_correction
procedure :: get_correction_error => process_get_correction_error
<<Process: procedures>>=
function process_get_correction (process) result (ratio)
real(default) :: ratio
class(process_t), intent(in) :: process
integer :: i_mci
real(default) :: int_born, int_nlo
int_nlo = zero
int_born = process%mci_entry(1)%get_integral ()
do i_mci = 2, size (process%mci_entry)
if (process%component_can_be_integrated (i_mci)) &
int_nlo = int_nlo + process%mci_entry(i_mci)%get_integral ()
end do
ratio = int_nlo / int_born * 100
end function process_get_correction
function process_get_correction_error (process) result (error)
real(default) :: error
class(process_t), intent(in) :: process
real(default) :: int_born, sum_int_nlo
real(default) :: err_born, err2
integer :: i_mci
sum_int_nlo = zero; err2 = zero
int_born = process%mci_entry(1)%get_integral ()
err_born = process%mci_entry(1)%get_error ()
do i_mci = 2, size (process%mci_entry)
if (process%component_can_be_integrated (i_mci)) then
sum_int_nlo = sum_int_nlo + process%mci_entry(i_mci)%get_integral ()
err2 = err2 + process%mci_entry(i_mci)%get_error()**2
end if
end do
error = sqrt (err2 / int_born**2 + sum_int_nlo**2 * err_born**2 / int_born**4) * 100
end function process_get_correction_error
@ %def process_get_correction process_get_correction_error
@
<<Process: process: TBP>>=
procedure :: lab_is_cm_frame => process_lab_is_cm_frame
<<Process: procedures>>=
pure function process_lab_is_cm_frame (process) result (cm_frame)
logical :: cm_frame
class(process_t), intent(in) :: process
cm_frame = process%beam_config%lab_is_cm_frame
end function process_lab_is_cm_frame
@ %def process_lab_is_cm_frame
@
<<Process: process: TBP>>=
procedure :: get_component_ptr => process_get_component_ptr
<<Process: procedures>>=
function process_get_component_ptr (process, i) result (component)
type(process_component_t), pointer :: component
class(process_t), intent(in), target :: process
integer, intent(in) :: i
component => process%component(i)
end function process_get_component_ptr
@ %def process_get_component_ptr
@
<<Process: process: TBP>>=
procedure :: get_qcd => process_get_qcd
<<Process: procedures>>=
function process_get_qcd (process) result (qcd)
type(qcd_t) :: qcd
class(process_t), intent(in) :: process
qcd = process%config%get_qcd ()
end function process_get_qcd
@ %def process_get_qcd
@
<<Process: process: TBP>>=
generic :: get_component_type => get_component_type_single
procedure :: get_component_type_single => process_get_component_type_single
<<Process: procedures>>=
elemental function process_get_component_type_single &
(process, i_component) result (comp_type)
integer :: comp_type
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
comp_type = process%component(i_component)%component_type
end function process_get_component_type_single
@ %def process_get_component_type_single
@
<<Process: process: TBP>>=
generic :: get_component_type => get_component_type_all
procedure :: get_component_type_all => process_get_component_type_all
<<Process: procedures>>=
function process_get_component_type_all &
(process) result (comp_type)
integer, dimension(:), allocatable :: comp_type
class(process_t), intent(in) :: process
allocate (comp_type (size (process%component)))
comp_type = process%component%component_type
end function process_get_component_type_all
@ %def process_get_component_type_all
@
<<Process: process: TBP>>=
procedure :: get_component_i_terms => process_get_component_i_terms
<<Process: procedures>>=
function process_get_component_i_terms (process, i_component) result (i_term)
integer, dimension(:), allocatable :: i_term
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
allocate (i_term (size (process%component(i_component)%i_term)))
i_term = process%component(i_component)%i_term
end function process_get_component_i_terms
@ %def process_get_component_i_terms
@
<<Process: process: TBP>>=
procedure :: get_n_allowed_born => process_get_n_allowed_born
<<Process: procedures>>=
function process_get_n_allowed_born (process, i_born) result (n_born)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_born
integer :: n_born
n_born = process%term(i_born)%n_allowed
end function process_get_n_allowed_born
@ %def process_get_n_allowed_born
@ Workaround getter. Would be better to remove this.
<<Process: process: TBP>>=
procedure :: get_pcm_ptr => process_get_pcm_ptr
<<Process: procedures>>=
function process_get_pcm_ptr (process) result (pcm)
class(pcm_t), pointer :: pcm
class(process_t), intent(in), target :: process
pcm => process%pcm
end function process_get_pcm_ptr
@ %def process_get_pcm_ptr
<<Process: process: TBP>>=
generic :: component_can_be_integrated => component_can_be_integrated_single
generic :: component_can_be_integrated => component_can_be_integrated_all
procedure :: component_can_be_integrated_single => process_component_can_be_integrated_single
<<Process: procedures>>=
function process_component_can_be_integrated_single (process, i_component) &
result (active)
logical :: active
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
logical :: combined_integration
select type (pcm => process%pcm)
type is (pcm_nlo_t)
combined_integration = pcm%settings%combined_integration
class default
combined_integration = .false.
end select
associate (component => process%component(i_component))
active = component%can_be_integrated ()
if (combined_integration) &
active = active .and. component%component_type <= COMP_MASTER
end associate
end function process_component_can_be_integrated_single
@ %def process_component_can_be_integrated_single
@
<<Process: process: TBP>>=
procedure :: component_can_be_integrated_all => process_component_can_be_integrated_all
<<Process: procedures>>=
function process_component_can_be_integrated_all (process) result (val)
logical, dimension(:), allocatable :: val
class(process_t), intent(in) :: process
integer :: i
allocate (val (size (process%component)))
do i = 1, size (process%component)
val(i) = process%component_can_be_integrated (i)
end do
end function process_component_can_be_integrated_all
@ %def process_component_can_be_integrated_all
@
<<Process: process: TBP>>=
procedure :: reset_selected_cores => process_reset_selected_cores
<<Process: procedures>>=
pure subroutine process_reset_selected_cores (process)
class(process_t), intent(inout) :: process
process%pcm%component_selected = .false.
end subroutine process_reset_selected_cores
@ %def process_reset_selected_cores
@
<<Process: process: TBP>>=
procedure :: select_components => process_select_components
<<Process: procedures>>=
pure subroutine process_select_components (process, indices)
class(process_t), intent(inout) :: process
integer, dimension(:), intent(in) :: indices
associate (pcm => process%pcm)
pcm%component_selected(indices) = .true.
end associate
end subroutine process_select_components
@ %def process_select_components
@
<<Process: process: TBP>>=
procedure :: component_is_selected => process_component_is_selected
<<Process: procedures>>=
pure function process_component_is_selected (process, index) result (val)
logical :: val
class(process_t), intent(in) :: process
integer, intent(in) :: index
associate (pcm => process%pcm)
val = pcm%component_selected(index)
end associate
end function process_component_is_selected
@ %def process_component_is_selected
@
<<Process: process: TBP>>=
procedure :: get_coupling_powers => process_get_coupling_powers
<<Process: procedures>>=
pure subroutine process_get_coupling_powers (process, alpha_power, alphas_power)
class(process_t), intent(in) :: process
integer, intent(out) :: alpha_power, alphas_power
call process%component(1)%config%get_coupling_powers (alpha_power, alphas_power)
end subroutine process_get_coupling_powers
@ %def process_get_coupling_powers
@
<<Process: process: TBP>>=
procedure :: get_real_component => process_get_real_component
<<Process: procedures>>=
function process_get_real_component (process) result (i_real)
integer :: i_real
class(process_t), intent(in) :: process
integer :: i_component
type(process_component_def_t), pointer :: config => null ()
i_real = 0
do i_component = 1, size (process%component)
config => process%get_component_def_ptr (i_component)
if (config%get_nlo_type () == NLO_REAL) then
i_real = i_component
exit
end if
end do
end function process_get_real_component
@ %def process_get_real_component
@
<<Process: process: TBP>>=
procedure :: extract_active_component_mci => process_extract_active_component_mci
<<Process: procedures>>=
function process_extract_active_component_mci (process) result (i_active)
integer :: i_active
class(process_t), intent(in) :: process
integer :: i_mci, j, i_component, n_active
call count_n_active ()
if (n_active /= 1) i_active = 0
contains
subroutine count_n_active ()
n_active = 0
do i_mci = 1, size (process%mci_entry)
associate (mci_entry => process%mci_entry(i_mci))
do j = 1, size (mci_entry%i_component)
i_component = mci_entry%i_component(j)
associate (component => process%component (i_component))
if (component%can_be_integrated ()) then
i_active = i_mci
n_active = n_active + 1
end if
end associate
end do
end associate
end do
end subroutine count_n_active
end function process_extract_active_component_mci
@ %def process_extract_active_component_mci
@
<<Process: process: TBP>>=
procedure :: uses_real_partition => process_uses_real_partition
<<Process: procedures>>=
function process_uses_real_partition (process) result (val)
logical :: val
class(process_t), intent(in) :: process
val = any (process%mci_entry%real_partition_type /= REAL_FULL)
end function process_uses_real_partition
@ %def process_uses_real_partition
@ Return the MD5 sums that summarize the process component
definitions. These values should be independent of parameters, beam
details, expressions, etc. They can be used for checking the
integrity of a process when reusing an old event file.
<<Process: process: TBP>>=
procedure :: get_md5sum_prc => process_get_md5sum_prc
<<Process: procedures>>=
function process_get_md5sum_prc (process, i_component) result (md5sum)
character(32) :: md5sum
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
if (process%component(i_component)%active) then
md5sum = process%component(i_component)%config%get_md5sum ()
else
md5sum = ""
end if
end function process_get_md5sum_prc
@ %def process_get_md5sum_prc
@ Return the MD5 sums that summarize the state of the MCI integrators.
These values should encode all process data, integration and phase
space configuration, etc., and the integration results. They can thus
be used for checking the integrity of an event-generation setup when
reusing an old event file.
<<Process: process: TBP>>=
procedure :: get_md5sum_mci => process_get_md5sum_mci
<<Process: procedures>>=
function process_get_md5sum_mci (process, i_mci) result (md5sum)
character(32) :: md5sum
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
md5sum = process%mci_entry(i_mci)%get_md5sum ()
end function process_get_md5sum_mci
@ %def process_get_md5sum_mci
@ Return the MD5 sum of the process configuration. This should encode
the process setup, data, and expressions, but no integration results.
<<Process: process: TBP>>=
procedure :: get_md5sum_cfg => process_get_md5sum_cfg
<<Process: procedures>>=
function process_get_md5sum_cfg (process) result (md5sum)
character(32) :: md5sum
class(process_t), intent(in) :: process
md5sum = process%config%md5sum
end function process_get_md5sum_cfg
@ %def process_get_md5sum_cfg
@
<<Process: process: TBP>>=
procedure :: get_n_cores => process_get_n_cores
<<Process: procedures>>=
function process_get_n_cores (process) result (n)
integer :: n
class(process_t), intent(in) :: process
n = process%pcm%n_cores
end function process_get_n_cores
@ %def process_get_n_cores
@
<<Process: process: TBP>>=
procedure :: get_base_i_term => process_get_base_i_term
<<Process: procedures>>=
function process_get_base_i_term (process, i_component) result (i_term)
integer :: i_term
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
i_term = process%component(i_component)%i_term(1)
end function process_get_base_i_term
@ %def process_get_base_i_term
@
<<Process: process: TBP>>=
procedure :: get_core_term => process_get_core_term
<<Process: procedures>>=
function process_get_core_term (process, i_term) result (core)
class(prc_core_t), pointer :: core
class(process_t), intent(in), target :: process
integer, intent(in) :: i_term
integer :: i_core
i_core = process%term(i_term)%i_core
core => process%core_entry(i_core)%get_core_ptr ()
end function process_get_core_term
@ %def process_get_core_term
@
<<Process: process: TBP>>=
procedure :: get_core_ptr => process_get_core_ptr
<<Process: procedures>>=
function process_get_core_ptr (process, i_core) result (core)
class(prc_core_t), pointer :: core
class(process_t), intent(in), target :: process
integer, intent(in) :: i_core
if (allocated (process%core_entry)) then
core => process%core_entry(i_core)%get_core_ptr ()
else
core => null ()
end if
end function process_get_core_ptr
@ %def process_get_core_ptr
@
<<Process: process: TBP>>=
procedure :: get_term_ptr => process_get_term_ptr
<<Process: procedures>>=
function process_get_term_ptr (process, i) result (term)
type(process_term_t), pointer :: term
class(process_t), intent(in), target :: process
integer, intent(in) :: i
term => process%term(i)
end function process_get_term_ptr
@ %def process_get_term_ptr
@
<<Process: process: TBP>>=
procedure :: get_i_term => process_get_i_term
<<Process: procedures>>=
function process_get_i_term (process, i_core) result (i_term)
integer :: i_term
class(process_t), intent(in) :: process
integer, intent(in) :: i_core
do i_term = 1, process%get_n_terms ()
if (process%term(i_term)%i_core == i_core) return
end do
i_term = -1
end function process_get_i_term
@ %def process_get_i_term
@
<<Process: process: TBP>>=
procedure :: set_i_mci_work => process_set_i_mci_work
<<Process: procedures>>=
subroutine process_set_i_mci_work (process, i_mci)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
process%mci_entry(i_mci)%i_mci = i_mci
end subroutine process_set_i_mci_work
@ %def process_set_i_mci_work
@
<<Process: process: TBP>>=
procedure :: get_i_mci_work => process_get_i_mci_work
<<Process: procedures>>=
pure function process_get_i_mci_work (process, i_mci) result (i_mci_work)
integer :: i_mci_work
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
i_mci_work = process%mci_entry(i_mci)%i_mci
end function process_get_i_mci_work
@ %def process_get_i_mci_work
@
<<Process: process: TBP>>=
procedure :: get_i_sub => process_get_i_sub
<<Process: procedures>>=
elemental function process_get_i_sub (process, i_term) result (i_sub)
integer :: i_sub
class(process_t), intent(in) :: process
integer, intent(in) :: i_term
i_sub = process%term(i_term)%i_sub
end function process_get_i_sub
@ %def process_get_i_sub
@
<<Process: process: TBP>>=
procedure :: get_i_term_virtual => process_get_i_term_virtual
<<Process: procedures>>=
elemental function process_get_i_term_virtual (process) result (i_term)
integer :: i_term
class(process_t), intent(in) :: process
integer :: i_component
i_term = 0
do i_component = 1, size (process%component)
if (process%component(i_component)%get_nlo_type () == NLO_VIRTUAL) &
i_term = process%component(i_component)%i_term(1)
end do
end function process_get_i_term_virtual
@ %def process_get_i_term_virtual
@
<<Process: process: TBP>>=
generic :: component_is_active => component_is_active_single
procedure :: component_is_active_single => process_component_is_active_single
<<Process: procedures>>=
elemental function process_component_is_active_single (process, i_comp) result (val)
logical :: val
class(process_t), intent(in) :: process
integer, intent(in) :: i_comp
val = process%component(i_comp)%is_active ()
end function process_component_is_active_single
@ %def process_component_is_active_single
@
<<Process: process: TBP>>=
generic :: component_is_active => component_is_active_all
procedure :: component_is_active_all => process_component_is_active_all
<<Process: procedures>>=
pure function process_component_is_active_all (process) result (val)
logical, dimension(:), allocatable :: val
class(process_t), intent(in) :: process
allocate (val (size (process%component)))
val = process%component%is_active ()
end function process_component_is_active_all
@ %def process_component_is_active_all
@
\subsection{Default iterations}
If the user does not specify the passes and iterations for
integration, we should be able to give reasonable defaults. These
depend on the process, therefore we implement the following procedures
as methods of the process object. The algorithm is not very
sophisticated yet, it may be improved by looking at the process in
more detail.
We investigate only the first process component, assuming that it
characterizes the complexity of the process reasonable well.
The number of passes is limited to two: one for adaption, one for
integration.
<<Process: process: TBP>>=
procedure :: get_n_pass_default => process_get_n_pass_default
procedure :: adapt_grids_default => process_adapt_grids_default
procedure :: adapt_weights_default => process_adapt_weights_default
<<Process: procedures>>=
function process_get_n_pass_default (process) result (n_pass)
class(process_t), intent(in) :: process
integer :: n_pass
integer :: n_eff
type(process_component_def_t), pointer :: config
config => process%component(1)%config
n_eff = config%get_n_tot () - 2
select case (n_eff)
case (1)
n_pass = 1
case default
n_pass = 2
end select
end function process_get_n_pass_default
function process_adapt_grids_default (process, pass) result (flag)
class(process_t), intent(in) :: process
integer, intent(in) :: pass
logical :: flag
integer :: n_eff
type(process_component_def_t), pointer :: config
config => process%component(1)%config
n_eff = config%get_n_tot () - 2
select case (n_eff)
case (1)
flag = .false.
case default
select case (pass)
case (1); flag = .true.
case (2); flag = .false.
case default
call msg_bug ("adapt grids default: impossible pass index")
end select
end select
end function process_adapt_grids_default
function process_adapt_weights_default (process, pass) result (flag)
class(process_t), intent(in) :: process
integer, intent(in) :: pass
logical :: flag
integer :: n_eff
type(process_component_def_t), pointer :: config
config => process%component(1)%config
n_eff = config%get_n_tot () - 2
select case (n_eff)
case (1)
flag = .false.
case default
select case (pass)
case (1); flag = .true.
case (2); flag = .false.
case default
call msg_bug ("adapt weights default: impossible pass index")
end select
end select
end function process_adapt_weights_default
@ %def process_get_n_pass_default
@ %def process_adapt_grids_default
@ %def process_adapt_weights_default
@ The number of iterations and calls per iteration depends on the
number of outgoing particles.
<<Process: process: TBP>>=
procedure :: get_n_it_default => process_get_n_it_default
procedure :: get_n_calls_default => process_get_n_calls_default
<<Process: procedures>>=
function process_get_n_it_default (process, pass) result (n_it)
class(process_t), intent(in) :: process
integer, intent(in) :: pass
integer :: n_it
integer :: n_eff
type(process_component_def_t), pointer :: config
config => process%component(1)%config
n_eff = config%get_n_tot () - 2
select case (pass)
case (1)
select case (n_eff)
case (1); n_it = 1
case (2); n_it = 3
case (3); n_it = 5
case (4:5); n_it = 10
case (6); n_it = 15
case (7:); n_it = 20
end select
case (2)
select case (n_eff)
case (:3); n_it = 3
case (4:); n_it = 5
end select
end select
end function process_get_n_it_default
function process_get_n_calls_default (process, pass) result (n_calls)
class(process_t), intent(in) :: process
integer, intent(in) :: pass
integer :: n_calls
integer :: n_eff
type(process_component_def_t), pointer :: config
config => process%component(1)%config
n_eff = config%get_n_tot () - 2
select case (pass)
case (1)
select case (n_eff)
case (1); n_calls = 100
case (2); n_calls = 1000
case (3); n_calls = 5000
case (4); n_calls = 10000
case (5); n_calls = 20000
case (6:); n_calls = 50000
end select
case (2)
select case (n_eff)
case (:3); n_calls = 10000
case (4); n_calls = 20000
case (5); n_calls = 50000
case (6); n_calls = 100000
case (7:); n_calls = 200000
end select
end select
end function process_get_n_calls_default
@ %def process_get_n_it_default
@ %def process_get_n_calls_default
@
\subsection{Constant process data}
Manually set the Run ID (unit test only).
<<Process: process: TBP>>=
procedure :: set_run_id => process_set_run_id
<<Process: procedures>>=
subroutine process_set_run_id (process, run_id)
class(process_t), intent(inout) :: process
type(string_t), intent(in) :: run_id
process%meta%run_id = run_id
end subroutine process_set_run_id
-
+
@ %def process_set_run_id
@
The following methods return basic process data that stay constant
after initialization.
The process and IDs.
<<Process: process: TBP>>=
procedure :: get_id => process_get_id
procedure :: get_num_id => process_get_num_id
procedure :: get_run_id => process_get_run_id
procedure :: get_library_name => process_get_library_name
<<Process: procedures>>=
function process_get_id (process) result (id)
class(process_t), intent(in) :: process
type(string_t) :: id
id = process%meta%id
end function process_get_id
function process_get_num_id (process) result (id)
class(process_t), intent(in) :: process
integer :: id
id = process%meta%num_id
end function process_get_num_id
function process_get_run_id (process) result (id)
class(process_t), intent(in) :: process
type(string_t) :: id
id = process%meta%run_id
end function process_get_run_id
function process_get_library_name (process) result (id)
class(process_t), intent(in) :: process
type(string_t) :: id
id = process%meta%lib_name
end function process_get_library_name
@ %def process_get_id process_get_num_id
@ %def process_get_run_id process_get_library_name
@ The number of incoming particles.
<<Process: process: TBP>>=
procedure :: get_n_in => process_get_n_in
<<Process: procedures>>=
function process_get_n_in (process) result (n)
class(process_t), intent(in) :: process
integer :: n
n = process%config%n_in
end function process_get_n_in
@ %def process_get_n_in
@ The number of MCI data sets.
<<Process: process: TBP>>=
procedure :: get_n_mci => process_get_n_mci
<<Process: procedures>>=
function process_get_n_mci (process) result (n)
class(process_t), intent(in) :: process
integer :: n
n = process%config%n_mci
end function process_get_n_mci
@ %def process_get_n_mci
@ The number of process components, total.
<<Process: process: TBP>>=
procedure :: get_n_components => process_get_n_components
<<Process: procedures>>=
function process_get_n_components (process) result (n)
class(process_t), intent(in) :: process
integer :: n
n = process%meta%n_components
end function process_get_n_components
@ %def process_get_n_components
@ The number of process terms, total.
<<Process: process: TBP>>=
procedure :: get_n_terms => process_get_n_terms
<<Process: procedures>>=
function process_get_n_terms (process) result (n)
class(process_t), intent(in) :: process
integer :: n
n = process%config%n_terms
end function process_get_n_terms
@ %def process_get_n_terms
@ Return the indices of the components that belong to a
specific MCI entry.
<<Process: process: TBP>>=
procedure :: get_i_component => process_get_i_component
<<Process: procedures>>=
subroutine process_get_i_component (process, i_mci, i_component)
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
integer, dimension(:), intent(out), allocatable :: i_component
associate (mci_entry => process%mci_entry(i_mci))
allocate (i_component (size (mci_entry%i_component)))
i_component = mci_entry%i_component
end associate
end subroutine process_get_i_component
@ %def process_get_i_component
@ Return the ID of a specific component.
<<Process: process: TBP>>=
procedure :: get_component_id => process_get_component_id
<<Process: procedures>>=
function process_get_component_id (process, i_component) result (id)
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
type(string_t) :: id
id = process%meta%component_id(i_component)
end function process_get_component_id
@ %def process_get_component_id
@ Return a pointer to the definition of a specific component.
<<Process: process: TBP>>=
procedure :: get_component_def_ptr => process_get_component_def_ptr
<<Process: procedures>>=
function process_get_component_def_ptr (process, i_component) result (ptr)
type(process_component_def_t), pointer :: ptr
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
ptr => process%config%process_def%get_component_def_ptr (i_component)
end function process_get_component_def_ptr
@ %def process_get_component_def_ptr
@ These procedures extract and restore (by transferring the
allocation) the process core. This is useful for changing process
parameters from outside this module.
<<Process: process: TBP>>=
procedure :: extract_core => process_extract_core
procedure :: restore_core => process_restore_core
<<Process: procedures>>=
subroutine process_extract_core (process, i_term, core)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_term
class(prc_core_t), intent(inout), allocatable :: core
integer :: i_core
i_core = process%term(i_term)%i_core
call move_alloc (from = process%core_entry(i_core)%core, to = core)
end subroutine process_extract_core
subroutine process_restore_core (process, i_term, core)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_term
class(prc_core_t), intent(inout), allocatable :: core
integer :: i_core
i_core = process%term(i_term)%i_core
call move_alloc (from = core, to = process%core_entry(i_core)%core)
end subroutine process_restore_core
@ %def process_extract_core
@ %def process_restore_core
@ The block of process constants.
<<Process: process: TBP>>=
procedure :: get_constants => process_get_constants
<<Process: procedures>>=
function process_get_constants (process, i_core) result (data)
type(process_constants_t) :: data
class(process_t), intent(in) :: process
integer, intent(in) :: i_core
data = process%core_entry(i_core)%core%data
end function process_get_constants
@ %def process_get_constants
@
<<Process: process: TBP>>=
procedure :: get_config => process_get_config
<<Process: procedures>>=
function process_get_config (process) result (config)
type(process_config_data_t) :: config
class(process_t), intent(in) :: process
config = process%config
end function process_get_config
@ %def process_get_config
@
Construct an MD5 sum for the constant data, including the NLO type.
For the NLO type [[NLO_MISMATCH]], we pretend that this was
[[NLO_SUBTRACTION]] instead.
TODO wk 2018: should not depend explicitly on NLO data.
<<Process: process: TBP>>=
procedure :: get_md5sum_constants => process_get_md5sum_constants
<<Process: procedures>>=
function process_get_md5sum_constants (process, i_component, &
type_string, nlo_type) result (this_md5sum)
character(32) :: this_md5sum
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
type(string_t), intent(in) :: type_string
integer, intent(in) :: nlo_type
type(process_constants_t) :: data
integer :: unit
call process%env%fill_process_constants (process%meta%id, i_component, data)
unit = data%fill_unit_for_md5sum (.false.)
write (unit, '(A)') char(type_string)
select case (nlo_type)
case (NLO_MISMATCH)
write (unit, '(I0)') NLO_SUBTRACTION
case default
write (unit, '(I0)') nlo_type
end select
rewind (unit)
this_md5sum = md5sum (unit)
close (unit)
end function process_get_md5sum_constants
@ %def process_get_md5sum_constants
@ Return the set of outgoing flavors that are associated with a particular
term. We deduce this from the effective interaction.
<<Process: process: TBP>>=
procedure :: get_term_flv_out => process_get_term_flv_out
<<Process: procedures>>=
subroutine process_get_term_flv_out (process, i_term, flv)
class(process_t), intent(in), target :: process
integer, intent(in) :: i_term
type(flavor_t), dimension(:,:), allocatable, intent(out) :: flv
type(interaction_t), pointer :: int
int => process%term(i_term)%int_eff
if (.not. associated (int)) int => process%term(i_term)%int
call interaction_get_flv_out (int, flv)
end subroutine process_get_term_flv_out
@ %def process_get_term_flv_out
@ Return true if there is any unstable particle in any of the process
terms. We decide this based on the provided model instance, not the
one that is stored in the process object.
<<Process: process: TBP>>=
procedure :: contains_unstable => process_contains_unstable
<<Process: procedures>>=
function process_contains_unstable (process, model) result (flag)
class(process_t), intent(in) :: process
class(model_data_t), intent(in), target :: model
logical :: flag
integer :: i_term
type(flavor_t), dimension(:,:), allocatable :: flv
flag = .false.
do i_term = 1, process%get_n_terms ()
call process%get_term_flv_out (i_term, flv)
call flv%set_model (model)
flag = .not. all (flv%is_stable ())
deallocate (flv)
if (flag) return
end do
end function process_contains_unstable
@ %def process_contains_unstable
@ The nominal process energy.
<<Process: process: TBP>>=
procedure :: get_sqrts => process_get_sqrts
<<Process: procedures>>=
function process_get_sqrts (process) result (sqrts)
class(process_t), intent(in) :: process
real(default) :: sqrts
sqrts = process%beam_config%data%get_sqrts ()
end function process_get_sqrts
@ %def process_get_sqrts
@ The beam polarization in case of simple degrees.
<<Process: process: TBP>>=
procedure :: get_polarization => process_get_polarization
<<Process: procedures>>=
function process_get_polarization (process) result (pol)
class(process_t), intent(in) :: process
real(default), dimension(2) :: pol
pol = process%beam_config%data%get_polarization ()
end function process_get_polarization
@ %def process_get_polarization
@
<<Process: process: TBP>>=
procedure :: get_meta => process_get_meta
<<Process: procedures>>=
function process_get_meta (process) result (meta)
type(process_metadata_t) :: meta
class(process_t), intent(in) :: process
meta = process%meta
end function process_get_meta
@ %def process_get_meta
<<Process: process: TBP>>=
procedure :: has_matrix_element => process_has_matrix_element
<<Process: procedures>>=
function process_has_matrix_element (process, i, is_term_index) result (active)
logical :: active
class(process_t), intent(in) :: process
integer, intent(in), optional :: i
logical, intent(in), optional :: is_term_index
integer :: i_component
logical :: is_term
is_term = .false.
if (present (i)) then
if (present (is_term_index)) is_term = is_term_index
if (is_term) then
i_component = process%term(i)%i_component
else
i_component = i
end if
active = process%component(i_component)%active
else
active = any (process%component%active)
end if
end function process_has_matrix_element
@ %def process_has_matrix_element
@ Pointer to the beam data object.
<<Process: process: TBP>>=
procedure :: get_beam_data_ptr => process_get_beam_data_ptr
<<Process: procedures>>=
function process_get_beam_data_ptr (process) result (beam_data)
class(process_t), intent(in), target :: process
type(beam_data_t), pointer :: beam_data
beam_data => process%beam_config%data
end function process_get_beam_data_ptr
@ %def process_get_beam_data_ptr
@
<<Process: process: TBP>>=
procedure :: get_beam_config => process_get_beam_config
<<Process: procedures>>=
function process_get_beam_config (process) result (beam_config)
type(process_beam_config_t) :: beam_config
class(process_t), intent(in) :: process
beam_config = process%beam_config
end function process_get_beam_config
@ %def process_get_beam_config
@
<<Process: process: TBP>>=
procedure :: get_beam_config_ptr => process_get_beam_config_ptr
<<Process: procedures>>=
function process_get_beam_config_ptr (process) result (beam_config)
type(process_beam_config_t), pointer :: beam_config
class(process_t), intent(in), target :: process
beam_config => process%beam_config
end function process_get_beam_config_ptr
@ %def process_get_beam_config_ptr
@ Return true if lab and c.m.\ frame coincide for this process.
<<Process: process: TBP>>=
procedure :: cm_frame => process_cm_frame
<<Process: procedures>>=
function process_cm_frame (process) result (flag)
class(process_t), intent(in), target :: process
logical :: flag
type(beam_data_t), pointer :: beam_data
beam_data => process%beam_config%data
flag = beam_data%cm_frame ()
end function process_cm_frame
@ %def process_cm_frame
@ Get the PDF set currently in use, if any.
<<Process: process: TBP>>=
procedure :: get_pdf_set => process_get_pdf_set
<<Process: procedures>>=
function process_get_pdf_set (process) result (pdf_set)
class(process_t), intent(in) :: process
integer :: pdf_set
pdf_set = process%beam_config%get_pdf_set ()
end function process_get_pdf_set
@ %def process_get_pdf_set
@
<<Process: process: TBP>>=
procedure :: pcm_contains_pdfs => process_pcm_contains_pdfs
<<Process: procedures>>=
function process_pcm_contains_pdfs (process) result (has_pdfs)
logical :: has_pdfs
class(process_t), intent(in) :: process
has_pdfs = process%pcm%has_pdfs
end function process_pcm_contains_pdfs
@ %def process_pcm_contains_pdfs
@ Get the beam spectrum file currently in use, if any.
<<Process: process: TBP>>=
procedure :: get_beam_file => process_get_beam_file
<<Process: procedures>>=
function process_get_beam_file (process) result (file)
class(process_t), intent(in) :: process
type(string_t) :: file
file = process%beam_config%get_beam_file ()
end function process_get_beam_file
@ %def process_get_beam_file
@ Pointer to the process variable list.
<<Process: process: TBP>>=
procedure :: get_var_list_ptr => process_get_var_list_ptr
<<Process: procedures>>=
function process_get_var_list_ptr (process) result (ptr)
class(process_t), intent(in), target :: process
type(var_list_t), pointer :: ptr
ptr => process%env%get_var_list_ptr ()
end function process_get_var_list_ptr
@ %def process_get_var_list_ptr
@ Pointer to the common model.
<<Process: process: TBP>>=
procedure :: get_model_ptr => process_get_model_ptr
<<Process: procedures>>=
function process_get_model_ptr (process) result (ptr)
class(process_t), intent(in) :: process
class(model_data_t), pointer :: ptr
ptr => process%config%model
end function process_get_model_ptr
@ %def process_get_model_ptr
@ Use the embedded RNG factory to spawn a new random-number generator
instance. (This modifies the state of the factory.)
<<Process: process: TBP>>=
procedure :: make_rng => process_make_rng
<<Process: procedures>>=
subroutine process_make_rng (process, rng)
class(process_t), intent(inout) :: process
class(rng_t), intent(out), allocatable :: rng
if (allocated (process%rng_factory)) then
call process%rng_factory%make (rng)
else
call msg_bug ("Process: make rng: factory not allocated")
end if
end subroutine process_make_rng
@ %def process_make_rng
@
\subsection{Compute an amplitude}
Each process variant should allow for computing an amplitude value
directly, without generating a process instance.
The process component is selected by the index [[i]]. The term within the
process component is selected by [[j]]. The momentum
combination is transferred as the array [[p]]. The function sets the specific
quantum state via the indices of a flavor [[f]], helicity [[h]], and color
[[c]] combination. Each index refers to the list of flavor, helicity, and
color states, respectively, as stored in the process data.
Optionally, we may set factorization and renormalization scale. If unset, the
partonic c.m.\ energy is inserted.
The function checks arguments for validity.
For invalid arguments (quantum states), we return zero.
<<Process: process: TBP>>=
procedure :: compute_amplitude => process_compute_amplitude
<<Process: procedures>>=
function process_compute_amplitude &
(process, i_core, i, j, p, f, h, c, fac_scale, ren_scale, alpha_qcd_forced) &
result (amp)
class(process_t), intent(in), target :: process
integer, intent(in) :: i_core
integer, intent(in) :: i, j
type(vector4_t), dimension(:), intent(in) :: p
integer, intent(in) :: f, h, c
real(default), intent(in), optional :: fac_scale, ren_scale
real(default), intent(in), allocatable, optional :: alpha_qcd_forced
real(default) :: fscale, rscale
real(default), allocatable :: aqcd_forced
complex(default) :: amp
class(prc_core_t), pointer :: core
amp = 0
if (0 < i .and. i <= process%meta%n_components) then
if (process%component(i)%active) then
associate (core => process%core_entry(i_core)%core)
associate (data => core%data)
if (size (p) == data%n_in + data%n_out &
.and. 0 < f .and. f <= data%n_flv &
.and. 0 < h .and. h <= data%n_hel &
.and. 0 < c .and. c <= data%n_col) then
if (present (fac_scale)) then
fscale = fac_scale
else
fscale = sum (p(data%n_in+1:)) ** 1
end if
if (present (ren_scale)) then
rscale = ren_scale
else
rscale = fscale
end if
if (present (alpha_qcd_forced)) then
if (allocated (alpha_qcd_forced)) &
allocate (aqcd_forced, source = alpha_qcd_forced)
end if
amp = core%compute_amplitude (j, p, f, h, c, &
fscale, rscale, aqcd_forced)
end if
end associate
end associate
else
amp = 0
end if
end if
end function process_compute_amplitude
@ %def process_compute_amplitude
@ Sanity check for the process library. We abort the program if it
has changed after process initialization.
<<Process: process: TBP>>=
procedure :: check_library_sanity => process_check_library_sanity
<<Process: procedures>>=
subroutine process_check_library_sanity (process)
class(process_t), intent(in) :: process
call process%env%check_lib_sanity (process%meta)
end subroutine process_check_library_sanity
@ %def process_check_library_sanity
@ Reset the association to a process library.
<<Process: process: TBP>>=
procedure :: reset_library_ptr => process_reset_library_ptr
<<Process: procedures>>=
subroutine process_reset_library_ptr (process)
class(process_t), intent(inout) :: process
call process%env%reset_lib_ptr ()
end subroutine process_reset_library_ptr
@ %def process_reset_library_ptr
@
<<Process: process: TBP>>=
procedure :: set_component_type => process_set_component_type
<<Process: procedures>>=
subroutine process_set_component_type (process, i_component, i_type)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_component, i_type
process%component(i_component)%component_type = i_type
end subroutine process_set_component_type
@ %def process_set_component_type
@
<<Process: process: TBP>>=
procedure :: set_counter_mci_entry => process_set_counter_mci_entry
<<Process: procedures>>=
subroutine process_set_counter_mci_entry (process, i_mci, counter)
class(process_t), intent(inout) :: process
integer, intent(in) :: i_mci
type(process_counter_t), intent(in) :: counter
process%mci_entry(i_mci)%counter = counter
end subroutine process_set_counter_mci_entry
@ %def process_set_counter_mci_entry
@ This is for suppression of numerical noise in the integration results
stored in the [[process_mci_entry]] type. As the error and efficiency
enter the MD5 sum, we recompute it.
<<Process: process: TBP>>=
procedure :: pacify => process_pacify
<<Process: procedures>>=
subroutine process_pacify (process, efficiency_reset, error_reset)
class(process_t), intent(inout) :: process
logical, intent(in), optional :: efficiency_reset, error_reset
logical :: eff_reset, err_reset
integer :: i
eff_reset = .false.
err_reset = .false.
if (present (efficiency_reset)) eff_reset = efficiency_reset
if (present (error_reset)) err_reset = error_reset
if (allocated (process%mci_entry)) then
do i = 1, size (process%mci_entry)
call process%mci_entry(i)%results%pacify (efficiency_reset)
if (allocated (process%mci_entry(i)%mci)) then
associate (mci => process%mci_entry(i)%mci)
if (process%mci_entry(i)%mci%error_known &
.and. err_reset) &
mci%error = 0
if (process%mci_entry(i)%mci%efficiency_known &
.and. eff_reset) &
mci%efficiency = 1
call mci%pacify (efficiency_reset, error_reset)
call mci%compute_md5sum ()
end associate
end if
end do
end if
end subroutine process_pacify
@ %def process_pacify
@ The following methods are used only in the unit tests; the access
process internals directly that would otherwise be hidden.
<<Process: process: TBP>>=
procedure :: test_allocate_sf_channels
procedure :: test_set_component_sf_channel
procedure :: test_get_mci_ptr
<<Process: procedures>>=
subroutine test_allocate_sf_channels (process, n)
class(process_t), intent(inout) :: process
integer, intent(in) :: n
call process%beam_config%allocate_sf_channels (n)
end subroutine test_allocate_sf_channels
subroutine test_set_component_sf_channel (process, c)
class(process_t), intent(inout) :: process
integer, dimension(:), intent(in) :: c
call process%component(1)%phs_config%set_sf_channel (c)
end subroutine test_set_component_sf_channel
subroutine test_get_mci_ptr (process, mci)
class(process_t), intent(in), target :: process
class(mci_t), intent(out), pointer :: mci
mci => process%mci_entry(1)%mci
end subroutine test_get_mci_ptr
@ %def test_allocate_sf_channels
@ %def test_set_component_sf_channel
@ %def test_get_mci_ptr
@
<<Process: process: TBP>>=
procedure :: init_mci_work => process_init_mci_work
<<Process: procedures>>=
subroutine process_init_mci_work (process, mci_work, i)
class(process_t), intent(in), target :: process
type(mci_work_t), intent(out) :: mci_work
integer, intent(in) :: i
call mci_work%init (process%mci_entry(i))
end subroutine process_init_mci_work
@ %def process_init_mci_work
@
Prepare the process core with type [[test_me]], or otherwise the externally
provided [[type_string]] version. The toy dispatchers as a procedure
argument come handy, knowing that we need to support only the [[test_me]] and
[[template]] matrix-element types.
<<Process: process: TBP>>=
procedure :: setup_test_cores => process_setup_test_cores
<<Process: procedures>>=
subroutine process_setup_test_cores (process, type_string)
class(process_t), intent(inout) :: process
class(prc_core_t), allocatable :: core
type(string_t), intent(in), optional :: type_string
if (present (type_string)) then
select case (char (type_string))
case ("template")
call process%setup_cores (dispatch_template_core)
case ("test_me")
call process%setup_cores (dispatch_test_me_core)
case default
call msg_bug ("process setup test cores: unsupported type string")
end select
- else
+ else
call process%setup_cores (dispatch_test_me_core)
end if
end subroutine process_setup_test_cores
-
+
subroutine dispatch_test_me_core (core, core_def, model, &
helicity_selection, qcd, use_color_factors, has_beam_pol)
use prc_test_core, only: test_t
class(prc_core_t), allocatable, intent(inout) :: core
class(prc_core_def_t), intent(in) :: core_def
class(model_data_t), intent(in), target, optional :: model
type(helicity_selection_t), intent(in), optional :: helicity_selection
type(qcd_t), intent(in), optional :: qcd
logical, intent(in), optional :: use_color_factors
logical, intent(in), optional :: has_beam_pol
allocate (test_t :: core)
end subroutine dispatch_test_me_core
-
+
subroutine dispatch_template_core (core, core_def, model, &
helicity_selection, qcd, use_color_factors, has_beam_pol)
use prc_template_me, only: prc_template_me_t
class(prc_core_t), allocatable, intent(inout) :: core
class(prc_core_def_t), intent(in) :: core_def
class(model_data_t), intent(in), target, optional :: model
type(helicity_selection_t), intent(in), optional :: helicity_selection
type(qcd_t), intent(in), optional :: qcd
logical, intent(in), optional :: use_color_factors
logical, intent(in), optional :: has_beam_pol
allocate (prc_template_me_t :: core)
select type (core)
type is (prc_template_me_t)
call core%set_parameters (model)
end select
end subroutine dispatch_template_core
@ %def process_setup_test_cores
-@
+@
<<Process: process: TBP>>=
procedure :: get_connected_states => process_get_connected_states
<<Process: procedures>>=
function process_get_connected_states (process, i_component, &
connected_terms) result (connected)
type(connected_state_t), dimension(:), allocatable :: connected
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
type(connected_state_t), dimension(:), intent(in) :: connected_terms
integer :: i, i_conn
integer :: n_conn
n_conn = 0
do i = 1, process%get_n_terms ()
if (process%term(i)%i_component == i_component) then
n_conn = n_conn + 1
end if
end do
allocate (connected (n_conn))
i_conn = 1
do i = 1, process%get_n_terms ()
if (process%term(i)%i_component == i_component) then
connected (i_conn) = connected_terms(i)
i_conn = i_conn + 1
end if
end do
end function process_get_connected_states
@ %def process_get_connected_states
@
\subsection{NLO specifics}
These subroutines (and the NLO specific properties they work on) could
potentially be moved to [[pcm_nlo_t]] and used more generically in
[[process_t]] with an appropriate interface in [[pcm_t]]
TODO wk 2018: This is used only by event initialization, which deals with an incomplete
process object.
<<Process: process: TBP>>=
procedure :: init_nlo_settings => process_init_nlo_settings
<<Process: procedures>>=
subroutine process_init_nlo_settings (process, var_list)
class(process_t), intent(inout) :: process
type(var_list_t), intent(in), target :: var_list
select type (pcm => process%pcm)
type is (pcm_nlo_t)
call pcm%init_nlo_settings (var_list)
if (debug_active (D_SUBTRACTION) .or. debug_active (D_VIRTUAL)) &
call pcm%settings%write ()
class default
call msg_fatal ("Attempt to set nlo_settings with a non-NLO pcm!")
end select
end subroutine process_init_nlo_settings
@ %def process_init_nlo_settings
@
<<Process: process: TBP>>=
generic :: get_nlo_type_component => get_nlo_type_component_single
procedure :: get_nlo_type_component_single => process_get_nlo_type_component_single
<<Process: procedures>>=
elemental function process_get_nlo_type_component_single (process, i_component) result (val)
integer :: val
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
val = process%component(i_component)%get_nlo_type ()
end function process_get_nlo_type_component_single
@ %def process_get_nlo_type_component_single
@
<<Process: process: TBP>>=
generic :: get_nlo_type_component => get_nlo_type_component_all
procedure :: get_nlo_type_component_all => process_get_nlo_type_component_all
<<Process: procedures>>=
pure function process_get_nlo_type_component_all (process) result (val)
integer, dimension(:), allocatable :: val
class(process_t), intent(in) :: process
allocate (val (size (process%component)))
val = process%component%get_nlo_type ()
end function process_get_nlo_type_component_all
@ %def process_get_nlo_type_component_all
@
<<Process: process: TBP>>=
procedure :: is_nlo_calculation => process_is_nlo_calculation
<<Process: procedures>>=
function process_is_nlo_calculation (process) result (nlo)
logical :: nlo
class(process_t), intent(in) :: process
select type (pcm => process%pcm)
type is (pcm_nlo_t)
nlo = .true.
class default
nlo = .false.
end select
end function process_is_nlo_calculation
@ %def process_is_nlo_calculation
@
<<Process: process: TBP>>=
procedure :: is_combined_nlo_integration &
=> process_is_combined_nlo_integration
<<Process: procedures>>=
function process_is_combined_nlo_integration (process) result (combined)
logical :: combined
class(process_t), intent(in) :: process
select type (pcm => process%pcm)
type is (pcm_nlo_t)
combined = pcm%settings%combined_integration
class default
combined = .false.
end select
end function process_is_combined_nlo_integration
@ %def process_is_combined_nlo_integration
@
<<Process: process: TBP>>=
procedure :: component_is_real_finite => process_component_is_real_finite
<<Process: procedures>>=
pure function process_component_is_real_finite (process, i_component) &
result (val)
logical :: val
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
val = process%component(i_component)%component_type == COMP_REAL_FIN
end function process_component_is_real_finite
@ %def process_component_is_real_finite
@ Return nlo data of a process component
<<Process: process: TBP>>=
procedure :: get_component_nlo_type => process_get_component_nlo_type
<<Process: procedures>>=
elemental function process_get_component_nlo_type (process, i_component) &
result (nlo_type)
integer :: nlo_type
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
nlo_type = process%component(i_component)%config%get_nlo_type ()
end function process_get_component_nlo_type
@ %def process_get_component_nlo_type
@ Return a pointer to the core that belongs to a component.
<<Process: process: TBP>>=
procedure :: get_component_core_ptr => process_get_component_core_ptr
<<Process: procedures>>=
function process_get_component_core_ptr (process, i_component) result (core)
class(process_t), intent(in), target :: process
integer, intent(in) :: i_component
class(prc_core_t), pointer :: core
integer :: i_core
i_core = process%pcm%get_i_core(i_component)
core => process%core_entry(i_core)%core
end function process_get_component_core_ptr
@ %def process_get_component_core_ptr
-@
+@
<<Process: process: TBP>>=
procedure :: get_component_associated_born &
=> process_get_component_associated_born
<<Process: procedures>>=
function process_get_component_associated_born (process, i_component) &
result (i_born)
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
integer :: i_born
i_born = process%component(i_component)%config%get_associated_born ()
end function process_get_component_associated_born
@ %def process_get_component_associated_born
@
<<Process: process: TBP>>=
procedure :: get_first_real_component => process_get_first_real_component
<<Process: procedures>>=
function process_get_first_real_component (process) result (i_real)
integer :: i_real
class(process_t), intent(in) :: process
i_real = process%component(1)%config%get_associated_real ()
end function process_get_first_real_component
@ %def process_get_first_real_component
@
<<Process: process: TBP>>=
procedure :: get_first_real_term => process_get_first_real_term
<<Process: procedures>>=
function process_get_first_real_term (process) result (i_real)
integer :: i_real
class(process_t), intent(in) :: process
integer :: i_component, i_term
i_component = process%component(1)%config%get_associated_real ()
i_real = 0
do i_term = 1, size (process%term)
if (process%term(i_term)%i_component == i_component) then
i_real = i_term
exit
end if
end do
if (i_real == 0) call msg_fatal ("Did not find associated real term!")
end function process_get_first_real_term
@ %def process_get_first_real_term
@
<<Process: process: TBP>>=
procedure :: get_associated_real_fin => process_get_associated_real_fin
<<Process: procedures>>=
elemental function process_get_associated_real_fin (process, i_component) result (i_real)
integer :: i_real
class(process_t), intent(in) :: process
integer, intent(in) :: i_component
i_real = process%component(i_component)%config%get_associated_real_fin ()
end function process_get_associated_real_fin
@ %def process_get_associated_real_fin
-@
+@
<<Process: process: TBP>>=
procedure :: select_i_term => process_select_i_term
<<Process: procedures>>=
pure function process_select_i_term (process, i_mci) result (i_term)
integer :: i_term
class(process_t), intent(in) :: process
integer, intent(in) :: i_mci
integer :: i_component, i_sub
i_component = process%mci_entry(i_mci)%i_component(1)
i_term = process%component(i_component)%i_term(1)
i_sub = process%term(i_term)%i_sub
if (i_sub > 0) &
i_term = process%term(i_sub)%i_term_global
end function process_select_i_term
@ %def process_select_i_term
@ Would be better to do this at the level of the writer of the core but
one has to bring NLO information there.
<<Process: process: TBP>>=
procedure :: prepare_any_external_code &
=> process_prepare_any_external_code
<<Process: procedures>>=
subroutine process_prepare_any_external_code (process)
class(process_t), intent(inout), target :: process
integer :: i
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, &
"process_prepare_external_code")
associate (pcm => process%pcm)
do i = 1, pcm%n_cores
call pcm%prepare_any_external_code ( &
process%core_entry(i), i, &
process%get_library_name (), &
process%config%model, &
process%env%get_var_list_ptr ())
end do
end associate
end subroutine process_prepare_any_external_code
@ %def process_prepare_any_external_code
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Process config}
<<[[process_config.f90]]>>=
<<File header>>
module process_config
<<Use kinds>>
<<Use strings>>
use format_utils, only: write_separator
use io_units
use md5
use os_interface
use diagnostics
use sf_base
use sf_mappings
use mappings, only: mapping_defaults_t
use phs_forests, only: phs_parameters_t
use sm_qcd
use physics_defs
use integration_results
use model_data
use models
use interactions
use quantum_numbers
use flavors
use helicities
use colors
use rng_base
use state_matrices
use process_libraries
use process_constants
use prc_core
use prc_external
use prc_openloops, only: prc_openloops_t
use prc_threshold, only: prc_threshold_t
use beams
use dispatch_beams, only: dispatch_qcd
use mci_base
use beam_structures
use phs_base
use variables
use expr_base
use blha_olp_interfaces, only: prc_blha_t
<<Standard module head>>
<<Process config: public>>
<<Process config: parameters>>
<<Process config: types>>
contains
<<Process config: procedures>>
end module process_config
@ %def process_config
@ Identifiers for the NLO setup.
<<Process config: parameters>>=
integer, parameter, public :: COMP_DEFAULT = 0
integer, parameter, public :: COMP_REAL_FIN = 1
integer, parameter, public :: COMP_MASTER = 2
integer, parameter, public :: COMP_VIRT = 3
integer, parameter, public :: COMP_REAL = 4
integer, parameter, public :: COMP_REAL_SING = 5
integer, parameter, public :: COMP_MISMATCH = 6
integer, parameter, public :: COMP_PDF = 7
integer, parameter, public :: COMP_SUB = 8
integer, parameter, public :: COMP_RESUM = 9
-@
+@
\subsection{Output selection flags}
We declare a number of identifiers for write methods, so they only
displays selected parts. The identifiers can be supplied to the [[vlist]]
array argument of the standard F2008 derived-type writer call.
<<Process config: parameters>>=
integer, parameter, public :: F_PACIFY = 1
integer, parameter, public :: F_SHOW_VAR_LIST = 11
integer, parameter, public :: F_SHOW_EXPRESSIONS = 12
integer, parameter, public :: F_SHOW_LIB = 13
integer, parameter, public :: F_SHOW_MODEL = 14
integer, parameter, public :: F_SHOW_QCD = 15
integer, parameter, public :: F_SHOW_OS_DATA = 16
integer, parameter, public :: F_SHOW_RNG = 17
integer, parameter, public :: F_SHOW_BEAMS = 18
@ %def SHOW_VAR_LIST
@ %def SHOW_EXPRESSIONS
@
This is a simple function that returns true if a flag value is present in
[[v_list]], but not its negative. If neither is present, it returns
-[[default]].
+[[default]].
<<Process config: public>>=
public :: flagged
<<Process config: procedures>>=
function flagged (v_list, id, def) result (flag)
logical :: flag
integer, dimension(:), intent(in) :: v_list
integer, intent(in) :: id
logical, intent(in), optional :: def
logical :: default_result
default_result = .false.; if (present (def)) default_result = def
if (default_result) then
flag = all (v_list /= -id)
else
flag = all (v_list /= -id) .and. any (v_list == id)
end if
end function flagged
-
+
@ %def flagged
@
Related: if flag is set (unset), append [[value]] (its negative) to the
[[v_list]], respectively. [[v_list]] must be allocated.
<<Process config: public>>=
- public :: set_flag
+ public :: set_flag
<<Process config: procedures>>=
subroutine set_flag (v_list, value, flag)
integer, dimension(:), intent(inout), allocatable :: v_list
integer, intent(in) :: value
logical, intent(in), optional :: flag
if (present (flag)) then
if (flag) then
v_list = [v_list, value]
else
v_list = [v_list, -value]
end if
end if
end subroutine set_flag
-
+
@ %def set_flag
@
\subsection{Generic configuration data}
This information concerns physical and technical properties of the
process. It is fixed upon initialization, using data from the
process specification and the variable list.
The number [[n_in]] is the number of incoming beam particles,
simultaneously the number of incoming partons, 1 for a decay and 2 for
a scattering process. (The number of outgoing partons may depend on
the process component.)
The number [[n_components]] is the number of components that constitute
the current process.
The number [[n_terms]] is the number of distinct contributions to the
scattering matrix that constitute the current process. Each component
may generate several terms.
The number [[n_mci]] is the number of independent MC
integration configurations that this process uses. Distinct process
components that share a MCI configuration may be combined pointwise.
(Nevertheless, a given MC variable set may correspond to several
``nearby'' kinematical configurations.) This is also the number of
distinct sampling-function results that this process can generate.
Process components that use distinct variable sets are added only once
after an integration pass has completed.
The [[model]] pointer identifies the physics model and its
parameters. This is a pointer to an external object.
Various [[parse_node_t]] objects are taken from the SINDARIN input.
They encode expressions for evaluating cuts and scales. The
workspaces for evaluating those expressions are set up in the
[[effective_state]] subobjects. Note that these are really pointers,
so the actual nodes are not stored inside the process object.
The [[md5sum]] is taken and used to verify the process configuration
when re-reading data from file.
<<Process config: public>>=
public :: process_config_data_t
<<Process config: types>>=
type :: process_config_data_t
class(process_def_t), pointer :: process_def => null ()
integer :: n_in = 0
integer :: n_components = 0
integer :: n_terms = 0
integer :: n_mci = 0
type(string_t) :: model_name
class(model_data_t), pointer :: model => null ()
type(qcd_t) :: qcd
class(expr_factory_t), allocatable :: ef_cuts
class(expr_factory_t), allocatable :: ef_scale
class(expr_factory_t), allocatable :: ef_fac_scale
class(expr_factory_t), allocatable :: ef_ren_scale
class(expr_factory_t), allocatable :: ef_weight
character(32) :: md5sum = ""
contains
<<Process config: process config data: TBP>>
end type process_config_data_t
@ %def process_config_data_t
@ Here, we may compress the expressions for cuts etc.
<<Process config: process config data: TBP>>=
procedure :: write => process_config_data_write
<<Process config: procedures>>=
subroutine process_config_data_write (config, u, counters, model, expressions)
class(process_config_data_t), intent(in) :: config
integer, intent(in) :: u
logical, intent(in) :: counters
logical, intent(in) :: model
logical, intent(in) :: expressions
write (u, "(1x,A)") "Configuration data:"
if (counters) then
write (u, "(3x,A,I0)") "Number of incoming particles = ", &
config%n_in
write (u, "(3x,A,I0)") "Number of process components = ", &
config%n_components
write (u, "(3x,A,I0)") "Number of process terms = ", &
config%n_terms
write (u, "(3x,A,I0)") "Number of MCI configurations = ", &
config%n_mci
end if
if (associated (config%model)) then
write (u, "(3x,A,A)") "Model = ", char (config%model_name)
if (model) then
call write_separator (u)
call config%model%write (u)
call write_separator (u)
end if
else
write (u, "(3x,A,A,A)") "Model = ", char (config%model_name), &
" [not associated]"
end if
call config%qcd%write (u, show_md5sum = .false.)
call write_separator (u)
if (expressions) then
if (allocated (config%ef_cuts)) then
call write_separator (u)
write (u, "(3x,A)") "Cut expression:"
call config%ef_cuts%write (u)
end if
if (allocated (config%ef_scale)) then
call write_separator (u)
write (u, "(3x,A)") "Scale expression:"
call config%ef_scale%write (u)
end if
if (allocated (config%ef_fac_scale)) then
call write_separator (u)
write (u, "(3x,A)") "Factorization scale expression:"
call config%ef_fac_scale%write (u)
end if
if (allocated (config%ef_ren_scale)) then
call write_separator (u)
write (u, "(3x,A)") "Renormalization scale expression:"
call config%ef_ren_scale%write (u)
end if
if (allocated (config%ef_weight)) then
call write_separator (u)
write (u, "(3x,A)") "Weight expression:"
call config%ef_weight%write (u)
end if
else
call write_separator (u)
write (u, "(3x,A)") "Expressions (cut, scales, weight): [not shown]"
end if
if (config%md5sum /= "") then
call write_separator (u)
write (u, "(3x,A,A,A)") "MD5 sum (config) = '", config%md5sum, "'"
end if
end subroutine process_config_data_write
@ %def process_config_data_write
@ Initialize. We use information from the process metadata and from
the process library, given the process ID. We also store the
currently active OS data set.
The model pointer references the model data within the [[env]] record. That
should be an instance of the global model.
We initialize the QCD object, unless the environment information is unavailable
(unit tests).
The RNG factory object is imported by moving the allocation.
<<Process config: process config data: TBP>>=
procedure :: init => process_config_data_init
<<Process config: procedures>>=
subroutine process_config_data_init (config, meta, env)
class(process_config_data_t), intent(out) :: config
type(process_metadata_t), intent(in) :: meta
type(process_environment_t), intent(in) :: env
config%process_def => env%lib%get_process_def_ptr (meta%id)
config%n_in = config%process_def%get_n_in ()
config%n_components = size (meta%component_id)
config%model => env%get_model_ptr ()
config%model_name = config%model%get_name ()
if (env%got_var_list ()) then
call dispatch_qcd &
(config%qcd, env%get_var_list_ptr (), env%get_os_data ())
end if
end subroutine process_config_data_init
@ %def process_config_data_init
@ Current implementation: nothing to finalize.
<<Process config: process config data: TBP>>=
procedure :: final => process_config_data_final
<<Process config: procedures>>=
subroutine process_config_data_final (config)
class(process_config_data_t), intent(inout) :: config
end subroutine process_config_data_final
@ %def process_config_data_final
@ Return a copy of the QCD data block.
<<Process config: process config data: TBP>>=
procedure :: get_qcd => process_config_data_get_qcd
<<Process config: procedures>>=
function process_config_data_get_qcd (config) result (qcd)
class(process_config_data_t), intent(in) :: config
type(qcd_t) :: qcd
qcd = config%qcd
end function process_config_data_get_qcd
-
+
@ %def process_config_data_get_qcd
@ Compute the MD5 sum of the configuration data. This encodes, in
particular, the model and the expressions for cut, scales, weight,
etc. It should not contain the IDs and number of components, etc.,
since the MD5 sum should be useful for integrating individual
components.
This is done only once. If the MD5 sum is nonempty, the calculation
is skipped.
<<Process config: process config data: TBP>>=
procedure :: compute_md5sum => process_config_data_compute_md5sum
<<Process config: procedures>>=
subroutine process_config_data_compute_md5sum (config)
class(process_config_data_t), intent(inout) :: config
integer :: u
if (config%md5sum == "") then
u = free_unit ()
open (u, status = "scratch", action = "readwrite")
call config%write (u, counters = .false., &
model = .true., expressions = .true.)
rewind (u)
config%md5sum = md5sum (u)
close (u)
end if
end subroutine process_config_data_compute_md5sum
@ %def process_config_data_compute_md5sum
@
<<Process config: process config data: TBP>>=
procedure :: get_md5sum => process_config_data_get_md5sum
<<Process config: procedures>>=
pure function process_config_data_get_md5sum (config) result (md5)
character(32) :: md5
class(process_config_data_t), intent(in) :: config
md5 = config%md5sum
end function process_config_data_get_md5sum
@ %def process_config_data_get_md5sum
@
\subsection{Environment}
This record stores a snapshot of the process environment at the point where
the process object is created.
Model and variable list are implemented as pointer, so they always have the
[[target]] attribute.
For unit-testing purposes, setting the var list is optional. If not set, the
pointer is null.
<<Process config: public>>=
public :: process_environment_t
<<Process config: types>>=
type :: process_environment_t
private
type(model_t), pointer :: model => null ()
type(var_list_t), pointer :: var_list => null ()
logical :: var_list_is_set = .false.
type(process_library_t), pointer :: lib => null ()
type(beam_structure_t) :: beam_structure
type(os_data_t) :: os_data
contains
<<Process config: process environment: TBP>>
end type process_environment_t
-
+
@ %def process_environment_t
@ Model and local var list are snapshots and need a finalizer.
<<Process config: process environment: TBP>>=
procedure :: final => process_environment_final
<<Process config: procedures>>=
subroutine process_environment_final (env)
class(process_environment_t), intent(inout) :: env
if (associated (env%model)) then
call env%model%final ()
deallocate (env%model)
end if
if (associated (env%var_list)) then
call env%var_list%final (follow_link=.true.)
deallocate (env%var_list)
end if
end subroutine process_environment_final
@ %def process_environment_final
@ Output, DTIO compatible.
<<Process config: process environment: TBP>>=
procedure :: write => process_environment_write
procedure :: write_formatted => process_environment_write_formatted
! generic :: write (formatted) => write_formatted
<<Process config: procedures>>=
subroutine process_environment_write (env, unit, &
show_var_list, show_model, show_lib, show_beams, show_os_data)
class(process_environment_t), intent(in) :: env
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_var_list
logical, intent(in), optional :: show_model
logical, intent(in), optional :: show_lib
logical, intent(in), optional :: show_beams
logical, intent(in), optional :: show_os_data
integer :: u, iostat
integer, dimension(:), allocatable :: v_list
character(0) :: iomsg
u = given_output_unit (unit)
allocate (v_list (0))
call set_flag (v_list, F_SHOW_VAR_LIST, show_var_list)
call set_flag (v_list, F_SHOW_MODEL, show_model)
call set_flag (v_list, F_SHOW_LIB, show_lib)
call set_flag (v_list, F_SHOW_BEAMS, show_beams)
call set_flag (v_list, F_SHOW_OS_DATA, show_os_data)
call env%write_formatted (u, "LISTDIRECTED", v_list, iostat, iomsg)
end subroutine process_environment_write
-
+
@ %def process_environment_write
@ DTIO standard write.
<<Process config: procedures>>=
subroutine process_environment_write_formatted &
(dtv, unit, iotype, v_list, iostat, iomsg)
class(process_environment_t), intent(in) :: dtv
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, dimension(:), intent(in) :: v_list
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
associate (env => dtv)
if (flagged (v_list, F_SHOW_VAR_LIST, .true.)) then
write (unit, "(1x,A)") "Variable list:"
if (associated (env%var_list)) then
call write_separator (unit)
call env%var_list%write (unit)
else
write (unit, "(3x,A)") "[not allocated]"
end if
call write_separator (unit)
end if
if (flagged (v_list, F_SHOW_MODEL, .true.)) then
write (unit, "(1x,A)") "Model:"
if (associated (env%model)) then
call write_separator (unit)
call env%model%write (unit)
else
write (unit, "(3x,A)") "[not allocated]"
end if
call write_separator (unit)
end if
if (flagged (v_list, F_SHOW_LIB, .true.)) then
write (unit, "(1x,A)") "Process library:"
if (associated (env%lib)) then
call write_separator (unit)
call env%lib%write (unit)
else
write (unit, "(3x,A)") "[not allocated]"
end if
end if
if (flagged (v_list, F_SHOW_BEAMS, .true.)) then
call write_separator (unit)
call env%beam_structure%write (unit)
end if
if (flagged (v_list, F_SHOW_OS_DATA, .true.)) then
write (unit, "(1x,A)") "Operating-system data:"
call write_separator (unit)
call env%os_data%write (unit)
end if
end associate
iostat = 0
end subroutine process_environment_write_formatted
-
+
@ %def process_environment_write_formatted
@ Initialize: Make a snapshot of the provided model. Make a link to the
current process library.
Also make a snapshot of the variable list, if provided. If none is
provided, there is an empty variable list nevertheless, so a pointer
lookup does not return null.
If no beam structure is provided, the beam-structure member is empty and will
yield a number of zero beams when queried.
<<Process config: process environment: TBP>>=
procedure :: init => process_environment_init
<<Process config: procedures>>=
subroutine process_environment_init &
(env, model, lib, os_data, var_list, beam_structure)
class(process_environment_t), intent(out) :: env
type(model_t), intent(in), target :: model
type(process_library_t), intent(in), target :: lib
type(os_data_t), intent(in) :: os_data
type(var_list_t), intent(in), target, optional :: var_list
type(beam_structure_t), intent(in), optional :: beam_structure
allocate (env%model)
call env%model%init_instance (model)
env%lib => lib
env%os_data = os_data
allocate (env%var_list)
if (present (var_list)) then
call env%var_list%init_snapshot (var_list, follow_link=.true.)
env%var_list_is_set = .true.
end if
if (present (beam_structure)) then
env%beam_structure = beam_structure
end if
end subroutine process_environment_init
@ %def process_environment_init
@ Indicate whether a variable list has been provided upon initialization.
<<Process config: process environment: TBP>>=
procedure :: got_var_list => process_environment_got_var_list
<<Process config: procedures>>=
function process_environment_got_var_list (env) result (flag)
class(process_environment_t), intent(in) :: env
logical :: flag
flag = env%var_list_is_set
end function process_environment_got_var_list
-
+
@ %def process_environment_got_var_list
@ Return a pointer to the variable list.
<<Process config: process environment: TBP>>=
procedure :: get_var_list_ptr => process_environment_get_var_list_ptr
<<Process config: procedures>>=
function process_environment_get_var_list_ptr (env) result (var_list)
class(process_environment_t), intent(in) :: env
type(var_list_t), pointer :: var_list
var_list => env%var_list
end function process_environment_get_var_list_ptr
-
+
@ %def process_environment_get_var_list_ptr
@ Return a pointer to the model, if it exists.
<<Process config: process environment: TBP>>=
procedure :: get_model_ptr => process_environment_get_model_ptr
<<Process config: procedures>>=
function process_environment_get_model_ptr (env) result (model)
class(process_environment_t), intent(in) :: env
type(model_t), pointer :: model
model => env%model
end function process_environment_get_model_ptr
-
+
@ %def process_environment_get_model_ptr
@ Return the process library pointer.
<<Process config: process environment: TBP>>=
procedure :: get_lib_ptr => process_environment_get_lib_ptr
<<Process config: procedures>>=
function process_environment_get_lib_ptr (env) result (lib)
class(process_environment_t), intent(inout) :: env
type(process_library_t), pointer :: lib
lib => env%lib
end function process_environment_get_lib_ptr
-
+
@ %def process_environment_get_lib_ptr
@ Clear the process library pointer, in case the library is deleted.
<<Process config: process environment: TBP>>=
procedure :: reset_lib_ptr => process_environment_reset_lib_ptr
<<Process config: procedures>>=
subroutine process_environment_reset_lib_ptr (env)
class(process_environment_t), intent(inout) :: env
env%lib => null ()
end subroutine process_environment_reset_lib_ptr
-
+
@ %def process_environment_reset_lib_ptr
@ Check whether the process library has changed, in case the library is
recompiled, etc.
<<Process config: process environment: TBP>>=
procedure :: check_lib_sanity => process_environment_check_lib_sanity
<<Process config: procedures>>=
subroutine process_environment_check_lib_sanity (env, meta)
class(process_environment_t), intent(in) :: env
type(process_metadata_t), intent(in) :: meta
if (associated (env%lib)) then
if (env%lib%get_update_counter () /= meta%lib_update_counter) then
call msg_fatal ("Process '" // char (meta%id) &
// "': library has been recompiled after integration")
end if
end if
end subroutine process_environment_check_lib_sanity
-
+
@ %def process_environment_check_lib_sanity
@ Fill the [[data]] block using the appropriate process-library access entry.
<<Process config: process environment: TBP>>=
procedure :: fill_process_constants => &
process_environment_fill_process_constants
<<Process config: procedures>>=
subroutine process_environment_fill_process_constants &
(env, id, i_component, data)
class(process_environment_t), intent(in) :: env
type(string_t), intent(in) :: id
integer, intent(in) :: i_component
type(process_constants_t), intent(out) :: data
call env%lib%fill_constants (id, i_component, data)
end subroutine process_environment_fill_process_constants
@ %def process_environment_fill_process_constants
@ Return the entire beam structure.
<<Process config: process environment: TBP>>=
procedure :: get_beam_structure => process_environment_get_beam_structure
<<Process config: procedures>>=
function process_environment_get_beam_structure (env) result (beam_structure)
class(process_environment_t), intent(in) :: env
type(beam_structure_t) :: beam_structure
beam_structure = env%beam_structure
end function process_environment_get_beam_structure
-
+
@ %def process_environment_get_beam_structure
@ Check the beam structure for PDFs.
<<Process config: process environment: TBP>>=
procedure :: has_pdfs => process_environment_has_pdfs
<<Process config: procedures>>=
function process_environment_has_pdfs (env) result (flag)
class(process_environment_t), intent(in) :: env
logical :: flag
flag = env%beam_structure%has_pdf ()
end function process_environment_has_pdfs
-
+
@ %def process_environment_has_pdfs
@ Check the beam structure for polarized beams.
<<Process config: process environment: TBP>>=
procedure :: has_polarized_beams => process_environment_has_polarized_beams
<<Process config: procedures>>=
function process_environment_has_polarized_beams (env) result (flag)
class(process_environment_t), intent(in) :: env
logical :: flag
flag = env%beam_structure%has_polarized_beams ()
end function process_environment_has_polarized_beams
-
+
@ %def process_environment_has_polarized_beams
@ Return a copy of the OS data block.
<<Process config: process environment: TBP>>=
procedure :: get_os_data => process_environment_get_os_data
<<Process config: procedures>>=
function process_environment_get_os_data (env) result (os_data)
class(process_environment_t), intent(in) :: env
type(os_data_t) :: os_data
os_data = env%os_data
end function process_environment_get_os_data
-
+
@ %def process_environment_get_os_data
@
\subsection{Metadata}
This information describes the process. It is fixed upon initialization.
The [[id]] string is the name of the process object, as given by the
user. The matrix element generator will use this string for naming
Fortran procedures and types, so it should qualify as a Fortran name.
The [[num_id]] is meaningful if nonzero. It is used for communication
with external programs or file standards which do not support string IDs.
The [[run_id]] string distinguishes among several runs for the same
process. It identifies process instances with respect to adapted
integration grids and similar run-specific data. The run ID is kept
when copying processes for creating instances, however, so it does not
distinguish event samples.
The [[lib_name]] identifies the process library where the process
definition and the process driver are located.
The [[lib_index]] is the index of entry in the process library that
corresponds to the current process.
The [[component_id]] array identifies the individual process components.
The [[component_description]] is an array of human-readable strings
that characterize the process components, for instance [[a, b => c, d]].
The [[active]] mask array marks those components which are active. The others
are skipped.
<<Process config: public>>=
public :: process_metadata_t
<<Process config: types>>=
type :: process_metadata_t
integer :: type = PRC_UNKNOWN
type(string_t) :: id
integer :: num_id = 0
type(string_t) :: run_id
type(string_t), allocatable :: lib_name
integer :: lib_update_counter = 0
integer :: lib_index = 0
integer :: n_components = 0
type(string_t), dimension(:), allocatable :: component_id
type(string_t), dimension(:), allocatable :: component_description
logical, dimension(:), allocatable :: active
contains
<<Process config: process metadata: TBP>>
end type process_metadata_t
@ %def process_metadata_t
@ Output: ID and run ID.
We write the variable list only upon request.
<<Process config: process metadata: TBP>>=
procedure :: write => process_metadata_write
<<Process config: procedures>>=
subroutine process_metadata_write (meta, u, screen)
class(process_metadata_t), intent(in) :: meta
integer, intent(in) :: u
logical, intent(in) :: screen
integer :: i
select case (meta%type)
case (PRC_UNKNOWN)
if (screen) then
write (msg_buffer, "(A)") "Process [undefined]"
else
write (u, "(1x,A)") "Process [undefined]"
end if
return
case (PRC_DECAY)
if (screen) then
write (msg_buffer, "(A,1x,A,A,A)") "Process [decay]:", &
"'", char (meta%id), "'"
else
write (u, "(1x,A)", advance="no") "Process [decay]:"
end if
case (PRC_SCATTERING)
if (screen) then
write (msg_buffer, "(A,1x,A,A,A)") "Process [scattering]:", &
"'", char (meta%id), "'"
else
write (u, "(1x,A)", advance="no") "Process [scattering]:"
end if
case default
call msg_bug ("process_write: undefined process type")
end select
if (screen) then
call msg_message ()
else
write (u, "(1x,A,A,A)") "'", char (meta%id), "'"
end if
if (meta%num_id /= 0) then
if (screen) then
write (msg_buffer, "(2x,A,I0)") "ID (num) = ", meta%num_id
call msg_message ()
else
write (u, "(3x,A,I0)") "ID (num) = ", meta%num_id
end if
end if
if (screen) then
if (meta%run_id /= "") then
write (msg_buffer, "(2x,A,A,A)") "Run ID = '", &
char (meta%run_id), "'"
call msg_message ()
end if
else
write (u, "(3x,A,A,A)") "Run ID = '", char (meta%run_id), "'"
end if
if (allocated (meta%lib_name)) then
if (screen) then
write (msg_buffer, "(2x,A,A,A)") "Library name = '", &
char (meta%lib_name), "'"
call msg_message ()
else
write (u, "(3x,A,A,A)") "Library name = '", &
char (meta%lib_name), "'"
end if
else
if (screen) then
write (msg_buffer, "(2x,A)") "Library name = [not associated]"
call msg_message ()
else
write (u, "(3x,A)") "Library name = [not associated]"
end if
end if
if (screen) then
write (msg_buffer, "(2x,A,I0)") "Process index = ", meta%lib_index
call msg_message ()
else
write (u, "(3x,A,I0)") "Process index = ", meta%lib_index
end if
if (allocated (meta%component_id)) then
if (screen) then
if (any (meta%active)) then
write (msg_buffer, "(2x,A)") "Process components:"
else
write (msg_buffer, "(2x,A)") "Process components: [none]"
end if
call msg_message ()
else
write (u, "(3x,A)") "Process components:"
end if
do i = 1, size (meta%component_id)
if (.not. meta%active(i)) cycle
if (screen) then
write (msg_buffer, "(4x,I0,9A)") i, ": '", &
char (meta%component_id (i)), "': ", &
char (meta%component_description (i))
call msg_message ()
else
write (u, "(5x,I0,9A)") i, ": '", &
char (meta%component_id (i)), "': ", &
char (meta%component_description (i))
end if
end do
end if
if (screen) then
write (msg_buffer, "(A)") repeat ("-", 72)
call msg_message ()
else
call write_separator (u)
end if
end subroutine process_metadata_write
@ %def process_metadata_write
@ Short output: list components.
<<Process config: process metadata: TBP>>=
procedure :: show => process_metadata_show
<<Process config: procedures>>=
subroutine process_metadata_show (meta, u, model_name)
class(process_metadata_t), intent(in) :: meta
integer, intent(in) :: u
type(string_t), intent(in) :: model_name
integer :: i
select case (meta%type)
case (PRC_UNKNOWN)
write (u, "(A)") "Process: [undefined]"
return
case default
write (u, "(A)", advance="no") "Process:"
end select
write (u, "(1x,A)", advance="no") char (meta%id)
select case (meta%num_id)
case (0)
case default
write (u, "(1x,'(',I0,')')", advance="no") meta%num_id
end select
select case (char (model_name))
case ("")
case default
write (u, "(1x,'[',A,']')", advance="no") char (model_name)
end select
write (u, *)
if (allocated (meta%component_id)) then
do i = 1, size (meta%component_id)
if (meta%active(i)) then
write (u, "(2x,I0,':',1x,A)") i, &
char (meta%component_description (i))
end if
end do
end if
end subroutine process_metadata_show
@ %def process_metadata_show
@ Initialize. Find process ID and run ID.
Also find the process ID in the process library and retrieve some metadata from
-there.
+there.
<<Process config: process metadata: TBP>>=
procedure :: init => process_metadata_init
<<Process config: procedures>>=
subroutine process_metadata_init (meta, id, lib, var_list)
class(process_metadata_t), intent(out) :: meta
type(string_t), intent(in) :: id
type(process_library_t), intent(in), target :: lib
type(var_list_t), intent(in) :: var_list
select case (lib%get_n_in (id))
case (1); meta%type = PRC_DECAY
case (2); meta%type = PRC_SCATTERING
case default
call msg_bug ("Process '" // char (id) // "': impossible n_in")
end select
meta%id = id
meta%run_id = var_list%get_sval (var_str ("$run_id"))
allocate (meta%lib_name)
meta%lib_name = lib%get_name ()
meta%lib_update_counter = lib%get_update_counter ()
if (lib%contains (id)) then
meta%lib_index = lib%get_entry_index (id)
meta%num_id = lib%get_num_id (id)
call lib%get_component_list (id, meta%component_id)
meta%n_components = size (meta%component_id)
call lib%get_component_description_list &
(id, meta%component_description)
allocate (meta%active (meta%n_components), source = .true.)
else
call msg_fatal ("Process library doesn't contain process '" &
// char (id) // "'")
end if
if (.not. lib%is_active ()) then
call msg_bug ("Process init: inactive library not handled yet")
end if
end subroutine process_metadata_init
@ %def process_metadata_init
@ Mark a component as inactive.
<<Process config: process metadata: TBP>>=
procedure :: deactivate_component => process_metadata_deactivate_component
<<Process config: procedures>>=
subroutine process_metadata_deactivate_component (meta, i)
class(process_metadata_t), intent(inout) :: meta
integer, intent(in) :: i
call msg_message ("Process component '" &
// char (meta%component_id(i)) // "': matrix element vanishes")
meta%active(i) = .false.
end subroutine process_metadata_deactivate_component
@ %def process_metadata_deactivate_component
@
\subsection{Phase-space configuration}
A process can have a number of independent phase-space configuration entries,
depending on the process definition and evaluation algorithm. Each entry
holds various configuration-parameter data and the actual [[phs_config_t]]
record, which can vary in concrete type.
<<Process config: public>>=
public :: process_phs_config_t
<<Process config: types>>=
type :: process_phs_config_t
type(phs_parameters_t) :: phs_par
type(mapping_defaults_t) :: mapping_defs
class(phs_config_t), allocatable :: phs_config
contains
<<Process config: process phs config: TBP>>
end type process_phs_config_t
-
+
@ %def process_phs_config_t
@ Output, DTIO compatible.
<<Process config: process phs config: TBP>>=
procedure :: write => process_phs_config_write
procedure :: write_formatted => process_phs_config_write_formatted
! generic :: write (formatted) => write_formatted
<<Process config: procedures>>=
subroutine process_phs_config_write (phs_config, unit)
class(process_phs_config_t), intent(in) :: phs_config
integer, intent(in), optional :: unit
integer :: u, iostat
integer, dimension(:), allocatable :: v_list
character(0) :: iomsg
u = given_output_unit (unit)
allocate (v_list (0))
call phs_config%write_formatted (u, "LISTDIRECTED", v_list, iostat, iomsg)
end subroutine process_phs_config_write
-
+
@ %def process_phs_config_write
@ DTIO standard write.
<<Process config: procedures>>=
subroutine process_phs_config_write_formatted &
(dtv, unit, iotype, v_list, iostat, iomsg)
class(process_phs_config_t), intent(in) :: dtv
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, dimension(:), intent(in) :: v_list
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
associate (phs_config => dtv)
write (unit, "(1x, A)") "Phase-space configuration entry:"
call phs_config%phs_par%write (unit)
call phs_config%mapping_defs%write (unit)
end associate
iostat = 0
end subroutine process_phs_config_write_formatted
-
+
@ %def process_phs_config_write_formatted
@
\subsection{Beam configuration}
The object [[data]] holds all details about the initial beam
configuration. The allocatable array [[sf]] holds the structure-function
configuration blocks. There are [[n_strfun]] entries in the
structure-function chain (not counting the initial beam object). We
maintain [[n_channel]] independent parameterizations of this chain.
If this is greater than zero, we need a multi-channel sampling
algorithm, where for each point one channel is selected to generate
kinematics.
The number of parameters that are required for generating a
structure-function chain is [[n_sfpar]].
The flag [[azimuthal_dependence]] tells whether the process setup is
symmetric about the beam axis in the c.m.\ system. This implies that
there is no transversal beam polarization. The flag [[lab_is_cm_frame]] is
obvious.
<<Process config: public>>=
public :: process_beam_config_t
<<Process config: types>>=
type :: process_beam_config_t
type(beam_data_t) :: data
integer :: n_strfun = 0
integer :: n_channel = 1
integer :: n_sfpar = 0
type(sf_config_t), dimension(:), allocatable :: sf
type(sf_channel_t), dimension(:), allocatable :: sf_channel
logical :: azimuthal_dependence = .false.
logical :: lab_is_cm_frame = .true.
character(32) :: md5sum = ""
logical :: sf_trace = .false.
type(string_t) :: sf_trace_file
contains
<<Process config: process beam config: TBP>>
end type process_beam_config_t
@ %def process_beam_config_t
@ Here we write beam data only if they are actually used.
The [[verbose]] flag is passed to the beam-data writer.
<<Process config: process beam config: TBP>>=
procedure :: write => process_beam_config_write
<<Process config: procedures>>=
subroutine process_beam_config_write (object, unit, verbose)
class(process_beam_config_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u, i, c
u = given_output_unit (unit)
call object%data%write (u, verbose = verbose)
if (object%data%initialized) then
write (u, "(3x,A,L1)") "Azimuthal dependence = ", &
object%azimuthal_dependence
write (u, "(3x,A,L1)") "Lab frame is c.m. frame = ", &
object%lab_is_cm_frame
if (object%md5sum /= "") then
write (u, "(3x,A,A,A)") "MD5 sum (beams/strf) = '", &
object%md5sum, "'"
end if
if (allocated (object%sf)) then
do i = 1, size (object%sf)
call object%sf(i)%write (u)
end do
if (any_sf_channel_has_mapping (object%sf_channel)) then
write (u, "(1x,A,L1)") "Structure-function mappings per channel:"
do c = 1, object%n_channel
write (u, "(3x,I0,':')", advance="no") c
call object%sf_channel(c)%write (u)
end do
end if
end if
end if
end subroutine process_beam_config_write
@ %def process_beam_config_write
@ The beam data have a finalizer. We assume that there is none for the
structure-function data.
<<Process config: process beam config: TBP>>=
procedure :: final => process_beam_config_final
<<Process config: procedures>>=
subroutine process_beam_config_final (object)
class(process_beam_config_t), intent(inout) :: object
call object%data%final ()
end subroutine process_beam_config_final
@ %def process_beam_config_final
@ Initialize the beam setup with a given beam structure object.
<<Process config: process beam config: TBP>>=
procedure :: init_beam_structure => process_beam_config_init_beam_structure
<<Process config: procedures>>=
subroutine process_beam_config_init_beam_structure &
(beam_config, beam_structure, sqrts, model, decay_rest_frame)
class(process_beam_config_t), intent(out) :: beam_config
type(beam_structure_t), intent(in) :: beam_structure
logical, intent(in), optional :: decay_rest_frame
real(default), intent(in) :: sqrts
class(model_data_t), intent(in), target :: model
call beam_config%data%init_structure (beam_structure, &
sqrts, model, decay_rest_frame)
beam_config%lab_is_cm_frame = beam_config%data%cm_frame ()
end subroutine process_beam_config_init_beam_structure
@ %def process_beam_config_init_beam_structure
@ Initialize the beam setup for a scattering process with specified
flavor combination, other properties taken from the beam structure
object (if any).
<<Process config: process beam config: TBP>>=
procedure :: init_scattering => process_beam_config_init_scattering
<<Process config: procedures>>=
subroutine process_beam_config_init_scattering &
(beam_config, flv_in, sqrts, beam_structure)
class(process_beam_config_t), intent(out) :: beam_config
type(flavor_t), dimension(2), intent(in) :: flv_in
real(default), intent(in) :: sqrts
type(beam_structure_t), intent(in), optional :: beam_structure
if (present (beam_structure)) then
if (beam_structure%polarized ()) then
call beam_config%data%init_sqrts (sqrts, flv_in, &
beam_structure%get_smatrix (), beam_structure%get_pol_f ())
else
call beam_config%data%init_sqrts (sqrts, flv_in)
end if
else
call beam_config%data%init_sqrts (sqrts, flv_in)
end if
end subroutine process_beam_config_init_scattering
@ %def process_beam_config_init_scattering
@ Initialize the beam setup for a decay process with specified flavor,
other properties taken from the beam structure object (if present).
For a cascade decay, we set
[[rest_frame]] to false, indicating a event-wise varying momentum.
The beam data itself are initialized for the particle at rest.
<<Process config: process beam config: TBP>>=
procedure :: init_decay => process_beam_config_init_decay
<<Process config: procedures>>=
subroutine process_beam_config_init_decay &
(beam_config, flv_in, rest_frame, beam_structure)
class(process_beam_config_t), intent(out) :: beam_config
type(flavor_t), dimension(1), intent(in) :: flv_in
logical, intent(in), optional :: rest_frame
type(beam_structure_t), intent(in), optional :: beam_structure
if (present (beam_structure)) then
if (beam_structure%polarized ()) then
call beam_config%data%init_decay (flv_in, &
beam_structure%get_smatrix (), beam_structure%get_pol_f (), &
rest_frame = rest_frame)
else
call beam_config%data%init_decay (flv_in, rest_frame = rest_frame)
end if
else
call beam_config%data%init_decay (flv_in, &
rest_frame = rest_frame)
end if
beam_config%lab_is_cm_frame = beam_config%data%cm_frame ()
end subroutine process_beam_config_init_decay
@ %def process_beam_config_init_decay
@ Print an informative message.
<<Process config: process beam config: TBP>>=
procedure :: startup_message => process_beam_config_startup_message
<<Process config: procedures>>=
subroutine process_beam_config_startup_message &
(beam_config, unit, beam_structure)
class(process_beam_config_t), intent(in) :: beam_config
integer, intent(in), optional :: unit
type(beam_structure_t), intent(in), optional :: beam_structure
integer :: u
u = free_unit ()
open (u, status="scratch", action="readwrite")
if (present (beam_structure)) then
call beam_structure%write (u)
end if
call beam_config%data%write (u)
rewind (u)
do
read (u, "(1x,A)", end=1) msg_buffer
call msg_message ()
end do
1 continue
close (u)
end subroutine process_beam_config_startup_message
@ %def process_beam_config_startup_message
@ Allocate the structure-function array.
<<Process config: process beam config: TBP>>=
procedure :: init_sf_chain => process_beam_config_init_sf_chain
<<Process config: procedures>>=
subroutine process_beam_config_init_sf_chain &
(beam_config, sf_config, sf_trace_file)
class(process_beam_config_t), intent(inout) :: beam_config
type(sf_config_t), dimension(:), intent(in) :: sf_config
type(string_t), intent(in), optional :: sf_trace_file
integer :: i
beam_config%n_strfun = size (sf_config)
allocate (beam_config%sf (beam_config%n_strfun))
do i = 1, beam_config%n_strfun
associate (sf => sf_config(i))
call beam_config%sf(i)%init (sf%i, sf%data)
if (.not. sf%data%is_generator ()) then
beam_config%n_sfpar = beam_config%n_sfpar + sf%data%get_n_par ()
end if
end associate
end do
if (present (sf_trace_file)) then
beam_config%sf_trace = .true.
beam_config%sf_trace_file = sf_trace_file
end if
end subroutine process_beam_config_init_sf_chain
@ %def process_beam_config_init_sf_chain
@ Allocate the structure-function mapping channel array, given the
requested number of channels.
<<Process config: process beam config: TBP>>=
procedure :: allocate_sf_channels => process_beam_config_allocate_sf_channels
<<Process config: procedures>>=
subroutine process_beam_config_allocate_sf_channels (beam_config, n_channel)
class(process_beam_config_t), intent(inout) :: beam_config
integer, intent(in) :: n_channel
beam_config%n_channel = n_channel
call allocate_sf_channels (beam_config%sf_channel, &
n_channel = n_channel, &
n_strfun = beam_config%n_strfun)
end subroutine process_beam_config_allocate_sf_channels
@ %def process_beam_config_allocate_sf_channels
@ Set a structure-function mapping channel for an array of
structure-function entries, for a single channel. (The default is no mapping.)
<<Process config: process beam config: TBP>>=
procedure :: set_sf_channel => process_beam_config_set_sf_channel
<<Process config: procedures>>=
subroutine process_beam_config_set_sf_channel (beam_config, c, sf_channel)
class(process_beam_config_t), intent(inout) :: beam_config
integer, intent(in) :: c
type(sf_channel_t), intent(in) :: sf_channel
beam_config%sf_channel(c) = sf_channel
end subroutine process_beam_config_set_sf_channel
@ %def process_beam_config_set_sf_channel
@ Print an informative startup message.
<<Process config: process beam config: TBP>>=
procedure :: sf_startup_message => process_beam_config_sf_startup_message
<<Process config: procedures>>=
subroutine process_beam_config_sf_startup_message &
(beam_config, sf_string, unit)
class(process_beam_config_t), intent(in) :: beam_config
type(string_t), intent(in) :: sf_string
integer, intent(in), optional :: unit
if (beam_config%n_strfun > 0) then
call msg_message ("Beam structure: " // char (sf_string), unit = unit)
write (msg_buffer, "(A,3(1x,I0,1x,A))") &
"Beam structure:", &
beam_config%n_channel, "channels,", &
beam_config%n_sfpar, "dimensions"
call msg_message (unit = unit)
if (beam_config%sf_trace) then
call msg_message ("Beam structure: tracing &
&values in '" // char (beam_config%sf_trace_file) // "'")
end if
end if
end subroutine process_beam_config_sf_startup_message
@ %def process_beam_config_startup_message
@ Return the PDF set currently in use, if any. This should be unique,
so we scan the structure functions until we get a nonzero number.
(This implies that if the PDF set is not unique (e.g., proton and
photon structure used together), this does not work correctly.)
<<Process config: process beam config: TBP>>=
procedure :: get_pdf_set => process_beam_config_get_pdf_set
<<Process config: procedures>>=
function process_beam_config_get_pdf_set (beam_config) result (pdf_set)
class(process_beam_config_t), intent(in) :: beam_config
integer :: pdf_set
integer :: i
pdf_set = 0
if (allocated (beam_config%sf)) then
do i = 1, size (beam_config%sf)
pdf_set = beam_config%sf(i)%get_pdf_set ()
if (pdf_set /= 0) return
end do
end if
end function process_beam_config_get_pdf_set
@ %def process_beam_config_get_pdf_set
@ Return the beam file.
<<Process config: process beam config: TBP>>=
procedure :: get_beam_file => process_beam_config_get_beam_file
<<Process config: procedures>>=
function process_beam_config_get_beam_file (beam_config) result (file)
class(process_beam_config_t), intent(in) :: beam_config
type(string_t) :: file
integer :: i
file = ""
if (allocated (beam_config%sf)) then
do i = 1, size (beam_config%sf)
file = beam_config%sf(i)%get_beam_file ()
if (file /= "") return
end do
end if
end function process_beam_config_get_beam_file
@ %def process_beam_config_get_beam_file
@ Compute the MD5 sum for the complete beam setup. We rely on the
default output of [[write]] to contain all relevant data.
This is done only once, when the MD5 sum is still empty.
<<Process config: process beam config: TBP>>=
procedure :: compute_md5sum => process_beam_config_compute_md5sum
<<Process config: procedures>>=
subroutine process_beam_config_compute_md5sum (beam_config)
class(process_beam_config_t), intent(inout) :: beam_config
integer :: u
if (beam_config%md5sum == "") then
u = free_unit ()
open (u, status = "scratch", action = "readwrite")
call beam_config%write (u, verbose=.true.)
rewind (u)
beam_config%md5sum = md5sum (u)
close (u)
end if
end subroutine process_beam_config_compute_md5sum
@ %def process_beam_config_compute_md5sum
@
<<Process config: process beam config: TBP>>=
procedure :: get_md5sum => process_beam_config_get_md5sum
<<Process config: procedures>>=
pure function process_beam_config_get_md5sum (beam_config) result (md5)
character(32) :: md5
class(process_beam_config_t), intent(in) :: beam_config
md5 = beam_config%md5sum
end function process_beam_config_get_md5sum
@ %def process_beam_config_get_md5sum
@
<<Process config: process beam config: TBP>>=
procedure :: has_structure_function => process_beam_config_has_structure_function
<<Process config: procedures>>=
pure function process_beam_config_has_structure_function (beam_config) result (has_sf)
logical :: has_sf
class(process_beam_config_t), intent(in) :: beam_config
has_sf = beam_config%n_strfun > 0
end function process_beam_config_has_structure_function
@ %def process_beam_config_has_structure_function
@
\subsection{Process components}
A process component is an individual contribution to a process
(scattering or decay) which needs not be physical. The sum over all
components should be physical.
The [[index]] indentifies this component within its parent process.
The actual process component is stored in the [[core]] subobject. We
use a polymorphic subobject instead of an extension of
[[process_component_t]], because the individual entries in the array
of process components can have different types. In short,
[[process_component_t]] is a wrapper for the actual process variants.
If the [[active]] flag is false, we should skip this component. This happens
if the associated process has vanishing matrix element.
The index array [[i_term]] points to the individual terms generated by
this component. The indices refer to the parent process.
The index [[i_mci]] is the index of the MC integrator and parameter set which
are associated to this process component.
<<Process config: public>>=
public :: process_component_t
<<Process config: types>>=
type :: process_component_t
type(process_component_def_t), pointer :: config => null ()
integer :: index = 0
logical :: active = .false.
integer, dimension(:), allocatable :: i_term
integer :: i_mci = 0
class(phs_config_t), allocatable :: phs_config
character(32) :: md5sum_phs = ""
integer :: component_type = COMP_DEFAULT
contains
<<Process config: process component: TBP>>
end type process_component_t
@ %def process_component_t
@ Finalizer. The MCI template may (potentially) need a finalizer. The process
configuration finalizer may include closing an open scratch file.
<<Process config: process component: TBP>>=
procedure :: final => process_component_final
<<Process config: procedures>>=
subroutine process_component_final (object)
class(process_component_t), intent(inout) :: object
if (allocated (object%phs_config)) then
call object%phs_config%final ()
end if
end subroutine process_component_final
@ %def process_component_final
@ The meaning of [[verbose]] depends on the process variant.
<<Process config: process component: TBP>>=
procedure :: write => process_component_write
<<Process config: procedures>>=
subroutine process_component_write (object, unit)
class(process_component_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
if (associated (object%config)) then
write (u, "(1x,A,I0)") "Component #", object%index
call object%config%write (u)
if (object%md5sum_phs /= "") then
write (u, "(3x,A,A,A)") "MD5 sum (phs) = '", &
object%md5sum_phs, "'"
end if
else
write (u, "(1x,A)") "Process component: [not allocated]"
end if
if (.not. object%active) then
write (u, "(1x,A)") "[Inactive]"
return
end if
write (u, "(1x,A)") "Referenced data:"
if (allocated (object%i_term)) then
write (u, "(3x,A,999(1x,I0))") "Terms =", &
object%i_term
else
write (u, "(3x,A)") "Terms = [undefined]"
end if
if (object%i_mci /= 0) then
write (u, "(3x,A,I0)") "MC dataset = ", object%i_mci
else
write (u, "(3x,A)") "MC dataset = [undefined]"
end if
if (allocated (object%phs_config)) then
call object%phs_config%write (u)
end if
end subroutine process_component_write
@ %def process_component_write
@ Initialize the component.
<<Process config: process component: TBP>>=
procedure :: init => process_component_init
<<Process config: procedures>>=
subroutine process_component_init (component, &
i_component, env, meta, config, &
active, &
phs_config_template)
class(process_component_t), intent(out) :: component
integer, intent(in) :: i_component
type(process_environment_t), intent(in) :: env
type(process_metadata_t), intent(in) :: meta
type(process_config_data_t), intent(in) :: config
logical, intent(in) :: active
class(phs_config_t), intent(in), allocatable :: phs_config_template
type(process_constants_t) :: data
component%index = i_component
component%config => &
config%process_def%get_component_def_ptr (i_component)
component%active = active
if (component%active) then
allocate (component%phs_config, source = phs_config_template)
call env%fill_process_constants (meta%id, i_component, data)
call component%phs_config%init (data, config%model)
end if
end subroutine process_component_init
@ %def process_component_init
@
<<Process config: process component: TBP>>=
procedure :: is_active => process_component_is_active
<<Process config: procedures>>=
elemental function process_component_is_active (component) result (active)
logical :: active
class(process_component_t), intent(in) :: component
active = component%active
end function process_component_is_active
@ %def process_component_is_active
@ Finalize the phase-space configuration.
<<Process config: process component: TBP>>=
procedure :: configure_phs => process_component_configure_phs
<<Process config: procedures>>=
subroutine process_component_configure_phs &
(component, sqrts, beam_config, rebuild, &
ignore_mismatch, subdir)
class(process_component_t), intent(inout) :: component
real(default), intent(in) :: sqrts
type(process_beam_config_t), intent(in) :: beam_config
logical, intent(in), optional :: rebuild
logical, intent(in), optional :: ignore_mismatch
type(string_t), intent(in), optional :: subdir
logical :: no_strfun
integer :: nlo_type
no_strfun = beam_config%n_strfun == 0
nlo_type = component%config%get_nlo_type ()
call component%phs_config%configure (sqrts, &
azimuthal_dependence = beam_config%azimuthal_dependence, &
sqrts_fixed = no_strfun, &
cm_frame = beam_config%lab_is_cm_frame .and. no_strfun, &
rebuild = rebuild, ignore_mismatch = ignore_mismatch, &
nlo_type = nlo_type, &
subdir = subdir)
end subroutine process_component_configure_phs
@ %def process_component_configure_phs
@ The process component possesses two MD5 sums: the checksum of the
component definition, which should be available when the component is
initialized, and the phase-space MD5 sum, which is available after
configuration.
<<Process config: process component: TBP>>=
procedure :: compute_md5sum => process_component_compute_md5sum
<<Process config: procedures>>=
subroutine process_component_compute_md5sum (component)
class(process_component_t), intent(inout) :: component
component%md5sum_phs = component%phs_config%get_md5sum ()
end subroutine process_component_compute_md5sum
@ %def process_component_compute_md5sum
@ Match phase-space channels with structure-function channels, where
applicable.
This calls a method of the [[phs_config]] phase-space implementation.
<<Process config: process component: TBP>>=
procedure :: collect_channels => process_component_collect_channels
<<Process config: procedures>>=
subroutine process_component_collect_channels (component, coll)
class(process_component_t), intent(inout) :: component
type(phs_channel_collection_t), intent(inout) :: coll
call component%phs_config%collect_channels (coll)
end subroutine process_component_collect_channels
@ %def process_component_collect_channels
@
<<Process config: process component: TBP>>=
procedure :: get_config => process_component_get_config
<<Process config: procedures>>=
function process_component_get_config (component) &
result (config)
type(process_component_def_t) :: config
class(process_component_t), intent(in) :: component
config = component%config
end function process_component_get_config
@ %def process_component_get_config
@
<<Process config: process component: TBP>>=
procedure :: get_md5sum => process_component_get_md5sum
<<Process config: procedures>>=
pure function process_component_get_md5sum (component) result (md5)
type(string_t) :: md5
class(process_component_t), intent(in) :: component
md5 = component%config%get_md5sum () // component%md5sum_phs
end function process_component_get_md5sum
@ %def process_component_get_md5sum
@ Return the number of phase-space parameters.
<<Process config: process component: TBP>>=
procedure :: get_n_phs_par => process_component_get_n_phs_par
<<Process config: procedures>>=
function process_component_get_n_phs_par (component) result (n_par)
class(process_component_t), intent(in) :: component
integer :: n_par
n_par = component%phs_config%get_n_par ()
end function process_component_get_n_phs_par
@ %def process_component_get_n_phs_par
@
<<Process config: process component: TBP>>=
procedure :: get_phs_config => process_component_get_phs_config
<<Process config: procedures>>=
subroutine process_component_get_phs_config (component, phs_config)
class(process_component_t), intent(in), target :: component
class(phs_config_t), intent(out), pointer :: phs_config
phs_config => component%phs_config
end subroutine process_component_get_phs_config
@ %def process_component_get_phs_config
@
<<Process config: process component: TBP>>=
procedure :: get_nlo_type => process_component_get_nlo_type
<<Process config: procedures>>=
elemental function process_component_get_nlo_type (component) result (nlo_type)
integer :: nlo_type
class(process_component_t), intent(in) :: component
nlo_type = component%config%get_nlo_type ()
end function process_component_get_nlo_type
@ %def process_component_get_nlo_type
@
<<Process config: process component: TBP>>=
procedure :: needs_mci_entry => process_component_needs_mci_entry
<<Process config: procedures>>=
function process_component_needs_mci_entry (component, combined_integration) result (value)
logical :: value
class(process_component_t), intent(in) :: component
logical, intent(in), optional :: combined_integration
value = component%active
if (present (combined_integration)) then
if (combined_integration) &
value = value .and. component%component_type <= COMP_MASTER
end if
end function process_component_needs_mci_entry
@ %def process_component_needs_mci_entry
@
<<Process config: process component: TBP>>=
procedure :: can_be_integrated => process_component_can_be_integrated
<<Process config: procedures>>=
elemental function process_component_can_be_integrated (component) result (active)
logical :: active
class(process_component_t), intent(in) :: component
active = component%config%can_be_integrated ()
end function process_component_can_be_integrated
@ %def process_component_can_be_integrated
@
\subsection{Process terms}
For straightforward tree-level calculations, each process component
corresponds to a unique elementary interaction. However, in the case
of NLO calculations with subtraction terms, a process component may
split into several separate contributions to the scattering, which are
qualified by interactions with distinct kinematics and particle
content. We represent their configuration as [[process_term_t]]
objects, the actual instances will be introduced below as
[[term_instance_t]]. In any case, the process term contains an
elementary interaction with a definite quantum-number and momentum
content.
The index [[i_term_global]] identifies the term relative to the
process.
The index [[i_component]] identifies the process component which
generates this term, relative to the parent process.
The index [[i_term]] identifies the term relative to the process
component (not the process).
The [[data]] subobject holds all process constants.
The number of allowed flavor/helicity/color combinations is stored as
[[n_allowed]]. This is the total number of independent entries in the
density matrix. For each combination, the index of the flavor,
helicity, and color state is stored in the arrays [[flv]], [[hel]],
and [[col]], respectively.
The flag [[rearrange]] is true if we need to rearrange the particles of the
hard interaction, to obtain the effective parton state.
The interaction [[int]] holds the quantum state for the (resolved) hard
interaction, the parent-child relations of the particles, and their momenta.
The momenta are not filled yet; this is postponed to copies of [[int]] which
go into the process instances.
If recombination is in effect, we should allocate [[int_eff]] to describe the
rearranged partonic state.
This type is public only for use in a unit test.
<<Process config: public>>=
public :: process_term_t
<<Process config: types>>=
type :: process_term_t
integer :: i_term_global = 0
integer :: i_component = 0
integer :: i_term = 0
integer :: i_sub = 0
integer :: i_core = 0
integer :: n_allowed = 0
type(process_constants_t) :: data
real(default) :: alpha_s = 0
integer, dimension(:), allocatable :: flv, hel, col
integer :: n_sub, n_sub_color, n_sub_spin
type(interaction_t) :: int
type(interaction_t), pointer :: int_eff => null ()
contains
<<Process config: process term: TBP>>
end type process_term_t
@ %def process_term_t
@ For the output, we skip the process constants and the tables of
allowed quantum numbers. Those can also be read off from the
interaction object.
<<Process config: process term: TBP>>=
procedure :: write => process_term_write
<<Process config: procedures>>=
subroutine process_term_write (term, unit)
class(process_term_t), intent(in) :: term
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A,I0)") "Term #", term%i_term_global
write (u, "(3x,A,I0)") "Process component index = ", &
term%i_component
write (u, "(3x,A,I0)") "Term index w.r.t. component = ", &
term%i_term
call write_separator (u)
write (u, "(1x,A)") "Hard interaction:"
call write_separator (u)
call term%int%basic_write (u)
end subroutine process_term_write
@ %def process_term_write
@ Write an account of all quantum number states and their current status.
<<Process config: process term: TBP>>=
procedure :: write_state_summary => process_term_write_state_summary
<<Process config: procedures>>=
subroutine process_term_write_state_summary (term, core, unit)
class(process_term_t), intent(in) :: term
class(prc_core_t), intent(in) :: core
integer, intent(in), optional :: unit
integer :: u, i, f, h, c
type(state_iterator_t) :: it
character :: sgn
u = given_output_unit (unit)
write (u, "(1x,A,I0)") "Term #", term%i_term_global
call it%init (term%int%get_state_matrix_ptr ())
do while (it%is_valid ())
i = it%get_me_index ()
f = term%flv(i)
h = term%hel(i)
if (allocated (term%col)) then
c = term%col(i)
else
c = 1
end if
if (core%is_allowed (term%i_term, f, h, c)) then
sgn = "+"
else
sgn = " "
end if
write (u, "(1x,A1,1x,I0,2x)", advance="no") sgn, i
call quantum_numbers_write (it%get_quantum_numbers (), u)
write (u, *)
call it%advance ()
end do
end subroutine process_term_write_state_summary
@ %def process_term_write_state_summary
@ Finalizer: the [[int]] and potentially [[int_eff]] components have a
finalizer that we must call.
<<Process config: process term: TBP>>=
procedure :: final => process_term_final
<<Process config: procedures>>=
subroutine process_term_final (term)
class(process_term_t), intent(inout) :: term
call term%int%final ()
end subroutine process_term_final
@ %def process_term_final
@ Initialize the term. We copy the process constants from the [[core]]
object and set up the [[int]] hard interaction accordingly.
The [[alpha_s]] value is useful for writing external event records. This is
the constant value which may be overridden by a event-specific running value.
If the model does not contain the strong coupling, the value is zero.
The [[rearrange]] part is commented out; this or something equivalent
could become relevant for NLO algorithms.
<<Process config: process term: TBP>>=
procedure :: init => process_term_init
<<Process config: procedures>>=
subroutine process_term_init &
(term, i_term_global, i_component, i_term, core, model, &
nlo_type, use_beam_pol, subtraction_method, &
has_pdfs, n_emitters)
class(process_term_t), intent(inout), target :: term
integer, intent(in) :: i_term_global
integer, intent(in) :: i_component
integer, intent(in) :: i_term
class(prc_core_t), intent(inout) :: core
class(model_data_t), intent(in), target :: model
integer, intent(in), optional :: nlo_type
logical, intent(in), optional :: use_beam_pol
type(string_t), intent(in), optional :: subtraction_method
logical, intent(in), optional :: has_pdfs
integer, intent(in), optional :: n_emitters
class(modelpar_data_t), pointer :: alpha_s_ptr
logical :: use_internal_color
term%i_term_global = i_term_global
term%i_component = i_component
term%i_term = i_term
call core%get_constants (term%data, i_term)
alpha_s_ptr => model%get_par_data_ptr (var_str ("alphas"))
if (associated (alpha_s_ptr)) then
term%alpha_s = alpha_s_ptr%get_real ()
else
term%alpha_s = -1
end if
use_internal_color = .false.
if (present (subtraction_method)) &
use_internal_color = (char (subtraction_method) == 'omega') &
.or. (char (subtraction_method) == 'threshold')
call term%setup_interaction (core, model, nlo_type = nlo_type, &
pol_beams = use_beam_pol, use_internal_color = use_internal_color, &
has_pdfs = has_pdfs, n_emitters = n_emitters)
end subroutine process_term_init
@ %def process_term_init
@ We fetch the process constants which determine the quantum numbers and
use those to create the interaction. The interaction contains
incoming and outgoing particles, no virtuals. The incoming particles
are parents of the outgoing ones.
Keeping previous \whizard\ conventions, we invert the color assignment
(but not flavor or helicity) for the incoming particles. When the
color-flow square matrix is evaluated, this inversion is done again,
so in the color-flow sequence we get the color assignments of the
matrix element.
\textbf{Why are these four subtraction entries for structure-function
aware interactions?} Taking the soft or collinear limit of the real-emission
matrix element, the behavior of the parton energy fractions has to be
taken into account. In the pure real case, $x_\oplus$ and $x_\ominus$
are given by
\begin{equation*}
x_\oplus = \frac{\bar{x}_\oplus}{\sqrt{1-\xi}}
\sqrt{\frac{2 - \xi(1-y)}{2 - \xi(1+y)}},
\quad
x_\ominus = \frac{\bar{x}_\ominus}{\sqrt{1-\xi}}
\sqrt{\frac{2 - \xi(1+y)}{2 - \xi(1-y)}}.
\end{equation*}
In the soft limit, $\xi \to 0$, this yields $x_\oplus = \bar{x}_\oplus$
and $x_\ominus = \bar{x}_\ominus$. In the collinear limit, $y \to 1$,
it is $x_\oplus = \bar{x}_\oplus / (1 - \xi)$ and $x_\ominus = \bar{x}_\ominus$.
Likewise, in the anti-collinear limit $y \-o -1$, the inverse relation holds.
We therefore have to distinguish four cases with the PDF assignments
$f(x_\oplus) \cdot f(x_\ominus)$, $f(\bar{x}_\oplus) \cdot f(\bar{x}_\ominus)$,
$f\left(\bar{x}_\oplus / (1-\xi)\right) \cdot f(\bar{x}_\ominus)$ and
$f(\bar{x}_\oplus) \cdot f\left(\bar{x}_\ominus / (1-\xi)\right)$.
The [[n_emitters]] optional argument is provided by the caller if this term
requires spin-correlated matrix elements, and thus involves additional
subtractions.
<<Process config: process term: TBP>>=
procedure :: setup_interaction => process_term_setup_interaction
<<Process config: procedures>>=
subroutine process_term_setup_interaction (term, core, model, &
nlo_type, pol_beams, has_pdfs, use_internal_color, n_emitters)
class(process_term_t), intent(inout) :: term
class(prc_core_t), intent(inout) :: core
class(model_data_t), intent(in), target :: model
logical, intent(in), optional :: pol_beams
logical, intent(in), optional :: has_pdfs
integer, intent(in), optional :: nlo_type
logical, intent(in), optional :: use_internal_color
integer, intent(in), optional :: n_emitters
integer :: n, n_tot
type(flavor_t), dimension(:), allocatable :: flv
type(color_t), dimension(:), allocatable :: col
type(helicity_t), dimension(:), allocatable :: hel
type(quantum_numbers_t), dimension(:), allocatable :: qn
logical :: is_pol, use_color
integer :: nlo_t, n_sub
is_pol = .false.; if (present (pol_beams)) is_pol = pol_beams
nlo_t = BORN; if (present (nlo_type)) nlo_t = nlo_type
n_tot = term%data%n_in + term%data%n_out
call count_number_of_states ()
term%n_allowed = n
call compute_n_sub (n_emitters, has_pdfs)
call fill_quantum_numbers ()
call term%int%basic_init &
(term%data%n_in, 0, term%data%n_out, set_relations = .true.)
select type (core)
class is (prc_blha_t)
call setup_states_blha_olp ()
type is (prc_threshold_t)
call setup_states_threshold ()
class is (prc_external_t)
call setup_states_other_prc_external ()
class default
call setup_states_omega ()
end select
call term%int%freeze ()
contains
subroutine count_number_of_states ()
integer :: f, h, c
n = 0
select type (core)
class is (prc_external_t)
do f = 1, term%data%n_flv
do h = 1, term%data%n_hel
do c = 1, term%data%n_col
n = n + 1
end do
end do
end do
class default !!! Omega and all test cores
do f = 1, term%data%n_flv
do h = 1, term%data%n_hel
do c = 1, term%data%n_col
if (core%is_allowed (term%i_term, f, h, c)) n = n + 1
end do
end do
end do
end select
end subroutine count_number_of_states
subroutine compute_n_sub (n_emitters, has_pdfs)
integer, intent(in), optional :: n_emitters
logical, intent(in), optional :: has_pdfs
logical :: can_have_sub
integer :: n_sub_color, n_sub_spin
use_color = .false.; if (present (use_internal_color)) &
use_color = use_internal_color
can_have_sub = nlo_t == NLO_VIRTUAL .or. &
(nlo_t == NLO_REAL .and. term%i_term_global == term%i_sub) .or. &
nlo_t == NLO_MISMATCH
n_sub_color = 0; n_sub_spin = 0
if (can_have_sub) then
if (.not. use_color) n_sub_color = n_tot * (n_tot - 1) / 2
if (nlo_t == NLO_REAL) then
if (present (n_emitters)) then
n_sub_spin = 16 * n_emitters
end if
end if
end if
n_sub = n_sub_color + n_sub_spin
!!! For the virtual subtraction we also need the finite virtual contribution
!!! corresponding to the $\epsilon^0$-pole
if (nlo_t == NLO_VIRTUAL) n_sub = n_sub + 1
if (present (has_pdfs)) then
if (has_pdfs &
.and. ((nlo_t == NLO_REAL .and. can_have_sub) &
.or. nlo_t == NLO_DGLAP)) then
- n_sub = n_sub + n_beam_structure_int
+ !!! necessary dummy, needs refactoring,
+ !!! c.f. [[term_instance_evaluate_interaction_userdef_tree]]
+ n_sub = n_sub + n_beams_rescaled
end if
end if
term%n_sub = n_sub
term%n_sub_color = n_sub_color
term%n_sub_spin = n_sub_spin
end subroutine compute_n_sub
subroutine fill_quantum_numbers ()
integer :: nn
logical :: can_have_sub
select type (core)
class is (prc_external_t)
can_have_sub = nlo_t == NLO_VIRTUAL .or. &
(nlo_t == NLO_REAL .and. term%i_term_global == term%i_sub) .or. &
nlo_t == NLO_MISMATCH .or. nlo_t == NLO_DGLAP
if (can_have_sub) then
nn = (n_sub + 1) * n
else
nn = n
end if
class default
nn = n
end select
allocate (term%flv (nn), term%col (nn), term%hel (nn))
allocate (flv (n_tot), col (n_tot), hel (n_tot))
allocate (qn (n_tot))
end subroutine fill_quantum_numbers
subroutine setup_states_blha_olp ()
integer :: s, f, c, h, i
i = 0
associate (data => term%data)
do s = 0, n_sub
do f = 1, data%n_flv
do h = 1, data%n_hel
do c = 1, data%n_col
i = i + 1
term%flv(i) = f
term%hel(i) = h
!!! Dummy-initialization of color
term%col(i) = c
call flv%init (data%flv_state (:,f), model)
call color_init_from_array (col, &
data%col_state(:,:,c), data%ghost_flag(:,c))
call col(1:data%n_in)%invert ()
if (is_pol) then
select type (core)
type is (prc_openloops_t)
call hel%init (data%hel_state (:,h))
call qn%init (flv, hel, col, s)
class default
call msg_fatal ("Polarized beams only supported by OpenLoops")
end select
else
call qn%init (flv, col, s)
end if
call qn%tag_hard_process ()
call term%int%add_state (qn)
end do
end do
end do
end do
end associate
end subroutine setup_states_blha_olp
subroutine setup_states_threshold ()
integer :: s, f, c, h, i
i = 0
n_sub = 0; if (nlo_t == NLO_VIRTUAL) n_sub = 1
associate (data => term%data)
do s = 0, n_sub
do f = 1, term%data%n_flv
do h = 1, data%n_hel
do c = 1, data%n_col
i = i + 1
term%flv(i) = f
term%hel(i) = h
!!! Dummy-initialization of color
term%col(i) = 1
call flv%init (term%data%flv_state (:,f), model)
if (is_pol) then
call hel%init (data%hel_state (:,h))
call qn%init (flv, hel, s)
else
call qn%init (flv, s)
end if
call qn%tag_hard_process ()
call term%int%add_state (qn)
end do
end do
end do
end do
end associate
end subroutine setup_states_threshold
subroutine setup_states_other_prc_external ()
integer :: s, f, i, c, h
if (is_pol) &
call msg_fatal ("Polarized beams only supported by OpenLoops")
i = 0
!!! n_sub = 0; if (nlo_t == NLO_VIRTUAL) n_sub = 1
associate (data => term%data)
do s = 0, n_sub
do f = 1, data%n_flv
do h = 1, data%n_hel
do c = 1, data%n_col
i = i + 1
term%flv(i) = f
term%hel(i) = h
!!! Dummy-initialization of color
term%col(i) = c
call flv%init (data%flv_state (:,f), model)
call color_init_from_array (col, &
data%col_state(:,:,c), data%ghost_flag(:,c))
call col(1:data%n_in)%invert ()
call qn%init (flv, col, s)
call qn%tag_hard_process ()
call term%int%add_state (qn)
end do
end do
end do
end do
end associate
end subroutine setup_states_other_prc_external
subroutine setup_states_omega ()
integer :: f, h, c, i
i = 0
associate (data => term%data)
do f = 1, data%n_flv
do h = 1, data%n_hel
do c = 1, data%n_col
if (core%is_allowed (term%i_term, f, h, c)) then
i = i + 1
term%flv(i) = f
term%hel(i) = h
term%col(i) = c
call flv%init (data%flv_state(:,f), model)
call color_init_from_array (col, &
data%col_state(:,:,c), &
data%ghost_flag(:,c))
call col(:data%n_in)%invert ()
call hel%init (data%hel_state(:,h))
call qn%init (flv, col, hel)
call qn%tag_hard_process ()
call term%int%add_state (qn)
end if
end do
end do
end do
end associate
end subroutine setup_states_omega
end subroutine process_term_setup_interaction
@ %def process_term_setup_interaction
@
<<Process config: process term: TBP>>=
procedure :: get_process_constants => process_term_get_process_constants
<<Process config: procedures>>=
subroutine process_term_get_process_constants &
(term, prc_constants)
class(process_term_t), intent(inout) :: term
type(process_constants_t), intent(out) :: prc_constants
prc_constants = term%data
end subroutine process_term_get_process_constants
@ %def process_term_get_process_constants
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Process call statistics}
Very simple object for statistics. Could be moved to a more basic chapter.
<<[[process_counter.f90]]>>=
<<File header>>
module process_counter
use io_units
<<Standard module head>>
<<Process counter: public>>
<<Process counter: parameters>>
<<Process counter: types>>
contains
<<Process counter: procedures>>
end module process_counter
@ %def process_counter
@ This object can record process calls, categorized by evaluation
status. It is a part of the [[mci_entry]] component below.
<<Process counter: public>>=
public :: process_counter_t
<<Process counter: types>>=
type :: process_counter_t
integer :: total = 0
integer :: failed_kinematics = 0
integer :: failed_cuts = 0
integer :: has_passed = 0
integer :: evaluated = 0
integer :: complete = 0
contains
<<Process counter: process counter: TBP>>
end type process_counter_t
@ %def process_counter_t
@ Here are the corresponding numeric codes:
<<Process counter: parameters>>=
integer, parameter, public :: STAT_UNDEFINED = 0
integer, parameter, public :: STAT_INITIAL = 1
integer, parameter, public :: STAT_ACTIVATED = 2
integer, parameter, public :: STAT_BEAM_MOMENTA = 3
integer, parameter, public :: STAT_FAILED_KINEMATICS = 4
integer, parameter, public :: STAT_SEED_KINEMATICS = 5
integer, parameter, public :: STAT_HARD_KINEMATICS = 6
integer, parameter, public :: STAT_EFF_KINEMATICS = 7
integer, parameter, public :: STAT_FAILED_CUTS = 8
integer, parameter, public :: STAT_PASSED_CUTS = 9
integer, parameter, public :: STAT_EVALUATED_TRACE = 10
integer, parameter, public :: STAT_EVENT_COMPLETE = 11
@ %def STAT_UNDEFINED STAT_INITIAL STAT_ACTIVATED
@ %def STAT_BEAM_MOMENTA STAT_FAILED_KINEMATICS
@ %def STAT_SEED_KINEMATICS STAT_HARD_KINEMATICS STAT_EFF_KINEMATICS
@ %def STAT_EVALUATED_TRACE STAT_EVENT_COMPLETE
@ Output.
<<Process counter: process counter: TBP>>=
procedure :: write => process_counter_write
<<Process counter: procedures>>=
subroutine process_counter_write (object, unit)
class(process_counter_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
if (object%total > 0) then
write (u, "(1x,A)") "Call statistics (current run):"
write (u, "(3x,A,I0)") "total = ", object%total
write (u, "(3x,A,I0)") "failed kin. = ", object%failed_kinematics
write (u, "(3x,A,I0)") "failed cuts = ", object%failed_cuts
write (u, "(3x,A,I0)") "passed cuts = ", object%has_passed
write (u, "(3x,A,I0)") "evaluated = ", object%evaluated
else
write (u, "(1x,A)") "Call statistics (current run): [no calls]"
end if
end subroutine process_counter_write
@ %def process_counter_write
@ Reset. Just enforce default initialization.
<<Process counter: process counter: TBP>>=
procedure :: reset => process_counter_reset
<<Process counter: procedures>>=
subroutine process_counter_reset (counter)
class(process_counter_t), intent(out) :: counter
counter%total = 0
counter%failed_kinematics = 0
counter%failed_cuts = 0
counter%has_passed = 0
counter%evaluated = 0
counter%complete = 0
end subroutine process_counter_reset
@ %def process_counter_reset
@ We record an event according to the lowest status code greater or
equal to the actual status. This is actually done by the process
instance; the process object just copies the instance counter.
<<Process counter: process counter: TBP>>=
procedure :: record => process_counter_record
<<Process counter: procedures>>=
subroutine process_counter_record (counter, status)
class(process_counter_t), intent(inout) :: counter
integer, intent(in) :: status
if (status <= STAT_FAILED_KINEMATICS) then
counter%failed_kinematics = counter%failed_kinematics + 1
else if (status <= STAT_FAILED_CUTS) then
counter%failed_cuts = counter%failed_cuts + 1
else if (status <= STAT_PASSED_CUTS) then
counter%has_passed = counter%has_passed + 1
else
counter%evaluated = counter%evaluated + 1
end if
counter%total = counter%total + 1
end subroutine process_counter_record
@ %def process_counter_record
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Multi-channel integration}
<<[[process_mci.f90]]>>=
<<File header>>
module process_mci
<<Use kinds>>
<<Use strings>>
<<Use debug>>
use io_units
use diagnostics
use physics_defs
use md5
use cputime
use rng_base
use mci_base
use variables
use integration_results
use process_libraries
use phs_base
use process_counter
use process_config
<<Standard module head>>
<<Process mci: public>>
<<Process mci: parameters>>
<<Process mci: types>>
contains
<<Process mci: procedures>>
end module process_mci
@ %def process_mci
\subsection{Process MCI entry}
The [[process_mci_entry_t]] block contains, for each process component that is
integrated independently, the configuration data for its MC input parameters.
Each input parameter set is handled by a [[mci_t]] integrator.
The MC input parameter set is broken down into the parameters required by the
structure-function chain and the parameters required by the phase space of the
elementary process.
The MD5 sum collects all information about the associated processes
that may affect the integration. It does not contain the MCI object
itself or integration results.
MC integration is organized in passes. Each pass may consist of
several iterations, and for each iteration there is a number of
calls. We store explicitly the values that apply to the current
pass. Previous values are archived in the [[results]] object.
The [[counter]] receives the counter statistics from the associated
process instance, for diagnostics.
The [[results]] object records results, broken down in passes and iterations.
<<Process mci: public>>=
public :: process_mci_entry_t
<<Process mci: types>>=
type :: process_mci_entry_t
integer :: i_mci = 0
integer, dimension(:), allocatable :: i_component
integer :: process_type = PRC_UNKNOWN
integer :: n_par = 0
integer :: n_par_sf = 0
integer :: n_par_phs = 0
character(32) :: md5sum = ""
integer :: pass = 0
integer :: n_it = 0
integer :: n_calls = 0
logical :: activate_timer = .false.
real(default) :: error_threshold = 0
class(mci_t), allocatable :: mci
type(process_counter_t) :: counter
type(integration_results_t) :: results
logical :: negative_weights = .false.
logical :: combined_integration = .false.
integer :: real_partition_type = REAL_FULL
integer :: associated_real_component = 0
contains
<<Process mci: process mci entry: TBP>>
end type process_mci_entry_t
@ %def process_mci_entry_t
@ Finalizer for the [[mci]] component.
<<Process mci: process mci entry: TBP>>=
procedure :: final => process_mci_entry_final
<<Process mci: procedures>>=
subroutine process_mci_entry_final (object)
class(process_mci_entry_t), intent(inout) :: object
if (allocated (object%mci)) call object%mci%final ()
end subroutine process_mci_entry_final
@ %def process_mci_entry_final
@ Output. Write pass/iteration information only if set (the pass
index is nonzero). Write the MCI block only if it exists (for some
self-tests it does not). Write results only if there are any.
<<Process mci: process mci entry: TBP>>=
procedure :: write => process_mci_entry_write
<<Process mci: procedures>>=
subroutine process_mci_entry_write (object, unit, pacify)
class(process_mci_entry_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: pacify
integer :: u
u = given_output_unit (unit)
write (u, "(3x,A,I0)") "Associated components = ", object%i_component
write (u, "(3x,A,I0)") "MC input parameters = ", object%n_par
write (u, "(3x,A,I0)") "MC parameters (SF) = ", object%n_par_sf
write (u, "(3x,A,I0)") "MC parameters (PHS) = ", object%n_par_phs
if (object%pass > 0) then
write (u, "(3x,A,I0)") "Current pass = ", object%pass
write (u, "(3x,A,I0)") "Number of iterations = ", object%n_it
write (u, "(3x,A,I0)") "Number of calls = ", object%n_calls
end if
if (object%md5sum /= "") then
write (u, "(3x,A,A,A)") "MD5 sum (components) = '", object%md5sum, "'"
end if
if (allocated (object%mci)) then
call object%mci%write (u)
end if
call object%counter%write (u)
if (object%results%exist ()) then
call object%results%write (u, suppress = pacify)
call object%results%write_chain_weights (u)
end if
end subroutine process_mci_entry_write
@ %def process_mci_entry_write
@ Configure the MCI entry. This is intent(inout) since some specific settings
may be done before this. The actual [[mci_t]] object is an instance of the
[[mci_template]] argument, which determines the concrete types.
In a unit-test context, the [[mci_template]] argument may be unallocated.
We obtain the number of channels and the number of parameters, separately for
the structure-function chain and for the associated process component. We
assume that the phase-space object has already been configured.
We assume that there is only one process component directly associated with a
MCI entry.
<<Process mci: process mci entry: TBP>>=
procedure :: configure => process_mci_entry_configure
<<Process mci: procedures>>=
subroutine process_mci_entry_configure (mci_entry, mci_template, &
process_type, i_mci, i_component, component, &
n_sfpar, rng_factory)
class(process_mci_entry_t), intent(inout) :: mci_entry
class(mci_t), intent(in), allocatable :: mci_template
integer, intent(in) :: process_type
integer, intent(in) :: i_mci
integer, intent(in) :: i_component
type(process_component_t), intent(in), target :: component
integer, intent(in) :: n_sfpar
class(rng_factory_t), intent(inout) :: rng_factory
class(rng_t), allocatable :: rng
associate (phs_config => component%phs_config)
mci_entry%i_mci = i_mci
call mci_entry%create_component_list (i_component, component%get_config ())
mci_entry%n_par_sf = n_sfpar
mci_entry%n_par_phs = phs_config%get_n_par ()
mci_entry%n_par = mci_entry%n_par_sf + mci_entry%n_par_phs
mci_entry%process_type = process_type
if (allocated (mci_template)) then
allocate (mci_entry%mci, source = mci_template)
call mci_entry%mci%record_index (mci_entry%i_mci)
call mci_entry%mci%set_dimensions &
(mci_entry%n_par, phs_config%get_n_channel ())
call mci_entry%mci%declare_flat_dimensions &
(phs_config%get_flat_dimensions ())
if (phs_config%provides_equivalences) then
call mci_entry%mci%declare_equivalences &
(phs_config%channel, mci_entry%n_par_sf)
end if
if (phs_config%provides_chains) then
call mci_entry%mci%declare_chains (phs_config%chain)
end if
call rng_factory%make (rng)
call mci_entry%mci%import_rng (rng)
end if
call mci_entry%results%init (process_type)
end associate
end subroutine process_mci_entry_configure
@ %def process_mci_entry_configure
@
<<Process mci: parameters>>=
integer, parameter, public :: REAL_FULL = 0
integer, parameter, public :: REAL_SINGULAR = 1
integer, parameter, public :: REAL_FINITE = 2
@
<<Process mci: process mci entry: TBP>>=
procedure :: create_component_list => &
process_mci_entry_create_component_list
<<Process mci: procedures>>=
subroutine process_mci_entry_create_component_list (mci_entry, &
i_component, component_config)
class (process_mci_entry_t), intent(inout) :: mci_entry
integer, intent(in) :: i_component
type(process_component_def_t), intent(in) :: component_config
integer, dimension(:), allocatable :: i_list
integer :: n
integer, save :: i_rfin_offset = 0
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, "process_mci_entry_create_component_list")
if (mci_entry%combined_integration) then
n = get_n_components (mci_entry%real_partition_type)
allocate (i_list (n))
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, &
"mci_entry%real_partition_type", mci_entry%real_partition_type)
select case (mci_entry%real_partition_type)
case (REAL_FULL)
i_list = component_config%get_association_list ()
allocate (mci_entry%i_component (size (i_list)))
mci_entry%i_component = i_list
case (REAL_SINGULAR)
i_list = component_config%get_association_list (ASSOCIATED_REAL_FIN)
allocate (mci_entry%i_component (size(i_list)))
mci_entry%i_component = i_list
case (REAL_FINITE)
allocate (mci_entry%i_component (1))
mci_entry%i_component(1) = &
component_config%get_associated_real_fin () + i_rfin_offset
i_rfin_offset = i_rfin_offset + 1
end select
else
allocate (mci_entry%i_component (1))
mci_entry%i_component(1) = i_component
end if
contains
function get_n_components (damping_type) result (n_components)
integer :: n_components
integer, intent(in) :: damping_type
select case (damping_type)
case (REAL_FULL)
n_components = size (component_config%get_association_list ())
case (REAL_SINGULAR)
n_components = size (component_config%get_association_list &
(ASSOCIATED_REAL_FIN))
end select
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, "n_components", n_components)
end function get_n_components
end subroutine process_mci_entry_create_component_list
@ %def process_mci_entry_create_component_list
@
<<Process mci: process mci entry: TBP>>=
procedure :: set_associated_real_component &
=> process_mci_entry_set_associated_real_component
<<Process mci: procedures>>=
subroutine process_mci_entry_set_associated_real_component (mci_entry, i)
class(process_mci_entry_t), intent(inout) :: mci_entry
integer, intent(in) :: i
mci_entry%associated_real_component = i
end subroutine process_mci_entry_set_associated_real_component
@ %def process_mci_entry_set_associated_real_component
@ Set some additional parameters.
<<Process mci: process mci entry: TBP>>=
procedure :: set_parameters => process_mci_entry_set_parameters
<<Process mci: procedures>>=
subroutine process_mci_entry_set_parameters (mci_entry, var_list)
class(process_mci_entry_t), intent(inout) :: mci_entry
type(var_list_t), intent(in) :: var_list
integer :: integration_results_verbosity
real(default) :: error_threshold
integration_results_verbosity = &
var_list%get_ival (var_str ("integration_results_verbosity"))
error_threshold = &
var_list%get_rval (var_str ("error_threshold"))
mci_entry%activate_timer = &
var_list%get_lval (var_str ("?integration_timer"))
call mci_entry%results%set_verbosity (integration_results_verbosity)
call mci_entry%results%set_error_threshold (error_threshold)
end subroutine process_mci_entry_set_parameters
@ %def process_mci_entry_set_parameters
@ Compute an MD5 sum that summarizes all information that could
influence integration results, for the associated process components.
We take the process-configuration MD5 sum which represents parameters,
cuts, etc., the MD5 sums for the process component definitions and
their phase space objects (which should be configured), and the beam
configuration MD5 sum. (The QCD setup is included in the process
configuration data MD5 sum.)
Done only once, when the MD5 sum is still empty.
<<Process mci: process mci entry: TBP>>=
procedure :: compute_md5sum => process_mci_entry_compute_md5sum
<<Process mci: procedures>>=
subroutine process_mci_entry_compute_md5sum (mci_entry, &
config, component, beam_config)
class(process_mci_entry_t), intent(inout) :: mci_entry
type(process_config_data_t), intent(in) :: config
type(process_component_t), dimension(:), intent(in) :: component
type(process_beam_config_t), intent(in) :: beam_config
type(string_t) :: buffer
integer :: i
if (mci_entry%md5sum == "") then
buffer = config%get_md5sum () // beam_config%get_md5sum ()
do i = 1, size (component)
if (component(i)%is_active ()) then
buffer = buffer // component(i)%get_md5sum ()
end if
end do
mci_entry%md5sum = md5sum (char (buffer))
end if
if (allocated (mci_entry%mci)) then
call mci_entry%mci%set_md5sum (mci_entry%md5sum)
end if
end subroutine process_mci_entry_compute_md5sum
@ %def process_mci_entry_compute_md5sum
@ Test the MCI sampler by calling it a given number of time, discarding the
results. The instance should be initialized.
The [[mci_entry]] is [[intent(inout)]] because the integrator contains
the random-number state.
<<Process mci: process mci entry: TBP>>=
procedure :: sampler_test => process_mci_entry_sampler_test
<<Process mci: procedures>>=
subroutine process_mci_entry_sampler_test (mci_entry, mci_sampler, n_calls)
class(process_mci_entry_t), intent(inout) :: mci_entry
class(mci_sampler_t), intent(inout), target :: mci_sampler
integer, intent(in) :: n_calls
call mci_entry%mci%sampler_test (mci_sampler, n_calls)
end subroutine process_mci_entry_sampler_test
@ %def process_mci_entry_sampler_test
@ Integrate.
The [[integrate]] method counts as an integration pass; the pass count is
increased by one. We transfer the pass parameters (number of iterations and
number of calls) to the actual integration routine.
The [[mci_entry]] is [[intent(inout)]] because the integrator contains
the random-number state.
Note: The results are written to screen and to logfile. This behavior
is hardcoded.
<<Process mci: process mci entry: TBP>>=
procedure :: integrate => process_mci_entry_integrate
procedure :: final_integration => process_mci_entry_final_integration
<<Process mci: procedures>>=
subroutine process_mci_entry_integrate (mci_entry, mci_instance, &
mci_sampler, n_it, n_calls, &
adapt_grids, adapt_weights, final, pacify, &
nlo_type)
class(process_mci_entry_t), intent(inout) :: mci_entry
class(mci_instance_t), intent(inout) :: mci_instance
class(mci_sampler_t), intent(inout) :: mci_sampler
integer, intent(in) :: n_it
integer, intent(in) :: n_calls
logical, intent(in), optional :: adapt_grids
logical, intent(in), optional :: adapt_weights
logical, intent(in), optional :: final, pacify
integer, intent(in), optional :: nlo_type
integer :: u_log
u_log = logfile_unit ()
mci_entry%pass = mci_entry%pass + 1
mci_entry%n_it = n_it
mci_entry%n_calls = n_calls
if (mci_entry%pass == 1) &
call mci_entry%mci%startup_message (n_calls = n_calls)
call mci_entry%mci%set_timer (active = mci_entry%activate_timer)
call mci_entry%results%display_init (screen = .true., unit = u_log)
call mci_entry%results%new_pass ()
if (present (nlo_type)) then
select case (nlo_type)
case (NLO_VIRTUAL, NLO_REAL, NLO_MISMATCH, NLO_DGLAP)
mci_instance%negative_weights = .true.
end select
end if
call mci_entry%mci%add_pass (adapt_grids, adapt_weights, final)
call mci_entry%mci%start_timer ()
call mci_entry%mci%integrate (mci_instance, mci_sampler, n_it, &
n_calls, mci_entry%results, pacify = pacify)
call mci_entry%mci%stop_timer ()
if (signal_is_pending ()) return
end subroutine process_mci_entry_integrate
subroutine process_mci_entry_final_integration (mci_entry)
class(process_mci_entry_t), intent(inout) :: mci_entry
call mci_entry%results%display_final ()
call mci_entry%time_message ()
end subroutine process_mci_entry_final_integration
@ %def process_mci_entry_integrate
@ %def process_mci_entry_final_integration
@ If appropriate, issue an informative message about the expected time
for an event sample.
<<Process mci: process mci entry: TBP>>=
procedure :: get_time => process_mci_entry_get_time
procedure :: time_message => process_mci_entry_time_message
<<Process mci: procedures>>=
subroutine process_mci_entry_get_time (mci_entry, time, sample)
class(process_mci_entry_t), intent(in) :: mci_entry
type(time_t), intent(out) :: time
integer, intent(in) :: sample
real(default) :: time_last_pass, efficiency, calls
time_last_pass = mci_entry%mci%get_time ()
calls = mci_entry%results%get_n_calls ()
efficiency = mci_entry%mci%get_efficiency ()
if (time_last_pass > 0 .and. calls > 0 .and. efficiency > 0) then
time = nint (time_last_pass / calls / efficiency * sample)
end if
end subroutine process_mci_entry_get_time
subroutine process_mci_entry_time_message (mci_entry)
class(process_mci_entry_t), intent(in) :: mci_entry
type(time_t) :: time
integer :: sample
sample = 10000
call mci_entry%get_time (time, sample)
if (time%is_known ()) then
call msg_message ("Time estimate for generating 10000 events: " &
// char (time%to_string_dhms ()))
end if
end subroutine process_mci_entry_time_message
@ %def process_mci_entry_time_message
@ Prepare event generation. (For the test integrator, this does nothing. It
is relevant for the VAMP integrator.)
<<Process mci: process mci entry: TBP>>=
procedure :: prepare_simulation => process_mci_entry_prepare_simulation
<<Process mci: procedures>>=
subroutine process_mci_entry_prepare_simulation (mci_entry)
class(process_mci_entry_t), intent(inout) :: mci_entry
call mci_entry%mci%prepare_simulation ()
end subroutine process_mci_entry_prepare_simulation
@ %def process_mci_entry_prepare_simulation
@ Generate an event. The instance should be initialized,
otherwise event generation is directed by the [[mci]] integrator
subobject. The integrator instance is contained in a [[mci_work]]
subobject of the process instance, which simultaneously serves as the
sampler object. (We avoid the anti-aliasing rules if we assume that
the sampling itself does not involve the integrator instance contained in the
process instance.)
Regarding weighted events, we only take events which are valid, which
means that they have valid kinematics and have passed cuts.
Therefore, we have a rejection loop. For unweighted events, the
unweighting routine should already take care of this.
The [[keep_failed]] flag determines whether events which failed cuts
are nevertheless produced, to be recorded with zero weight.
Alternatively, failed events are dropped, and this fact is recorded by
the counter [[n_dropped]].
<<Process mci: process mci entry: TBP>>=
procedure :: generate_weighted_event => &
process_mci_entry_generate_weighted_event
procedure :: generate_unweighted_event => &
process_mci_entry_generate_unweighted_event
<<Process mci: procedures>>=
subroutine process_mci_entry_generate_weighted_event (mci_entry, &
mci_instance, mci_sampler, keep_failed)
class(process_mci_entry_t), intent(inout) :: mci_entry
class(mci_instance_t), intent(inout) :: mci_instance
class(mci_sampler_t), intent(inout) :: mci_sampler
logical, intent(in) :: keep_failed
logical :: generate_new
generate_new = .true.
call mci_instance%reset_n_event_dropped ()
REJECTION: do while (generate_new)
call mci_entry%mci%generate_weighted_event (mci_instance, mci_sampler)
if (signal_is_pending ()) return
if (.not. mci_sampler%is_valid()) then
if (keep_failed) then
generate_new = .false.
else
call mci_instance%record_event_dropped ()
generate_new = .true.
end if
else
generate_new = .false.
end if
end do REJECTION
end subroutine process_mci_entry_generate_weighted_event
subroutine process_mci_entry_generate_unweighted_event (mci_entry, mci_instance, mci_sampler)
class(process_mci_entry_t), intent(inout) :: mci_entry
class(mci_instance_t), intent(inout) :: mci_instance
class(mci_sampler_t), intent(inout) :: mci_sampler
call mci_entry%mci%generate_unweighted_event (mci_instance, mci_sampler)
end subroutine process_mci_entry_generate_unweighted_event
@ %def process_mci_entry_generate_weighted_event
@ %def process_mci_entry_generate_unweighted_event
@ Extract results.
<<Process mci: process mci entry: TBP>>=
procedure :: has_integral => process_mci_entry_has_integral
procedure :: get_integral => process_mci_entry_get_integral
procedure :: get_error => process_mci_entry_get_error
procedure :: get_accuracy => process_mci_entry_get_accuracy
procedure :: get_chi2 => process_mci_entry_get_chi2
procedure :: get_efficiency => process_mci_entry_get_efficiency
<<Process mci: procedures>>=
function process_mci_entry_has_integral (mci_entry) result (flag)
class(process_mci_entry_t), intent(in) :: mci_entry
logical :: flag
flag = mci_entry%results%exist ()
end function process_mci_entry_has_integral
function process_mci_entry_get_integral (mci_entry) result (integral)
class(process_mci_entry_t), intent(in) :: mci_entry
real(default) :: integral
integral = mci_entry%results%get_integral ()
end function process_mci_entry_get_integral
function process_mci_entry_get_error (mci_entry) result (error)
class(process_mci_entry_t), intent(in) :: mci_entry
real(default) :: error
error = mci_entry%results%get_error ()
end function process_mci_entry_get_error
function process_mci_entry_get_accuracy (mci_entry) result (accuracy)
class(process_mci_entry_t), intent(in) :: mci_entry
real(default) :: accuracy
accuracy = mci_entry%results%get_accuracy ()
end function process_mci_entry_get_accuracy
function process_mci_entry_get_chi2 (mci_entry) result (chi2)
class(process_mci_entry_t), intent(in) :: mci_entry
real(default) :: chi2
chi2 = mci_entry%results%get_chi2 ()
end function process_mci_entry_get_chi2
function process_mci_entry_get_efficiency (mci_entry) result (efficiency)
class(process_mci_entry_t), intent(in) :: mci_entry
real(default) :: efficiency
efficiency = mci_entry%results%get_efficiency ()
end function process_mci_entry_get_efficiency
@ %def process_mci_entry_get_integral process_mci_entry_get_error
@ %def process_mci_entry_get_accuracy process_mci_entry_get_chi2
@ %def process_mci_entry_get_efficiency
@ Return the MCI checksum. This may be the one used for
configuration, but may also incorporate results, if they change the
state of the integrator (adaptation).
<<Process mci: process mci entry: TBP>>=
procedure :: get_md5sum => process_mci_entry_get_md5sum
<<Process mci: procedures>>=
pure function process_mci_entry_get_md5sum (entry) result (md5sum)
class(process_mci_entry_t), intent(in) :: entry
character(32) :: md5sum
md5sum = entry%mci%get_md5sum ()
end function process_mci_entry_get_md5sum
@ %def process_mci_entry_get_md5sum
@
\subsection{MC parameter set and MCI instance}
For each process component that is associated with a multi-channel integration
(MCI) object, the [[mci_work_t]] object contains the currently active
parameter set. It also holds the implementation of the [[mci_instance_t]]
that the integrator needs for doing its work.
<<Process mci: public>>=
public :: mci_work_t
<<Process mci: types>>=
type :: mci_work_t
type(process_mci_entry_t), pointer :: config => null ()
real(default), dimension(:), allocatable :: x
class(mci_instance_t), pointer :: mci => null ()
type(process_counter_t) :: counter
logical :: keep_failed_events = .false.
integer :: n_event_dropped = 0
contains
<<Process mci: mci work: TBP>>
end type mci_work_t
@ %def mci_work_t
@ First write configuration data, then the current values.
<<Process mci: mci work: TBP>>=
procedure :: write => mci_work_write
<<Process mci: procedures>>=
subroutine mci_work_write (mci_work, unit, testflag)
class(mci_work_t), intent(in) :: mci_work
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u, i
u = given_output_unit (unit)
write (u, "(1x,A,I0,A)") "Active MCI instance #", &
mci_work%config%i_mci, " ="
write (u, "(2x)", advance="no")
do i = 1, mci_work%config%n_par
write (u, "(1x,F7.5)", advance="no") mci_work%x(i)
if (i == mci_work%config%n_par_sf) &
write (u, "(1x,'|')", advance="no")
end do
write (u, *)
if (associated (mci_work%mci)) then
call mci_work%mci%write (u, pacify = testflag)
call mci_work%counter%write (u)
end if
end subroutine mci_work_write
@ %def mci_work_write
@ The [[mci]] component may require finalization.
<<Process mci: mci work: TBP>>=
procedure :: final => mci_work_final
<<Process mci: procedures>>=
subroutine mci_work_final (mci_work)
class(mci_work_t), intent(inout) :: mci_work
if (associated (mci_work%mci)) then
call mci_work%mci%final ()
deallocate (mci_work%mci)
end if
end subroutine mci_work_final
@ %def mci_work_final
@ Initialize with the maximum length that we will need. Contents are
not initialized.
The integrator inside the [[mci_entry]] object is responsible for
allocating and initializing its own instance, which is referred to by
a pointer in the [[mci_work]] object.
<<Process mci: mci work: TBP>>=
procedure :: init => mci_work_init
<<Process mci: procedures>>=
subroutine mci_work_init (mci_work, mci_entry)
class(mci_work_t), intent(out) :: mci_work
type(process_mci_entry_t), intent(in), target :: mci_entry
mci_work%config => mci_entry
allocate (mci_work%x (mci_entry%n_par))
if (allocated (mci_entry%mci)) then
call mci_entry%mci%allocate_instance (mci_work%mci)
call mci_work%mci%init (mci_entry%mci)
end if
end subroutine mci_work_init
@ %def mci_work_init
@ Set parameters explicitly, either all at once, or separately for the
structure-function and process parts.
<<Process mci: mci work: TBP>>=
procedure :: set => mci_work_set
procedure :: set_x_strfun => mci_work_set_x_strfun
procedure :: set_x_process => mci_work_set_x_process
<<Process mci: procedures>>=
subroutine mci_work_set (mci_work, x)
class(mci_work_t), intent(inout) :: mci_work
real(default), dimension(:), intent(in) :: x
mci_work%x = x
end subroutine mci_work_set
subroutine mci_work_set_x_strfun (mci_work, x)
class(mci_work_t), intent(inout) :: mci_work
real(default), dimension(:), intent(in) :: x
mci_work%x(1 : mci_work%config%n_par_sf) = x
end subroutine mci_work_set_x_strfun
subroutine mci_work_set_x_process (mci_work, x)
class(mci_work_t), intent(inout) :: mci_work
real(default), dimension(:), intent(in) :: x
mci_work%x(mci_work%config%n_par_sf + 1 : mci_work%config%n_par) = x
end subroutine mci_work_set_x_process
@ %def mci_work_set
@ %def mci_work_set_x_strfun
@ %def mci_work_set_x_process
@ Return the array of active components, i.e., those that correspond
to the currently selected MC parameter set.
<<Process mci: mci work: TBP>>=
procedure :: get_active_components => mci_work_get_active_components
<<Process mci: procedures>>=
function mci_work_get_active_components (mci_work) result (i_component)
class(mci_work_t), intent(in) :: mci_work
integer, dimension(:), allocatable :: i_component
allocate (i_component (size (mci_work%config%i_component)))
i_component = mci_work%config%i_component
end function mci_work_get_active_components
@ %def mci_work_get_active_components
@ Return the active parameters as a simple array with correct length.
Do this separately for the structure-function parameters and the
process parameters.
<<Process mci: mci work: TBP>>=
procedure :: get_x_strfun => mci_work_get_x_strfun
procedure :: get_x_process => mci_work_get_x_process
<<Process mci: procedures>>=
pure function mci_work_get_x_strfun (mci_work) result (x)
class(mci_work_t), intent(in) :: mci_work
real(default), dimension(mci_work%config%n_par_sf) :: x
x = mci_work%x(1 : mci_work%config%n_par_sf)
end function mci_work_get_x_strfun
pure function mci_work_get_x_process (mci_work) result (x)
class(mci_work_t), intent(in) :: mci_work
real(default), dimension(mci_work%config%n_par_phs) :: x
x = mci_work%x(mci_work%config%n_par_sf + 1 : mci_work%config%n_par)
end function mci_work_get_x_process
@ %def mci_work_get_x_strfun
@ %def mci_work_get_x_process
@ Initialize and finalize event generation for the specified MCI
entry. This also resets the counter.
<<Process mci: mci work: TBP>>=
procedure :: init_simulation => mci_work_init_simulation
procedure :: final_simulation => mci_work_final_simulation
<<Process mci: procedures>>=
subroutine mci_work_init_simulation (mci_work, safety_factor, keep_failed_events)
class(mci_work_t), intent(inout) :: mci_work
real(default), intent(in), optional :: safety_factor
logical, intent(in), optional :: keep_failed_events
call mci_work%mci%init_simulation (safety_factor)
call mci_work%counter%reset ()
if (present (keep_failed_events)) &
mci_work%keep_failed_events = keep_failed_events
end subroutine mci_work_init_simulation
subroutine mci_work_final_simulation (mci_work)
class(mci_work_t), intent(inout) :: mci_work
call mci_work%mci%final_simulation ()
end subroutine mci_work_final_simulation
@ %def mci_work_init_simulation
@ %def mci_work_final_simulation
@ Counter.
<<Process mci: mci work: TBP>>=
procedure :: reset_counter => mci_work_reset_counter
procedure :: record_call => mci_work_record_call
procedure :: get_counter => mci_work_get_counter
<<Process mci: procedures>>=
subroutine mci_work_reset_counter (mci_work)
class(mci_work_t), intent(inout) :: mci_work
call mci_work%counter%reset ()
end subroutine mci_work_reset_counter
subroutine mci_work_record_call (mci_work, status)
class(mci_work_t), intent(inout) :: mci_work
integer, intent(in) :: status
call mci_work%counter%record (status)
end subroutine mci_work_record_call
pure function mci_work_get_counter (mci_work) result (counter)
class(mci_work_t), intent(in) :: mci_work
type(process_counter_t) :: counter
counter = mci_work%counter
end function mci_work_get_counter
@ %def mci_work_reset_counter
@ %def mci_work_record_call
@ %def mci_work_get_counter
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Process component manager}
<<[[pcm.f90]]>>=
<<File header>>
module pcm
<<Use kinds>>
<<Use strings>>
<<Use debug>>
use constants, only: zero, two
use diagnostics
use lorentz
use io_units, only: free_unit
use os_interface
use process_constants, only: process_constants_t
use physics_defs
use model_data, only: model_data_t
use models, only: model_t
use interactions, only: interaction_t
use quantum_numbers, only: quantum_numbers_t, quantum_numbers_mask_t
use flavors, only: flavor_t
use variables, only: var_list_t
use nlo_data, only: nlo_settings_t
use mci_base, only: mci_t
use phs_base, only: phs_config_t
use mappings, only: mapping_defaults_t
use phs_forests, only: phs_parameters_t
use phs_fks, only: isr_kinematics_t, real_kinematics_t
use phs_fks, only: phs_identifier_t
use dispatch_fks, only: dispatch_fks_s
use fks_regions, only: region_data_t
use nlo_data, only: fks_template_t
use phs_fks, only: phs_fks_generator_t
use phs_fks, only: dalitz_plot_t
use phs_fks, only: phs_fks_config_t, get_filtered_resonance_histories
use dispatch_phase_space, only: dispatch_phs
use process_libraries, only: process_component_def_t
use real_subtraction, only: real_subtraction_t, soft_mismatch_t
use real_subtraction, only: FIXED_ORDER_EVENTS, POWHEG
use real_subtraction, only: real_partition_t, powheg_damping_simple_t
use real_subtraction, only: real_partition_fixed_order_t
use virtual, only: virtual_t
use dglap_remnant, only: dglap_remnant_t
use prc_threshold, only: threshold_def_t
use resonances, only: resonance_history_t, resonance_history_set_t
use nlo_data, only: FKS_DEFAULT, FKS_RESONANCES
use blha_config, only: blha_master_t
use blha_olp_interfaces, only: prc_blha_t
use pcm_base
use process_config
use process_mci, only: process_mci_entry_t
use process_mci, only: REAL_SINGULAR, REAL_FINITE
<<Standard module head>>
<<Pcm: public>>
<<Pcm: types>>
contains
<<Pcm: procedures>>
end module pcm
@ %def pcm
@
\subsection{Default process component manager}
This is the configuration object which has the duty of allocating the
corresponding instance. The default version is trivial.
<<Pcm: public>>=
public :: pcm_default_t
<<Pcm: types>>=
type, extends (pcm_t) :: pcm_default_t
contains
<<Pcm: pcm default: TBP>>
end type pcm_default_t
@ %def pcm_default_t
<<Pcm: pcm default: TBP>>=
procedure :: allocate_instance => pcm_default_allocate_instance
<<Pcm: procedures>>=
subroutine pcm_default_allocate_instance (pcm, instance)
class(pcm_default_t), intent(in) :: pcm
class(pcm_instance_t), intent(inout), allocatable :: instance
allocate (pcm_instance_default_t :: instance)
end subroutine pcm_default_allocate_instance
@ %def pcm_default_allocate_instance
@
Finalizer: apply to core manager.
<<Pcm: pcm default: TBP>>=
procedure :: final => pcm_default_final
<<Pcm: procedures>>=
subroutine pcm_default_final (pcm)
class(pcm_default_t), intent(inout) :: pcm
end subroutine pcm_default_final
@ %def pcm_default_final
@
<<Pcm: pcm default: TBP>>=
procedure :: is_nlo => pcm_default_is_nlo
<<Pcm: procedures>>=
function pcm_default_is_nlo (pcm) result (is_nlo)
logical :: is_nlo
class(pcm_default_t), intent(in) :: pcm
is_nlo = .false.
end function pcm_default_is_nlo
@ %def pcm_default_is_nlo
@
Initialize configuration data, using environment variables.
<<Pcm: pcm default: TBP>>=
procedure :: init => pcm_default_init
<<Pcm: procedures>>=
subroutine pcm_default_init (pcm, env, meta)
class(pcm_default_t), intent(out) :: pcm
type(process_environment_t), intent(in) :: env
type(process_metadata_t), intent(in) :: meta
pcm%has_pdfs = env%has_pdfs ()
call pcm%set_blha_defaults &
(env%has_polarized_beams (), env%get_var_list_ptr ())
pcm%os_data = env%get_os_data ()
end subroutine pcm_default_init
-
+
@ %def pcm_default_init
@
<<Pcm: types>>=
type, extends (pcm_instance_t) :: pcm_instance_default_t
contains
<<Pcm: pcm instance default: TBP>>
end type pcm_instance_default_t
@ %def pcm_instance_default_t
@
<<Pcm: pcm instance default: TBP>>=
procedure :: final => pcm_instance_default_final
<<Pcm: procedures>>=
subroutine pcm_instance_default_final (pcm_instance)
class(pcm_instance_default_t), intent(inout) :: pcm_instance
end subroutine pcm_instance_default_final
@ %def pcm_instance_default_final
@
\subsection{Implementations for the default manager}
Categorize components. Nothing to do here, all components are of Born type.
<<Pcm: pcm default: TBP>>=
procedure :: categorize_components => pcm_default_categorize_components
<<Pcm: procedures>>=
subroutine pcm_default_categorize_components (pcm, config)
class(pcm_default_t), intent(inout) :: pcm
type(process_config_data_t), intent(in) :: config
end subroutine pcm_default_categorize_components
@ %def pcm_default_categorize_components
@
\subsubsection{Phase-space configuration}
Default setup for tree processes: a single phase-space configuration that is
valid for all components.
<<Pcm: pcm default: TBP>>=
procedure :: init_phs_config => pcm_default_init_phs_config
<<Pcm: procedures>>=
subroutine pcm_default_init_phs_config &
(pcm, phs_entry, meta, env, phs_par, mapping_defs)
class(pcm_default_t), intent(inout) :: pcm
type(process_phs_config_t), &
dimension(:), allocatable, intent(out) :: phs_entry
type(process_metadata_t), intent(in) :: meta
type(process_environment_t), intent(in) :: env
type(mapping_defaults_t), intent(in) :: mapping_defs
type(phs_parameters_t), intent(in) :: phs_par
allocate (phs_entry (1))
allocate (pcm%i_phs_config (pcm%n_components), source=1)
call dispatch_phs (phs_entry(1)%phs_config, &
env%get_var_list_ptr (), &
env%get_os_data (), &
meta%id, &
mapping_defs, phs_par)
end subroutine pcm_default_init_phs_config
-
+
@ %def pcm_default_init_phs_config
@
\subsubsection{Core management}
The default component manager assigns one core per component. We allocate and
configure the core objects, using the process-component configuration data.
<<Pcm: pcm default: TBP>>=
procedure :: allocate_cores => pcm_default_allocate_cores
<<Pcm: procedures>>=
subroutine pcm_default_allocate_cores (pcm, config, core_entry)
class(pcm_default_t), intent(inout) :: pcm
type(process_config_data_t), intent(in) :: config
type(core_entry_t), dimension(:), allocatable, intent(out) :: core_entry
type(process_component_def_t), pointer :: component_def
integer :: i
allocate (pcm%i_core (pcm%n_components), source = 0)
pcm%n_cores = pcm%n_components
allocate (core_entry (pcm%n_cores))
do i = 1, pcm%n_cores
pcm%i_core(i) = i
core_entry(i)%i_component = i
component_def => config%process_def%get_component_def_ptr (i)
core_entry(i)%core_def => component_def%get_core_def_ptr ()
core_entry(i)%active = component_def%can_be_integrated ()
end do
end subroutine pcm_default_allocate_cores
-
+
@ %def pcm_default_allocate_cores
@ Extra code is required for certain core types (threshold) or if BLHA uses an
external OLP (Born only, this case) for getting its matrix elements.
<<Pcm: pcm default: TBP>>=
procedure :: prepare_any_external_code => &
pcm_default_prepare_any_external_code
<<Pcm: procedures>>=
subroutine pcm_default_prepare_any_external_code &
(pcm, core_entry, i_core, libname, model, var_list)
class(pcm_default_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
integer, intent(in) :: i_core
type(string_t), intent(in) :: libname
type(model_data_t), intent(in), target :: model
type(var_list_t), intent(in) :: var_list
if (core_entry%active) then
associate (core => core_entry%core)
if (core%needs_external_code ()) then
call core%prepare_external_code &
(core%data%flv_state, &
var_list, pcm%os_data, libname, model, i_core, .false.)
end if
end associate
end if
end subroutine pcm_default_prepare_any_external_code
-
+
@ %def pcm_default_prepare_any_external_code
@ Allocate and configure the BLHA record for a specific core, assuming that
the core type requires it. In the default case, this is a Born
configuration.
<<Pcm: pcm default: TBP>>=
procedure :: setup_blha => pcm_default_setup_blha
<<Pcm: procedures>>=
subroutine pcm_default_setup_blha (pcm, core_entry)
class(pcm_default_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
allocate (core_entry%blha_config, source = pcm%blha_defaults)
call core_entry%blha_config%set_born ()
end subroutine pcm_default_setup_blha
-
+
@ %def pcm_default_setup_blha
@ Apply the configuration, using [[pcm]] data.
<<Pcm: pcm default: TBP>>=
procedure :: prepare_blha_core => pcm_default_prepare_blha_core
<<Pcm: procedures>>=
subroutine pcm_default_prepare_blha_core (pcm, core_entry, model)
class(pcm_default_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
class(model_data_t), intent(in), target :: model
integer :: n_in
integer :: n_legs
integer :: n_flv
integer :: n_hel
select type (core => core_entry%core)
class is (prc_blha_t)
associate (blha_config => core_entry%blha_config)
n_in = core%data%n_in
n_legs = core%data%get_n_tot ()
n_flv = core%data%n_flv
n_hel = blha_config%get_n_hel (core%data%flv_state (1:n_in,1), model)
call core%init_blha (blha_config, n_in, n_legs, n_flv, n_hel)
call core%init_driver (pcm%os_data)
end associate
end select
end subroutine pcm_default_prepare_blha_core
-
+
@ %def pcm_default_prepare_blha_core
@ Read the method settings from the variable list and store them in the BLHA
master. This version: no NLO flag.
<<Pcm: pcm default: TBP>>=
procedure :: set_blha_methods => pcm_default_set_blha_methods
<<Pcm: procedures>>=
subroutine pcm_default_set_blha_methods (pcm, blha_master, var_list)
class(pcm_default_t), intent(in) :: pcm
type(blha_master_t), intent(inout) :: blha_master
type(var_list_t), intent(in) :: var_list
call blha_master%set_methods (.false., var_list)
end subroutine pcm_default_set_blha_methods
-
+
@ %def pcm_default_set_blha_methods
@ Produce the LO and NLO flavor-state tables (as far as available), as
appropriate for BLHA configuration.
The default version looks at the first process core only, to get the Born
data. (Multiple cores are thus unsupported.) The NLO flavor table is left
unallocated.
<<Pcm: pcm default: TBP>>=
procedure :: get_blha_flv_states => pcm_default_get_blha_flv_states
<<Pcm: procedures>>=
subroutine pcm_default_get_blha_flv_states &
(pcm, core_entry, flv_born, flv_real)
class(pcm_default_t), intent(in) :: pcm
type(core_entry_t), dimension(:), intent(in) :: core_entry
integer, dimension(:,:), allocatable, intent(out) :: flv_born
integer, dimension(:,:), allocatable, intent(out) :: flv_real
flv_born = core_entry(1)%core%data%flv_state
end subroutine pcm_default_get_blha_flv_states
-
+
@ %def pcm_default_get_blha_flv_states
@ Allocate and configure the MCI (multi-channel integrator) records. There is
one record per active process component. Second procedure: call the MCI
dispatcher with default-setup arguments.
<<Pcm: pcm default: TBP>>=
procedure :: setup_mci => pcm_default_setup_mci
procedure :: call_dispatch_mci => pcm_default_call_dispatch_mci
<<Pcm: procedures>>=
subroutine pcm_default_setup_mci (pcm, mci_entry)
class(pcm_default_t), intent(inout) :: pcm
type(process_mci_entry_t), &
dimension(:), allocatable, intent(out) :: mci_entry
class(mci_t), allocatable :: mci_template
integer :: i, i_mci
pcm%n_mci = count (pcm%component_active)
allocate (pcm%i_mci (pcm%n_components), source = 0)
i_mci = 0
do i = 1, pcm%n_components
if (pcm%component_active(i)) then
i_mci = i_mci + 1
pcm%i_mci(i) = i_mci
end if
end do
allocate (mci_entry (pcm%n_mci))
end subroutine pcm_default_setup_mci
-
+
subroutine pcm_default_call_dispatch_mci (pcm, &
dispatch_mci, var_list, process_id, mci_template)
class(pcm_default_t), intent(inout) :: pcm
procedure(dispatch_mci_proc) :: dispatch_mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
class(mci_t), allocatable, intent(out) :: mci_template
call dispatch_mci (mci_template, var_list, process_id)
end subroutine pcm_default_call_dispatch_mci
-
+
@ %def pcm_default_setup_mci
@ %def pcm_default_call_dispatch_mci
@ Nothing left to do for the default algorithm.
<<Pcm: pcm default: TBP>>=
procedure :: complete_setup => pcm_default_complete_setup
<<Pcm: procedures>>=
subroutine pcm_default_complete_setup (pcm, core_entry, component, model)
class(pcm_default_t), intent(inout) :: pcm
type(core_entry_t), dimension(:), intent(in) :: core_entry
type(process_component_t), dimension(:), intent(inout) :: component
type(model_t), intent(in), target :: model
end subroutine pcm_default_complete_setup
-
+
@ %def pcm_default_complete_setup
@
\subsubsection{Component management}
Initialize a single component. We require all process-configuration blocks,
and specific templates for the phase-space and integrator configuration.
We also provide the current component index [[i]] and the [[active]] flag.
In the default mode, all components are marked as master components.
<<Pcm: pcm default: TBP>>=
procedure :: init_component => pcm_default_init_component
<<Pcm: procedures>>=
subroutine pcm_default_init_component &
(pcm, component, i, active, &
phs_config, env, meta, config)
class(pcm_default_t), intent(in) :: pcm
type(process_component_t), intent(out) :: component
integer, intent(in) :: i
logical, intent(in) :: active
class(phs_config_t), allocatable, intent(in) :: phs_config
type(process_environment_t), intent(in) :: env
type(process_metadata_t), intent(in) :: meta
type(process_config_data_t), intent(in) :: config
call component%init (i, &
env, meta, config, &
active, &
phs_config)
component%component_type = COMP_MASTER
end subroutine pcm_default_init_component
-
+
@ %def pcm_default_init_component
@
\subsection{NLO process component manager}
The NLO-aware version of the process-component manager.
This is the configuration object, which has the duty of allocating the
corresponding instance. This is the nontrivial NLO version.
<<Pcm: public>>=
public :: pcm_nlo_t
<<Pcm: types>>=
type, extends (pcm_t) :: pcm_nlo_t
type(string_t) :: id
logical :: combined_integration = .false.
logical :: vis_fks_regions = .false.
integer, dimension(:), allocatable :: nlo_type
integer, dimension(:), allocatable :: nlo_type_core
integer, dimension(:), allocatable :: component_type
integer :: i_born = 0
integer :: i_real = 0
integer :: i_sub = 0
type(nlo_settings_t) :: settings
type(region_data_t) :: region_data
logical :: use_real_partition = .false.
real(default) :: real_partition_scale = 0
class(real_partition_t), allocatable :: real_partition
type(dalitz_plot_t) :: dalitz_plot
type(quantum_numbers_t), dimension(:,:), allocatable :: qn_real, qn_born
contains
<<Pcm: pcm nlo: TBP>>
end type pcm_nlo_t
@ %def pcm_nlo_t
@
Initialize configuration data, using environment variables.
<<Pcm: pcm nlo: TBP>>=
procedure :: init => pcm_nlo_init
<<Pcm: procedures>>=
subroutine pcm_nlo_init (pcm, env, meta)
class(pcm_nlo_t), intent(out) :: pcm
type(process_metadata_t), intent(in) :: meta
type(process_environment_t), intent(in) :: env
type(var_list_t), pointer :: var_list
type(fks_template_t) :: fks_template
pcm%id = meta%id
pcm%has_pdfs = env%has_pdfs ()
var_list => env%get_var_list_ptr ()
call dispatch_fks_s (fks_template, var_list)
call pcm%settings%init (var_list, fks_template)
pcm%combined_integration = &
var_list%get_lval (var_str ('?combined_nlo_integration'))
pcm%use_real_partition = &
var_list%get_lval (var_str ("?nlo_use_real_partition"))
pcm%real_partition_scale = &
var_list%get_rval (var_str ("real_partition_scale"))
pcm%vis_fks_regions = &
var_list%get_lval (var_str ("?vis_fks_regions"))
call pcm%set_blha_defaults &
(env%has_polarized_beams (), env%get_var_list_ptr ())
pcm%os_data = env%get_os_data ()
end subroutine pcm_nlo_init
-
+
@ %def pcm_nlo_init
@ Init/rewrite NLO settings without the FKS template.
<<Pcm: pcm nlo: TBP>>=
procedure :: init_nlo_settings => pcm_nlo_init_nlo_settings
<<Pcm: procedures>>=
subroutine pcm_nlo_init_nlo_settings (pcm, var_list)
class(pcm_nlo_t), intent(inout) :: pcm
type(var_list_t), intent(in), target :: var_list
call pcm%settings%init (var_list)
end subroutine pcm_nlo_init_nlo_settings
-
+
@ %def pcm_nlo_init_nlo_settings
@
As appropriate for the NLO/FKS algorithm, the category defined by the
process, is called [[nlo_type]]. We refine this by setting the component
category [[component_type]] separately.
The component types [[COMP_MISMATCH]], [[COMP_PDF]], [[COMP_SUB]] are set only
if the algorithm uses combined integration. Otherwise, they are set to
[[COMP_DEFAULT]].
The component type [[COMP_REAL]] is further distinguished between
[[COMP_REAL_SING]] or [[COMP_REAL_FIN]], if the algorithm uses real
partitions. The former acts as a reference component for the latter, and we
always assume that it is the first real component.
Each component is assigned its own core. Exceptions: the finite-real
component gets the same core as the singular-real component. The mismatch
component gets the same core as the subtraction component.
TODO wk 2018: this convention for real components can be improved. Check whether
all component types should be assigned, not just for combined
-integration.
+integration.
<<Pcm: pcm nlo: TBP>>=
procedure :: categorize_components => pcm_nlo_categorize_components
<<Pcm: procedures>>=
subroutine pcm_nlo_categorize_components (pcm, config)
class(pcm_nlo_t), intent(inout) :: pcm
type(process_config_data_t), intent(in) :: config
type(process_component_def_t), pointer :: component_def
integer :: i
allocate (pcm%nlo_type (pcm%n_components), source = COMPONENT_UNDEFINED)
allocate (pcm%component_type (pcm%n_components), source = COMP_DEFAULT)
do i = 1, pcm%n_components
component_def => config%process_def%get_component_def_ptr (i)
pcm%nlo_type(i) = component_def%get_nlo_type ()
if (pcm%combined_integration) then
select case (pcm%nlo_type(i))
case (BORN)
pcm%i_born = i
pcm%component_type(i) = COMP_MASTER
case (NLO_REAL)
pcm%component_type(i) = COMP_REAL
case (NLO_VIRTUAL)
pcm%component_type(i) = COMP_VIRT
case (NLO_MISMATCH)
pcm%component_type(i) = COMP_MISMATCH
case (NLO_DGLAP)
pcm%component_type(i) = COMP_PDF
case (NLO_SUBTRACTION)
pcm%component_type(i) = COMP_SUB
pcm%i_sub = i
end select
else
select case (pcm%nlo_type(i))
case (BORN)
pcm%i_born = i
pcm%component_type(i) = COMP_MASTER
case (NLO_REAL)
pcm%component_type(i) = COMP_REAL
case (NLO_VIRTUAL)
pcm%component_type(i) = COMP_VIRT
case (NLO_MISMATCH)
pcm%component_type(i) = COMP_MISMATCH
case (NLO_SUBTRACTION)
pcm%i_sub = i
end select
end if
end do
call refine_real_type ( &
pack ([(i, i=1, pcm%n_components)], &
pcm%component_type==COMP_REAL))
contains
subroutine refine_real_type (i_real)
integer, dimension(:), intent(in) :: i_real
pcm%i_real = i_real(1)
if (pcm%use_real_partition) then
pcm%component_type (i_real(1)) = COMP_REAL_SING
pcm%component_type (i_real(2:)) = COMP_REAL_FIN
end if
end subroutine refine_real_type
end subroutine pcm_nlo_categorize_components
@ %def pcm_nlo_categorize_components
@
\subsubsection{Phase-space initial configuration}
Setup for the NLO/PHS processes: two phase-space configurations, (1)
Born/wood, (2) real correction/FKS. All components use either one of these
two configurations.
TODO wk 2018: The [[first_real_component]] identifier is really ugly. Nothing should
rely on the ordering.
<<Pcm: pcm nlo: TBP>>=
procedure :: init_phs_config => pcm_nlo_init_phs_config
<<Pcm: procedures>>=
subroutine pcm_nlo_init_phs_config &
(pcm, phs_entry, meta, env, phs_par, mapping_defs)
class(pcm_nlo_t), intent(inout) :: pcm
type(process_phs_config_t), &
dimension(:), allocatable, intent(out) :: phs_entry
type(process_metadata_t), intent(in) :: meta
type(process_environment_t), intent(in) :: env
type(mapping_defaults_t), intent(in) :: mapping_defs
type(phs_parameters_t), intent(in) :: phs_par
integer :: i
logical :: first_real_component
allocate (phs_entry (2))
call dispatch_phs (phs_entry(1)%phs_config, &
env%get_var_list_ptr (), &
env%get_os_data (), &
meta%id, &
mapping_defs, phs_par, &
var_str ("wood"))
call dispatch_phs (phs_entry(2)%phs_config, &
env%get_var_list_ptr (), &
env%get_os_data (), &
meta%id, &
mapping_defs, phs_par, &
var_str ("fks"))
allocate (pcm%i_phs_config (pcm%n_components), source=0)
first_real_component = .true.
do i = 1, pcm%n_components
select case (pcm%nlo_type(i))
case (BORN, NLO_VIRTUAL, NLO_SUBTRACTION)
pcm%i_phs_config(i) = 1
case (NLO_REAL)
if (first_real_component) then
pcm%i_phs_config(i) = 2
if (pcm%use_real_partition) first_real_component = .false.
else
pcm%i_phs_config(i) = 1
end if
case (NLO_MISMATCH, NLO_DGLAP, GKS)
pcm%i_phs_config(i) = 2
end select
end do
end subroutine pcm_nlo_init_phs_config
-
+
@ %def pcm_nlo_init_phs_config
@
\subsubsection{Core management}
Allocate the core (matrix-element interface) objects that we will need for
evaluation. Every component gets an associated core, except for the
real-finite and mismatch components (if any). Those components are associated
with their previous corresponding real-singular and subtraction cores,
respectively.
After cores are allocated, configure the region-data block that is maintained
by the NLO process-component manager.
<<Pcm: pcm nlo: TBP>>=
procedure :: allocate_cores => pcm_nlo_allocate_cores
<<Pcm: procedures>>=
subroutine pcm_nlo_allocate_cores (pcm, config, core_entry)
class(pcm_nlo_t), intent(inout) :: pcm
type(process_config_data_t), intent(in) :: config
type(core_entry_t), dimension(:), allocatable, intent(out) :: core_entry
type(process_component_def_t), pointer :: component_def
integer :: i, i_core
allocate (pcm%i_core (pcm%n_components), source = 0)
pcm%n_cores = pcm%n_components &
- count (pcm%component_type(:) == COMP_REAL_FIN) &
- count (pcm%component_type(:) == COMP_MISMATCH)
allocate (core_entry (pcm%n_cores))
allocate (pcm%nlo_type_core (pcm%n_cores), source = BORN)
i_core = 0
do i = 1, pcm%n_components
select case (pcm%component_type(i))
case default
i_core = i_core + 1
pcm%i_core(i) = i_core
pcm%nlo_type_core(i_core) = pcm%nlo_type(i)
core_entry(i_core)%i_component = i
component_def => config%process_def%get_component_def_ptr (i)
core_entry(i_core)%core_def => component_def%get_core_def_ptr ()
select case (pcm%nlo_type(i))
case default
core_entry(i)%active = component_def%can_be_integrated ()
case (NLO_REAL, NLO_SUBTRACTION)
core_entry(i)%active = .true.
end select
case (COMP_REAL_FIN)
pcm%i_core(i) = pcm%i_core(pcm%i_real)
case (COMP_MISMATCH)
pcm%i_core(i) = pcm%i_core(pcm%i_sub)
end select
end do
end subroutine pcm_nlo_allocate_cores
-
+
@ %def pcm_nlo_allocate_cores
@ Extra code is required for certain core types (threshold) or if BLHA uses an
external OLP for getting its matrix elements. OMega matrix elements, by
definition, do not need extra code. NLO-virtual or subtraction
matrix elements always need extra code.
More precisely: for the Born and virtual matrix element, the extra code is
accessed only if the component is active. The radiation (real) and the
subtraction corrections (singular and finite), extra code is accessed in any
case.
The flavor state is taken from the [[region_data]] table in the [[pcm]]
record. We use the Born and real flavor-state tables as appropriate.
<<Pcm: pcm nlo: TBP>>=
procedure :: prepare_any_external_code => &
pcm_nlo_prepare_any_external_code
<<Pcm: procedures>>=
subroutine pcm_nlo_prepare_any_external_code &
(pcm, core_entry, i_core, libname, model, var_list)
class(pcm_nlo_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
integer, intent(in) :: i_core
type(string_t), intent(in) :: libname
type(model_data_t), intent(in), target :: model
type(var_list_t), intent(in) :: var_list
integer, dimension(:,:), allocatable :: flv_born, flv_real
integer :: i
call pcm%region_data%get_all_flv_states (flv_born, flv_real)
if (core_entry%active) then
associate (core => core_entry%core)
if (core%needs_external_code ()) then
select case (pcm%nlo_type (core_entry%i_component))
case default
call core%data%set_flv_state (flv_born)
case (NLO_REAL)
call core%data%set_flv_state (flv_real)
end select
call core%prepare_external_code &
(core%data%flv_state, &
var_list, pcm%os_data, libname, model, i_core, .true.)
end if
end associate
end if
end subroutine pcm_nlo_prepare_any_external_code
-
+
@ %def pcm_nlo_prepare_any_external_code
@ Allocate and configure the BLHA record for a specific core, assuming that
the core type requires it. The configuration depends on the NLO type of the
core.
<<Pcm: pcm nlo: TBP>>=
procedure :: setup_blha => pcm_nlo_setup_blha
<<Pcm: procedures>>=
subroutine pcm_nlo_setup_blha (pcm, core_entry)
class(pcm_nlo_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
allocate (core_entry%blha_config, source = pcm%blha_defaults)
select case (pcm%nlo_type(core_entry%i_component))
case (BORN)
call core_entry%blha_config%set_born ()
case (NLO_REAL)
call core_entry%blha_config%set_real_trees ()
case (NLO_VIRTUAL)
call core_entry%blha_config%set_loop ()
case (NLO_SUBTRACTION)
call core_entry%blha_config%set_subtraction ()
call core_entry%blha_config%set_internal_color_correlations ()
case (NLO_DGLAP)
call core_entry%blha_config%set_dglap ()
end select
end subroutine pcm_nlo_setup_blha
@ %def pcm_nlo_setup_blha
@ After phase-space configuration data and core entries are available, we fill
tables and compute the remaining NLO data that will steer the integration
and subtraction algorithm.
There are three parts: recognize a threshold-type process core (if it exists),
prepare the region-data tables (always), and prepare for real partitioning (if
requested).
The real-component phase space acts as the source for resonance-history
information, required for the region data.
<<Pcm: pcm nlo: TBP>>=
procedure :: complete_setup => pcm_nlo_complete_setup
<<Pcm: procedures>>=
subroutine pcm_nlo_complete_setup (pcm, core_entry, component, model)
class(pcm_nlo_t), intent(inout) :: pcm
type(core_entry_t), dimension(:), intent(in) :: core_entry
type(process_component_t), dimension(:), intent(inout) :: component
type(model_t), intent(in), target :: model
integer :: i
call pcm%handle_threshold_core (core_entry)
call pcm%setup_region_data &
(core_entry, component(pcm%i_real)%phs_config, model)
call pcm%setup_real_partition ()
end subroutine pcm_nlo_complete_setup
@ %def pcm_nlo_complete_setup
@ Apply the BLHA configuration to a core object, using the region data from
[[pcm]] for determining the particle content.
<<Pcm: pcm nlo: TBP>>=
procedure :: prepare_blha_core => pcm_nlo_prepare_blha_core
<<Pcm: procedures>>=
subroutine pcm_nlo_prepare_blha_core (pcm, core_entry, model)
class(pcm_nlo_t), intent(in) :: pcm
type(core_entry_t), intent(inout) :: core_entry
class(model_data_t), intent(in), target :: model
integer :: n_in
integer :: n_legs
integer :: n_flv
integer :: n_hel
select type (core => core_entry%core)
class is (prc_blha_t)
associate (blha_config => core_entry%blha_config)
n_in = core%data%n_in
select case (pcm%nlo_type(core_entry%i_component))
case (NLO_REAL)
n_legs = pcm%region_data%get_n_legs_real ()
n_flv = pcm%region_data%get_n_flv_real ()
case default
n_legs = pcm%region_data%get_n_legs_born ()
n_flv = pcm%region_data%get_n_flv_born ()
end select
n_hel = blha_config%get_n_hel (core%data%flv_state (1:n_in,1), model)
call core%init_blha (blha_config, n_in, n_legs, n_flv, n_hel)
call core%init_driver (pcm%os_data)
end associate
end select
end subroutine pcm_nlo_prepare_blha_core
-
+
@ %def pcm_nlo_prepare_blha_core
@ Read the method settings from the variable list and store them in the BLHA
master. This version: NLO flag set.
<<Pcm: pcm nlo: TBP>>=
procedure :: set_blha_methods => pcm_nlo_set_blha_methods
<<Pcm: procedures>>=
subroutine pcm_nlo_set_blha_methods (pcm, blha_master, var_list)
class(pcm_nlo_t), intent(in) :: pcm
type(blha_master_t), intent(inout) :: blha_master
type(var_list_t), intent(in) :: var_list
call blha_master%set_methods (.true., var_list)
end subroutine pcm_nlo_set_blha_methods
-
+
@ %def pcm_nlo_set_blha_methods
@ Produce the LO and NLO flavor-state tables (as far as available), as
appropriate for BLHA configuration.
The NLO version copies the tables from the region data inside [[pcm]]. The
core array is not needed.
<<Pcm: pcm nlo: TBP>>=
procedure :: get_blha_flv_states => pcm_nlo_get_blha_flv_states
<<Pcm: procedures>>=
subroutine pcm_nlo_get_blha_flv_states &
(pcm, core_entry, flv_born, flv_real)
class(pcm_nlo_t), intent(in) :: pcm
type(core_entry_t), dimension(:), intent(in) :: core_entry
integer, dimension(:,:), allocatable, intent(out) :: flv_born
integer, dimension(:,:), allocatable, intent(out) :: flv_real
call pcm%region_data%get_all_flv_states (flv_born, flv_real)
end subroutine pcm_nlo_get_blha_flv_states
-
+
@ %def pcm_nlo_get_blha_flv_states
@ Allocate and configure the MCI (multi-channel integrator) records. The
relation depends on the [[combined_integration]] setting. If we integrate
components separately, each component gets its own record, except for the
subtraction component. If we do the combination, there is one record for
the master (Born) component and a second one for the real-finite component, if
present.
Each entry acquires some NLO-specific initialization. Generic configuration
follows later.
Second procedure: call the MCI dispatcher with NLO-setup arguments.
<<Pcm: pcm nlo: TBP>>=
procedure :: setup_mci => pcm_nlo_setup_mci
procedure :: call_dispatch_mci => pcm_nlo_call_dispatch_mci
<<Pcm: procedures>>=
subroutine pcm_nlo_setup_mci (pcm, mci_entry)
class(pcm_nlo_t), intent(inout) :: pcm
type(process_mci_entry_t), &
dimension(:), allocatable, intent(out) :: mci_entry
class(mci_t), allocatable :: mci_template
integer :: i, i_mci
if (pcm%combined_integration) then
pcm%n_mci = 1 &
+ count (pcm%component_active(:) &
& .and. pcm%component_type(:) == COMP_REAL_FIN)
allocate (pcm%i_mci (pcm%n_components), source = 0)
do i = 1, pcm%n_components
if (pcm%component_active(i)) then
select case (pcm%component_type(i))
case (COMP_MASTER)
pcm%i_mci(i) = 1
case (COMP_REAL_FIN)
pcm%i_mci(i) = 2
end select
end if
end do
else
pcm%n_mci = count (pcm%component_active(:) &
& .and. pcm%nlo_type(:) /= NLO_SUBTRACTION)
allocate (pcm%i_mci (pcm%n_components), source = 0)
i_mci = 0
do i = 1, pcm%n_components
if (pcm%component_active(i)) then
select case (pcm%nlo_type(i))
case default
i_mci = i_mci + 1
pcm%i_mci(i) = i_mci
case (NLO_SUBTRACTION)
end select
end if
end do
end if
allocate (mci_entry (pcm%n_mci))
mci_entry(:)%combined_integration = pcm%combined_integration
if (pcm%use_real_partition) then
do i = 1, pcm%n_components
i_mci = pcm%i_mci(i)
if (i_mci > 0) then
select case (pcm%component_type(i))
case (COMP_REAL_FIN)
mci_entry(i_mci)%real_partition_type = REAL_FINITE
case default
mci_entry(i_mci)%real_partition_type = REAL_SINGULAR
end select
end if
end do
end if
end subroutine pcm_nlo_setup_mci
-
+
subroutine pcm_nlo_call_dispatch_mci (pcm, &
dispatch_mci, var_list, process_id, mci_template)
class(pcm_nlo_t), intent(inout) :: pcm
procedure(dispatch_mci_proc) :: dispatch_mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
class(mci_t), allocatable, intent(out) :: mci_template
call dispatch_mci (mci_template, var_list, process_id, is_nlo = .true.)
end subroutine pcm_nlo_call_dispatch_mci
-
+
@ %def pcm_nlo_setup_mci
@ %def pcm_nlo_call_dispatch_mci
@ Check for a threshold core and adjust the configuration accordingly, before
singular region data are considered.
<<Pcm: pcm nlo: TBP>>=
procedure :: handle_threshold_core => pcm_nlo_handle_threshold_core
<<Pcm: procedures>>=
subroutine pcm_nlo_handle_threshold_core (pcm, core_entry)
class(pcm_nlo_t), intent(inout) :: pcm
type(core_entry_t), dimension(:), intent(in) :: core_entry
integer :: i
do i = 1, size (core_entry)
select type (core => core_entry(i)%core_def)
type is (threshold_def_t)
pcm%settings%factorization_mode = FACTORIZATION_THRESHOLD
return
end select
end do
end subroutine pcm_nlo_handle_threshold_core
-
+
@ %def pcm_nlo_handle_threshold_core
@ Configure the singular-region tables based on the process data for the Born
and Real (singular) cores, using also the appropriate FKS phase-space
configuration object.
In passing, we may create a table of resonance histories that are relevant for
the singular-region configuration.
TODO wk 2018: check whether [[phs_entry]] needs to be intent(inout).
<<Pcm: pcm nlo: TBP>>=
procedure :: setup_region_data => pcm_nlo_setup_region_data
<<Pcm: procedures>>=
subroutine pcm_nlo_setup_region_data (pcm, core_entry, phs_config, model)
class(pcm_nlo_t), intent(inout) :: pcm
type(core_entry_t), dimension(:), intent(in) :: core_entry
class(phs_config_t), intent(inout) :: phs_config
type(model_t), intent(in), target :: model
type(process_constants_t) :: data_born, data_real
integer, dimension (:,:), allocatable :: flavor_born, flavor_real
type(resonance_history_t), dimension(:), allocatable :: resonance_histories
type(var_list_t), pointer :: var_list
logical :: success
data_born = core_entry(pcm%i_core(pcm%i_born))%core%data
data_real = core_entry(pcm%i_core(pcm%i_real))%core%data
call data_born%get_flv_state (flavor_born)
call data_real%get_flv_state (flavor_real)
call pcm%region_data%init &
(data_born%n_in, model, flavor_born, flavor_real, &
pcm%settings%nlo_correction_type)
associate (template => pcm%settings%fks_template)
if (template%mapping_type == FKS_RESONANCES) then
select type (phs_config)
type is (phs_fks_config_t)
call get_filtered_resonance_histories (phs_config, &
data_born%n_in, flavor_born, model, &
template%excluded_resonances, &
resonance_histories, success)
end select
if (.not. success) template%mapping_type = FKS_DEFAULT
end if
call pcm%region_data%setup_fks_mappings (template, data_born%n_in)
!!! Check again, mapping_type might have changed
if (template%mapping_type == FKS_RESONANCES) then
call pcm%region_data%set_resonance_mappings (resonance_histories)
call pcm%region_data%init_resonance_information ()
pcm%settings%use_resonance_mappings = .true.
end if
end associate
if (pcm%settings%factorization_mode == FACTORIZATION_THRESHOLD) then
call pcm%region_data%set_isr_pseudo_regions ()
call pcm%region_data%split_up_interference_regions_for_threshold ()
end if
call pcm%region_data%compute_number_of_phase_spaces ()
call pcm%region_data%set_i_phs_to_i_con ()
call pcm%region_data%write_to_file &
(pcm%id, pcm%vis_fks_regions, pcm%os_data)
if (debug_active (D_SUBTRACTION)) &
call pcm%region_data%check_consistency (.true.)
end subroutine pcm_nlo_setup_region_data
@ %def pcm_nlo_setup_region_data
@ After region data are set up, we allocate and configure the
[[real_partition]] objects, if requested.
<<Pcm: pcm nlo: TBP>>=
procedure :: setup_real_partition => pcm_nlo_setup_real_partition
<<Pcm: procedures>>=
subroutine pcm_nlo_setup_real_partition (pcm)
class(pcm_nlo_t), intent(inout) :: pcm
if (pcm%use_real_partition) then
if (.not. allocated (pcm%real_partition)) then
allocate (real_partition_fixed_order_t :: pcm%real_partition)
select type (partition => pcm%real_partition)
type is (real_partition_fixed_order_t)
call pcm%region_data%get_all_ftuples (partition%fks_pairs)
partition%scale = pcm%real_partition_scale
end select
end if
end if
end subroutine pcm_nlo_setup_real_partition
@ %def pcm_nlo_setup_real_partition
@
Initialize a single component. We require all process-configuration blocks,
and specific templates for the phase-space and integrator configuration.
We also provide the current component index [[i]] and the [[active]] flag.
For a subtraction component, the [[active]] flag is overridden.
In the nlo mode, the component types have been determined before.
TODO wk 2018: the component type need not be stored in the component; we may remove
this when everything is controlled by [[pcm]].
<<Pcm: pcm nlo: TBP>>=
procedure :: init_component => pcm_nlo_init_component
<<Pcm: procedures>>=
subroutine pcm_nlo_init_component &
(pcm, component, i, active, &
phs_config, env, meta, config)
class(pcm_nlo_t), intent(in) :: pcm
type(process_component_t), intent(out) :: component
integer, intent(in) :: i
logical, intent(in) :: active
class(phs_config_t), allocatable, intent(in) :: phs_config
type(process_environment_t), intent(in) :: env
type(process_metadata_t), intent(in) :: meta
type(process_config_data_t), intent(in) :: config
logical :: activate
select case (pcm%nlo_type(i))
case default; activate = active
case (NLO_SUBTRACTION); activate = .false.
end select
call component%init (i, &
env, meta, config, &
activate, &
phs_config)
component%component_type = pcm%component_type(i)
end subroutine pcm_nlo_init_component
-
+
@ %def pcm_nlo_init_component
@
Override the base method: record the active components in the PCM object, and
report inactive components (except for the subtraction component).
<<Pcm: pcm nlo: TBP>>=
procedure :: record_inactive_components => pcm_nlo_record_inactive_components
<<Pcm: procedures>>=
subroutine pcm_nlo_record_inactive_components (pcm, component, meta)
class(pcm_nlo_t), intent(inout) :: pcm
type(process_component_t), dimension(:), intent(in) :: component
type(process_metadata_t), intent(inout) :: meta
integer :: i
pcm%component_active = component%active
do i = 1, pcm%n_components
select case (pcm%nlo_type(i))
case (NLO_SUBTRACTION)
case default
if (.not. component(i)%active) call meta%deactivate_component (i)
end select
end do
end subroutine pcm_nlo_record_inactive_components
-
+
@ %def pcm_nlo_record_inactive_components
@
<<Pcm: pcm nlo: TBP>>=
procedure :: core_is_radiation => pcm_nlo_core_is_radiation
<<Pcm: procedures>>=
function pcm_nlo_core_is_radiation (pcm, i_core) result (is_rad)
logical :: is_rad
class(pcm_nlo_t), intent(in) :: pcm
integer, intent(in) :: i_core
is_rad = pcm%nlo_type(i_core) == NLO_REAL ! .and. .not. pcm%cm%sub(i_core)
end function pcm_nlo_core_is_radiation
@ %def pcm_nlo_core_is_radiation
@
<<Pcm: pcm nlo: TBP>>=
procedure :: get_n_flv_born => pcm_nlo_get_n_flv_born
<<Pcm: procedures>>=
function pcm_nlo_get_n_flv_born (pcm_nlo) result (n_flv)
integer :: n_flv
class(pcm_nlo_t), intent(in) :: pcm_nlo
n_flv = pcm_nlo%region_data%n_flv_born
end function pcm_nlo_get_n_flv_born
@ %def pcm_nlo_get_n_flv_born
@
<<Pcm: pcm nlo: TBP>>=
procedure :: get_n_flv_real => pcm_nlo_get_n_flv_real
<<Pcm: procedures>>=
function pcm_nlo_get_n_flv_real (pcm_nlo) result (n_flv)
integer :: n_flv
class(pcm_nlo_t), intent(in) :: pcm_nlo
n_flv = pcm_nlo%region_data%n_flv_real
end function pcm_nlo_get_n_flv_real
@ %def pcm_nlo_get_n_flv_real
@
<<Pcm: pcm nlo: TBP>>=
procedure :: get_n_alr => pcm_nlo_get_n_alr
<<Pcm: procedures>>=
function pcm_nlo_get_n_alr (pcm) result (n_alr)
integer :: n_alr
class(pcm_nlo_t), intent(in) :: pcm
n_alr = pcm%region_data%n_regions
end function pcm_nlo_get_n_alr
@ %def pcm_nlo_get_n_alr
@
<<Pcm: pcm nlo: TBP>>=
procedure :: get_flv_states => pcm_nlo_get_flv_states
<<Pcm: procedures>>=
function pcm_nlo_get_flv_states (pcm, born) result (flv)
integer, dimension(:,:), allocatable :: flv
class(pcm_nlo_t), intent(in) :: pcm
logical, intent(in) :: born
if (born) then
flv = pcm%region_data%get_flv_states_born ()
else
flv = pcm%region_data%get_flv_states_real ()
end if
end function pcm_nlo_get_flv_states
@ %def pcm_nlo_get_flv_states
@
<<Pcm: pcm nlo: TBP>>=
procedure :: get_qn => pcm_nlo_get_qn
<<Pcm: procedures>>=
function pcm_nlo_get_qn (pcm, born) result (qn)
type(quantum_numbers_t), dimension(:,:), allocatable :: qn
class(pcm_nlo_t), intent(in) :: pcm
logical, intent(in) :: born
if (born) then
qn = pcm%qn_born
else
qn = pcm%qn_real
end if
end function pcm_nlo_get_qn
@ %def pcm_nlo_get_qn
@ Check if there are massive emitters. Since the mass-structure of all
underlying Born configurations have to be the same (\textbf{This does
not have to be the case when different components are generated at LO})
, we just use the first one to determine this.
<<Pcm: pcm nlo: TBP>>=
procedure :: has_massive_emitter => pcm_nlo_has_massive_emitter
<<Pcm: procedures>>=
function pcm_nlo_has_massive_emitter (pcm) result (val)
logical :: val
class(pcm_nlo_t), intent(in) :: pcm
integer :: i
val = .false.
associate (reg_data => pcm%region_data)
do i = reg_data%n_in + 1, reg_data%n_legs_born
if (any (i == reg_data%emitters)) &
val = val .or. reg_data%flv_born(1)%massive(i)
end do
end associate
end function pcm_nlo_has_massive_emitter
@ %def pcm_nlo_has_massive_emitter
@ Returns an array which specifies if the particle at position [[i]] is massive.
<<Pcm: pcm nlo: TBP>>=
procedure :: get_mass_info => pcm_nlo_get_mass_info
<<Pcm: procedures>>=
function pcm_nlo_get_mass_info (pcm, i_flv) result (massive)
class(pcm_nlo_t), intent(in) :: pcm
integer, intent(in) :: i_flv
logical, dimension(:), allocatable :: massive
allocate (massive (size (pcm%region_data%flv_born(i_flv)%massive)))
massive = pcm%region_data%flv_born(i_flv)%massive
end function pcm_nlo_get_mass_info
@ %def pcm_nlo_get_mass_info
@
<<Pcm: pcm nlo: TBP>>=
procedure :: allocate_instance => pcm_nlo_allocate_instance
<<Pcm: procedures>>=
subroutine pcm_nlo_allocate_instance (pcm, instance)
class(pcm_nlo_t), intent(in) :: pcm
class(pcm_instance_t), intent(inout), allocatable :: instance
allocate (pcm_instance_nlo_t :: instance)
end subroutine pcm_nlo_allocate_instance
@ %def pcm_nlo_allocate_instance
@
<<Pcm: pcm nlo: TBP>>=
procedure :: init_qn => pcm_nlo_init_qn
<<Pcm: procedures>>=
subroutine pcm_nlo_init_qn (pcm, model)
class(pcm_nlo_t), intent(inout) :: pcm
class(model_data_t), intent(in) :: model
integer, dimension(:,:), allocatable :: flv_states
type(flavor_t), dimension(:), allocatable :: flv
integer :: i
type(quantum_numbers_t), dimension(:), allocatable :: qn
allocate (flv_states (pcm%region_data%n_legs_born, pcm%region_data%n_flv_born))
flv_states = pcm%get_flv_states (.true.)
allocate (pcm%qn_born (size (flv_states, dim = 1), size (flv_states, dim = 2)))
allocate (flv (size (flv_states, dim = 1)))
allocate (qn (size (flv_states, dim = 1)))
do i = 1, pcm%get_n_flv_born ()
call flv%init (flv_states (:,i), model)
call qn%init (flv)
pcm%qn_born(:,i) = qn
end do
deallocate (flv); deallocate (qn)
deallocate (flv_states)
allocate (flv_states (pcm%region_data%n_legs_real, pcm%region_data%n_flv_real))
flv_states = pcm%get_flv_states (.false.)
allocate (pcm%qn_real (size (flv_states, dim = 1), size (flv_states, dim = 2)))
allocate (flv (size (flv_states, dim = 1)))
allocate (qn (size (flv_states, dim = 1)))
do i = 1, pcm%get_n_flv_real ()
call flv%init (flv_states (:,i), model)
call qn%init (flv)
pcm%qn_real(:,i) = qn
end do
end subroutine pcm_nlo_init_qn
@ %def pcm_nlo_init_qn
@
<<Pcm: pcm nlo: TBP>>=
procedure :: allocate_ps_matching => pcm_nlo_allocate_ps_matching
<<Pcm: procedures>>=
subroutine pcm_nlo_allocate_ps_matching (pcm)
class(pcm_nlo_t), intent(inout) :: pcm
if (.not. allocated (pcm%real_partition)) then
allocate (powheg_damping_simple_t :: pcm%real_partition)
end if
end subroutine pcm_nlo_allocate_ps_matching
@ %def pcm_nlo_allocate_ps_matching
@
<<Pcm: pcm nlo: TBP>>=
procedure :: activate_dalitz_plot => pcm_nlo_activate_dalitz_plot
<<Pcm: procedures>>=
subroutine pcm_nlo_activate_dalitz_plot (pcm, filename)
class(pcm_nlo_t), intent(inout) :: pcm
type(string_t), intent(in) :: filename
call pcm%dalitz_plot%init (free_unit (), filename, .false.)
call pcm%dalitz_plot%write_header ()
end subroutine pcm_nlo_activate_dalitz_plot
@ %def pcm_nlo_activate_dalitz_plot
@
<<Pcm: pcm nlo: TBP>>=
procedure :: register_dalitz_plot => pcm_nlo_register_dalitz_plot
<<Pcm: procedures>>=
subroutine pcm_nlo_register_dalitz_plot (pcm, emitter, p)
class(pcm_nlo_t), intent(inout) :: pcm
integer, intent(in) :: emitter
type(vector4_t), intent(in), dimension(:) :: p
real(default) :: k0_n, k0_np1
k0_n = p(emitter)%p(0)
k0_np1 = p(size(p))%p(0)
call pcm%dalitz_plot%register (k0_n, k0_np1)
end subroutine pcm_nlo_register_dalitz_plot
@ %def pcm_nlo_register_dalitz_plot
@
<<Pcm: pcm nlo: TBP>>=
procedure :: setup_phs_generator => pcm_nlo_setup_phs_generator
<<Pcm: procedures>>=
subroutine pcm_nlo_setup_phs_generator (pcm, pcm_instance, generator, &
sqrts, mode, singular_jacobian)
class(pcm_nlo_t), intent(in) :: pcm
type(phs_fks_generator_t), intent(inout) :: generator
type(pcm_instance_nlo_t), intent(in), target :: pcm_instance
real(default), intent(in) :: sqrts
integer, intent(in), optional:: mode
logical, intent(in), optional :: singular_jacobian
logical :: yorn
yorn = .false.; if (present (singular_jacobian)) yorn = singular_jacobian
call generator%connect_kinematics (pcm_instance%isr_kinematics, &
pcm_instance%real_kinematics, pcm%has_massive_emitter ())
generator%n_in = pcm%region_data%n_in
call generator%set_sqrts_hat (sqrts)
call generator%set_emitters (pcm%region_data%emitters)
call generator%setup_masses (pcm%region_data%n_legs_born)
generator%is_massive = pcm%get_mass_info (1)
generator%singular_jacobian = yorn
if (present (mode)) generator%mode = mode
end subroutine pcm_nlo_setup_phs_generator
@ %def pcm_nlo_setup_phs_generator
@
<<Pcm: pcm nlo: TBP>>=
procedure :: final => pcm_nlo_final
<<Pcm: procedures>>=
subroutine pcm_nlo_final (pcm)
class(pcm_nlo_t), intent(inout) :: pcm
if (allocated (pcm%real_partition)) deallocate (pcm%real_partition)
call pcm%dalitz_plot%final ()
end subroutine pcm_nlo_final
@ %def pcm_nlo_final
@
<<Pcm: pcm nlo: TBP>>=
procedure :: is_nlo => pcm_nlo_is_nlo
<<Pcm: procedures>>=
function pcm_nlo_is_nlo (pcm) result (is_nlo)
logical :: is_nlo
class(pcm_nlo_t), intent(in) :: pcm
is_nlo = .true.
end function pcm_nlo_is_nlo
@ %def pcm_nlo_is_nlo
@ As a first implementation, it acts as a wrapper for the NLO controller
object and the squared matrix-element collector.
<<Pcm: public>>=
public :: pcm_instance_nlo_t
<<Pcm: types>>=
type, extends (pcm_instance_t) :: pcm_instance_nlo_t
logical :: use_internal_color_correlation = .true.
type(real_kinematics_t), pointer :: real_kinematics => null ()
type(isr_kinematics_t), pointer :: isr_kinematics => null ()
type(real_subtraction_t) :: real_sub
type(virtual_t) :: virtual
type(soft_mismatch_t) :: soft_mismatch
type(dglap_remnant_t) :: dglap_remnant
integer, dimension(:), allocatable :: i_mci_to_real_component
contains
<<Pcm: pcm instance: TBP>>
end type pcm_instance_nlo_t
@ %def pcm_instance_nlo_t
@
<<Pcm: pcm instance: TBP>>=
procedure :: set_radiation_event => pcm_instance_nlo_set_radiation_event
procedure :: set_subtraction_event => pcm_instance_nlo_set_subtraction_event
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_set_radiation_event (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
pcm_instance%real_sub%radiation_event = .true.
pcm_instance%real_sub%subtraction_event = .false.
end subroutine pcm_instance_nlo_set_radiation_event
subroutine pcm_instance_nlo_set_subtraction_event (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
pcm_instance%real_sub%radiation_event = .false.
pcm_instance%real_sub%subtraction_event = .true.
end subroutine pcm_instance_nlo_set_subtraction_event
@ %def pcm_instance_nlo_set_radiation_event
@ %def pcm_instance_nlo_set_subtraction_event
<<Pcm: pcm instance: TBP>>=
procedure :: disable_subtraction => pcm_instance_nlo_disable_subtraction
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_disable_subtraction (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
pcm_instance%real_sub%subtraction_deactivated = .true.
end subroutine pcm_instance_nlo_disable_subtraction
@ %def pcm_instance_nlo_disable_subtraction
@
<<Pcm: pcm instance: TBP>>=
procedure :: init_config => pcm_instance_nlo_init_config
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_init_config (pcm_instance, active_components, &
nlo_types, sqrts, i_real_fin, model)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
logical, intent(in), dimension(:) :: active_components
integer, intent(in), dimension(:) :: nlo_types
real(default), intent(in) :: sqrts
integer, intent(in) :: i_real_fin
class(model_data_t), intent(in) :: model
integer :: i_component
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, "pcm_instance_nlo_init_config")
call pcm_instance%init_real_and_isr_kinematics (sqrts)
select type (pcm => pcm_instance%config)
type is (pcm_nlo_t)
do i_component = 1, size (active_components)
if (active_components(i_component) .or. pcm%settings%combined_integration) then
select case (nlo_types(i_component))
case (NLO_REAL)
if (i_component /= i_real_fin) then
call pcm_instance%setup_real_component &
(pcm%settings%fks_template%subtraction_disabled)
end if
case (NLO_VIRTUAL)
call pcm_instance%init_virtual (model)
case (NLO_MISMATCH)
call pcm_instance%init_soft_mismatch ()
case (NLO_DGLAP)
call pcm_instance%init_dglap_remnant ()
end select
end if
end do
end select
end subroutine pcm_instance_nlo_init_config
@ %def pcm_instance_nlo_init_config
@
<<Pcm: pcm instance: TBP>>=
procedure :: setup_real_component => pcm_instance_nlo_setup_real_component
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_setup_real_component (pcm_instance, &
subtraction_disabled)
class(pcm_instance_nlo_t), intent(inout), target :: pcm_instance
logical, intent(in) :: subtraction_disabled
call pcm_instance%init_real_subtraction ()
if (subtraction_disabled) call pcm_instance%disable_subtraction ()
end subroutine pcm_instance_nlo_setup_real_component
@ %def pcm_instance_nlo_setup_real_component
@
<<Pcm: pcm instance: TBP>>=
procedure :: init_real_and_isr_kinematics => &
pcm_instance_nlo_init_real_and_isr_kinematics
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_init_real_and_isr_kinematics (pcm_instance, sqrts)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
real(default) :: sqrts
integer :: n_contr
allocate (pcm_instance%real_kinematics)
allocate (pcm_instance%isr_kinematics)
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
associate (region_data => config%region_data)
if (allocated (region_data%alr_contributors)) then
n_contr = size (region_data%alr_contributors)
else if (config%settings%factorization_mode == FACTORIZATION_THRESHOLD) then
n_contr = 2
else
n_contr = 1
end if
call pcm_instance%real_kinematics%init &
(region_data%n_legs_real, region_data%n_phs, &
region_data%n_regions, n_contr)
if (config%settings%factorization_mode == FACTORIZATION_THRESHOLD) &
call pcm_instance%real_kinematics%init_onshell &
(region_data%n_legs_real, region_data%n_phs)
pcm_instance%isr_kinematics%n_in = region_data%n_in
end associate
end select
pcm_instance%isr_kinematics%beam_energy = sqrts / two
end subroutine pcm_instance_nlo_init_real_and_isr_kinematics
@ %def pcm_instance_nlo_init_real_and_isr_kinematics
@
<<Pcm: pcm instance: TBP>>=
procedure :: set_real_and_isr_kinematics => &
pcm_instance_nlo_set_real_and_isr_kinematics
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_set_real_and_isr_kinematics (pcm_instance, phs_identifiers, sqrts)
class(pcm_instance_nlo_t), intent(inout), target :: pcm_instance
type(phs_identifier_t), intent(in), dimension(:) :: phs_identifiers
real(default), intent(in) :: sqrts
call pcm_instance%real_sub%set_real_kinematics &
(pcm_instance%real_kinematics)
call pcm_instance%real_sub%set_isr_kinematics &
(pcm_instance%isr_kinematics)
end subroutine pcm_instance_nlo_set_real_and_isr_kinematics
@ %def pcm_instance_nlo_set_real_and_isr_kinematics
@
<<Pcm: pcm instance: TBP>>=
procedure :: init_real_subtraction => pcm_instance_nlo_init_real_subtraction
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_init_real_subtraction (pcm_instance)
class(pcm_instance_nlo_t), intent(inout), target :: pcm_instance
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
associate (region_data => config%region_data)
call pcm_instance%real_sub%init (region_data, config%settings)
if (allocated (config%settings%selected_alr)) then
associate (selected_alr => config%settings%selected_alr)
if (any (selected_alr < 0)) then
call msg_fatal ("Fixed alpha region must be non-negative!")
else if (any (selected_alr > region_data%n_regions)) then
call msg_fatal ("Fixed alpha region is larger than the total"&
&" number of singular regions!")
else
allocate (pcm_instance%real_sub%selected_alr (size (selected_alr)))
pcm_instance%real_sub%selected_alr = selected_alr
end if
end associate
end if
end associate
end select
end subroutine pcm_instance_nlo_init_real_subtraction
@ %def pcm_instance_nlo_init_real_subtraction
@
<<Pcm: pcm instance: TBP>>=
procedure :: set_momenta_and_scales_virtual => &
pcm_instance_nlo_set_momenta_and_scales_virtual
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_set_momenta_and_scales_virtual (pcm_instance, p, &
ren_scale, fac_scale)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(in) :: ren_scale, fac_scale
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
associate (virtual => pcm_instance%virtual)
call virtual%set_ren_scale (p, ren_scale)
call virtual%set_fac_scale (p, fac_scale)
call virtual%set_ellis_sexton_scale ()
end associate
end select
end subroutine pcm_instance_nlo_set_momenta_and_scales_virtual
@ %def pcm_instance_nlo_set_momenta_and_scales_virtual
@
<<Pcm: pcm instance: TBP>>=
procedure :: set_fac_scale => pcm_instance_nlo_set_fac_scale
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_set_fac_scale (pcm_instance, fac_scale)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
real(default), intent(in) :: fac_scale
pcm_instance%isr_kinematics%fac_scale = fac_scale
end subroutine pcm_instance_nlo_set_fac_scale
@ %def pcm_instance_nlo_set_fac_scale
@
<<Pcm: pcm instance: TBP>>=
procedure :: set_momenta => pcm_instance_nlo_set_momenta
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_set_momenta (pcm_instance, p_born, p_real, i_phs, cms)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
type(vector4_t), dimension(:), intent(in) :: p_born, p_real
integer, intent(in) :: i_phs
logical, intent(in), optional :: cms
logical :: yorn
yorn = .false.; if (present (cms)) yorn = cms
associate (kinematics => pcm_instance%real_kinematics)
if (yorn) then
if (.not. kinematics%p_born_cms%initialized) &
call kinematics%p_born_cms%init (size (p_born), 1)
if (.not. kinematics%p_real_cms%initialized) &
call kinematics%p_real_cms%init (size (p_real), 1)
kinematics%p_born_cms%phs_point(1)%p = p_born
kinematics%p_real_cms%phs_point(i_phs)%p = p_real
else
if (.not. kinematics%p_born_lab%initialized) &
call kinematics%p_born_lab%init (size (p_born), 1)
if (.not. kinematics%p_real_lab%initialized) &
call kinematics%p_real_lab%init (size (p_real), 1)
kinematics%p_born_lab%phs_point(1)%p = p_born
kinematics%p_real_lab%phs_point(i_phs)%p = p_real
end if
end associate
end subroutine pcm_instance_nlo_set_momenta
@ %def pcm_instance_nlo_set_momenta
@
<<Pcm: pcm instance: TBP>>=
procedure :: get_momenta => pcm_instance_nlo_get_momenta
<<Pcm: procedures>>=
function pcm_instance_nlo_get_momenta (pcm_instance, i_phs, born_phsp, cms) result (p)
type(vector4_t), dimension(:), allocatable :: p
class(pcm_instance_nlo_t), intent(in) :: pcm_instance
integer, intent(in) :: i_phs
logical, intent(in) :: born_phsp
logical, intent(in), optional :: cms
logical :: yorn
yorn = .false.; if (present (cms)) yorn = cms
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
if (born_phsp) then
if (yorn) then
allocate (p (1 : config%region_data%n_legs_born), &
source = pcm_instance%real_kinematics%p_born_cms%phs_point(1)%p)
else
allocate (p (1 : config%region_data%n_legs_born), &
source = pcm_instance%real_kinematics%p_born_lab%phs_point(1)%p)
end if
else
if (yorn) then
allocate (p (1 : config%region_data%n_legs_real), &
source = pcm_instance%real_kinematics%p_real_cms%phs_point(i_phs)%p)
else
allocate (p ( 1 : config%region_data%n_legs_real), &
source = pcm_instance%real_kinematics%p_real_lab%phs_point(i_phs)%p)
end if
end if
end select
end function pcm_instance_nlo_get_momenta
@ %def pcm_instance_nlo_get_momenta
@
<<Pcm: pcm instance: TBP>>=
procedure :: get_xi_max => pcm_instance_nlo_get_xi_max
<<Pcm: procedures>>=
function pcm_instance_nlo_get_xi_max (pcm_instance, alr) result (xi_max)
real(default) :: xi_max
class(pcm_instance_nlo_t), intent(in) :: pcm_instance
integer, intent(in) :: alr
integer :: i_phs
i_phs = pcm_instance%real_kinematics%alr_to_i_phs (alr)
xi_max = pcm_instance%real_kinematics%xi_max (i_phs)
end function pcm_instance_nlo_get_xi_max
@ %def pcm_instance_nlo_get_xi_max
@
<<Pcm: pcm instance: TBP>>=
procedure :: get_n_born => pcm_instance_nlo_get_n_born
<<Pcm: procedures>>=
function pcm_instance_nlo_get_n_born (pcm_instance) result (n_born)
integer :: n_born
class(pcm_instance_nlo_t), intent(in) :: pcm_instance
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
n_born = config%region_data%n_legs_born
end select
end function pcm_instance_nlo_get_n_born
@ %def pcm_instance_nlo_get_n_born
@
<<Pcm: pcm instance: TBP>>=
procedure :: get_n_real => pcm_instance_nlo_get_n_real
<<Pcm: procedures>>=
function pcm_instance_nlo_get_n_real (pcm_instance) result (n_real)
integer :: n_real
class(pcm_instance_nlo_t), intent(in) :: pcm_instance
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
n_real = config%region_data%n_legs_real
end select
end function pcm_instance_nlo_get_n_real
@ %def pcm_instance_nlo_get_n_real
@
<<Pcm: pcm instance: TBP>>=
procedure :: get_n_regions => pcm_instance_nlo_get_n_regions
<<Pcm: procedures>>=
function pcm_instance_nlo_get_n_regions (pcm_instance) result (n_regions)
integer :: n_regions
class(pcm_instance_nlo_t), intent(in) :: pcm_instance
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
n_regions = config%region_data%n_regions
end select
end function pcm_instance_nlo_get_n_regions
@ %def pcm_instance_nlo_get_n_regions
@
<<Pcm: pcm instance: TBP>>=
procedure :: set_x_rad => pcm_instance_nlo_set_x_rad
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_set_x_rad (pcm_instance, x_tot)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
real(default), intent(in), dimension(:) :: x_tot
integer :: n_par
n_par = size (x_tot)
if (n_par < 3) then
pcm_instance%real_kinematics%x_rad = zero
else
pcm_instance%real_kinematics%x_rad = x_tot (n_par - 2 : n_par)
end if
end subroutine pcm_instance_nlo_set_x_rad
@ %def pcm_instance_nlo_set_x_rad
@
<<Pcm: pcm instance: TBP>>=
procedure :: init_virtual => pcm_instance_nlo_init_virtual
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_init_virtual (pcm_instance, model)
class(pcm_instance_nlo_t), intent(inout), target :: pcm_instance
class(model_data_t), intent(in) :: model
type(nlo_settings_t), pointer :: settings
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
associate (region_data => config%region_data)
settings => config%settings
call pcm_instance%virtual%init (region_data%get_flv_states_born (), &
region_data%n_in, settings, &
region_data%regions(1)%nlo_correction_type, model, config%has_pdfs)
end associate
end select
end subroutine pcm_instance_nlo_init_virtual
@ %def pcm_instance_nlo_init_virtual
@
<<Pcm: pcm instance: TBP>>=
procedure :: disable_virtual_subtraction => pcm_instance_nlo_disable_virtual_subtraction
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_disable_virtual_subtraction (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
end subroutine pcm_instance_nlo_disable_virtual_subtraction
@ %def pcm_instance_nlo_disable_virtual_subtraction
@
<<Pcm: pcm instance: TBP>>=
procedure :: compute_sqme_virt => pcm_instance_nlo_compute_sqme_virt
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_compute_sqme_virt (pcm_instance, p, &
alpha_coupling, separate_alrs, sqme_virt)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(in) :: alpha_coupling
logical, intent(in) :: separate_alrs
real(default), dimension(:), allocatable, intent(inout) :: sqme_virt
type(vector4_t), dimension(:), allocatable :: pp
associate (virtual => pcm_instance%virtual)
allocate (pp (size (p)))
if (virtual%settings%factorization_mode == FACTORIZATION_THRESHOLD) then
pp = pcm_instance%real_kinematics%p_born_onshell%get_momenta (1)
else
pp = p
end if
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
if (separate_alrs) then
allocate (sqme_virt (config%get_n_flv_born ()))
else
allocate (sqme_virt (1))
end if
sqme_virt = zero
call virtual%evaluate (config%region_data, &
alpha_coupling, pp, separate_alrs, sqme_virt)
end select
end associate
end subroutine pcm_instance_nlo_compute_sqme_virt
@ %def pcm_instance_nlo_compute_sqme_virt
@
<<Pcm: pcm instance: TBP>>=
procedure :: compute_sqme_mismatch => pcm_instance_nlo_compute_sqme_mismatch
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_compute_sqme_mismatch (pcm_instance, &
alpha_s, separate_alrs, sqme_mism)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
real(default), intent(in) :: alpha_s
logical, intent(in) :: separate_alrs
real(default), dimension(:), allocatable, intent(inout) :: sqme_mism
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
if (separate_alrs) then
allocate (sqme_mism (config%get_n_flv_born ()))
else
allocate (sqme_mism (1))
end if
sqme_mism = zero
sqme_mism = pcm_instance%soft_mismatch%evaluate (alpha_s)
end select
end subroutine pcm_instance_nlo_compute_sqme_mismatch
@ %def pcm_instance_nlo_compute_sqme_mismatch
@
<<Pcm: pcm instance: TBP>>=
procedure :: compute_sqme_dglap_remnant => pcm_instance_nlo_compute_sqme_dglap_remnant
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_compute_sqme_dglap_remnant (pcm_instance, &
alpha_s, separate_alrs, sqme_dglap)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
real(default), intent(in) :: alpha_s
logical, intent(in) :: separate_alrs
real(default), dimension(:), allocatable, intent(inout) :: sqme_dglap
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
if (separate_alrs) then
allocate (sqme_dglap (config%get_n_flv_born ()))
else
allocate (sqme_dglap (1))
end if
end select
sqme_dglap = zero
call pcm_instance%dglap_remnant%evaluate (alpha_s, separate_alrs, sqme_dglap)
end subroutine pcm_instance_nlo_compute_sqme_dglap_remnant
@ %def pcm_instance_nlo_compute_sqme_dglap_remnant
@
<<Pcm: pcm instance: TBP>>=
procedure :: set_fixed_order_event_mode => pcm_instance_nlo_set_fixed_order_event_mode
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_set_fixed_order_event_mode (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
pcm_instance%real_sub%purpose = FIXED_ORDER_EVENTS
end subroutine pcm_instance_nlo_set_fixed_order_event_mode
<<Pcm: pcm instance: TBP>>=
procedure :: set_powheg_mode => pcm_instance_nlo_set_powheg_mode
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_set_powheg_mode (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
pcm_instance%real_sub%purpose = POWHEG
end subroutine pcm_instance_nlo_set_powheg_mode
@ %def pcm_instance_nlo_set_fixed_order_event_mode
@ %def pcm_instance_nlo_set_powheg_mode
@
<<Pcm: pcm instance: TBP>>=
procedure :: init_soft_mismatch => pcm_instance_nlo_init_soft_mismatch
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_init_soft_mismatch (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
call pcm_instance%soft_mismatch%init (config%region_data, &
pcm_instance%real_kinematics, config%settings%factorization_mode)
end select
end subroutine pcm_instance_nlo_init_soft_mismatch
@ %def pcm_instance_nlo_init_soft_mismatch
@
<<Pcm: pcm instance: TBP>>=
procedure :: init_dglap_remnant => pcm_instance_nlo_init_dglap_remnant
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_init_dglap_remnant (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
call pcm_instance%dglap_remnant%init ( &
config%settings, &
- config%region_data%n_flv_born, &
- pcm_instance%isr_kinematics, &
- config%region_data%get_flv_states_born (), config%get_n_alr ())
+ config%region_data, &
+ pcm_instance%isr_kinematics)
end select
end subroutine pcm_instance_nlo_init_dglap_remnant
@ %def pcm_instance_nlo_init_dglap_remnant
@
<<Pcm: pcm instance: TBP>>=
procedure :: is_fixed_order_nlo_events &
=> pcm_instance_nlo_is_fixed_order_nlo_events
<<Pcm: procedures>>=
function pcm_instance_nlo_is_fixed_order_nlo_events (pcm_instance) result (is_nlo)
logical :: is_nlo
class(pcm_instance_nlo_t), intent(in) :: pcm_instance
is_nlo = pcm_instance%real_sub%purpose == FIXED_ORDER_EVENTS
end function pcm_instance_nlo_is_fixed_order_nlo_events
@ %def pcm_instance_nlo_is_fixed_order_nlo_events
@
<<Pcm: pcm instance: TBP>>=
procedure :: final => pcm_instance_nlo_final
<<Pcm: procedures>>=
subroutine pcm_instance_nlo_final (pcm_instance)
class(pcm_instance_nlo_t), intent(inout) :: pcm_instance
call pcm_instance%real_sub%final ()
call pcm_instance%virtual%final ()
call pcm_instance%soft_mismatch%final ()
call pcm_instance%dglap_remnant%final ()
if (associated (pcm_instance%real_kinematics)) then
call pcm_instance%real_kinematics%final ()
nullify (pcm_instance%real_kinematics)
end if
if (associated (pcm_instance%isr_kinematics)) then
nullify (pcm_instance%isr_kinematics)
end if
end subroutine pcm_instance_nlo_final
@ %def pcm_instance_nlo_final
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Kinematics instance}
In this data type we combine all objects (instances) necessary for
generating (or recovering) a kinematical configuration. The
components work together as an implementation of multi-channel phase
space.
[[sf_chain]] is an instance of the structure-function chain. It is
used both for generating kinematics and, after the proper scale has
been determined, evaluating the structure function entries.
[[phs]] is an instance of the phase space for the elementary process.
The array [[f]] contains the products of the Jacobians that originate
from parameter mappings in the structure-function chain or in the
phase space. We allocate this explicitly if either [[sf_chain]] or
[[phs]] are explicitly allocated, otherwise we can take over a pointer.
All components are implemented as pointers to (anonymous) targets.
For each component, there is a flag that tells whether this component
is to be regarded as a proper component (`owned' by the object) or as
a pointer.
@
<<[[kinematics.f90]]>>=
<<File header>>
module kinematics
<<Use kinds>>
use format_utils, only: write_separator
use diagnostics
use io_units
use lorentz
use physics_defs
use sf_base
use phs_base
use interactions
use mci_base
use phs_fks
use fks_regions
use process_config
use process_mci
use pcm, only: pcm_instance_nlo_t
use ttv_formfactors, only: m1s_to_mpole
<<Standard module head>>
<<Kinematics: public>>
<<Kinematics: types>>
contains
<<Kinematics: procedures>>
end module kinematics
@ %def kinematics
<<Kinematics: public>>=
public :: kinematics_t
<<Kinematics: types>>=
type :: kinematics_t
integer :: n_in = 0
integer :: n_channel = 0
integer :: selected_channel = 0
type(sf_chain_instance_t), pointer :: sf_chain => null ()
class(phs_t), pointer :: phs => null ()
real(default), dimension(:), pointer :: f => null ()
real(default) :: phs_factor
logical :: sf_chain_allocated = .false.
logical :: phs_allocated = .false.
logical :: f_allocated = .false.
integer :: emitter = -1
integer :: i_phs = 0
integer :: i_con = 0
logical :: only_cm_frame = .false.
logical :: new_seed = .true.
logical :: threshold = .false.
contains
<<Kinematics: kinematics: TBP>>
end type kinematics_t
@ %def kinematics_t
@ Output. Show only those components which are marked as owned.
<<Kinematics: kinematics: TBP>>=
procedure :: write => kinematics_write
<<Kinematics: procedures>>=
subroutine kinematics_write (object, unit)
class(kinematics_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u, c
u = given_output_unit (unit)
if (object%f_allocated) then
write (u, "(1x,A)") "Flux * PHS volume:"
write (u, "(2x,ES19.12)") object%phs_factor
write (u, "(1x,A)") "Jacobian factors per channel:"
do c = 1, size (object%f)
write (u, "(3x,I0,':',1x,ES14.7)", advance="no") c, object%f(c)
if (c == object%selected_channel) then
write (u, "(1x,A)") "[selected]"
else
write (u, *)
end if
end do
end if
if (object%sf_chain_allocated) then
call write_separator (u)
call object%sf_chain%write (u)
end if
if (object%phs_allocated) then
call write_separator (u)
call object%phs%write (u)
end if
end subroutine kinematics_write
@ %def kinematics_write
@ Finalizer. Delete only those components which are marked as owned.
<<Kinematics: kinematics: TBP>>=
procedure :: final => kinematics_final
<<Kinematics: procedures>>=
subroutine kinematics_final (object)
class(kinematics_t), intent(inout) :: object
if (object%sf_chain_allocated) then
call object%sf_chain%final ()
deallocate (object%sf_chain)
object%sf_chain_allocated = .false.
end if
if (object%phs_allocated) then
call object%phs%final ()
deallocate (object%phs)
object%phs_allocated = .false.
end if
if (object%f_allocated) then
deallocate (object%f)
object%f_allocated = .false.
end if
end subroutine kinematics_final
@ %def kinematics_final
@ Set the flags indicating whether the phase space shall be set up for the calculation of the real contribution. For this case, also set the emitter.
<<Kinematics: kinematics: TBP>>=
procedure :: set_nlo_info => kinematics_set_nlo_info
<<Kinematics: procedures>>=
subroutine kinematics_set_nlo_info (k, nlo_type)
class(kinematics_t), intent(inout) :: k
integer, intent(in) :: nlo_type
if (nlo_type == NLO_VIRTUAL) k%only_cm_frame = .true.
end subroutine kinematics_set_nlo_info
@ %def kinematics_set_nlo_info
@ Allocate the structure-function chain instance, initialize it as a
copy of the [[sf_chain]] template, and prepare it for evaluation.
The [[sf_chain]] remains a target because the (usually constant) beam momenta
are taken from there.
<<Kinematics: kinematics: TBP>>=
procedure :: init_sf_chain => kinematics_init_sf_chain
<<Kinematics: procedures>>=
subroutine kinematics_init_sf_chain (k, sf_chain, config, extended_sf)
class(kinematics_t), intent(inout) :: k
type(sf_chain_t), intent(in), target :: sf_chain
type(process_beam_config_t), intent(in) :: config
logical, intent(in), optional :: extended_sf
integer :: n_strfun, n_channel
integer :: c
k%n_in = config%data%get_n_in ()
n_strfun = config%n_strfun
n_channel = config%n_channel
allocate (k%sf_chain)
k%sf_chain_allocated = .true.
call k%sf_chain%init (sf_chain, n_channel)
if (n_strfun /= 0) then
do c = 1, n_channel
call k%sf_chain%set_channel (c, config%sf_channel(c))
end do
end if
call k%sf_chain%link_interactions ()
call k%sf_chain%exchange_mask ()
call k%sf_chain%init_evaluators (extended_sf = extended_sf)
end subroutine kinematics_init_sf_chain
@ %def kinematics_init_sf_chain
@ Allocate and initialize the phase-space part and the array of
Jacobian factors.
<<Kinematics: kinematics: TBP>>=
procedure :: init_phs => kinematics_init_phs
<<Kinematics: procedures>>=
subroutine kinematics_init_phs (k, config)
class(kinematics_t), intent(inout) :: k
class(phs_config_t), intent(in), target :: config
k%n_channel = config%get_n_channel ()
call config%allocate_instance (k%phs)
call k%phs%init (config)
k%phs_allocated = .true.
allocate (k%f (k%n_channel))
k%f = 0
k%f_allocated = .true.
end subroutine kinematics_init_phs
@ %def kinematics_init_phs
@
<<Kinematics: kinematics: TBP>>=
procedure :: evaluate_radiation_kinematics => kinematics_evaluate_radiation_kinematics
<<Kinematics: procedures>>=
subroutine kinematics_evaluate_radiation_kinematics (k, r_in)
class(kinematics_t), intent(inout) :: k
real(default), intent(in), dimension(:) :: r_in
select type (phs => k%phs)
type is (phs_fks_t)
call phs%generate_radiation_variables &
(r_in(phs%n_r_born + 1 : phs%n_r_born + 3), k%threshold)
call phs%compute_cms_energy ()
end select
end subroutine kinematics_evaluate_radiation_kinematics
@ %def kinematics_evaluate_radiation_kinematics
@
<<Kinematics: kinematics: TBP>>=
procedure :: compute_xi_ref_momenta => kinematics_compute_xi_ref_momenta
<<Kinematics: procedures>>=
subroutine kinematics_compute_xi_ref_momenta (k, reg_data, nlo_type)
class(kinematics_t), intent(inout) :: k
type(region_data_t), intent(in) :: reg_data
integer, intent(in) :: nlo_type
logical :: use_contributors
use_contributors = allocated (reg_data%alr_contributors)
select type (phs => k%phs)
type is (phs_fks_t)
if (use_contributors) then
call phs%compute_xi_ref_momenta (contributors = reg_data%alr_contributors)
else if (k%threshold) then
if (.not. is_subtraction_component (k%emitter, nlo_type)) &
call phs%compute_xi_ref_momenta_threshold ()
else
call phs%compute_xi_ref_momenta ()
end if
end select
end subroutine kinematics_compute_xi_ref_momenta
@ %def kinematics_compute_xi_ref_momenta
@ Generate kinematics, given a phase-space channel and a MC
parameter set. The main result is the momentum array [[p]], but we
also fill the momentum entries in the structure-function chain and the
Jacobian-factor array [[f]]. Regarding phase space, We fill only the
parameter arrays for the selected channel.
<<Kinematics: kinematics: TBP>>=
procedure :: compute_selected_channel => kinematics_compute_selected_channel
<<Kinematics: procedures>>=
subroutine kinematics_compute_selected_channel &
(k, mci_work, phs_channel, p, success)
class(kinematics_t), intent(inout) :: k
type(mci_work_t), intent(in) :: mci_work
integer, intent(in) :: phs_channel
type(vector4_t), dimension(:), intent(out) :: p
logical, intent(out) :: success
integer :: sf_channel
k%selected_channel = phs_channel
sf_channel = k%phs%config%get_sf_channel (phs_channel)
call k%sf_chain%compute_kinematics (sf_channel, mci_work%get_x_strfun ())
call k%sf_chain%get_out_momenta (p(1:k%n_in))
call k%phs%set_incoming_momenta (p(1:k%n_in))
call k%phs%compute_flux ()
call k%phs%select_channel (phs_channel)
call k%phs%evaluate_selected_channel (phs_channel, &
mci_work%get_x_process ())
select type (phs => k%phs)
type is (phs_fks_t)
if (phs%q_defined) then
call phs%get_born_momenta (p)
k%phs_factor = phs%get_overall_factor ()
success = .true.
else
k%phs_factor = 0
success = .false.
end if
class default
if (phs%q_defined) then
call k%phs%get_outgoing_momenta (p(k%n_in + 1 :))
k%phs_factor = k%phs%get_overall_factor ()
success = .true.
if (k%only_cm_frame) then
if (.not. k%lab_is_cm_frame()) &
call k%boost_to_cm_frame (p)
end if
else
k%phs_factor = 0
success = .false.
end if
end select
end subroutine kinematics_compute_selected_channel
@ %def kinematics_compute_selected_channel
@ Complete kinematics by filling the non-selected phase-space parameter
arrays.
<<Kinematics: kinematics: TBP>>=
procedure :: compute_other_channels => kinematics_compute_other_channels
<<Kinematics: procedures>>=
subroutine kinematics_compute_other_channels (k, mci_work, phs_channel)
class(kinematics_t), intent(inout) :: k
type(mci_work_t), intent(in) :: mci_work
integer, intent(in) :: phs_channel
integer :: c, c_sf
call k%phs%evaluate_other_channels (phs_channel)
do c = 1, k%n_channel
c_sf = k%phs%config%get_sf_channel (c)
k%f(c) = k%sf_chain%get_f (c_sf) * k%phs%get_f (c)
end do
end subroutine kinematics_compute_other_channels
@ %def kinematics_compute_other_channels
@ Just fetch the outgoing momenta of the [[sf_chain]] subobject, which
become the incoming (seed) momenta of the hard interaction.
This is a stripped down-version of the above which we use when
recovering kinematics. Momenta are known, but no MC parameters yet.
(We do not use the [[get_out_momenta]] method of the chain, since this
relies on the structure-function interactions, which are not necessary
filled here. We do rely on the momenta of the last evaluator in the
chain, however.)
<<Kinematics: kinematics: TBP>>=
procedure :: get_incoming_momenta => kinematics_get_incoming_momenta
<<Kinematics: procedures>>=
subroutine kinematics_get_incoming_momenta (k, p)
class(kinematics_t), intent(in) :: k
type(vector4_t), dimension(:), intent(out) :: p
type(interaction_t), pointer :: int
integer :: i
int => k%sf_chain%get_out_int_ptr ()
do i = 1, k%n_in
p(i) = int%get_momentum (k%sf_chain%get_out_i (i))
end do
end subroutine kinematics_get_incoming_momenta
@ %def kinematics_get_incoming_momenta
@ This inverts the remainder of the above [[compute]] method. We know
the momenta and recover the rest, as far as needed. If we select a
channel, we can complete the inversion and reconstruct the
MC parameter set.
<<Kinematics: kinematics: TBP>>=
procedure :: recover_mcpar => kinematics_recover_mcpar
<<Kinematics: procedures>>=
subroutine kinematics_recover_mcpar (k, mci_work, phs_channel, p)
class(kinematics_t), intent(inout) :: k
type(mci_work_t), intent(inout) :: mci_work
integer, intent(in) :: phs_channel
type(vector4_t), dimension(:), intent(in) :: p
integer :: c, c_sf
real(default), dimension(:), allocatable :: x_sf, x_phs
c = phs_channel
c_sf = k%phs%config%get_sf_channel (c)
k%selected_channel = c
call k%sf_chain%recover_kinematics (c_sf)
call k%phs%set_incoming_momenta (p(1:k%n_in))
call k%phs%compute_flux ()
call k%phs%set_outgoing_momenta (p(k%n_in+1:))
call k%phs%inverse ()
do c = 1, k%n_channel
c_sf = k%phs%config%get_sf_channel (c)
k%f(c) = k%sf_chain%get_f (c_sf) * k%phs%get_f (c)
end do
k%phs_factor = k%phs%get_overall_factor ()
c = phs_channel
c_sf = k%phs%config%get_sf_channel (c)
allocate (x_sf (k%sf_chain%config%get_n_bound ()))
allocate (x_phs (k%phs%config%get_n_par ()))
call k%phs%select_channel (c)
call k%sf_chain%get_mcpar (c_sf, x_sf)
call k%phs%get_mcpar (c, x_phs)
call mci_work%set_x_strfun (x_sf)
call mci_work%set_x_process (x_phs)
end subroutine kinematics_recover_mcpar
@ %def kinematics_recover_mcpar
@ This first part of [[recover_mcpar]]: just handle the sfchain.
<<Kinematics: kinematics: TBP>>=
procedure :: recover_sfchain => kinematics_recover_sfchain
<<Kinematics: procedures>>=
subroutine kinematics_recover_sfchain (k, channel, p)
class(kinematics_t), intent(inout) :: k
integer, intent(in) :: channel
type(vector4_t), dimension(:), intent(in) :: p
k%selected_channel = channel
call k%sf_chain%recover_kinematics (channel)
end subroutine kinematics_recover_sfchain
@ %def kinematics_recover_sfchain
@ Retrieve the MC input parameter array for a specific channel. We assume
that the kinematics is complete, so this is known for all channels.
<<Kinematics: kinematics: TBP>>=
procedure :: get_mcpar => kinematics_get_mcpar
<<Kinematics: procedures>>=
subroutine kinematics_get_mcpar (k, phs_channel, r)
class(kinematics_t), intent(in) :: k
integer, intent(in) :: phs_channel
real(default), dimension(:), intent(out) :: r
integer :: sf_channel, n_par_sf, n_par_phs
sf_channel = k%phs%config%get_sf_channel (phs_channel)
n_par_phs = k%phs%config%get_n_par ()
n_par_sf = k%sf_chain%config%get_n_bound ()
if (n_par_sf > 0) then
call k%sf_chain%get_mcpar (sf_channel, r(1:n_par_sf))
end if
if (n_par_phs > 0) then
call k%phs%get_mcpar (phs_channel, r(n_par_sf+1:))
end if
end subroutine kinematics_get_mcpar
@ %def kinematics_get_mcpar
@ Evaluate the structure function chain, assuming that kinematics is known.
The status must be precisely [[SF_DONE_KINEMATICS]]. We thus avoid
evaluating the chain twice via different pointers to the same target.
<<Kinematics: kinematics: TBP>>=
procedure :: evaluate_sf_chain => kinematics_evaluate_sf_chain
<<Kinematics: procedures>>=
subroutine kinematics_evaluate_sf_chain (k, fac_scale, sf_rescale)
class(kinematics_t), intent(inout) :: k
real(default), intent(in) :: fac_scale
class(sf_rescale_t), intent(inout), optional :: sf_rescale
select case (k%sf_chain%get_status ())
case (SF_DONE_KINEMATICS)
call k%sf_chain%evaluate (fac_scale, sf_rescale)
end select
end subroutine kinematics_evaluate_sf_chain
@ %def kinematics_evaluate_sf_chain
@ Recover beam momenta, i.e., return the beam momenta stored in the
current [[sf_chain]] to their source. This is a side effect.
<<Kinematics: kinematics: TBP>>=
procedure :: return_beam_momenta => kinematics_return_beam_momenta
<<Kinematics: procedures>>=
subroutine kinematics_return_beam_momenta (k)
class(kinematics_t), intent(in) :: k
call k%sf_chain%return_beam_momenta ()
end subroutine kinematics_return_beam_momenta
@ %def kinematics_return_beam_momenta
@ Check wether the phase space is configured in the center-of-mass frame.
Relevant for using the proper momenta input for BLHA matrix elements.
<<Kinematics: kinematics: TBP>>=
procedure :: lab_is_cm_frame => kinematics_lab_is_cm_frame
<<Kinematics: procedures>>=
function kinematics_lab_is_cm_frame (k) result (cm_frame)
logical :: cm_frame
class(kinematics_t), intent(in) :: k
cm_frame = k%phs%config%cm_frame
end function kinematics_lab_is_cm_frame
@ %def kinematics_lab_is_cm_frame
@ Boost to center-of-mass frame
<<Kinematics: kinematics: TBP>>=
procedure :: boost_to_cm_frame => kinematics_boost_to_cm_frame
<<Kinematics: procedures>>=
subroutine kinematics_boost_to_cm_frame (k, p)
class(kinematics_t), intent(in) :: k
type(vector4_t), intent(inout), dimension(:) :: p
p = inverse (k%phs%lt_cm_to_lab) * p
end subroutine kinematics_boost_to_cm_frame
@ %def kinematics_boost_to_cm_frame
@
<<Kinematics: kinematics: TBP>>=
procedure :: modify_momenta_for_subtraction => kinematics_modify_momenta_for_subtraction
<<Kinematics: procedures>>=
subroutine kinematics_modify_momenta_for_subtraction (k, p_in, p_out)
class(kinematics_t), intent(inout) :: k
type(vector4_t), intent(in), dimension(:) :: p_in
type(vector4_t), intent(out), dimension(:), allocatable :: p_out
allocate (p_out (size (p_in)))
if (k%threshold) then
select type (phs => k%phs)
type is (phs_fks_t)
p_out = phs%get_onshell_projected_momenta ()
end select
else
p_out = p_in
end if
end subroutine kinematics_modify_momenta_for_subtraction
@ %def kinematics_modify_momenta_for_subtraction
@
<<Kinematics: kinematics: TBP>>=
procedure :: threshold_projection => kinematics_threshold_projection
<<Kinematics: procedures>>=
subroutine kinematics_threshold_projection (k, pcm_instance, nlo_type)
class(kinematics_t), intent(inout) :: k
type(pcm_instance_nlo_t), intent(inout) :: pcm_instance
integer, intent(in) :: nlo_type
real(default) :: sqrts, mtop
type(lorentz_transformation_t) :: L_to_cms
type(vector4_t), dimension(:), allocatable :: p_tot
integer :: n_tot
n_tot = k%phs%get_n_tot ()
allocate (p_tot (size (pcm_instance%real_kinematics%p_born_cms%phs_point(1)%p)))
select type (phs => k%phs)
type is (phs_fks_t)
p_tot = pcm_instance%real_kinematics%p_born_cms%phs_point(1)%p
class default
p_tot(1 : k%n_in) = phs%p
p_tot(k%n_in + 1 : n_tot) = phs%q
end select
sqrts = sum (p_tot (1:k%n_in))**1
mtop = m1s_to_mpole (sqrts)
L_to_cms = get_boost_for_threshold_projection (p_tot, sqrts, mtop)
call pcm_instance%real_kinematics%p_born_cms%set_momenta (1, p_tot)
associate (p_onshell => pcm_instance%real_kinematics%p_born_onshell%phs_point(1)%p)
call threshold_projection_born (mtop, L_to_cms, p_tot, p_onshell)
if (debug2_active (D_THRESHOLD)) then
print *, 'On-shell projected Born: '
call vector4_write_set (p_onshell)
end if
end associate
end subroutine kinematics_threshold_projection
@ %def kinematics_threshold_projection
@
<<Kinematics: kinematics: TBP>>=
procedure :: evaluate_radiation => kinematics_evaluate_radiation
<<Kinematics: procedures>>=
subroutine kinematics_evaluate_radiation (k, p_in, p_out, success)
class(kinematics_t), intent(inout) :: k
type(vector4_t), intent(in), dimension(:) :: p_in
type(vector4_t), intent(out), dimension(:), allocatable :: p_out
logical, intent(out) :: success
type(vector4_t), dimension(:), allocatable :: p_real
type(vector4_t), dimension(:), allocatable :: p_born
real(default) :: xi_max_offshell, xi_offshell, y_offshell, jac_rand_dummy, phi
select type (phs => k%phs)
type is (phs_fks_t)
allocate (p_born (size (p_in)))
if (k%threshold) then
p_born = phs%get_onshell_projected_momenta ()
else
p_born = p_in
end if
if (.not. k%phs%is_cm_frame () .and. .not. k%threshold) then
p_born = inverse (k%phs%lt_cm_to_lab) * p_born
end if
call phs%compute_xi_max (p_born, k%threshold)
if (k%emitter >= 0) then
allocate (p_real (size (p_born) + 1))
allocate (p_out (size (p_born) + 1))
if (k%emitter <= k%n_in) then
call phs%generate_isr (k%i_phs, p_real)
else
if (k%threshold) then
jac_rand_dummy = 1._default
call compute_y_from_emitter (phs%generator%real_kinematics%x_rad (I_Y), &
phs%generator%real_kinematics%p_born_cms%get_momenta(1), &
k%n_in, k%emitter, .false., phs%generator%y_max, jac_rand_dummy, &
y_offshell)
call phs%compute_xi_max (k%emitter, k%i_phs, y_offshell, &
phs%generator%real_kinematics%p_born_cms%get_momenta(1), &
xi_max_offshell)
xi_offshell = xi_max_offshell * phs%generator%real_kinematics%xi_tilde
phi = phs%generator%real_kinematics%phi
call phs%generate_fsr (k%emitter, k%i_phs, p_real, &
xi_y_phi = [xi_offshell, y_offshell, phi], no_jacobians = .true.)
call phs%generator%real_kinematics%p_real_cms%set_momenta (k%i_phs, p_real)
call phs%generate_fsr_threshold (k%emitter, k%i_phs, p_real)
if (debug2_active (D_SUBTRACTION)) &
call generate_fsr_threshold_for_other_emitters (k%emitter, k%i_phs)
else if (k%i_con > 0) then
call phs%generate_fsr (k%emitter, k%i_phs, p_real, k%i_con)
else
call phs%generate_fsr (k%emitter, k%i_phs, p_real)
end if
end if
success = check_scalar_products (p_real)
if (debug2_active (D_SUBTRACTION)) then
call msg_debug2 (D_SUBTRACTION, "Real phase-space: ")
call vector4_write_set (p_real)
end if
p_out = p_real
else
allocate (p_out (size (p_in))); p_out = p_in
success = .true.
end if
end select
contains
subroutine generate_fsr_threshold_for_other_emitters (emitter, i_phs)
integer, intent(in) :: emitter, i_phs
integer :: ii_phs, this_emitter
select type (phs => k%phs)
type is (phs_fks_t)
do ii_phs = 1, size (phs%phs_identifiers)
this_emitter = phs%phs_identifiers(ii_phs)%emitter
if (ii_phs /= i_phs .and. this_emitter /= emitter) &
call phs%generate_fsr_threshold (this_emitter, i_phs)
end do
end select
end subroutine
end subroutine kinematics_evaluate_radiation
@ %def kinematics_evaluate_radiation
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Instances}
<<[[instances.f90]]>>=
<<File header>>
module instances
<<Use kinds>>
<<Use strings>>
<<Use debug>>
use io_units
use format_utils, only: write_separator
use constants
use diagnostics
use os_interface
use numeric_utils
use lorentz
use mci_base
use particles
use sm_qcd, only: qcd_t
use interactions
use quantum_numbers
use model_data
use helicities
use flavors
use beam_structures
use variables
use pdg_arrays, only: is_quark
use sf_base
- use isr_collinear
use physics_defs
use process_constants
use process_libraries
use state_matrices
use integration_results
use phs_base
use prc_core, only: prc_core_t, prc_core_state_t
!!! We should depend less on these modules (move it to pcm_nlo_t e.g.)
use phs_wood, only: phs_wood_t
use phs_fks
use blha_olp_interfaces, only: prc_blha_t
use blha_config, only: BLHA_AMP_COLOR_C
use prc_external, only: prc_external_t, prc_external_state_t
use prc_threshold, only: prc_threshold_t
use blha_olp_interfaces, only: blha_result_array_size
use prc_openloops, only: prc_openloops_t, openloops_state_t
use prc_recola, only: prc_recola_t
use blha_olp_interfaces, only: blha_color_c_fill_offdiag, blha_color_c_fill_diag
use ttv_formfactors, only: m1s_to_mpole
!!! local modules
use parton_states
use process_counter
use pcm_base
use pcm
use process_config
use process_mci
use process
use kinematics
<<Standard module head>>
<<Instances: public>>
<<Instances: types>>
<<Instances: interfaces>>
contains
<<Instances: procedures>>
end module instances
@ %def instances
@
\subsection{Term instance}
A [[term_instance_t]] object contains all data that describe a term. Each
process component consists of one or more distinct terms which may differ in
kinematics, but whose squared transition matrices have to be added pointwise.
The [[active]] flag is set when this term is connected to an active
process component. Inactive terms are skipped for kinematics and evaluation.
The [[k_term]] object is the instance of the kinematics setup
(structure-function chain, phase space, etc.) that applies
specifically to this term. In ordinary cases, it consists of straight
pointers to the seed kinematics.
The [[amp]] array stores the amplitude values when we get them from evaluating
the associated matrix-element code.
The [[int_hard]] interaction describes the elementary hard process.
It receives the momenta and the amplitude entries for each sampling point.
The [[isolated]] object holds the effective parton state for the
elementary interaction. The amplitude entries are
computed from [[int_hard]].
The [[connected]] evaluator set
convolutes this scattering matrix with the beam (and possibly
structure-function) density matrix.
The [[checked]] flag is set once we have applied cuts on this term.
The result of this is stored in the [[passed]] flag. Once the term
has passed cuts, we calculate the various scale and weight expressions.
<<Instances: types>>=
type :: term_instance_t
type(process_term_t), pointer :: config => null ()
logical :: active = .false.
type(kinematics_t) :: k_term
complex(default), dimension(:), allocatable :: amp
type(interaction_t) :: int_hard
type(isolated_state_t) :: isolated
type(connected_state_t) :: connected
class(prc_core_state_t), allocatable :: core_state
logical :: checked = .false.
logical :: passed = .false.
real(default) :: scale = 0
real(default) :: fac_scale = 0
real(default) :: ren_scale = 0
real(default), allocatable :: alpha_qcd_forced
real(default) :: weight = 1
type(vector4_t), dimension(:), allocatable :: p_seed
type(vector4_t), dimension(:), allocatable :: p_hard
class(pcm_instance_t), pointer :: pcm_instance => null ()
integer :: nlo_type = BORN
integer, dimension(:), allocatable :: same_kinematics
type(qn_index_map_t) :: connected_qn_index
type(qn_index_map_t) :: hard_qn_index
+ type(qn_index_map_t) :: sf_qn_index
contains
<<Instances: term instance: TBP>>
end type term_instance_t
@ %def term_instance_t
@
<<Instances: term instance: TBP>>=
procedure :: write => term_instance_write
<<Instances: procedures>>=
subroutine term_instance_write (term, unit, show_eff_state, testflag)
class(term_instance_t), intent(in) :: term
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_eff_state
logical, intent(in), optional :: testflag
integer :: u
logical :: state
u = given_output_unit (unit)
state = .true.; if (present (show_eff_state)) state = show_eff_state
if (term%active) then
if (associated (term%config)) then
write (u, "(1x,A,I0,A,I0,A)") "Term #", term%config%i_term, &
" (component #", term%config%i_component, ")"
else
write (u, "(1x,A)") "Term [undefined]"
end if
else
write (u, "(1x,A,I0,A)") "Term #", term%config%i_term, &
" [inactive]"
end if
if (term%checked) then
write (u, "(3x,A,L1)") "passed cuts = ", term%passed
end if
if (term%passed) then
write (u, "(3x,A,ES19.12)") "overall scale = ", term%scale
write (u, "(3x,A,ES19.12)") "factorization scale = ", term%fac_scale
write (u, "(3x,A,ES19.12)") "renormalization scale = ", term%ren_scale
if (allocated (term%alpha_qcd_forced)) then
write (u, "(3x,A,ES19.12)") "alpha(QCD) forced = ", &
term%alpha_qcd_forced
end if
write (u, "(3x,A,ES19.12)") "reweighting factor = ", term%weight
end if
call term%k_term%write (u)
call write_separator (u)
write (u, "(1x,A)") "Amplitude (transition matrix of the &
&hard interaction):"
call write_separator (u)
call term%int_hard%basic_write (u, testflag = testflag)
if (state .and. term%isolated%has_trace) then
call write_separator (u)
write (u, "(1x,A)") "Evaluators for the hard interaction:"
call term%isolated%write (u, testflag = testflag)
end if
if (state .and. term%connected%has_trace) then
call write_separator (u)
write (u, "(1x,A)") "Evaluators for the connected process:"
call term%connected%write (u, testflag = testflag)
end if
end subroutine term_instance_write
@ %def term_instance_write
@ The interactions and evaluators must be finalized.
<<Instances: term instance: TBP>>=
procedure :: final => term_instance_final
<<Instances: procedures>>=
subroutine term_instance_final (term)
class(term_instance_t), intent(inout) :: term
if (allocated (term%amp)) deallocate (term%amp)
if (allocated (term%core_state)) deallocate (term%core_state)
if (allocated (term%alpha_qcd_forced)) &
deallocate (term%alpha_qcd_forced)
if (allocated (term%p_seed)) deallocate(term%p_seed)
if (allocated (term%p_hard)) deallocate (term%p_hard)
call term%k_term%final ()
call term%connected%final ()
call term%isolated%final ()
call term%int_hard%final ()
term%pcm_instance => null ()
end subroutine term_instance_final
@ %def term_instance_final
@ For initialization, we make use of defined assignment for the
[[interaction_t]] type. This creates a deep copy.
The hard interaction (incoming momenta) is linked to the structure
function instance. In the isolated state, we either set pointers to
both, or we create modified copies ([[rearrange]]) as effective
structure-function chain and interaction, respectively.
Finally, we set up the [[subevt]] component that will be used for
evaluating observables, collecting particles from the trace evaluator
in the effective connected state. Their quantum numbers must be
determined by following back source links and set explicitly, since
they are already eliminated in that trace.
The [[rearrange]] parts are still commented out; they could become
relevant for a NLO algorithm.
<<Instances: term instance: TBP>>=
procedure :: init => term_instance_init
<<Instances: procedures>>=
subroutine term_instance_init (term, process, i_term, real_finite)
class(term_instance_t), intent(inout), target :: term
type(process_t), intent(in), target:: process
integer, intent(in) :: i_term
logical, intent(in), optional :: real_finite
class(prc_core_t), pointer :: core => null ()
type(process_beam_config_t) :: beam_config
type(interaction_t), pointer :: sf_chain_int
type(interaction_t), pointer :: src_int
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask_in
type(state_matrix_t), pointer :: state_matrix
type(flavor_t), dimension(:), allocatable :: flv_int, flv_src, f_in, f_out
+ integer, dimension(:,:), allocatable :: flv_born, flv_real
+ type(flavor_t), dimension(:,:), allocatable :: flv_pdf
+ type(quantum_numbers_t), dimension(:,:), allocatable :: qn_pdf
integer :: n_in, n_vir, n_out, n_tot, n_sub
+ integer :: n_flv_born, n_flv_real, n_flv_total
integer :: i, j
logical :: me_already_squared, keep_fs_flavors
logical :: decrease_n_tot
logical :: requires_extended_sf
me_already_squared = .false.
keep_fs_flavors = .false.
term%config => process%get_term_ptr (i_term)
term%int_hard = term%config%int
core => process%get_core_term (i_term)
call core%allocate_workspace (term%core_state)
select type (core)
class is (prc_external_t)
call reduce_interaction (term%int_hard, &
core%includes_polarization (), .true., .false.)
me_already_squared = .true.
allocate (term%amp (term%int_hard%get_n_matrix_elements ()))
class default
allocate (term%amp (term%config%n_allowed))
end select
if (allocated (term%core_state)) then
select type (core_state => term%core_state)
type is (openloops_state_t)
call core_state%init_threshold (process%get_model_ptr ())
end select
end if
term%amp = cmplx (0, 0, default)
decrease_n_tot = term%nlo_type == NLO_REAL .and. &
term%config%i_term_global /= term%config%i_sub
if (present (real_finite)) then
if (real_finite) decrease_n_tot = .false.
end if
if (decrease_n_tot) then
allocate (term%p_seed (term%int_hard%get_n_tot () - 1))
else
allocate (term%p_seed (term%int_hard%get_n_tot ()))
end if
allocate (term%p_hard (term%int_hard%get_n_tot ()))
sf_chain_int => term%k_term%sf_chain%get_out_int_ptr ()
n_in = term%int_hard%get_n_in ()
do j = 1, n_in
i = term%k_term%sf_chain%get_out_i (j)
call term%int_hard%set_source_link (j, sf_chain_int, i)
end do
call term%isolated%init (term%k_term%sf_chain, term%int_hard)
allocate (mask_in (n_in))
mask_in = term%k_term%sf_chain%get_out_mask ()
select type (phs => term%k_term%phs)
type is (phs_wood_t)
if (me_already_squared) then
call term%isolated%setup_identity_trace (core, mask_in, .true., .false.)
else
call term%isolated%setup_square_trace (core, mask_in, term%config%col, .false.)
end if
type is (phs_fks_t)
select case (phs%mode)
case (PHS_MODE_ADDITIONAL_PARTICLE)
if (me_already_squared) then
call term%isolated%setup_identity_trace (core, mask_in, .true., .false.)
else
keep_fs_flavors = term%config%data%n_flv > 1
call term%isolated%setup_square_trace (core, mask_in, term%config%col, &
keep_fs_flavors)
end if
case (PHS_MODE_COLLINEAR_REMNANT)
if (me_already_squared) then
call term%isolated%setup_identity_trace (core, mask_in, .true., .false.)
else
call term%isolated%setup_square_trace (core, mask_in, term%config%col, .false.)
end if
end select
class default
call term%isolated%setup_square_trace (core, mask_in, term%config%col, .false.)
end select
if (term%nlo_type == NLO_VIRTUAL .or. (term%nlo_type == NLO_REAL .and. &
term%config%i_term_global == term%config%i_sub) .or. &
term%nlo_type == NLO_MISMATCH) then
n_sub = term%get_n_sub ()
else if (term%nlo_type == NLO_DGLAP) then
- n_sub = n_beam_structure_int
+ n_sub = n_beams_rescaled
else
!!! No integration of real subtraction in interactions yet
n_sub = 0
end if
keep_fs_flavors = keep_fs_flavors .or. me_already_squared
requires_extended_sf = term%nlo_type == NLO_DGLAP .or. &
(term%is_subtraction () .and. process%pcm_contains_pdfs ())
call term%connected%setup_connected_trace (term%isolated, &
undo_helicities = undo_helicities (core, me_already_squared), &
keep_fs_flavors = keep_fs_flavors, &
- extended_sf = requires_extended_sf)
+ requires_extended_sf = requires_extended_sf)
associate (int_eff => term%isolated%int_eff)
state_matrix => int_eff%get_state_matrix_ptr ()
n_tot = int_eff%get_n_tot ()
flv_int = quantum_numbers_get_flavor &
(state_matrix%get_quantum_number (1))
allocate (f_in (n_in))
f_in = flv_int(1:n_in)
deallocate (flv_int)
end associate
n_in = term%connected%trace%get_n_in ()
n_vir = term%connected%trace%get_n_vir ()
n_out = term%connected%trace%get_n_out ()
allocate (f_out (n_out))
do j = 1, n_out
call term%connected%trace%find_source &
(n_in + n_vir + j, src_int, i)
if (associated (src_int)) then
state_matrix => src_int%get_state_matrix_ptr ()
flv_src = quantum_numbers_get_flavor &
(state_matrix%get_quantum_number (1))
f_out(j) = flv_src(i)
deallocate (flv_src)
end if
end do
beam_config = process%get_beam_config ()
call term%connected%setup_subevt (term%isolated%sf_chain_eff, &
beam_config%data%flv, f_in, f_out)
call term%connected%setup_var_list &
(process%get_var_list_ptr (), beam_config%data)
select type (core)
class is (prc_external_t)
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
associate (is_born => .not. (term%nlo_type == NLO_REAL .and. .not. term%is_subtraction ()))
! Does connected%trace never have any helicity qn?
call setup_qn_index (term%connected_qn_index, term%connected%trace, pcm_instance, &
n_sub = n_sub, is_born = is_born, is_polarized = .false.)
call setup_qn_index (term%hard_qn_index, term%int_hard, pcm_instance, &
n_sub = n_sub, is_born = is_born, is_polarized = core%includes_polarization ())
end associate
class default
call term%connected_qn_index%init (term%connected%trace)
- call term%hard_qn_index%init (term%int_hard)
+ call term%hard_qn_index%init (term%int_hard)
end select
class default
call term%connected_qn_index%init (term%connected%trace)
call term%hard_qn_index%init (term%int_hard)
end select
+ if (requires_extended_sf) then
+ select type (config => term%pcm_instance%config)
+ type is (pcm_nlo_t)
+ n_in = config%region_data%get_n_in ()
+ flv_born = config%region_data%get_flv_states_born ()
+ flv_real = config%region_data%get_flv_states_real ()
+ n_flv_born = config%region_data%get_n_flv_born ()
+ n_flv_real = config%region_data%get_n_flv_real ()
+ n_flv_total = n_flv_born + n_flv_real
+ allocate (flv_pdf(n_in, n_flv_total), &
+ qn_pdf(n_in, n_flv_total))
+ call flv_pdf(:, :n_flv_born)%init (flv_born(:n_in, :))
+ call flv_pdf(:, n_flv_born + 1:n_flv_total)%init (flv_real(:n_in, :))
+ call qn_pdf%init (flv_pdf)
+ call term%sf_qn_index%init_sf (sf_chain_int, qn_pdf, n_flv_born, n_flv_real)
+ end select
+ end if
contains
function undo_helicities (core, me_squared) result (val)
logical :: val
class(prc_core_t), intent(in) :: core
logical, intent(in) :: me_squared
select type (core)
class is (prc_external_t)
val = me_squared .and. .not. core%includes_polarization ()
class default
val = .false.
end select
end function undo_helicities
subroutine reduce_interaction (int, polarized_beams, keep_fs_flavors, &
keep_colors)
type(interaction_t), intent(inout) :: int
logical, intent(in) :: polarized_beams
logical, intent(in) :: keep_fs_flavors, keep_colors
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask
logical, dimension(:), allocatable :: mask_f, mask_c, mask_h
integer :: n_tot, n_in
n_in = int%get_n_in (); n_tot = int%get_n_tot ()
allocate (qn_mask (n_tot))
allocate (mask_f (n_tot), mask_c (n_tot), mask_h (n_tot))
mask_c = .not. keep_colors
mask_f (1 : n_in) = .false.
if (keep_fs_flavors) then
mask_f (n_in + 1 : ) = .false.
else
mask_f (n_in + 1 : ) = .true.
end if
if (polarized_beams) then
mask_h (1 : n_in) = .false.
else
mask_h (1 : n_in) = .true.
end if
mask_h (n_in + 1 : ) = .true.
call qn_mask%init (mask_f, mask_c, mask_h)
call int%reduce_state_matrix (qn_mask, keep_order = .true.)
end subroutine reduce_interaction
<<Instances: term instance init: procedures>>
end subroutine term_instance_init
@ %def term_instance_init
@ Setup index mapping from state matrix to index pair [[i_flv]], [[i_sub]].
<<Instances: term instance init: procedures>>=
subroutine setup_qn_index (qn_index, int, pcm_instance, n_sub, is_born, is_polarized)
type(qn_index_map_t), intent(out) :: qn_index
class(interaction_t), intent(in) :: int
class(pcm_instance_t), intent(in) :: pcm_instance
integer, intent(in) :: n_sub
logical, intent(in) :: is_born
logical, intent(in) :: is_polarized
integer :: i
type(quantum_numbers_t), dimension(:, :), allocatable :: qn_config
type(quantum_numbers_t), dimension(:, :), allocatable :: qn_hel
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
qn_config = config%get_qn (is_born)
end select
if (is_polarized) then
! term%config%data from higher scope
call setup_qn_hel (int, term%config%data, qn_hel)
call qn_index%init (int, qn_config, n_sub, qn_hel)
call qn_index%set_helicity_flip (.true.)
else
call qn_index%init (int, qn_config, n_sub)
end if
end subroutine setup_qn_index
@ %def setup_qn_index
@ Setup beam polarisation quantum numbers, iff beam polarisation is required.
We retrieve the full helicity information from [[term%config%data]] and reduce
the information only to the inital state. Afterwards, we uniquify the initial
state polarization by a applying a index (hash) table.
The helicity information is fed into an array of quantum numbers to assign
flavor, helicity and subtraction indices correctly to their matrix element.
<<Instances: term instance init: procedures>>=
subroutine setup_qn_hel (int, data, qn_hel)
class(interaction_t), intent(in) :: int
class(process_constants_t), intent(in) :: data
type(quantum_numbers_t), dimension(:, :), allocatable, intent(out) :: qn_hel
type(helicity_t), dimension(:), allocatable :: hel
integer, dimension(:), allocatable :: index_table
integer, dimension(:, :), allocatable :: hel_state
integer :: i, j, n_hel_unique
associate (n_in => int%get_n_in (), n_tot => int%get_n_tot ())
allocate (hel_state (n_tot, data%get_n_hel ()), &
source = data%hel_state)
allocate (index_table (data%get_n_hel ()), &
source = 0)
forall (j=1:data%get_n_hel (), i=n_in+1:n_tot) hel_state(i, j) = 0
n_hel_unique = 0
HELICITY: do i = 1, data%get_n_hel ()
do j = 1, data%get_n_hel ()
if (index_table (j) == 0) then
index_table(j) = i; n_hel_unique = n_hel_unique + 1
cycle HELICITY
else if (all (hel_state(:, i) == &
hel_state(:, index_table(j)))) then
cycle HELICITY
end if
end do
end do HELICITY
allocate (qn_hel (n_tot, n_hel_unique))
allocate (hel (n_tot))
do j = 1, n_hel_unique
call hel%init (hel_state(:, index_table(j)))
call qn_hel(:, j)%init (hel)
end do
end associate
end subroutine setup_qn_hel
@ %def setup_qn_hel
@
<<Instances: term instance: TBP>>=
procedure :: init_from_process => term_instance_init_from_process
<<Instances: procedures>>=
subroutine term_instance_init_from_process (term_instance, &
process, i, pcm_instance, sf_chain)
class(term_instance_t), intent(inout), target :: term_instance
type(process_t), intent(in), target :: process
integer, intent(in) :: i
class(pcm_instance_t), intent(in), target :: pcm_instance
type(sf_chain_t), intent(in), target :: sf_chain
type(process_term_t) :: term
integer :: i_component
logical :: requires_extended_sf
term = process%get_term_ptr (i)
i_component = term%i_component
if (i_component /= 0) then
term_instance%pcm_instance => pcm_instance
term_instance%nlo_type = process%get_nlo_type_component (i_component)
requires_extended_sf = term_instance%nlo_type == NLO_DGLAP .or. &
(term_instance%nlo_type == NLO_REAL .and. process%get_i_sub (i) == i)
call term_instance%setup_kinematics (sf_chain, &
process%get_beam_config_ptr (), &
process%get_phs_config (i_component), &
requires_extended_sf)
call term_instance%init (process, i, &
real_finite = process%component_is_real_finite (i_component))
select type (phs => term_instance%k_term%phs)
type is (phs_fks_t)
call term_instance%set_emitter (process%get_pcm_ptr ())
call term_instance%setup_fks_kinematics (process%get_var_list_ptr (), &
process%get_beam_config_ptr ())
end select
call term_instance%set_threshold (process%get_pcm_ptr ())
call term_instance%setup_expressions (process%get_meta (), process%get_config ())
end if
end subroutine term_instance_init_from_process
@ %def term_instance_init_from_process
@ Initialize the seed-kinematics configuration. All subobjects are
allocated explicitly.
<<Instances: term instance: TBP>>=
procedure :: setup_kinematics => term_instance_setup_kinematics
<<Instances: procedures>>=
subroutine term_instance_setup_kinematics (term, sf_chain, &
beam_config, phs_config, extended_sf)
class(term_instance_t), intent(inout) :: term
type(sf_chain_t), intent(in), target :: sf_chain
type(process_beam_config_t), intent(in), target :: beam_config
class(phs_config_t), intent(in), target :: phs_config
logical, intent(in) :: extended_sf
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
call term%k_term%init_sf_chain (sf_chain, beam_config, &
extended_sf = config%has_pdfs .and. extended_sf)
class default
call term%k_term%init_sf_chain (sf_chain, beam_config)
end select
!!! Add one for additional Born matrix element
call term%k_term%init_phs (phs_config)
call term%k_term%set_nlo_info (term%nlo_type)
select type (phs => term%k_term%phs)
type is (phs_fks_t)
call phs%allocate_momenta (phs_config, &
.not. (term%nlo_type == NLO_REAL))
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
call config%region_data%init_phs_identifiers (phs%phs_identifiers)
!!! The triple select type pyramid of doom
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
if (allocated (pcm_instance%real_kinematics%alr_to_i_phs)) &
call config%region_data%set_alr_to_i_phs (phs%phs_identifiers, &
pcm_instance%real_kinematics%alr_to_i_phs)
end select
end select
end select
end subroutine term_instance_setup_kinematics
@ %def term_instance_setup_kinematics
@
<<Instances: term instance: TBP>>=
procedure :: setup_fks_kinematics => term_instance_setup_fks_kinematics
<<Instances: procedures>>=
subroutine term_instance_setup_fks_kinematics (term, var_list, beam_config)
class(term_instance_t), intent(inout), target :: term
type(var_list_t), intent(in) :: var_list
type(process_beam_config_t), intent(in) :: beam_config
integer :: mode
logical :: singular_jacobian
if (.not. (term%nlo_type == NLO_REAL .or. term%nlo_type == NLO_DGLAP .or. &
term%nlo_type == NLO_MISMATCH)) return
singular_jacobian = var_list%get_lval (var_str ("?powheg_use_singular_jacobian"))
if (term%nlo_type == NLO_REAL) then
mode = check_generator_mode (GEN_REAL_PHASE_SPACE)
else if (term%nlo_type == NLO_MISMATCH) then
mode = check_generator_mode (GEN_SOFT_MISMATCH)
else
mode = PHS_MODE_UNDEFINED
end if
select type (phs => term%k_term%phs)
type is (phs_fks_t)
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
call config%setup_phs_generator (pcm_instance, &
phs%generator, phs%config%sqrts, mode, singular_jacobian)
if (beam_config%has_structure_function ()) then
pcm_instance%isr_kinematics%isr_mode = SQRTS_VAR
else
pcm_instance%isr_kinematics%isr_mode = SQRTS_FIXED
end if
if (debug_on) call msg_debug (D_PHASESPACE, "isr_mode: ", pcm_instance%isr_kinematics%isr_mode)
end select
end select
class default
call msg_fatal ("Phase space should be an FKS phase space!")
end select
contains
function check_generator_mode (gen_mode_default) result (gen_mode)
integer :: gen_mode
integer, intent(in) :: gen_mode_default
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
associate (settings => config%settings)
if (settings%test_coll_limit .and. settings%test_anti_coll_limit) &
call msg_fatal ("You cannot check the collinear and anti-collinear limit "&
&"at the same time!")
if (settings%test_soft_limit .and. .not. settings%test_coll_limit &
.and. .not. settings%test_anti_coll_limit) then
gen_mode = GEN_SOFT_LIMIT_TEST
else if (.not. settings%test_soft_limit .and. settings%test_coll_limit) then
gen_mode = GEN_COLL_LIMIT_TEST
else if (.not. settings%test_soft_limit .and. settings%test_anti_coll_limit) then
gen_mode = GEN_ANTI_COLL_LIMIT_TEST
else if (settings%test_soft_limit .and. settings%test_coll_limit) then
gen_mode = GEN_SOFT_COLL_LIMIT_TEST
else if (settings%test_soft_limit .and. settings%test_anti_coll_limit) then
gen_mode = GEN_SOFT_ANTI_COLL_LIMIT_TEST
else
gen_mode = gen_mode_default
end if
end associate
end select
end function check_generator_mode
end subroutine term_instance_setup_fks_kinematics
@ %def term_instance_setup_fks_kinematics
@ Setup seed kinematics, starting from the MC parameter set given as
argument. As a result, the [[k_seed]] kinematics object is evaluated
(except for the structure-function matrix-element evaluation, which we
postpone until we know the factorization scale), and we have a valid
[[p_seed]] momentum array.
<<Instances: term instance: TBP>>=
procedure :: compute_seed_kinematics => term_instance_compute_seed_kinematics
<<Instances: procedures>>=
subroutine term_instance_compute_seed_kinematics &
(term, mci_work, phs_channel, success)
class(term_instance_t), intent(inout), target :: term
type(mci_work_t), intent(in) :: mci_work
integer, intent(in) :: phs_channel
logical, intent(out) :: success
call term%k_term%compute_selected_channel &
(mci_work, phs_channel, term%p_seed, success)
end subroutine term_instance_compute_seed_kinematics
@ %def term_instance_compute_seed_kinematics
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_radiation_kinematics => term_instance_evaluate_radiation_kinematics
<<Instances: procedures>>=
subroutine term_instance_evaluate_radiation_kinematics (term, x)
class(term_instance_t), intent(inout) :: term
real(default), dimension(:), intent(in) :: x
select type (phs => term%k_term%phs)
type is (phs_fks_t)
if (phs%mode == PHS_MODE_ADDITIONAL_PARTICLE) &
call term%k_term%evaluate_radiation_kinematics (x)
end select
end subroutine term_instance_evaluate_radiation_kinematics
@ %def term_instance_evaluate_radiation_kinematics
@
<<Instances: term instance: TBP>>=
procedure :: compute_xi_ref_momenta => term_instance_compute_xi_ref_momenta
<<Instances: procedures>>=
subroutine term_instance_compute_xi_ref_momenta (term)
class(term_instance_t), intent(inout) :: term
select type (pcm => term%pcm_instance%config)
type is (pcm_nlo_t)
call term%k_term%compute_xi_ref_momenta (pcm%region_data, term%nlo_type)
end select
end subroutine term_instance_compute_xi_ref_momenta
@ %def term_instance_compute_xi_ref_momenta
@
<<Instances: term instance: TBP>>=
procedure :: generate_fsr_in => term_instance_generate_fsr_in
<<Instances: procedures>>=
subroutine term_instance_generate_fsr_in (term)
class(term_instance_t), intent(inout) :: term
select type (phs => term%k_term%phs)
type is (phs_fks_t)
call phs%generate_fsr_in ()
end select
end subroutine term_instance_generate_fsr_in
@ %def term_instance_generate_fsr_in
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_projections => term_instance_evaluate_projections
<<Instances: procedures>>=
subroutine term_instance_evaluate_projections (term)
class(term_instance_t), intent(inout) :: term
if (term%k_term%threshold .and. term%nlo_type > BORN) then
if (debug2_active (D_THRESHOLD)) &
print *, 'Evaluate on-shell projection: ', &
char (component_status (term%nlo_type))
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
call term%k_term%threshold_projection (pcm_instance, term%nlo_type)
end select
end if
end subroutine term_instance_evaluate_projections
@ %def term_instance_evaluate_projections
@
<<Instances: term instance: TBP>>=
procedure :: redo_sf_chain => term_instance_redo_sf_chain
<<Instances: procedures>>=
subroutine term_instance_redo_sf_chain (term, mci_work, phs_channel)
class(term_instance_t), intent(inout) :: term
type(mci_work_t), intent(in) :: mci_work
integer, intent(in) :: phs_channel
real(default), dimension(:), allocatable :: x
integer :: sf_channel, n
real(default) :: xi, y
n = size (mci_work%get_x_strfun ())
if (n > 0) then
allocate (x(n))
x = mci_work%get_x_strfun ()
associate (k => term%k_term)
sf_channel = k%phs%config%get_sf_channel (phs_channel)
call k%sf_chain%compute_kinematics (sf_channel, x)
deallocate (x)
end associate
end if
end subroutine term_instance_redo_sf_chain
@ %def term_instance_redo_sf_chain
@ Inverse: recover missing parts of the kinematics, given a complete
set of seed momenta. Select a channel and reconstruct the MC parameter set.
<<Instances: term instance: TBP>>=
procedure :: recover_mcpar => term_instance_recover_mcpar
<<Instances: procedures>>=
subroutine term_instance_recover_mcpar (term, mci_work, phs_channel)
class(term_instance_t), intent(inout), target :: term
type(mci_work_t), intent(inout) :: mci_work
integer, intent(in) :: phs_channel
call term%k_term%recover_mcpar (mci_work, phs_channel, term%p_seed)
end subroutine term_instance_recover_mcpar
@ %def term_instance_recover_mcpar
@ Part of [[recover_mcpar]], separately accessible. Reconstruct all
kinematics data in the structure-function chain instance.
<<Instances: term instance: TBP>>=
procedure :: recover_sfchain => term_instance_recover_sfchain
<<Instances: procedures>>=
subroutine term_instance_recover_sfchain (term, channel)
class(term_instance_t), intent(inout), target :: term
integer, intent(in) :: channel
call term%k_term%recover_sfchain (channel, term%p_seed)
end subroutine term_instance_recover_sfchain
@ %def term_instance_recover_sfchain
@ Compute the momenta in the hard interactions, one for each term that
constitutes this process component. In simple cases this amounts to
just copying momenta. In more advanced cases, we may generate
distinct sets of momenta from the seed kinematics.
The interactions in the term instances are accessed individually. We may
choose to calculate all terms at once together with the seed kinematics, use
[[component%core_state]] for storage, and just fill the interactions here.
<<Instances: term instance: TBP>>=
procedure :: compute_hard_kinematics => &
term_instance_compute_hard_kinematics
<<Instances: procedures>>=
subroutine term_instance_compute_hard_kinematics (term, skip_term, success)
class(term_instance_t), intent(inout) :: term
integer, intent(in), optional :: skip_term
logical, intent(out) :: success
type(vector4_t), dimension(:), allocatable :: p
if (allocated (term%core_state)) &
call term%core_state%reset_new_kinematics ()
if (present (skip_term)) then
if (term%config%i_term_global == skip_term) return
end if
if (term%nlo_type == NLO_REAL .and. term%k_term%emitter >= 0) then
call term%k_term%evaluate_radiation (term%p_seed, p, success)
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
if (config%dalitz_plot%active) then
if (term%k_term%emitter > term%k_term%n_in) then
if (p(term%k_term%emitter)**2 > tiny_07) &
call config%register_dalitz_plot (term%k_term%emitter, p)
end if
end if
end select
else if (is_subtraction_component (term%k_term%emitter, term%nlo_type)) then
call term%k_term%modify_momenta_for_subtraction (term%p_seed, p)
success = .true.
else
allocate (p (size (term%p_seed))); p = term%p_seed
success = .true.
end if
call term%int_hard%set_momenta (p)
end subroutine term_instance_compute_hard_kinematics
@ %def term_instance_compute_hard_kinematics
@ Here, we invert this. We fetch the incoming momenta which reside
in the appropriate [[sf_chain]] object, stored within the [[k_seed]]
subobject. On the other hand, we have the outgoing momenta of the
effective interaction. We rely on the process core to compute the
remaining seed momenta and to fill the momenta within the hard
interaction. (The latter is trivial if hard and effective interaction
coincide.)
After this is done, the incoming momenta in the trace evaluator that
corresponds to the hard (effective) interaction, are still
left undefined. We remedy this by calling [[receive_kinematics]] once.
<<Instances: term instance: TBP>>=
procedure :: recover_seed_kinematics => &
term_instance_recover_seed_kinematics
<<Instances: procedures>>=
subroutine term_instance_recover_seed_kinematics (term)
class(term_instance_t), intent(inout) :: term
integer :: n_in
n_in = term%k_term%n_in
call term%k_term%get_incoming_momenta (term%p_seed(1:n_in))
associate (int_eff => term%isolated%int_eff)
call int_eff%set_momenta (term%p_seed(1:n_in), outgoing = .false.)
term%p_seed(n_in + 1 : ) = int_eff%get_momenta (outgoing = .true.)
end associate
call term%isolated%receive_kinematics ()
end subroutine term_instance_recover_seed_kinematics
@ %def term_instance_recover_seed_kinematics
@ Compute the integration parameters for all channels except the selected
one.
<<Instances: term instance: TBP>>=
procedure :: compute_other_channels => &
term_instance_compute_other_channels
<<Instances: procedures>>=
subroutine term_instance_compute_other_channels &
(term, mci_work, phs_channel)
class(term_instance_t), intent(inout), target :: term
type(mci_work_t), intent(in) :: mci_work
integer, intent(in) :: phs_channel
call term%k_term%compute_other_channels (mci_work, phs_channel)
end subroutine term_instance_compute_other_channels
@ %def term_instance_compute_other_channels
@ Recover beam momenta, i.e., return the beam momenta as currently
stored in the kinematics subobject to their source. This is a side effect.
<<Instances: term instance: TBP>>=
procedure :: return_beam_momenta => term_instance_return_beam_momenta
<<Instances: procedures>>=
subroutine term_instance_return_beam_momenta (term)
class(term_instance_t), intent(in) :: term
call term%k_term%return_beam_momenta ()
end subroutine term_instance_return_beam_momenta
@ %def term_instance_return_beam_momenta
@
<<Instances: term instance: TBP>>=
procedure :: apply_real_partition => term_instance_apply_real_partition
<<Instances: procedures>>=
subroutine term_instance_apply_real_partition (term, process)
class(term_instance_t), intent(inout) :: term
type(process_t), intent(in) :: process
real(default) :: f, sqme
integer :: i_component
integer :: i_amp, n_amps
logical :: is_subtraction
i_component = term%config%i_component
if (process%component_is_selected (i_component) .and. &
process%get_component_nlo_type (i_component) == NLO_REAL) then
is_subtraction = process%get_component_type (i_component) == COMP_REAL_SING &
.and. term%k_term%emitter < 0
if (is_subtraction) return
select type (pcm => process%get_pcm_ptr ())
type is (pcm_nlo_t)
f = pcm%real_partition%get_f (term%p_hard)
end select
n_amps = term%connected%trace%get_n_matrix_elements ()
do i_amp = 1, n_amps
sqme = real (term%connected%trace%get_matrix_element ( &
term%connected_qn_index%get_index (i_amp, i_sub = 0)))
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "term_instance_apply_real_partition")
select type (pcm => term%pcm_instance%config)
type is (pcm_nlo_t)
select case (process%get_component_type (i_component))
case (COMP_REAL_FIN, COMP_REAL_SING)
select case (process%get_component_type (i_component))
case (COMP_REAL_FIN)
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "Real finite")
sqme = sqme * (one - f)
case (COMP_REAL_SING)
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "Real singular")
sqme = sqme * f
end select
end select
end select
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "apply_damping: sqme", sqme)
call term%connected%trace%set_matrix_element (i_amp, cmplx (sqme, zero, default))
end do
end if
end subroutine term_instance_apply_real_partition
@ %def term_instance_apply_real_partition
@
<<Instances: term instance: TBP>>=
procedure :: get_lorentz_transformation => term_instance_get_lorentz_transformation
<<Instances: procedures>>=
function term_instance_get_lorentz_transformation (term) result (lt)
type(lorentz_transformation_t) :: lt
class(term_instance_t), intent(in) :: term
lt = term%k_term%phs%get_lorentz_transformation ()
end function term_instance_get_lorentz_transformation
@ %def term_instance_get_lorentz_transformation
@
<<Instances: term instance: TBP>>=
procedure :: get_p_hard => term_instance_get_p_hard
<<Instances: procedures>>=
pure function term_instance_get_p_hard (term_instance) result (p_hard)
type(vector4_t), dimension(:), allocatable :: p_hard
class(term_instance_t), intent(in) :: term_instance
allocate (p_hard (size (term_instance%p_hard)))
p_hard = term_instance%p_hard
end function term_instance_get_p_hard
@ %def term_instance_get_p_hard
@
<<Instances: term instance: TBP>>=
procedure :: set_emitter => term_instance_set_emitter
<<Instances: procedures>>=
subroutine term_instance_set_emitter (term, pcm)
class(term_instance_t), intent(inout) :: term
class(pcm_t), intent(in) :: pcm
integer :: i_phs
logical :: set_emitter
select type (pcm)
type is (pcm_nlo_t)
!!! Without resonances, i_alr = i_phs
i_phs = term%config%i_term
term%k_term%i_phs = term%config%i_term
select type (phs => term%k_term%phs)
type is (phs_fks_t)
set_emitter = i_phs <= pcm%region_data%n_phs .and. term%nlo_type == NLO_REAL
if (set_emitter) then
term%k_term%emitter = phs%phs_identifiers(i_phs)%emitter
select type (pcm => term%pcm_instance%config)
type is (pcm_nlo_t)
if (allocated (pcm%region_data%i_phs_to_i_con)) &
term%k_term%i_con = pcm%region_data%i_phs_to_i_con (i_phs)
end select
end if
end select
end select
end subroutine term_instance_set_emitter
@ %def term_instance_set_emitter
@
<<Instances: term instance: TBP>>=
procedure :: set_threshold => term_instance_set_threshold
<<Instances: procedures>>=
subroutine term_instance_set_threshold (term, pcm)
class(term_instance_t), intent(inout) :: term
class(pcm_t), intent(in) :: pcm
select type (pcm)
type is (pcm_nlo_t)
term%k_term%threshold = pcm%settings%factorization_mode == FACTORIZATION_THRESHOLD
class default
term%k_term%threshold = .false.
end select
end subroutine term_instance_set_threshold
@ %def term_instance_set_threshold
@ For initializing the expressions, we need the local variable list and the
parse trees.
<<Instances: term instance: TBP>>=
procedure :: setup_expressions => term_instance_setup_expressions
<<Instances: procedures>>=
subroutine term_instance_setup_expressions (term, meta, config)
class(term_instance_t), intent(inout), target :: term
type(process_metadata_t), intent(in), target :: meta
type(process_config_data_t), intent(in) :: config
if (allocated (config%ef_cuts)) &
call term%connected%setup_cuts (config%ef_cuts)
if (allocated (config%ef_scale)) &
call term%connected%setup_scale (config%ef_scale)
if (allocated (config%ef_fac_scale)) &
call term%connected%setup_fac_scale (config%ef_fac_scale)
if (allocated (config%ef_ren_scale)) &
call term%connected%setup_ren_scale (config%ef_ren_scale)
if (allocated (config%ef_weight)) &
call term%connected%setup_weight (config%ef_weight)
end subroutine term_instance_setup_expressions
@ %def term_instance_setup_expressions
@ Prepare the extra evaluators that we need for processing events.
The quantum numbers mask of the incoming particle
<<Instances: term instance: TBP>>=
procedure :: setup_event_data => term_instance_setup_event_data
<<Instances: procedures>>=
subroutine term_instance_setup_event_data (term, core, model)
class(term_instance_t), intent(inout), target :: term
class(prc_core_t), intent(in) :: core
class(model_data_t), intent(in), target :: model
integer :: n_in
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask_in
n_in = term%int_hard%get_n_in ()
allocate (mask_in (n_in))
mask_in = term%k_term%sf_chain%get_out_mask ()
call setup_isolated (term%isolated, core, model, mask_in, term%config%col)
call setup_connected (term%connected, term%isolated, term%nlo_type)
contains
subroutine setup_isolated (isolated, core, model, mask, color)
type(isolated_state_t), intent(inout), target :: isolated
class(prc_core_t), intent(in) :: core
class(model_data_t), intent(in), target :: model
type(quantum_numbers_mask_t), intent(in), dimension(:) :: mask
integer, intent(in), dimension(:) :: color
call isolated%setup_square_matrix (core, model, mask, color)
call isolated%setup_square_flows (core, model, mask)
end subroutine setup_isolated
subroutine setup_connected (connected, isolated, nlo_type)
type(connected_state_t), intent(inout), target :: connected
type(isolated_state_t), intent(in), target :: isolated
integer :: nlo_type
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask
call connected%setup_connected_matrix (isolated)
if (term%nlo_type == NLO_VIRTUAL .or. (term%nlo_type == NLO_REAL &
.and. term%config%i_term_global == term%config%i_sub) &
.or. term%nlo_type == NLO_DGLAP) then
!!! We don't care about the subtraction matrix elements in
!!! connected%matrix, because all entries there are supposed
!!! to be squared. To be able to match with flavor quantum numbers,
!!! we remove the subtraction quantum entries from the state matrix.
allocate (mask (connected%matrix%get_n_tot()))
call mask%set_sub (1)
call connected%matrix%reduce_state_matrix (mask, keep_order = .true.)
end if
call connected%setup_connected_flows (isolated)
call connected%setup_state_flv (isolated%get_n_out ())
end subroutine setup_connected
end subroutine term_instance_setup_event_data
@ %def term_instance_setup_event_data
@ Color-correlated matrix elements should be obtained from
the external BLHA provider. According to the standard, the
matrix elements output is a one-dimensional array. For FKS
subtraction, we require the matrix $B_{ij}$. BLHA prescribes
a mapping $(i, j) \to k$, where $k$ is the index of the matrix
element in the output array. It focusses on the off-diagonal entries,
i.e. $i \neq j$. The subroutine [[blha_color_c_fill_offdiag]] realizes
this mapping. The diagonal entries can simply be obtained as
the product of the Born matrix element and either $C_A$ or $C_F$,
which is achieved by [[blha_color_c_fill_diag]].
For simple processes, i.e. those with only one color line, it is
$B_{ij} = C_F \cdot B$. For those, we keep the possibility of computing
color correlations by a multiplication of the Born matrix element with $C_F$.
It is triggered by the [[use_internal_color_correlations]] flag and should
be used only for testing purposes. However, it is also used for
the threshold computation where the process is well-defined and fixed.
<<Instances: term instance: TBP>>=
procedure :: evaluate_color_correlations => &
term_instance_evaluate_color_correlations
<<Instances: procedures>>=
subroutine term_instance_evaluate_color_correlations (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(inout) :: core
integer :: i_flv_born
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
if (debug_on) call msg_debug2 (D_SUBTRACTION, &
"term_instance_evaluate_color_correlations: " // &
"use_internal_color_correlations:", &
config%settings%use_internal_color_correlations)
if (debug_on) call msg_debug2 (D_SUBTRACTION, "fac_scale", term%fac_scale)
do i_flv_born = 1, config%region_data%n_flv_born
select case (term%nlo_type)
case (NLO_REAL)
call transfer_me_array_to_bij (config, i_flv_born, &
pcm_instance%real_sub%sqme_born (i_flv_born), &
pcm_instance%real_sub%sqme_born_color_c (:, :, i_flv_born))
case (NLO_MISMATCH)
call transfer_me_array_to_bij (config, i_flv_born, &
pcm_instance%soft_mismatch%sqme_born (i_flv_born), &
pcm_instance%soft_mismatch%sqme_born_color_c (:, :, i_flv_born))
case (NLO_VIRTUAL)
!!! This is just a copy of the above with a different offset and can for sure be unified
call transfer_me_array_to_bij (config, i_flv_born, &
-one, pcm_instance%virtual%sqme_color_c (:, :, i_flv_born))
end select
end do
end select
end select
contains
function get_trivial_cf_factors (n_tot, flv, factorization_mode) result (beta_ij)
integer, intent(in) :: n_tot, factorization_mode
integer, intent(in), dimension(:) :: flv
real(default), dimension(n_tot, n_tot) :: beta_ij
if (factorization_mode == NO_FACTORIZATION) then
beta_ij = get_trivial_cf_factors_default (n_tot, flv)
else
beta_ij = get_trivial_cf_factors_threshold (n_tot, flv)
end if
end function get_trivial_cf_factors
function get_trivial_cf_factors_default (n_tot, flv) result (beta_ij)
integer, intent(in) :: n_tot
integer, intent(in), dimension(:) :: flv
real(default), dimension(n_tot, n_tot) :: beta_ij
integer :: i, j
beta_ij = zero
if (count (is_quark (flv)) == 2) then
do i = 1, n_tot
do j = 1, n_tot
if (is_quark(flv(i)) .and. is_quark(flv(j))) then
if (i == j) then
beta_ij(i,j)= -cf
else
beta_ij(i,j) = cf
end if
end if
end do
end do
end if
end function get_trivial_cf_factors_default
function get_trivial_cf_factors_threshold (n_tot, flv) result (beta_ij)
integer, intent(in) :: n_tot
integer, intent(in), dimension(:) :: flv
real(default), dimension(n_tot, n_tot) :: beta_ij
integer :: i
beta_ij = zero
do i = 1, 4
beta_ij(i,i) = -cf
end do
beta_ij(1,2) = cf; beta_ij(2,1) = cf
beta_ij(3,4) = cf; beta_ij(4,3) = cf
end function get_trivial_cf_factors_threshold
subroutine transfer_me_array_to_bij (pcm, i_flv, &
sqme_born, sqme_color_c)
type(pcm_nlo_t), intent(in) :: pcm
integer, intent(in) :: i_flv
real(default), intent(in) :: sqme_born
real(default), dimension(:,:), intent(inout) :: sqme_color_c
- integer :: i_color_c, i_sub, n_pdf_off, virt_off, n_offset
+ integer :: i_color_c, i_sub, n_offset
real(default), dimension(:), allocatable :: sqme
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "transfer_me_array_to_bij")
if (pcm%settings%use_internal_color_correlations) then
!!! A negative value for sqme_born indicates that the Born matrix
!!! element is multiplied at a different place, e.g. in the case
!!! of the virtual component
sqme_color_c = get_trivial_cf_factors &
(pcm%region_data%get_n_legs_born (), &
pcm%region_data%get_flv_states_born (i_flv), &
pcm%settings%factorization_mode)
if (sqme_born > zero) then
sqme_color_c = sqme_born * sqme_color_c
else if (sqme_born == zero) then
sqme_color_c = zero
end if
else
n_offset = 0
if (term%nlo_type == NLO_VIRTUAL) then
n_offset = 1
else if (pcm%has_pdfs .and. term%is_subtraction ()) then
- n_offset = n_beam_structure_int
+ n_offset = n_beams_rescaled
end if
allocate (sqme (term%get_n_sub_color ()), source = zero)
do i_sub = 1, term%get_n_sub_color ()
sqme(i_sub) = real(term%connected%trace%get_matrix_element ( &
term%connected_qn_index%get_index (i_flv, i_sub = i_sub + n_offset)), default)
end do
call blha_color_c_fill_offdiag (pcm%region_data%n_legs_born, &
sqme, sqme_color_c)
call blha_color_c_fill_diag (real(term%connected%trace%get_matrix_element ( &
term%connected_qn_index%get_index (i_flv, i_sub = 0)), default), &
pcm%region_data%get_flv_states_born (i_flv), &
sqme_color_c)
end if
end subroutine transfer_me_array_to_bij
end subroutine term_instance_evaluate_color_correlations
@ %def term_instance_evaluate_color_correlations
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_charge_correlations => &
term_instance_evaluate_charge_correlations
<<Instances: procedures>>=
subroutine term_instance_evaluate_charge_correlations (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(inout) :: core
integer :: i_flv_born
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
do i_flv_born = 1, config%region_data%n_flv_born
select case (term%nlo_type)
case (NLO_REAL)
call transfer_me_array_to_bij (config, i_flv_born, &
pcm_instance%real_sub%sqme_born (i_flv_born), &
pcm_instance%real_sub%sqme_born_charge_c (:, :, i_flv_born))
case (NLO_MISMATCH)
call transfer_me_array_to_bij (config, i_flv_born, &
pcm_instance%soft_mismatch%sqme_born (i_flv_born), &
pcm_instance%soft_mismatch%sqme_born_charge_c (:, :, i_flv_born))
case (NLO_VIRTUAL)
call transfer_me_array_to_bij (config, i_flv_born, &
-one, pcm_instance%virtual%sqme_charge_c (:, :, i_flv_born))
end select
end do
end select
end select
contains
subroutine transfer_me_array_to_bij (pcm, i_flv, sqme_born, sqme_charge_c)
type(pcm_nlo_t), intent(in) :: pcm
integer, intent(in) :: i_flv
real(default), intent(in) :: sqme_born
real(default), dimension(:,:), intent(inout) :: sqme_charge_c
integer :: n_legs_born, i, j
integer, dimension(:), allocatable :: sigma
real(default), dimension(:), allocatable :: Q
n_legs_born = pcm%region_data%n_legs_born
associate (flv_born => pcm%region_data%flv_born(i_flv))
allocate (sigma (n_legs_born), Q (size (flv_born%charge)))
Q = flv_born%charge
sigma(1:flv_born%n_in) = sign (1, flv_born%flst(1:flv_born%n_in))
sigma(flv_born%n_in + 1: ) = -sign (1, flv_born%flst(flv_born%n_in + 1: ))
end associate
do i = 1, n_legs_born
do j = 1, n_legs_born
sqme_charge_c(i, j) = sigma(i) * sigma(j) * Q(i) * Q(j) * (-one)
end do
end do
sqme_charge_c = sqme_charge_c * sqme_born
end subroutine transfer_me_array_to_bij
end subroutine term_instance_evaluate_charge_correlations
@ %def term_instance_evaluate_charge_correlations
@ The information about spin correlations is not stored in the [[nlo_settings]] because
it is only available after the [[fks_regions]] have been created.
<<Instances: term instance: TBP>>=
procedure :: evaluate_spin_correlations => term_instance_evaluate_spin_correlations
<<Instances: procedures>>=
subroutine term_instance_evaluate_spin_correlations (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(inout) :: core
integer :: i_flv, i_hel, i_sub, i_emitter, emitter
integer :: n_flv, n_sub_color, n_sub_spin, n_offset
real(default), dimension(0:3, 0:3) :: sqme_spin_c
real(default), dimension(:), allocatable :: sqme_spin_c_all
real(default), dimension(:), allocatable :: sqme_spin_c_arr
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, &
"term_instance_evaluate_spin_correlations")
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
if (pcm_instance%real_sub%requires_spin_correlations () &
.and. term%nlo_type == NLO_REAL) then
select type (core)
type is (prc_openloops_t)
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
n_flv = term%connected_qn_index%get_n_flv ()
n_sub_color = term%get_n_sub_color ()
n_sub_spin = term%get_n_sub_spin ()
- n_offset = 0; if(config%has_pdfs) n_offset = n_beam_structure_int
+ n_offset = 0; if(config%has_pdfs) n_offset = n_beams_rescaled
allocate (sqme_spin_c_arr(16))
do i_flv = 1, n_flv
allocate (sqme_spin_c_all(n_sub_spin))
do i_sub = 1, n_sub_spin
sqme_spin_c_all(i_sub) = real(term%connected%trace%get_matrix_element &
(term%connected_qn_index%get_index (i_flv, &
i_sub = i_sub + n_offset + n_sub_color)), default)
end do
do i_emitter = 1, config%region_data%n_emitters
emitter = config%region_data%emitters(i_emitter)
if (emitter > 0) then
call split_array (sqme_spin_c_all, sqme_spin_c_arr)
sqme_spin_c = reshape (sqme_spin_c_arr, (/4,4/))
pcm_instance%real_sub%sqme_born_spin_c(:,:,emitter,i_flv) = sqme_spin_c
end if
end do
deallocate (sqme_spin_c_all)
end do
end select
class default
call msg_fatal ("Spin correlations so far only supported by OpenLoops.")
end select
end if
end select
end subroutine term_instance_evaluate_spin_correlations
@ %def term_instance_evaluate_spin_correlations
-@ Compute collinear ISR from interactions, real component and DLGAP remnant are
-handled accordingly.
-<<Instances: term instance: TBP>>=
- procedure :: compute_sqme_coll_isr => term_instance_compute_sqme_coll_isr
-<<Instances: procedures>>=
- subroutine term_instance_compute_sqme_coll_isr (term)
- class(term_instance_t), intent(in) :: term
- integer :: i_flv
- integer, parameter :: BEAM_PLUS = 1, BEAM_MINUS = 2, &
- PDF = 1, PDF_SINGLET = 2
- select type (pcm_instance => term%pcm_instance)
- type is (pcm_instance_nlo_t)
- select type (pcm => term%pcm_instance%config)
- type is (pcm_nlo_t)
- associate (me => term%connected%trace%get_matrix_element ())
- do i_flv = 1, pcm%region_data%n_flv_born
- call set_sqme_coll_isr (BEAM_PLUS, PDF, i_flv, &
- real(me(term%connected_qn_index%get_index (i_flv, i_sub = 1))))
- call set_sqme_coll_isr (BEAM_MINUS, PDF, i_flv, &
- real(me(term%connected_qn_index%get_index (i_flv, i_sub = 2))))
- if (pcm%settings%nlo_correction_type == "QCD" .or. &
- pcm%settings%nlo_correction_type == "Full") then
- call set_sqme_coll_isr (BEAM_PLUS, PDF_SINGLET, i_flv, &
- real(me(term%connected_qn_index%get_index (i_flv, i_sub = 3))))
- call set_sqme_coll_isr (BEAM_MINUS, PDF_SINGLET, i_flv, &
- real(me(term%connected_qn_index%get_index (i_flv, i_sub = 4))))
- end if
- end do
- end associate
- if (debug2_active (D_BEAMS)) then
- call msg_debug2 (D_BEAMS, "term_instance_compute_sqme_coll_isr")
- if (term%nlo_type == NLO_REAL) then
- print *, "nlo_type: REAL"
- print *, "n_flv: ", pcm%region_data%n_flv_born
- print *, "i_flv: ", i_flv
- print *, "Beam 1: "
- print *, " quarks: ", pcm_instance%real_sub%sqme_coll_isr (BEAM_PLUS, PDF, :)
- print *, " gluon: ", pcm_instance%real_sub%sqme_coll_isr (BEAM_PLUS, PDF_SINGLET, :)
- print *, "Beam 2: "
- print *, " quarks: ", pcm_instance%real_sub%sqme_coll_isr (BEAM_MINUS, PDF, :)
- print *, " gluon: ", pcm_instance%real_sub%sqme_coll_isr (BEAM_MINUS, PDF_SINGLET, :)
- else if (term%nlo_type == NLO_DGLAP) then
- print *, "nlo_type: DGLAP"
- print *, "n_flv: ", pcm%region_data%n_flv_born
- print *, "i_flv: ", i_flv
- print *, "Beam 1: "
- print *, " quarks: ", pcm_instance%dglap_remnant%sqme_coll_isr (BEAM_PLUS, PDF, :)
- print *, " gluon: ", pcm_instance%dglap_remnant%sqme_coll_isr (BEAM_PLUS, PDF_SINGLET, :)
- print *, "Beam 2: "
- print *, " quarks: ", pcm_instance%dglap_remnant%sqme_coll_isr (BEAM_MINUS, PDF, :)
- print *, " gluon: ", pcm_instance%dglap_remnant%sqme_coll_isr (BEAM_MINUS, PDF_SINGLET, :)
- end if
- end if
- end select
- end select
- contains
- subroutine set_sqme_coll_isr (i_beam, i_type, i_flv, me)
- integer, intent(in) :: i_beam, i_type, i_flv
- real(default), intent(in) :: me
- select type (pcm_instance => term%pcm_instance)
- type is (pcm_instance_nlo_t)
- select case (term%nlo_type)
- case (NLO_REAL)
- pcm_instance%real_sub%sqme_coll_isr (i_beam, i_type, i_flv) = me
- case (NLO_DGLAP)
- pcm_instance%dglap_remnant%sqme_coll_isr (i_beam, i_type, i_flv) = me
- end select
- end select
- end subroutine set_sqme_coll_isr
- end subroutine term_instance_compute_sqme_coll_isr
-
-@ %def term_instance_compute_sqme_coll_isr
@
<<Instances: term instance: TBP>>=
procedure :: apply_fks => term_instance_apply_fks
<<Instances: procedures>>=
subroutine term_instance_apply_fks (term, alpha_s_sub, alpha_qed_sub)
class(term_instance_t), intent(inout) :: term
real(default), intent(in) :: alpha_s_sub, alpha_qed_sub
real(default), dimension(:), allocatable :: sqme
integer :: i, i_phs, emitter
logical :: is_subtraction
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
select type (config => pcm_instance%config)
type is (pcm_nlo_t)
if (term%connected%has_matrix) then
allocate (sqme (config%get_n_alr ()))
else
allocate (sqme (1))
end if
sqme = zero
select type (phs => term%k_term%phs)
type is (phs_fks_t)
call pcm_instance%set_real_and_isr_kinematics &
(phs%phs_identifiers, term%k_term%phs%get_sqrts ())
if (term%k_term%emitter < 0) then
call pcm_instance%set_subtraction_event ()
do i_phs = 1, config%region_data%n_phs
emitter = phs%phs_identifiers(i_phs)%emitter
call pcm_instance%real_sub%compute (emitter, &
i_phs, alpha_s_sub, alpha_qed_sub, term%connected%has_matrix, sqme)
end do
else
call pcm_instance%set_radiation_event ()
emitter = term%k_term%emitter; i_phs = term%k_term%i_phs
do i = 1, term%connected_qn_index%get_n_flv ()
pcm_instance%real_sub%sqme_real_non_sub (i, i_phs) = &
real (term%connected%trace%get_matrix_element ( &
term%connected_qn_index%get_index (i)))
end do
call pcm_instance%real_sub%compute (emitter, i_phs, alpha_s_sub, &
alpha_qed_sub, term%connected%has_matrix, sqme)
end if
end select
end select
end select
if (term%connected%has_trace) &
call term%connected%trace%set_only_matrix_element &
(1, cmplx (sum(sqme), 0, default))
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
is_subtraction = term%k_term%emitter < 0
if (term%connected%has_matrix) &
call refill_evaluator (cmplx (sqme, 0, default), &
config%get_qn (is_subtraction), &
config%region_data%get_flavor_indices (is_subtraction), &
term%connected%matrix)
if (term%connected%has_flows) &
call refill_evaluator (cmplx (sqme, 0, default), &
config%get_qn (is_subtraction), &
config%region_data%get_flavor_indices (is_subtraction), &
term%connected%flows)
end select
end subroutine term_instance_apply_fks
@ %def term_instance_apply_fks
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_sqme_virt => term_instance_evaluate_sqme_virt
<<Instances: procedures>>=
subroutine term_instance_evaluate_sqme_virt (term, alpha_s, alpha_qed)
class(term_instance_t), intent(inout) :: term
real(default), intent(in) :: alpha_s, alpha_qed
real(default) :: alpha_coupling
type(vector4_t), dimension(:), allocatable :: p_born
real(default), dimension(:), allocatable :: sqme_virt
integer :: i_flv
if (term%nlo_type /= NLO_VIRTUAL) call msg_fatal &
("Trying to evaluate virtual matrix element with unsuited term_instance.")
if (debug2_active (D_VIRTUAL)) then
call msg_debug2 (D_VIRTUAL, "Evaluating virtual-subtracted matrix elements")
print *, 'ren_scale: ', term%ren_scale
print *, 'fac_scale: ', term%fac_scale
end if
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
associate (nlo_corr_type => config%region_data%regions(1)%nlo_correction_type)
if (nlo_corr_type == "QCD") then
alpha_coupling = alpha_s
if (debug2_active (D_VIRTUAL)) print *, 'alpha_s: ', alpha_coupling
else if (nlo_corr_type == "QED") then
alpha_coupling = alpha_qed
if (debug2_active (D_VIRTUAL)) print *, 'alpha_qed: ', alpha_coupling
end if
end associate
allocate (p_born (config%region_data%n_legs_born))
if (config%settings%factorization_mode == FACTORIZATION_THRESHOLD) then
p_born = pcm_instance%real_kinematics%p_born_onshell%get_momenta(1)
else
p_born = term%int_hard%get_momenta ()
end if
call pcm_instance%set_momenta_and_scales_virtual &
(p_born, term%ren_scale, term%fac_scale)
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
associate (virtual => pcm_instance%virtual)
do i_flv = 1, term%connected_qn_index%get_n_flv ()
virtual%sqme_born(i_flv) = &
real (term%connected%trace%get_matrix_element ( &
term%connected_qn_index%get_index (i_flv, i_sub = 0)))
virtual%sqme_virt_fin(i_flv) = &
real (term%connected%trace%get_matrix_element ( &
term%connected_qn_index%get_index (i_flv, i_sub = 1)))
end do
end associate
end select
call pcm_instance%compute_sqme_virt (term%p_hard, alpha_coupling, &
term%connected%has_matrix, sqme_virt)
call term%connected%trace%set_only_matrix_element &
(1, cmplx (sum(sqme_virt) * term%weight, 0, default))
if (term%connected%has_matrix) then
call refill_evaluator (cmplx (sqme_virt * term%weight, 0, default), &
config%get_qn (.true.), &
config%region_data%get_flavor_indices (.true.), &
term%connected%matrix)
end if
end select
end select
end subroutine term_instance_evaluate_sqme_virt
@ %def term_instance_evaluate_sqme_virt
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_sqme_mismatch => term_instance_evaluate_sqme_mismatch
<<Instances: procedures>>=
subroutine term_instance_evaluate_sqme_mismatch (term, alpha_s)
class(term_instance_t), intent(inout) :: term
real(default), intent(in) :: alpha_s
real(default), dimension(:), allocatable :: sqme_mism
if (term%nlo_type /= NLO_MISMATCH) call msg_fatal &
("Trying to evaluate soft mismatch with unsuited term_instance.")
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
call pcm_instance%compute_sqme_mismatch &
(alpha_s, term%connected%has_matrix, sqme_mism)
end select
call term%connected%trace%set_only_matrix_element &
(1, cmplx (sum (sqme_mism) * term%weight, 0, default))
if (term%connected%has_matrix) then
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
call refill_evaluator (cmplx (sqme_mism * term%weight, 0, default), &
config%get_qn (.true.), config%region_data%get_flavor_indices (.true.), &
term%connected%matrix)
end select
end if
end subroutine term_instance_evaluate_sqme_mismatch
@ %def term_instance_evaluate_sqme_mismatch
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_sqme_dglap => term_instance_evaluate_sqme_dglap
<<Instances: procedures>>=
subroutine term_instance_evaluate_sqme_dglap (term, alpha_s)
class(term_instance_t), intent(inout) :: term
real(default), intent(in) :: alpha_s
real(default), dimension(:), allocatable :: sqme_dglap
integer :: i_flv
if (term%nlo_type /= NLO_DGLAP) call msg_fatal &
("Trying to evaluate DGLAP remnant with unsuited term_instance.")
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "term_instance_evaluate_sqme_dglap")
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
if (debug2_active (D_PROCESS_INTEGRATION)) then
- associate (n_flv => pcm_instance%dglap_remnant%n_flv)
+ associate (n_flv => pcm_instance%dglap_remnant%reg_data%n_flv_born)
print *, "size(sqme_born) = ", size (pcm_instance%dglap_remnant%sqme_born)
call term%connected%trace%write ()
do i_flv = 1, n_flv
print *, "i_flv = ", i_flv, ", n_flv = ", n_flv
print *, "sqme_born(i_flv) = ", pcm_instance%dglap_remnant%sqme_born(i_flv)
end do
end associate
end if
call pcm_instance%compute_sqme_dglap_remnant (alpha_s, &
term%connected%has_matrix, sqme_dglap)
end select
call term%connected%trace%set_only_matrix_element &
(1, cmplx (sum (sqme_dglap) * term%weight, 0, default))
if (term%connected%has_matrix) then
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
call refill_evaluator (cmplx (sqme_dglap * term%weight, 0, default), &
config%get_qn (.true.), &
config%region_data%get_flavor_indices (.true.), &
term%connected%matrix)
end select
end if
end subroutine term_instance_evaluate_sqme_dglap
@ %def term_instance_evaluate_sqme_dglap
@ Reset the term instance: clear the parton-state expressions and deactivate.
<<Instances: term instance: TBP>>=
procedure :: reset => term_instance_reset
<<Instances: procedures>>=
subroutine term_instance_reset (term)
class(term_instance_t), intent(inout) :: term
call term%connected%reset_expressions ()
if (allocated (term%alpha_qcd_forced)) deallocate (term%alpha_qcd_forced)
term%active = .false.
end subroutine term_instance_reset
@ %def term_instance_reset
@ Force an $\alpha_s$ value that should be used in the matrix-element
calculation.
<<Instances: term instance: TBP>>=
procedure :: set_alpha_qcd_forced => term_instance_set_alpha_qcd_forced
<<Instances: procedures>>=
subroutine term_instance_set_alpha_qcd_forced (term, alpha_qcd)
class(term_instance_t), intent(inout) :: term
real(default), intent(in) :: alpha_qcd
if (allocated (term%alpha_qcd_forced)) then
term%alpha_qcd_forced = alpha_qcd
else
allocate (term%alpha_qcd_forced, source = alpha_qcd)
end if
end subroutine term_instance_set_alpha_qcd_forced
@ %def term_instance_set_alpha_qcd_forced
@ Complete the kinematics computation for the effective parton states.
We assume that the [[compute_hard_kinematics]] method of the process
component instance has already been called, so the [[int_hard]]
contains the correct hard kinematics. The duty of this procedure is
first to compute the effective kinematics and store this in the
[[int_eff]] effective interaction inside the [[isolated]] parton
state. The effective kinematics may differ from the kinematics in the hard
interaction. It may involve parton recombination or parton splitting.
The [[rearrange_partons]] method is responsible for this part.
We may also call a method to compute the effective structure-function
chain at this point. This is not implemented yet.
In the simple case that no rearrangement is necessary, as indicated by
the [[rearrange]] flag, the effective interaction is a pointer to the
hard interaction, and we can skip the rearrangement method. Similarly
for the effective structure-function chain. (If we have an algorithm
that uses rarrangement, it should evaluate [[k_term]] explicitly.)
The final step of kinematics setup is to transfer the effective
kinematics to the evaluators and to the [[subevt]].
<<Instances: term instance: TBP>>=
procedure :: compute_eff_kinematics => &
term_instance_compute_eff_kinematics
<<Instances: procedures>>=
subroutine term_instance_compute_eff_kinematics (term)
class(term_instance_t), intent(inout) :: term
term%checked = .false.
term%passed = .false.
call term%isolated%receive_kinematics ()
call term%connected%receive_kinematics ()
end subroutine term_instance_compute_eff_kinematics
@ %def term_instance_compute_eff_kinematics
@ Inverse. Reconstruct the connected state from the momenta in the
trace evaluator (which we assume to be set), then reconstruct the
isolated state as far as possible. The second part finalizes the
momentum configuration, using the incoming seed momenta
<<Instances: term instance: TBP>>=
procedure :: recover_hard_kinematics => &
term_instance_recover_hard_kinematics
<<Instances: procedures>>=
subroutine term_instance_recover_hard_kinematics (term)
class(term_instance_t), intent(inout) :: term
term%checked = .false.
term%passed = .false.
call term%connected%send_kinematics ()
call term%isolated%send_kinematics ()
end subroutine term_instance_recover_hard_kinematics
@ %def term_instance_recover_hard_kinematics
@ Check the term whether it passes cuts and, if successful, evaluate
scales and weights. The factorization scale is also given to the term
kinematics, enabling structure-function evaluation.
<<Instances: term instance: TBP>>=
procedure :: evaluate_expressions => &
term_instance_evaluate_expressions
<<Instances: procedures>>=
subroutine term_instance_evaluate_expressions (term, scale_forced)
class(term_instance_t), intent(inout) :: term
real(default), intent(in), allocatable, optional :: scale_forced
call term%connected%evaluate_expressions (term%passed, &
term%scale, term%fac_scale, term%ren_scale, term%weight, &
scale_forced, force_evaluation = .true.)
term%checked = .true.
end subroutine term_instance_evaluate_expressions
@ %def term_instance_evaluate_expressions
@ Evaluate the trace: first evaluate the hard interaction, then the trace
evaluator. We use the [[evaluate_interaction]] method of the process
component which generated this term. The [[subevt]] and cut expressions are
not yet filled.
The [[component]] argument is intent(inout) because the [[compute_amplitude]]
method may modify the [[core_state]] workspace object.
<<Instances: term instance: TBP>>=
procedure :: evaluate_interaction => term_instance_evaluate_interaction
<<Instances: procedures>>=
subroutine term_instance_evaluate_interaction (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(in), pointer :: core
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, &
"term_instance_evaluate_interaction")
term%p_hard = term%int_hard%get_momenta ()
select type (core)
class is (prc_external_t)
call term%evaluate_interaction_userdef (core)
class default
call term%evaluate_interaction_default (core)
end select
call term%int_hard%set_matrix_element (term%amp)
end subroutine term_instance_evaluate_interaction
@ %def term_instance_evaluate_interaction
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_interaction_default &
=> term_instance_evaluate_interaction_default
<<Instances: procedures>>=
subroutine term_instance_evaluate_interaction_default (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(in) :: core
integer :: i
do i = 1, term%config%n_allowed
term%amp(i) = core%compute_amplitude (term%config%i_term, term%p_hard, &
term%config%flv(i), term%config%hel(i), term%config%col(i), &
term%fac_scale, term%ren_scale, term%alpha_qcd_forced, &
term%core_state)
end do
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
call pcm_instance%set_fac_scale (term%fac_scale)
end select
end subroutine term_instance_evaluate_interaction_default
@ %def term_instance_evaluate_interaction_default
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_interaction_userdef &
=> term_instance_evaluate_interaction_userdef
<<Instances: procedures>>=
subroutine term_instance_evaluate_interaction_userdef (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(inout) :: core
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, &
"term_instance_evaluate_interaction_userdef")
select type (core_state => term%core_state)
type is (openloops_state_t)
select type (core)
type is (prc_openloops_t)
call core%compute_alpha_s (core_state, term%ren_scale)
if (allocated (core_state%threshold_data)) &
call evaluate_threshold_parameters (core_state, core, term%k_term%phs%get_sqrts ())
end select
class is (prc_external_state_t)
select type (core)
class is (prc_external_t)
call core%compute_alpha_s (core_state, term%ren_scale)
end select
end select
call evaluate_threshold_interaction ()
if (term%nlo_type == NLO_VIRTUAL) then
call term%evaluate_interaction_userdef_loop (core)
else
call term%evaluate_interaction_userdef_tree (core)
end if
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
call pcm_instance%set_fac_scale (term%fac_scale)
end select
-
contains
subroutine evaluate_threshold_parameters (core_state, core, sqrts)
type(openloops_state_t), intent(inout) :: core_state
type(prc_openloops_t), intent(inout) :: core
real(default), intent(in) :: sqrts
real(default) :: mtop, wtop
mtop = m1s_to_mpole (sqrts)
wtop = core_state%threshold_data%compute_top_width &
(mtop, core_state%alpha_qcd)
call core%set_mass_and_width (6, mtop, wtop)
end subroutine
subroutine evaluate_threshold_interaction ()
integer :: leg
select type (core)
type is (prc_threshold_t)
if (term%nlo_type > BORN) then
select type (pcm => term%pcm_instance)
type is (pcm_instance_nlo_t)
if (term%k_term%emitter >= 0) then
call core%set_offshell_momenta &
(pcm%real_kinematics%p_real_cms%get_momenta(term%config%i_term))
leg = thr_leg (term%k_term%emitter)
call core%set_leg (leg)
call core%set_onshell_momenta &
(pcm%real_kinematics%p_real_onshell(leg)%get_momenta(term%config%i_term))
else
call core%set_leg (0)
call core%set_offshell_momenta &
(pcm%real_kinematics%p_born_cms%get_momenta(1))
end if
end select
else
call core%set_leg (-1)
call core%set_offshell_momenta (term%p_hard)
end if
end select
end subroutine evaluate_threshold_interaction
end subroutine term_instance_evaluate_interaction_userdef
@ %def term_instance_evaluate_interaction_userdef
@ Retrieve the matrix elements from a matrix element provider and place them
into [[term%amp]].
For the handling of NLO calculations, FKS applies a book keeping handling
flavor and/or particle type (e.g. for QCD: quark/gluon and quark flavor) in
order to calculate the subtraction terms. Therefore, we have to insert the
calculated matrix elements correctly into the state matrix where each entry
corresponds to a set of quantum numbers. We apply a mapping [[hard_qn_ind]] from a list of
quantum numbers provided by FKS to the hard process [[int_hard]].
The calculated matrix elements are insert into [[term%amp]] in the following
way. The first [[n_born]] particles are the matrix element of the hard process.
-In non-trivial beams, we store another [[n_beam_structure_int]] copies of these
-matrix elements as the first [[n_beam_structure_int]] subtractions.
+In non-trivial beams, we store another [[n_beams_rescaled]] copies of these
+matrix elements as the first [[n_beams_rescaled]] subtractions. This is a remnant
+from times before the method [[term_instance_set_sf_factors]] and these entries are
+not used anymore. However, eliminating these entries involves deeper changes in how
+the connection tables for the evaluator product are set up and should therefore be
+part of a larger refactoring of the interactions \& state matrices.
The next $n_{\text{born}}\times n_{sub}$ are color-correlated born matrix elements.
<<Instances: term instance: TBP>>=
procedure :: evaluate_interaction_userdef_tree &
=> term_instance_evaluate_interaction_userdef_tree
<<Instances: procedures>>=
subroutine term_instance_evaluate_interaction_userdef_tree (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(inout) :: core
real(default) :: sqme
real(default), dimension(:), allocatable :: sqme_color_c
real(default), dimension(:), allocatable :: sqme_spin_c
real(default), dimension(16) :: sqme_spin_c_tmp
integer :: n_flv, n_hel, n_sub_color, n_sub_spin, n_pdf_off
integer :: i_flv, i_hel, i_sub, i_color_c, i_spin_c, i_emitter
integer :: emitter
logical :: bad_point, bp
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, &
"term_instance_evaluate_interaction_userdef_tree")
allocate (sqme_color_c (blha_result_array_size &
(term%int_hard%get_n_tot (), BLHA_AMP_COLOR_C)))
n_flv = term%hard_qn_index%get_n_flv ()
n_hel = term%hard_qn_index%get_n_hel ()
n_sub_color = term%get_n_sub_color ()
n_sub_spin = term%get_n_sub_spin ()
do i_flv = 1, n_flv
do i_hel = 1, n_hel
select type (core)
class is (prc_external_t)
call core%update_alpha_s (term%core_state, term%ren_scale)
call core%compute_sqme (i_flv, i_hel, term%p_hard, term%ren_scale, &
sqme, bad_point)
call term%pcm_instance%set_bad_point (bad_point)
associate (i_int => term%hard_qn_index%get_index (i_flv = i_flv, i_hel = i_hel, i_sub = 0))
term%amp(i_int) = cmplx (sqme, 0, default)
end associate
end select
n_pdf_off = 0
if (term%pcm_instance%config%has_pdfs .and. &
(term%is_subtraction () .or. term%nlo_type == NLO_DGLAP)) then
- n_pdf_off = n_pdf_off + n_beam_structure_int
+ n_pdf_off = n_pdf_off + n_beams_rescaled
do i_sub = 1, n_pdf_off
term%amp(term%hard_qn_index%get_index (i_flv, i_hel, i_sub)) = &
term%amp(term%hard_qn_index%get_index (i_flv, i_hel, i_sub = 0))
end do
end if
if ((term%nlo_type == NLO_REAL .and. term%is_subtraction ()) .or. &
term%nlo_type == NLO_MISMATCH) then
sqme_color_c = zero
select type (core)
class is (prc_blha_t)
call core%compute_sqme_color_c_raw (i_flv, i_hel, &
term%p_hard, term%ren_scale, sqme_color_c, bad_point)
call term%pcm_instance%set_bad_point (bad_point)
class is (prc_recola_t)
call core%compute_sqme_color_c_raw (i_flv, i_hel, &
term%p_hard, term%ren_scale, sqme_color_c, bad_point)
call term%pcm_instance%set_bad_point (bad_point)
end select
do i_sub = 1, n_sub_color
i_color_c = term%hard_qn_index%get_index &
(i_flv, i_hel, i_sub + n_pdf_off)
term%amp(i_color_c) = cmplx (sqme_color_c(i_sub), 0, default)
end do
if (n_sub_spin > 0) then
bad_point = .false.
allocate (sqme_spin_c(0))
select type (core)
type is (prc_openloops_t)
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
do i_emitter = 1, config%region_data%n_emitters
emitter = config%region_data%emitters(i_emitter)
if (emitter > 0) then
call core%compute_sqme_spin_c &
(i_flv, &
i_hel, &
emitter, &
term%p_hard, &
term%ren_scale, &
sqme_spin_c_tmp, &
bp)
sqme_spin_c = [sqme_spin_c, sqme_spin_c_tmp]
bad_point = bad_point .or. bp
end if
end do
end select
do i_sub = 1, n_sub_spin
i_spin_c = term%hard_qn_index%get_index (i_flv, i_hel, &
i_sub + n_pdf_off + n_sub_color)
term%amp(i_spin_c) = cmplx &
(sqme_spin_c(i_sub), 0, default)
end do
end select
deallocate (sqme_spin_c)
end if
end if
end do
end do
end subroutine term_instance_evaluate_interaction_userdef_tree
@ %def term_instance_evaluate_interaction_userdef_tree
@
<<Instances: term instance: TBP>>=
procedure :: evaluate_interaction_userdef_loop &
=> term_instance_evaluate_interaction_userdef_loop
<<Instances: procedures>>=
subroutine term_instance_evaluate_interaction_userdef_loop (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(in) :: core
integer :: n_hel, n_sub, n_flv
integer :: i, i_flv, i_hel, i_sub, i_virt, i_color_c
real(default), dimension(4) :: sqme_virt
real(default), dimension(:), allocatable :: sqme_color_c
logical :: bad_point
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, &
"term_instance_evaluate_interaction_userdef_loop")
allocate (sqme_color_c (blha_result_array_size &
(term%int_hard%get_n_tot (), BLHA_AMP_COLOR_C)))
n_flv = term%hard_qn_index%get_n_flv ()
n_hel = term%hard_qn_index%get_n_hel ()
n_sub = term%hard_qn_index%get_n_sub ()
i_virt = 1
do i_flv = 1, n_flv
do i_hel = 1, n_hel
select type (core)
class is (prc_external_t)
call core%compute_sqme_virt (i_flv, i_hel, term%p_hard, &
term%ren_scale, sqme_virt, bad_point)
call term%pcm_instance%set_bad_point (bad_point)
end select
associate (i_born => term%hard_qn_index%get_index (i_flv, i_hel = i_hel, i_sub = 0), &
i_loop => term%hard_qn_index%get_index (i_flv, i_hel = i_hel, i_sub = i_virt))
term%amp(i_loop) = cmplx (sqme_virt(3), 0, default)
term%amp(i_born) = cmplx (sqme_virt(4), 0, default)
end associate
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
select type (core)
class is (prc_blha_t)
call core%compute_sqme_color_c_raw (i_flv, i_hel, &
term%p_hard, term%ren_scale, &
sqme_color_c, bad_point)
call term%pcm_instance%set_bad_point (bad_point)
do i_sub = 1 + i_virt, n_sub
i_color_c = term%hard_qn_index%get_index &
(i_flv, i_hel = i_hel, i_sub = i_sub)
! Index shift: i_sub - i_virt
term%amp(i_color_c) = &
cmplx (sqme_color_c(i_sub - i_virt), 0, default)
end do
type is (prc_recola_t)
call core%compute_sqme_color_c_raw (i_flv, i_hel, &
term%p_hard, term%ren_scale, sqme_color_c, bad_point)
call term%pcm_instance%set_bad_point (bad_point)
do i_sub = 1 + i_virt, n_sub
i_color_c = term%hard_qn_index%get_index &
(i_flv, i_hel = i_hel, i_sub = i_sub)
! Index shift: i_sub - i_virt
term%amp(i_color_c) = &
cmplx (sqme_color_c(i_sub - i_virt), 0, default)
end do
end select
end select
end do
end do
end subroutine term_instance_evaluate_interaction_userdef_loop
@ %def term_instance_evaluate_interaction_userdef_loop
@ Evaluate the trace. First evaluate the
structure-function chain (i.e., the density matrix of the incoming
partons). Do this twice, in case the sf-chain instances within
[[k_term]] and [[isolated]] differ. Next, evaluate the hard
interaction, then compute the convolution with the initial state.
<<Instances: term instance: TBP>>=
procedure :: evaluate_trace => term_instance_evaluate_trace
<<Instances: procedures>>=
subroutine term_instance_evaluate_trace (term)
class(term_instance_t), intent(inout) :: term
- class(sf_rescale_t), allocatable :: func
call term%k_term%evaluate_sf_chain (term%fac_scale)
call term%evaluate_scaled_sf_chains ()
call term%isolated%evaluate_sf_chain (term%fac_scale)
call term%isolated%evaluate_trace ()
call term%connected%evaluate_trace ()
end subroutine term_instance_evaluate_trace
@ %def term_instance_evaluate_trace
@ Include rescaled structure functions due to NLO calculation.
-We rescale the structure function for the real subtraction [[sf_rescale_collinear]], the collinear
-counter terms [[sf_rescale_dglap_t]] and for the case, we have an emitter in the initial state,
-rescale the kinematics for it using [[sf_rescale_real_t]].
-
-References: arXiv:0709.2092, (2.35)-(2.42).
+We rescale the structure function for the real subtraction [[sf_rescale_collinear]],
+the collinear counter terms [[sf_rescale_dglap_t]] and for the case, in which we have
+an emitter in the initial state, we rescale the kinematics for it using [[sf_rescale_real_t]].\\
+References: arXiv:0709.2092, (2.35)-(2.42).\\
Obviously, it is completely irrelevant, which beam is treated.
It becomes problematic when handling [[e, p]]-beams.
<<Instances: term instance: TBP>>=
procedure :: evaluate_scaled_sf_chains => term_instance_evaluate_scaled_sf_chains
<<Instances: procedures>>=
subroutine term_instance_evaluate_scaled_sf_chains (term)
class(term_instance_t), intent(inout) :: term
- class(sf_rescale_t), allocatable :: func
- integer :: i_sub
+ class(sf_rescale_t), allocatable :: sf_rescale
if (.not. term%pcm_instance%config%has_pdfs) return
if (term%nlo_type == NLO_REAL) then
if (term%is_subtraction ()) then
- allocate (sf_rescale_collinear_t :: func)
+ allocate (sf_rescale_collinear_t :: sf_rescale)
select type (pcm => term%pcm_instance)
type is (pcm_instance_nlo_t)
- select type (func)
+ select type (sf_rescale)
type is (sf_rescale_collinear_t)
- call func%set (pcm%real_kinematics%xi_tilde)
- call func%set_gluons (.true.)
+ call sf_rescale%set (pcm%real_kinematics%xi_tilde)
end select
end select
- call term%k_term%sf_chain%evaluate (term%fac_scale, func)
- deallocate (func)
+ call term%k_term%sf_chain%evaluate (term%fac_scale, sf_rescale)
+ deallocate (sf_rescale)
else if (term%k_term%emitter >= 0 .and. term%k_term%emitter <= term%k_term%n_in) then
- allocate (sf_rescale_real_t :: func)
+ allocate (sf_rescale_real_t :: sf_rescale)
select type (pcm => term%pcm_instance)
type is (pcm_instance_nlo_t)
- select type (func)
+ select type (sf_rescale)
type is (sf_rescale_real_t)
- call func%set (pcm%real_kinematics%xi_tilde * &
+ call sf_rescale%set (pcm%real_kinematics%xi_tilde * &
pcm%real_kinematics%xi_max (term%k_term%i_phs), &
pcm%real_kinematics%y (term%k_term%i_phs))
- call func%restrict_to_beam (term%k_term%emitter)
end select
end select
- call term%k_term%sf_chain%evaluate (term%fac_scale, func)
- deallocate (func)
+ call term%k_term%sf_chain%evaluate (term%fac_scale, sf_rescale)
+ deallocate (sf_rescale)
else
call term%k_term%sf_chain%evaluate (term%fac_scale)
end if
else if (term%nlo_type == NLO_DGLAP) then
- allocate (sf_rescale_dglap_t :: func)
+ allocate (sf_rescale_dglap_t :: sf_rescale)
select type (pcm => term%pcm_instance)
type is (pcm_instance_nlo_t)
- select type (func)
+ select type (sf_rescale)
type is (sf_rescale_dglap_t)
- call func%set (pcm%isr_kinematics%z)
- call func%set_gluons (.true.)
+ call sf_rescale%set (pcm%isr_kinematics%z)
end select
end select
- call term%k_term%sf_chain%evaluate (term%fac_scale, func)
- deallocate (func)
+ call term%k_term%sf_chain%evaluate (term%fac_scale, sf_rescale)
+ deallocate (sf_rescale)
end if
end subroutine term_instance_evaluate_scaled_sf_chains
@ %def term_instance_evaluate_scaled_sf_chains
@ Evaluate the extra data that we need for processing the object as a
physical event.
<<Instances: term instance: TBP>>=
procedure :: evaluate_event_data => term_instance_evaluate_event_data
<<Instances: procedures>>=
subroutine term_instance_evaluate_event_data (term)
class(term_instance_t), intent(inout) :: term
logical :: only_momenta
only_momenta = term%nlo_type > BORN
call term%isolated%evaluate_event_data (only_momenta)
call term%connected%evaluate_event_data (only_momenta)
end subroutine term_instance_evaluate_event_data
@ %def term_instance_evaluate_event_data
@
<<Instances: term instance: TBP>>=
procedure :: set_fac_scale => term_instance_set_fac_scale
<<Instances: procedures>>=
subroutine term_instance_set_fac_scale (term, fac_scale)
class(term_instance_t), intent(inout) :: term
real(default), intent(in) :: fac_scale
term%fac_scale = fac_scale
end subroutine term_instance_set_fac_scale
@ %def term_instance_set_fac_scale
@ Return data that might be useful for external processing. The
factorization scale:
<<Instances: term instance: TBP>>=
procedure :: get_fac_scale => term_instance_get_fac_scale
<<Instances: procedures>>=
function term_instance_get_fac_scale (term) result (fac_scale)
class(term_instance_t), intent(in) :: term
real(default) :: fac_scale
fac_scale = term%fac_scale
end function term_instance_get_fac_scale
@ %def term_instance_get_fac_scale
@ We take the strong coupling from the process core. The value is calculated
when a new event is requested, so we should call it only after the event has
been evaluated. If it is not available there (a negative number is returned),
we take the value stored in the term configuration, which should be determined
by the model. If the model does not provide a value, the result is zero.
<<Instances: term instance: TBP>>=
procedure :: get_alpha_s => term_instance_get_alpha_s
<<Instances: procedures>>=
function term_instance_get_alpha_s (term, core) result (alpha_s)
class(term_instance_t), intent(in) :: term
class(prc_core_t), intent(in) :: core
real(default) :: alpha_s
alpha_s = core%get_alpha_s (term%core_state)
if (alpha_s < zero) alpha_s = term%config%alpha_s
end function term_instance_get_alpha_s
@ %def term_instance_get_alpha_s
@
<<Instances: term instance: TBP>>=
procedure :: reset_phs_identifiers => term_instance_reset_phs_identifiers
<<Instances: procedures>>=
subroutine term_instance_reset_phs_identifiers (term)
class(term_instance_t), intent(inout) :: term
select type (phs => term%k_term%phs)
type is (phs_fks_t)
phs%phs_identifiers%evaluated = .false.
end select
end subroutine term_instance_reset_phs_identifiers
@ %def term_instance_reset_phs_identifiers
@ The second helicity for [[helicities]] comes with a minus sign
because OpenLoops inverts the helicity index of antiparticles.
<<Instances: term instance: TBP>>=
procedure :: get_helicities_for_openloops => term_instance_get_helicities_for_openloops
<<Instances: procedures>>=
subroutine term_instance_get_helicities_for_openloops (term, helicities)
class(term_instance_t), intent(in) :: term
integer, dimension(:,:), allocatable, intent(out) :: helicities
type(helicity_t), dimension(:), allocatable :: hel
type(quantum_numbers_t), dimension(:,:), allocatable :: qn
type(quantum_numbers_mask_t) :: qn_mask
integer :: h, i, j, n_in
call qn_mask%set_sub (1)
call term%isolated%trace%get_quantum_numbers_mask (qn_mask, qn)
n_in = term%int_hard%get_n_in ()
allocate (helicities (size (qn, dim=1), n_in))
allocate (hel (n_in))
do i = 1, size (qn, dim=1)
do j = 1, n_in
hel(j) = qn(i, j)%get_helicity ()
call hel(j)%diagonalize ()
call hel(j)%get_indices (h, h)
helicities (i, j) = h
end do
end do
end subroutine term_instance_get_helicities_for_openloops
@ %def term_instance_get_helicities_for_openloops
@
<<Instances: term instance: TBP>>=
procedure :: get_boost_to_lab => term_instance_get_boost_to_lab
<<Instances: procedures>>=
function term_instance_get_boost_to_lab (term) result (lt)
type(lorentz_transformation_t) :: lt
class(term_instance_t), intent(in) :: term
lt = term%k_term%phs%get_lorentz_transformation ()
end function term_instance_get_boost_to_lab
@ %def term_instance_get_boost_to_lab
@
<<Instances: term instance: TBP>>=
procedure :: get_boost_to_cms => term_instance_get_boost_to_cms
<<Instances: procedures>>=
function term_instance_get_boost_to_cms (term) result (lt)
type(lorentz_transformation_t) :: lt
class(term_instance_t), intent(in) :: term
lt = inverse (term%k_term%phs%get_lorentz_transformation ())
end function term_instance_get_boost_to_cms
@ %def term_instance_get_boost_to_cms
@
<<Instances: term instance: TBP>>=
procedure :: get_i_term_global => term_instance_get_i_term_global
<<Instances: procedures>>=
elemental function term_instance_get_i_term_global (term) result (i_term)
integer :: i_term
class(term_instance_t), intent(in) :: term
i_term = term%config%i_term_global
end function term_instance_get_i_term_global
@ %def term_instance_get_i_term_global
@
<<Instances: term instance: TBP>>=
procedure :: is_subtraction => term_instance_is_subtraction
<<Instances: procedures>>=
elemental function term_instance_is_subtraction (term) result (sub)
logical :: sub
class(term_instance_t), intent(in) :: term
sub = term%config%i_term_global == term%config%i_sub
end function term_instance_is_subtraction
@ %def term_instance_is_subtraction
@ Retrieve [[n_sub]] which was calculated in [[process_term_setup_interaction]].
<<Instances: term instance: TBP>>=
procedure :: get_n_sub => term_instance_get_n_sub
procedure :: get_n_sub_color => term_instance_get_n_sub_color
procedure :: get_n_sub_spin => term_instance_get_n_sub_spin
<<Instances: procedures>>=
function term_instance_get_n_sub (term) result (n_sub)
integer :: n_sub
class(term_instance_t), intent(in) :: term
n_sub = term%config%n_sub
end function term_instance_get_n_sub
function term_instance_get_n_sub_color (term) result (n_sub_color)
integer :: n_sub_color
class(term_instance_t), intent(in) :: term
n_sub_color = term%config%n_sub_color
end function term_instance_get_n_sub_color
function term_instance_get_n_sub_spin (term) result (n_sub_spin)
integer :: n_sub_spin
class(term_instance_t), intent(in) :: term
n_sub_spin = term%config%n_sub_spin
end function term_instance_get_n_sub_spin
@ %def term_instance_get_n_sub
@ %def term_instance_get_n_sub_color
@ %def term_instance_get_n_sub_spin
@
\subsection{The process instance}
A process instance contains all process data that depend on the
sampling point and thus change often. In essence, it is an event
record at the elementary (parton) level. We do not call it such, to
avoid confusion with the actual event records. If decays are
involved, the latter are compositions of several elementary processes
(i.e., their instances).
We implement the process instance as an extension of the
[[mci_sampler_t]] that we need for computing integrals and generate
events.
The base type contains: the [[integrand]], the [[selected_channel]],
the two-dimensional array [[x]] of parameters, and the one-dimensional
array [[f]] of Jacobians. These subobjects are public and used for
communicating with the multi-channel integrator.
The [[process]] pointer accesses the process of which this record is
an instance. It is required whenever the calculation needs invariant
configuration data, therefore the process should stay in memory for
the whole lifetime of its instances.
The [[evaluation_status]] code is used to check the current status.
In particular, failure at various stages is recorded there.
The [[count]] object records process evaluations, broken down
according to status.
The [[sqme]] value is the single real number that results from
evaluating and tracing the kinematics and matrix elements. This
is the number that is handed over to an integration routine.
The [[weight]] value is the event weight. It is defined when an event
has been generated from the process instance, either weighted or
unweighted. The value is the [[sqme]] value times Jacobian weights
from the integration, or unity, respectively.
The [[i_mci]] index chooses a subset of components that are associated with
a common parameter set and integrator, i.e., that are added coherently.
The [[sf_chain]] subobject is a realization of the beam and
structure-function configuration in the [[process]] object. It is not
used for calculation directly but serves as the template for the
sf-chain instances that are contained in the [[component]] objects.
The [[component]] subobjects determine the state of each component.
The [[term]] subobjects are workspace for evaluating kinematics,
matrix elements, cuts etc.
The [[mci_work]] subobject contains the array of real input parameters (random
numbers) that generates the kinematical point. It also contains the workspace
for the MC integrators. The active entry of the [[mci_work]] array is
selected by the [[i_mci]] index above.
The [[hook]] pointer accesses a list of after evaluate objects which are
evalutated after the matrix element.
<<Instances: public>>=
public :: process_instance_t
<<Instances: types>>=
type, extends (mci_sampler_t) :: process_instance_t
type(process_t), pointer :: process => null ()
integer :: evaluation_status = STAT_UNDEFINED
real(default) :: sqme = 0
real(default) :: weight = 0
real(default) :: excess = 0
integer :: n_dropped = 0
integer :: i_mci = 0
integer :: selected_channel = 0
type(sf_chain_t) :: sf_chain
type(term_instance_t), dimension(:), allocatable :: term
type(mci_work_t), dimension(:), allocatable :: mci_work
class(pcm_instance_t), allocatable :: pcm
class(process_instance_hook_t), pointer :: hook => null ()
contains
<<Instances: process instance: TBP>>
end type process_instance_t
@ %def process_instance
@
Wrapper type for storing pointers to process instance objects in arrays.
<<Instances: public>>=
public :: process_instance_ptr_t
<<Instances: types>>=
type :: process_instance_ptr_t
type(process_instance_t), pointer :: p => null ()
end type process_instance_ptr_t
@ %def process_instance_ptr_t
@ The process hooks are first-in-last-out list of objects which are evaluated
after the phase space and matrixelement are evaluated. It is possible to
retrieve the sampler object and read the sampler information.
The hook object are part of the [[process_instance]] and therefore, share a
common lifetime. A data transfer, after the usual lifetime of the
[[process_instance]], is not provided, as such the finalisation procedure has to take care
of this! E.g. write the object to file from which later the collected
information can then be retrieved.
<<Instances: public>>=
public :: process_instance_hook_t
<<Instances: types>>=
type, abstract :: process_instance_hook_t
class(process_instance_hook_t), pointer :: next => null ()
contains
procedure(process_instance_hook_init), deferred :: init
procedure(process_instance_hook_final), deferred :: final
procedure(process_instance_hook_evaluate), deferred :: evaluate
end type process_instance_hook_t
@ %def process_instance_hook_t
@ We have to provide a [[init]], a [[final]] procedure and, for after evaluation, the
[[evaluate]] procedure.
The [[init]] procedures accesses [[var_list]] and current [[instance]] object.
<<Instances: public>>=
public :: process_instance_hook_final, process_instance_hook_evaluate
<<Instances: interfaces>>=
abstract interface
subroutine process_instance_hook_init (hook, var_list, instance)
import :: process_instance_hook_t, var_list_t, process_instance_t
class(process_instance_hook_t), intent(inout), target :: hook
type(var_list_t), intent(in) :: var_list
class(process_instance_t), intent(in), target :: instance
end subroutine process_instance_hook_init
subroutine process_instance_hook_final (hook)
import :: process_instance_hook_t
class(process_instance_hook_t), intent(inout) :: hook
end subroutine process_instance_hook_final
subroutine process_instance_hook_evaluate (hook, instance)
import :: process_instance_hook_t, process_instance_t
class(process_instance_hook_t), intent(inout) :: hook
class(process_instance_t), intent(in), target :: instance
end subroutine process_instance_hook_evaluate
end interface
@ %def process_instance_hook_final, process_instance_hook_evaluate
@ The output routine contains a header with the most relevant
information about the process, copied from
[[process_metadata_write]]. We mark the active components by an asterisk.
The next section is the MC parameter input. The following sections
are written only if the evaluation status is beyond setting the
parameters, or if the [[verbose]] option is set.
<<Instances: process instance: TBP>>=
procedure :: write_header => process_instance_write_header
procedure :: write => process_instance_write
<<Instances: procedures>>=
subroutine process_instance_write_header (object, unit, testflag)
class(process_instance_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
call write_separator (u, 2)
if (associated (object%process)) then
call object%process%write_meta (u, testflag)
else
write (u, "(1x,A)") "Process instance [undefined process]"
return
end if
write (u, "(3x,A)", advance = "no") "status = "
select case (object%evaluation_status)
case (STAT_INITIAL); write (u, "(A)") "initialized"
case (STAT_ACTIVATED); write (u, "(A)") "activated"
case (STAT_BEAM_MOMENTA); write (u, "(A)") "beam momenta set"
case (STAT_FAILED_KINEMATICS); write (u, "(A)") "failed kinematics"
case (STAT_SEED_KINEMATICS); write (u, "(A)") "seed kinematics"
case (STAT_HARD_KINEMATICS); write (u, "(A)") "hard kinematics"
case (STAT_EFF_KINEMATICS); write (u, "(A)") "effective kinematics"
case (STAT_FAILED_CUTS); write (u, "(A)") "failed cuts"
case (STAT_PASSED_CUTS); write (u, "(A)") "passed cuts"
case (STAT_EVALUATED_TRACE); write (u, "(A)") "evaluated trace"
call write_separator (u)
write (u, "(3x,A,ES19.12)") "sqme = ", object%sqme
case (STAT_EVENT_COMPLETE); write (u, "(A)") "event complete"
call write_separator (u)
write (u, "(3x,A,ES19.12)") "sqme = ", object%sqme
write (u, "(3x,A,ES19.12)") "weight = ", object%weight
if (.not. vanishes (object%excess)) &
write (u, "(3x,A,ES19.12)") "excess = ", object%excess
case default; write (u, "(A)") "undefined"
end select
if (object%i_mci /= 0) then
call write_separator (u)
call object%mci_work(object%i_mci)%write (u, testflag)
end if
call write_separator (u, 2)
end subroutine process_instance_write_header
subroutine process_instance_write (object, unit, testflag)
class(process_instance_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u, i
u = given_output_unit (unit)
call object%write_header (u)
if (object%evaluation_status >= STAT_BEAM_MOMENTA) then
call object%sf_chain%write (u)
call write_separator (u, 2)
if (object%evaluation_status >= STAT_SEED_KINEMATICS) then
if (object%evaluation_status >= STAT_HARD_KINEMATICS) then
call write_separator (u, 2)
write (u, "(1x,A)") "Active terms:"
if (any (object%term%active)) then
do i = 1, size (object%term)
if (object%term(i)%active) then
call write_separator (u)
call object%term(i)%write (u, &
show_eff_state = &
object%evaluation_status >= STAT_EFF_KINEMATICS, &
testflag = testflag)
end if
end do
end if
end if
call write_separator (u, 2)
end if
end if
end subroutine process_instance_write
@ %def process_instance_write_header
@ %def process_instance_write
@ Initialization connects the instance with a process. All initial
information is transferred from the process object. The process
object contains templates for the interaction subobjects (beam and
term), but no evaluators. The initialization routine
creates evaluators for the matrix element trace, other evaluators
are left untouched.
Before we start generating, we double-check if the process library
has been updated after the process was initializated
([[check_library_sanity]]). This may happen if between integration
and event generation the library has been recompiled, so all links
become broken.
The [[instance]] object must have the [[target]] attribute (also in
any caller) since the initialization routine assigns various pointers
to subobject of [[instance]].
<<Instances: process instance: TBP>>=
procedure :: init => process_instance_init
<<Instances: procedures>>=
subroutine process_instance_init (instance, process)
class(process_instance_t), intent(out), target :: instance
type(process_t), intent(inout), target :: process
integer :: i
class(pcm_t), pointer :: pcm
type(process_term_t) :: term
type(var_list_t), pointer :: var_list
integer :: i_born, i_real, i_real_fin
if (debug_on) call msg_debug (D_PROCESS_INTEGRATION, "process_instance_init")
instance%process => process
call instance%process%check_library_sanity ()
call instance%setup_sf_chain (process%get_beam_config_ptr ())
allocate (instance%mci_work (process%get_n_mci ()))
do i = 1, size (instance%mci_work)
call instance%process%init_mci_work (instance%mci_work(i), i)
end do
call instance%process%reset_selected_cores ()
pcm => instance%process%get_pcm_ptr ()
call pcm%allocate_instance (instance%pcm)
call instance%pcm%link_config (pcm)
select type (pcm)
type is (pcm_nlo_t)
!!! The process is kept when the integration is finalized, but not the
!!! process_instance. Thus, we check whether pcm has been initialized
!!! but set up the pcm_instance each time.
i_real_fin = process%get_associated_real_fin (1)
if (.not. pcm%initialized) then
! i_born = pcm%get_i_core_nlo_type (BORN)
i_born = pcm%get_i_core (pcm%i_born)
! i_real = pcm%get_i_core_nlo_type (NLO_REAL, include_sub = .false.)
! i_real = pcm%get_i_core_nlo_type (NLO_REAL)
i_real = pcm%get_i_core (pcm%i_real)
term = process%get_term_ptr (process%get_i_term (i_real))
call pcm%init_qn (process%get_model_ptr ())
if (i_real_fin > 0) call pcm%allocate_ps_matching ()
var_list => process%get_var_list_ptr ()
if (var_list%get_sval (var_str ("$dalitz_plot")) /= var_str ('')) &
call pcm%activate_dalitz_plot (var_list%get_sval (var_str ("$dalitz_plot")))
end if
pcm%initialized = .true.
select type (pcm_instance => instance%pcm)
type is (pcm_instance_nlo_t)
call pcm_instance%init_config (process%component_can_be_integrated (), &
process%get_nlo_type_component (), process%get_sqrts (), i_real_fin, &
process%get_model_ptr ())
end select
end select
allocate (instance%term (process%get_n_terms ()))
do i = 1, process%get_n_terms ()
call instance%term(i)%init_from_process (process, i, instance%pcm, &
instance%sf_chain)
end do
call instance%set_i_mci_to_real_component ()
call instance%find_same_kinematics ()
instance%evaluation_status = STAT_INITIAL
end subroutine process_instance_init
@ %def process_instance_init
@
@ Finalize all subobjects that may contain allocated pointers.
<<Instances: process instance: TBP>>=
procedure :: final => process_instance_final
<<Instances: procedures>>=
subroutine process_instance_final (instance)
class(process_instance_t), intent(inout) :: instance
class(process_instance_hook_t), pointer :: current
integer :: i
instance%process => null ()
if (allocated (instance%mci_work)) then
do i = 1, size (instance%mci_work)
call instance%mci_work(i)%final ()
end do
deallocate (instance%mci_work)
end if
call instance%sf_chain%final ()
if (allocated (instance%term)) then
do i = 1, size (instance%term)
call instance%term(i)%final ()
end do
deallocate (instance%term)
end if
call instance%pcm%final ()
instance%evaluation_status = STAT_UNDEFINED
do while (associated (instance%hook))
current => instance%hook
call current%final ()
instance%hook => current%next
deallocate (current)
end do
instance%hook => null ()
end subroutine process_instance_final
@ %def process_instance_final
@ Revert the process instance to initial state. We do not deallocate
anything, just reset the state index and deactivate all components and
terms.
We do not reset the choice of the MCI set [[i_mci]] unless this is
required explicitly.
<<Instances: process instance: TBP>>=
procedure :: reset => process_instance_reset
<<Instances: procedures>>=
subroutine process_instance_reset (instance, reset_mci)
class(process_instance_t), intent(inout) :: instance
logical, intent(in), optional :: reset_mci
integer :: i
call instance%process%reset_selected_cores ()
do i = 1, size (instance%term)
call instance%term(i)%reset ()
end do
instance%term%checked = .false.
instance%term%passed = .false.
instance%term%k_term%new_seed = .true.
if (present (reset_mci)) then
if (reset_mci) instance%i_mci = 0
end if
instance%selected_channel = 0
instance%evaluation_status = STAT_INITIAL
end subroutine process_instance_reset
@ %def process_instance_reset
@
\subsubsection{Integration and event generation}
The sampler test should just evaluate the squared matrix element [[n_calls]]
times, discarding the results, and return. This can be done before
integration, e.g., for timing estimates.
<<Instances: process instance: TBP>>=
procedure :: sampler_test => process_instance_sampler_test
<<Instances: procedures>>=
subroutine process_instance_sampler_test (instance, i_mci, n_calls)
class(process_instance_t), intent(inout), target :: instance
integer, intent(in) :: i_mci
integer, intent(in) :: n_calls
integer :: i_mci_work
i_mci_work = instance%process%get_i_mci_work (i_mci)
call instance%choose_mci (i_mci_work)
call instance%reset_counter ()
call instance%process%sampler_test (instance, n_calls, i_mci_work)
call instance%process%set_counter_mci_entry (i_mci_work, instance%get_counter ())
end subroutine process_instance_sampler_test
@ %def process_instance_sampler_test
@ Generate a weighted event. We select one of the available MCI
integrators by its index [[i_mci]] and thus generate an event for the
associated (group of) process component(s). The arguments exactly
correspond to the initializer and finalizer above.
The resulting event is stored in the [[process_instance]] object,
which also holds the workspace of the integrator.
Note: The [[process]] object contains the random-number state, which
changes for each event.
Otherwise, all volatile data are inside the [[instance]] object.
<<Instances: process instance: TBP>>=
procedure :: generate_weighted_event => process_instance_generate_weighted_event
<<Instances: procedures>>=
subroutine process_instance_generate_weighted_event (instance, i_mci)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_mci
integer :: i_mci_work
i_mci_work = instance%process%get_i_mci_work (i_mci)
call instance%choose_mci (i_mci_work)
associate (mci_work => instance%mci_work(i_mci_work))
call instance%process%generate_weighted_event &
(i_mci_work, mci_work, instance, &
instance%keep_failed_events ())
end associate
end subroutine process_instance_generate_weighted_event
@ %def process_instance_generate_weighted_event
@
<<Instances: process instance: TBP>>=
procedure :: generate_unweighted_event => process_instance_generate_unweighted_event
<<Instances: procedures>>=
subroutine process_instance_generate_unweighted_event (instance, i_mci)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_mci
integer :: i_mci_work
i_mci_work = instance%process%get_i_mci_work (i_mci)
call instance%choose_mci (i_mci_work)
associate (mci_work => instance%mci_work(i_mci_work))
call instance%process%generate_unweighted_event &
(i_mci_work, mci_work, instance)
end associate
end subroutine process_instance_generate_unweighted_event
@ %def process_instance_generate_unweighted_event
@
This replaces the event generation methods for the situation that the
process instance object has been filled by other means (i.e., reading
and/or recalculating its contents). We just have to fill in missing
MCI data, especially the event weight.
<<Instances: process instance: TBP>>=
procedure :: recover_event => process_instance_recover_event
<<Instances: procedures>>=
subroutine process_instance_recover_event (instance)
class(process_instance_t), intent(inout) :: instance
integer :: i_mci
i_mci = instance%i_mci
call instance%process%set_i_mci_work (i_mci)
associate (mci_instance => instance%mci_work(i_mci)%mci)
call mci_instance%fetch (instance, instance%selected_channel)
end associate
end subroutine process_instance_recover_event
@ %def process_instance_recover_event
@
@ Activate the components and terms that correspond to a currently
selected MCI parameter set.
<<Instances: process instance: TBP>>=
procedure :: activate => process_instance_activate
<<Instances: procedures>>=
subroutine process_instance_activate (instance)
class(process_instance_t), intent(inout) :: instance
integer :: i, j
integer, dimension(:), allocatable :: i_term
associate (mci_work => instance%mci_work(instance%i_mci))
call instance%process%select_components (mci_work%get_active_components ())
end associate
associate (process => instance%process)
do i = 1, instance%process%get_n_components ()
if (instance%process%component_is_selected (i)) then
allocate (i_term (size (process%get_component_i_terms (i))))
i_term = process%get_component_i_terms (i)
do j = 1, size (i_term)
instance%term(i_term(j))%active = .true.
end do
end if
if (allocated (i_term)) deallocate (i_term)
end do
end associate
instance%evaluation_status = STAT_ACTIVATED
end subroutine process_instance_activate
@ %def process_instance_activate
@
<<Instances: process instance: TBP>>=
procedure :: find_same_kinematics => process_instance_find_same_kinematics
<<Instances: procedures>>=
subroutine process_instance_find_same_kinematics (instance)
class(process_instance_t), intent(inout) :: instance
integer :: i_term1, i_term2, k, n_same
do i_term1 = 1, size (instance%term)
if (.not. allocated (instance%term(i_term1)%same_kinematics)) then
n_same = 1 !!! Index group includes the index of its term_instance
do i_term2 = 1, size (instance%term)
if (i_term1 == i_term2) cycle
if (compare_md5s (i_term1, i_term2)) n_same = n_same + 1
end do
allocate (instance%term(i_term1)%same_kinematics (n_same))
associate (same_kinematics1 => instance%term(i_term1)%same_kinematics)
same_kinematics1 = 0
k = 1
do i_term2 = 1, size (instance%term)
if (compare_md5s (i_term1, i_term2)) then
same_kinematics1(k) = i_term2
k = k + 1
end if
end do
do k = 1, size (same_kinematics1)
if (same_kinematics1(k) == i_term1) cycle
i_term2 = same_kinematics1(k)
allocate (instance%term(i_term2)%same_kinematics (n_same))
instance%term(i_term2)%same_kinematics = same_kinematics1
end do
end associate
end if
end do
contains
function compare_md5s (i, j) result (same)
logical :: same
integer, intent(in) :: i, j
character(32) :: md5sum_1, md5sum_2
integer :: mode_1, mode_2
mode_1 = 0; mode_2 = 0
select type (phs => instance%term(i)%k_term%phs%config)
type is (phs_fks_config_t)
md5sum_1 = phs%md5sum_born_config
mode_1 = phs%mode
class default
md5sum_1 = phs%md5sum_phs_config
end select
select type (phs => instance%term(j)%k_term%phs%config)
type is (phs_fks_config_t)
md5sum_2 = phs%md5sum_born_config
mode_2 = phs%mode
class default
md5sum_2 = phs%md5sum_phs_config
end select
same = (md5sum_1 == md5sum_2) .and. (mode_1 == mode_2)
end function compare_md5s
end subroutine process_instance_find_same_kinematics
@ %def process_instance_find_same_kinematics
@
<<Instances: process instance: TBP>>=
procedure :: transfer_same_kinematics => process_instance_transfer_same_kinematics
<<Instances: procedures>>=
subroutine process_instance_transfer_same_kinematics (instance, i_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_term
integer :: i, i_term_same
associate (same_kinematics => instance%term(i_term)%same_kinematics)
do i = 1, size (same_kinematics)
i_term_same = same_kinematics(i)
if (i_term_same /= i_term) then
instance%term(i_term_same)%p_seed = instance%term(i_term)%p_seed
associate (phs => instance%term(i_term_same)%k_term%phs)
call phs%set_lorentz_transformation &
(instance%term(i_term)%k_term%phs%get_lorentz_transformation ())
select type (phs)
type is (phs_fks_t)
call phs%set_momenta (instance%term(i_term_same)%p_seed)
call phs%set_reference_frames (.false.)
end select
end associate
end if
instance%term(i_term_same)%k_term%new_seed = .false.
end do
end associate
end subroutine process_instance_transfer_same_kinematics
@ %def process_instance_transfer_same_kinematics
@
<<Instances: process instance: TBP>>=
procedure :: redo_sf_chains => process_instance_redo_sf_chains
<<Instances: procedures>>=
subroutine process_instance_redo_sf_chains (instance, i_term, phs_channel)
class(process_instance_t), intent(inout) :: instance
integer, intent(in), dimension(:) :: i_term
integer, intent(in) :: phs_channel
integer :: i
do i = 1, size (i_term)
call instance%term(i_term(i))%redo_sf_chain &
(instance%mci_work(instance%i_mci), phs_channel)
end do
end subroutine process_instance_redo_sf_chains
@ %def process_instance_redo_sf_chains
@ Integrate the process, using a previously initialized process
instance. We select one of the available MCI integrators by its index
[[i_mci]] and thus integrate over (structure functions and) phase
space for the associated (group of) process component(s).
<<Instances: process instance: TBP>>=
procedure :: integrate => process_instance_integrate
<<Instances: procedures>>=
subroutine process_instance_integrate (instance, i_mci, n_it, n_calls, &
adapt_grids, adapt_weights, final, pacify)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_mci
integer, intent(in) :: n_it
integer, intent(in) :: n_calls
logical, intent(in), optional :: adapt_grids
logical, intent(in), optional :: adapt_weights
logical, intent(in), optional :: final, pacify
integer :: nlo_type, i_mci_work
nlo_type = instance%process%get_component_nlo_type (i_mci)
i_mci_work = instance%process%get_i_mci_work (i_mci)
call instance%choose_mci (i_mci_work)
call instance%reset_counter ()
associate (mci_work => instance%mci_work(i_mci_work), &
process => instance%process)
call process%integrate (i_mci_work, mci_work, &
instance, n_it, n_calls, adapt_grids, adapt_weights, &
final, pacify, nlo_type = nlo_type)
call process%set_counter_mci_entry (i_mci_work, instance%get_counter ())
end associate
end subroutine process_instance_integrate
@ %def process_instance_integrate
@ Subroutine of the initialization above: initialize the beam and
structure-function chain template. We establish pointers to the
configuration data, so [[beam_config]] must have a [[target]]
attribute.
The resulting chain is not used directly for calculation. It will
acquire instances which are stored in the process-component instance
objects.
<<Instances: process instance: TBP>>=
procedure :: setup_sf_chain => process_instance_setup_sf_chain
<<Instances: procedures>>=
subroutine process_instance_setup_sf_chain (instance, config)
class(process_instance_t), intent(inout) :: instance
type(process_beam_config_t), intent(in), target :: config
integer :: n_strfun
n_strfun = config%n_strfun
if (n_strfun /= 0) then
call instance%sf_chain%init (config%data, config%sf)
else
call instance%sf_chain%init (config%data)
end if
if (config%sf_trace) then
call instance%sf_chain%setup_tracing (config%sf_trace_file)
end if
end subroutine process_instance_setup_sf_chain
@ %def process_instance_setup_sf_chain
@ This initialization routine should be called only for process
instances which we intend as a source for physical events. It
initializes the evaluators in the parton states of the terms. They
describe the (semi-)exclusive transition matrix and the distribution
of color flow for the partonic process, convoluted with the beam and
structure-function chain.
If the model is not provided explicitly, we may use the model instance that
belongs to the process. However, an explicit model allows us to override
particle settings.
<<Instances: process instance: TBP>>=
procedure :: setup_event_data => process_instance_setup_event_data
<<Instances: procedures>>=
subroutine process_instance_setup_event_data (instance, model, i_core)
class(process_instance_t), intent(inout), target :: instance
class(model_data_t), intent(in), optional, target :: model
integer, intent(in), optional :: i_core
class(model_data_t), pointer :: current_model
integer :: i
class(prc_core_t), pointer :: core => null ()
if (present (model)) then
current_model => model
else
current_model => instance%process%get_model_ptr ()
end if
do i = 1, size (instance%term)
associate (term => instance%term(i))
if (associated (term%config)) then
core => instance%process%get_core_term (i)
call term%setup_event_data (core, current_model)
end if
end associate
end do
core => null ()
end subroutine process_instance_setup_event_data
@ %def process_instance_setup_event_data
@ Choose a MC parameter set and the corresponding integrator.
The choice persists beyond calls of the [[reset]] method above. This method
is automatically called here.
<<Instances: process instance: TBP>>=
procedure :: choose_mci => process_instance_choose_mci
<<Instances: procedures>>=
subroutine process_instance_choose_mci (instance, i_mci)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_mci
instance%i_mci = i_mci
call instance%reset ()
end subroutine process_instance_choose_mci
@ %def process_instance_choose_mci
@ Explicitly set a MC parameter set. Works only if we are in initial
state. We assume that the length of the parameter set is correct.
After setting the parameters, activate the components and terms that
correspond to the chosen MC parameter set.
The [[warmup_flag]] is used when a dummy phase-space point is computed
for the warmup of e.g. OpenLoops helicities. The setting of the
the [[evaluation_status]] has to be avoided then.
<<Instances: process instance: TBP>>=
procedure :: set_mcpar => process_instance_set_mcpar
<<Instances: procedures>>=
subroutine process_instance_set_mcpar (instance, x, warmup_flag)
class(process_instance_t), intent(inout) :: instance
real(default), dimension(:), intent(in) :: x
logical, intent(in), optional :: warmup_flag
logical :: activate
activate = .true.; if (present (warmup_flag)) activate = .not. warmup_flag
if (instance%evaluation_status == STAT_INITIAL) then
associate (mci_work => instance%mci_work(instance%i_mci))
call mci_work%set (x)
end associate
if (activate) call instance%activate ()
end if
end subroutine process_instance_set_mcpar
@ %def process_instance_set_mcpar
@ Receive the beam momentum/momenta from a source interaction. This
applies to a cascade decay process instance, where the `beam' momentum
varies event by event.
The master beam momentum array is contained in the main structure
function chain subobject [[sf_chain]]. The sf-chain instance that
reside in the components will take their beam momenta from there.
The procedure transforms the instance status into
[[STAT_BEAM_MOMENTA]]. For process instance with fixed beam, this
intermediate status is skipped.
<<Instances: process instance: TBP>>=
procedure :: receive_beam_momenta => process_instance_receive_beam_momenta
<<Instances: procedures>>=
subroutine process_instance_receive_beam_momenta (instance)
class(process_instance_t), intent(inout) :: instance
if (instance%evaluation_status >= STAT_INITIAL) then
call instance%sf_chain%receive_beam_momenta ()
instance%evaluation_status = STAT_BEAM_MOMENTA
end if
end subroutine process_instance_receive_beam_momenta
@ %def process_instance_receive_beam_momenta
@ Set the beam momentum/momenta explicitly. Otherwise, analogous to
the previous procedure.
<<Instances: process instance: TBP>>=
procedure :: set_beam_momenta => process_instance_set_beam_momenta
<<Instances: procedures>>=
subroutine process_instance_set_beam_momenta (instance, p)
class(process_instance_t), intent(inout) :: instance
type(vector4_t), dimension(:), intent(in) :: p
if (instance%evaluation_status >= STAT_INITIAL) then
call instance%sf_chain%set_beam_momenta (p)
instance%evaluation_status = STAT_BEAM_MOMENTA
end if
end subroutine process_instance_set_beam_momenta
@ %def process_instance_set_beam_momenta
@ Recover the initial beam momenta (those in the [[sf_chain]]
component), given a valid (recovered) [[sf_chain_instance]] in one of
the active components. We need to do this only if the lab frame is
not the c.m.\ frame, otherwise those beams would be fixed anyway.
<<Instances: process instance: TBP>>=
procedure :: recover_beam_momenta => process_instance_recover_beam_momenta
<<Instances: procedures>>=
subroutine process_instance_recover_beam_momenta (instance, i_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_term
if (.not. instance%process%lab_is_cm_frame ()) then
if (instance%evaluation_status >= STAT_EFF_KINEMATICS) then
call instance%term(i_term)%return_beam_momenta ()
end if
end if
end subroutine process_instance_recover_beam_momenta
@ %def process_instance_recover_beam_momenta
@ Explicitly choose MC integration channel. We assume here that the channel
count is identical for all active components.
<<Instances: process instance: TBP>>=
procedure :: select_channel => process_instance_select_channel
<<Instances: procedures>>=
subroutine process_instance_select_channel (instance, channel)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: channel
instance%selected_channel = channel
end subroutine process_instance_select_channel
@ %def process_instance_select_channel
@ First step of process evaluation: set up seed kinematics. That is, for each
active process component, compute a momentum array from the MC input
parameters.
If [[skip_term]] is set, we skip the component that accesses this
term. We can assume that the associated data have already been
recovered, and we are just computing the rest.
<<Instances: process instance: TBP>>=
procedure :: compute_seed_kinematics => &
process_instance_compute_seed_kinematics
<<Instances: procedures>>=
subroutine process_instance_compute_seed_kinematics (instance, skip_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in), optional :: skip_term
integer :: channel, skip_component, i, j
logical :: success
integer, dimension(:), allocatable :: i_term
channel = instance%selected_channel
if (channel == 0) then
call msg_bug ("Compute seed kinematics: undefined integration channel")
end if
if (present (skip_term)) then
skip_component = instance%term(skip_term)%config%i_component
else
skip_component = 0
end if
if (instance%evaluation_status >= STAT_ACTIVATED) then
success = .true.
do i = 1, instance%process%get_n_components ()
if (i == skip_component) cycle
if (instance%process%component_is_selected (i)) then
allocate (i_term (size (instance%process%get_component_i_terms (i))))
i_term = instance%process%get_component_i_terms (i)
do j = 1, size (i_term)
if (instance%term(i_term(j))%k_term%new_seed) then
call instance%term(i_term(j))%compute_seed_kinematics &
(instance%mci_work(instance%i_mci), channel, success)
call instance%transfer_same_kinematics (i_term(j))
end if
if (.not. success) exit
call instance%term(i_term(j))%evaluate_projections ()
call instance%term(i_term(j))%evaluate_radiation_kinematics &
(instance%mci_work(instance%i_mci)%get_x_process ())
call instance%term(i_term(j))%generate_fsr_in ()
call instance%term(i_term(j))%compute_xi_ref_momenta ()
end do
end if
if (allocated (i_term)) deallocate (i_term)
end do
if (success) then
instance%evaluation_status = STAT_SEED_KINEMATICS
else
instance%evaluation_status = STAT_FAILED_KINEMATICS
end if
end if
associate (mci_work => instance%mci_work(instance%i_mci))
select type (pcm => instance%pcm)
class is (pcm_instance_nlo_t)
call pcm%set_x_rad (mci_work%get_x_process ())
end select
end associate
end subroutine process_instance_compute_seed_kinematics
@ %def process_instance_compute_seed_kinematics
@
<<Instances: process instance: TBP>>=
procedure :: get_x_process => process_instance_get_x_process
<<Instances: procedures>>=
pure function process_instance_get_x_process (instance) result (x)
real(default), dimension(:), allocatable :: x
class(process_instance_t), intent(in) :: instance
allocate (x(size (instance%mci_work(instance%i_mci)%get_x_process ())))
x = instance%mci_work(instance%i_mci)%get_x_process ()
end function process_instance_get_x_process
@ %def process_instance_get_x_process
@
<<Instances: process instance: TBP>>=
procedure :: get_active_component_type => process_instance_get_active_component_type
<<Instances: procedures>>=
pure function process_instance_get_active_component_type (instance) &
result (nlo_type)
integer :: nlo_type
class(process_instance_t), intent(in) :: instance
nlo_type = instance%process%get_component_nlo_type (instance%i_mci)
end function process_instance_get_active_component_type
@ %def process_instance_get_active_component_type
@ Inverse: recover missing parts of the kinematics from the momentum
configuration, which we know for a single term and component. Given
a channel, reconstruct the MC parameter set.
<<Instances: process instance: TBP>>=
procedure :: recover_mcpar => process_instance_recover_mcpar
<<Instances: procedures>>=
subroutine process_instance_recover_mcpar (instance, i_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_term
integer :: channel
if (instance%evaluation_status >= STAT_EFF_KINEMATICS) then
channel = instance%selected_channel
if (channel == 0) then
call msg_bug ("Recover MC parameters: undefined integration channel")
end if
call instance%term(i_term)%recover_mcpar &
(instance%mci_work(instance%i_mci), channel)
end if
end subroutine process_instance_recover_mcpar
@ %def process_instance_recover_mcpar
@ This is part of [[recover_mcpar]], extracted for the case when there is
no phase space and parameters to recover, but we still need the structure
function kinematics for evaluation.
<<Instances: process instance: TBP>>=
procedure :: recover_sfchain => process_instance_recover_sfchain
<<Instances: procedures>>=
subroutine process_instance_recover_sfchain (instance, i_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_term
integer :: channel
if (instance%evaluation_status >= STAT_EFF_KINEMATICS) then
channel = instance%selected_channel
if (channel == 0) then
call msg_bug ("Recover sfchain: undefined integration channel")
end if
call instance%term(i_term)%recover_sfchain (channel)
end if
end subroutine process_instance_recover_sfchain
@ %def process_instance_recover_sfchain
@ Second step of process evaluation: compute all momenta, for all active
components, from the seed kinematics.
<<Instances: process instance: TBP>>=
procedure :: compute_hard_kinematics => &
process_instance_compute_hard_kinematics
<<Instances: procedures>>=
subroutine process_instance_compute_hard_kinematics (instance, skip_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in), optional :: skip_term
integer :: i
logical :: success
success = .true.
if (instance%evaluation_status >= STAT_SEED_KINEMATICS) then
do i = 1, size (instance%term)
if (instance%term(i)%active) then
call instance%term(i)%compute_hard_kinematics (skip_term, success)
if (.not. success) exit
!!! Ren scale is zero when this is commented out! Understand!
if (instance%term(i)%nlo_type == NLO_REAL) &
call instance%term(i)%redo_sf_chain (instance%mci_work(instance%i_mci), &
instance%selected_channel)
end if
end do
if (success) then
instance%evaluation_status = STAT_HARD_KINEMATICS
else
instance%evaluation_status = STAT_FAILED_KINEMATICS
end if
end if
end subroutine process_instance_compute_hard_kinematics
@ %def process_instance_setup_compute_hard_kinematics
@ Inverse: recover seed kinematics. We know the beam momentum
configuration and the outgoing momenta of the effective interaction,
for one specific term.
<<Instances: process instance: TBP>>=
procedure :: recover_seed_kinematics => &
process_instance_recover_seed_kinematics
<<Instances: procedures>>=
subroutine process_instance_recover_seed_kinematics (instance, i_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_term
if (instance%evaluation_status >= STAT_EFF_KINEMATICS) &
call instance%term(i_term)%recover_seed_kinematics ()
end subroutine process_instance_recover_seed_kinematics
@ %def process_instance_recover_seed_kinematics
@ Third step of process evaluation: compute the effective momentum
configurations, for all active terms, from the hard kinematics.
<<Instances: process instance: TBP>>=
procedure :: compute_eff_kinematics => &
process_instance_compute_eff_kinematics
<<Instances: procedures>>=
subroutine process_instance_compute_eff_kinematics (instance, skip_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in), optional :: skip_term
integer :: i
if (instance%evaluation_status >= STAT_HARD_KINEMATICS) then
do i = 1, size (instance%term)
if (present (skip_term)) then
if (i == skip_term) cycle
end if
if (instance%term(i)%active) then
call instance%term(i)%compute_eff_kinematics ()
end if
end do
instance%evaluation_status = STAT_EFF_KINEMATICS
end if
end subroutine process_instance_compute_eff_kinematics
@ %def process_instance_setup_compute_eff_kinematics
@ Inverse: recover the hard kinematics from effective kinematics for
one term, then compute effective kinematics for the other terms.
<<Instances: process instance: TBP>>=
procedure :: recover_hard_kinematics => &
process_instance_recover_hard_kinematics
<<Instances: procedures>>=
subroutine process_instance_recover_hard_kinematics (instance, i_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_term
integer :: i
if (instance%evaluation_status >= STAT_EFF_KINEMATICS) then
call instance%term(i_term)%recover_hard_kinematics ()
do i = 1, size (instance%term)
if (i /= i_term) then
if (instance%term(i)%active) then
call instance%term(i)%compute_eff_kinematics ()
end if
end if
end do
instance%evaluation_status = STAT_EFF_KINEMATICS
end if
end subroutine process_instance_recover_hard_kinematics
@ %def recover_hard_kinematics
@ Fourth step of process evaluation: check cuts for all terms. Where
sucessful, compute any scales and weights. Otherwise, deactive the term.
If any of the terms has passed, set the state to [[STAT_PASSED_CUTS]].
The argument [[scale_forced]], if present, will override the scale calculation
in the term expressions.
<<Instances: process instance: TBP>>=
procedure :: evaluate_expressions => &
process_instance_evaluate_expressions
<<Instances: procedures>>=
subroutine process_instance_evaluate_expressions (instance, scale_forced)
class(process_instance_t), intent(inout) :: instance
real(default), intent(in), allocatable, optional :: scale_forced
integer :: i
logical :: passed_real
if (instance%evaluation_status >= STAT_EFF_KINEMATICS) then
do i = 1, size (instance%term)
if (instance%term(i)%active) then
call instance%term(i)%evaluate_expressions (scale_forced)
end if
end do
call evaluate_real_scales_and_cuts ()
if (.not. passed_real) then
instance%evaluation_status = STAT_FAILED_CUTS
else
if (any (instance%term%passed)) then
instance%evaluation_status = STAT_PASSED_CUTS
else
instance%evaluation_status = STAT_FAILED_CUTS
end if
end if
end if
contains
subroutine evaluate_real_scales_and_cuts ()
integer :: i
passed_real = .true.
select type (config => instance%pcm%config)
type is (pcm_nlo_t)
do i = 1, size (instance%term)
if (instance%term(i)%active .and. instance%term(i)%nlo_type == NLO_REAL) then
if (config%settings%cut_all_sqmes) &
passed_real = passed_real .and. instance%term(i)%passed
if (config%settings%use_born_scale) &
call replace_scales (instance%term(i))
end if
end do
end select
end subroutine evaluate_real_scales_and_cuts
subroutine replace_scales (this_term)
type(term_instance_t), intent(inout) :: this_term
integer :: i_sub
i_sub = this_term%config%i_sub
if (this_term%config%i_term_global /= i_sub .and. i_sub > 0) then
this_term%ren_scale = instance%term(i_sub)%ren_scale
this_term%fac_scale = instance%term(i_sub)%fac_scale
end if
end subroutine replace_scales
end subroutine process_instance_evaluate_expressions
@ %def process_instance_evaluate_expressions
@ Fifth step of process evaluation: fill the parameters for the non-selected
,channels, that have not been used for seeding. We should do this after
evaluating cuts, since we may save some expensive calculations if the phase
space point fails the cuts.
If [[skip_term]] is set, we skip the component that accesses this
term. We can assume that the associated data have already been
recovered, and we are just computing the rest.
<<Instances: process instance: TBP>>=
procedure :: compute_other_channels => &
process_instance_compute_other_channels
<<Instances: procedures>>=
subroutine process_instance_compute_other_channels (instance, skip_term)
class(process_instance_t), intent(inout) :: instance
integer, intent(in), optional :: skip_term
integer :: channel, skip_component, i, j
integer, dimension(:), allocatable :: i_term
channel = instance%selected_channel
if (channel == 0) then
call msg_bug ("Compute other channels: undefined integration channel")
end if
if (present (skip_term)) then
skip_component = instance%term(skip_term)%config%i_component
else
skip_component = 0
end if
if (instance%evaluation_status >= STAT_PASSED_CUTS) then
do i = 1, instance%process%get_n_components ()
if (i == skip_component) cycle
if (instance%process%component_is_selected (i)) then
allocate (i_term (size (instance%process%get_component_i_terms (i))))
i_term = instance%process%get_component_i_terms (i)
do j = 1, size (i_term)
call instance%term(i_term(j))%compute_other_channels &
(instance%mci_work(instance%i_mci), channel)
end do
end if
if (allocated (i_term)) deallocate (i_term)
end do
end if
end subroutine process_instance_compute_other_channels
@ %def process_instance_compute_other_channels
@ If not done otherwise, we an flag the kinematics as new for the core state,
such that the routine below will actually compute the matrix element and not
just look it up.
<<Instances: process instance: TBP>>=
procedure :: reset_core_kinematics => process_instance_reset_core_kinematics
<<Instances: procedures>>=
subroutine process_instance_reset_core_kinematics (instance)
class(process_instance_t), intent(inout) :: instance
integer :: i
if (instance%evaluation_status >= STAT_PASSED_CUTS) then
do i = 1, size (instance%term)
associate (term => instance%term(i))
if (term%active .and. term%passed) then
if (allocated (term%core_state)) &
call term%core_state%reset_new_kinematics ()
end if
end associate
end do
end if
end subroutine process_instance_reset_core_kinematics
@ %def process_instance_reset_core_kinematics
@ Sixth step of process evaluation: evaluate the matrix elements, and compute
the trace (summed over quantum numbers) for all terms. Finally, sum up the
terms, iterating over all active process components.
<<Instances: process instance: TBP>>=
procedure :: evaluate_trace => process_instance_evaluate_trace
<<Instances: procedures>>=
subroutine process_instance_evaluate_trace (instance)
class(process_instance_t), intent(inout) :: instance
class(prc_core_t), pointer :: core => null ()
integer :: i, i_real_fin, i_core
real(default) :: alpha_s, alpha_qed
class(prc_core_t), pointer :: core_sub => null ()
class(model_data_t), pointer :: model => null ()
+ logical :: has_pdfs
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "process_instance_evaluate_trace")
+ has_pdfs = instance%process%pcm_contains_pdfs ()
instance%sqme = zero
call instance%reset_matrix_elements ()
if (instance%evaluation_status >= STAT_PASSED_CUTS) then
do i = 1, size (instance%term)
associate (term => instance%term(i))
if (term%active .and. term%passed) then
core => instance%process%get_core_term (i)
select type (pcm => instance%process%get_pcm_ptr ())
class is (pcm_nlo_t)
i_core = pcm%get_i_core (pcm%i_sub)
core_sub => instance%process%get_core_ptr (i_core)
end select
-! if (instance%pcm%config%is_nlo ()) &
-! core_sub => instance%process%get_subtraction_core ()
call term%evaluate_interaction (core)
call term%evaluate_trace ()
i_real_fin = instance%process%get_associated_real_fin (1)
if (instance%process%uses_real_partition ()) &
call term%apply_real_partition (instance%process)
if (term%config%i_component /= i_real_fin) then
if ((term%nlo_type == NLO_REAL .and. term%k_term%emitter < 0) &
.or. term%nlo_type == NLO_MISMATCH &
.or. term%nlo_type == NLO_DGLAP) &
call term%set_born_sqmes (core)
+ if (term%is_subtraction () .or. &
+ term%nlo_type == NLO_DGLAP) &
+ call term%set_sf_factors (has_pdfs)
if (term%nlo_type > BORN) then
if (.not. (term%nlo_type == NLO_REAL .and. term%k_term%emitter >= 0)) then
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
if (char (config%settings%nlo_correction_type) == "QCD" .or. &
char (config%settings%nlo_correction_type) == "Full") &
call term%evaluate_color_correlations (core_sub)
if (char (config%settings%nlo_correction_type) == "QED" .or. &
char (config%settings%nlo_correction_type) == "Full") &
call term%evaluate_charge_correlations (core_sub)
end select
end if
if (term%is_subtraction ()) then
call term%evaluate_spin_correlations (core_sub)
end if
- if ((term%is_subtraction () .or. term%nlo_type == NLO_DGLAP) &
- .and. term%pcm_instance%config%has_pdfs) &
- call term%compute_sqme_coll_isr ()
end if
alpha_s = core%get_alpha_s (term%core_state)
!!!! TODO (wk 2019-02-07): this method for resetting alpha_em is not used (yet),
!!!! and it slows down the program significantly by using string handling.
!!! Should be removed or replaced by an efficient method.
alpha_qed = 0
!!! if (associated (instance%process%get_model_ptr ())) then
!!! model => instance%process%get_model_ptr ()
!!! if (associated (model%get_par_data_ptr (var_str ('alpha_em_i')))) &
!!! alpha_qed = one / model%get_real (var_str ('alpha_em_i'))
!!! model => null ()
!!! end if
select case (term%nlo_type)
case (NLO_REAL)
call term%apply_fks (alpha_s, alpha_qed)
case (NLO_VIRTUAL)
call term%evaluate_sqme_virt (alpha_s, alpha_qed)
case (NLO_MISMATCH)
call term%evaluate_sqme_mismatch (alpha_s)
case (NLO_DGLAP)
call term%evaluate_sqme_dglap (alpha_s)
end select
end if
end if
core_sub => null ()
instance%sqme = instance%sqme + real (sum (&
term%connected%trace%get_matrix_element () * &
term%weight))
end associate
end do
core => null ()
if (instance%pcm%is_valid ()) then
instance%evaluation_status = STAT_EVALUATED_TRACE
else
instance%evaluation_status = STAT_FAILED_KINEMATICS
end if
else
!!! Failed kinematics or failed cuts: set sqme to zero
instance%sqme = zero
end if
end subroutine process_instance_evaluate_trace
@ %def process_instance_evaluate_trace
<<Instances: term instance: TBP>>=
procedure :: set_born_sqmes => term_instance_set_born_sqmes
<<Instances: procedures>>=
subroutine term_instance_set_born_sqmes (term, core)
class(term_instance_t), intent(inout) :: term
class(prc_core_t), intent(in) :: core
integer :: i_flv, ii_flv
real(default) :: sqme
select type (pcm_instance => term%pcm_instance)
type is (pcm_instance_nlo_t)
do i_flv = 1, term%connected_qn_index%get_n_flv ()
ii_flv = term%connected_qn_index%get_index (i_flv, i_sub = 0)
sqme = real (term%connected%trace%get_matrix_element (ii_flv))
select case (term%nlo_type)
case (NLO_REAL)
pcm_instance%real_sub%sqme_born(i_flv) = sqme
case (NLO_MISMATCH)
pcm_instance%soft_mismatch%sqme_born(i_flv) = sqme
case (NLO_DGLAP)
pcm_instance%dglap_remnant%sqme_born(i_flv) = sqme
end select
end do
end select
end subroutine term_instance_set_born_sqmes
+
+@ %def term_instance_set_born_sqmes
+@ Calculates and then saves the ratio of the value of the (rescaled) real
+structure function chain of each ISR alpha region over the value of the
+corresponding underlying born flavor structure.
+In the case of emitter 0 we also need the rescaled ratio for emitter 1 and 2
+in that region for the (soft-)collinear limits.
+Altough this procedure is implying functionality for general structure functions,
+it should be reviewed for anything else besides PDFs, as there might be complications
+in the details. The general idea of getting the ratio in this way should hold up in
+these cases as well, however.
+<<Instances: term instance: TBP>>=
+ procedure :: set_sf_factors => term_instance_set_sf_factors
+<<Instances: procedures>>=
+ subroutine term_instance_set_sf_factors (term, has_pdfs)
+ class(term_instance_t), intent(inout) :: term
+ logical, intent(in) :: has_pdfs
+ type(interaction_t), pointer :: sf_chain_int
+ real(default) :: factor_born, factor_real
+ integer :: n_in, alr, em
+ integer :: i_born, i_real
+ select type (pcm_instance => term%pcm_instance)
+ type is (pcm_instance_nlo_t)
+ if (.not. has_pdfs) then
+ pcm_instance%real_sub%sf_factors = one
+ return
+ end if
+ select type (config => pcm_instance%config)
+ type is (pcm_nlo_t)
+ sf_chain_int => term%k_term%sf_chain%get_out_int_ptr ()
+ associate (reg_data => config%region_data)
+ n_in = reg_data%get_n_in ()
+ do alr = 1, reg_data%n_regions
+ em = reg_data%regions(alr)%emitter
+ if (em <= n_in) then
+ i_born = reg_data%regions(alr)%uborn_index
+ i_real = reg_data%regions(alr)%real_index
+ factor_born = sf_chain_int%get_matrix_element &
+ (term%sf_qn_index%get_sf_index_born (i_born, i_sub = 0))
+ factor_real = sf_chain_int%get_matrix_element &
+ (term%sf_qn_index%get_sf_index_real (i_real, i_sub = em))
+ call set_factor (pcm_instance, alr, em, factor_born, factor_real)
+ if (em == 0) then
+ do em = 1, 2
+ factor_real = sf_chain_int%get_matrix_element &
+ (term%sf_qn_index%get_sf_index_real (i_real, i_sub = em))
+ call set_factor (pcm_instance, alr, em, factor_born, factor_real)
+ end do
+ end if
+ end if
+ end do
+ end associate
+ end select
+ end select
+ contains
+ subroutine set_factor (pcm_instance, alr, em, factor_born, factor_real)
+ type(pcm_instance_nlo_t), intent(in), pointer :: pcm_instance
+ integer, intent(in) :: alr, em
+ real(default), intent(in) :: factor_born, factor_real
+ real(default) :: factor
+ if (any (vanishes ([factor_real, factor_born], tiny(1._default), tiny(1._default)))) then
+ factor = zero
+ else
+ factor = factor_real / factor_born
+ end if
+ select case (term%nlo_type)
+ case (NLO_REAL)
+ pcm_instance%real_sub%sf_factors(alr, em) = factor
+ case (NLO_DGLAP)
+ pcm_instance%dglap_remnant%sf_factors(alr, em) = factor
+ end select
+ end subroutine
+ end subroutine term_instance_set_sf_factors
+
+@ %def term_instance_set_sf_factors
@
<<Instances: process instance: TBP>>=
procedure :: apply_real_partition => process_instance_apply_real_partition
<<Instances: procedures>>=
subroutine process_instance_apply_real_partition (instance)
class(process_instance_t), intent(inout) :: instance
integer :: i_component, i_term
integer, dimension(:), allocatable :: i_terms
associate (process => instance%process)
i_component = process%get_first_real_component ()
if (process%component_is_selected (i_component) .and. &
process%get_component_nlo_type (i_component) == NLO_REAL) then
allocate (i_terms (size (process%get_component_i_terms (i_component))))
i_terms = process%get_component_i_terms (i_component)
do i_term = 1, size (i_terms)
call instance%term(i_terms(i_term))%apply_real_partition (process)
end do
end if
if (allocated (i_terms)) deallocate (i_terms)
end associate
end subroutine process_instance_apply_real_partition
@ %def process_instance_apply_real_partition
@
<<Instances: process instance: TBP>>=
procedure :: set_i_mci_to_real_component => process_instance_set_i_mci_to_real_component
<<Instances: procedures>>=
subroutine process_instance_set_i_mci_to_real_component (instance)
class(process_instance_t), intent(inout) :: instance
integer :: i_mci, i_component
type(process_component_t), pointer :: component => null ()
select type (pcm_instance => instance%pcm)
type is (pcm_instance_nlo_t)
if (allocated (pcm_instance%i_mci_to_real_component)) then
call msg_warning ("i_mci_to_real_component already allocated - replace it")
deallocate (pcm_instance%i_mci_to_real_component)
end if
allocate (pcm_instance%i_mci_to_real_component (size (instance%mci_work)))
do i_mci = 1, size (instance%mci_work)
do i_component = 1, instance%process%get_n_components ()
component => instance%process%get_component_ptr (i_component)
if (component%i_mci /= i_mci) cycle
select case (component%component_type)
case (COMP_MASTER, COMP_REAL)
pcm_instance%i_mci_to_real_component (i_mci) = &
component%config%get_associated_real ()
case (COMP_REAL_FIN)
pcm_instance%i_mci_to_real_component (i_mci) = &
component%config%get_associated_real_fin ()
case (COMP_REAL_SING)
pcm_instance%i_mci_to_real_component (i_mci) = &
component%config%get_associated_real_sing ()
end select
end do
end do
component => null ()
end select
end subroutine process_instance_set_i_mci_to_real_component
@ %def process_instance_set_i_mci_to_real_component
@ Final step of process evaluation: evaluate the matrix elements, and compute
the trace (summed over quantum numbers) for all terms. Finally, sum up the
terms, iterating over all active process components.
If [[weight]] is provided, we already know the kinematical event
weight (the MCI weight which depends on the kinematics sampling
algorithm, but not on the matrix element), so we do not need to take
it from the MCI record.
<<Instances: process instance: TBP>>=
procedure :: evaluate_event_data => process_instance_evaluate_event_data
<<Instances: procedures>>=
subroutine process_instance_evaluate_event_data (instance, weight)
class(process_instance_t), intent(inout) :: instance
real(default), intent(in), optional :: weight
integer :: i
if (instance%evaluation_status >= STAT_EVALUATED_TRACE) then
do i = 1, size (instance%term)
associate (term => instance%term(i))
if (term%active .and. term%passed) then
call term%evaluate_event_data ()
end if
end associate
end do
if (present (weight)) then
instance%weight = weight
else
instance%weight = &
instance%mci_work(instance%i_mci)%mci%get_event_weight ()
instance%excess = &
instance%mci_work(instance%i_mci)%mci%get_event_excess ()
end if
instance%n_dropped = &
instance%mci_work(instance%i_mci)%mci%get_n_event_dropped ()
instance%evaluation_status = STAT_EVENT_COMPLETE
else
!!! failed kinematics etc.: set weight to zero
instance%weight = zero
!!! Maybe we want to keep the event nevertheless
if (instance%keep_failed_events ()) then
!!! Force factorization scale, otherwise writing to event output fails
do i = 1, size (instance%term)
instance%term(i)%fac_scale = zero
end do
instance%evaluation_status = STAT_EVENT_COMPLETE
end if
end if
end subroutine process_instance_evaluate_event_data
@ %def process_instance_evaluate_event_data
@ Computes the real-emission matrix element for externally supplied momenta. Also,
e.g. for Powheg, there is the possibility to supply an external $\alpha_s$
<<Instances: process instance: TBP>>=
procedure :: compute_sqme_rad => process_instance_compute_sqme_rad
<<Instances: procedures>>=
subroutine process_instance_compute_sqme_rad &
(instance, i_term, i_phs, is_subtraction, alpha_s_external)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_term, i_phs
logical, intent(in) :: is_subtraction
real(default), intent(in), optional :: alpha_s_external
class(prc_core_t), pointer :: core
integer :: i_real_fin
if (debug_on) call msg_debug2 (D_PROCESS_INTEGRATION, "process_instance_compute_sqme_rad")
select type (pcm => instance%pcm)
type is (pcm_instance_nlo_t)
associate (term => instance%term(i_term))
core => instance%process%get_core_term (i_term)
if (is_subtraction) then
call pcm%set_subtraction_event ()
else
call pcm%set_radiation_event ()
end if
call term%int_hard%set_momenta (pcm%get_momenta &
(i_phs = i_phs, born_phsp = is_subtraction))
if (allocated (term%core_state)) &
call term%core_state%reset_new_kinematics ()
if (present (alpha_s_external)) &
call term%set_alpha_qcd_forced (alpha_s_external)
call term%compute_eff_kinematics ()
call term%evaluate_expressions ()
call term%evaluate_interaction (core)
call term%evaluate_trace ()
pcm%real_sub%sqme_born (1) = &
real (term%connected%trace%get_matrix_element (1))
if (term%is_subtraction ()) then
select type (config => term%pcm_instance%config)
type is (pcm_nlo_t)
if (char (config%settings%nlo_correction_type) == "QCD" .or. &
char (config%settings%nlo_correction_type) == "Full") &
call term%evaluate_color_correlations (core)
if (char (config%settings%nlo_correction_type) == "QED" .or. &
char (config%settings%nlo_correction_type) == "Full") &
call term%evaluate_charge_correlations (core)
end select
call term%evaluate_spin_correlations (core)
- if (term%pcm_instance%config%has_pdfs) &
- call term%compute_sqme_coll_isr ()
- else if (term%nlo_type == NLO_DGLAP) then
- call term%compute_sqme_coll_isr ()
end if
i_real_fin = instance%process%get_associated_real_fin (1)
if (term%config%i_component /= i_real_fin) &
call term%apply_fks (core%get_alpha_s (term%core_state), 0._default)
if (instance%process%uses_real_partition ()) &
call instance%apply_real_partition ()
end associate
end select
core => null ()
end subroutine process_instance_compute_sqme_rad
@ %def process_instance_compute_sqme_rad
@ For unweighted event generation, we should reset the reported event
weight to unity (signed) or zero. The latter case is appropriate for
an event which failed for whatever reason.
<<Instances: process instance: TBP>>=
procedure :: normalize_weight => process_instance_normalize_weight
<<Instances: procedures>>=
subroutine process_instance_normalize_weight (instance)
class(process_instance_t), intent(inout) :: instance
if (.not. vanishes (instance%weight)) then
instance%weight = sign (1._default, instance%weight)
end if
end subroutine process_instance_normalize_weight
@ %def process_instance_normalize_weight
@ This is a convenience routine that performs the computations of the
steps 1 to 5 in a single step. The arguments are the input for
[[set_mcpar]]. After this, the evaluation status should be either
[[STAT_FAILED_KINEMATICS]], [[STAT_FAILED_CUTS]] or [[STAT_EVALUATED_TRACE]].
Before calling this, we should call [[choose_mci]].
<<Instances: process instance: TBP>>=
procedure :: evaluate_sqme => process_instance_evaluate_sqme
<<Instances: procedures>>=
subroutine process_instance_evaluate_sqme (instance, channel, x)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: channel
real(default), dimension(:), intent(in) :: x
call instance%reset ()
call instance%set_mcpar (x)
call instance%select_channel (channel)
call instance%compute_seed_kinematics ()
call instance%compute_hard_kinematics ()
call instance%compute_eff_kinematics ()
call instance%evaluate_expressions ()
call instance%compute_other_channels ()
call instance%evaluate_trace ()
end subroutine process_instance_evaluate_sqme
@ %def process_instance_evaluate_sqme
@ This is the inverse. Assuming that the final trace evaluator
contains a valid momentum configuration, recover kinematics
and recalculate the matrix elements and their trace.
To be precise, we first recover kinematics for the given term and
associated component, then recalculate from that all other terms and
active components. The [[channel]] is not really required to obtain
the matrix element, but it allows us to reconstruct the exact MC
parameter set that corresponds to the given phase space point.
Before calling this, we should call [[choose_mci]].
<<Instances: process instance: TBP>>=
procedure :: recover => process_instance_recover
<<Instances: procedures>>=
subroutine process_instance_recover &
(instance, channel, i_term, update_sqme, recover_phs, scale_forced)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: channel
integer, intent(in) :: i_term
logical, intent(in) :: update_sqme
logical, intent(in) :: recover_phs
real(default), intent(in), allocatable, optional :: scale_forced
logical :: skip_phs
call instance%activate ()
instance%evaluation_status = STAT_EFF_KINEMATICS
call instance%recover_hard_kinematics (i_term)
call instance%recover_seed_kinematics (i_term)
call instance%select_channel (channel)
if (recover_phs) then
call instance%recover_mcpar (i_term)
call instance%recover_beam_momenta (i_term)
call instance%compute_seed_kinematics (i_term)
call instance%compute_hard_kinematics (i_term)
call instance%compute_eff_kinematics (i_term)
call instance%compute_other_channels (i_term)
else
call instance%recover_sfchain (i_term)
end if
call instance%evaluate_expressions (scale_forced)
if (update_sqme) then
call instance%reset_core_kinematics ()
call instance%evaluate_trace ()
end if
end subroutine process_instance_recover
@ %def process_instance_recover
@ The [[evaluate]] method is required by the [[sampler_t]] base type of which
the process instance is an extension.
The requirement is that after the process instance is evaluated, the
integrand, the selected channel, the $x$ array, and the $f$ Jacobian array are
exposed by the [[sampler_t]] object.
We allow for the additional [[hook]] to be called, if associated, for outlying
object to access information from the current state of the [[sampler]].
<<Instances: process instance: TBP>>=
procedure :: evaluate => process_instance_evaluate
<<Instances: procedures>>=
subroutine process_instance_evaluate (sampler, c, x_in, val, x, f)
class(process_instance_t), intent(inout) :: sampler
integer, intent(in) :: c
real(default), dimension(:), intent(in) :: x_in
real(default), intent(out) :: val
real(default), dimension(:,:), intent(out) :: x
real(default), dimension(:), intent(out) :: f
call sampler%evaluate_sqme (c, x_in)
if (sampler%is_valid ()) then
call sampler%fetch (val, x, f)
end if
call sampler%record_call ()
call sampler%evaluate_after_hook ()
end subroutine process_instance_evaluate
@ %def process_instance_evaluate
@ The phase-space point is valid if the event has valid kinematics and
has passed the cuts.
<<Instances: process instance: TBP>>=
procedure :: is_valid => process_instance_is_valid
<<Instances: procedures>>=
function process_instance_is_valid (sampler) result (valid)
class(process_instance_t), intent(in) :: sampler
logical :: valid
valid = sampler%evaluation_status >= STAT_PASSED_CUTS
end function process_instance_is_valid
@ %def process_instance_is_valid
@ Add a [[process_instance_hook]] object..
<<Instances: process instance: TBP>>=
procedure :: append_after_hook => process_instance_append_after_hook
<<Instances: procedures>>=
subroutine process_instance_append_after_hook (sampler, new_hook)
class(process_instance_t), intent(inout), target :: sampler
class(process_instance_hook_t), intent(inout), target :: new_hook
class(process_instance_hook_t), pointer :: last
if (associated (new_hook%next)) then
call msg_bug ("process_instance_append_after_hook: reuse of SAME hook object is forbidden.")
end if
if (associated (sampler%hook)) then
last => sampler%hook
do while (associated (last%next))
last => last%next
end do
last%next => new_hook
else
sampler%hook => new_hook
end if
end subroutine process_instance_append_after_hook
@ %def process_instance_append_after_evaluate_hook
@ Evaluate the after hook as first in, last out.
<<Instances: process instance: TBP>>=
procedure :: evaluate_after_hook => process_instance_evaluate_after_hook
<<Instances: procedures>>=
subroutine process_instance_evaluate_after_hook (sampler)
class(process_instance_t), intent(in) :: sampler
class(process_instance_hook_t), pointer :: current
current => sampler%hook
do while (associated(current))
call current%evaluate (sampler)
current => current%next
end do
end subroutine process_instance_evaluate_after_hook
@ %def process_instance_evaluate_after_hook
@ The [[rebuild]] method should rebuild the kinematics section out of
the [[x_in]] parameter set. The integrand value [[val]] should not be
computed, but is provided as input.
<<Instances: process instance: TBP>>=
procedure :: rebuild => process_instance_rebuild
<<Instances: procedures>>=
subroutine process_instance_rebuild (sampler, c, x_in, val, x, f)
class(process_instance_t), intent(inout) :: sampler
integer, intent(in) :: c
real(default), dimension(:), intent(in) :: x_in
real(default), intent(in) :: val
real(default), dimension(:,:), intent(out) :: x
real(default), dimension(:), intent(out) :: f
call msg_bug ("process_instance_rebuild not implemented yet")
x = 0
f = 0
end subroutine process_instance_rebuild
@ %def process_instance_rebuild
@ This is another method required by the [[sampler_t]] base type:
fetch the data that are relevant for the MCI record.
<<Instances: process instance: TBP>>=
procedure :: fetch => process_instance_fetch
<<Instances: procedures>>=
subroutine process_instance_fetch (sampler, val, x, f)
class(process_instance_t), intent(in) :: sampler
real(default), intent(out) :: val
real(default), dimension(:,:), intent(out) :: x
real(default), dimension(:), intent(out) :: f
integer, dimension(:), allocatable :: i_terms
integer :: i, i_term_base, cc
integer :: n_channel
val = 0
associate (process => sampler%process)
FIND_COMPONENT: do i = 1, process%get_n_components ()
if (sampler%process%component_is_selected (i)) then
allocate (i_terms (size (process%get_component_i_terms (i))))
i_terms = process%get_component_i_terms (i)
i_term_base = i_terms(1)
associate (k => sampler%term(i_term_base)%k_term)
n_channel = k%n_channel
do cc = 1, n_channel
call k%get_mcpar (cc, x(:,cc))
end do
f = k%f
val = sampler%sqme * k%phs_factor
end associate
if (allocated (i_terms)) deallocate (i_terms)
exit FIND_COMPONENT
end if
end do FIND_COMPONENT
end associate
end subroutine process_instance_fetch
@ %def process_instance_fetch
@ Initialize and finalize event generation for the specified MCI
entry.
<<Instances: process instance: TBP>>=
procedure :: init_simulation => process_instance_init_simulation
procedure :: final_simulation => process_instance_final_simulation
<<Instances: procedures>>=
subroutine process_instance_init_simulation (instance, i_mci, &
safety_factor, keep_failed_events)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_mci
real(default), intent(in), optional :: safety_factor
logical, intent(in), optional :: keep_failed_events
call instance%mci_work(i_mci)%init_simulation (safety_factor, keep_failed_events)
end subroutine process_instance_init_simulation
subroutine process_instance_final_simulation (instance, i_mci)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_mci
call instance%mci_work(i_mci)%final_simulation ()
end subroutine process_instance_final_simulation
@ %def process_instance_init_simulation
@ %def process_instance_final_simulation
@
\subsubsection{Accessing the process instance}
Once the seed kinematics is complete, we can retrieve the MC input parameters
for all channels, not just the seed channel.
Note: We choose the first active component. This makes sense only if the seed
kinematics is identical for all active components.
<<Instances: process instance: TBP>>=
procedure :: get_mcpar => process_instance_get_mcpar
<<Instances: procedures>>=
subroutine process_instance_get_mcpar (instance, channel, x)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: channel
real(default), dimension(:), intent(out) :: x
integer :: i
if (instance%evaluation_status >= STAT_SEED_KINEMATICS) then
do i = 1, size (instance%term)
if (instance%term(i)%active) then
call instance%term(i)%k_term%get_mcpar (channel, x)
return
end if
end do
call msg_bug ("Process instance: get_mcpar: no active channels")
else
call msg_bug ("Process instance: get_mcpar: no seed kinematics")
end if
end subroutine process_instance_get_mcpar
@ %def process_instance_get_mcpar
@ Return true if the [[sqme]] value is known. This also implies that the
event is kinematically valid and has passed all cuts.
<<Instances: process instance: TBP>>=
procedure :: has_evaluated_trace => process_instance_has_evaluated_trace
<<Instances: procedures>>=
function process_instance_has_evaluated_trace (instance) result (flag)
class(process_instance_t), intent(in) :: instance
logical :: flag
flag = instance%evaluation_status >= STAT_EVALUATED_TRACE
end function process_instance_has_evaluated_trace
@ %def process_instance_has_evaluated_trace
@ Return true if the event is complete. In particular, the event must
be kinematically valid, passed all cuts, and the event data have been
computed.
<<Instances: process instance: TBP>>=
procedure :: is_complete_event => process_instance_is_complete_event
<<Instances: procedures>>=
function process_instance_is_complete_event (instance) result (flag)
class(process_instance_t), intent(in) :: instance
logical :: flag
flag = instance%evaluation_status >= STAT_EVENT_COMPLETE
end function process_instance_is_complete_event
@ %def process_instance_is_complete_event
@ Select the term for the process instance that will provide the basic
event record (used in [[evt_trivial_make_particle_set]]). It might be
necessary to write out additional events corresponding to other terms
(done in [[evt_nlo]]).
<<Instances: process instance: TBP>>=
procedure :: select_i_term => process_instance_select_i_term
<<Instances: procedures>>=
function process_instance_select_i_term (instance) result (i_term)
integer :: i_term
class(process_instance_t), intent(in) :: instance
integer :: i_mci
i_mci = instance%i_mci
i_term = instance%process%select_i_term (i_mci)
end function process_instance_select_i_term
@ %def process_instance_select_i_term
@ Return pointer to the master beam interaction.
<<Instances: process instance: TBP>>=
procedure :: get_beam_int_ptr => process_instance_get_beam_int_ptr
<<Instances: procedures>>=
function process_instance_get_beam_int_ptr (instance) result (ptr)
class(process_instance_t), intent(in), target :: instance
type(interaction_t), pointer :: ptr
ptr => instance%sf_chain%get_beam_int_ptr ()
end function process_instance_get_beam_int_ptr
@ %def process_instance_get_beam_int_ptr
@ Return pointers to the matrix and flows interactions, given a term index.
<<Instances: process instance: TBP>>=
procedure :: get_trace_int_ptr => process_instance_get_trace_int_ptr
procedure :: get_matrix_int_ptr => process_instance_get_matrix_int_ptr
procedure :: get_flows_int_ptr => process_instance_get_flows_int_ptr
<<Instances: procedures>>=
function process_instance_get_trace_int_ptr (instance, i_term) result (ptr)
class(process_instance_t), intent(in), target :: instance
integer, intent(in) :: i_term
type(interaction_t), pointer :: ptr
ptr => instance%term(i_term)%connected%get_trace_int_ptr ()
end function process_instance_get_trace_int_ptr
function process_instance_get_matrix_int_ptr (instance, i_term) result (ptr)
class(process_instance_t), intent(in), target :: instance
integer, intent(in) :: i_term
type(interaction_t), pointer :: ptr
ptr => instance%term(i_term)%connected%get_matrix_int_ptr ()
end function process_instance_get_matrix_int_ptr
function process_instance_get_flows_int_ptr (instance, i_term) result (ptr)
class(process_instance_t), intent(in), target :: instance
integer, intent(in) :: i_term
type(interaction_t), pointer :: ptr
ptr => instance%term(i_term)%connected%get_flows_int_ptr ()
end function process_instance_get_flows_int_ptr
@ %def process_instance_get_trace_int_ptr
@ %def process_instance_get_matrix_int_ptr
@ %def process_instance_get_flows_int_ptr
@ Return the complete account of flavor combinations in the underlying
interaction object, including beams, radiation, and hard interaction.
<<Instances: process instance: TBP>>=
procedure :: get_state_flv => process_instance_get_state_flv
<<Instances: procedures>>=
function process_instance_get_state_flv (instance, i_term) result (state_flv)
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_term
type(state_flv_content_t) :: state_flv
state_flv = instance%term(i_term)%connected%get_state_flv ()
end function process_instance_get_state_flv
@ %def process_instance_get_state_flv
@ Return pointers to the parton states of a selected term.
<<Instances: process instance: TBP>>=
procedure :: get_isolated_state_ptr => &
process_instance_get_isolated_state_ptr
procedure :: get_connected_state_ptr => &
process_instance_get_connected_state_ptr
<<Instances: procedures>>=
function process_instance_get_isolated_state_ptr (instance, i_term) &
result (ptr)
class(process_instance_t), intent(in), target :: instance
integer, intent(in) :: i_term
type(isolated_state_t), pointer :: ptr
ptr => instance%term(i_term)%isolated
end function process_instance_get_isolated_state_ptr
function process_instance_get_connected_state_ptr (instance, i_term) &
result (ptr)
class(process_instance_t), intent(in), target :: instance
integer, intent(in) :: i_term
type(connected_state_t), pointer :: ptr
ptr => instance%term(i_term)%connected
end function process_instance_get_connected_state_ptr
@ %def process_instance_get_isolated_state_ptr
@ %def process_instance_get_connected_state_ptr
@ Return the indices of the beam particles and incoming partons within the
currently active state matrix, respectively.
<<Instances: process instance: TBP>>=
procedure :: get_beam_index => process_instance_get_beam_index
procedure :: get_in_index => process_instance_get_in_index
<<Instances: procedures>>=
subroutine process_instance_get_beam_index (instance, i_term, i_beam)
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_term
integer, dimension(:), intent(out) :: i_beam
call instance%term(i_term)%connected%get_beam_index (i_beam)
end subroutine process_instance_get_beam_index
subroutine process_instance_get_in_index (instance, i_term, i_in)
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_term
integer, dimension(:), intent(out) :: i_in
call instance%term(i_term)%connected%get_in_index (i_in)
end subroutine process_instance_get_in_index
@ %def process_instance_get_beam_index
@ %def process_instance_get_in_index
@ Return squared matrix element and event weight, and event weight
excess where applicable. [[n_dropped]] is a number that can be
nonzero when a weighted event has been generated, dropping events with
zero weight (failed cuts) on the fly.
<<Instances: process instance: TBP>>=
procedure :: get_sqme => process_instance_get_sqme
procedure :: get_weight => process_instance_get_weight
procedure :: get_excess => process_instance_get_excess
procedure :: get_n_dropped => process_instance_get_n_dropped
<<Instances: procedures>>=
function process_instance_get_sqme (instance, i_term) result (sqme)
real(default) :: sqme
class(process_instance_t), intent(in) :: instance
integer, intent(in), optional :: i_term
if (instance%evaluation_status >= STAT_EVALUATED_TRACE) then
if (present (i_term)) then
sqme = instance%term(i_term)%connected%trace%get_matrix_element (1)
else
sqme = instance%sqme
end if
else
sqme = 0
end if
end function process_instance_get_sqme
function process_instance_get_weight (instance) result (weight)
real(default) :: weight
class(process_instance_t), intent(in) :: instance
if (instance%evaluation_status >= STAT_EVENT_COMPLETE) then
weight = instance%weight
else
weight = 0
end if
end function process_instance_get_weight
function process_instance_get_excess (instance) result (excess)
real(default) :: excess
class(process_instance_t), intent(in) :: instance
if (instance%evaluation_status >= STAT_EVENT_COMPLETE) then
excess = instance%excess
else
excess = 0
end if
end function process_instance_get_excess
function process_instance_get_n_dropped (instance) result (n_dropped)
integer :: n_dropped
class(process_instance_t), intent(in) :: instance
if (instance%evaluation_status >= STAT_EVENT_COMPLETE) then
n_dropped = instance%n_dropped
else
n_dropped = 0
end if
end function process_instance_get_n_dropped
@ %def process_instance_get_sqme
@ %def process_instance_get_weight
@ %def process_instance_get_excess
@ %def process_instance_get_n_dropped
@ Return the currently selected MCI channel.
<<Instances: process instance: TBP>>=
procedure :: get_channel => process_instance_get_channel
<<Instances: procedures>>=
function process_instance_get_channel (instance) result (channel)
integer :: channel
class(process_instance_t), intent(in) :: instance
channel = instance%selected_channel
end function process_instance_get_channel
@ %def process_instance_get_channel
@
<<Instances: process instance: TBP>>=
procedure :: set_fac_scale => process_instance_set_fac_scale
<<Instances: procedures>>=
subroutine process_instance_set_fac_scale (instance, fac_scale)
class(process_instance_t), intent(inout) :: instance
real(default), intent(in) :: fac_scale
integer :: i_term
i_term = 1
call instance%term(i_term)%set_fac_scale (fac_scale)
end subroutine process_instance_set_fac_scale
@ %def process_instance_set_fac_scale
@ Return factorization scale and strong coupling. We have to select a
term instance.
<<Instances: process instance: TBP>>=
procedure :: get_fac_scale => process_instance_get_fac_scale
procedure :: get_alpha_s => process_instance_get_alpha_s
<<Instances: procedures>>=
function process_instance_get_fac_scale (instance, i_term) result (fac_scale)
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_term
real(default) :: fac_scale
fac_scale = instance%term(i_term)%get_fac_scale ()
end function process_instance_get_fac_scale
function process_instance_get_alpha_s (instance, i_term) result (alpha_s)
real(default) :: alpha_s
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_term
class(prc_core_t), pointer :: core => null ()
core => instance%process%get_core_term (i_term)
alpha_s = instance%term(i_term)%get_alpha_s (core)
core => null ()
end function process_instance_get_alpha_s
@ %def process_instance_get_fac_scale
@ %def process_instance_get_alpha_s
@
<<Instances: process instance: TBP>>=
procedure :: get_qcd => process_instance_get_qcd
<<Instances: procedures>>=
function process_instance_get_qcd (process_instance) result (qcd)
type(qcd_t) :: qcd
class(process_instance_t), intent(in) :: process_instance
qcd = process_instance%process%get_qcd ()
end function process_instance_get_qcd
@ %def process_instance_get_qcd
@ Counter.
<<Instances: process instance: TBP>>=
procedure :: reset_counter => process_instance_reset_counter
procedure :: record_call => process_instance_record_call
procedure :: get_counter => process_instance_get_counter
<<Instances: procedures>>=
subroutine process_instance_reset_counter (process_instance)
class(process_instance_t), intent(inout) :: process_instance
call process_instance%mci_work(process_instance%i_mci)%reset_counter ()
end subroutine process_instance_reset_counter
subroutine process_instance_record_call (process_instance)
class(process_instance_t), intent(inout) :: process_instance
call process_instance%mci_work(process_instance%i_mci)%record_call &
(process_instance%evaluation_status)
end subroutine process_instance_record_call
pure function process_instance_get_counter (process_instance) result (counter)
class(process_instance_t), intent(in) :: process_instance
type(process_counter_t) :: counter
counter = process_instance%mci_work(process_instance%i_mci)%get_counter ()
end function process_instance_get_counter
@ %def process_instance_reset_counter
@ %def process_instance_record_call
@ %def process_instance_get_counter
@ Sum up the total number of calls for all MCI records.
<<Instances: process instance: TBP>>=
procedure :: get_actual_calls_total => process_instance_get_actual_calls_total
<<Instances: procedures>>=
pure function process_instance_get_actual_calls_total (process_instance) &
result (n)
class(process_instance_t), intent(in) :: process_instance
integer :: n
integer :: i
type(process_counter_t) :: counter
n = 0
do i = 1, size (process_instance%mci_work)
counter = process_instance%mci_work(i)%get_counter ()
n = n + counter%total
end do
end function process_instance_get_actual_calls_total
@ %def process_instance_get_actual_calls_total
@
<<Instances: process instance: TBP>>=
procedure :: reset_matrix_elements => process_instance_reset_matrix_elements
<<Instances: procedures>>=
subroutine process_instance_reset_matrix_elements (instance)
class(process_instance_t), intent(inout) :: instance
integer :: i_term
do i_term = 1, size (instance%term)
call instance%term(i_term)%connected%trace%set_matrix_element (cmplx (0, 0, default))
call instance%term(i_term)%connected%matrix%set_matrix_element (cmplx (0, 0, default))
end do
end subroutine process_instance_reset_matrix_elements
@ %def process_instance_reset_matrix_elements
@
<<Instances: process instance: TBP>>=
procedure :: get_test_phase_space_point &
=> process_instance_get_test_phase_space_point
<<Instances: procedures>>=
subroutine process_instance_get_test_phase_space_point (instance, &
i_component, i_core, p)
type(vector4_t), dimension(:), allocatable, intent(out) :: p
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_component, i_core
real(default), dimension(:), allocatable :: x
logical :: success
integer :: i_term
instance%i_mci = i_component
i_term = instance%process%get_i_term (i_core)
associate (term => instance%term(i_term))
allocate (x (instance%mci_work(i_component)%config%n_par))
x = 0.5_default
call instance%set_mcpar (x, .true.)
call instance%select_channel (1)
call term%compute_seed_kinematics &
(instance%mci_work(i_component), 1, success)
call instance%term(i_term)%evaluate_radiation_kinematics &
(instance%mci_work(instance%i_mci)%get_x_process ())
call instance%term(i_term)%compute_hard_kinematics (success = success)
allocate (p (size (term%p_hard)))
p = term%int_hard%get_momenta ()
end associate
end subroutine process_instance_get_test_phase_space_point
@ %def process_instance_get_test_phase_space_point
@
<<Instances: process instance: TBP>>=
procedure :: get_p_hard => process_instance_get_p_hard
<<Instances: procedures>>=
pure function process_instance_get_p_hard (process_instance, i_term) &
result (p_hard)
type(vector4_t), dimension(:), allocatable :: p_hard
class(process_instance_t), intent(in) :: process_instance
integer, intent(in) :: i_term
allocate (p_hard (size (process_instance%term(i_term)%get_p_hard ())))
p_hard = process_instance%term(i_term)%get_p_hard ()
end function process_instance_get_p_hard
@ %def process_instance_get_p_hard
@
<<Instances: process instance: TBP>>=
procedure :: get_first_active_i_term => process_instance_get_first_active_i_term
<<Instances: procedures>>=
function process_instance_get_first_active_i_term (instance) result (i_term)
integer :: i_term
class(process_instance_t), intent(in) :: instance
integer :: i
i_term = 0
do i = 1, size (instance%term)
if (instance%term(i)%active) then
i_term = i
exit
end if
end do
end function process_instance_get_first_active_i_term
@ %def process_instance_get_first_active_i_term
@
<<Instances: process instance: TBP>>=
procedure :: get_real_of_mci => process_instance_get_real_of_mci
<<Instances: procedures>>=
function process_instance_get_real_of_mci (instance) result (i_real)
integer :: i_real
class(process_instance_t), intent(in) :: instance
select type (pcm => instance%pcm)
type is (pcm_instance_nlo_t)
i_real = pcm%i_mci_to_real_component (instance%i_mci)
end select
end function process_instance_get_real_of_mci
@ %def process_instance_get_real_of_mci
@
<<Instances: process instance: TBP>>=
procedure :: get_connected_states => process_instance_get_connected_states
<<Instances: procedures>>=
function process_instance_get_connected_states (instance, i_component) result (connected)
type(connected_state_t), dimension(:), allocatable :: connected
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_component
connected = instance%process%get_connected_states (i_component, &
instance%term(:)%connected)
end function process_instance_get_connected_states
@ %def process_instance_get_connected_states
@ Get the hadronic center-of-mass energy
<<Instances: process instance: TBP>>=
procedure :: get_sqrts => process_instance_get_sqrts
<<Instances: procedures>>=
function process_instance_get_sqrts (instance) result (sqrts)
class(process_instance_t), intent(in) :: instance
real(default) :: sqrts
sqrts = instance%process%get_sqrts ()
end function process_instance_get_sqrts
@ %def process_instance_get_sqrts
@ Get the polarizations
<<Instances: process instance: TBP>>=
procedure :: get_polarization => process_instance_get_polarization
<<Instances: procedures>>=
function process_instance_get_polarization (instance) result (pol)
class(process_instance_t), intent(in) :: instance
real(default), dimension(2) :: pol
pol = instance%process%get_polarization ()
end function process_instance_get_polarization
@ %def process_instance_get_polarization
@ Get the beam spectrum
<<Instances: process instance: TBP>>=
procedure :: get_beam_file => process_instance_get_beam_file
<<Instances: procedures>>=
function process_instance_get_beam_file (instance) result (file)
class(process_instance_t), intent(in) :: instance
type(string_t) :: file
file = instance%process%get_beam_file ()
end function process_instance_get_beam_file
@ %def process_instance_get_beam_file
@ Get the process name
<<Instances: process instance: TBP>>=
procedure :: get_process_name => process_instance_get_process_name
<<Instances: procedures>>=
function process_instance_get_process_name (instance) result (name)
class(process_instance_t), intent(in) :: instance
type(string_t) :: name
name = instance%process%get_id ()
end function process_instance_get_process_name
@ %def process_instance_get_process_name
@
\subsubsection{Particle sets}
Here we provide two procedures that convert the process instance
from/to a particle set. The conversion applies to the trace evaluator
which has no quantum-number information, thus it involves only the
momenta and the parent-child relations. We keep virtual particles.
If [[n_incoming]] is provided, the status code of the first
[[n_incoming]] particles will be reset to incoming. Otherwise, they
would be classified as virtual.
Nevertheless, it is possible to reconstruct the complete structure
from a particle set. The reconstruction implies a re-evaluation of
the structure function and matrix-element codes.
The [[i_term]] index is needed for both input and output, to select
among different active trace evaluators.
In both cases, the [[instance]] object must be properly initialized.
NB: The [[recover_beams]] option should be used only when the particle
set originates from an external event file, and the user has asked for
it. It should be switched off when reading from raw event file.
<<Instances: process instance: TBP>>=
procedure :: get_trace => process_instance_get_trace
procedure :: set_trace => process_instance_set_trace
<<Instances: procedures>>=
subroutine process_instance_get_trace (instance, pset, i_term, n_incoming)
class(process_instance_t), intent(in), target :: instance
type(particle_set_t), intent(out) :: pset
integer, intent(in) :: i_term
integer, intent(in), optional :: n_incoming
type(interaction_t), pointer :: int
logical :: ok
int => instance%get_trace_int_ptr (i_term)
call pset%init (ok, int, int, FM_IGNORE_HELICITY, &
[0._default, 0._default], .false., .true., n_incoming)
end subroutine process_instance_get_trace
subroutine process_instance_set_trace &
(instance, pset, i_term, recover_beams, check_match)
class(process_instance_t), intent(inout), target :: instance
type(particle_set_t), intent(in) :: pset
integer, intent(in) :: i_term
logical, intent(in), optional :: recover_beams, check_match
type(interaction_t), pointer :: int
integer :: n_in
int => instance%get_trace_int_ptr (i_term)
n_in = instance%process%get_n_in ()
call pset%fill_interaction (int, n_in, &
recover_beams = recover_beams, &
check_match = check_match, &
state_flv = instance%get_state_flv (i_term))
end subroutine process_instance_set_trace
@ %def process_instance_get_trace
@ %def process_instance_set_trace
@ This procedure allows us to override any QCD setting of the WHIZARD process
and directly set the coupling value that comes together with a particle set.
<<Instances: process instance: TBP>>=
procedure :: set_alpha_qcd_forced => process_instance_set_alpha_qcd_forced
<<Instances: procedures>>=
subroutine process_instance_set_alpha_qcd_forced (instance, i_term, alpha_qcd)
class(process_instance_t), intent(inout) :: instance
integer, intent(in) :: i_term
real(default), intent(in) :: alpha_qcd
call instance%term(i_term)%set_alpha_qcd_forced (alpha_qcd)
end subroutine process_instance_set_alpha_qcd_forced
@ %def process_instance_set_alpha_qcd_forced
@
<<Instances: process instance: TBP>>=
procedure :: has_nlo_component => process_instance_has_nlo_component
<<Instances: procedures>>=
function process_instance_has_nlo_component (instance) result (nlo)
class(process_instance_t), intent(in) :: instance
logical :: nlo
nlo = instance%process%is_nlo_calculation ()
end function process_instance_has_nlo_component
@ %def process_instance_has_nlo_component
@
<<Instances: process instance: TBP>>=
procedure :: keep_failed_events => process_instance_keep_failed_events
<<Instances: procedures>>=
function process_instance_keep_failed_events (instance) result (keep)
logical :: keep
class(process_instance_t), intent(in) :: instance
keep = instance%mci_work(instance%i_mci)%keep_failed_events
end function process_instance_keep_failed_events
@ %def process_instance_keep_failed_events
@
<<Instances: process instance: TBP>>=
procedure :: get_term_indices => process_instance_get_term_indices
<<Instances: procedures>>=
function process_instance_get_term_indices (instance, nlo_type) result (i_term)
integer, dimension(:), allocatable :: i_term
class(process_instance_t), intent(in) :: instance
integer :: nlo_type
allocate (i_term (count (instance%term%nlo_type == nlo_type)))
i_term = pack (instance%term%get_i_term_global (), instance%term%nlo_type == nlo_type)
end function process_instance_get_term_indices
@ %def process_instance_get_term_indices
@
<<Instances: process instance: TBP>>=
procedure :: get_boost_to_lab => process_instance_get_boost_to_lab
<<Instances: procedures>>=
function process_instance_get_boost_to_lab (instance, i_term) result (lt)
type(lorentz_transformation_t) :: lt
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_term
lt = instance%term(i_term)%get_boost_to_lab ()
end function process_instance_get_boost_to_lab
@ %def process_instance_get_boost_to_lab
@
<<Instances: process instance: TBP>>=
procedure :: get_boost_to_cms => process_instance_get_boost_to_cms
<<Instances: procedures>>=
function process_instance_get_boost_to_cms (instance, i_term) result (lt)
type(lorentz_transformation_t) :: lt
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_term
lt = instance%term(i_term)%get_boost_to_cms ()
end function process_instance_get_boost_to_cms
@ %def process_instance_get_boost_to_cms
@
<<Instances: process instance: TBP>>=
procedure :: is_cm_frame => process_instance_is_cm_frame
<<Instances: procedures>>=
function process_instance_is_cm_frame (instance, i_term) result (cm_frame)
logical :: cm_frame
class(process_instance_t), intent(in) :: instance
integer, intent(in) :: i_term
cm_frame = instance%term(i_term)%k_term%phs%is_cm_frame ()
end function process_instance_is_cm_frame
@ %def protcess_instance_is_cm_frame
@
The [[pacify]] subroutine has the purpose of setting numbers to zero
which are (by comparing with a [[tolerance]] parameter) considered
equivalent with zero. We do this in some unit tests. Here, we a
apply this to the phase space subobject of the process instance.
<<Instances: public>>=
public :: pacify
<<Instances: interfaces>>=
interface pacify
module procedure pacify_process_instance
end interface pacify
<<Instances: procedures>>=
subroutine pacify_process_instance (instance)
type(process_instance_t), intent(inout) :: instance
integer :: i
do i = 1, size (instance%term)
call pacify (instance%term(i)%k_term%phs)
end do
end subroutine pacify_process_instance
@ %def pacify
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[processes_ut.f90]]>>=
<<File header>>
module processes_ut
use unit_tests
use processes_uti
<<Standard module head>>
<<Processes: public test>>
<<Processes: public test auxiliary>>
contains
<<Processes: test driver>>
end module processes_ut
@ %def processes_ut
@
<<[[processes_uti.f90]]>>=
<<File header>>
module processes_uti
<<Use kinds>>
<<Use strings>>
use format_utils, only: write_separator
use constants, only: TWOPI4
use physics_defs, only: CONV
use os_interface
use sm_qcd
use lorentz
use pdg_arrays
use model_data
use models
use var_base, only: vars_t
use variables, only: var_list_t
use model_testbed, only: prepare_model
use particle_specifiers, only: new_prt_spec
use flavors
use interactions, only: reset_interaction_counter
use particles
use rng_base
use mci_base
use mci_none, only: mci_none_t
use mci_midpoint
use sf_mappings
use sf_base
use phs_base
use phs_single
use phs_forests, only: syntax_phs_forest_init, syntax_phs_forest_final
use phs_wood, only: phs_wood_config_t
use resonances, only: resonance_history_set_t
use process_constants
use prc_core_def, only: prc_core_def_t
use prc_core
use prc_test, only: prc_test_create_library
use prc_template_me, only: template_me_def_t
use process_libraries
use prc_test_core
use process_counter
use process_config, only: process_term_t
use process, only: process_t
use instances, only: process_instance_t, process_instance_hook_t
use rng_base_ut, only: rng_test_factory_t
use sf_base_ut, only: sf_test_data_t
use mci_base_ut, only: mci_test_t
use phs_base_ut, only: phs_test_config_t
<<Standard module head>>
<<Processes: public test auxiliary>>
<<Processes: test declarations>>
<<Processes: test types>>
contains
<<Processes: tests>>
<<Processes: test auxiliary>>
end module processes_uti
@ %def processes_uti
@ API: driver for the unit tests below.
<<Processes: public test>>=
public :: processes_test
<<Processes: test driver>>=
subroutine processes_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Processes: execute tests>>
end subroutine processes_test
@ %def processes_test
\subsubsection{Write an empty process object}
The most trivial test is to write an uninitialized process object.
<<Processes: execute tests>>=
call test (processes_1, "processes_1", &
"write an empty process object", &
u, results)
<<Processes: test declarations>>=
public :: processes_1
<<Processes: tests>>=
subroutine processes_1 (u)
integer, intent(in) :: u
type(process_t) :: process
write (u, "(A)") "* Test output: processes_1"
write (u, "(A)") "* Purpose: display an empty process object"
write (u, "(A)")
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_1"
end subroutine processes_1
@ %def processes_1
@
\subsubsection{Initialize a process object}
Initialize a process and display it.
<<Processes: execute tests>>=
call test (processes_2, "processes_2", &
"initialize a simple process object", &
u, results)
<<Processes: test declarations>>=
public :: processes_2
<<Processes: tests>>=
subroutine processes_2 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable :: process
class(mci_t), allocatable :: mci_template
class(phs_config_t), allocatable :: phs_config_template
write (u, "(A)") "* Test output: processes_2"
write (u, "(A)") "* Purpose: initialize a simple process object"
write (u, "(A)")
write (u, "(A)") "* Build and load a test library with one process"
write (u, "(A)")
libname = "processes2"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
-
+
write (u, "(A)") "* Initialize a process object"
write (u, "(A)")
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%set_run_id (var_str ("run_2"))
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
call process%setup_mci (dispatch_mci_empty)
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_2"
end subroutine processes_2
@ %def processes_2
@ Trivial for testing: do not allocate the MCI record.
<<Processes: test auxiliary>>=
subroutine dispatch_mci_empty (mci, var_list, process_id, is_nlo)
class(mci_t), allocatable, intent(out) :: mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
logical, intent(in), optional :: is_nlo
end subroutine dispatch_mci_empty
-
+
@ %def dispatch_mci_empty
@
\subsubsection{Compute a trivial matrix element}
Initialize a process, retrieve some information and compute a matrix
element.
We use the same trivial process as for the previous test. All
momentum and state dependence is trivial, so we just test basic
functionality.
<<Processes: execute tests>>=
call test (processes_3, "processes_3", &
"retrieve a trivial matrix element", &
u, results)
<<Processes: test declarations>>=
public :: processes_3
<<Processes: tests>>=
subroutine processes_3 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable :: process
class(phs_config_t), allocatable :: phs_config_template
type(process_constants_t) :: data
type(vector4_t), dimension(:), allocatable :: p
write (u, "(A)") "* Test output: processes_3"
write (u, "(A)") "* Purpose: create a process &
&and compute a matrix element"
write (u, "(A)")
write (u, "(A)") "* Build and load a test library with one process"
write (u, "(A)")
libname = "processes3"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
call process%setup_mci (dispatch_mci_test3)
write (u, "(A)") "* Return the number of process components"
write (u, "(A)")
write (u, "(A,I0)") "n_components = ", process%get_n_components ()
write (u, "(A)")
write (u, "(A)") "* Return the number of flavor states"
write (u, "(A)")
data = process%get_constants (1)
write (u, "(A,I0)") "n_flv(1) = ", data%n_flv
write (u, "(A)")
write (u, "(A)") "* Return the first flavor state"
write (u, "(A)")
write (u, "(A,4(1x,I0))") "flv_state(1) =", data%flv_state (:,1)
write (u, "(A)")
write (u, "(A)") "* Set up kinematics &
&[arbitrary, the matrix element is constant]"
allocate (p (4))
write (u, "(A)")
write (u, "(A)") "* Retrieve the matrix element"
write (u, "(A)")
write (u, "(A,F5.3,' + ',F5.3,' I')") "me (1, p, 1, 1, 1) = ", &
process%compute_amplitude (1, 1, 1, p, 1, 1, 1)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_3"
end subroutine processes_3
@ %def processes_3
@ MCI record with some contents.
<<Processes: test auxiliary>>=
subroutine dispatch_mci_test3 (mci, var_list, process_id, is_nlo)
class(mci_t), allocatable, intent(out) :: mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
logical, intent(in), optional :: is_nlo
allocate (mci_test_t :: mci)
select type (mci)
type is (mci_test_t)
call mci%set_dimensions (2, 2)
call mci%set_divisions (100)
end select
end subroutine dispatch_mci_test3
-
+
@ %def dispatch_mci_test3
@
\subsubsection{Generate a process instance}
Initialize a process and process instance, choose a sampling point and
fill the process instance.
We use the same trivial process as for the previous test. All
momentum and state dependence is trivial, so we just test basic
functionality.
<<Processes: execute tests>>=
call test (processes_4, "processes_4", &
"create and fill a process instance (partonic event)", &
u, results)
<<Processes: test declarations>>=
public :: processes_4
<<Processes: tests>>=
subroutine processes_4 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
type(process_instance_t), allocatable, target :: process_instance
type(particle_set_t) :: pset
write (u, "(A)") "* Test output: processes_4"
write (u, "(A)") "* Purpose: create a process &
&and fill a process instance"
write (u, "(A)")
write (u, "(A)") "* Build and initialize a test process"
write (u, "(A)")
libname = "processes4"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call reset_interaction_counter ()
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Prepare a trivial beam setup"
write (u, "(A)")
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
call process%configure_phs ()
call process%setup_mci (dispatch_mci_empty)
write (u, "(A)") "* Complete process initialization"
write (u, "(A)")
call process%setup_terms ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Create a process instance"
write (u, "(A)")
allocate (process_instance)
call process_instance%init (process)
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Inject a set of random numbers"
write (u, "(A)")
call process_instance%choose_mci (1)
call process_instance%set_mcpar ([0._default, 0._default])
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Set up hard kinematics"
write (u, "(A)")
call process_instance%select_channel (1)
call process_instance%compute_seed_kinematics ()
call process_instance%compute_hard_kinematics ()
call process_instance%compute_eff_kinematics ()
call process_instance%evaluate_expressions ()
call process_instance%compute_other_channels ()
write (u, "(A)") "* Evaluate matrix element and square"
write (u, "(A)")
call process_instance%evaluate_trace ()
call process_instance%write (u)
call process_instance%get_trace (pset, 1)
call process_instance%final ()
deallocate (process_instance)
write (u, "(A)")
write (u, "(A)") "* Particle content:"
write (u, "(A)")
call write_separator (u)
call pset%write (u)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Recover process instance"
write (u, "(A)")
allocate (process_instance)
call process_instance%init (process)
call process_instance%choose_mci (1)
call process_instance%set_trace (pset, 1, check_match = .false.)
call process_instance%activate ()
process_instance%evaluation_status = STAT_EFF_KINEMATICS
call process_instance%recover_hard_kinematics (i_term = 1)
call process_instance%recover_seed_kinematics (i_term = 1)
call process_instance%select_channel (1)
call process_instance%recover_mcpar (i_term = 1)
call process_instance%compute_seed_kinematics (skip_term = 1)
call process_instance%compute_hard_kinematics (skip_term = 1)
call process_instance%compute_eff_kinematics (skip_term = 1)
call process_instance%evaluate_expressions ()
call process_instance%compute_other_channels (skip_term = 1)
call process_instance%evaluate_trace ()
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call pset%final ()
call process_instance%final ()
deallocate (process_instance)
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_4"
end subroutine processes_4
@ %def processes_4
@
\subsubsection{Structure function configuration}
Configure structure functions (multi-channel) in a process object.
<<Processes: execute tests>>=
call test (processes_7, "processes_7", &
"process configuration with structure functions", &
u, results)
<<Processes: test declarations>>=
public :: processes_7
<<Processes: tests>>=
subroutine processes_7 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
type(sf_config_t), dimension(:), allocatable :: sf_config
type(sf_channel_t), dimension(2) :: sf_channel
write (u, "(A)") "* Test output: processes_7"
write (u, "(A)") "* Purpose: initialize a process with &
&structure functions"
write (u, "(A)")
write (u, "(A)") "* Build and initialize a process object"
write (u, "(A)")
libname = "processes7"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Set beam, structure functions, and mappings"
write (u, "(A)")
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
call process%configure_phs ()
pdg_in = 25
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (process%get_model_ptr (), pdg_in)
end select
allocate (sf_config (2))
call sf_config(1)%init ([1], data)
call sf_config(2)%init ([2], data)
call process%init_sf_chain (sf_config)
deallocate (sf_config)
call process%test_allocate_sf_channels (3)
call sf_channel(1)%init (2)
call sf_channel(1)%activate_mapping ([1,2])
call process%set_sf_channel (2, sf_channel(1))
call sf_channel(2)%init (2)
call sf_channel(2)%set_s_mapping ([1,2])
call process%set_sf_channel (3, sf_channel(2))
call process%setup_mci (dispatch_mci_empty)
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_7"
end subroutine processes_7
@ %def processes_7
@
\subsubsection{Evaluating a process with structure function}
Configure structure functions (single-channel) in a process object,
create an instance, compute kinematics and evaluate.
Note the order of operations when setting up structure functions and
phase space. The beams are first, they determine the [[sqrts]] value.
We can also set up the chain of structure functions. We then
configure the phase space. From this, we can obtain information about
special configurations (resonances, etc.), which we need for
allocating the possible structure-function channels (parameterizations
and mappings). Finally, we match phase-space channels onto
structure-function channels.
In the current example, this matching is trivial; we only have one
structure-function channel.
<<Processes: execute tests>>=
call test (processes_8, "processes_8", &
"process evaluation with structure functions", &
u, results)
<<Processes: test declarations>>=
public :: processes_8
<<Processes: tests>>=
subroutine processes_8 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
type(process_instance_t), allocatable, target :: process_instance
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
type(sf_config_t), dimension(:), allocatable :: sf_config
type(sf_channel_t) :: sf_channel
type(particle_set_t) :: pset
write (u, "(A)") "* Test output: processes_8"
write (u, "(A)") "* Purpose: evaluate a process with &
&structure functions"
write (u, "(A)")
write (u, "(A)") "* Build and initialize a process object"
write (u, "(A)")
libname = "processes8"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call reset_interaction_counter ()
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Set beam, structure functions, and mappings"
write (u, "(A)")
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
pdg_in = 25
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (process%get_model_ptr (), pdg_in)
end select
allocate (sf_config (2))
call sf_config(1)%init ([1], data)
call sf_config(2)%init ([2], data)
call process%init_sf_chain (sf_config)
deallocate (sf_config)
call process%configure_phs ()
call process%test_allocate_sf_channels (1)
call sf_channel%init (2)
call sf_channel%activate_mapping ([1,2])
call process%set_sf_channel (1, sf_channel)
write (u, "(A)") "* Complete process initialization"
write (u, "(A)")
call process%setup_mci (dispatch_mci_empty)
call process%setup_terms ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Create a process instance"
write (u, "(A)")
allocate (process_instance)
call process_instance%init (process)
write (u, "(A)") "* Set up kinematics and evaluate"
write (u, "(A)")
call process_instance%choose_mci (1)
call process_instance%evaluate_sqme (1, &
[0.8_default, 0.8_default, 0.1_default, 0.2_default])
call process_instance%write (u)
call process_instance%get_trace (pset, 1)
call process_instance%final ()
deallocate (process_instance)
write (u, "(A)")
write (u, "(A)") "* Particle content:"
write (u, "(A)")
call write_separator (u)
call pset%write (u)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Recover process instance"
write (u, "(A)")
call reset_interaction_counter (2)
allocate (process_instance)
call process_instance%init (process)
call process_instance%choose_mci (1)
call process_instance%set_trace (pset, 1, check_match = .false.)
call process_instance%recover &
(channel = 1, i_term = 1, update_sqme = .true., recover_phs = .true.)
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call pset%final ()
call process_instance%final ()
deallocate (process_instance)
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_8"
end subroutine processes_8
@ %def processes_8
@
\subsubsection{Multi-channel phase space and structure function}
This is an extension of the previous example. This time, we have two
distinct structure-function channels which are matched to the two
distinct phase-space channels.
<<Processes: execute tests>>=
call test (processes_9, "processes_9", &
"multichannel kinematics and structure functions", &
u, results)
<<Processes: test declarations>>=
public :: processes_9
<<Processes: tests>>=
subroutine processes_9 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
type(process_instance_t), allocatable, target :: process_instance
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
type(sf_config_t), dimension(:), allocatable :: sf_config
type(sf_channel_t) :: sf_channel
real(default), dimension(4) :: x_saved
type(particle_set_t) :: pset
write (u, "(A)") "* Test output: processes_9"
write (u, "(A)") "* Purpose: evaluate a process with &
&structure functions"
write (u, "(A)") "* in a multi-channel configuration"
write (u, "(A)")
write (u, "(A)") "* Build and initialize a process object"
write (u, "(A)")
libname = "processes9"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call reset_interaction_counter ()
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Set beam, structure functions, and mappings"
write (u, "(A)")
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
pdg_in = 25
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (process%get_model_ptr (), pdg_in)
end select
allocate (sf_config (2))
call sf_config(1)%init ([1], data)
call sf_config(2)%init ([2], data)
call process%init_sf_chain (sf_config)
deallocate (sf_config)
call process%configure_phs ()
call process%test_allocate_sf_channels (2)
call sf_channel%init (2)
call process%set_sf_channel (1, sf_channel)
call sf_channel%init (2)
call sf_channel%activate_mapping ([1,2])
call process%set_sf_channel (2, sf_channel)
call process%test_set_component_sf_channel ([1, 2])
write (u, "(A)") "* Complete process initialization"
write (u, "(A)")
call process%setup_mci (dispatch_mci_empty)
call process%setup_terms ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Create a process instance"
write (u, "(A)")
allocate (process_instance)
call process_instance%init (process)
write (u, "(A)") "* Set up kinematics in channel 1 and evaluate"
write (u, "(A)")
call process_instance%choose_mci (1)
call process_instance%evaluate_sqme (1, &
[0.8_default, 0.8_default, 0.1_default, 0.2_default])
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Extract MC input parameters"
write (u, "(A)")
write (u, "(A)") "Channel 1:"
call process_instance%get_mcpar (1, x_saved)
write (u, "(2x,9(1x,F7.5))") x_saved
write (u, "(A)") "Channel 2:"
call process_instance%get_mcpar (2, x_saved)
write (u, "(2x,9(1x,F7.5))") x_saved
write (u, "(A)")
write (u, "(A)") "* Set up kinematics in channel 2 and evaluate"
write (u, "(A)")
call process_instance%evaluate_sqme (2, x_saved)
call process_instance%write (u)
call process_instance%get_trace (pset, 1)
call process_instance%final ()
deallocate (process_instance)
write (u, "(A)")
write (u, "(A)") "* Recover process instance for channel 2"
write (u, "(A)")
call reset_interaction_counter (2)
allocate (process_instance)
call process_instance%init (process)
call process_instance%choose_mci (1)
call process_instance%set_trace (pset, 1, check_match = .false.)
call process_instance%recover &
(channel = 2, i_term = 1, update_sqme = .true., recover_phs = .true.)
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call pset%final ()
call process_instance%final ()
deallocate (process_instance)
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_9"
end subroutine processes_9
@ %def processes_9
@
\subsubsection{Event generation}
Activate the MC integrator for the process object and use it to
generate a single event. Note that the test integrator does not
require integration in preparation for generating events.
<<Processes: execute tests>>=
call test (processes_10, "processes_10", &
"event generation", &
u, results)
<<Processes: test declarations>>=
public :: processes_10
<<Processes: tests>>=
subroutine processes_10 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(mci_t), pointer :: mci
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
type(process_instance_t), allocatable, target :: process_instance
write (u, "(A)") "* Test output: processes_10"
write (u, "(A)") "* Purpose: generate events for a process without &
&structure functions"
write (u, "(A)") "* in a multi-channel configuration"
write (u, "(A)")
write (u, "(A)") "* Build and initialize a process object"
write (u, "(A)")
libname = "processes10"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call reset_interaction_counter ()
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Prepare a trivial beam setup"
write (u, "(A)")
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
call process%configure_phs ()
call process%setup_mci (dispatch_mci_test10)
write (u, "(A)") "* Complete process initialization"
write (u, "(A)")
call process%setup_terms ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Create a process instance"
write (u, "(A)")
allocate (process_instance)
call process_instance%init (process)
write (u, "(A)") "* Generate weighted event"
write (u, "(A)")
call process%test_get_mci_ptr (mci)
select type (mci)
type is (mci_test_t)
! This ensures that the next 'random' numbers are 0.3, 0.5, 0.7
call mci%rng%init (3)
! Include the constant PHS factor in the stored maximum of the integrand
call mci%set_max_factor (conv * twopi4 &
/ (2 * sqrt (lambda (sqrts **2, 125._default**2, 125._default**2))))
end select
call process_instance%generate_weighted_event (1)
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Generate unweighted event"
write (u, "(A)")
call process_instance%generate_unweighted_event (1)
call process%test_get_mci_ptr (mci)
select type (mci)
type is (mci_test_t)
write (u, "(A,I0)") " Success in try ", mci%tries
write (u, "(A)")
end select
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process_instance%final ()
deallocate (process_instance)
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_10"
end subroutine processes_10
@ %def processes_10
@ MCI record with some contents.
<<Processes: test auxiliary>>=
subroutine dispatch_mci_test10 (mci, var_list, process_id, is_nlo)
class(mci_t), allocatable, intent(out) :: mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
logical, intent(in), optional :: is_nlo
allocate (mci_test_t :: mci)
select type (mci)
type is (mci_test_t); call mci%set_divisions (100)
end select
end subroutine dispatch_mci_test10
-
+
@ %def dispatch_mci_test10
@
\subsubsection{Integration}
Activate the MC integrator for the process object and use it to
integrate over phase space.
<<Processes: execute tests>>=
call test (processes_11, "processes_11", &
"integration", &
u, results)
<<Processes: test declarations>>=
public :: processes_11
<<Processes: tests>>=
subroutine processes_11 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(mci_t), allocatable :: mci_template
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
type(process_instance_t), allocatable, target :: process_instance
write (u, "(A)") "* Test output: processes_11"
write (u, "(A)") "* Purpose: integrate a process without &
&structure functions"
write (u, "(A)") "* in a multi-channel configuration"
write (u, "(A)")
write (u, "(A)") "* Build and initialize a process object"
write (u, "(A)")
libname = "processes11"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call reset_interaction_counter ()
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Prepare a trivial beam setup"
write (u, "(A)")
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
call process%configure_phs ()
call process%setup_mci (dispatch_mci_test10)
write (u, "(A)") "* Complete process initialization"
write (u, "(A)")
call process%setup_terms ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Create a process instance"
write (u, "(A)")
allocate (process_instance)
call process_instance%init (process)
write (u, "(A)") "* Integrate with default test parameters"
write (u, "(A)")
call process_instance%integrate (1, n_it=1, n_calls=10000)
call process%final_integration (1)
call process%write (.false., u)
write (u, "(A)")
write (u, "(A,ES13.7)") " Integral divided by phs factor = ", &
process%get_integral (1) &
/ process_instance%term(1)%k_term%phs_factor
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process_instance%final ()
deallocate (process_instance)
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_11"
end subroutine processes_11
@ %def processes_11
@
\subsubsection{Complete events}
For the purpose of simplifying further tests, we implement a
convenience routine that initializes a process and prepares a single
event. This is a wrapup of the test [[processes_10]].
The procedure is re-exported by the [[processes_ut]] module.
<<Processes: public test auxiliary>>=
public :: prepare_test_process
<<Processes: test auxiliary>>=
subroutine prepare_test_process &
(process, process_instance, model, var_list, run_id)
type(process_t), intent(out), target :: process
type(process_instance_t), intent(out), target :: process_instance
class(model_data_t), intent(in), target :: model
type(var_list_t), intent(inout), optional :: var_list
type(string_t), intent(in), optional :: run_id
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), allocatable, target :: process_model
class(mci_t), pointer :: mci
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
libname = "processes_test"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call reset_interaction_counter ()
allocate (process_model)
call process_model%init (model%get_name (), &
model%get_n_real (), &
model%get_n_complex (), &
model%get_n_field (), &
model%get_n_vtx ())
call process_model%copy_from (model)
call process%init (procname, lib, os_data, process_model, var_list)
if (present (run_id)) call process%set_run_id (run_id)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
call process%configure_phs ()
call process%setup_mci (dispatch_mci_test10)
call process%setup_terms ()
call process_instance%init (process)
call process%test_get_mci_ptr (mci)
select type (mci)
type is (mci_test_t)
! This ensures that the next 'random' numbers are 0.3, 0.5, 0.7
call mci%rng%init (3)
! Include the constant PHS factor in the stored maximum of the integrand
call mci%set_max_factor (conv * twopi4 &
/ (2 * sqrt (lambda (sqrts **2, 125._default**2, 125._default**2))))
end select
call process%reset_library_ptr () ! avoid dangling pointer
call process_model%final ()
end subroutine prepare_test_process
@ %def prepare_test_process
@ Here we do the cleanup of the process and process instance emitted
by the previous routine.
<<Processes: public test auxiliary>>=
public :: cleanup_test_process
<<Processes: test auxiliary>>=
subroutine cleanup_test_process (process, process_instance)
type(process_t), intent(inout) :: process
type(process_instance_t), intent(inout) :: process_instance
call process_instance%final ()
call process%final ()
end subroutine cleanup_test_process
@ %def cleanup_test_process
@
This is the actual test. Prepare the test process and event, fill
all evaluators, and display the results. Use a particle set as
temporary storage, read kinematics and recalculate the event.
<<Processes: execute tests>>=
call test (processes_12, "processes_12", &
"event post-processing", &
u, results)
<<Processes: test declarations>>=
public :: processes_12
<<Processes: tests>>=
subroutine processes_12 (u)
integer, intent(in) :: u
type(process_t), allocatable, target :: process
type(process_instance_t), allocatable, target :: process_instance
type(particle_set_t) :: pset
type(model_data_t), target :: model
write (u, "(A)") "* Test output: processes_12"
write (u, "(A)") "* Purpose: generate a complete partonic event"
write (u, "(A)")
call model%init_test ()
write (u, "(A)") "* Build and initialize process and process instance &
&and generate event"
write (u, "(A)")
allocate (process)
allocate (process_instance)
call prepare_test_process (process, process_instance, model, &
run_id = var_str ("run_12"))
call process_instance%setup_event_data (i_core = 1)
call process%prepare_simulation (1)
call process_instance%init_simulation (1)
call process_instance%generate_weighted_event (1)
call process_instance%evaluate_event_data ()
call process_instance%write (u)
call process_instance%get_trace (pset, 1)
call process_instance%final_simulation (1)
call process_instance%final ()
deallocate (process_instance)
write (u, "(A)")
write (u, "(A)") "* Recover kinematics and recalculate"
write (u, "(A)")
call reset_interaction_counter (2)
allocate (process_instance)
call process_instance%init (process)
call process_instance%setup_event_data ()
call process_instance%choose_mci (1)
call process_instance%set_trace (pset, 1, check_match = .false.)
call process_instance%recover &
(channel = 1, i_term = 1, update_sqme = .true., recover_phs = .true.)
call process_instance%recover_event ()
call process_instance%evaluate_event_data ()
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call cleanup_test_process (process, process_instance)
deallocate (process_instance)
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_12"
end subroutine processes_12
@ %def processes_12
@
\subsubsection{Colored interaction}
This test specifically checks the transformation of process data
(flavor, helicity, and color) into an interaction in a process term.
We use the [[test_t]] process core (which has no nontrivial
particles), but call only the [[is_allowed]] method, which always
returns true.
<<Processes: execute tests>>=
call test (processes_13, "processes_13", &
"colored interaction", &
u, results)
<<Processes: test declarations>>=
public :: processes_13
<<Processes: tests>>=
subroutine processes_13 (u)
integer, intent(in) :: u
type(os_data_t) :: os_data
type(model_data_t), target :: model
type(process_term_t) :: term
class(prc_core_t), allocatable :: core
write (u, "(A)") "* Test output: processes_13"
write (u, "(A)") "* Purpose: initialized a colored interaction"
write (u, "(A)")
write (u, "(A)") "* Set up a process constants block"
write (u, "(A)")
call os_data%init ()
call model%init_sm_test ()
allocate (test_t :: core)
associate (data => term%data)
data%n_in = 2
data%n_out = 3
data%n_flv = 2
data%n_hel = 2
data%n_col = 2
data%n_cin = 2
allocate (data%flv_state (5, 2))
data%flv_state (:,1) = [ 1, 21, 1, 21, 21]
data%flv_state (:,2) = [ 2, 21, 2, 21, 21]
allocate (data%hel_state (5, 2))
data%hel_state (:,1) = [1, 1, 1, 1, 0]
data%hel_state (:,2) = [1,-1, 1,-1, 0]
allocate (data%col_state (2, 5, 2))
data%col_state (:,:,1) = &
reshape ([[1, 0], [2,-1], [3, 0], [2,-3], [0,0]], [2,5])
data%col_state (:,:,2) = &
reshape ([[1, 0], [2,-3], [3, 0], [2,-1], [0,0]], [2,5])
allocate (data%ghost_flag (5, 2))
data%ghost_flag(1:4,:) = .false.
data%ghost_flag(5,:) = .true.
end associate
write (u, "(A)") "* Set up the interaction"
write (u, "(A)")
call reset_interaction_counter ()
call term%setup_interaction (core, model)
call term%int%basic_write (u)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_13"
end subroutine processes_13
@ %def processes_13
@
\subsubsection{MD5 sums}
Configure a process with structure functions (multi-channel) and
compute MD5 sums
<<Processes: execute tests>>=
call test (processes_14, "processes_14", &
"process configuration and MD5 sum", &
u, results)
<<Processes: test declarations>>=
public :: processes_14
<<Processes: tests>>=
subroutine processes_14 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
type(sf_config_t), dimension(:), allocatable :: sf_config
type(sf_channel_t), dimension(3) :: sf_channel
write (u, "(A)") "* Test output: processes_14"
write (u, "(A)") "* Purpose: initialize a process with &
&structure functions"
write (u, "(A)") "* and compute MD5 sum"
write (u, "(A)")
write (u, "(A)") "* Build and initialize a process object"
write (u, "(A)")
libname = "processes7"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib)
call lib%compute_md5sum ()
call model%init_test ()
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_test_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Set beam, structure functions, and mappings"
write (u, "(A)")
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
call process%configure_phs ()
pdg_in = 25
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (process%get_model_ptr (), pdg_in)
end select
call process%test_allocate_sf_channels (3)
allocate (sf_config (2))
call sf_config(1)%init ([1], data)
call sf_config(2)%init ([2], data)
call process%init_sf_chain (sf_config)
deallocate (sf_config)
call sf_channel(1)%init (2)
call process%set_sf_channel (1, sf_channel(1))
call sf_channel(2)%init (2)
call sf_channel(2)%activate_mapping ([1,2])
call process%set_sf_channel (2, sf_channel(2))
call sf_channel(3)%init (2)
call sf_channel(3)%set_s_mapping ([1,2])
call process%set_sf_channel (3, sf_channel(3))
call process%setup_mci (dispatch_mci_empty)
call process%compute_md5sum ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_14"
end subroutine processes_14
@ %def processes_14
@
\subsubsection{Decay Process Evaluation}
Initialize an evaluate a decay process.
<<Processes: execute tests>>=
call test (processes_15, "processes_15", &
"decay process", &
u, results)
<<Processes: test declarations>>=
public :: processes_15
<<Processes: tests>>=
subroutine processes_15 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
type(process_instance_t), allocatable, target :: process_instance
type(particle_set_t) :: pset
write (u, "(A)") "* Test output: processes_15"
write (u, "(A)") "* Purpose: initialize a decay process object"
write (u, "(A)")
write (u, "(A)") "* Build and load a test library with one process"
write (u, "(A)")
libname = "processes15"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib, scattering = .false., &
decay = .true.)
call model%init_test ()
call model%set_par (var_str ("ff"), 0.4_default)
call model%set_par (var_str ("mf"), &
model%get_real (var_str ("ff")) * model%get_real (var_str ("ms")))
write (u, "(A)") "* Initialize a process object"
write (u, "(A)")
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_single_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Prepare a trivial beam setup"
write (u, "(A)")
call process%setup_beams_decay (i_core = 1)
call process%configure_phs ()
call process%setup_mci (dispatch_mci_empty)
write (u, "(A)") "* Complete process initialization"
write (u, "(A)")
call process%setup_terms ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Create a process instance"
write (u, "(A)")
call reset_interaction_counter (3)
allocate (process_instance)
call process_instance%init (process)
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Inject a set of random numbers"
write (u, "(A)")
call process_instance%choose_mci (1)
call process_instance%set_mcpar ([0._default, 0._default])
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Set up hard kinematics"
write (u, "(A)")
call process_instance%select_channel (1)
call process_instance%compute_seed_kinematics ()
call process_instance%compute_hard_kinematics ()
write (u, "(A)") "* Evaluate matrix element and square"
write (u, "(A)")
call process_instance%compute_eff_kinematics ()
call process_instance%evaluate_expressions ()
call process_instance%compute_other_channels ()
call process_instance%evaluate_trace ()
call process_instance%write (u)
call process_instance%get_trace (pset, 1)
call process_instance%final ()
deallocate (process_instance)
write (u, "(A)")
write (u, "(A)") "* Particle content:"
write (u, "(A)")
call write_separator (u)
call pset%write (u)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Recover process instance"
write (u, "(A)")
call reset_interaction_counter (3)
allocate (process_instance)
call process_instance%init (process)
call process_instance%choose_mci (1)
call process_instance%set_trace (pset, 1, check_match = .false.)
call process_instance%recover (1, 1, .true., .true.)
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call pset%final ()
call process_instance%final ()
deallocate (process_instance)
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_15"
end subroutine processes_15
@ %def processes_15
@
\subsubsection{Integration: decay}
Activate the MC integrator for the decay object and use it to
integrate over phase space.
<<Processes: execute tests>>=
call test (processes_16, "processes_16", &
"decay integration", &
u, results)
<<Processes: test declarations>>=
public :: processes_16
<<Processes: tests>>=
subroutine processes_16 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
type(process_instance_t), allocatable, target :: process_instance
write (u, "(A)") "* Test output: processes_16"
write (u, "(A)") "* Purpose: integrate a process without &
&structure functions"
write (u, "(A)") "* in a multi-channel configuration"
write (u, "(A)")
write (u, "(A)") "* Build and initialize a process object"
write (u, "(A)")
libname = "processes16"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib, scattering = .false., &
decay = .true.)
call reset_interaction_counter ()
call model%init_test ()
call model%set_par (var_str ("ff"), 0.4_default)
call model%set_par (var_str ("mf"), &
model%get_real (var_str ("ff")) * model%get_real (var_str ("ms")))
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_single_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Prepare a trivial beam setup"
write (u, "(A)")
call process%setup_beams_decay (i_core = 1)
call process%configure_phs ()
call process%setup_mci (dispatch_mci_test_midpoint)
write (u, "(A)") "* Complete process initialization"
write (u, "(A)")
call process%setup_terms ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Create a process instance"
write (u, "(A)")
allocate (process_instance)
call process_instance%init (process)
write (u, "(A)") "* Integrate with default test parameters"
write (u, "(A)")
call process_instance%integrate (1, n_it=1, n_calls=10000)
call process%final_integration (1)
call process%write (.false., u)
write (u, "(A)")
write (u, "(A,ES13.7)") " Integral divided by phs factor = ", &
process%get_integral (1) &
/ process_instance%term(1)%k_term%phs_factor
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process_instance%final ()
deallocate (process_instance)
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_16"
end subroutine processes_16
@ %def processes_16
@ MCI record prepared for midpoint integrator.
<<Processes: test auxiliary>>=
subroutine dispatch_mci_test_midpoint (mci, var_list, process_id, is_nlo)
class(mci_t), allocatable, intent(out) :: mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
logical, intent(in), optional :: is_nlo
allocate (mci_midpoint_t :: mci)
end subroutine dispatch_mci_test_midpoint
-
+
@ %def dispatch_mci_test_midpoint
@
\subsubsection{Decay Process Evaluation}
Initialize an evaluate a decay process for a moving particle.
<<Processes: execute tests>>=
call test (processes_17, "processes_17", &
"decay of moving particle", &
u, results)
<<Processes: test declarations>>=
public :: processes_17
<<Processes: tests>>=
subroutine processes_17 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
type(process_instance_t), allocatable, target :: process_instance
type(particle_set_t) :: pset
type(flavor_t) :: flv_beam
real(default) :: m, p, E
write (u, "(A)") "* Test output: processes_17"
write (u, "(A)") "* Purpose: initialize a decay process object"
write (u, "(A)")
write (u, "(A)") "* Build and load a test library with one process"
write (u, "(A)")
libname = "processes17"
procname = libname
call os_data%init ()
call prc_test_create_library (libname, lib, scattering = .false., &
decay = .true.)
write (u, "(A)") "* Initialize a process object"
write (u, "(A)")
call model%init_test ()
call model%set_par (var_str ("ff"), 0.4_default)
call model%set_par (var_str ("mf"), &
model%get_real (var_str ("ff")) * model%get_real (var_str ("ms")))
allocate (process)
call process%init (procname, lib, os_data, model)
call process%setup_test_cores ()
allocate (phs_single_config_t :: phs_config_template)
call process%init_components (phs_config_template)
write (u, "(A)") "* Prepare a trivial beam setup"
write (u, "(A)")
call process%setup_beams_decay (rest_frame = .false., i_core = 1)
call process%configure_phs ()
call process%setup_mci (dispatch_mci_empty)
write (u, "(A)") "* Complete process initialization"
write (u, "(A)")
call process%setup_terms ()
call process%write (.false., u)
write (u, "(A)")
write (u, "(A)") "* Create a process instance"
write (u, "(A)")
call reset_interaction_counter (3)
allocate (process_instance)
call process_instance%init (process)
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Set parent momentum and random numbers"
write (u, "(A)")
call process_instance%choose_mci (1)
call process_instance%set_mcpar ([0._default, 0._default])
call flv_beam%init (25, process%get_model_ptr ())
m = flv_beam%get_mass ()
p = 3 * m / 4
E = sqrt (m**2 + p**2)
call process_instance%set_beam_momenta ([vector4_moving (E, p, 3)])
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Set up hard kinematics"
write (u, "(A)")
call process_instance%select_channel (1)
call process_instance%compute_seed_kinematics ()
call process_instance%compute_hard_kinematics ()
write (u, "(A)") "* Evaluate matrix element and square"
write (u, "(A)")
call process_instance%compute_eff_kinematics ()
call process_instance%evaluate_expressions ()
call process_instance%compute_other_channels ()
call process_instance%evaluate_trace ()
call process_instance%write (u)
call process_instance%get_trace (pset, 1)
call process_instance%final ()
deallocate (process_instance)
write (u, "(A)")
write (u, "(A)") "* Particle content:"
write (u, "(A)")
call write_separator (u)
call pset%write (u)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Recover process instance"
write (u, "(A)")
call reset_interaction_counter (3)
allocate (process_instance)
call process_instance%init (process)
call process_instance%choose_mci (1)
call process_instance%set_trace (pset, 1, check_match = .false.)
call process_instance%recover (1, 1, .true., .true.)
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call pset%final ()
call process_instance%final ()
deallocate (process_instance)
call process%final ()
deallocate (process)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_17"
end subroutine processes_17
@ %def processes_17
@
\subsubsection{Resonances in Phase Space}
This test demonstrates the extraction of the resonance-history set from the
generated phase space. We need a nontrivial process, but no matrix element.
This is provided by the [[prc_template]] method, using the [[SM]] model. We
also need the [[phs_wood]] method, otherwise we would not have resonances in
the phase space configuration.
<<Processes: execute tests>>=
call test (processes_18, "processes_18", &
"extract resonance history set", &
u, results)
<<Processes: test declarations>>=
public :: processes_18
<<Processes: tests>>=
subroutine processes_18 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(string_t) :: model_name
type(os_data_t) :: os_data
class(model_data_t), pointer :: model
class(vars_t), pointer :: vars
type(process_t), pointer :: process
type(resonance_history_set_t) :: res_set
integer :: i
write (u, "(A)") "* Test output: processes_18"
write (u, "(A)") "* Purpose: extra resonance histories"
write (u, "(A)")
write (u, "(A)") "* Build and load a test library with one process"
write (u, "(A)")
libname = "processes_18_lib"
procname = "processes_18_p"
call os_data%init ()
call syntax_phs_forest_init ()
model_name = "SM"
model => null ()
call prepare_model (model, model_name, vars)
write (u, "(A)") "* Initialize a process library with one process"
write (u, "(A)")
select type (model)
class is (model_t)
call prepare_resonance_test_library (lib, libname, procname, model, os_data, u)
end select
write (u, "(A)")
write (u, "(A)") "* Initialize a process object with phase space"
allocate (process)
select type (model)
class is (model_t)
call prepare_resonance_test_process (process, lib, procname, model, os_data)
end select
write (u, "(A)")
write (u, "(A)") "* Extract resonance history set"
write (u, "(A)")
call process%extract_resonance_history_set (res_set)
call res_set%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process%final ()
deallocate (process)
call model%final ()
deallocate (model)
call syntax_phs_forest_final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_18"
end subroutine processes_18
@ %def processes_18
@ Auxiliary subroutine that constructs the process library for the above test.
<<Processes: test auxiliary>>=
subroutine prepare_resonance_test_library &
(lib, libname, procname, model, os_data, u)
type(process_library_t), target, intent(out) :: lib
type(string_t), intent(in) :: libname
type(string_t), intent(in) :: procname
type(model_t), intent(in), target :: model
type(os_data_t), intent(in) :: os_data
integer, intent(in) :: u
type(string_t), dimension(:), allocatable :: prt_in, prt_out
class(prc_core_def_t), allocatable :: def
type(process_def_entry_t), pointer :: entry
call lib%init (libname)
allocate (prt_in (2), prt_out (3))
prt_in = [var_str ("e+"), var_str ("e-")]
prt_out = [var_str ("d"), var_str ("ubar"), var_str ("W+")]
allocate (template_me_def_t :: def)
select type (def)
type is (template_me_def_t)
call def%init (model, prt_in, prt_out, unity = .false.)
end select
allocate (entry)
call entry%init (procname, &
model_name = model%get_name (), &
n_in = 2, n_components = 1)
call entry%import_component (1, n_out = size (prt_out), &
prt_in = new_prt_spec (prt_in), &
prt_out = new_prt_spec (prt_out), &
method = var_str ("template"), &
variant = def)
call entry%write (u)
call lib%append (entry)
call lib%configure (os_data)
call lib%write_makefile (os_data, force = .true., verbose = .false.)
call lib%clean (os_data, distclean = .false.)
call lib%write_driver (force = .true.)
call lib%load (os_data)
end subroutine prepare_resonance_test_library
@ %def prepare_resonance_test_library
@ We want a test process which has been initialized up to the point where we
can evaluate the matrix element. This is in fact rather complicated. We copy
the steps from [[integration_setup_process]] in the [[integrate]] module,
which is not available at this point.
<<Processes: test auxiliary>>=
subroutine prepare_resonance_test_process &
(process, lib, procname, model, os_data)
class(process_t), intent(out), target :: process
type(process_library_t), intent(in), target :: lib
type(string_t), intent(in) :: procname
type(model_t), intent(in), target :: model
type(os_data_t), intent(in) :: os_data
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
call process%init (procname, lib, os_data, model)
allocate (phs_wood_config_t :: phs_config_template)
call process%init_components (phs_config_template)
call process%setup_test_cores (type_string = var_str ("template"))
sqrts = 1000
call process%setup_beams_sqrts (sqrts, i_core = 1)
call process%configure_phs ()
call process%setup_mci (dispatch_mci_none)
call process%setup_terms ()
end subroutine prepare_resonance_test_process
@ %def prepare_resonance_test_process
@ MCI record prepared for the none (dummy) integrator.
<<Processes: test auxiliary>>=
subroutine dispatch_mci_none (mci, var_list, process_id, is_nlo)
class(mci_t), allocatable, intent(out) :: mci
type(var_list_t), intent(in) :: var_list
type(string_t), intent(in) :: process_id
logical, intent(in), optional :: is_nlo
allocate (mci_none_t :: mci)
end subroutine dispatch_mci_none
-
+
@ %def dispatch_mci_none
@
\subsubsection{Add after evaluate hook(s)}
Initialize a process and process instance, add a trivial process hook,
choose a sampling point and fill the process instance.
We use the same trivial process as for the previous test. All
momentum and state dependence is trivial, so we just test basic
functionality.
<<Processes: test types>>=
type, extends(process_instance_hook_t) :: process_instance_hook_test_t
integer :: unit
character(len=15) :: name
contains
procedure :: init => process_instance_hook_test_init
procedure :: final => process_instance_hook_test_final
procedure :: evaluate => process_instance_hook_test_evaluate
end type process_instance_hook_test_t
@
<<Processes: test auxiliary>>=
subroutine process_instance_hook_test_init (hook, var_list, instance)
class(process_instance_hook_test_t), intent(inout), target :: hook
type(var_list_t), intent(in) :: var_list
class(process_instance_t), intent(in), target :: instance
end subroutine process_instance_hook_test_init
subroutine process_instance_hook_test_final (hook)
class(process_instance_hook_test_t), intent(inout) :: hook
end subroutine process_instance_hook_test_final
subroutine process_instance_hook_test_evaluate (hook, instance)
class(process_instance_hook_test_t), intent(inout) :: hook
class(process_instance_t), intent(in), target :: instance
write (hook%unit, "(A)") "Execute hook:"
write (hook%unit, "(2X,A,1X,A,I0,A)") hook%name, "(", len (trim (hook%name)), ")"
end subroutine process_instance_hook_test_evaluate
@
<<Processes: execute tests>>=
call test (processes_19, "processes_19", &
"add trivial hooks to a process instance ", &
u, results)
<<Processes: test declarations>>=
public :: processes_19
<<Processes: tests>>=
subroutine processes_19 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
class(model_data_t), pointer :: model
type(process_t), allocatable, target :: process
class(phs_config_t), allocatable :: phs_config_template
real(default) :: sqrts
type(process_instance_t) :: process_instance
class(process_instance_hook_t), allocatable, target :: process_instance_hook, process_instance_hook2
type(particle_set_t) :: pset
write (u, "(A)") "* Test output: processes_19"
write (u, "(A)") "* Purpose: allocate process instance &
&and add an after evaluate hook"
write (u, "(A)")
write (u, "(A)")
write (u, "(A)") "* Allocate a process instance"
write (u, "(A)")
call process_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Allocate hook and add to process instance"
write (u, "(A)")
allocate (process_instance_hook_test_t :: process_instance_hook)
call process_instance%append_after_hook (process_instance_hook)
allocate (process_instance_hook_test_t :: process_instance_hook2)
call process_instance%append_after_hook (process_instance_hook2)
select type (process_instance_hook)
type is (process_instance_hook_test_t)
process_instance_hook%unit = u
process_instance_hook%name = "Hook 1"
end select
select type (process_instance_hook2)
type is (process_instance_hook_test_t)
process_instance_hook2%unit = u
process_instance_hook2%name = "Hook 2"
end select
write (u, "(A)") "* Evaluate matrix element and square"
write (u, "(A)")
call process_instance%evaluate_after_hook ()
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call process_instance_hook%final ()
deallocate (process_instance_hook)
write (u, "(A)")
write (u, "(A)") "* Test output end: processes_19"
end subroutine processes_19
@ %def processes_19
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Process Stacks}
For storing and handling multiple processes, we define process stacks.
These are ordinary stacks where new process entries are pushed onto
the top. We allow for multiple entries with identical process ID, but
distinct run ID.
The implementation is essentially identical to the [[prclib_stacks]] module
above. Unfortunately, Fortran supports no generic programming, so we do not
make use of this fact.
When searching for a specific process ID, we will get (a pointer to)
the topmost process entry with that ID on the stack, which was entered
last. Usually, this is the best version of the process (in terms of
integral, etc.) Thus the stack terminology makes sense.
<<[[process_stacks.f90]]>>=
<<File header>>
module process_stacks
<<Use kinds>>
<<Use strings>>
use io_units
use format_utils, only: write_separator
use diagnostics
use os_interface
use sm_qcd
use model_data
use rng_base
use variables
use observables
use process_libraries
use process
<<Standard module head>>
<<Process stacks: public>>
<<Process stacks: types>>
contains
<<Process stacks: procedures>>
end module process_stacks
@ %def process_stacks
@
\subsection{The process entry type}
A process entry is a process object, augmented by a pointer to the
next entry. We do not need specific methods, all relevant methods are
inherited.
On higher level, processes should be prepared as process entry objects.
<<Process stacks: public>>=
public :: process_entry_t
<<Process stacks: types>>=
type, extends (process_t) :: process_entry_t
type(process_entry_t), pointer :: next => null ()
end type process_entry_t
@ %def process_entry_t
@
\subsection{The process stack type}
For easy conversion and lookup it is useful to store the filling
number in the object. The content is stored as a linked list.
The [[var_list]] component stores process-specific results, so they
can be retrieved as (pseudo) variables.
The process stack can be linked to another one. This allows us to
work with stacks of local scope.
<<Process stacks: public>>=
public :: process_stack_t
<<Process stacks: types>>=
type :: process_stack_t
integer :: n = 0
type(process_entry_t), pointer :: first => null ()
type(var_list_t), pointer :: var_list => null ()
type(process_stack_t), pointer :: next => null ()
contains
<<Process stacks: process stack: TBP>>
end type process_stack_t
@ %def process_stack_t
@ Finalize partly: deallocate the process stack and variable list
entries, but keep the variable list as an empty object. This way, the
variable list links are kept.
<<Process stacks: process stack: TBP>>=
procedure :: clear => process_stack_clear
<<Process stacks: procedures>>=
subroutine process_stack_clear (stack)
class(process_stack_t), intent(inout) :: stack
type(process_entry_t), pointer :: process
if (associated (stack%var_list)) then
call stack%var_list%final ()
end if
do while (associated (stack%first))
process => stack%first
stack%first => process%next
call process%final ()
deallocate (process)
end do
stack%n = 0
end subroutine process_stack_clear
@ %def process_stack_clear
@ Finalizer. Clear and deallocate the variable list.
<<Process stacks: process stack: TBP>>=
procedure :: final => process_stack_final
<<Process stacks: procedures>>=
subroutine process_stack_final (object)
class(process_stack_t), intent(inout) :: object
call object%clear ()
if (associated (object%var_list)) then
deallocate (object%var_list)
end if
end subroutine process_stack_final
@ %def process_stack_final
@ Output. The processes on the stack will be ordered LIFO, i.e.,
backwards.
<<Process stacks: process stack: TBP>>=
procedure :: write => process_stack_write
<<Process stacks: procedures>>=
recursive subroutine process_stack_write (object, unit, pacify)
class(process_stack_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: pacify
type(process_entry_t), pointer :: process
integer :: u
u = given_output_unit (unit)
call write_separator (u, 2)
select case (object%n)
case (0)
write (u, "(1x,A)") "Process stack: [empty]"
call write_separator (u, 2)
case default
write (u, "(1x,A)") "Process stack:"
process => object%first
do while (associated (process))
call process%write (.false., u, pacify = pacify)
process => process%next
end do
end select
if (associated (object%next)) then
write (u, "(1x,A)") "[Processes from context environment:]"
call object%next%write (u, pacify)
end if
end subroutine process_stack_write
@ %def process_stack_write
@ The variable list is printed by a separate routine, since
it should be linked to the global variable list, anyway.
<<Process stacks: process stack: TBP>>=
procedure :: write_var_list => process_stack_write_var_list
<<Process stacks: procedures>>=
subroutine process_stack_write_var_list (object, unit)
class(process_stack_t), intent(in) :: object
integer, intent(in), optional :: unit
if (associated (object%var_list)) then
call var_list_write (object%var_list, unit)
end if
end subroutine process_stack_write_var_list
@ %def process_stack_write_var_list
@ Short output.
Since this is a stack, the default output ordering for each stack will be
last-in, first-out. To enable first-in, first-out, which is more likely to be
requested, there is an optional [[fifo]] argument.
<<Process stacks: process stack: TBP>>=
procedure :: show => process_stack_show
<<Process stacks: procedures>>=
recursive subroutine process_stack_show (object, unit, fifo)
class(process_stack_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: fifo
type(process_entry_t), pointer :: process
logical :: reverse
integer :: u, i, j
u = given_output_unit (unit)
reverse = .false.; if (present (fifo)) reverse = fifo
select case (object%n)
case (0)
case default
if (.not. reverse) then
process => object%first
do while (associated (process))
call process%show (u, verbose=.false.)
process => process%next
- end do
+ end do
else
do i = 1, object%n
process => object%first
do j = 1, object%n - i
process => process%next
end do
call process%show (u, verbose=.false.)
end do
end if
end select
if (associated (object%next)) call object%next%show ()
end subroutine process_stack_show
@ %def process_stack_show
@
\subsection{Link}
Link the current process stack to a global one.
<<Process stacks: process stack: TBP>>=
procedure :: link => process_stack_link
<<Process stacks: procedures>>=
subroutine process_stack_link (local_stack, global_stack)
class(process_stack_t), intent(inout) :: local_stack
type(process_stack_t), intent(in), target :: global_stack
local_stack%next => global_stack
end subroutine process_stack_link
@ %def process_stack_link
@ Initialize the process variable list and link the main variable list
to it.
<<Process stacks: process stack: TBP>>=
procedure :: init_var_list => process_stack_init_var_list
<<Process stacks: procedures>>=
subroutine process_stack_init_var_list (stack, var_list)
class(process_stack_t), intent(inout) :: stack
type(var_list_t), intent(inout), optional :: var_list
allocate (stack%var_list)
if (present (var_list)) call var_list%link (stack%var_list)
end subroutine process_stack_init_var_list
@ %def process_stack_init_var_list
@ Link the process variable list to a global
variable list.
<<Process stacks: process stack: TBP>>=
procedure :: link_var_list => process_stack_link_var_list
<<Process stacks: procedures>>=
subroutine process_stack_link_var_list (stack, var_list)
class(process_stack_t), intent(inout) :: stack
type(var_list_t), intent(in), target :: var_list
call stack%var_list%link (var_list)
end subroutine process_stack_link_var_list
@ %def process_stack_link_var_list
@
\subsection{Push}
We take a process pointer and push it onto the stack. The previous
pointer is nullified. Subsequently, the process is `owned' by the
stack and will be finalized when the stack is deleted.
<<Process stacks: process stack: TBP>>=
procedure :: push => process_stack_push
<<Process stacks: procedures>>=
subroutine process_stack_push (stack, process)
class(process_stack_t), intent(inout) :: stack
type(process_entry_t), intent(inout), pointer :: process
process%next => stack%first
stack%first => process
process => null ()
stack%n = stack%n + 1
end subroutine process_stack_push
@ %def process_stack_push
@ Inverse: Remove the last process pointer in the list and return it.
<<Process stacks: process stack: TBP>>=
procedure :: pop_last => process_stack_pop_last
<<Process stacks: procedures>>=
subroutine process_stack_pop_last (stack, process)
class(process_stack_t), intent(inout) :: stack
type(process_entry_t), intent(inout), pointer :: process
type(process_entry_t), pointer :: previous
integer :: i
select case (stack%n)
case (:0)
process => null ()
case (1)
process => stack%first
stack%first => null ()
stack%n = 0
case (2:)
process => stack%first
do i = 2, stack%n
previous => process
process => process%next
end do
previous%next => null ()
stack%n = stack%n - 1
end select
end subroutine process_stack_pop_last
-
+
@ %def process_stack_pop_last
@ Initialize process variables for a given process ID, without setting
values.
<<Process stacks: process stack: TBP>>=
procedure :: init_result_vars => process_stack_init_result_vars
<<Process stacks: procedures>>=
subroutine process_stack_init_result_vars (stack, id)
class(process_stack_t), intent(inout) :: stack
type(string_t), intent(in) :: id
call var_list_init_num_id (stack%var_list, id)
call var_list_init_process_results (stack%var_list, id)
end subroutine process_stack_init_result_vars
@ %def process_stack_init_result_vars
@ Fill process variables with values. This is executed after the
integration pass.
Note: We set only integral and error. With multiple MCI records
possible, the results for [[n_calls]], [[chi2]] etc. are not
necessarily unique. (We might set the efficiency, though.)
<<Process stacks: process stack: TBP>>=
procedure :: fill_result_vars => process_stack_fill_result_vars
<<Process stacks: procedures>>=
subroutine process_stack_fill_result_vars (stack, id)
class(process_stack_t), intent(inout) :: stack
type(string_t), intent(in) :: id
type(process_t), pointer :: process
process => stack%get_process_ptr (id)
if (associated (process)) then
call var_list_init_num_id (stack%var_list, id, process%get_num_id ())
if (process%has_integral ()) then
call var_list_init_process_results (stack%var_list, id, &
integral = process%get_integral (), &
error = process%get_error ())
end if
else
call msg_bug ("process_stack_fill_result_vars: unknown process ID")
end if
end subroutine process_stack_fill_result_vars
@ %def process_stack_fill_result_vars
@ If one of the result variables has a local image in [[var_list_local]],
update the value there as well.
<<Process stacks: process stack: TBP>>=
procedure :: update_result_vars => process_stack_update_result_vars
<<Process stacks: procedures>>=
subroutine process_stack_update_result_vars (stack, id, var_list_local)
class(process_stack_t), intent(inout) :: stack
type(string_t), intent(in) :: id
type(var_list_t), intent(inout) :: var_list_local
call update ("integral(" // id // ")")
call update ("error(" // id // ")")
contains
subroutine update (var_name)
type(string_t), intent(in) :: var_name
real(default) :: value
if (var_list_local%contains (var_name, follow_link = .false.)) then
value = stack%var_list%get_rval (var_name)
call var_list_local%set_real (var_name, value, is_known = .true.)
end if
end subroutine update
end subroutine process_stack_update_result_vars
@ %def process_stack_update_result_vars
@
\subsection{Data Access}
Tell if a process exists.
<<Process stacks: process stack: TBP>>=
procedure :: exists => process_stack_exists
<<Process stacks: procedures>>=
function process_stack_exists (stack, id) result (flag)
class(process_stack_t), intent(in) :: stack
type(string_t), intent(in) :: id
logical :: flag
type(process_t), pointer :: process
process => stack%get_process_ptr (id)
flag = associated (process)
end function process_stack_exists
@ %def process_stack_exists
@ Return a pointer to a process with specific ID. Look also at a
linked stack, if necessary.
<<Process stacks: process stack: TBP>>=
procedure :: get_process_ptr => process_stack_get_process_ptr
<<Process stacks: procedures>>=
recursive function process_stack_get_process_ptr (stack, id) result (ptr)
class(process_stack_t), intent(in) :: stack
type(string_t), intent(in) :: id
type(process_t), pointer :: ptr
type(process_entry_t), pointer :: entry
ptr => null ()
entry => stack%first
do while (associated (entry))
if (entry%get_id () == id) then
ptr => entry%process_t
return
end if
entry => entry%next
end do
if (associated (stack%next)) ptr => stack%next%get_process_ptr (id)
end function process_stack_get_process_ptr
@ %def process_stack_get_process_ptr
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[process_stacks_ut.f90]]>>=
<<File header>>
module process_stacks_ut
use unit_tests
use process_stacks_uti
<<Standard module head>>
<<Process stacks: public test>>
contains
<<Process stacks: test driver>>
end module process_stacks_ut
@ %def process_stacks_ut
@
<<[[process_stacks_uti.f90]]>>=
<<File header>>
module process_stacks_uti
<<Use strings>>
use os_interface
use sm_qcd
use models
use model_data
use variables, only: var_list_t
use process_libraries
use rng_base
use prc_test, only: prc_test_create_library
use process, only: process_t
use instances, only: process_instance_t
use processes_ut, only: prepare_test_process
use process_stacks
use rng_base_ut, only: rng_test_factory_t
<<Standard module head>>
<<Process stacks: test declarations>>
contains
<<Process stacks: tests>>
end module process_stacks_uti
@ %def process_stacks_uti
@ API: driver for the unit tests below.
<<Process stacks: public test>>=
public :: process_stacks_test
<<Process stacks: test driver>>=
subroutine process_stacks_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Process stacks: execute tests>>
end subroutine process_stacks_test
@ %def process_stacks_test
@
\subsubsection{Write an empty process stack}
The most trivial test is to write an uninitialized process stack.
<<Process stacks: execute tests>>=
call test (process_stacks_1, "process_stacks_1", &
"write an empty process stack", &
u, results)
<<Process stacks: test declarations>>=
public :: process_stacks_1
<<Process stacks: tests>>=
subroutine process_stacks_1 (u)
integer, intent(in) :: u
type(process_stack_t) :: stack
write (u, "(A)") "* Test output: process_stacks_1"
write (u, "(A)") "* Purpose: display an empty process stack"
write (u, "(A)")
call stack%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: process_stacks_1"
end subroutine process_stacks_1
@ %def process_stacks_1
@
\subsubsection{Fill a process stack}
Fill a process stack with two (identical) processes.
<<Process stacks: execute tests>>=
call test (process_stacks_2, "process_stacks_2", &
"fill a process stack", &
u, results)
<<Process stacks: test declarations>>=
public :: process_stacks_2
<<Process stacks: tests>>=
subroutine process_stacks_2 (u)
integer, intent(in) :: u
type(process_stack_t) :: stack
type(process_library_t), target :: lib
type(string_t) :: libname
type(string_t) :: procname
type(os_data_t) :: os_data
type(model_t), target :: model
type(var_list_t) :: var_list
type(process_entry_t), pointer :: process => null ()
write (u, "(A)") "* Test output: process_stacks_2"
write (u, "(A)") "* Purpose: fill a process stack"
write (u, "(A)")
write (u, "(A)") "* Build, initialize and store two test processes"
write (u, "(A)")
libname = "process_stacks2"
procname = libname
-
+
call os_data%init ()
call prc_test_create_library (libname, lib)
call model%init_test ()
call var_list%append_string (var_str ("$run_id"))
call var_list%append_log (var_str ("?alphas_is_fixed"), .true.)
call var_list%append_int (var_str ("seed"), 0)
allocate (process)
call var_list%set_string &
(var_str ("$run_id"), var_str ("run1"), is_known=.true.)
call process%init (procname, lib, os_data, model, var_list)
call stack%push (process)
allocate (process)
call var_list%set_string &
(var_str ("$run_id"), var_str ("run2"), is_known=.true.)
call process%init (procname, lib, os_data, model, var_list)
call stack%push (process)
call stack%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call stack%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: process_stacks_2"
end subroutine process_stacks_2
@ %def process_stacks_2
@
\subsubsection{Fill a process stack}
Fill a process stack with two (identical) processes.
<<Process stacks: execute tests>>=
call test (process_stacks_3, "process_stacks_3", &
"process variables", &
u, results)
<<Process stacks: test declarations>>=
public :: process_stacks_3
<<Process stacks: tests>>=
subroutine process_stacks_3 (u)
integer, intent(in) :: u
type(process_stack_t) :: stack
type(model_t), target :: model
type(string_t) :: procname
type(process_entry_t), pointer :: process => null ()
type(process_instance_t), target :: process_instance
write (u, "(A)") "* Test output: process_stacks_3"
write (u, "(A)") "* Purpose: setup process variables"
write (u, "(A)")
write (u, "(A)") "* Initialize process variables"
write (u, "(A)")
procname = "processes_test"
call model%init_test ()
write (u, "(A)") "* Initialize process variables"
write (u, "(A)")
call stack%init_var_list ()
call stack%init_result_vars (procname)
call stack%write_var_list (u)
write (u, "(A)")
write (u, "(A)") "* Build and integrate a test process"
write (u, "(A)")
allocate (process)
call prepare_test_process (process%process_t, process_instance, model)
call process_instance%integrate (1, 1, 1000)
call process_instance%final ()
call process%final_integration (1)
call stack%push (process)
write (u, "(A)") "* Fill process variables"
write (u, "(A)")
call stack%fill_result_vars (procname)
call stack%write_var_list (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call stack%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: process_stacks_3"
end subroutine process_stacks_3
@ %def process_stacks_3
@
\subsubsection{Linked a process stack}
Fill two process stack, linked to each other.
<<Process stacks: execute tests>>=
call test (process_stacks_4, "process_stacks_4", &
"linked stacks", &
u, results)
<<Process stacks: test declarations>>=
public :: process_stacks_4
<<Process stacks: tests>>=
subroutine process_stacks_4 (u)
integer, intent(in) :: u
type(process_library_t), target :: lib
type(process_stack_t), target :: stack1, stack2
type(model_t), target :: model
type(string_t) :: libname
type(string_t) :: procname1, procname2
type(os_data_t) :: os_data
type(process_entry_t), pointer :: process => null ()
write (u, "(A)") "* Test output: process_stacks_4"
write (u, "(A)") "* Purpose: link process stacks"
write (u, "(A)")
write (u, "(A)") "* Initialize process variables"
write (u, "(A)")
libname = "process_stacks_4_lib"
procname1 = "process_stacks_4a"
procname2 = "process_stacks_4b"
call os_data%init ()
write (u, "(A)") "* Initialize first process"
write (u, "(A)")
call prc_test_create_library (procname1, lib)
call model%init_test ()
allocate (process)
call process%init (procname1, lib, os_data, model)
call stack1%push (process)
write (u, "(A)") "* Initialize second process"
write (u, "(A)")
call stack2%link (stack1)
call prc_test_create_library (procname2, lib)
allocate (process)
call process%init (procname2, lib, os_data, model)
call stack2%push (process)
write (u, "(A)") "* Show linked stacks"
write (u, "(A)")
call stack2%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call stack2%final ()
call stack1%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: process_stacks_4"
end subroutine process_stacks_4
@ %def process_stacks_4
@
Index: trunk/src/fks/fks.nw
===================================================================
--- trunk/src/fks/fks.nw (revision 8293)
+++ trunk/src/fks/fks.nw (revision 8294)
@@ -1,9779 +1,9662 @@
% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: matrix elements and process libraries
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{FKS Subtraction Scheme}
\includemodulegraph{fks}
The code in this chapter implements the FKS subtraction scheme for use
with \whizard.
These are the modules:
\begin{description}
\item[fks\_regions]
Given a process definition, identify singular regions in the
associated phase space.
\item[virtual]
Handle the virtual correction matrix element.
\item[real\_subtraction]
Handle the real-subtraction matrix element.
\item[nlo\_data]
Manage the subtraction objects.
\end{description}
This chapter deals with next-to-leading order contributions to cross sections.
Basically, there are three major issues to be adressed: The creation
of the $N+1$-particle flavor structure, the construction of the
$N+1$-particle phase space and the actual calculation of the real- and
virtual-subtracted matrix elements. The first is dealt with using the
[[auto_components]] class, and it will be shown that the second
and third issue are connected in FKS subtraction.
\section{Brief outline of FKS subtraction}
{\em In the current state, this discussion is only concerned with
lepton collisions. For hadron collisions, renormalization of parton
distributions has to be taken into account. Further, for QCD
corrections, initial-state radiation is necessarily
present. However, most quantities have so far been only constructed
for final-state emissions}
The aim is to calculate the next-to-leading order cross section
according to
\begin{equation*}
d\sigma_{\rm{NLO}} = \mathcal{B} + \mathcal{V} +
\mathcal{R}d\Phi_{\rm{rad}}.
\end{equation*}
Analytically, the divergences, in terms of poles in the complex
quantity $\varepsilon = 2-d/2$, cancel. However, this is in general
only valid in an arbitrary, comlex number of dimensions. This is,
roughly, the content of the KLN-theorem. \whizard, as any
other numerical program, is confined to four dimensions. We will
assume that the KLN-theorem is valid and that there exist subtraction
terms $\mathcal{C}$ such that
\begin{equation*}
d\sigma_{\rm{NLO}} = \mathcal{B} + \underbrace{\mathcal{V} +
\mathcal{C}}_{\text{finite}} + \underbrace{\mathcal{R} -
\mathcal{C}}_{\text{finite}},
\end{equation*}
i.e. the subtraction terms correspond to the divergent limits of the
real and virtual matrix element.
Because $\mathcal{C}$ subtracts the divergences of $\mathcal{R}$ as
well as those of $\mathcal{V}$, it suffices to consider one of them,
so we focus on $\mathcal{R}$. For this purpose, $\mathcal{R}$ is
rewritten,
\begin{equation*}
\mathcal{R} = \frac{1}{\xi^2}\frac{1}{1-y} \left(\xi^2
(1-y)\mathcal{R}\right) =
\frac{1}{\xi^2}\frac{1}{1-y}\tilde{\mathcal{R}},
\end{equation*}
with $\xi = \left(2k_{\rm{rad}}^0\right)/\sqrt{s}$ and $y =
\cos\theta$, where $k_{\rm{rad}}^0$ denotes the energy of the radiated
parton and $\theta$ is the angle between emitter and radiated
parton. $\tilde{\mathcal{R}}$ is finite, therefore the whole
singularity structure is contained in the prefactor
$\xi^{-2}(1-y)^{-1}$. Combined with the d-dimensional phase space
element,
\begin{equation*}
\frac{d^{d-1}k}{2k^0(2\pi)^{d-1}} =
\frac{s^{1-\varepsilon}}{(4\pi)^{d-1}}\xi^{1-2\varepsilon}\left(1-y^2\right)^{-\varepsilon}
d\xi dy d\Omega^{d-2},
\end{equation*}
this yields
\begin{equation*}
d\Phi_{\rm{rad}} \mathcal{R} = dy (1-y)^{-1-\varepsilon} d\xi
\xi^{-1-2\varepsilon} \tilde{R}.
\end{equation*}
This can further be rewritten in terms of plus-distributions,
\begin{align*}
\xi^{-1-2\varepsilon} &= -\frac{1}{2\varepsilon}\delta(\xi) +
\left(\frac{1}{\xi}\right)_+ -
2\varepsilon\left(\frac{\log\xi}{\xi}\right)_+ +
\mathcal{O}(\varepsilon^2),\\
(1-y)^{-1-\varepsilon} &= -\frac{2^{-\varepsilon}}{\varepsilon}
\delta(1-y) + \left(\frac{1}{1-y}\right)_+ - \varepsilon
\left(\frac{1}{1-y}\right)_+\log(1-y) + \mathcal{O}(\varepsilon^2),
\end{align*}
(imagine that all this is written inside of integrals, which are
spared for ease of notation) such that
\begin{align*}
d\Phi_{\rm{rad}} \mathcal{R} &= -\frac{1}{2\varepsilon} dy
(1-y)^{-1-\varepsilon}\tilde{R} (0,y) -
d\xi\left[\frac{2^{-\varepsilon}}{\varepsilon}\left(\frac{1}{\xi}\right)_+
- 2\left(\frac{\log\xi}{\xi}\right)_+\right] \tilde{R}(\xi,1) \\
&+ dy d\xi \left(\frac{1}{\xi}\right)_+
\left(\frac{1}{1-y}\right)_+
\tilde{R}(\xi, y) +
\mathcal{O}(\varepsilon).\\
\end{align*}
The summand in the second line is of order $\mathcal{O}(1)$ and is the
only one to reproduce $\mathcal{R}(\xi,y)$. It thus constitutes the
sum of the real matrix element and the corresponding counterterms.
The first summand consequently consists of the subtraction terms to
the virtual matrix elements. Above formula thus allows to calculate
all quantities to render the matrix elements finite.
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Identifying singular regions}
In the FKS subtraction scheme, the phase space is decomposed into
disjoint singular regions, such that
\begin{equation}
\label{eq:S_complete}
\sum_i \mathcal{S}_i + \sum_{ij}\mathcal{S}_{ij} = 1.
\end{equation}
The quantities $\mathcal{S}_i$ and $\mathcal{S}_{ij}$ are functions of
phase space corresponding to a pair of particles indices which can
make up a divergent phase space region. We call such an index pair a
fundamental tuple. For example, the process $e^+ \, e^- \rightarrow u
\, \bar{u} \, g$ has two singular regions, $(3,5)$ and $(4,5)$,
indicating that the gluon can be soft or collinear with respect to
either the quark or the anti-quark. Therefore, the functions $S_{ij}$
have to be chosen in such a way that their contribution makes up most
of \eqref{eq:S_complete} in phase-space configurations where
(final-state) particle $j$ is collinear to particle $i$ or/and
particle $j$ is soft. The functions $S_i$ is the corresponding
quantity for initial-state divergences.
As a singular region we understand the collection of real flavor
structures associated with an emitter and a list of all possible
fundamental tuples. As an example, consider the process $e^+ \, e^-
\rightarrow u \, \bar{u} \, g$. At next-to-leading order, processes
with an additionally radiated particle have to be considered. In this
case, these are $e^+ \, e^- \rightarrow u \, \bar{u}, \, g \, g$,
and $e^+ \, e^- \rightarrow u \, \bar{u} \, u \, \bar{u}$ (or the same
process with any other quark). Table \ref{table:singular regions} sums
up all possible singular regions for this problem.
\begin{table}
\begin{tabular}{|c|c|c|c|}
\hline
\texttt{alr} & \texttt{flst\_alr} & \texttt{emi} &
\texttt{ftuple\_list}\\ \hline
1 & [-11,11,2,-2,21,21] & 3 & {(3,5), (3,6), (4,5), (4,6), (5,6)} \\ \hline
2 & [-11,11,2,-2,21,21] & 4 & {(3,5), (3,6), (4,5), (4,6), (5,6)} \\ \hline
3 & [-11,11,2,-2,21,21] & 5 & {(3,5), (3,6), (4,5), (4,6), (5,6)} \\ \hline
4 & [-11,11,2,-2,2,-2] & 5 & {(5,6)} \\
\hline
\end{tabular}
\caption{List of singular regions. The particles are represented by
their PDG codes. The third column contains the emitter for the
specific singular region. For the process involving an additional
gluon, the gluon can either be emitted from one of the quarks or
from the first gluon. Each emitter yields the same list of
fundamental tuples, five in total. The last singular region
corresponds to the process where the gluon splits up into two
quarks. Here, there is only one fundamental tuple, corresponding to
a singular configuration of the momenta of the additional quarks.}
\label{table:singular regions}
\end{table}
\\
\begin{table}
\begin{tabular}{|c|c|c|c|}
\hline
\texttt{alr} & \texttt{ftuple} & \texttt{emitter} &
\texttt{flst\_alr} \\ \hline
1 & $(3,5)$ & 5 & [-11,11,-2,21,2,21] \\ \hline
2 & $(4,5)$ & 5 & [-11,11,2,21,-2,21] \\ \hline
3 & $(3,6)$ & 5 & [-11,11,-2,21,2,21] \\ \hline
4 & $(4,6)$ & 5 & [-11,11,2,21,-2,21] \\ \hline
5 & $(5,6)$ & 5 & [-11,11,2,-2,21,21] \\ \hline
6 & $(5,6)$ & 5 & [-11,11,2,-2,2,-2] \\ \hline
\end{tabular}
\caption{Initial list of singular regions}
\label{table:ftuples and flavors}
\end{table}
Thus, during the preparation of a NLO-calculation, the possible
singular regions have to be identified. [[fks_regions.f90]] deals
with this issue.
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{FKS Regions}
<<[[fks_regions.f90]]>>=
<<File header>>
module fks_regions
<<Use kinds>>
use format_utils, only: write_separator
use numeric_utils, only: remove_duplicates_from_int_array
use string_utils, only: str
use io_units
use os_interface
<<Use strings>>
<<Use debug>>
use constants
use permutations
use diagnostics
use flavors
use process_constants
use lorentz
use pdg_arrays
use models
use physics_defs
use resonances, only: resonance_contributors_t, resonance_history_t
use phs_fks, only: phs_identifier_t, check_for_phs_identifier
use nlo_data
<<Standard module head>>
<<fks regions: public>>
<<fks regions: parameters>>
<<fks regions: types>>
<<fks regions: interfaces>>
contains
<<fks regions: procedures>>
end module fks_regions
@ %def fks_regions
@ There are three fundamental splitting types: $q \rightarrow qg$, $g \rightarrow gg$ and
-$g \rightarrow qq$.
+$g \rightarrow qq$ for FSR and additionally $q \rightarrow gq$ for ISR which is different
+from $q \rightarrow qg$ by which particle enters the hard process.
<<fks regions: parameters>>=
integer, parameter :: UNDEFINED_SPLITTING = 0
integer, parameter :: F_TO_FV = 1
integer, parameter :: V_TO_VV = 2
integer, parameter :: V_TO_FF = 3
+ integer, parameter :: F_TO_VF = 4
@
@ We group the indices of the emitting and the radiated particle in
the [[ftuple]]-object.
<<fks regions: public>>=
public :: ftuple_t
<<fks regions: types>>=
type :: ftuple_t
integer, dimension(2) :: ireg = [-1,-1]
integer :: i_res = 0
integer :: splitting_type
logical :: pseudo_isr = .false.
contains
<<fks regions: ftuple: TBP>>
end type ftuple_t
@ %def ftuple_t
@
<<fks regions: interfaces>>=
interface assignment(=)
module procedure ftuple_assign
end interface
interface operator(==)
module procedure ftuple_equal
end interface
interface operator(>)
module procedure ftuple_greater
end interface
interface operator(<)
module procedure ftuple_less
end interface
<<fks regions: procedures>>=
pure subroutine ftuple_assign (ftuple_out, ftuple_in)
type(ftuple_t), intent(out) :: ftuple_out
type(ftuple_t), intent(in) :: ftuple_in
ftuple_out%ireg = ftuple_in%ireg
ftuple_out%i_res = ftuple_in%i_res
ftuple_out%splitting_type = ftuple_in%splitting_type
ftuple_out%pseudo_isr = ftuple_in%pseudo_isr
end subroutine ftuple_assign
@ %def ftuple_assign
@
<<fks regions: procedures>>=
elemental function ftuple_equal (f1, f2) result (value)
logical :: value
type(ftuple_t), intent(in) :: f1, f2
value = all (f1%ireg == f2%ireg) .and. f1%i_res == f2%i_res &
.and. f1%splitting_type == f2%splitting_type &
.and. (f1%pseudo_isr .eqv. f2%pseudo_isr)
end function ftuple_equal
@ %def ftuple_equal
@
<<fks regions: procedures>>=
elemental function ftuple_equal_ireg (f1, f2) result (value)
logical :: value
type(ftuple_t), intent(in) :: f1, f2
value = all (f1%ireg == f2%ireg)
end function ftuple_equal_ireg
@ %def ftuple_equal_ireg
@
<<fks regions: procedures>>=
elemental function ftuple_greater (f1, f2) result (greater)
logical :: greater
type(ftuple_t), intent(in) :: f1, f2
if (f1%ireg(1) == f2%ireg(1)) then
greater = f1%ireg(2) > f2%ireg(2)
else
greater = f1%ireg(1) > f2%ireg(1)
end if
end function ftuple_greater
@ %def ftuple_greater
@
<<fks regions: procedures>>=
elemental function ftuple_less (f1, f2) result (less)
logical :: less
type(ftuple_t), intent(in) :: f1, f2
if (f1%ireg(1) == f2%ireg(1)) then
less = f1%ireg(2) < f2%ireg(2)
else
less = f1%ireg(1) < f2%ireg(1)
end if
end function ftuple_less
@ %def ftuple_less
<<fks regions: procedures>>=
subroutine ftuple_sort_array (ftuple_array, equivalences)
type(ftuple_t), intent(inout), dimension(:), allocatable :: ftuple_array
logical, intent(inout), dimension(:,:), allocatable :: equivalences
type(ftuple_t) :: ftuple_tmp
logical, dimension(:), allocatable :: eq_tmp
integer :: i1, i2, n
n = size (ftuple_array)
allocate (eq_tmp (n))
do i1 = 2, n
i2 = i1
do while (ftuple_array(i2 - 1) > ftuple_array(i2))
ftuple_tmp = ftuple_array(i2 - 1)
eq_tmp = equivalences(i2, :)
ftuple_array(i2 - 1) = ftuple_array(i2)
ftuple_array(i2) = ftuple_tmp
equivalences(i2 - 1, :) = equivalences(i2, :)
equivalences(i2, :) = eq_tmp
i2 = i2 - 1
if (i2 == 1) exit
end do
end do
end subroutine ftuple_sort_array
@ %def ftuple_sort_array
@
<<fks regions: ftuple: TBP>>=
procedure :: write => ftuple_write
<<fks regions: procedures>>=
subroutine ftuple_write (ftuple, unit, newline)
class(ftuple_t), intent(in) :: ftuple
integer, intent(in), optional :: unit
logical, intent(in), optional :: newline
integer :: u
logical :: nl
u = given_output_unit (unit); if (u < 0) return
nl = .true.; if (present(newline)) nl = newline
if (all (ftuple%ireg > -1)) then
if (ftuple%i_res > 0) then
if (nl) then
write (u, "(A1,I1,A1,I1,A1,I1,A1)") &
'(', ftuple%ireg(1), ',', ftuple%ireg(2), ';', ftuple%i_res, ')'
else
write (u, "(A1,I1,A1,I1,A1,I1,A1)", advance = "no") &
'(', ftuple%ireg(1), ',', ftuple%ireg(2), ';', ftuple%i_res, ')'
end if
else
if (nl) then
write (u, "(A1,I1,A1,I1,A1)") &
'(', ftuple%ireg(1), ',', ftuple%ireg(2), ')'
else
write (u, "(A1,I1,A1,I1,A1)", advance = "no") &
'(', ftuple%ireg(1), ',', ftuple%ireg(2), ')'
end if
end if
else
write (u, "(A)") "(Empty)"
end if
end subroutine ftuple_write
@ %def ftuple_write
@
<<fks regions: procedures>>=
function ftuple_string (ftuples, latex)
type(string_t) :: ftuple_string
type(ftuple_t), intent(in), dimension(:) :: ftuples
logical, intent(in) :: latex
integer :: i, nreg
if (latex) then
ftuple_string = var_str ("$\left\{")
else
ftuple_string = var_str ("{")
end if
nreg = size(ftuples)
do i = 1, nreg
if (ftuples(i)%i_res == 0) then
ftuple_string = ftuple_string // var_str ("(") // &
str (ftuples(i)%ireg(1)) // var_str (",") // &
str (ftuples(i)%ireg(2)) // var_str (")")
else
ftuple_string = ftuple_string // var_str ("(") // &
str (ftuples(i)%ireg(1)) // var_str (",") // &
str (ftuples(i)%ireg(2)) // var_str (";") // &
str (ftuples(i)%i_res) // var_str (")")
end if
if (ftuples(i)%pseudo_isr) ftuple_string = ftuple_string // var_str ("*")
if (i < nreg) ftuple_string = ftuple_string // var_str (",")
end do
if (latex) then
ftuple_string = ftuple_string // var_str ("\right\}$")
else
ftuple_string = ftuple_string // var_str ("}")
end if
end function ftuple_string
@ %def ftuple_string
@
<<fks regions: ftuple: TBP>>=
procedure :: get => ftuple_get
<<fks regions: procedures>>=
subroutine ftuple_get (ftuple, pos1, pos2)
class(ftuple_t), intent(in) :: ftuple
integer, intent(out) :: pos1, pos2
pos1 = ftuple%ireg(1)
pos2 = ftuple%ireg(2)
end subroutine ftuple_get
@ %def ftuple_get
@
<<fks regions: ftuple: TBP>>=
procedure :: set => ftuple_set
<<fks regions: procedures>>=
subroutine ftuple_set (ftuple, pos1, pos2)
class(ftuple_t), intent(inout) :: ftuple
integer, intent(in) :: pos1, pos2
ftuple%ireg(1) = pos1
ftuple%ireg(2) = pos2
end subroutine ftuple_set
@ %def ftuple_set
-@
+@ Determines the splitting type for FSR. There are three different types of splittings
+relevant here: $g \to gg$ tagged [[V_TO_VV]], $g \to qq$ tagged [[V_TO_FF]] and
+$q \to qg$ tagged [[F_TO_FV]]. For FSR, there is no need to differentiate between
+$q \to qg$ and $q \to gq$ splittings.
<<fks regions: ftuple: TBP>>=
procedure :: determine_splitting_type_fsr => ftuple_determine_splitting_type_fsr
<<fks regions: procedures>>=
subroutine ftuple_determine_splitting_type_fsr (ftuple, flv, i, j)
class(ftuple_t), intent(inout) :: ftuple
type(flv_structure_t), intent(in) :: flv
integer, intent(in) :: i, j
associate (flst => flv%flst)
if (is_vector (flst(i)) .and. is_vector (flst(j))) then
ftuple%splitting_type = V_TO_VV
else if (flst(i)+flst(j) == 0 &
- .and. is_fermion (abs(flst(i)))) then
+ .and. is_fermion (flst(i))) then
ftuple%splitting_type = V_TO_FF
- else if (is_fermion(abs(flst(i))) .and. is_massless_vector (flst(j)) &
- .or. is_fermion(abs(flst(j))) .and. is_massless_vector (flst(i))) then
+ else if (is_fermion(flst(i)) .and. is_massless_vector (flst(j)) &
+ .or. is_fermion(flst(j)) .and. is_massless_vector (flst(i))) then
ftuple%splitting_type = F_TO_FV
else
ftuple%splitting_type = UNDEFINED_SPLITTING
end if
end associate
end subroutine ftuple_determine_splitting_type_fsr
@ %def ftuple_determine_splitting_type_fsr
-@
+@ Determines the splitting type for ISR. There are four different types of splittings
+relevant here: $g \to gg$ tagged [[V_TO_VV]], $g \to qq$ tagged [[V_TO_FF]], $q \to qg$
+tagged [[F_TO_FV]] and $q \to gq$ tagged [[F_TO_VF]]. The latter two need to be considered
+separately for ISR as they differ with respect to which particle enters the hard process.
+A splitting [[F_TO_FV]] may lead to soft divergences while [[F_TO_VF]] does not.\\
+We also want to emphasize that the splitting type naming convention for ISR names the
+splittings considering backwards evolution. So in the splitting [[V_TO_FF]], it is the
+\textit{gluon} that enteres the hard process!\\
+Special treatment here is required if emitter $0$ is assigned. This is the case only when a
+gluon was radiated from any of the IS particles. In this case, both splittings are soft divergent
+so we can equivalently choose $1$ or $2$ as the emitter here even if both have different flavors.
<<fks regions: ftuple: TBP>>=
procedure :: determine_splitting_type_isr => ftuple_determine_splitting_type_isr
<<fks regions: procedures>>=
subroutine ftuple_determine_splitting_type_isr (ftuple, flv, i, j)
class(ftuple_t), intent(inout) :: ftuple
type(flv_structure_t), intent(in) :: flv
integer, intent(in) :: i, j
integer :: em
em = i; if (i == 0) em = 1
associate (flst => flv%flst)
if (is_vector (flst(em)) .and. is_vector (flst(j))) then
ftuple%splitting_type = V_TO_VV
- else if (is_massless_vector (flst(em)) .and. is_fermion(abs(flst(j)))) then
- ftuple%splitting_type = V_TO_FF
- else if (is_fermion(abs(flst(em))) .and. is_massless_vector (flst(j))) then
+ else if (is_massless_vector(flst(em)) .and. is_fermion(flst(j))) then
+ ftuple%splitting_type = F_TO_VF
+ else if (is_fermion(flst(em)) .and. is_massless_vector(flst(j))) then
ftuple%splitting_type = F_TO_FV
+ else if (is_fermion(flst(em)) .and. is_fermion(flst(j))) then
+ ftuple%splitting_type = V_TO_FF
else
ftuple%splitting_type = UNDEFINED_SPLITTING
end if
end associate
end subroutine ftuple_determine_splitting_type_isr
@ %def ftuple_determine_splitting_type_isr
@ Two debug functions to check the consistency of [[ftuples]]
<<fks regions: ftuple: TBP>>=
procedure :: has_negative_elements => ftuple_has_negative_elements
procedure :: has_identical_elements => ftuple_has_identical_elements
<<fks regions: procedures>>=
elemental function ftuple_has_negative_elements (ftuple) result (value)
logical :: value
class(ftuple_t), intent(in) :: ftuple
value = any (ftuple%ireg < 0)
end function ftuple_has_negative_elements
elemental function ftuple_has_identical_elements (ftuple) result (value)
logical :: value
class(ftuple_t), intent(in) :: ftuple
value = ftuple%ireg(1) == ftuple%ireg(2)
end function ftuple_has_identical_elements
@ %def ftuple_has_negative_elements, ftuple_has_identical_elements
@ Each singular region can have a different number of
emitter-radiation pairs. This is coped with using the linked list
[[ftuple_list]].
<<fks regions: types>>=
type :: ftuple_list_t
integer :: index = 0
type(ftuple_t) :: ftuple
type(ftuple_list_t), pointer :: next => null ()
type(ftuple_list_t), pointer :: prev => null ()
type(ftuple_list_t), pointer :: equiv => null ()
contains
<<fks regions: ftuple list: TBP>>
end type ftuple_list_t
@ %def ftuple_list_t
@
<<fks regions: ftuple list: TBP>>=
procedure :: write => ftuple_list_write
<<fks regions: procedures>>=
subroutine ftuple_list_write (list, unit, verbose)
class(ftuple_list_t), intent(in), target :: list
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
type(ftuple_list_t), pointer :: current
logical :: verb
integer :: u
u = given_output_unit (unit); if (u < 0) return
verb = .false.; if (present (verbose)) verb = verbose
select type (list)
type is (ftuple_list_t)
current => list
do
call current%ftuple%write (unit = u, newline = .false.)
if (verb .and. associated (current%equiv)) write (u, '(A)', advance = "no") "'"
if (associated (current%next)) then
current => current%next
else
exit
end if
end do
write (u, *) ""
end select
end subroutine ftuple_list_write
@ %def ftuple_list_write
@
<<fks regions: ftuple list: TBP>>=
procedure :: append => ftuple_list_append
<<fks regions: procedures>>=
subroutine ftuple_list_append (list, ftuple)
class(ftuple_list_t), intent(inout), target :: list
type(ftuple_t), intent(in) :: ftuple
type(ftuple_list_t), pointer :: current
select type (list)
type is (ftuple_list_t)
if (list%index == 0) then
nullify (list%next)
list%index = 1
list%ftuple = ftuple
else
current => list
do
if (associated (current%next)) then
current => current%next
else
allocate (current%next)
nullify (current%next%next)
nullify (current%next%equiv)
current%next%prev => current
current%next%index = current%index + 1
current%next%ftuple = ftuple
exit
end if
end do
end if
end select
end subroutine ftuple_list_append
@ %def ftuple_list_append
@
<<fks regions: ftuple list: TBP>>=
procedure :: get_n_tuples => ftuple_list_get_n_tuples
<<fks regions: procedures>>=
impure elemental function ftuple_list_get_n_tuples (list) result(n_tuples)
integer :: n_tuples
class(ftuple_list_t), intent(in), target :: list
type(ftuple_list_t), pointer :: current
n_tuples = 0
select type (list)
type is (ftuple_list_t)
current => list
if (current%index > 0) then
n_tuples = 1
do
if (associated (current%next)) then
current => current%next
n_tuples = n_tuples + 1
else
exit
end if
end do
end if
end select
end function ftuple_list_get_n_tuples
@ %def ftuple_list_get_n_tuples
@
<<fks regions: ftuple list: TBP>>=
procedure :: get_entry => ftuple_list_get_entry
<<fks regions: procedures>>=
function ftuple_list_get_entry (list, index) result (entry)
type(ftuple_list_t), pointer :: entry
class(ftuple_list_t), intent(in), target :: list
integer, intent(in) :: index
type(ftuple_list_t), pointer :: current
integer :: i
entry => null()
select type (list)
type is (ftuple_list_t)
current => list
if (index == 1) then
entry => current
else
do i = 1, index - 1
current => current%next
end do
entry => current
end if
end select
end function ftuple_list_get_entry
@ %def ftuple_list_get_entry
@
<<fks regions: ftuple list: TBP>>=
procedure :: get_ftuple => ftuple_list_get_ftuple
<<fks regions: procedures>>=
function ftuple_list_get_ftuple (list, index) result (ftuple)
type(ftuple_t) :: ftuple
class(ftuple_list_t), intent(in), target :: list
integer, intent(in) :: index
type(ftuple_list_t), pointer :: entry
entry => list%get_entry (index)
ftuple = entry%ftuple
end function ftuple_list_get_ftuple
@ %def ftuple_list_get_ftuple
@
<<fks regions: ftuple list: TBP>>=
procedure :: set_equiv => ftuple_list_set_equiv
<<fks regions: procedures>>=
subroutine ftuple_list_set_equiv (list, i1, i2)
class(ftuple_list_t), intent(in) :: list
integer, intent(in) :: i1, i2
type(ftuple_list_t), pointer :: list1, list2 => null ()
select type (list)
type is (ftuple_list_t)
if (list%get_ftuple (i1) > list%get_ftuple (i2)) then
list1 => list%get_entry (i2)
list2 => list%get_entry (i1)
else
list1 => list%get_entry (i1)
list2 => list%get_entry (i2)
end if
do
if (associated (list1%equiv)) then
list1 => list1%equiv
else
exit
end if
end do
list1%equiv => list2
end select
end subroutine ftuple_list_set_equiv
@ %def ftuple_list_set_equiv
@
<<fks regions: ftuple list: TBP>>=
procedure :: check_equiv => ftuple_list_check_equiv
<<fks regions: procedures>>=
function ftuple_list_check_equiv(list, i1, i2) result(eq)
class(ftuple_list_t), intent(in) :: list
integer, intent(in) :: i1, i2
logical :: eq
type(ftuple_list_t), pointer :: current
eq = .false.
select type (list)
type is (ftuple_list_t)
current => list%get_entry (i1)
do
if (associated (current%equiv)) then
current => current%equiv
if (current%index == i2) then
eq = .true.
exit
end if
else
exit
end if
end do
end select
end function ftuple_list_check_equiv
@ %def ftuple_list_sort
@
<<fks regions: ftuple list: TBP>>=
procedure :: to_array => ftuple_list_to_array
<<fks regions: procedures>>=
subroutine ftuple_list_to_array (ftuple_list, ftuple_array, equivalences, ordered)
class(ftuple_list_t), intent(in), target :: ftuple_list
type(ftuple_t), intent(out), dimension(:), allocatable :: ftuple_array
logical, intent(out), dimension(:,:), allocatable :: equivalences
logical, intent(in) :: ordered
integer :: i_tuple, n
type(ftuple_list_t), pointer :: current => null ()
integer :: i1, i2
type(ftuple_t) :: ftuple_tmp
logical, dimension(:), allocatable :: eq_tmp
n = ftuple_list%get_n_tuples ()
allocate (ftuple_array (n), equivalences (n, n))
equivalences = .false.
select type (ftuple_list)
type is (ftuple_list_t)
current => ftuple_list
i_tuple = 1
do
ftuple_array(i_tuple) = current%ftuple
if (associated (current%equiv)) then
i1 = current%index
i2 = current%equiv%index
equivalences (i1, i2) = .true.
end if
if (associated (current%next)) then
current => current%next
i_tuple = i_tuple + 1
else
exit
end if
end do
end select
if (ordered) call ftuple_sort_array (ftuple_array, equivalences)
end subroutine ftuple_list_to_array
@ %def ftuple_list_to_array
@
<<fks regions: procedures>>=
subroutine print_equivalence_matrix (ftuple_array, equivalences)
type(ftuple_t), intent(in), dimension(:) :: ftuple_array
logical, intent(in), dimension(:,:) :: equivalences
integer :: i, i1, i2
print *, 'Equivalence matrix: '
do i = 1, size (ftuple_array)
call ftuple_array(i)%get(i1,i2)
print *, 'i: ', i, '(', i1, i2, '): ', equivalences(i,:)
end do
end subroutine print_equivalence_matrix
@ %def print_equivalence_matrix
@ Class for working with the flavor specification arrays.
<<fks regions: public>>=
public :: flv_structure_t
<<fks regions: types>>=
type :: flv_structure_t
integer, dimension(:), allocatable :: flst
integer, dimension(:), allocatable :: tag
integer :: nlegs = 0
integer :: n_in = 0
logical, dimension(:), allocatable :: massive
logical, dimension(:), allocatable :: colored
real(default), dimension(:), allocatable :: charge
real(default) :: prt_symm_fs = 1._default
contains
<<fks regions: flv structure: TBP>>
end type flv_structure_t
@ %def flv_structure_t
@
Returns \texttt{true} if the two particles at position \texttt{i}
and \texttt{j} in the flavor array can originate from the same
splitting. For this purpose, the function first checks whether the splitting is
allowed at all. If this is the case, the emitter is removed from the
flavor array. If the resulting array is equivalent to the Born flavor
structure \texttt{flv\_born}, the pair is accepted as a valid
-splitting. We first check whether the splitting is possible. The array
+splitting.
+
+We first check whether the splitting is possible. The array
[[flv_orig]] contains all particles which share a vertex with the
-particles at position [[i]] and [[j]]. If its size is equal to zero,
-no splitting is possible and the subroutine is exited. Otherwise,
-we loop over all possible underlying Born flavor structures and check
-if any of them equals the actual underlying Born flavor structure.
-For a quark emitting a gluon, [[flv_orig]] contains the PDG code of
-the anti-quark. To be on the safe side, a second array is created,
+particles at position [[i]] and [[j]]. If any of these particles belongs
+to the initial state, a PDG-ID flip is necessary to correctly recognize
+the vertex. If its size is equal to zero, no splitting is possible and
+the subroutine is exited. Otherwise, we loop over all possible underlying
+Born flavor structures and check if any of them equals the actual underlying
+Born flavor structure. For a quark emitting a gluon, [[flv_orig]] contains
+the PDG code of the anti-quark. To be on the safe side, a second array is created,
which contains both the positively and negatively signed PDG
codes. Then, the origial tuple $(i,j)$ is removed from the real flavor
structure and the particles in [[flv_orig2]] are inserted.
If the resulting Born configuration is equal to the underlying Born
configuration, up to a permutation of final-state particles, the tuple
$(i,j)$ is accepted as valid.
<<fks regions: flv structure: TBP>>=
procedure :: valid_pair => flv_structure_valid_pair
<<fks regions: procedures>>=
function flv_structure_valid_pair &
(flv, i, j, flv_ref, model) result (valid)
logical :: valid
class(flv_structure_t), intent(in) :: flv
integer, intent(in) :: i,j
type(flv_structure_t), intent(in) :: flv_ref
type(model_t), intent(in) :: model
integer :: k, n_orig
type(flv_structure_t) :: flv_test
integer, dimension(:), allocatable :: flv_orig
valid = .false.
if (all ([i, j] <= flv%n_in)) return
- call model%match_vertex (flv%flst(i), flv%flst(j), flv_orig)
+ if (i <= flv%n_in .and. is_fermion(flv%flst(i))) then
+ call model%match_vertex (-flv%flst(i), flv%flst(j), flv_orig)
+ else if (j <= flv%n_in .and. is_fermion(flv%flst(j))) then
+ call model%match_vertex (flv%flst(i), -flv%flst(j), flv_orig)
+ else
+ call model%match_vertex (flv%flst(i), flv%flst(j), flv_orig)
+ end if
n_orig = size (flv_orig)
if (n_orig == 0) then
return
else
do k = 1, n_orig
if (any ([i, j] <= flv%n_in)) then
flv_test = flv%insert_particle_isr (i, j, flv_orig(k))
else
flv_test = flv%insert_particle_fsr (i, j, flv_orig(k))
end if
valid = flv_ref .equiv. flv_test
call flv_test%final ()
if (valid) return
end do
end if
deallocate (flv_orig)
end function flv_structure_valid_pair
@ %def flv_structure_valid_pair
@ This function checks whether two flavor arrays are the same up to a
permutation of the final-state particles
<<fks regions: procedures>>=
function flv_structure_equivalent (flv1, flv2, with_tag) result(equiv)
logical :: equiv
type(flv_structure_t), intent(in) :: flv1, flv2
logical, intent(in) :: with_tag
type(flavor_permutation_t) :: perm
integer :: n
n = size (flv1%flst)
equiv = .true.
if (n /= size (flv2%flst)) then
call msg_fatal &
('flv_structure_equivalent: flavor arrays do not have equal lengths')
else if (flv1%n_in /= flv2%n_in) then
call msg_fatal &
('flv_structure_equivalent: flavor arrays do not have equal n_in')
else
call perm%init (flv1, flv2, flv1%n_in, flv1%nlegs, with_tag)
equiv = perm%test (flv2, flv1, with_tag)
call perm%final ()
end if
end function flv_structure_equivalent
@ %def flv_structure_equivalent
@
<<fks regions: procedures>>=
function flv_structure_equivalent_no_tag (flv1, flv2) result(equiv)
logical :: equiv
type(flv_structure_t), intent(in) :: flv1, flv2
equiv = flv_structure_equivalent (flv1, flv2, .false.)
end function flv_structure_equivalent_no_tag
function flv_structure_equivalent_with_tag (flv1, flv2) result(equiv)
logical :: equiv
type(flv_structure_t), intent(in) :: flv1, flv2
equiv = flv_structure_equivalent (flv1, flv2, .true.)
end function flv_structure_equivalent_with_tag
@ %def flv_structure_equivalent_no_tag, flv_structure_equivalent_with_tag
@
<<fks regions: procedures>>=
pure subroutine flv_structure_assign_flv (flv_out, flv_in)
type(flv_structure_t), intent(out) :: flv_out
type(flv_structure_t), intent(in) :: flv_in
flv_out%nlegs = flv_in%nlegs
flv_out%n_in = flv_in%n_in
flv_out%prt_symm_fs = flv_in%prt_symm_fs
if (allocated (flv_in%flst)) then
allocate (flv_out%flst (size (flv_in%flst)))
flv_out%flst = flv_in%flst
end if
if (allocated (flv_in%tag)) then
allocate (flv_out%tag (size (flv_in%tag)))
flv_out%tag = flv_in%tag
end if
if (allocated (flv_in%massive)) then
allocate (flv_out%massive (size (flv_in%massive)))
flv_out%massive = flv_in%massive
end if
if (allocated (flv_in%colored)) then
allocate (flv_out%colored (size (flv_in%colored)))
flv_out%colored = flv_in%colored
end if
end subroutine flv_structure_assign_flv
@ %def flv_structure_assign_flv
@
<<fks regions: procedures>>=
pure subroutine flv_structure_assign_integer (flv_out, iarray)
type(flv_structure_t), intent(out) :: flv_out
integer, intent(in), dimension(:) :: iarray
integer :: i
flv_out%nlegs = size (iarray)
allocate (flv_out%flst (flv_out%nlegs))
allocate (flv_out%tag (flv_out%nlegs))
flv_out%flst = iarray
flv_out%tag = [(i, i = 1, flv_out%nlegs)]
end subroutine flv_structure_assign_integer
@ %def flv_structure_assign_integer
@ Returs a new flavor array with the particle at position
\texttt{index} removed.
<<fks regions: flv structure: TBP>>=
procedure :: remove_particle => flv_structure_remove_particle
<<fks regions: procedures>>=
function flv_structure_remove_particle (flv, index) result(flv_new)
type(flv_structure_t) :: flv_new
class(flv_structure_t), intent(in) :: flv
integer, intent(in) :: index
integer :: n1, n2
integer :: i, removed_tag
n1 = size (flv%flst); n2 = n1 - 1
allocate (flv_new%flst (n2), flv_new%tag (n2))
flv_new%nlegs = n2
flv_new%n_in = flv%n_in
removed_tag = flv%tag(index)
if (index == 1) then
flv_new%flst(1 : n2) = flv%flst(2 : n1)
flv_new%tag(1 : n2) = flv%tag(2 : n1)
else if (index == n1) then
flv_new%flst(1 : n2) = flv%flst(1 : n2)
flv_new%tag(1 : n2) = flv%tag(1 : n2)
else
flv_new%flst(1 : index - 1) = flv%flst(1 : index - 1)
flv_new%flst(index : n2) = flv%flst(index + 1 : n1)
flv_new%tag(1 : index - 1) = flv%tag(1 : index - 1)
flv_new%tag(index : n2) = flv%tag(index + 1 : n1)
end if
do i = 1, n2
if (flv_new%tag(i) > removed_tag) &
flv_new%tag(i) = flv_new%tag(i) - 1
end do
call flv_new%compute_prt_symm_fs (flv_new%n_in)
end function flv_structure_remove_particle
@ %def flv_structure_remove_particle
-@
+@ Removes the particles at position i1 and i2 and inserts a new
+particle of matching flavor at position i1.
<<fks regions: flv structure: TBP>>=
procedure :: insert_particle_fsr => flv_structure_insert_particle_fsr
<<fks regions: procedures>>=
function flv_structure_insert_particle_fsr (flv, i1, i2, flv_add) result (flv_new)
type(flv_structure_t) :: flv_new
class(flv_structure_t), intent(in) :: flv
integer, intent(in) :: i1, i2, flv_add
if (flv%flst(i1) + flv_add == 0 .or. flv%flst(i2) + flv_add == 0) then
flv_new = flv%insert_particle (i1, i2, -flv_add)
else
flv_new = flv%insert_particle (i1, i2, flv_add)
end if
end function flv_structure_insert_particle_fsr
@ %def flv_structure_insert_particle_fsr
-@ For ISR, the two particles are not exchangable.
+@ Same as [[insert_particle_fsr]] but for ISR, the two particles are not exchangable.
<<fks regions: flv structure: TBP>>=
procedure :: insert_particle_isr => flv_structure_insert_particle_isr
<<fks regions: procedures>>=
function flv_structure_insert_particle_isr (flv, i_in, i_out, flv_add) result (flv_new)
type(flv_structure_t) :: flv_new
class(flv_structure_t), intent(in) :: flv
integer, intent(in) :: i_in, i_out, flv_add
if (flv%flst(i_in) + flv_add == 0) then
flv_new = flv%insert_particle (i_in, i_out, -flv_add)
else
flv_new = flv%insert_particle (i_in, i_out, flv_add)
end if
end function flv_structure_insert_particle_isr
@ %def flv_structure_insert_particle_isr
-@ Removes the paritcles at position i1 and i2 and inserts a new
+@ Removes the particles at position i1 and i2 and inserts a new
particle at position i1.
<<fks regions: flv structure: TBP>>=
procedure :: insert_particle => flv_structure_insert_particle
<<fks regions: procedures>>=
function flv_structure_insert_particle (flv, i1, i2, particle) result (flv_new)
type(flv_structure_t) :: flv_new
class(flv_structure_t), intent(in) :: flv
integer, intent(in) :: i1, i2, particle
type(flv_structure_t) :: flv_tmp
integer :: n1, n2
integer :: new_tag
n1 = size (flv%flst); n2 = n1 - 1
allocate (flv_new%flst (n2), flv_new%tag (n2))
flv_new%nlegs = n2
flv_new%n_in = flv%n_in
new_tag = maxval(flv%tag) + 1
if (i1 < i2) then
flv_tmp = flv%remove_particle (i1)
flv_tmp = flv_tmp%remove_particle (i2 - 1)
else if(i2 < i1) then
flv_tmp = flv%remove_particle(i2)
flv_tmp = flv_tmp%remove_particle(i1 - 1)
else
call msg_fatal ("flv_structure_insert_particle: Indices are identical!")
end if
if (i1 == 1) then
flv_new%flst(1) = particle
flv_new%flst(2 : n2) = flv_tmp%flst(1 : n2 - 1)
flv_new%tag(1) = new_tag
flv_new%tag(2 : n2) = flv_tmp%tag(1 : n2 - 1)
else if (i1 == n1 .or. i1 == n2) then
flv_new%flst(1 : n2 - 1) = flv_tmp%flst(1 : n2 - 1)
flv_new%flst(n2) = particle
flv_new%tag(1 : n2 - 1) = flv_tmp%tag(1 : n2 - 1)
flv_new%tag(n2) = new_tag
else
flv_new%flst(1 : i1 - 1) = flv_tmp%flst(1 : i1 - 1)
flv_new%flst(i1) = particle
flv_new%flst(i1 + 1 : n2) = flv_tmp%flst(i1 : n2 - 1)
flv_new%tag(1 : i1 - 1) = flv_tmp%tag(1 : i1 - 1)
flv_new%tag(i1) = new_tag
flv_new%tag(i1 + 1 : n2) = flv_tmp%tag(i1 : n2 - 1)
end if
call flv_new%compute_prt_symm_fs (flv_new%n_in)
end function flv_structure_insert_particle
@ %def flv_structure_insert_particle
@ Counts the number of occurances of a particle in a
flavor array
<<fks regions: flv structure: TBP>>=
procedure :: count_particle => flv_structure_count_particle
<<fks regions: procedures>>=
function flv_structure_count_particle (flv, part) result (n)
class(flv_structure_t), intent(in) :: flv
integer, intent(in) :: part
integer :: n
n = count (flv%flst == part)
end function flv_structure_count_particle
@ %def flv_structure_count_particle
@ Initializer for flavor structures
<<fks regions: flv structure: TBP>>=
procedure :: init => flv_structure_init
<<fks regions: procedures>>=
subroutine flv_structure_init (flv, aval, n_in, tags)
class(flv_structure_t), intent(inout) :: flv
integer, intent(in), dimension(:) :: aval
integer, intent(in) :: n_in
integer, intent(in), dimension(:), optional :: tags
integer :: i, n
integer, dimension(:), allocatable :: aval_unique
integer, dimension(:), allocatable :: mult
n = size (aval)
allocate (flv%flst (n), flv%tag (n))
flv%flst = aval
if (present (tags)) then
flv%tag = tags
else
do i = 1, n
flv%tag(i) = i
end do
end if
flv%nlegs = n
flv%n_in = n_in
call flv%compute_prt_symm_fs (flv%n_in)
end subroutine flv_structure_init
@ %def flv_structure_init
@
<<fks regions: flv structure: TBP>>=
procedure :: compute_prt_symm_fs => flv_structure_compute_prt_symm_fs
<<fks regions: procedures>>=
subroutine flv_structure_compute_prt_symm_fs (flv, n_in)
class(flv_structure_t), intent(inout) :: flv
integer, intent(in) :: n_in
integer, dimension(:), allocatable :: flst_unique
integer, dimension(:), allocatable :: mult
integer :: i
flst_unique = remove_duplicates_from_int_array (flv%flst(n_in + 1 :))
allocate (mult(size (flst_unique)))
do i = 1, size (flst_unique)
mult(i) = count (flv%flst(n_in + 1 :) == flst_unique(i))
end do
flv%prt_symm_fs = one / product (gamma (real (mult + 1, default)))
end subroutine flv_structure_compute_prt_symm_fs
@ %def flv_structure_compute_prt_symm_fs
@
<<fks regions: flv structure: TBP>>=
procedure :: write => flv_structure_write
<<fks regions: procedures>>=
subroutine flv_structure_write (flv, unit)
class(flv_structure_t), intent(in) :: flv
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, '(A)') char (flv%to_string ())
end subroutine flv_structure_write
@ %def flv_structure_write
@
<<fks regions: flv structure: TBP>>=
procedure :: to_string => flv_structure_to_string
<<fks regions: procedures>>=
function flv_structure_to_string (flv) result (flv_string)
type(string_t) :: flv_string
class(flv_structure_t), intent(in) :: flv
integer :: i, n
if (allocated (flv%flst)) then
flv_string = var_str ("[")
n = size (flv%flst)
do i = 1, n - 1
flv_string = flv_string // str (flv%flst(i)) // var_str(",")
end do
flv_string = flv_string // str (flv%flst(n)) // var_str("]")
else
flv_string = var_str ("[not allocated]")
end if
end function flv_structure_to_string
@ %def flv_structure_to_string
@ Creates the underlying Born flavor structure for a given real flavor
structure if the particle at position \texttt{emitter} is removed
<<fks regions: flv structure: TBP>>=
procedure :: create_uborn => flv_structure_create_uborn
<<fks regions: procedures>>=
function flv_structure_create_uborn (flv, emitter, nlo_correction_type) result(flv_uborn)
type(flv_structure_t) :: flv_uborn
class(flv_structure_t), intent(in) :: flv
type(string_t), intent(in) :: nlo_correction_type
integer, intent(in) :: emitter
integer n_legs
integer :: f1, f2
integer :: gauge_boson
n_legs = size(flv%flst)
allocate (flv_uborn%flst (n_legs - 1), flv_uborn%tag (n_legs - 1))
gauge_boson = determine_gauge_boson_to_be_inserted ()
if (emitter > flv%n_in) then
f1 = flv%flst(n_legs); f2 = flv%flst(n_legs - 1)
if (is_massless_vector (f1)) then
!!! Emitted particle is a gluon or photon => just remove it
flv_uborn = flv%remove_particle(n_legs)
else if (is_fermion (f1) .and. is_fermion (f2) .and. f1 + f2 == 0) then
!!! Emission type is a gauge boson splitting into two fermions
flv_uborn = flv%insert_particle(n_legs - 1, n_legs, gauge_boson)
else
call msg_error ("Create underlying Born: Unsupported splitting type.")
call msg_error (char (str (flv%flst)))
call msg_fatal ("FKS - FAIL")
end if
else if (emitter > 0) then
f1 = flv%flst(n_legs); f2 = flv%flst(emitter)
if (is_massless_vector (f1)) then
flv_uborn = flv%remove_particle(n_legs)
else if (is_fermion (f1) .and. is_massless_vector (f2)) then
flv_uborn = flv%insert_particle (emitter, n_legs, -f1)
else if (is_fermion (f1) .and. is_fermion (f2) .and. f1 == f2) then
flv_uborn = flv%insert_particle(emitter, n_legs, gauge_boson)
end if
else
flv_uborn = flv%remove_particle (n_legs)
end if
contains
integer function determine_gauge_boson_to_be_inserted ()
select case (char (nlo_correction_type))
case ("QCD")
determine_gauge_boson_to_be_inserted = GLUON
case ("QED")
determine_gauge_boson_to_be_inserted = PHOTON
case ("Full")
call msg_fatal ("NLO correction type 'Full' not yet implemented!")
case default
call msg_fatal ("Invalid NLO correction type! Valid inputs are: QCD, QED, Full (default: QCD)")
end select
end function determine_gauge_boson_to_be_inserted
end function flv_structure_create_uborn
@ %def flv_structure_create_uborn
@
<<fks regions: flv structure: TBP>>=
procedure :: init_mass_color_and_charge => flv_structure_init_mass_color_and_charge
<<fks regions: procedures>>=
subroutine flv_structure_init_mass_color_and_charge (flv, model)
class(flv_structure_t), intent(inout) :: flv
type(model_t), intent(in) :: model
integer :: i
type(flavor_t) :: flavor
allocate (flv%massive (flv%nlegs), flv%colored(flv%nlegs), flv%charge(flv%nlegs))
do i = 1, flv%nlegs
call flavor%init (flv%flst(i), model)
flv%massive(i) = flavor%get_mass () > 0
flv%colored(i) = &
is_quark (flv%flst(i)) .or. is_gluon (flv%flst(i))
if (flavor%is_antiparticle ()) then
flv%charge(i) = -flavor%get_charge ()
else
flv%charge(i) = flavor%get_charge ()
end if
end do
end subroutine flv_structure_init_mass_color_and_charge
@ %def flv_structure_init_mass_color_and_charge
@
<<fks regions: flv structure: TBP>>=
procedure :: get_last_two => flv_structure_get_last_two
<<fks regions: procedures>>=
function flv_structure_get_last_two (flv, n) result (flst_last)
integer, dimension(2) :: flst_last
class(flv_structure_t), intent(in) :: flv
integer, intent(in) :: n
flst_last = [flv%flst(n - 1), flv%flst(n)]
end function flv_structure_get_last_two
@ %def flv_structure_get_last_two
@
<<fks regions: flv structure: TBP>>=
procedure :: final => flv_structure_final
<<fks regions: procedures>>=
subroutine flv_structure_final (flv)
class(flv_structure_t), intent(inout) :: flv
if (allocated (flv%flst)) deallocate (flv%flst)
if (allocated (flv%tag)) deallocate (flv%tag)
if (allocated (flv%massive)) deallocate (flv%massive)
if (allocated (flv%colored)) deallocate (flv%colored)
if (allocated (flv%charge)) deallocate (flv%charge)
end subroutine flv_structure_final
@ %def flv_structure_final
@
<<fks regions: public>>=
public :: flavor_permutation_t
<<fks regions: types>>=
type :: flavor_permutation_t
integer, dimension(:,:), allocatable :: perms
contains
<<fks regions: flavor permutation: TBP>>
end type flavor_permutation_t
@ %def flavor_permutation_t
@
<<fks regions: flavor permutation: TBP>>=
procedure :: init => flavor_permutation_init
<<fks regions: procedures>>=
subroutine flavor_permutation_init (perm, flv_in, flv_ref, n_first, n_last, with_tag)
class(flavor_permutation_t), intent(out) :: perm
type(flv_structure_t), intent(in) :: flv_in, flv_ref
integer, intent(in) :: n_first, n_last
logical, intent(in) :: with_tag
integer :: flv1, flv2, tmp
integer :: tag1, tag2
integer :: i, j, j_min, i_perm
integer, dimension(:,:), allocatable :: perm_list_tmp
type(flv_structure_t) :: flv_copy
logical :: condition
logical, dimension(:), allocatable :: already_correct
flv_copy = flv_in
allocate (perm_list_tmp (factorial (n_last - n_first - 1), 2))
allocate (already_correct (flv_in%nlegs))
already_correct = flv_in%flst == flv_ref%flst
if (with_tag) &
already_correct = already_correct .and. (flv_in%tag == flv_ref%tag)
j_min = n_first + 1
i_perm = 0
do i = n_first + 1, n_last
flv1 = flv_ref%flst(i)
tag1 = flv_ref%tag(i)
do j = j_min, n_last
if (already_correct(i) .or. already_correct(j)) cycle
flv2 = flv_copy%flst(j)
tag2 = flv_copy%tag(j)
condition = (flv1 == flv2) .and. i /= j
if (with_tag) condition = condition .and. (tag1 == tag2)
if (condition) then
i_perm = i_perm + 1
tmp = flv_copy%flst(i)
flv_copy%flst(i) = flv2
flv_copy%flst(j) = tmp
tmp = flv_copy%tag(i)
flv_copy%tag(i) = tag2
flv_copy%tag(j) = tmp
perm_list_tmp (i_perm, 1) = i
perm_list_tmp (i_perm, 2) = j
exit
end if
end do
j_min = j_min + 1
end do
allocate (perm%perms (i_perm, 2))
perm%perms = perm_list_tmp (1 : i_perm, :)
deallocate (perm_list_tmp)
call flv_copy%final ()
end subroutine flavor_permutation_init
@ %def flavor_permutation_init
@
<<fks regions: flavor permutation: TBP>>=
procedure :: write => flavor_permutation_write
<<fks regions: procedures>>=
subroutine flavor_permutation_write (perm, unit)
class(flavor_permutation_t), intent(in) :: perm
integer, intent(in), optional :: unit
integer :: i, n, u
u = given_output_unit (unit); if (u < 0) return
write (u, "(A)") "Flavor permutation list: "
n = size (perm%perms, dim = 1)
if (n > 0) then
do i = 1, n
write (u, "(A1,I1,1X,I1,A1)", advance = "no") "[", perm%perms(i,1), perm%perms(i,2), "]"
if (i < n) write (u, "(A4)", advance = "no") " // "
end do
write (u, "(A)") ""
else
write (u, "(A)") "[Empty]"
end if
end subroutine flavor_permutation_write
@ %def flavor_permutation_write
@
<<fks regions: flavor permutation: TBP>>=
procedure :: reset => flavor_permutation_final
procedure :: final => flavor_permutation_final
<<fks regions: procedures>>=
subroutine flavor_permutation_final (perm)
class(flavor_permutation_t), intent(inout) :: perm
if (allocated (perm%perms)) deallocate (perm%perms)
end subroutine flavor_permutation_final
@ %def flavor_permutation_final
@
<<fks regions: flavor permutation: TBP>>=
generic :: apply => apply_permutation, &
apply_flavor, apply_integer, apply_ftuple
procedure :: apply_permutation => flavor_permutation_apply_permutation
procedure :: apply_flavor => flavor_permutation_apply_flavor
procedure :: apply_integer => flavor_permutation_apply_integer
procedure :: apply_ftuple => flavor_permutation_apply_ftuple
<<fks regions: procedures>>=
elemental function flavor_permutation_apply_permutation (perm_1, perm_2) &
result (perm_out)
type(flavor_permutation_t) :: perm_out
class(flavor_permutation_t), intent(in) :: perm_1
type(flavor_permutation_t), intent(in) :: perm_2
integer :: n1, n2
n1 = size (perm_1%perms, dim = 1)
n2 = size (perm_2%perms, dim = 1)
allocate (perm_out%perms (n1 + n2, 2))
perm_out%perms (1 : n1, :) = perm_1%perms
perm_out%perms (n1 + 1: n1 + n2, :) = perm_2%perms
end function flavor_permutation_apply_permutation
@ %def flavor_permutation_apply_permutation
@
<<fks regions: procedures>>=
elemental function flavor_permutation_apply_flavor (perm, flv_in, invert) &
result (flv_out)
type(flv_structure_t) :: flv_out
class(flavor_permutation_t), intent(in) :: perm
type(flv_structure_t), intent(in) :: flv_in
logical, intent(in), optional :: invert
integer :: i, i1, i2
integer :: p1, p2, incr
integer :: flv_tmp, tag_tmp
logical :: inv
inv = .false.; if (present(invert)) inv = invert
flv_out = flv_in
if (inv) then
p1 = 1
p2 = size (perm%perms, dim = 1)
incr = 1
else
p1 = size (perm%perms, dim = 1)
p2 = 1
incr = -1
end if
do i = p1, p2, incr
i1 = perm%perms(i,1)
i2 = perm%perms(i,2)
flv_tmp = flv_out%flst(i1)
tag_tmp = flv_out%tag(i1)
flv_out%flst(i1) = flv_out%flst(i2)
flv_out%flst(i2) = flv_tmp
flv_out%tag(i1) = flv_out%tag(i2)
flv_out%tag(i2) = tag_tmp
end do
end function flavor_permutation_apply_flavor
@ %def flavor_permutation_apply_flavor
@
<<fks regions: procedures>>=
elemental function flavor_permutation_apply_integer (perm, i_in) result (i_out)
integer :: i_out
class(flavor_permutation_t), intent(in) :: perm
integer, intent(in) :: i_in
integer :: i, i1, i2
i_out = i_in
do i = size (perm%perms(:,1)), 1, -1
i1 = perm%perms(i,1)
i2 = perm%perms(i,2)
if (i_out == i1) then
i_out = i2
else if (i_out == i2) then
i_out = i1
end if
end do
end function flavor_permutation_apply_integer
@ %def flavor_permutation_apply_integer
@
<<fks regions: procedures>>=
elemental function flavor_permutation_apply_ftuple (perm, f_in) result (f_out)
type(ftuple_t) :: f_out
class(flavor_permutation_t), intent(in) :: perm
type(ftuple_t), intent(in) :: f_in
integer :: i, i1, i2
f_out = f_in
do i = size (perm%perms, dim = 1), 1, -1
i1 = perm%perms(i,1)
i2 = perm%perms(i,2)
if (f_out%ireg(1) == i1) then
f_out%ireg(1) = i2
else if (f_out%ireg(1) == i2) then
f_out%ireg(1) = i1
end if
if (f_out%ireg(2) == i1) then
f_out%ireg(2) = i2
else if (f_out%ireg(2) == i2) then
f_out%ireg(2) = i1
end if
end do
if (f_out%ireg(1) > f_out%ireg(2)) f_out%ireg = f_out%ireg([2,1])
end function flavor_permutation_apply_ftuple
@ %def flavor_permutation_apply_ftuple
@
<<fks regions: flavor permutation: TBP>>=
procedure :: test => flavor_permutation_test
<<fks regions: procedures>>=
function flavor_permutation_test (perm, flv1, flv2, with_tag) result (valid)
logical :: valid
class(flavor_permutation_t), intent(in) :: perm
type(flv_structure_t), intent(in) :: flv1, flv2
logical, intent(in) :: with_tag
type(flv_structure_t) :: flv_test
flv_test = perm%apply (flv2, invert = .true.)
valid = all (flv_test%flst == flv1%flst)
if (with_tag) valid = valid .and. all (flv_test%tag == flv1%tag)
call flv_test%final ()
end function flavor_permutation_test
@ %def flavor_permutation_test
@ A singular region is a partition of phase space which is associated with
an individual emitter and, if relevant, resonance. It is associated with
an $\alpha_r$- and resonance-index, with a real flavor structure and
its underlying Born flavor structure. To compute the FKS weights, it is
relevant to know all the other particle indices which can result in a
divergenent phase space configuration, which are collected in the
[[ftuples]]-array.
Some singular regions might behave physically identical. E.g. a real
flavor structure associated with three-jet production is $[11,-11,0,2-2,0]$.
Here, there are two possible [[ftuples]] which contribute to the same
$u \rightarrow u g$ splitting, namely $(3,4)$ and $(4,6)$. The resulting
singular regions will be identical. To avoid this, one singular region
is associated with the multiplicity factor [[mult]]. When computing the
subtraction terms for each singular region, the result is then simply
multiplied by this factor.\\
The [[double_fsr]]-flag indicates whether the singular region should
also be supplied by a symmetry factor, explained below.
<<fks regions: public>>=
public :: singular_region_t
<<fks regions: types>>=
type :: singular_region_t
integer :: alr
integer :: i_res
type(flv_structure_t) :: flst_real
type(flv_structure_t) :: flst_uborn
integer :: mult
integer :: emitter
integer :: nregions
integer :: real_index
type(ftuple_t), dimension(:), allocatable :: ftuples
integer :: uborn_index
logical :: double_fsr = .false.
logical :: soft_divergence = .false.
logical :: coll_divergence = .false.
type(string_t) :: nlo_correction_type
integer, dimension(:), allocatable :: i_reg_to_i_con
logical :: pseudo_isr = .false.
logical :: sc_required = .false.
contains
<<fks regions: singular region: TBP>>
end type singular_region_t
@ %def singular_region_t
@
<<fks regions: singular region: TBP>>=
procedure :: init => singular_region_init
<<fks regions: procedures>>=
subroutine singular_region_init (sregion, alr, mult, i_res, &
flst_real, flst_uborn, flv_born, emitter, ftuples, equivalences, &
nlo_correction_type)
class(singular_region_t), intent(out) :: sregion
integer, intent(in) :: alr, mult, i_res
type(flv_structure_t), intent(in) :: flst_real
type(flv_structure_t), intent(in) :: flst_uborn
type(flv_structure_t), dimension(:), intent(in) :: flv_born
integer, intent(in) :: emitter
type(ftuple_t), intent(inout), dimension(:) :: ftuples
logical, intent(inout), dimension(:,:) :: equivalences
type(string_t), intent(in) :: nlo_correction_type
integer :: i
call debug_input_values ()
sregion%alr = alr
sregion%mult = mult
sregion%i_res = i_res
sregion%flst_real = flst_real
sregion%flst_uborn = flst_uborn
sregion%emitter = emitter
sregion%nlo_correction_type = nlo_correction_type
sregion%nregions = size (ftuples)
allocate (sregion%ftuples (sregion%nregions))
sregion%ftuples = ftuples
do i = 1, size(flv_born)
if (flv_born (i) .equiv. sregion%flst_uborn) then
sregion%uborn_index = i
exit
end if
end do
sregion%sc_required = any (sregion%flst_uborn%flst == GLUON) .or. &
any (sregion%flst_uborn%flst == PHOTON)
contains
subroutine debug_input_values()
if (debug_on) call msg_debug2 (D_SUBTRACTION, "singular_region_init")
if (debug2_active (D_SUBTRACTION)) then
print *, 'alr = ', alr
print *, 'mult = ', mult
print *, 'i_res = ', i_res
call flst_real%write ()
call flst_uborn%write ()
print *, 'emitter = ', emitter
call print_equivalence_matrix (ftuples, equivalences)
end if
end subroutine debug_input_values
end subroutine singular_region_init
@ %def singular_region_init
<<fks regions: singular region: TBP>>=
procedure :: write => singular_region_write
<<fks regions: procedures>>=
subroutine singular_region_write (sregion, unit, maxnregions)
class(singular_region_t), intent(in) :: sregion
integer, intent(in), optional :: unit
integer, intent(in), optional :: maxnregions
character(len=7), parameter :: flst_format = "(I3,A1)"
character(len=7), parameter :: ireg_space_format = "(7X,A1)"
integer :: nreal, nborn, i, u, mr
integer :: nleft, nright, nreg, nreg_diff
u = given_output_unit (unit); if (u < 0) return
mr = sregion%nregions; if (present (maxnregions)) mr = maxnregions
nreal = size (sregion%flst_real%flst)
nborn = size (sregion%flst_uborn%flst)
call write_vline (u)
write (u, '(A1)', advance = 'no') '['
do i = 1, nreal - 1
write (u, flst_format, advance = 'no') sregion%flst_real%flst(i), ','
end do
write (u, flst_format, advance = 'no') sregion%flst_real%flst(nreal), ']'
call write_vline (u)
write (u, '(I6)', advance = 'no') sregion%real_index
call write_vline (u)
write (u, '(I3)', advance = 'no') sregion%emitter
call write_vline (u)
write (u, '(I3)', advance = 'no') sregion%mult
call write_vline (u)
write (u, '(I4)', advance = 'no') sregion%nregions
call write_vline (u)
if (sregion%i_res > 0) then
write (u, '(I3)', advance = 'no') sregion%i_res
call write_vline (u)
end if
nreg = sregion%nregions
if (nreg == mr) then
nleft = 0
nright = 0
else
nreg_diff = mr - nreg
nleft = nreg_diff / 2
if (mod(nreg_diff , 2) == 0) then
nright = nleft
else
nright = nleft + 1
end if
end if
if (nleft > 0) then
do i = 1, nleft
write(u, ireg_space_format, advance='no') ' '
end do
end if
write (u, '(A)', advance = 'no') char (ftuple_string (sregion%ftuples, .false.))
call write_vline (u)
write (u,'(A1)',advance = 'no') '['
do i = 1, nborn - 1
write(u, flst_format, advance = 'no') sregion%flst_uborn%flst(i), ','
end do
write (u, flst_format, advance = 'no') sregion%flst_uborn%flst(nborn), ']'
call write_vline (u)
write (u, '(I7)', advance = 'no') sregion%uborn_index
write (u, '(A)')
end subroutine singular_region_write
@ %def singular_region_write
@
<<fks regions: singular region: TBP>>=
procedure :: write_latex => singular_region_write_latex
<<fks regions: procedures>>=
subroutine singular_region_write_latex (region, unit)
class(singular_region_t), intent(in) :: region
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(I2,A3,A,A3,I2,A3,I1,A3,I1,A3,A,A3,I2,A3,A,A3)") &
region%alr, " & ", char (region%flst_real%to_string ()), &
" & ", region%real_index, " & ", region%emitter, " & ", &
region%mult, " & ", char (ftuple_string (region%ftuples, .true.)), &
" & ", region%uborn_index, " & ", char (region%flst_uborn%to_string ()), &
" \\"
end subroutine singular_region_write_latex
@ %def singular_region_write_latex
-@ In case of a $g \rightarrow gg$ or $g \rightarrow qq$ splitting, the factor
+@ In case of a $g \rightarrow gg$ splitting, the factor
\begin{equation*}
\frac{2E_{\rm{em}}}{E_{\rm{em}} + E_{\rm{rad}}}
\end{equation*}
is multiplied to the real matrix element. This way, the symmetry of the splitting is used
and only one singular region has to be taken into account. However, the factor ensures that
there is only a soft singularity if the radiated parton becomes soft.
<<fks regions: singular region: TBP>>=
procedure :: set_splitting_info => singular_region_set_splitting_info
<<fks regions: procedures>>=
subroutine singular_region_set_splitting_info (region, n_in)
class(singular_region_t), intent(inout) :: region
integer, intent(in) :: n_in
integer :: i1, i2
integer :: reg
region%double_fsr = .false.
+ region%soft_divergence = .false.
associate (ftuple => region%ftuples)
do reg = 1, region%nregions
call ftuple(reg)%get (i1, i2)
- if (i1 /= region%emitter) cycle
- if (i2 /= region%flst_real%nlegs) cycle
- region%soft_divergence = &
- ftuple(reg)%splitting_type /= V_TO_FF
-
- if (i1 == 0) then
- region%coll_divergence = .not. any (region%flst_real%massive(1:n_in))
+ if (i1 /= region%emitter .or. i2 /= region%flst_real%nlegs) then
+ cycle
else
- region%coll_divergence = .not. region%flst_real%massive(i1)
- end if
+ if (ftuple(reg)%splitting_type == V_TO_VV .or. &
+ ftuple(reg)%splitting_type == F_TO_FV ) then
+ region%soft_divergence = .true.
+ end if
- if (ftuple(reg)%splitting_type == V_TO_VV) then
- if (all (ftuple(reg)%ireg > n_in)) &
- region%double_fsr = all (is_gluon (region%flst_real%flst(ftuple(reg)%ireg)))
- exit
- else if (ftuple(reg)%splitting_type == UNDEFINED_SPLITTING) then
- call msg_fatal ("All splittings should be defined!")
+ if (i1 == 0) then
+ region%coll_divergence = .not. all (region%flst_real%massive(1:n_in))
+ else
+ region%coll_divergence = .not. region%flst_real%massive(i1)
+ end if
+
+ if (ftuple(reg)%splitting_type == V_TO_VV) then
+ if (all (ftuple(reg)%ireg > n_in)) &
+ region%double_fsr = all (is_gluon (region%flst_real%flst(ftuple(reg)%ireg)))
+ exit
+ else if (ftuple(reg)%splitting_type == UNDEFINED_SPLITTING) then
+ call msg_fatal ("All splittings should be defined!")
+ end if
end if
end do
if (.not. region%soft_divergence .and. .not. region%coll_divergence) &
call msg_fatal ("Singular region defined without divergence!")
end associate
end subroutine singular_region_set_splitting_info
@ %def singular_region_set_splitting_info
@
<<fks regions: singular region: TBP>>=
procedure :: double_fsr_factor => singular_region_double_fsr_factor
<<fks regions: procedures>>=
function singular_region_double_fsr_factor (region, p) result (val)
class(singular_region_t), intent(in) :: region
type(vector4_t), intent(in), dimension(:) :: p
real(default) :: val
real(default) :: E_rad, E_em
if (region%double_fsr) then
E_em = energy (p(region%emitter))
E_rad = energy (p(region%flst_real%nlegs))
val = two * E_em / (E_em + E_rad)
else
val = one
end if
end function singular_region_double_fsr_factor
@ %def singular_region_double_fsr_factor
@
<<fks regions: singular region: TBP>>=
procedure :: has_soft_divergence => singular_region_has_soft_divergence
<<fks regions: procedures>>=
function singular_region_has_soft_divergence (region) result (div)
logical :: div
class(singular_region_t), intent(in) :: region
div = region%soft_divergence
end function singular_region_has_soft_divergence
@ %def singular_region_has_soft_divergence
@
<<fks regions: singular region: TBP>>=
procedure :: has_collinear_divergence => &
singular_region_has_collinear_divergence
<<fks regions: procedures>>=
function singular_region_has_collinear_divergence (region) result (div)
logical :: div
class(singular_region_t), intent(in) :: region
div = region%coll_divergence
end function singular_region_has_collinear_divergence
@ %def singular_region_has_collinear_divergence
@
<<fks regions: singular region: TBP>>=
procedure :: has_identical_ftuples => singular_region_has_identical_ftuples
<<fks regions: procedures>>=
elemental function singular_region_has_identical_ftuples (sregion) result (value)
logical :: value
class(singular_region_t), intent(in) :: sregion
integer :: alr
value = .false.
do alr = 1, sregion%nregions
value = value .or. (count (sregion%ftuples(alr) == sregion%ftuples) > 1)
end do
end function singular_region_has_identical_ftuples
@ %def singular_region_has_identical_ftuples
@
<<fks regions: interfaces>>=
interface assignment(=)
module procedure singular_region_assign
end interface
<<fks regions: procedures>>=
subroutine singular_region_assign (reg_out, reg_in)
type(singular_region_t), intent(out) :: reg_out
type(singular_region_t), intent(in) :: reg_in
reg_out%alr = reg_in%alr
reg_out%i_res = reg_in%i_res
reg_out%flst_real = reg_in%flst_real
reg_out%flst_uborn = reg_in%flst_uborn
reg_out%mult = reg_in%mult
reg_out%emitter = reg_in%emitter
reg_out%nregions = reg_in%nregions
reg_out%real_index = reg_in%real_index
reg_out%uborn_index = reg_in%uborn_index
reg_out%double_fsr = reg_in%double_fsr
reg_out%soft_divergence = reg_in%soft_divergence
reg_out%coll_divergence = reg_in%coll_divergence
reg_out%nlo_correction_type = reg_in%nlo_correction_type
if (allocated (reg_in%ftuples)) then
allocate (reg_out%ftuples (size (reg_in%ftuples)))
reg_out%ftuples = reg_in%ftuples
else
call msg_bug ("singular_region_assign: Trying to copy a singular region without allocated ftuples!")
end if
end subroutine singular_region_assign
@ %def singular_region_assign
@
<<fks regions: types>>=
type :: resonance_mapping_t
type(resonance_history_t), dimension(:), allocatable :: res_histories
integer, dimension(:), allocatable :: alr_to_i_res
integer, dimension(:,:), allocatable :: i_res_to_alr
type(vector4_t), dimension(:), allocatable :: p_res
contains
<<fks regions: resonance mapping: TBP>>
end type resonance_mapping_t
@ %def resonance_mapping_t
@ Testing: Init resonance mapping for $\mu \mu b b$ final state.
<<fks regions: resonance mapping: TBP>>=
procedure :: init => resonance_mapping_init
<<fks regions: procedures>>=
subroutine resonance_mapping_init (res_map, res_hist)
class(resonance_mapping_t), intent(inout) :: res_map
type(resonance_history_t), intent(in), dimension(:) :: res_hist
integer :: n_hist, i_hist1, i_hist2, n_contributors
n_contributors = 0
n_hist = size (res_hist)
allocate (res_map%res_histories (n_hist))
do i_hist1 = 1, n_hist
if (i_hist1 + 1 <= n_hist) then
do i_hist2 = i_hist1 + 1, n_hist
if (.not. (res_hist(i_hist1) .contains. res_hist(i_hist2))) &
n_contributors = n_contributors + res_hist(i_hist2)%n_resonances
end do
else
n_contributors = n_contributors + res_hist(i_hist1)%n_resonances
end if
end do
allocate (res_map%p_res (n_contributors))
res_map%res_histories = res_hist
res_map%p_res = vector4_null
end subroutine resonance_mapping_init
@ %def resonance_mapping_init
@
<<fks regions: resonance mapping: TBP>>=
procedure :: set_alr_to_i_res => resonance_mapping_set_alr_to_i_res
<<fks regions: procedures>>=
subroutine resonance_mapping_set_alr_to_i_res (res_map, regions, alr_new_to_old)
class(resonance_mapping_t), intent(inout) :: res_map
type(singular_region_t), intent(in), dimension(:) :: regions
integer, intent(out), dimension(:), allocatable :: alr_new_to_old
integer :: alr, i_res
integer :: alr_new, n_alr_res
integer :: k
if (debug_on) call msg_debug (D_SUBTRACTION, "resonance_mapping_set_alr_to_i_res")
n_alr_res = 0
do alr = 1, size (regions)
do i_res = 1, size (res_map%res_histories)
if (res_map%res_histories(i_res)%contains_leg (regions(alr)%emitter)) &
n_alr_res = n_alr_res + 1
end do
end do
allocate (res_map%alr_to_i_res (n_alr_res))
allocate (res_map%i_res_to_alr (size (res_map%res_histories), 10))
res_map%i_res_to_alr = 0
allocate (alr_new_to_old (n_alr_res))
alr_new = 1
do alr = 1, size (regions)
do i_res = 1, size (res_map%res_histories)
if (res_map%res_histories(i_res)%contains_leg (regions(alr)%emitter)) then
res_map%alr_to_i_res (alr_new) = i_res
alr_new_to_old (alr_new) = alr
alr_new = alr_new + 1
end if
end do
end do
do i_res = 1, size (res_map%res_histories)
k = 1
do alr = 1, size (regions)
if (res_map%res_histories(i_res)%contains_leg (regions(alr)%emitter)) then
res_map%i_res_to_alr (i_res, k) = alr
k = k + 1
end if
end do
end do
if (debug_active (D_SUBTRACTION)) then
print *, 'i_res_to_alr:'
do i_res = 1, size(res_map%i_res_to_alr, dim=1)
print *, res_map%i_res_to_alr (i_res, :)
end do
print *, 'alr_new_to_old:', alr_new_to_old
end if
end subroutine resonance_mapping_set_alr_to_i_res
@ %def resonance_mapping_set_alr_to_i_res
@
<<fks regions: resonance mapping: TBP>>=
procedure :: get_resonance_history => resonance_mapping_get_resonance_history
<<fks regions: procedures>>=
function resonance_mapping_get_resonance_history (res_map, alr) result (res_hist)
type(resonance_history_t) :: res_hist
class(resonance_mapping_t), intent(in) :: res_map
integer, intent(in) :: alr
res_hist = res_map%res_histories(res_map%alr_to_i_res (alr))
end function resonance_mapping_get_resonance_history
@ %def resonance_mapping_get_resonance_history
@
<<fks regions: resonance mapping: TBP>>=
procedure :: write => resonance_mapping_write
<<fks regions: procedures>>=
subroutine resonance_mapping_write (res_map)
class(resonance_mapping_t), intent(in) :: res_map
integer :: i_res
do i_res = 1, size (res_map%res_histories)
call res_map%res_histories(i_res)%write ()
end do
end subroutine resonance_mapping_write
@ %def resonance_mapping_write
@
<<fks regions: resonance mapping: TBP>>=
procedure :: get_resonance_value => resonance_mapping_get_resonance_value
<<fks regions: procedures>>=
function resonance_mapping_get_resonance_value (res_map, i_res, p, i_gluon) result (p_map)
real(default) :: p_map
class(resonance_mapping_t), intent(in) :: res_map
integer, intent(in) :: i_res
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in), optional :: i_gluon
p_map = res_map%res_histories(i_res)%mapping (p, i_gluon)
end function resonance_mapping_get_resonance_value
@ %def resonance_mapping_get_resonance_value
@
<<fks regions: resonance mapping: TBP>>=
procedure :: get_resonance_all => resonance_mapping_get_resonance_all
<<fks regions: procedures>>=
function resonance_mapping_get_resonance_all (res_map, alr, p, i_gluon) result (p_map)
real(default) :: p_map
class(resonance_mapping_t), intent(in) :: res_map
integer, intent(in) :: alr
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in), optional :: i_gluon
integer :: i_res
p_map = zero
do i_res = 1, size (res_map%res_histories)
associate (res => res_map%res_histories(i_res))
if (any (res_map%i_res_to_alr (i_res, :) == alr)) &
p_map = p_map + res%mapping (p, i_gluon)
end associate
end do
end function resonance_mapping_get_resonance_all
@ %def resonance_mapping_get_resonance_all
@
<<fks regions: resonance mapping: TBP>>=
procedure :: get_weight => resonance_mapping_get_weight
<<fks regions: procedures>>=
function resonance_mapping_get_weight (res_map, alr, p) result (pfr)
real(default) :: pfr
class(resonance_mapping_t), intent(in) :: res_map
integer, intent(in) :: alr
type(vector4_t), intent(in), dimension(:) :: p
real(default) :: sumpfr
integer :: i_res
sumpfr = zero
do i_res = 1, size (res_map%res_histories)
sumpfr = sumpfr + res_map%get_resonance_value (i_res, p)
end do
pfr = res_map%get_resonance_value (res_map%alr_to_i_res (alr), p) / sumpfr
end function resonance_mapping_get_weight
@ %def resonance_mapping_get_weight
@
<<fks regions: resonance mapping: TBP>>=
procedure :: get_resonance_alr => resonance_mapping_get_resonance_alr
<<fks regions: procedures>>=
function resonance_mapping_get_resonance_alr (res_map, alr, p, i_gluon) result (p_map)
real(default) :: p_map
class(resonance_mapping_t), intent(in) :: res_map
integer, intent(in) :: alr
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in), optional :: i_gluon
integer :: i_res
i_res = res_map%alr_to_i_res (alr)
p_map = res_map%res_histories(i_res)%mapping (p, i_gluon)
end function resonance_mapping_get_resonance_alr
@ %def resonance_mapping_get_resonance_alr
@
<<fks regions: interfaces>>=
interface assignment(=)
module procedure resonance_mapping_assign
end interface
<<fks regions: procedures>>=
subroutine resonance_mapping_assign (res_map_out, res_map_in)
type(resonance_mapping_t), intent(out) :: res_map_out
type(resonance_mapping_t), intent(in) :: res_map_in
if (allocated (res_map_in%res_histories)) then
allocate (res_map_out%res_histories (size (res_map_in%res_histories)))
res_map_out%res_histories = res_map_in%res_histories
end if
if (allocated (res_map_in%alr_to_i_res)) then
allocate (res_map_out%alr_to_i_res (size (res_map_in%alr_to_i_res)))
res_map_out%alr_to_i_res = res_map_in%alr_to_i_res
end if
if (allocated (res_map_in%i_res_to_alr)) then
allocate (res_map_out%i_res_to_alr &
(size (res_map_in%i_res_to_alr, 1), size (res_map_in%i_res_to_alr, 2)))
res_map_out%i_res_to_alr = res_map_in%i_res_to_alr
end if
if (allocated (res_map_in%p_res)) then
allocate (res_map_out%p_res (size (res_map_in%p_res)))
res_map_out%p_res = res_map_in%p_res
end if
end subroutine resonance_mapping_assign
@ %def resonance_mapping_assign
@ Every FKS mapping should store the $\sum_\alpha d_{ij}^{-1}$ and
$\sum_\alpha d_{ij,\rm{soft}}^{-1}$.
Also we keep the option open to use a normlization factor, which ensures
$\sum_\alpha S_\alpha = 1$.
<<fks regions: types>>=
type, abstract :: fks_mapping_t
real(default) :: sumdij
real(default) :: sumdij_soft
logical :: pseudo_isr = .false.
real(default) :: normalization_factor = one
contains
<<fks regions: fks mapping: TBP>>
end type fks_mapping_t
@ %def fks_mapping_t
@
<<fks regions: public>>=
public :: fks_mapping_default_t
<<fks regions: types>>=
type, extends (fks_mapping_t) :: fks_mapping_default_t
real(default) :: exp_1, exp_2
integer :: n_in
contains
<<fks regions: fks mapping default: TBP>>
end type fks_mapping_default_t
@ %def fks_mapping_default_t
@
<<fks regions: public>>=
public :: fks_mapping_resonances_t
<<fks regions: types>>=
type, extends (fks_mapping_t) :: fks_mapping_resonances_t
real(default) :: exp_1, exp_2
type(resonance_mapping_t) :: res_map
integer :: i_con = 0
contains
<<fks regions: fks mapping resonances: TBP>>
end type fks_mapping_resonances_t
@ %def fks_mapping_resonances_t
@
<<fks regions: public>>=
public :: operator(.equiv.)
public :: operator(.equivtag.)
<<fks regions: interfaces>>=
interface operator(.equiv.)
module procedure flv_structure_equivalent_no_tag
end interface
interface operator(.equivtag.)
module procedure flv_structure_equivalent_with_tag
end interface
interface assignment(=)
module procedure flv_structure_assign_flv
module procedure flv_structure_assign_integer
end interface
@ %def operator_equiv
@
<<fks regions: public>>=
public :: region_data_t
<<fks regions: types>>=
type :: region_data_t
type(singular_region_t), dimension(:), allocatable :: regions
type(flv_structure_t), dimension(:), allocatable :: flv_born
type(flv_structure_t), dimension(:), allocatable :: flv_real
integer, dimension(:), allocatable :: emitters
integer :: n_regions = 0
integer :: n_emitters = 0
integer :: n_flv_born = 0
integer :: n_flv_real = 0
integer :: n_in = 0
integer :: n_legs_born = 0
integer :: n_legs_real = 0
integer :: n_phs = 0
class(fks_mapping_t), allocatable :: fks_mapping
integer, dimension(:), allocatable :: resonances
type(resonance_contributors_t), dimension(:), allocatable :: alr_contributors
integer, dimension(:), allocatable :: alr_to_i_contributor
integer, dimension(:), allocatable :: i_phs_to_i_con
contains
<<fks regions: reg data: TBP>>
end type region_data_t
@ %def region_data_t
@
<<fks regions: reg data: TBP>>=
procedure :: allocate_fks_mappings => region_data_allocate_fks_mappings
<<fks regions: procedures>>=
subroutine region_data_allocate_fks_mappings (reg_data, mapping_type)
class(region_data_t), intent(inout) :: reg_data
integer, intent(in) :: mapping_type
select case (mapping_type)
case (FKS_DEFAULT)
allocate (fks_mapping_default_t :: reg_data%fks_mapping)
case (FKS_RESONANCES)
allocate (fks_mapping_resonances_t :: reg_data%fks_mapping)
case default
call msg_fatal ("Init region_data: FKS mapping not implemented!")
end select
end subroutine region_data_allocate_fks_mappings
@ %def region_data_allocate_fks_mappings
@
<<fks regions: reg data: TBP>>=
procedure :: init => region_data_init
<<fks regions: procedures>>=
subroutine region_data_init (reg_data, n_in, model, flavor_born, &
flavor_real, nlo_correction_type)
class(region_data_t), intent(inout) :: reg_data
integer, intent(in) :: n_in
type(model_t), intent(in) :: model
integer, intent(in), dimension(:,:) :: flavor_born, flavor_real
type(ftuple_list_t), dimension(:), allocatable :: ftuples
integer, dimension(:), allocatable :: emitter
type(flv_structure_t), dimension(:), allocatable :: flst_alr
integer :: i
integer :: n_flv_real_before_check
type(string_t), intent(in) :: nlo_correction_type
reg_data%n_in = n_in
reg_data%n_flv_born = size (flavor_born, dim = 2)
reg_data%n_legs_born = size (flavor_born, dim = 1)
reg_data%n_legs_real = reg_data%n_legs_born + 1
n_flv_real_before_check = size (flavor_real, dim = 2)
allocate (reg_data%flv_born (reg_data%n_flv_born))
allocate (reg_data%flv_real (n_flv_real_before_check))
do i = 1, reg_data%n_flv_born
call reg_data%flv_born(i)%init (flavor_born (:, i), n_in)
end do
do i = 1, n_flv_real_before_check
call reg_data%flv_real(i)%init (flavor_real (:, i), n_in)
end do
call reg_data%find_regions (model, ftuples, emitter, flst_alr)
call reg_data%init_singular_regions (ftuples, emitter, flst_alr, nlo_correction_type)
reg_data%n_flv_real = maxval (reg_data%regions%real_index)
call reg_data%find_emitters ()
call reg_data%set_mass_color_and_charge (model)
call reg_data%set_splitting_info ()
end subroutine region_data_init
@ %def region_data_init
@
<<fks regions: reg data: TBP>>=
procedure :: init_resonance_information => region_data_init_resonance_information
<<fks regions: procedures>>=
subroutine region_data_init_resonance_information (reg_data)
class(region_data_t), intent(inout) :: reg_data
call reg_data%enlarge_singular_regions_with_resonances ()
call reg_data%find_resonances ()
end subroutine region_data_init_resonance_information
@ %def region_data_init_resonance_information
@
<<fks regions: reg data: TBP>>=
procedure :: set_resonance_mappings => region_data_set_resonance_mappings
<<fks regions: procedures>>=
subroutine region_data_set_resonance_mappings (reg_data, resonance_histories)
class(region_data_t), intent(inout) :: reg_data
type(resonance_history_t), intent(in), dimension(:) :: resonance_histories
select type (map => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
call map%res_map%init (resonance_histories)
end select
end subroutine region_data_set_resonance_mappings
@ %def region_data_set_resonance_mappings
@
<<fks regions: reg data: TBP>>=
procedure :: setup_fks_mappings => region_data_setup_fks_mappings
<<fks regions: procedures>>=
subroutine region_data_setup_fks_mappings (reg_data, template, n_in)
class(region_data_t), intent(inout) :: reg_data
type(fks_template_t), intent(in) :: template
integer, intent(in) :: n_in
call reg_data%allocate_fks_mappings (template%mapping_type)
select type (map => reg_data%fks_mapping)
type is (fks_mapping_default_t)
call map%set_parameter (n_in, template%fks_dij_exp1, template%fks_dij_exp2)
end select
end subroutine region_data_setup_fks_mappings
@ %def region_data_setup_fks_mappings
@ So far, we have only created singular regions for a non-resonant case. When
resonance mappings are required, we have more singular regions, since they
must now be identified by their emitter-resonance pair index, where the emitter
must be compatible with the resonance.
<<fks regions: reg data: TBP>>=
procedure :: enlarge_singular_regions_with_resonances &
=> region_data_enlarge_singular_regions_with_resonances
<<fks regions: procedures>>=
subroutine region_data_enlarge_singular_regions_with_resonances (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: alr
integer, dimension(:), allocatable :: alr_new_to_old
integer :: n_alr_new
type(singular_region_t), dimension(:), allocatable :: save_regions
if (debug_on) call msg_debug (D_SUBTRACTION, "region_data_enlarge_singular_regions_with_resonances")
call debug_input_values ()
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_default_t)
return
type is (fks_mapping_resonances_t)
allocate (save_regions (reg_data%n_regions))
do alr = 1, reg_data%n_regions
save_regions(alr) = reg_data%regions(alr)
end do
associate (res_map => fks_mapping%res_map)
call res_map%set_alr_to_i_res (reg_data%regions, alr_new_to_old)
deallocate (reg_data%regions)
n_alr_new = size (alr_new_to_old)
reg_data%n_regions = n_alr_new
allocate (reg_data%regions (n_alr_new))
do alr = 1, n_alr_new
reg_data%regions(alr) = save_regions(alr_new_to_old (alr))
reg_data%regions(alr)%i_res = res_map%alr_to_i_res (alr)
end do
end associate
end select
contains
subroutine debug_input_values ()
if (debug2_active (D_SUBTRACTION)) then
call reg_data%write ()
end if
end subroutine debug_input_values
end subroutine region_data_enlarge_singular_regions_with_resonances
@ %def region_data_enlarge_singular_regions_with_resonances
@
<<fks regions: reg data: TBP>>=
procedure :: set_isr_pseudo_regions => region_data_set_isr_pseudo_regions
<<fks regions: procedures>>=
subroutine region_data_set_isr_pseudo_regions (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: alr
integer :: n_alr_new
!!! Subroutine called for threshold factorization ->
!!! Size of singular regions at this point is fixed
type(singular_region_t), dimension(2) :: save_regions
integer, dimension(4) :: alr_new_to_old
do alr = 1, reg_data%n_regions
save_regions(alr) = reg_data%regions(alr)
end do
n_alr_new = reg_data%n_regions * 2
alr_new_to_old = [1, 1, 2, 2]
deallocate (reg_data%regions)
allocate (reg_data%regions (n_alr_new))
reg_data%n_regions = n_alr_new
do alr = 1, n_alr_new
reg_data%regions(alr) = save_regions(alr_new_to_old (alr))
call add_pseudo_emitters (reg_data%regions(alr))
if (mod (alr, 2) == 0) reg_data%regions(alr)%pseudo_isr = .true.
end do
contains
subroutine add_pseudo_emitters (sregion)
type(singular_region_t), intent(inout) :: sregion
type(ftuple_t), dimension(2) :: ftuples_save
integer :: alr
do alr = 1, 2
ftuples_save(alr) = sregion%ftuples(alr)
end do
deallocate (sregion%ftuples)
sregion%nregions = sregion%nregions * 2
allocate (sregion%ftuples (sregion%nregions))
do alr = 1, sregion%nregions
sregion%ftuples(alr) = ftuples_save (alr_new_to_old(alr))
if (mod (alr, 2) == 0) sregion%ftuples(alr)%pseudo_isr = .true.
end do
end subroutine add_pseudo_emitters
end subroutine region_data_set_isr_pseudo_regions
@ %def region_data_set_isr_pseudo_regions
@ This subroutine splits up the ftuple-list of the singular regions into interference-free
lists, i.e. lists which only contain the same emitter. This is relevant for factorized
NLO calculations. In the current implementation, it is hand-tailored for the threshold
computation, but should be generalized further in the future.
<<fks regions: reg data: TBP>>=
procedure :: split_up_interference_regions_for_threshold => &
region_data_split_up_interference_regions_for_threshold
<<fks regions: procedures>>=
subroutine region_data_split_up_interference_regions_for_threshold (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: alr, i_ftuple
integer :: current_emitter
integer :: i1, i2
integer :: n_new_reg
type(ftuple_t), dimension(2) :: ftuples
do alr = 1, reg_data%n_regions
associate (region => reg_data%regions(alr))
current_emitter = region%emitter
n_new_reg = 0
do i_ftuple = 1, region%nregions
call region%ftuples(i_ftuple)%get (i1, i2)
if (i1 == current_emitter) then
n_new_reg = n_new_reg + 1
ftuples(n_new_reg) = region%ftuples(i_ftuple)
end if
end do
deallocate (region%ftuples)
allocate (region%ftuples(n_new_reg))
region%ftuples = ftuples (1 : n_new_reg)
region%nregions = n_new_reg
end associate
end do
reg_data%fks_mapping%normalization_factor = 0.5_default
end subroutine region_data_split_up_interference_regions_for_threshold
@ %def region_data_split_up_interference_regions_for_threshold
@
<<fks regions: reg data: TBP>>=
procedure :: set_mass_color_and_charge => region_data_set_mass_color_and_charge
<<fks regions: procedures>>=
subroutine region_data_set_mass_color_and_charge (reg_data, model)
class(region_data_t), intent(inout) :: reg_data
type(model_t), intent(in) :: model
integer :: i
do i = 1, reg_data%n_regions
associate (region => reg_data%regions(i))
call region%flst_uborn%init_mass_color_and_charge (model)
call region%flst_real%init_mass_color_and_charge (model)
end associate
end do
do i = 1, reg_data%n_flv_born
call reg_data%flv_born(i)%init_mass_color_and_charge (model)
end do
do i = 1, size (reg_data%flv_real)
call reg_data%flv_real(i)%init_mass_color_and_charge (model)
end do
end subroutine region_data_set_mass_color_and_charge
@ %def region_data_set_mass_color_and_charge
@
<<fks regions: reg data: TBP>>=
procedure :: uses_resonances => region_data_uses_resonances
<<fks regions: procedures>>=
function region_data_uses_resonances (reg_data) result (val)
logical :: val
class(region_data_t), intent(in) :: reg_data
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
val = .true.
class default
val = .false.
end select
end function region_data_uses_resonances
@ %def region_data_uses_resonances
@ Creates a list containing the emitter of each singular region.
<<fks regions: reg data: TBP>>=
procedure :: get_emitter_list => region_data_get_emitter_list
<<fks regions: procedures>>=
pure function region_data_get_emitter_list (reg_data) result (emitters)
class(region_data_t), intent(in) :: reg_data
integer, dimension(:), allocatable :: emitters
integer :: i
allocate (emitters (reg_data%n_regions))
do i = 1, reg_data%n_regions
emitters(i) = reg_data%regions(i)%emitter
end do
end function region_data_get_emitter_list
@ %def region_data_get_emitter_list
@ Returns the number of emitters not equal to 0 to avoid double counting
between emitters 0, 1 and 2.
<<fks regions: reg data: TBP>>=
procedure :: get_n_emitters_sc => region_data_get_n_emitters_sc
<<fks regions: procedures>>=
function region_data_get_n_emitters_sc (reg_data) result (n_emitters_sc)
class(region_data_t), intent(in) :: reg_data
integer :: n_emitters_sc
n_emitters_sc = count (reg_data%emitters /= 0)
end function region_data_get_n_emitters_sc
@ %def region_data_get_n_emitters_sc
@
<<fks regions: reg data: TBP>>=
procedure :: get_associated_resonances => region_data_get_associated_resonances
<<fks regions: procedures>>=
function region_data_get_associated_resonances (reg_data, emitter) result (res)
integer, dimension(:), allocatable :: res
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: emitter
integer :: alr, i
integer :: n_res
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
n_res = 0
do alr = 1, reg_data%n_regions
if (reg_data%regions(alr)%emitter == emitter) &
n_res = n_res + 1
end do
if (n_res > 0) then
allocate (res (n_res))
else
return
end if
i = 1
do alr = 1, reg_data%n_regions
if (reg_data%regions(alr)%emitter == emitter) then
res (i) = fks_mapping%res_map%alr_to_i_res (alr)
i = i + 1
end if
end do
end select
end function region_data_get_associated_resonances
@ %def region_data_get_associated_resonances
@
<<fks regions: reg data: TBP>>=
procedure :: emitter_is_compatible_with_resonance => &
region_data_emitter_is_compatible_with_resonance
<<fks regions: procedures>>=
function region_data_emitter_is_compatible_with_resonance &
(reg_data, i_res, emitter) result (compatible)
logical :: compatible
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: i_res, emitter
integer :: i_res_alr, alr
compatible = .false.
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
do alr = 1, reg_data%n_regions
i_res_alr = fks_mapping%res_map%alr_to_i_res (alr)
if (i_res_alr == i_res .and. reg_data%get_emitter(alr) == emitter) then
compatible = .true.
exit
end if
end do
end select
end function region_data_emitter_is_compatible_with_resonance
@ %def region_data_emitter_is_compatible_with_resonance
@
<<fks regions: reg data: TBP>>=
procedure :: emitter_is_in_resonance => region_data_emitter_is_in_resonance
<<fks regions: procedures>>=
function region_data_emitter_is_in_resonance (reg_data, i_res, emitter) result (exist)
logical :: exist
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: i_res, emitter
integer :: i
exist = .false.
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
associate (res_history => fks_mapping%res_map%res_histories(i_res))
do i = 1, res_history%n_resonances
exist = exist .or. any (res_history%resonances(i)%contributors%c == emitter)
end do
end associate
end select
end function region_data_emitter_is_in_resonance
@ %def region_data_emitter_is_in_resonance
@
<<fks regions: reg data: TBP>>=
procedure :: get_contributors => region_data_get_contributors
<<fks regions: procedures>>=
subroutine region_data_get_contributors (reg_data, i_res, emitter, c, success)
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: i_res, emitter
integer, intent(inout), dimension(:), allocatable :: c
logical, intent(out) :: success
integer :: i
success = .false.
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
associate (res_history => fks_mapping%res_map%res_histories (i_res))
do i = 1, res_history%n_resonances
if (any (res_history%resonances(i)%contributors%c == emitter)) then
allocate (c (size (res_history%resonances(i)%contributors%c)))
c = res_history%resonances(i)%contributors%c
success = .true.
exit
end if
end do
end associate
end select
end subroutine region_data_get_contributors
@ %def region_data_get_contributors
@
<<fks regions: reg data: TBP>>=
procedure :: get_emitter => region_data_get_emitter
<<fks regions: procedures>>=
pure function region_data_get_emitter (reg_data, alr) result (emitter)
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: alr
integer :: emitter
emitter = reg_data%regions(alr)%emitter
end function region_data_get_emitter
@ %def region_data_get_emitter
@
<<fks regions: reg data: TBP>>=
procedure :: map_real_to_born_index => region_data_map_real_to_born_index
<<fks regions: procedures>>=
function region_data_map_real_to_born_index (reg_data, real_index) result (uborn_index)
integer :: uborn_index
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: real_index
integer :: alr
uborn_index = 0
do alr = 1, size (reg_data%regions)
if (reg_data%regions(alr)%real_index == real_index) then
uborn_index = reg_data%regions(alr)%uborn_index
exit
end if
end do
end function region_data_map_real_to_born_index
@ %def region_data_map_real_to_born_index
@
<<fks regions: reg data: TBP>>=
generic :: get_flv_states_born => get_flv_states_born_single, get_flv_states_born_array
procedure :: get_flv_states_born_single => region_data_get_flv_states_born_single
procedure :: get_flv_states_born_array => region_data_get_flv_states_born_array
<<fks regions: procedures>>=
function region_data_get_flv_states_born_array (reg_data) result (flv_states)
integer, dimension(:,:), allocatable :: flv_states
class(region_data_t), intent(in) :: reg_data
integer :: i_flv
allocate (flv_states (reg_data%n_legs_born, reg_data%n_flv_born))
do i_flv = 1, reg_data%n_flv_born
flv_states (:, i_flv) = reg_data%flv_born(i_flv)%flst
end do
end function region_data_get_flv_states_born_array
function region_data_get_flv_states_born_single (reg_data, i_flv) result (flv_states)
integer, dimension(:), allocatable :: flv_states
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: i_flv
allocate (flv_states (reg_data%n_legs_born))
flv_states = reg_data%flv_born(i_flv)%flst
end function region_data_get_flv_states_born_single
@ %def region_data_get_flv_states_born
@
<<fks regions: reg data: TBP>>=
generic :: get_flv_states_real => get_flv_states_real_single, get_flv_states_real_array
procedure :: get_flv_states_real_single => region_data_get_flv_states_real_single
procedure :: get_flv_states_real_array => region_data_get_flv_states_real_array
<<fks regions: procedures>>=
function region_data_get_flv_states_real_single (reg_data, i_flv) result (flv_states)
integer, dimension(:), allocatable :: flv_states
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: i_flv
integer :: i_reg
allocate (flv_states (reg_data%n_legs_real))
do i_reg = 1, reg_data%n_regions
if (i_flv == reg_data%regions(i_reg)%real_index) then
flv_states = reg_data%regions(i_reg)%flst_real%flst
exit
end if
end do
end function region_data_get_flv_states_real_single
function region_data_get_flv_states_real_array (reg_data) result (flv_states)
integer, dimension(:,:), allocatable :: flv_states
class(region_data_t), intent(in) :: reg_data
integer :: i_flv
allocate (flv_states (reg_data%n_legs_real, reg_data%n_flv_real))
do i_flv = 1, reg_data%n_flv_real
flv_states (:, i_flv) = reg_data%get_flv_states_real (i_flv)
end do
end function region_data_get_flv_states_real_array
@ %def region_data_get_flv_states_real
@
<<fks regions: reg data: TBP>>=
procedure :: get_all_flv_states => region_data_get_all_flv_states
<<fks regions: procedures>>=
subroutine region_data_get_all_flv_states (reg_data, flv_born, flv_real)
class(region_data_t), intent(in) :: reg_data
integer, dimension(:,:), allocatable, intent(out) :: flv_born, flv_real
allocate (flv_born (reg_data%n_legs_born, reg_data%n_flv_born))
flv_born = reg_data%get_flv_states_born ()
allocate (flv_real (reg_data%n_legs_real, reg_data%n_flv_real))
flv_real = reg_data%get_flv_states_real ()
end subroutine region_data_get_all_flv_states
@ %def region_data_get_all_flv_states
@
<<fks regions: reg data: TBP>>=
procedure :: get_n_in => region_data_get_n_in
<<fks regions: procedures>>=
function region_data_get_n_in (reg_data) result (n_in)
integer :: n_in
class(region_data_t), intent(in) :: reg_data
n_in = reg_data%n_in
end function region_data_get_n_in
@ %def region_data_get_n_in
@
<<fks regions: reg data: TBP>>=
procedure :: get_n_legs_real => region_data_get_n_legs_real
<<fks regions: procedures>>=
function region_data_get_n_legs_real (reg_data) result (n_legs)
integer :: n_legs
class(region_data_t), intent(in) :: reg_data
n_legs = reg_data%n_legs_real
end function region_data_get_n_legs_real
@ %def region_data_get_n_legs_real
<<fks regions: reg data: TBP>>=
procedure :: get_n_legs_born => region_data_get_n_legs_born
<<fks regions: procedures>>=
function region_data_get_n_legs_born (reg_data) result (n_legs)
integer :: n_legs
class(region_data_t), intent(in) :: reg_data
n_legs = reg_data%n_legs_born
end function region_data_get_n_legs_born
@ %def region_data_get_n_legs_born
<<fks regions: reg data: TBP>>=
procedure :: get_n_flv_real => region_data_get_n_flv_real
<<fks regions: procedures>>=
function region_data_get_n_flv_real (reg_data) result (n_flv)
integer :: n_flv
class(region_data_t), intent(in) :: reg_data
n_flv = reg_data%n_flv_real
end function region_data_get_n_flv_real
@ %def region_data_get_n_flv_real
<<fks regions: reg data: TBP>>=
procedure :: get_n_flv_born => region_data_get_n_flv_born
<<fks regions: procedures>>=
function region_data_get_n_flv_born (reg_data) result (n_flv)
integer :: n_flv
class(region_data_t), intent(in) :: reg_data
n_flv = reg_data%n_flv_born
end function region_data_get_n_flv_born
@ %def region_data_get_n_flv_born
@ Returns $S_i = \frac{1}{\mathcal{D}d_i}$ or $S_{ij} =
\frac{1}{\mathcal{D}d_{ij}}$ for one particular singular region. At
this point, the flavor array should be rearranged in such a way that
the emitted particle is at the last position of
the flavor structure list.
<<fks regions: reg data: TBP>>=
generic :: get_svalue => get_svalue_last_pos, get_svalue_ij
procedure :: get_svalue_last_pos => region_data_get_svalue_last_pos
procedure :: get_svalue_ij => region_data_get_svalue_ij
<<fks regions: procedures>>=
function region_data_get_svalue_ij (reg_data, p, alr, i, j, i_res) result (sval)
class(region_data_t), intent(inout) :: reg_data
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: alr, i, j
integer, intent(in) :: i_res
real(default) :: sval
associate (map => reg_data%fks_mapping)
call map%compute_sumdij (reg_data%regions(alr), p)
select type (map)
type is (fks_mapping_resonances_t)
map%i_con = reg_data%alr_to_i_contributor (alr)
end select
map%pseudo_isr = reg_data%regions(alr)%pseudo_isr
sval = map%svalue (p, i, j, i_res) * map%normalization_factor
end associate
end function region_data_get_svalue_ij
function region_data_get_svalue_last_pos (reg_data, p, alr, emitter, i_res) result (sval)
class(region_data_t), intent(inout) :: reg_data
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: alr, emitter
integer, intent(in) :: i_res
real(default) :: sval
sval = reg_data%get_svalue (p, alr, emitter, reg_data%n_legs_real, i_res)
end function region_data_get_svalue_last_pos
@ %def region_data_get_svalue
@ The same as above, but for the soft limit.
<<fks regions: reg data: TBP>>=
procedure :: get_svalue_soft => region_data_get_svalue_soft
<<fks regions: procedures>>=
function region_data_get_svalue_soft &
(reg_data, p, p_soft, alr, emitter, i_res) result (sval)
class(region_data_t), intent(inout) :: reg_data
type(vector4_t), intent(in), dimension(:) :: p
type(vector4_t), intent(in) :: p_soft
integer, intent(in) :: alr, emitter, i_res
real(default) :: sval
associate (map => reg_data%fks_mapping)
call map%compute_sumdij_soft (reg_data%regions(alr), p, p_soft)
select type (map)
type is (fks_mapping_resonances_t)
map%i_con = reg_data%alr_to_i_contributor (alr)
end select
map%pseudo_isr = reg_data%regions(alr)%pseudo_isr
sval = map%svalue_soft (p, p_soft, emitter, i_res) * map%normalization_factor
end associate
end function region_data_get_svalue_soft
@ %def region_data_get_svalue_soft
@ This subroutine starts with a specification of $N$- and
$N+1$-particle configurations, [[flst_born]] and [[flst_real]], saved
in [[reg_data]]. From these, it creates a list of fundamental tuples,
a list of emitters and a list containing the $N+1$-particle
configuration, rearranged in such a way that the emitter-radiation
pair is last ([[flst_alr]]). For the $e^+ \, e^- \, \rightarrow u \,
\bar{u} \, g$- example, the generated objects are shown in table
\ref{table:ftuples and flavors}. Note that at this point, [[flst_alr]]
is arranged in such a way that the emitter can only be equal to
$n_{legs}-1$ for final-state radiation or 0, 1, or 2 for initial-state
radiation. Further, it occurs that regions can be equivalent. For
example in table \ref{table:ftuples and flavors} the regions
corresponding to \texttt{alr} = 1 and \texttt{alr} = 3 as well as
\texttt{alr} = 2 and \texttt{alr} = 4 describe the same physics and
are therefore equivalent.
@
<<fks regions: reg data: TBP>>=
procedure :: find_regions => region_data_find_regions
<<fks regions: procedures>>=
subroutine region_data_find_regions &
(reg_data, model, ftuples, emitters, flst_alr)
class(region_data_t), intent(in) :: reg_data
type(model_t), intent(in) :: model
type(ftuple_list_t), intent(out), dimension(:), allocatable :: ftuples
integer, intent(out), dimension(:), allocatable :: emitters
type(flv_structure_t), intent(out), dimension(:), allocatable :: flst_alr
type(ftuple_list_t), dimension(:,:), allocatable :: ftuples_tmp
integer, dimension(:,:), allocatable :: ftuple_index
integer :: n_born, n_real
integer :: n_legreal
integer :: i_born, i_real, i_ftuple
integer :: last_registered_i_born, last_registered_i_real
n_born = size (reg_data%flv_born)
n_real = size (reg_data%flv_real)
n_legreal = size (reg_data%flv_real(1)%flst)
allocate (emitters (0))
allocate (flst_alr (0))
allocate (ftuples (0))
i_ftuple = 0
last_registered_i_born = 0; last_registered_i_real = 0
do i_real = 1, n_real
do i_born = 1, n_born
- call check_final_state_emissions &
+ call setup_flsts_emitters_and_ftuples_fsr &
(i_real, i_born, i_ftuple, flst_alr, emitters, ftuples)
- call check_initial_state_emissions &
+ call setup_flsts_emitters_and_ftuples_isr &
(i_real, i_born, i_ftuple, flst_alr, emitters, ftuples)
end do
end do
contains
function incr_i_ftuple_if_required (i_born, i_real, i_ftuple_in) result (i_ftuple)
integer :: i_ftuple
integer, intent(in) :: i_born, i_real, i_ftuple_in
if (last_registered_i_born /= i_born .or. last_registered_i_real /= i_real) then
last_registered_i_born = i_born
last_registered_i_real = i_real
i_ftuple = i_ftuple_in + 1
else
i_ftuple = i_ftuple_in
end if
end function incr_i_ftuple_if_required
- subroutine check_final_state_emissions &
+ subroutine setup_flsts_emitters_and_ftuples_fsr &
(i_real, i_born, i_ftuple, flst_alr, emitters, ftuples)
integer, intent(in) :: i_real, i_born
integer, intent(inout) :: i_ftuple
type(flv_structure_t), intent(inout), dimension(:), allocatable :: flst_alr
integer, intent(inout), dimension(:), allocatable :: emitters
type(ftuple_list_t), intent(inout), dimension(:), allocatable :: ftuples
type(ftuple_list_t) :: ftuples_tmp
type(flv_structure_t) :: flst_alr_tmp
type(ftuple_t) :: current_ftuple
integer :: leg1, leg2
logical :: valid1, valid2
associate (flv_born => reg_data%flv_born(i_born), &
flv_real => reg_data%flv_real(i_real))
do leg1 = reg_data%n_in + 1, n_legreal
do leg2 = leg1 + 1, n_legreal
valid1 = flv_real%valid_pair(leg1, leg2, flv_born, model)
valid2 = flv_real%valid_pair(leg2, leg1, flv_born, model)
if (valid1 .or. valid2) then
if(valid1) then
flst_alr_tmp = create_alr (flv_real, &
reg_data%n_in, leg1, leg2)
else
flst_alr_tmp = create_alr (flv_real, &
reg_data%n_in, leg2, leg1)
end if
flst_alr = [flst_alr, flst_alr_tmp]
emitters = [emitters, n_legreal - 1]
call current_ftuple%set (leg1, leg2)
call current_ftuple%determine_splitting_type_fsr &
(flv_real, leg1, leg2)
i_ftuple = incr_i_ftuple_if_required (i_born, i_real, i_ftuple)
if (i_ftuple > size (ftuples)) then
call ftuples_tmp%append (current_ftuple)
ftuples = [ftuples, ftuples_tmp]
else
call ftuples(i_ftuple)%append (current_ftuple)
end if
end if
end do
end do
end associate
- end subroutine check_final_state_emissions
+ end subroutine setup_flsts_emitters_and_ftuples_fsr
- subroutine check_initial_state_emissions &
+ subroutine setup_flsts_emitters_and_ftuples_isr &
(i_real, i_born, i_ftuple, flst_alr, emitters, ftuples)
integer, intent(in) :: i_real, i_born
integer, intent(inout) :: i_ftuple
type(flv_structure_t), intent(inout), dimension(:), allocatable :: flst_alr
integer, intent(inout), dimension(:), allocatable :: emitters
type(ftuple_list_t), intent(inout), dimension(:), allocatable :: ftuples
type(ftuple_list_t) :: ftuples_tmp
type(flv_structure_t) :: flst_alr_tmp
type(ftuple_t) :: current_ftuple
integer :: leg, emitter
logical :: valid1, valid2
associate (flv_born => reg_data%flv_born(i_born), &
flv_real => reg_data%flv_real(i_real))
do leg = reg_data%n_in + 1, n_legreal
valid1 = flv_real%valid_pair(1, leg, flv_born, model)
if (reg_data%n_in > 1) then
valid2 = flv_real%valid_pair(2, leg, flv_born, model)
else
valid2 = .false.
end if
if (valid1 .and. valid2) then
emitter = 0
else if (valid1 .and. .not. valid2) then
emitter = 1
else if (.not. valid1 .and. valid2) then
emitter = 2
else
emitter = -1
end if
if (valid1 .or. valid2) then
flst_alr_tmp = create_alr (flv_real, reg_data%n_in, emitter, leg)
flst_alr = [flst_alr, flst_alr_tmp]
emitters = [emitters, emitter]
call current_ftuple%set(emitter, leg)
call current_ftuple%determine_splitting_type_isr &
(flv_real, emitter, leg)
i_ftuple = incr_i_ftuple_if_required (i_born, i_real, i_ftuple)
if (i_ftuple > size (ftuples)) then
call ftuples_tmp%append (current_ftuple)
ftuples = [ftuples, ftuples_tmp]
else
call ftuples(i_ftuple)%append (current_ftuple)
end if
end if
end do
end associate
- end subroutine check_initial_state_emissions
+ end subroutine setup_flsts_emitters_and_ftuples_isr
end subroutine region_data_find_regions
@ %def region_data_find_regions
@ Creates singular regions according to table \ref{table:singular
regions}. It scans all regions in table \ref{table:ftuples and
flavors} and records the real flavor structures. If they are
equivalent, the flavor structure is not recorded, but the multiplicity
of the present one is increased.
<<fks regions: reg data: TBP>>=
procedure :: init_singular_regions => region_data_init_singular_regions
<<fks regions: procedures>>=
subroutine region_data_init_singular_regions &
(reg_data, ftuples, emitter, flv_alr, nlo_correction_type)
class(region_data_t), intent(inout) :: reg_data
type(ftuple_list_t), intent(inout), dimension(:), allocatable :: ftuples
type(string_t), intent(in) :: nlo_correction_type
integer :: n_independent_flv
integer, intent(in), dimension(:) :: emitter
type(flv_structure_t), intent(in), dimension(:) :: flv_alr
type(flv_structure_t), dimension(:), allocatable :: flv_uborn, flv_alr_registered
integer, dimension(:), allocatable :: mult
integer, dimension(:), allocatable :: flst_emitter
integer :: n_regions, maxregions
integer, dimension(:), allocatable :: index
integer :: i, i_flv, n_legs
logical :: equiv, valid_fs_splitting
integer :: i_first, i_reg, i_reg_prev
integer, dimension(:), allocatable :: region_to_ftuple, alr_limits
integer, dimension(:), allocatable :: equiv_index
maxregions = size (emitter)
n_legs = flv_alr(1)%nlegs
allocate (flv_uborn (maxregions))
allocate (flv_alr_registered (maxregions))
allocate (mult (maxregions))
mult = 0
allocate (flst_emitter (maxregions))
allocate (index (0))
allocate (region_to_ftuple (maxregions))
allocate (equiv_index (maxregions))
call setup_region_mappings (n_independent_flv, alr_limits, region_to_ftuple)
i_first = 1
i_reg = 1
SCAN_FLAVORS: do i_flv = 1, n_independent_flv
SCAN_FTUPLES: do i = i_first, i_first + alr_limits (i_flv) - 1
equiv = .false.
if (i == i_first) then
flv_alr_registered(i_reg) = flv_alr(i)
mult(i_reg) = mult(i_reg) + 1
flv_uborn(i_reg) = flv_alr(i)%create_uborn (emitter(i), nlo_correction_type)
flst_emitter(i_reg) = emitter(i)
index = [index, region_to_real_index(ftuples, i)]
equiv_index(i_reg) = region_to_ftuple(i)
i_reg = i_reg + 1
else
!!! Check for equivalent flavor structures
do i_reg_prev = 1, i_reg - 1
if (emitter(i) == flst_emitter(i_reg_prev) .and. emitter(i) > reg_data%n_in) then
valid_fs_splitting = check_fs_splitting (flv_alr(i)%get_last_two(n_legs), &
flv_alr_registered(i_reg_prev)%get_last_two(n_legs), &
flv_alr(i)%tag(n_legs - 1), flv_alr_registered(i_reg_prev)%tag(n_legs - 1))
if ((flv_alr(i) .equiv. flv_alr_registered(i_reg_prev)) &
.and. valid_fs_splitting) then
mult(i_reg_prev) = mult(i_reg_prev) + 1
equiv = .true.
call ftuples(region_to_real_index(ftuples, i))%set_equiv &
(equiv_index(i_reg_prev), region_to_ftuple(i))
exit
end if
else if (emitter(i) == flst_emitter(i_reg_prev) .and. emitter(i) <= reg_data%n_in) then
if (flv_alr(i) .equiv. flv_alr_registered(i_reg_prev)) then
mult(i_reg_prev) = mult(i_reg_prev) + 1
equiv = .true.
call ftuples(region_to_real_index(ftuples, i))%set_equiv &
(equiv_index(i_reg_prev), region_to_ftuple(i))
exit
end if
end if
end do
if (.not. equiv) then
flv_alr_registered(i_reg) = flv_alr(i)
mult(i_reg) = mult(i_reg) + 1
flv_uborn(i_reg) = flv_alr(i)%create_uborn (emitter(i), nlo_correction_type)
flst_emitter(i_reg) = emitter(i)
index = [index, region_to_real_index(ftuples, i)]
equiv_index (i_reg) = region_to_ftuple(i)
i_reg = i_reg + 1
end if
end if
end do SCAN_FTUPLES
i_first = i_first + alr_limits(i_flv)
end do SCAN_FLAVORS
n_regions = i_reg - 1
allocate (reg_data%regions (n_regions))
reg_data%n_regions = n_regions
call account_for_regions_from_other_uborns (ftuples)
call init_regions_with_permuted_flavors ()
call assign_real_indices ()
deallocate (flv_uborn)
deallocate (flv_alr_registered)
deallocate (mult)
deallocate (flst_emitter)
deallocate (index)
deallocate (region_to_ftuple)
deallocate (equiv_index)
contains
subroutine account_for_regions_from_other_uborns (ftuples)
type(ftuple_list_t), intent(inout), dimension(:), allocatable :: ftuples
integer :: alr1, alr2, i
type(ftuple_t), dimension(:), allocatable :: ftuples_alr1, ftuples_alr2
type(flavor_permutation_t) :: perm_list
logical, dimension(:,:), allocatable :: equivalences
do alr1 = 1, n_regions
do alr2 = 1, n_regions
if (index(alr1) == index(alr2)) cycle
if (flv_alr_registered(alr1) .equiv. flv_alr_registered(alr2)) then
call ftuples(index(alr1))%to_array (ftuples_alr1, equivalences, .false.)
call ftuples(index(alr2))%to_array (ftuples_alr2, equivalences, .false.)
do i = 1, size (ftuples_alr2)
if (.not. any (ftuple_equal_ireg (ftuples_alr1, ftuples_alr2(i)))) then
call ftuples(index(alr1))%append (ftuples_alr2(i))
end if
end do
end if
end do
end do
end subroutine account_for_regions_from_other_uborns
subroutine setup_region_mappings (n_independent_flv, &
alr_limits, region_to_ftuple)
integer, intent(inout) :: n_independent_flv
integer, intent(inout), dimension(:), allocatable :: alr_limits
integer, intent(inout), dimension(:), allocatable :: region_to_ftuple
integer :: i, j, i_flv
if (any (ftuples%get_n_tuples() == 0)) &
call msg_fatal ("Inconsistent collection of FKS pairs!")
n_independent_flv = size (ftuples)
alr_limits = ftuples%get_n_tuples()
if (.not. (sum (alr_limits) == maxregions)) &
call msg_fatal ("Too many regions!")
j = 1
do i_flv = 1, n_independent_flv
do i = 1, alr_limits(i_flv)
region_to_ftuple(j) = i
j = j + 1
end do
end do
end subroutine setup_region_mappings
subroutine check_permutation (perm, flv_perm, flv_orig, i_reg)
type(flavor_permutation_t), intent(in) :: perm
type(flv_structure_t), intent(in) :: flv_perm, flv_orig
integer, intent(in) :: i_reg
type(flv_structure_t) :: flv_test
flv_test = perm%apply (flv_orig, invert = .true.)
if (.not. all (flv_test%flst == flv_perm%flst)) then
print *, 'Fail at: ', i_reg
print *, 'Original flavor structure: ', flv_orig%flst
call perm%write ()
print *, 'Permuted flavor: ', flv_perm%flst
print *, 'Should be: ', flv_test%flst
call msg_fatal ("Permutation does not reproduce original flavor!")
end if
end subroutine check_permutation
subroutine init_regions_with_permuted_flavors ()
type(flavor_permutation_t) :: perm_list
type(ftuple_t), dimension(:), allocatable :: ftuple_array
logical, dimension(:,:), allocatable :: equivalences
integer :: i, j
do j = 1, n_regions
do i = 1, reg_data%n_flv_born
if (reg_data%flv_born (i) .equiv. flv_uborn (j)) then
call perm_list%reset ()
call perm_list%init (reg_data%flv_born(i), flv_uborn(j), &
reg_data%n_in, reg_data%n_legs_born, .true.)
flv_uborn(j) = perm_list%apply (flv_uborn(j))
flv_alr_registered(j) = perm_list%apply (flv_alr_registered(j))
flst_emitter(j) = perm_list%apply (flst_emitter(j))
end if
end do
call ftuples(index(j))%to_array (ftuple_array, equivalences, .false.)
do i = 1, size (reg_data%flv_real)
if (reg_data%flv_real(i) .equiv. flv_alr_registered(j)) then
call perm_list%reset ()
call perm_list%init (flv_alr_registered(j), reg_data%flv_real(i), &
reg_data%n_in, reg_data%n_legs_real, .false.)
if (debug_active (D_SUBTRACTION)) call check_permutation &
(perm_list, reg_data%flv_real(i), flv_alr_registered(j), j)
ftuple_array = perm_list%apply (ftuple_array)
call ftuple_sort_array (ftuple_array, equivalences)
end if
end do
call reg_data%regions(j)%init (j, mult(j), 0, flv_alr_registered(j), &
flv_uborn(j), reg_data%flv_born, flst_emitter(j), ftuple_array, &
equivalences, nlo_correction_type)
if (allocated (ftuple_array)) deallocate (ftuple_array)
if (allocated (equivalences)) deallocate (equivalences)
end do
end subroutine init_regions_with_permuted_flavors
subroutine assign_real_indices ()
type(flv_structure_t) :: current_flv_real
type(flv_structure_t), dimension(:), allocatable :: these_flv
integer :: i_real, current_uborn_index
integer :: i, j, this_i_real
allocate (these_flv (size (flv_alr_registered)))
i_real = 1
associate (regions => reg_data%regions)
do i = 1, reg_data%n_regions
do j = 1, size (these_flv)
if (.not. allocated (these_flv(j)%flst)) then
this_i_real = i_real
call these_flv(i_real)%init (flv_alr_registered(i)%flst, reg_data%n_in)
i_real = i_real + 1
exit
else if (all (these_flv(j)%flst == flv_alr_registered(i)%flst)) then
this_i_real = j
exit
end if
end do
regions(i)%real_index = this_i_real
end do
end associate
deallocate (these_flv)
end subroutine assign_real_indices
subroutine write_perm_list (perm_list)
integer, intent(in), dimension(:,:) :: perm_list
integer :: i
do i = 1, size (perm_list(:,1))
write (*,'(I1,1x,I1,A)', advance = "no" ) perm_list(i,1), perm_list(i,2), '/'
end do
print *, ''
end subroutine write_perm_list
function check_fs_splitting (flv1, flv2, tag1, tag2) result (valid)
logical :: valid
integer, intent(in), dimension(2) :: flv1, flv2
integer, intent(in) :: tag1, tag2
if (flv1(1) + flv1(2) == 0) then
valid = abs(flv1(1)) == abs(flv2(1)) .and. abs(flv1(2)) == abs(flv2(2))
else
valid = flv1(1) == flv2(1) .and. flv1(2) == flv2(2) .and. tag1 == tag2
end if
end function check_fs_splitting
end subroutine region_data_init_singular_regions
@ %def region_data_init_singular_regions
@ Create an array containing all emitters and resonances of [[region_data]].
<<fks regions: reg data: TBP>>=
procedure :: find_emitters => region_data_find_emitters
<<fks regions: procedures>>=
subroutine region_data_find_emitters (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: alr, j, n_em, em
integer, dimension(:), allocatable :: em_count
allocate (em_count(reg_data%n_regions))
em_count = -1
n_em = 0
!!!Count the number of different emitters
do alr = 1, reg_data%n_regions
em = reg_data%regions(alr)%emitter
if (.not. any (em_count == em)) then
n_em = n_em + 1
em_count(alr) = em
end if
end do
if (n_em < 1) call msg_fatal ("region_data_find_emitters: No emitters found!")
reg_data%n_emitters = n_em
allocate (reg_data%emitters (reg_data%n_emitters))
reg_data%emitters = -1
j = 1
do alr = 1, size (reg_data%regions)
em = reg_data%regions(alr)%emitter
if (.not. any (reg_data%emitters == em)) then
reg_data%emitters(j) = em
j = j + 1
end if
end do
end subroutine region_data_find_emitters
@ %def region_data_find_emitters
@
<<fks regions: reg data: TBP>>=
procedure :: find_resonances => region_data_find_resonances
<<fks regions: procedures>>=
subroutine region_data_find_resonances (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: alr, j, k, n_res, n_contr
integer :: res
integer, dimension(10) :: res_count
type(resonance_contributors_t), dimension(10) :: contributors_count
type(resonance_contributors_t) :: contributors
integer :: i_res, emitter
logical :: share_emitter
res_count = -1
n_res = 0; n_contr = 0
!!! Count the number of different resonances
do alr = 1, reg_data%n_regions
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
res = fks_mapping%res_map%alr_to_i_res (alr)
if (.not. any (res_count == res)) then
n_res = n_res + 1
res_count(alr) = res
end if
end select
end do
if (n_res > 0) allocate (reg_data%resonances (n_res))
j = 1
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
do alr = 1, size (reg_data%regions)
res = fks_mapping%res_map%alr_to_i_res (alr)
if (.not. any (reg_data%resonances == res)) then
reg_data%resonances(j) = res
j = j + 1
end if
end do
allocate (reg_data%alr_to_i_contributor (size (reg_data%regions)))
do alr = 1, size (reg_data%regions)
i_res = fks_mapping%res_map%alr_to_i_res (alr)
emitter = reg_data%regions(alr)%emitter
call reg_data%get_contributors (i_res, emitter, contributors%c, share_emitter)
if (.not. share_emitter) cycle
if (.not. any (contributors_count == contributors)) then
n_contr = n_contr + 1
contributors_count(alr) = contributors
end if
if (allocated (contributors%c)) deallocate (contributors%c)
end do
allocate (reg_data%alr_contributors (n_contr))
j = 1
do alr = 1, size (reg_data%regions)
i_res = fks_mapping%res_map%alr_to_i_res (alr)
emitter = reg_data%regions(alr)%emitter
call reg_data%get_contributors (i_res, emitter, contributors%c, share_emitter)
if (.not. share_emitter) cycle
if (.not. any (reg_data%alr_contributors == contributors)) then
reg_data%alr_contributors(j) = contributors
reg_data%alr_to_i_contributor (alr) = j
j = j + 1
else
do k = 1, size (reg_data%alr_contributors)
if (reg_data%alr_contributors(k) == contributors) exit
end do
reg_data%alr_to_i_contributor (alr) = k
end if
if (allocated (contributors%c)) deallocate (contributors%c)
end do
end select
call reg_data%extend_ftuples (n_res)
call reg_data%set_contributors ()
end subroutine region_data_find_resonances
@ %def region_data_find_resonances
@
<<fks regions: reg data: TBP>>=
procedure :: set_i_phs_to_i_con => region_data_set_i_phs_to_i_con
<<fks regions: procedures>>=
subroutine region_data_set_i_phs_to_i_con (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: alr
integer :: i_res, emitter, i_con, i_phs, i_em
type(phs_identifier_t), dimension(:), allocatable :: phs_id_tmp
logical :: share_emitter, phs_exist
type(resonance_contributors_t) :: contributors
allocate (phs_id_tmp (reg_data%n_phs))
if (allocated (reg_data%resonances)) then
allocate (reg_data%i_phs_to_i_con (reg_data%n_phs))
do i_em = 1, size (reg_data%emitters)
emitter = reg_data%emitters(i_em)
do i_res = 1, size (reg_data%resonances)
if (reg_data%emitter_is_compatible_with_resonance (i_res, emitter)) then
alr = find_alr (emitter, i_res)
if (alr == 0) call msg_fatal ("Could not find requested alpha region!")
i_con = reg_data%alr_to_i_contributor (alr)
call reg_data%get_contributors (i_res, emitter, contributors%c, share_emitter)
if (.not. share_emitter) cycle
call check_for_phs_identifier &
(phs_id_tmp, reg_data%n_in, emitter, contributors%c, phs_exist, i_phs)
if (phs_id_tmp(i_phs)%emitter < 0) then
phs_id_tmp(i_phs)%emitter = emitter
allocate (phs_id_tmp(i_phs)%contributors (size (contributors%c)))
phs_id_tmp(i_phs)%contributors = contributors%c
end if
reg_data%i_phs_to_i_con (i_phs) = i_con
end if
if (allocated (contributors%c)) deallocate (contributors%c)
end do
end do
end if
contains
function find_alr (emitter, i_res) result (alr)
integer :: alr
integer, intent(in) :: emitter, i_res
integer :: i
do i = 1, reg_data%n_regions
if (reg_data%regions(i)%emitter == emitter .and. &
reg_data%regions(i)%i_res == i_res) then
alr = i
return
end if
end do
alr = 0
end function find_alr
end subroutine region_data_set_i_phs_to_i_con
@ %def region_data_set_i_phs_to_i_con
@
<<fks regions: reg data: TBP>>=
procedure :: set_alr_to_i_phs => region_data_set_alr_to_i_phs
<<fks regions: procedures>>=
subroutine region_data_set_alr_to_i_phs (reg_data, phs_identifiers, alr_to_i_phs)
class(region_data_t), intent(inout) :: reg_data
type(phs_identifier_t), intent(in), dimension(:) :: phs_identifiers
integer, intent(out), dimension(:) :: alr_to_i_phs
integer :: alr, i_phs
integer :: emitter, i_res
type(resonance_contributors_t) :: contributors
logical :: share_emitter, phs_exist
do alr = 1, reg_data%n_regions
associate (region => reg_data%regions(alr))
emitter = region%emitter
i_res = region%i_res
if (i_res /= 0) then
call reg_data%get_contributors (i_res, emitter, &
contributors%c, share_emitter)
if (.not. share_emitter) cycle
end if
if (allocated (contributors%c)) then
call check_for_phs_identifier (phs_identifiers, reg_data%n_in, &
emitter, contributors%c, phs_exist = phs_exist, i_phs = i_phs)
else
call check_for_phs_identifier (phs_identifiers, reg_data%n_in, &
emitter, phs_exist = phs_exist, i_phs = i_phs)
end if
if (.not. phs_exist) &
call msg_fatal ("phs identifiers are not set up correctly!")
alr_to_i_phs(alr) = i_phs
end associate
if (allocated (contributors%c)) deallocate (contributors%c)
end do
end subroutine region_data_set_alr_to_i_phs
@ %def region_data_set_alr_to_i_phs
@
<<fks regions: reg data: TBP>>=
procedure :: set_contributors => region_data_set_contributors
<<fks regions: procedures>>=
subroutine region_data_set_contributors (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: alr, i_res, i_reg, i_con
integer :: i1, i2, i_em
integer, dimension(:), allocatable :: contributors
logical :: share_emitter
do alr = 1, size (reg_data%regions)
associate (sregion => reg_data%regions(alr))
allocate (sregion%i_reg_to_i_con (sregion%nregions))
do i_reg = 1, sregion%nregions
call sregion%ftuples(i_reg)%get (i1, i2)
i_em = get_emitter_index (i1, i2, reg_data%n_legs_real)
i_res = sregion%ftuples(i_reg)%i_res
call reg_data%get_contributors (i_res, i_em, contributors, share_emitter)
!!! Lookup contributor index
do i_con = 1, size (reg_data%alr_contributors)
if (all (reg_data%alr_contributors(i_con)%c == contributors)) then
sregion%i_reg_to_i_con (i_reg) = i_con
exit
end if
end do
deallocate (contributors)
end do
end associate
end do
contains
function get_emitter_index (i1, i2, n) result (i_em)
integer :: i_em
integer, intent(in) :: i1, i2, n
if (i1 == n) then
i_em = i2
else
i_em = i1
end if
end function get_emitter_index
end subroutine region_data_set_contributors
@ %def region_data_set_contributors
@ This extension of the ftuples is still too naive as it assumes that the same
resonances are possible for all ftuples
<<fks regions: reg data: TBP>>=
procedure :: extend_ftuples => region_data_extend_ftuples
<<fks regions: procedures>>=
subroutine region_data_extend_ftuples (reg_data, n_res)
class(region_data_t), intent(inout) :: reg_data
integer, intent(in) :: n_res
integer :: alr, n_reg_save
integer :: i_reg, i_res, i_em, k
type(ftuple_t), dimension(:), allocatable :: ftuple_save
integer :: n_new
do alr = 1, size (reg_data%regions)
associate (sregion => reg_data%regions(alr))
n_reg_save = sregion%nregions
allocate (ftuple_save (n_reg_save))
ftuple_save = sregion%ftuples
n_new = count_n_new_ftuples (sregion, n_res)
deallocate (sregion%ftuples)
sregion%nregions = n_new
allocate (sregion%ftuples (n_new))
k = 1
do i_res = 1, n_res
do i_reg = 1, n_reg_save
associate (ftuple_new => sregion%ftuples(k))
i_em = ftuple_save(i_reg)%ireg(1)
if (reg_data%emitter_is_in_resonance (i_res, i_em)) then
call ftuple_new%set (i_em, ftuple_save(i_reg)%ireg(2))
ftuple_new%i_res = i_res
ftuple_new%splitting_type = ftuple_save(i_reg)%splitting_type
k = k + 1
end if
end associate
end do
end do
end associate
deallocate (ftuple_save)
end do
contains
function count_n_new_ftuples (sregion, n_res) result (n_new)
integer :: n_new
type(singular_region_t), intent(in) :: sregion
integer, intent(in) :: n_res
integer :: i_reg, i_res, i_em
n_new = 0
do i_reg = 1, sregion%nregions
do i_res = 1, n_res
i_em = sregion%ftuples(i_reg)%ireg(1)
if (reg_data%emitter_is_in_resonance (i_res, i_em)) &
n_new = n_new + 1
end do
end do
end function count_n_new_ftuples
end subroutine region_data_extend_ftuples
@ %def region_data_extend_ftuples
@
<<fks regions: reg data: TBP>>=
procedure :: get_flavor_indices => region_data_get_flavor_indices
<<fks regions: procedures>>=
function region_data_get_flavor_indices (reg_data, born) result (i_flv)
integer, dimension(:), allocatable :: i_flv
class(region_data_t), intent(in) :: reg_data
logical, intent(in) :: born
allocate (i_flv (reg_data%n_regions))
if (born) then
i_flv = reg_data%regions%uborn_index
else
i_flv = reg_data%regions%real_index
end if
end function region_data_get_flavor_indices
@ %def region_data_get_flavor_indices
@
<<fks regions: reg data: TBP>>=
procedure :: get_matrix_element_index => region_data_get_matrix_element_index
<<fks regions: procedures>>=
function region_data_get_matrix_element_index (reg_data, i_reg) result (i_me)
integer :: i_me
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: i_reg
i_me = reg_data%regions(i_reg)%real_index
end function region_data_get_matrix_element_index
@ %def region_data_get_matrix_element_index
@
<<fks regions: reg data: TBP>>=
procedure :: compute_number_of_phase_spaces &
=> region_data_compute_number_of_phase_spaces
<<fks regions: procedures>>=
subroutine region_data_compute_number_of_phase_spaces (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: i_em, i_res, i_phs
integer :: emitter
type(resonance_contributors_t) :: contributors
integer, parameter :: n_max_phs = 10
type(phs_identifier_t), dimension(n_max_phs) :: phs_id_tmp
logical :: share_emitter, phs_exist
if (allocated (reg_data%resonances)) then
reg_data%n_phs = 0
do i_em = 1, size (reg_data%emitters)
emitter = reg_data%emitters(i_em)
do i_res = 1, size (reg_data%resonances)
if (reg_data%emitter_is_compatible_with_resonance (i_res, emitter)) then
call reg_data%get_contributors (i_res, emitter, contributors%c, share_emitter)
if (.not. share_emitter) cycle
call check_for_phs_identifier &
(phs_id_tmp, reg_data%n_in, emitter, contributors%c, phs_exist, i_phs)
if (.not. phs_exist) then
reg_data%n_phs = reg_data%n_phs + 1
if (reg_data%n_phs > n_max_phs) call msg_fatal &
("Buffer of phase space identifieres: Too much phase spaces!")
call phs_id_tmp(i_phs)%init (emitter, contributors%c)
end if
end if
if (allocated (contributors%c)) deallocate (contributors%c)
end do
end do
else
reg_data%n_phs = size (remove_duplicates_from_int_array (reg_data%emitters))
end if
end subroutine region_data_compute_number_of_phase_spaces
@ %def region_data_compute_number_of_phase_spaces
@
<<fks regions: reg data: TBP>>=
procedure :: get_n_phs => region_data_get_n_phs
<<fks regions: procedures>>=
function region_data_get_n_phs (reg_data) result (n_phs)
integer :: n_phs
class(region_data_t), intent(in) :: reg_data
n_phs = reg_data%n_phs
end function region_data_get_n_phs
@ %def region_data_get_n_phs
@
<<fks regions: reg data: TBP>>=
procedure :: set_splitting_info => region_data_set_splitting_info
<<fks regions: procedures>>=
subroutine region_data_set_splitting_info (reg_data)
class(region_data_t), intent(inout) :: reg_data
integer :: alr
do alr = 1, reg_data%n_regions
call reg_data%regions(alr)%set_splitting_info (reg_data%n_in)
end do
end subroutine region_data_set_splitting_info
@ %def region_data_set_splitting_info
@
<<fks regions: reg data: TBP>>=
procedure :: init_phs_identifiers => region_data_init_phs_identifiers
<<fks regions: procedures>>=
subroutine region_data_init_phs_identifiers (reg_data, phs_id)
class(region_data_t), intent(in) :: reg_data
type(phs_identifier_t), intent(out), dimension(:), allocatable :: phs_id
integer :: i_em, i_res, i_phs
integer :: emitter
type(resonance_contributors_t) :: contributors
logical :: share_emitter, phs_exist
allocate (phs_id (reg_data%n_phs))
do i_em = 1, size (reg_data%emitters)
emitter = reg_data%emitters(i_em)
if (allocated (reg_data%resonances)) then
do i_res = 1, size (reg_data%resonances)
call reg_data%get_contributors (i_res, emitter, contributors%c, share_emitter)
if (.not. share_emitter) cycle
call check_for_phs_identifier &
(phs_id, reg_data%n_in, emitter, contributors%c, phs_exist, i_phs)
if (.not. phs_exist) &
call phs_id(i_phs)%init (emitter, contributors%c)
if (allocated (contributors%c)) deallocate (contributors%c)
end do
else
call check_for_phs_identifier (phs_id, reg_data%n_in, emitter, &
phs_exist = phs_exist, i_phs = i_phs)
if (.not. phs_exist) call phs_id(i_phs)%init (emitter)
end if
end do
end subroutine region_data_init_phs_identifiers
@ %def region_data_init_phs_identifiers
@
<<fks regions: reg data: TBP>>=
procedure :: get_all_ftuples => region_data_get_all_ftuples
<<fks regions: procedures>>=
subroutine region_data_get_all_ftuples (reg_data, ftuples)
class(region_data_t), intent(in) :: reg_data
type(ftuple_t), intent(inout), dimension(:), allocatable :: ftuples
type(ftuple_t), dimension(:), allocatable :: ftuple_tmp
integer :: i, j, alr
!!! Can have at most n * (n-1) ftuples
j = 0
allocate (ftuple_tmp (reg_data%n_legs_real * (reg_data%n_legs_real - 1)))
do i = 1, reg_data%n_regions
associate (region => reg_data%regions(i))
do alr = 1, region%nregions
if (.not. any (region%ftuples(alr) == ftuple_tmp)) then
j = j + 1
ftuple_tmp(j) = region%ftuples(alr)
end if
end do
end associate
end do
allocate (ftuples (j))
ftuples = ftuple_tmp(1:j)
deallocate (ftuple_tmp)
end subroutine region_data_get_all_ftuples
@ %def region_data_get_all_ftuples
@
<<fks regions: reg data: TBP>>=
procedure :: write_to_file => region_data_write_to_file
<<fks regions: procedures>>=
subroutine region_data_write_to_file (reg_data, proc_id, latex, os_data)
class(region_data_t), intent(inout) :: reg_data
type(string_t), intent(in) :: proc_id
logical, intent(in) :: latex
type(os_data_t), intent(in) :: os_data
type(string_t) :: filename
integer :: u
integer :: status
if (latex) then
filename = proc_id // "_fks_regions.tex"
else
filename = proc_id // "_fks_regions.out"
end if
u = free_unit ()
open (u, file=char(filename), action = "write", status="replace")
if (latex) then
call reg_data%write_latex (u)
close (u)
call os_data%build_latex_file &
(proc_id // "_fks_regions", stat_out = status)
if (status /= 0) &
call msg_error (char ("Failed to compile " // filename))
else
call reg_data%write (u)
close (u)
end if
end subroutine region_data_write_to_file
@ %def region_data_write_to_file
@
<<fks regions: reg data: TBP>>=
procedure :: write_latex => region_data_write_latex
<<fks regions: procedures>>=
subroutine region_data_write_latex (reg_data, unit)
class(region_data_t), intent(in) :: reg_data
integer, intent(in), optional :: unit
integer :: i, u
u = given_output_unit (); if (present (unit)) u = unit
write (u, "(A)") "\documentclass{article}"
write (u, "(A)") "\begin{document}"
write (u, "(A)") "%FKS region data, automatically created by WHIZARD"
write (u, "(A)") "\begin{table}"
write (u, "(A)") "\begin{center}"
write (u, "(A)") "\begin{tabular} {|c|c|c|c|c|c|c|c|}"
write (u, "(A)") "\hline"
write (u, "(A)") "$\alpha_r$ & $f_r$ & $i_r$ & $\varepsilon$ & $\varsigma$ & $\mathcal{P}_{\rm{FKS}}$ & $i_b$ & $f_b$ \\"
write (u, "(A)") "\hline"
do i = 1, reg_data%n_regions
call reg_data%regions(i)%write_latex (u)
end do
write (u, "(A)") "\hline"
write (u, "(A)") "\end{tabular}"
write (u, "(A)") "\caption{List of singular regions}"
write (u, "(A)") "\begin{description}"
write (u, "(A)") "\item[$\alpha_r$] Index of the singular region"
write (u, "(A)") "\item[$f_r$] Real flavor structure"
write (u, "(A)") "\item[$i_r$] Index of the associated real flavor structure"
write (u, "(A)") "\item[$\varepsilon$] Emitter"
write (u, "(A)") "\item[$\varsigma$] Multiplicity" !!! The symbol used by 0908.4272 for multiplicities
write (u, "(A)") "\item[$\mathcal{P}_{\rm{FKS}}$] The set of singular FKS-pairs"
write (u, "(A)") "\item[$i_b$] Underlying Born index"
write (u, "(A)") "\item[$f_b$] Underlying Born flavor structure"
write (u, "(A)") "\end{description}"
write (u, "(A)") "\end{center}"
write (u, "(A)") "\end{table}"
write (u, "(A)") "\end{document}"
end subroutine region_data_write_latex
@ %def region_data_write_latex
@ Creates a table with information about all singular regions and
writes it to a file.
@ Returns the index of the real flavor structure an ftuple belongs to.
<<fks regions: reg data: TBP>>=
procedure :: write => region_data_write
<<fks regions: procedures>>=
subroutine region_data_write (reg_data, unit)
class(region_data_t), intent(in) :: reg_data
integer, intent(in), optional :: unit
integer :: j
integer :: maxnregions, i_reg_max
type(string_t) :: flst_title, ftuple_title
integer :: n_res, u
u = given_output_unit (unit); if (u < 0) return
maxnregions = 1; i_reg_max = 1
do j = 1, reg_data%n_regions
if (size (reg_data%regions(j)%ftuples) > maxnregions) then
maxnregions = reg_data%regions(j)%nregions
i_reg_max = j
end if
end do
flst_title = '(A' // flst_title_format(reg_data%n_legs_real) // ')'
ftuple_title = '(A' // ftuple_title_format() // ')'
write (u,'(A,1X,I3)') 'Total number of regions: ', size(reg_data%regions)
write (u, '(A3)', advance = 'no') 'alr'
call write_vline (u)
write (u, char (flst_title), advance = 'no') 'flst_real'
call write_vline (u)
write (u, '(A6)', advance = 'no') 'i_real'
call write_vline (u)
write (u, '(A3)', advance = 'no') 'em'
call write_vline (u)
write (u, '(A3)', advance = 'no') 'mult'
call write_vline (u)
write (u, '(A4)', advance = 'no') 'nreg'
call write_vline (u)
if (allocated (reg_data%fks_mapping)) then
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
write (u, '(A3)', advance = 'no') 'res'
call write_vline (u)
end select
end if
write (u, char (ftuple_title), advance = 'no') 'ftuples'
call write_vline (u)
flst_title = '(A' // flst_title_format(reg_data%n_legs_born) // ')'
write (u, char (flst_title), advance = 'no') 'flst_born'
call write_vline (u)
write (u, '(A7)') 'i_born'
do j = 1, reg_data%n_regions
write (u, '(I3)', advance = 'no') j
call reg_data%regions(j)%write (u, maxnregions)
end do
call write_separator (u)
if (allocated (reg_data%fks_mapping)) then
select type (fks_mapping => reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
write (u, '(A)')
write (u, '(A)') "The FKS regions are combined with resonance information: "
n_res = size (fks_mapping%res_map%res_histories)
write (u, '(A,1X,I1)') "Number of QCD resonance histories: ", n_res
do j = 1, n_res
write (u, '(A,1X,I1)') "i_res = ", j
call fks_mapping%res_map%res_histories(j)%write (u)
call write_separator (u)
end do
end select
end if
contains
function flst_title_format (n) result (frmt)
integer, intent(in) :: n
type(string_t) :: frmt
character(len=2) :: frmt_char
write (frmt_char, '(I2)') 4 * n + 1
frmt = var_str (frmt_char)
end function flst_title_format
function ftuple_title_format () result (frmt)
type(string_t) :: frmt
integer :: n_ftuple_char
!!! An ftuple (x,x) consists of five characters. In the string, they
!!! are separated by maxregions - 1 commas. In total these are
!!! 5 * maxnregions + maxnregions - 1 = 6 * maxnregions - 1 characters.
!!! The {} brackets at add two additional characters.
n_ftuple_char = 6 * maxnregions + 1
!!! If there are resonances, each ftuple with a resonance adds a ";x"
!!! to the ftuple
n_ftuple_char = n_ftuple_char + 2 * count (reg_data%regions(i_reg_max)%ftuples%i_res > 0)
!!! Pseudo-ISR regions are denoted with a * at the end
n_ftuple_char = n_ftuple_char + count (reg_data%regions(i_reg_max)%ftuples%pseudo_isr)
frmt = str (n_ftuple_char)
end function ftuple_title_format
end subroutine region_data_write
@ %def region_data_write
@
<<fks regions: procedures>>=
subroutine write_vline (u)
integer, intent(in) :: u
character(len=10), parameter :: sep_format = "(1X,A2,1X)"
write (u, sep_format, advance = 'no') '||'
end subroutine write_vline
@ %def write_vline
@
<<fks regions: public>>=
public :: assignment(=)
<<fks regions: interfaces>>=
interface assignment(=)
module procedure region_data_assign
end interface
<<fks regions: procedures>>=
subroutine region_data_assign (reg_data_out, reg_data_in)
type(region_data_t), intent(out) :: reg_data_out
type(region_data_t), intent(in) :: reg_data_in
integer :: i
if (allocated (reg_data_in%regions)) then
allocate (reg_data_out%regions (size (reg_data_in%regions)))
do i = 1, size (reg_data_in%regions)
reg_data_out%regions(i) = reg_data_in%regions(i)
end do
else
call msg_warning ("Copying region data without allocated singular regions!")
end if
if (allocated (reg_data_in%flv_born)) then
allocate (reg_data_out%flv_born (size (reg_data_in%flv_born)))
do i = 1, size (reg_data_in%flv_born)
reg_data_out%flv_born(i) = reg_data_in%flv_born(i)
end do
else
call msg_warning ("Copying region data without allocated born flavor structure!")
end if
if (allocated (reg_data_in%flv_real)) then
allocate (reg_data_out%flv_real (size (reg_data_in%flv_real)))
do i = 1, size (reg_data_in%flv_real)
reg_data_out%flv_real(i) = reg_data_in%flv_real(i)
end do
else
call msg_warning ("Copying region data without allocated real flavor structure!")
end if
if (allocated (reg_data_in%emitters)) then
allocate (reg_data_out%emitters (size (reg_data_in%emitters)))
do i = 1, size (reg_data_in%emitters)
reg_data_out%emitters(i) = reg_data_in%emitters(i)
end do
else
call msg_warning ("Copying region data without allocated emitters!")
end if
reg_data_out%n_regions = reg_data_in%n_regions
reg_data_out%n_emitters = reg_data_in%n_emitters
reg_data_out%n_flv_born = reg_data_in%n_flv_born
reg_data_out%n_flv_real = reg_data_in%n_flv_real
reg_data_out%n_in = reg_data_in%n_in
reg_data_out%n_legs_born = reg_data_in%n_legs_born
reg_data_out%n_legs_real = reg_data_in%n_legs_real
if (allocated (reg_data_in%fks_mapping)) then
select type (fks_mapping_in => reg_data_in%fks_mapping)
type is (fks_mapping_default_t)
allocate (fks_mapping_default_t :: reg_data_out%fks_mapping)
select type (fks_mapping_out => reg_data_out%fks_mapping)
type is (fks_mapping_default_t)
fks_mapping_out = fks_mapping_in
end select
type is (fks_mapping_resonances_t)
allocate (fks_mapping_resonances_t :: reg_data_out%fks_mapping)
select type (fks_mapping_out => reg_data_out%fks_mapping)
type is (fks_mapping_resonances_t)
fks_mapping_out = fks_mapping_in
end select
end select
else
call msg_warning ("Copying region data without allocated FKS regions!")
end if
if (allocated (reg_data_in%resonances)) then
allocate (reg_data_out%resonances (size (reg_data_in%resonances)))
reg_data_out%resonances = reg_data_in%resonances
end if
reg_data_out%n_phs = reg_data_in%n_phs
if (allocated (reg_data_in%alr_contributors)) then
allocate (reg_data_out%alr_contributors (size (reg_data_in%alr_contributors)))
reg_data_out%alr_contributors = reg_data_in%alr_contributors
end if
if (allocated (reg_data_in%alr_to_i_contributor)) then
allocate (reg_data_out%alr_to_i_contributor &
(size (reg_data_in%alr_to_i_contributor)))
reg_data_out%alr_to_i_contributor = reg_data_in%alr_to_i_contributor
end if
end subroutine region_data_assign
@ %def region_data_assign
@ Returns the index of the real flavor structure an ftuple belogs to.
<<fks regions: procedures>>=
function region_to_real_index (list, i) result(index)
type(ftuple_list_t), intent(in), dimension(:), allocatable :: list
integer, intent(in) :: i
integer, dimension(:), allocatable :: nreg
integer :: index, j
allocate (nreg (0))
index = 0
do j = 1, size (list)
nreg = [nreg, sum (list(:j)%get_n_tuples ())]
if (j == 1) then
if (i <= nreg(j)) then
index = j
exit
end if
else
if (i > nreg(j - 1) .and. i <= nreg(j)) then
index = j
exit
end if
end if
end do
end function region_to_real_index
@ %def region_to_real_index
@ Final state emission: Rearrange the flavor array in such a way that
the emitted particle is last and the emitter is second last. [[i1]] is
the index of the emitter, [[i2]] is the index of the emitted particle.
Initial state emission: Just put the emitted particle to the last
position.
<<fks regions: procedures>>=
function create_alr (flv1, n_in, i_em, i_rad) result(flv2)
type(flv_structure_t), intent(in) :: flv1
integer, intent(in) :: n_in
integer, intent(in) :: i_em, i_rad
type(flv_structure_t) :: flv2
integer :: n
n = size (flv1%flst)
allocate (flv2%flst (n), flv2%tag (n))
flv2%nlegs = n
flv2%n_in = n_in
if (i_em > n_in) then
flv2%flst(1 : n_in) = flv1%flst(1 : n_in)
flv2%flst(n - 1) = flv1%flst(i_em)
flv2%flst(n) = flv1%flst(i_rad)
flv2%tag(1 : n_in) = flv1%tag(1 : n_in)
flv2%tag(n - 1) = flv1%tag(i_em)
flv2%tag(n) = flv1%tag(i_rad)
call fill_remaining_flavors (n_in, .true.)
else
flv2%flst(1 : n_in) = flv1%flst(1 : n_in)
flv2%flst(n) = flv1%flst(i_rad)
flv2%tag(1 : n_in) = flv1%tag(1 : n_in)
flv2%tag(n) = flv1%tag(i_rad)
call fill_remaining_flavors (n_in, .false.)
end if
call flv2%compute_prt_symm_fs (flv2%n_in)
contains
@ Order remaining particles according to their original position
<<fks regions: procedures>>=
subroutine fill_remaining_flavors (n_in, final_final)
integer, intent(in) :: n_in
logical, intent(in) :: final_final
integer :: i, j
logical :: check
j = n_in + 1
do i = n_in + 1, n
if (final_final) then
check = (i /= i_em .and. i /= i_rad)
else
check = (i /= i_rad)
end if
if (check) then
flv2%flst(j) = flv1%flst(i)
flv2%tag(j) = flv1%tag(i)
j = j + 1
end if
end do
end subroutine fill_remaining_flavors
end function create_alr
@ %def create_alr
@
<<fks regions: reg data: TBP>>=
procedure :: has_pseudo_isr => region_data_has_pseudo_isr
<<fks regions: procedures>>=
function region_data_has_pseudo_isr (reg_data) result (val)
logical :: val
class(region_data_t), intent(in) :: reg_data
val = any (reg_data%regions%pseudo_isr)
end function region_data_has_pseudo_isr
@ %def region_data_has_pseudo_isr
@ Performs consistency checks on [[region_data]]. Up to now only
-checks that no [[futple]] appears more than once.
+checks that no [[ftuple]] appears more than once.
<<fks regions: reg data: TBP>>=
procedure :: check_consistency => region_data_check_consistency
<<fks regions: procedures>>=
subroutine region_data_check_consistency (reg_data, fail_fatal, unit)
class(region_data_t), intent(in) :: reg_data
logical, intent(in) :: fail_fatal
integer, intent(in), optional :: unit
integer :: u
integer :: i_reg, alr
integer :: i1, f1, f2
logical :: undefined_ftuples, same_ftuple_indices, valid_splitting
logical, dimension(4) :: no_fail
u = given_output_unit(unit); if (u < 0) return
no_fail = .true.
call msg_message ("Check that no negative ftuple indices occur", unit = u)
do i_reg = 1, reg_data%n_regions
if (any (reg_data%regions(i_reg)%ftuples%has_negative_elements ())) then
!!! This error is so severe that we stop immediately
call msg_fatal ("Negative ftuple indices!")
end if
end do
call msg_message ("Success!", unit = u)
call msg_message ("Check that there is no ftuple with identical elements", unit = u)
do i_reg = 1, reg_data%n_regions
if (any (reg_data%regions(i_reg)%ftuples%has_identical_elements ())) then
!!! This error is so severe that we stop immediately
call msg_fatal ("Identical ftuple indices!")
end if
end do
call msg_message ("Success!", unit = u)
call msg_message ("Check that there are no duplicate ftuples in a region", unit = u)
do i_reg = 1, reg_data%n_regions
if (reg_data%regions(i_reg)%has_identical_ftuples ()) then
if (no_fail(1)) then
call msg_error ("FAIL: ", unit = u)
no_fail(1) = .false.
end if
write (u, '(A,1x,I3)') 'i_reg:', i_reg
end if
end do
if (no_fail(1)) call msg_message ("Success!", unit = u)
call msg_message ("Check that ftuples add up to a valid splitting", unit = u)
do i_reg = 1, reg_data%n_regions
do alr = 1, reg_data%regions(i_reg)%nregions
associate (region => reg_data%regions(i_reg))
i1 = region%ftuples(alr)%ireg(1)
- if (i1 == 0) i1 = 1 !!! Gluon emission from both initial-state quarks
+ if (i1 == 0) i1 = 1 !!! Gluon emission from both initial-state particles
f1 = region%flst_real%flst(i1)
f2 = region%flst_real%flst(region%ftuples(alr)%ireg(2))
+ ! Flip PDG sign of IS fermions to allow a q -> g q splitting
+ ! in which the ftuple has the flavors (q,q).
+ if (i1 <= reg_data%n_in .and. is_fermion(f1)) then
+ f1 = -f1
+ end if
valid_splitting = f1 + f2 == 0 &
- .or. (f1 == 21 .and. f2 == 21) &
- .or. (is_massive_vector (f1) .and. f2 == 22) &
+ .or. (is_gluon(f1) .and. is_gluon(f2)) &
+ .or. (is_massive_vector(f1) .and. is_photon(f2)) &
.or. is_fermion_vector_splitting (f1, f2)
if (.not. valid_splitting) then
if (no_fail(2)) then
call msg_error ("FAIL: ", unit = u)
no_fail(2) = .false.
end if
write (u, '(A,1x,I3)') 'i_reg:', i_reg
exit
end if
end associate
end do
end do
if (no_fail(2)) call msg_message ("Success!", unit = u)
call msg_message ("Check that at least one ftuple contains the emitter", unit = u)
do i_reg = 1, reg_data%n_regions
associate (region => reg_data%regions(i_reg))
if (.not. any (region%emitter == region%ftuples%ireg(1))) then
if (no_fail(3)) then
call msg_error ("FAIL: ", unit = u)
no_fail(3) = .false.
end if
write (u, '(A,1x,I3)') 'i_reg:', i_reg
end if
end associate
end do
if (no_fail(3)) call msg_message ("Success!", unit = u)
call msg_message ("Check that each region has at least one ftuple &
&with index n + 1", unit = u)
do i_reg = 1, reg_data%n_regions
if (.not. any (reg_data%regions(i_reg)%ftuples%ireg(2) == reg_data%n_legs_real)) then
if (no_fail(4)) then
call msg_error ("FAIL: ", unit = u)
no_fail(4) = .false.
end if
write (u, '(A,1x,I3)') 'i_reg:', i_reg
end if
end do
if (no_fail(4)) call msg_message ("Success!", unit = u)
if (.not. all (no_fail)) &
call abort_with_message ("Stop due to inconsistent region data!")
contains
subroutine abort_with_message (msg)
character(len=*), intent(in) :: msg
if (fail_fatal) then
call msg_fatal (msg)
else
call msg_error (msg, unit = u)
end if
end subroutine abort_with_message
function is_fermion_vector_splitting (pdg_1, pdg_2) result (value)
logical :: value
integer, intent(in) :: pdg_1, pdg_2
value = (is_fermion (pdg_1) .and. is_massless_vector (pdg_2)) .or. &
(is_fermion (pdg_2) .and. is_massless_vector (pdg_1))
end function
end subroutine region_data_check_consistency
@ %def region_data_check_consistency
@
<<fks regions: reg data: TBP>>=
procedure :: requires_spin_correlations => region_data_requires_spin_correlations
<<fks regions: procedures>>=
function region_data_requires_spin_correlations (reg_data) result (val)
class(region_data_t), intent(in) :: reg_data
logical :: val
integer :: alr
val = .false.
do alr = 1, reg_data%n_regions
val = reg_data%regions(alr)%sc_required
if (val) return
end do
end function region_data_requires_spin_correlations
@ %def region_data_requires_spin_correlations
@ We have to apply the symmetry factor for identical particles of the
real flavor structure to the born squared matrix element. The corresponding
factor from the born flavor structure has to be cancelled.
<<fks regions: reg data: TBP>>=
procedure :: born_to_real_symm_factor_fs => region_data_born_to_real_symm_factor_fs
<<fks regions: procedures>>=
function region_data_born_to_real_symm_factor_fs (reg_data, alr) result (factor)
class(region_data_t), intent(in) :: reg_data
integer, intent(in) :: alr
real(default) :: factor
associate (flv_real => reg_data%regions(alr)%flst_real, &
flv_uborn => reg_data%regions(alr)%flst_uborn)
factor = flv_real%prt_symm_fs / flv_uborn%prt_symm_fs
end associate
end function region_data_born_to_real_symm_factor_fs
@ %def region_data_born_to_real_symm_factor_fs
@
<<fks regions: reg data: TBP>>=
procedure :: final => region_data_final
<<fks regions: procedures>>=
subroutine region_data_final (reg_data)
class(region_data_t), intent(inout) :: reg_data
if (allocated (reg_data%regions)) deallocate (reg_data%regions)
if (allocated (reg_data%flv_born)) deallocate (reg_data%flv_born)
if (allocated (reg_data%flv_real)) deallocate (reg_data%flv_real)
if (allocated (reg_data%emitters)) deallocate (reg_data%emitters)
if (allocated (reg_data%fks_mapping)) deallocate (reg_data%fks_mapping)
if (allocated (reg_data%resonances)) deallocate (reg_data%resonances)
if (allocated (reg_data%alr_contributors)) deallocate (reg_data%alr_contributors)
if (allocated (reg_data%alr_to_i_contributor)) deallocate (reg_data%alr_to_i_contributor)
end subroutine region_data_final
@ %def region_data_final
@
<<fks regions: fks mapping: TBP>>=
procedure (fks_mapping_dij), deferred :: dij
<<fks regions: interfaces>>=
abstract interface
function fks_mapping_dij (map, p, i, j, i_con) result (d)
import
real(default) :: d
class(fks_mapping_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: i, j
integer, intent(in), optional :: i_con
end function fks_mapping_dij
end interface
@ %def fks_mapping_dij
@
<<fks regions: fks mapping: TBP>>=
procedure (fks_mapping_compute_sumdij), deferred :: compute_sumdij
<<fks regions: interfaces>>=
abstract interface
subroutine fks_mapping_compute_sumdij (map, sregion, p)
import
class(fks_mapping_t), intent(inout) :: map
type(singular_region_t), intent(in) :: sregion
type(vector4_t), intent(in), dimension(:) :: p
end subroutine fks_mapping_compute_sumdij
end interface
@ %def fks_mapping_compute_sumdij
@
<<fks regions: fks mapping: TBP>>=
procedure (fks_mapping_svalue), deferred :: svalue
<<fks regions: interfaces>>=
abstract interface
function fks_mapping_svalue (map, p, i, j, i_res) result (value)
import
real(default) :: value
class(fks_mapping_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: i, j
integer, intent(in), optional :: i_res
end function fks_mapping_svalue
end interface
@ %def fks_mapping_svalue
<<fks regions: fks mapping: TBP>>=
procedure (fks_mapping_dij_soft), deferred :: dij_soft
<<fks regions: interfaces>>=
abstract interface
function fks_mapping_dij_soft (map, p_born, p_soft, em, i_con) result (d)
import
real(default) :: d
class(fks_mapping_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
integer, intent(in) :: em
integer, intent(in), optional :: i_con
end function fks_mapping_dij_soft
end interface
@ %def fks_mapping_dij_soft
@
<<fks regions: fks mapping: TBP>>=
procedure (fks_mapping_compute_sumdij_soft), deferred :: compute_sumdij_soft
<<fks regions: interfaces>>=
abstract interface
subroutine fks_mapping_compute_sumdij_soft (map, sregion, p_born, p_soft)
import
class(fks_mapping_t), intent(inout) :: map
type(singular_region_t), intent(in) :: sregion
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
end subroutine fks_mapping_compute_sumdij_soft
end interface
@ %def fks_mapping_compute_sumdij_soft
@
<<fks regions: fks mapping: TBP>>=
procedure (fks_mapping_svalue_soft), deferred :: svalue_soft
<<fks regions: interfaces>>=
abstract interface
function fks_mapping_svalue_soft (map, p_born, p_soft, em, i_res) result (value)
import
real(default) :: value
class(fks_mapping_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
integer, intent(in) :: em
integer, intent(in), optional :: i_res
end function fks_mapping_svalue_soft
end interface
@ %def fks_mapping_svalue_soft
@
<<fks regions: fks mapping default: TBP>>=
procedure :: set_parameter => fks_mapping_default_set_parameter
<<fks regions: procedures>>=
subroutine fks_mapping_default_set_parameter (map, n_in, dij_exp1, dij_exp2)
class(fks_mapping_default_t), intent(inout) :: map
integer, intent(in) :: n_in
real(default), intent(in) :: dij_exp1, dij_exp2
map%n_in = n_in
map%exp_1 = dij_exp1
map%exp_2 = dij_exp2
end subroutine fks_mapping_default_set_parameter
@ %def fks_mapping_default_set_parameter
@ Computes the $d_{ij}$-quantities defined als follows:
\begin{align*}
d_{0i} &= \left[E_i^2\left(1-y_i\right)\right]^{p_1}\\,
d_{1i} &= \left[2E_i^2\left(1-y_i\right)\right]^{p_1}\\,
d_{2i} &= \left[2E_i^2\left(1+y_i\right)\right]^{p_1}\\,
\end{align*}
for initial state regions and
\begin{align*}
d_{ij} = \left[2(k_i \cdot k_j) \frac{E_i E_j}{(E_i+E_j)^2}\right]^{p_2}
\end{align*}
for final state regions. The exponents $p_1$ and $p_2$ can be used for
tuning the efficiency of the mapping and are set to $1$ per default.
<<fks regions: fks mapping default: TBP>>=
procedure :: dij => fks_mapping_default_dij
<<fks regions: procedures>>=
function fks_mapping_default_dij (map, p, i, j, i_con) result (d)
real(default) :: d
class(fks_mapping_default_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: i, j
integer, intent(in), optional :: i_con
d = zero
if (map%pseudo_isr) then
d = dij_threshold_gluon_from_top (i, j, p, map%exp_1)
else if (i > map%n_in .and. j > map%n_in) then
d = dij_fsr (p(i), p(j), map%exp_1)
else
d = dij_isr (map%n_in, i, j, p, map%exp_2)
end if
contains
function dij_fsr (p1, p2, expo) result (d_ij)
real(default) :: d_ij
type(vector4_t), intent(in) :: p1, p2
real(default), intent(in) :: expo
real(default) :: E1, E2
E1 = p1%p(0); E2 = p2%p(0)
d_ij = (two * p1 * p2 * E1 * E2 / (E1 + E2)**2)**expo
end function dij_fsr
function dij_threshold_gluon_from_top (i, j, p, expo) result (d_ij)
real(default) :: d_ij
integer, intent(in) :: i, j
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(in) :: expo
type(vector4_t) :: p_top
if (i == THR_POS_B) then
p_top = p(THR_POS_WP) + p(THR_POS_B)
else
p_top = p(THR_POS_WM) + p(THR_POS_BBAR)
end if
d_ij = dij_fsr (p_top, p(j), expo)
end function dij_threshold_gluon_from_top
function dij_isr (n_in, i, j, p, expo) result (d_ij)
real(default) :: d_ij
integer, intent(in) :: n_in, i, j
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(in) :: expo
real(default) :: E, y
select case (n_in)
case (1)
call get_emitter_variables (1, i, j, p, E, y)
d_ij = (E**2 * (one - y**2))**expo
case (2)
if ((i == 0 .and. j > 2) .or. (j == 0 .and. i > 2)) then
call get_emitter_variables (0, i, j, p, E, y)
d_ij = (E**2 * (one - y**2))**expo
else if ((i == 1 .and. j > 2) .or. (j == 1 .and. i > 2)) then
call get_emitter_variables (1, i, j, p, E, y)
d_ij = (two * E**2 * (one - y))**expo
else if ((i == 2 .and. j > 2) .or. (j == 2 .and. i > 2)) then
call get_emitter_variables (2, i, j, p, E, y)
d_ij = (two * E**2 * (one + y))**expo
end if
end select
end function dij_isr
subroutine get_emitter_variables (i_check, i, j, p, E, y)
integer, intent(in) :: i_check, i, j
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(out) :: E, y
if (j == i_check) then
E = energy (p(i))
y = polar_angle_ct (p(i))
else
E = energy (p(j))
y = polar_angle_ct(p(j))
end if
end subroutine get_emitter_variables
end function fks_mapping_default_dij
@ %def fks_mapping_default_dij
@ Computes the quantity
\begin{equation*}
\mathcal{D} = \sum_k \frac{1}{d_{0k}} + \sum_{kl} \frac{1}{d_{kl}}.
\end{equation*}
<<fks regions: fks mapping default: TBP>>=
procedure :: compute_sumdij => fks_mapping_default_compute_sumdij
<<fks regions: procedures>>=
subroutine fks_mapping_default_compute_sumdij (map, sregion, p)
class(fks_mapping_default_t), intent(inout) :: map
type(singular_region_t), intent(in) :: sregion
type(vector4_t), intent(in), dimension(:) :: p
real(default) :: d
integer :: alr, i, j
associate (ftuples => sregion%ftuples)
d = zero
do alr = 1, sregion%nregions
call ftuples(alr)%get (i, j)
map%pseudo_isr = ftuples(alr)%pseudo_isr
d = d + one / map%dij (p, i, j)
end do
end associate
map%sumdij = d
end subroutine fks_mapping_default_compute_sumdij
@ %def fks_mapping_default_compute_sumdij
@ Computes
\begin{equation*}
S_i = \frac{1}{\mathcal{D} d_{0i}}
\end{equation*}
or
\begin{equation*}
S_{ij} = \frac{1}{\mathcal{D} d_{ij}},
\end{equation*}
respectively.
<<fks regions: fks mapping default: TBP>>=
procedure :: svalue => fks_mapping_default_svalue
<<fks regions: procedures>>=
function fks_mapping_default_svalue (map, p, i, j, i_res) result (value)
real(default) :: value
class(fks_mapping_default_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: i, j
integer, intent(in), optional :: i_res
value = one / (map%dij (p, i, j) * map%sumdij)
end function fks_mapping_default_svalue
@ %def fks_mapping_default_svalue
@ In the soft limit, our treatment of the divergences requires a
modification of the mapping functions. Recall that there, the ratios of
the $d$-functions must approach either $1$ or $0$. This means
\begin{equation*}
\frac{d_{lm}}{d_{0m}} = \frac{(2k_l \cdot k_m) \left[E_lE_m /(E_l + E_m)^2\right]}{E_m^2 (1-y^2)} =
\overset {k_m = E_m \hat{k}} {=} \frac{E_l E_m^2}{(E_l + E_m)^2} \frac{2k_l \cdot \hat{k}}{E_m^2 (1-y^2)}
\overset {E_m \rightarrow 0}{=} \frac{2}{k_l \cdot \hat{k}}{(1-y^2)E_l},
\end{equation*}
where we have written the gluon momentum in terms of the soft momentum
$\hat{k}$. In the same limit
\begin{equation*}
\frac{d_{lm}}{d_{nm}} = \frac{k_l \cdot \hat{k}}{k_n \cdot \hat{k}} \frac{E_n}{E_l}.
\end{equation*}
From these equations we can deduce the soft limit of $d$:
\begin{align*}
d_0^{\rm{soft}} &= 1 - y^2,\\
d_1^{\rm{soft}} &= 2(1-y),\\
d_2^{\rm{soft}} &= 2(1+y),\\
d_i^{\rm{soft}} &= \frac{2 k_i \cdot \hat{k}}{E_i}.
\end{align*}
<<fks regions: fks mapping default: TBP>>=
procedure :: dij_soft => fks_mapping_default_dij_soft
<<fks regions: procedures>>=
function fks_mapping_default_dij_soft (map, p_born, p_soft, em, i_con) result (d)
real(default) :: d
class(fks_mapping_default_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
integer, intent(in) :: em
integer, intent(in), optional :: i_con
if (map%pseudo_isr) then
d = dij_soft_threshold_gluon_from_top (em, p_born, p_soft, map%exp_1)
else if (em <= map%n_in) then
d = dij_soft_isr (map%n_in, p_soft, map%exp_2)
else
d = dij_soft_fsr (p_born(em), p_soft, map%exp_1)
end if
contains
function dij_soft_threshold_gluon_from_top (em, p, p_soft, expo) result (dij_soft)
real(default) :: dij_soft
integer, intent(in) :: em
type(vector4_t), intent(in), dimension(:) :: p
type(vector4_t), intent(in) :: p_soft
real(default), intent(in) :: expo
type(vector4_t) :: p_top
if (em == THR_POS_B) then
p_top = p(THR_POS_WP) + p(THR_POS_B)
else
p_top = p(THR_POS_WM) + p(THR_POS_BBAR)
end if
dij_soft = dij_soft_fsr (p_top, p_soft, expo)
end function dij_soft_threshold_gluon_from_top
function dij_soft_fsr (p_em, p_soft, expo) result (dij_soft)
real(default) :: dij_soft
type(vector4_t), intent(in) :: p_em, p_soft
real(default), intent(in) :: expo
dij_soft = (two * p_em * p_soft / p_em%p(0))**expo
end function dij_soft_fsr
function dij_soft_isr (n_in, p_soft, expo) result (dij_soft)
real(default) :: dij_soft
integer, intent(in) :: n_in
type(vector4_t), intent(in) :: p_soft
real(default), intent(in) :: expo
real(default) :: y
y = polar_angle_ct (p_soft)
select case (n_in)
case (1)
dij_soft = one - y**2
case (2)
select case (em)
case (0)
dij_soft = one - y**2
case (1)
dij_soft = two * (one - y)
case (2)
dij_soft = two * (one + y)
case default
dij_soft = zero
call msg_fatal ("fks_mappings_default_dij_soft: n_in > 2")
end select
case default
dij_soft = zero
call msg_fatal ("fks_mappings_default_dij_soft: n_in > 2")
end select
dij_soft = dij_soft**expo
end function dij_soft_isr
end function fks_mapping_default_dij_soft
@ %def fks_mapping_default_dij_soft
@
<<fks regions: fks mapping default: TBP>>=
procedure :: compute_sumdij_soft => fks_mapping_default_compute_sumdij_soft
<<fks regions: procedures>>=
subroutine fks_mapping_default_compute_sumdij_soft (map, sregion, p_born, p_soft)
class(fks_mapping_default_t), intent(inout) :: map
type(singular_region_t), intent(in) :: sregion
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
real(default) :: d
integer :: alr, i, j
integer :: nlegs
d = zero
nlegs = size (sregion%flst_real%flst)
associate (ftuples => sregion%ftuples)
do alr = 1, sregion%nregions
call ftuples(alr)%get (i ,j)
if (j == nlegs) then
map%pseudo_isr = ftuples(alr)%pseudo_isr
d = d + one / map%dij_soft (p_born, p_soft, i)
end if
end do
end associate
map%sumdij_soft = d
end subroutine fks_mapping_default_compute_sumdij_soft
@ %def fks_mapping_default_compute_sumdij_soft
@
<<fks regions: fks mapping default: TBP>>=
procedure :: svalue_soft => fks_mapping_default_svalue_soft
<<fks regions: procedures>>=
function fks_mapping_default_svalue_soft (map, p_born, p_soft, em, i_res) result (value)
real(default) :: value
class(fks_mapping_default_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
integer, intent(in) :: em
integer, intent(in), optional :: i_res
value = one / (map%sumdij_soft * map%dij_soft (p_born, p_soft, em))
end function fks_mapping_default_svalue_soft
@ %def fks_mapping_default_svalue_soft
@
<<fks regions: interfaces>>=
interface assignment(=)
module procedure fks_mapping_default_assign
end interface
<<fks regions: procedures>>=
subroutine fks_mapping_default_assign (fks_map_out, fks_map_in)
type(fks_mapping_default_t), intent(out) :: fks_map_out
type(fks_mapping_default_t), intent(in) :: fks_map_in
fks_map_out%exp_1 = fks_map_in%exp_1
fks_map_out%exp_2 = fks_map_in%exp_2
fks_map_out%n_in = fks_map_in%n_in
end subroutine fks_mapping_default_assign
@ %def fks_mapping_default_assign
@ The $d_{ij,k}$-functions for the resonance mapping are basically the same
as in the default case, but the kinematical values here must be evaluated
in the resonance frame of reference. The energy of parton $i$ in a given
resonance frame with momentum $p_{res}$ is
\begin{equation*}
E_i = \frac{p_i^0 \cdot p_{res}}{m_{res}}.
\end{equation*}
However, since the expressions only depend on ratios of four-momenta, we
leave out the denominator because it will cancel out anyway.
<<fks regions: fks mapping resonances: TBP>>=
procedure :: dij => fks_mapping_resonances_dij
<<fks regions: procedures>>=
function fks_mapping_resonances_dij (map, p, i, j, i_con) result (d)
real(default) :: d
class(fks_mapping_resonances_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: i, j
integer, intent(in), optional :: i_con
real(default) :: E1, E2
integer :: ii_con
if (present (i_con)) then
ii_con = i_con
else
call msg_fatal ("Resonance mappings require resonance index as input!")
end if
d = 0
if (i /= j) then
if (i > 2 .and. j > 2) then
associate (p_res => map%res_map%p_res (ii_con))
E1 = p(i) * p_res
E2 = p(j) * p_res
d = two * p(i) * p(j) * E1 * E2 / (E1 + E2)**2
end associate
else
call msg_fatal ("Resonance mappings are not implemented for ISR")
end if
end if
end function fks_mapping_resonances_dij
@ %def fks_mapping_resonances_dij
@ Computes
\begin{equation*}
S_\alpha = \frac{P^{f_r(\alpha)}d^{-1}(\alpha)}
{\sum_{f_r' \in T(F_r(\alpha))}P^{f_r'}\sum_{\alpha' \in Sr(f_r')}d^{-1}(\alpha)}.
\end{equation*}
<<fks regions: fks mapping resonances: TBP>>=
procedure :: compute_sumdij => fks_mapping_resonances_compute_sumdij
<<fks regions: procedures>>=
subroutine fks_mapping_resonances_compute_sumdij (map, sregion, p)
class(fks_mapping_resonances_t), intent(inout) :: map
type(singular_region_t), intent(in) :: sregion
type(vector4_t), intent(in), dimension(:) :: p
real(default) :: d, pfr
integer :: i_res, i_reg, i, j, i_con
integer :: nlegreal
nlegreal = size (p)
d = zero
do i_reg = 1, sregion%nregions
associate (ftuple => sregion%ftuples(i_reg))
call ftuple%get (i, j)
i_res = ftuple%i_res
end associate
pfr = map%res_map%get_resonance_value (i_res, p, nlegreal)
i_con = sregion%i_reg_to_i_con (i_reg)
d = d + pfr / map%dij (p, i, j, i_con)
end do
map%sumdij = d
end subroutine fks_mapping_resonances_compute_sumdij
@ %def fks_mapping_resonances_compute_sumdij
@
<<fks regions: fks mapping resonances: TBP>>=
procedure :: svalue => fks_mapping_resonances_svalue
<<fks regions: procedures>>=
function fks_mapping_resonances_svalue (map, p, i, j, i_res) result (value)
real(default) :: value
class(fks_mapping_resonances_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: i, j
integer, intent(in), optional :: i_res
real(default) :: pfr
integer :: i_gluon
i_gluon = size (p)
pfr = map%res_map%get_resonance_value (i_res, p, i_gluon)
value = pfr / (map%dij (p, i, j, map%i_con) * map%sumdij)
end function fks_mapping_resonances_svalue
@ %def fks_mapping_resonances_svalue
@
<<fks regions: fks mapping resonances: TBP>>=
procedure :: get_resonance_weight => fks_mapping_resonances_get_resonance_weight
<<fks regions: procedures>>=
function fks_mapping_resonances_get_resonance_weight (map, alr, p) result (pfr)
real(default) :: pfr
class(fks_mapping_resonances_t), intent(in) :: map
integer, intent(in) :: alr
type(vector4_t), intent(in), dimension(:) :: p
pfr = map%res_map%get_weight (alr, p)
end function fks_mapping_resonances_get_resonance_weight
@ %def fks_mapping_resonances_get_resonance_weight
@ As above, the soft limit of $d_{ij,k}$ must be computed in the resonance frame of
reference.
<<fks regions: fks mapping resonances: TBP>>=
procedure :: dij_soft => fks_mapping_resonances_dij_soft
<<fks regions: procedures>>=
function fks_mapping_resonances_dij_soft (map, p_born, p_soft, em, i_con) result (d)
real(default) :: d
class(fks_mapping_resonances_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
integer, intent(in) :: em
integer, intent(in), optional :: i_con
real(default) :: E1, E2
integer :: ii_con
type(vector4_t) :: pb
if (present (i_con)) then
ii_con = i_con
else
call msg_fatal ("fks_mapping_resonances requires resonance index")
end if
associate (p_res => map%res_map%p_res(ii_con))
pb = p_born(em)
E1 = pb * p_res
E2 = p_soft * p_res
d = two * pb * p_soft * E1 * E2 / E1**2
end associate
end function fks_mapping_resonances_dij_soft
@ %def fks_mapping_resonances_dij_soft
@
<<fks regions: fks mapping resonances: TBP>>=
procedure :: compute_sumdij_soft => fks_mapping_resonances_compute_sumdij_soft
<<fks regions: procedures>>=
subroutine fks_mapping_resonances_compute_sumdij_soft (map, sregion, p_born, p_soft)
class(fks_mapping_resonances_t), intent(inout) :: map
type(singular_region_t), intent(in) :: sregion
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
real(default) :: d
real(default) :: pfr
integer :: i_res, i, j, i_reg, i_con
integer :: nlegs
d = zero
nlegs = size (sregion%flst_real%flst)
do i_reg = 1, sregion%nregions
associate (ftuple => sregion%ftuples(i_reg))
call ftuple%get(i, j)
i_res = ftuple%i_res
end associate
pfr = map%res_map%get_resonance_value (i_res, p_born)
i_con = sregion%i_reg_to_i_con (i_reg)
if (j == nlegs) d = d + pfr / map%dij_soft (p_born, p_soft, i, i_con)
end do
map%sumdij_soft = d
end subroutine fks_mapping_resonances_compute_sumdij_soft
@ %def fks_mapping_resonances_compute_sumdij_soft
@
<<fks regions: fks mapping resonances: TBP>>=
procedure :: svalue_soft => fks_mapping_resonances_svalue_soft
<<fks regions: procedures>>=
function fks_mapping_resonances_svalue_soft (map, p_born, p_soft, em, i_res) result (value)
real(default) :: value
class(fks_mapping_resonances_t), intent(in) :: map
type(vector4_t), intent(in), dimension(:) :: p_born
type(vector4_t), intent(in) :: p_soft
integer, intent(in) :: em
integer, intent(in), optional :: i_res
real(default) :: pfr
pfr = map%res_map%get_resonance_value (i_res, p_born)
value = pfr / (map%sumdij_soft * map%dij_soft (p_born, p_soft, em, map%i_con))
end function fks_mapping_resonances_svalue_soft
@ %def fks_mapping_resonances_svalue_soft
@
<<fks regions: fks mapping resonances: TBP>>=
procedure :: set_resonance_momentum => fks_mapping_resonances_set_resonance_momentum
<<fks regions: procedures>>=
subroutine fks_mapping_resonances_set_resonance_momentum (map, p)
class(fks_mapping_resonances_t), intent(inout) :: map
type(vector4_t), intent(in) :: p
map%res_map%p_res = p
end subroutine fks_mapping_resonances_set_resonance_momentum
@ %def fks_mapping_resonances_set_resonance_momentum
@
<<fks regions: fks mapping resonances: TBP>>=
procedure :: set_resonance_momenta => fks_mapping_resonances_set_resonance_momenta
<<fks regions: procedures>>=
subroutine fks_mapping_resonances_set_resonance_momenta (map, p)
class(fks_mapping_resonances_t), intent(inout) :: map
type(vector4_t), intent(in), dimension(:) :: p
map%res_map%p_res = p
end subroutine fks_mapping_resonances_set_resonance_momenta
@ %def fks_mapping_resonances_set_resonance_momenta
@
<<fks regions: interfaces>>=
interface assignment(=)
module procedure fks_mapping_resonances_assign
end interface
<<fks regions: procedures>>=
subroutine fks_mapping_resonances_assign (fks_map_out, fks_map_in)
type(fks_mapping_resonances_t), intent(out) :: fks_map_out
type(fks_mapping_resonances_t), intent(in) :: fks_map_in
fks_map_out%exp_1 = fks_map_in%exp_1
fks_map_out%exp_2 = fks_map_in%exp_2
fks_map_out%res_map = fks_map_in%res_map
end subroutine fks_mapping_resonances_assign
@ %def fks_mapping_resonances_assign
@
<<fks regions: public>>=
public :: create_resonance_histories_for_threshold
<<fks regions: procedures>>=
function create_resonance_histories_for_threshold () result (res_history)
type(resonance_history_t) :: res_history
res_history%n_resonances = 2
allocate (res_history%resonances (2))
allocate (res_history%resonances(1)%contributors%c(2))
allocate (res_history%resonances(2)%contributors%c(2))
res_history%resonances(1)%contributors%c = [THR_POS_WP, THR_POS_B]
res_history%resonances(2)%contributors%c = [THR_POS_WM, THR_POS_BBAR]
end function create_resonance_histories_for_threshold
@ %def create_resonance_histories_for_threshold
@
<<fks regions: public>>=
public :: setup_region_data_for_test
<<fks regions: procedures>>=
subroutine setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, nlo_corr_type)
integer, intent(in) :: n_in
integer, intent(in), dimension(:,:) :: flv_born, flv_real
type(string_t), intent(in) :: nlo_corr_type
type(region_data_t), intent(out) :: reg_data
type(model_t), pointer :: test_model => null ()
call create_test_model (var_str ("SM"), test_model)
call test_model%set_real (var_str ("me"), 0._default)
call test_model%set_real (var_str ("mmu"), 0._default)
call test_model%set_real (var_str ("mtau"), 0._default)
call test_model%set_real (var_str ("ms"), 0._default)
call test_model%set_real (var_str ("mc"), 0._default)
call test_model%set_real (var_str ("mb"), 0._default)
call reg_data%init (n_in, test_model, flv_born, flv_real, nlo_corr_type)
end subroutine setup_region_data_for_test
@ %def setup_region_data_for_test
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Unit tests}
\clearpage
<<[[fks_regions_ut.f90]]>>=
<<File header>>
module fks_regions_ut
use unit_tests
use fks_regions_uti
<<Standard module head>>
<<fks regions: public test>>
contains
<<fks regions: test driver>>
end module fks_regions_ut
@ %def fks_regions_ut
@
<<[[fks_regions_uti.f90]]>>=
<<File header>>
module fks_regions_uti
<<Use strings>>
use format_utils, only: write_separator
use os_interface
use models
use fks_regions
<<Standard module head>>
<<fks regions: test declarations>>
contains
<<fks regions: tests>>
end module fks_regions_uti
@ %def fks_regions_uti
@
<<fks regions: public test>>=
public :: fks_regions_test
<<fks regions: test driver>>=
subroutine fks_regions_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
call test(fks_regions_1, "fks_regions_1", &
"Test flavor structure utilities", u, results)
call test(fks_regions_2, "fks_regions_2", &
"Test singular regions for final-state radiation for n = 2", &
u, results)
call test(fks_regions_3, "fks_regions_3", &
"Test singular regions for final-state radiation for n = 3", &
u, results)
call test(fks_regions_4, "fks_regions_4", &
"Test singular regions for final-state radiation for n = 4", &
u, results)
call test(fks_regions_5, "fks_regions_5", &
"Test singular regions for final-state radiation for n = 5", &
u, results)
call test(fks_regions_6, "fks_regions_6", &
"Test singular regions for initial-state radiation", &
u, results)
call test(fks_regions_7, "fks_regions_7", &
"Check Latex output", u, results)
call test(fks_regions_8, "fks_regions_8", &
"Test singular regions for initial-state photon contributions", &
u, results)
end subroutine fks_regions_test
@ %def fks_regions_test
@
<<fks regions: test declarations>>=
public :: fks_regions_1
<<fks regions: tests>>=
subroutine fks_regions_1 (u)
integer, intent(in) :: u
type(flv_structure_t) :: flv_born, flv_real
type(model_t), pointer :: test_model => null ()
write (u, "(A)") "* Test output: fks_regions_1"
write (u, "(A)") "* Purpose: Test utilities of flavor structure manipulation"
write (u, "(A)")
call create_test_model (var_str ("SM"), test_model)
flv_born = [11, -11, 2, -2]
flv_real = [11, -11, 2, -2, 21]
flv_born%n_in = 2; flv_real%n_in = 2
write (u, "(A)") "* Valid splittings of ee -> uu"
write (u, "(A)") "Born Flavors: "
call flv_born%write (u)
write (u, "(A)") "Real Flavors: "
call flv_real%write (u)
write (u, "(A,L1)") "3, 4 (2, -2) : ", flv_real%valid_pair (3, 4, flv_born, test_model)
write (u, "(A,L1)") "4, 3 (-2, 2) : ", flv_real%valid_pair (4, 3, flv_born, test_model)
write (u, "(A,L1)") "3, 5 (2, 21) : ", flv_real%valid_pair (3, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 3 (21, 2) : ", flv_real%valid_pair (5, 3, flv_born, test_model)
write (u, "(A,L1)") "4, 5 (-2, 21): ", flv_real%valid_pair (4, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 4 (21, -2): ", flv_real%valid_pair (5, 4, flv_born, test_model)
call write_separator (u)
call flv_born%final ()
call flv_real%final ()
flv_born = [2, -2, 11, -11]
flv_real = [2, -2, 11, -11, 21]
flv_born%n_in = 2; flv_real%n_in = 2
write (u, "(A)") "* Valid splittings of uu -> ee"
write (u, "(A)") "Born Flavors: "
call flv_born%write (u)
write (u, "(A)") "Real Flavors: "
call flv_real%write (u)
write (u, "(A,L1)") "1, 2 (2, -2) : " , flv_real%valid_pair (1, 2, flv_born, test_model)
write (u, "(A,L1)") "2, 1 (-2, 2) : " , flv_real%valid_pair (2, 1, flv_born, test_model)
write (u, "(A,L1)") "5, 2 (21, -2): " , flv_real%valid_pair (5, 2, flv_born, test_model)
write (u, "(A,L1)") "2, 5 (-2, 21): " , flv_real%valid_pair (2, 5, flv_born, test_model)
write (u, "(A,L1)") "1, 5 (21, 2) : " , flv_real%valid_pair (5, 1, flv_born, test_model)
write (u, "(A,L1)") "5, 1 (2, 21) : " , flv_real%valid_pair (1, 5, flv_born, test_model)
call flv_real%final ()
flv_real = [21, -2, 11, -11, -2]
flv_real%n_in = 2
write (u, "(A)") "Real Flavors: "
call flv_real%write (u)
write (u, "(A,L1)") "1, 2 (21, -2): " , flv_real%valid_pair (1, 2, flv_born, test_model)
write (u, "(A,L1)") "2, 1 (-2, 21): " , flv_real%valid_pair (2, 1, flv_born, test_model)
write (u, "(A,L1)") "5, 2 (-2, -2): " , flv_real%valid_pair (5, 2, flv_born, test_model)
write (u, "(A,L1)") "2, 5 (-2, -2): " , flv_real%valid_pair (2, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 1 (-2, 21): " , flv_real%valid_pair (5, 1, flv_born, test_model)
write (u, "(A,L1)") "1, 5 (21, -2): " , flv_real%valid_pair (1, 5, flv_born, test_model)
call flv_real%final ()
flv_real = [2, 21, 11, -11, 2]
flv_real%n_in = 2
write (u, "(A)") "Real Flavors: "
call flv_real%write (u)
write (u, "(A,L1)") "1, 2 (2, 21) : " , flv_real%valid_pair (1, 2, flv_born, test_model)
write (u, "(A,L1)") "2, 1 (21, 2) : " , flv_real%valid_pair (2, 1, flv_born, test_model)
write (u, "(A,L1)") "5, 2 (2, 21) : " , flv_real%valid_pair (5, 2, flv_born, test_model)
write (u, "(A,L1)") "2, 5 (21, 2) : " , flv_real%valid_pair (2, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 1 (2, 2) : " , flv_real%valid_pair (5, 1, flv_born, test_model)
write (u, "(A,L1)") "1, 5 (2, 2) : " , flv_real%valid_pair (1, 5, flv_born, test_model)
call write_separator (u)
call flv_born%final ()
call flv_real%final ()
flv_born = [11, -11, 2, -2, 21]
flv_real = [11, -11, 2, -2, 21, 21]
flv_born%n_in = 2; flv_real%n_in = 2
write (u, "(A)") "* Valid splittings of ee -> uug"
write (u, "(A)") "Born Flavors: "
call flv_born%write (u)
write (u, "(A)") "Real Flavors: "
call flv_real%write (u)
write (u, "(A,L1)") "3, 4 (2, -2) : " , flv_real%valid_pair (3, 4, flv_born, test_model)
write (u, "(A,L1)") "4, 3 (-2, 2) : " , flv_real%valid_pair (4, 3, flv_born, test_model)
write (u, "(A,L1)") "3, 5 (2, 21) : " , flv_real%valid_pair (3, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 3 (21, 2) : " , flv_real%valid_pair (5, 3, flv_born, test_model)
write (u, "(A,L1)") "4, 5 (-2, 21): " , flv_real%valid_pair (4, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 4 (21, -2): " , flv_real%valid_pair (5, 4, flv_born, test_model)
write (u, "(A,L1)") "3, 6 (2, 21) : " , flv_real%valid_pair (3, 6, flv_born, test_model)
write (u, "(A,L1)") "6, 3 (21, 2) : " , flv_real%valid_pair (6, 3, flv_born, test_model)
write (u, "(A,L1)") "4, 6 (-2, 21): " , flv_real%valid_pair (4, 6, flv_born, test_model)
write (u, "(A,L1)") "6, 4 (21, -2): " , flv_real%valid_pair (6, 4, flv_born, test_model)
write (u, "(A,L1)") "5, 6 (21, 21): " , flv_real%valid_pair (5, 6, flv_born, test_model)
write (u, "(A,L1)") "6, 5 (21, 21): " , flv_real%valid_pair (6, 5, flv_born, test_model)
call flv_real%final ()
flv_real = [11, -11, 2, -2, 1, -1]
flv_real%n_in = 2
write (u, "(A)") "Real Flavors (exemplary g -> dd splitting): "
call flv_real%write (u)
write (u, "(A,L1)") "3, 4 (2, -2) : " , flv_real%valid_pair (3, 4, flv_born, test_model)
write (u, "(A,L1)") "4, 3 (-2, 2) : " , flv_real%valid_pair (4, 3, flv_born, test_model)
write (u, "(A,L1)") "3, 5 (2, 1) : " , flv_real%valid_pair (3, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 3 (1, 2) : " , flv_real%valid_pair (5, 3, flv_born, test_model)
write (u, "(A,L1)") "4, 5 (-2, 1) : " , flv_real%valid_pair (4, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 4 (1, -2) : " , flv_real%valid_pair (5, 4, flv_born, test_model)
write (u, "(A,L1)") "3, 6 (2, -1) : " , flv_real%valid_pair (3, 6, flv_born, test_model)
write (u, "(A,L1)") "6, 3 (-1, 2) : " , flv_real%valid_pair (6, 3, flv_born, test_model)
write (u, "(A,L1)") "4, 6 (-2, -1): " , flv_real%valid_pair (4, 6, flv_born, test_model)
write (u, "(A,L1)") "6, 4 (-1, -2): " , flv_real%valid_pair (6, 4, flv_born, test_model)
write (u, "(A,L1)") "5, 6 (1, -1) : " , flv_real%valid_pair (5, 6, flv_born, test_model)
write (u, "(A,L1)") "6, 5 (-1, 1) : " , flv_real%valid_pair (6, 5, flv_born, test_model)
call write_separator (u)
call flv_born%final ()
call flv_real%final ()
flv_born = [6, -5, 2, -1 ]
flv_real = [6, -5, 2, -1, 21]
flv_born%n_in = 1; flv_real%n_in = 1
write (u, "(A)") "* Valid splittings of t -> b u d~"
write (u, "(A)") "Born Flavors: "
call flv_born%write (u)
write (u, "(A)") "Real Flavors: "
call flv_real%write (u)
write (u, "(A,L1)") "1, 2 (6, -5) : " , flv_real%valid_pair (1, 2, flv_born, test_model)
write (u, "(A,L1)") "1, 3 (6, 2) : " , flv_real%valid_pair (1, 3, flv_born, test_model)
write (u, "(A,L1)") "1, 4 (6, -1) : " , flv_real%valid_pair (1, 4, flv_born, test_model)
write (u, "(A,L1)") "2, 1 (-5, 6) : " , flv_real%valid_pair (2, 1, flv_born, test_model)
write (u, "(A,L1)") "3, 1 (2, 6) : " , flv_real%valid_pair (3, 1, flv_born, test_model)
write (u, "(A,L1)") "4, 1 (-1, 6) : " , flv_real%valid_pair (4, 1, flv_born, test_model)
write (u, "(A,L1)") "2, 3 (-5, 2) : " , flv_real%valid_pair (2, 3, flv_born, test_model)
write (u, "(A,L1)") "2, 4 (-5, -1): " , flv_real%valid_pair (2, 4, flv_born, test_model)
write (u, "(A,L1)") "3, 2 (2, -5) : " , flv_real%valid_pair (3, 2, flv_born, test_model)
write (u, "(A,L1)") "4, 2 (-1, -5): " , flv_real%valid_pair (4, 2, flv_born, test_model)
write (u, "(A,L1)") "3, 4 (2, -1) : " , flv_real%valid_pair (3, 4, flv_born, test_model)
write (u, "(A,L1)") "4, 3 (-1, 2) : " , flv_real%valid_pair (4, 3, flv_born, test_model)
write (u, "(A,L1)") "1, 5 (6, 21) : " , flv_real%valid_pair (1, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 1 (21, 6) : " , flv_real%valid_pair (5, 1, flv_born, test_model)
write (u, "(A,L1)") "2, 5 (-5, 21): " , flv_real%valid_pair (2, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 2 (21, 5) : " , flv_real%valid_pair (5, 2, flv_born, test_model)
write (u, "(A,L1)") "3, 5 (2, 21) : " , flv_real%valid_pair (3, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 3 (21, 2) : " , flv_real%valid_pair (5, 3, flv_born, test_model)
write (u, "(A,L1)") "4, 5 (-1, 21): " , flv_real%valid_pair (4, 5, flv_born, test_model)
write (u, "(A,L1)") "5, 4 (21, -1): " , flv_real%valid_pair (5, 4, flv_born, test_model)
call flv_born%final ()
call flv_real%final ()
end subroutine fks_regions_1
@ %def fks_regions_1
@
<<fks regions: test declarations>>=
public :: fks_regions_2
<<fks regions: tests>>=
subroutine fks_regions_2 (u)
integer, intent(in) :: u
integer :: n_flv_born, n_flv_real
integer :: n_legs_born, n_legs_real
integer :: n_in
integer, dimension(:,:), allocatable :: flv_born, flv_real
type(region_data_t) :: reg_data
write (u, "(A)") "* Test output: fks_regions_2"
write (u, "(A)") "* Create singular regions for processes with up to four singular regions"
write (u, "(A)") "* ee -> qq with QCD corrections"
write (u, "(A)")
n_flv_born = 1; n_flv_real = 1
n_legs_born = 4; n_legs_real = 5
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, 2, -2]
flv_real (:, 1) = [11, -11, 2, -2, 21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
call write_separator (u)
write (u, "(A)") "* ee -> qq with QED corrections"
write (u, "(A)")
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, 2, -2]
flv_real (:, 1) = [11, -11, 2, -2, 22]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QED"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
call write_separator (u)
write (u, "(A)") "* ee -> tt"
write (u, "(A)")
write (u, "(A)") "* This process has four singular regions because they are not equivalent."
n_flv_born = 1; n_flv_real = 1
n_legs_born = 6; n_legs_real = 7
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, 6, -6, 6, -6]
flv_real (:, 1) = [11, -11, 6, -6, 6, -6, 21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
end subroutine fks_regions_2
@ %def fks_regions_2
@
<<fks regions: test declarations>>=
public :: fks_regions_3
<<fks regions: tests>>=
subroutine fks_regions_3 (u)
integer, intent(in) :: u
integer :: n_flv_born, n_flv_real
integer :: n_legs_born, n_legs_real
integer :: n_in, i, j
integer, dimension(:,:), allocatable :: flv_born, flv_real
type(region_data_t) :: reg_data
write (u, "(A)") "* Test output: fks_regions_3"
write (u, "(A)") "* Create singular regions for processes with three final-state particles"
write (u, "(A)") "* ee -> qqg"
write (u, "(A)")
n_flv_born = 1; n_flv_real = 2
n_legs_born = 5; n_legs_real = 6
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, 2, -2, 21]
flv_real (:, 1) = [11, -11, 2, -2, 21, 21]
flv_real (:, 2) = [11, -11, 2, -2, 1, -1]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
call write_separator (u)
write (u, "(A)") "* ee -> qqA"
write (u, "(A)")
n_flv_born = 1; n_flv_real = 2
n_legs_born = 5; n_legs_real = 6
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, 2, -2, 22]
flv_real (:, 1) = [11, -11, 2, -2, 22, 22]
flv_real (:, 2) = [11, -11, 2, -2, 11, -11]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QED"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
call write_separator (u)
write (u, "(A)") "* ee -> jet jet jet"
write (u, "(A)") "* with jet = u:U:d:D:s:S:c:C:b:B:gl"
write (u, "(A)")
n_flv_born = 5; n_flv_real = 22
n_legs_born = 5; n_legs_real = 6
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, -4, 4, 21]
flv_born (:, 2) = [11, -11, -2, 2, 21]
flv_born (:, 3) = [11, -11, -5, 5, 21]
flv_born (:, 4) = [11, -11, -3, 3, 21]
flv_born (:, 5) = [11, -11, -1, 1, 21]
flv_real (:, 1) = [11, -11, -4, -4, 4, 4]
flv_real (:, 2) = [11, -11, -4, -2, 2, 4]
flv_real (:, 3) = [11, -11, -4, 4, 21, 21]
flv_real (:, 4) = [11, -11, -4, -5, 4, 5]
flv_real (:, 5) = [11, -11, -4, -3, 4, 3]
flv_real (:, 6) = [11, -11, -4, -1, 2, 3]
flv_real (:, 7) = [11, -11, -4, -1, 4, 1]
flv_real (:, 8) = [11, -11, -2, -2, 2, 2]
flv_real (:, 9) = [11, -11, -2, 2, 21, 21]
flv_real (:, 10) = [11, -11, -2, -5, 2, 5]
flv_real (:, 11) = [11, -11, -2, -3, 2, 3]
flv_real (:, 12) = [11, -11, -2, -3, 4, 1]
flv_real (:, 13) = [11, -11, -2, -1, 2, 1]
flv_real (:, 14) = [11, -11, -5, -5, 5, 5]
flv_real (:, 15) = [11, -11, -5, -3, 3, 5]
flv_real (:, 16) = [11, -11, -5, -1, 1, 5]
flv_real (:, 17) = [11, -11, -5, 5, 21, 21]
flv_real (:, 18) = [11, -11, -3, -3, 3, 3]
flv_real (:, 19) = [11, -11, -3, -1, 1, 3]
flv_real (:, 20) = [11, -11, -3, 3, 21, 21]
flv_real (:, 21) = [11, -11, -1, -1, 1, 1]
flv_real (:, 22) = [11, -11, -1, 1, 21, 21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
call write_separator (u)
write (u, "(A)") "* ee -> L L A"
write (u, "(A)") "* with L = e2:E2:e3:E3"
write (u, "(A)")
n_flv_born = 2; n_flv_real = 6
n_legs_born = 5; n_legs_real = 6
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, -15, 15, 22]
flv_born (:, 2) = [11, -11, -13, 13, 22]
flv_real (:, 1) = [11, -11, -15, -15, 15, 15]
flv_real (:, 2) = [11, -11, -15, -13, 13, 13]
flv_real (:, 3) = [11, -11, -13, -15, 13, 15]
flv_real (:, 4) = [11, -11, -15, 15, 22, 22]
flv_real (:, 5) = [11, -11, -13, -13, 13, 13]
flv_real (:, 6) = [11, -11, -13, 13, 22, 22]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QED"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
end subroutine fks_regions_3
@ %def fks_regions_3
@
<<fks regions: test declarations>>=
public :: fks_regions_4
<<fks regions: tests>>=
subroutine fks_regions_4 (u)
integer, intent(in) :: u
integer :: n_flv_born, n_flv_real
integer :: n_legs_born, n_legs_real
integer :: n_in
integer, dimension(:,:), allocatable :: flv_born, flv_real
type(region_data_t) :: reg_data
write (u, "(A)") "* Test output: fks_regions_4"
write (u, "(A)") "* Create singular regions for processes with four final-state particles"
write (u, "(A)") "* ee -> 4 jet"
write (u, "(A)") "* with jet = u:U:d:D:s:S:c:C:b:B:gl"
write (u, "(A)")
n_flv_born = 22; n_flv_real = 22
n_legs_born = 6; n_legs_real = 7
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, -4, -4, 4, 4]
flv_born (:, 2) = [11, -11, -4, -2, 2, 4]
flv_born (:, 3) = [11, -11, -4, 4, 21, 21]
flv_born (:, 4) = [11, -11, -4, -5, 4, 5]
flv_born (:, 5) = [11, -11, -4, -3, 4, 3]
flv_born (:, 6) = [11, -11, -4, -1, 2, 3]
flv_born (:, 7) = [11, -11, -4, -1, 4, 1]
flv_born (:, 8) = [11, -11, -2, -2, 2, 2]
flv_born (:, 9) = [11, -11, -2, 2, 21, 21]
flv_born (:, 10) = [11, -11, -2, -5, 2, 5]
flv_born (:, 11) = [11, -11, -2, -3, 2, 3]
flv_born (:, 12) = [11, -11, -2, -3, 4, 1]
flv_born (:, 13) = [11, -11, -2, -1, 2, 1]
flv_born (:, 14) = [11, -11, -5, -5, 5, 5]
flv_born (:, 15) = [11, -11, -5, -3, 3, 5]
flv_born (:, 16) = [11, -11, -5, -1, 1, 5]
flv_born (:, 17) = [11, -11, -5, 5, 21, 21]
flv_born (:, 18) = [11, -11, -3, -3, 3, 3]
flv_born (:, 19) = [11, -11, -3, -1, 1, 3]
flv_born (:, 20) = [11, -11, -3, -3, 21, 21]
flv_born (:, 21) = [11, -11, -1, -1, 1, 1]
flv_born (:, 22) = [11, -11, -1, 1, 21, 21]
flv_real (:, 1) = [11, -11, -4, -4, 4, 4, 21]
flv_real (:, 2) = [11, -11, -4, -2, 2, 4, 21]
flv_real (:, 3) = [11, -11, -4, 4, 21, 21, 21]
flv_real (:, 4) = [11, -11, -4, -5, 4, 5, 21]
flv_real (:, 5) = [11, -11, -4, -3, 4, 3, 21]
flv_real (:, 6) = [11, -11, -4, -1, 2, 3, 21]
flv_real (:, 7) = [11, -11, -4, -1, 4, 1, 21]
flv_real (:, 8) = [11, -11, -2, -2, 2, 2, 21]
flv_real (:, 9) = [11, -11, -2, 2, 21, 21, 21]
flv_real (:, 10) = [11, -11, -2, -5, 2, 5, 21]
flv_real (:, 11) = [11, -11, -2, -3, 2, 3, 21]
flv_real (:, 12) = [11, -11, -2, -3, 4, 1, 21]
flv_real (:, 13) = [11, -11, -2, -1, 2, 1, 21]
flv_real (:, 14) = [11, -11, -5, -5, 5, 5, 21]
flv_real (:, 15) = [11, -11, -5, -3, 3, 5, 21]
flv_real (:, 16) = [11, -11, -5, -1, 1, 5, 21]
flv_real (:, 17) = [11, -11, -5, 5, 21, 21, 21]
flv_real (:, 18) = [11, -11, -3, -3, 3, 3, 21]
flv_real (:, 19) = [11, -11, -3, -1, 1, 3, 21]
flv_real (:, 20) = [11, -11, -3, 3, 21, 21, 21]
flv_real (:, 21) = [11, -11, -1, -1, 1, 1, 21]
flv_real (:, 22) = [11, -11, -1, 1, 21, 21, 21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
call write_separator (u)
write (u, "(A)") "* ee -> bbmumu with QCD corrections"
write (u, "(A)")
n_flv_born = 1; n_flv_real = 1
n_legs_born = 6; n_legs_real = 7
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, -5, 5, -13, 13]
flv_real (:, 1) = [11, -11, -5, 5, -13, 13, 21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
call write_separator (u)
write (u, "(A)") "* ee -> bbmumu with QED corrections"
write (u, "(A)")
n_flv_born = 1; n_flv_real = 1
n_legs_born = 6; n_legs_real = 7
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, -5, 5, -13, 13]
flv_real (:, 1) = [11, -11, -5, 5, -13, 13, 22]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
end subroutine fks_regions_4
@ %def fks_regions_4
@
<<fks regions: test declarations>>=
public :: fks_regions_5
<<fks regions: tests>>=
subroutine fks_regions_5 (u)
integer, intent(in) :: u
integer :: n_flv_born, n_flv_real
integer :: n_legs_born, n_legs_real
integer :: n_in
integer, dimension(:,:), allocatable :: flv_born, flv_real
type(region_data_t) :: reg_data
write (u, "(A)") "* Test output: fks_regions_5"
write (u, "(A)") "* Create singular regions for processes with five final-state particles"
write (u, "(A)") "* ee -> 5 jet"
write (u, "(A)") "* with jet = u:U:d:D:s:S:c:C:b:B:gl"
write (u, "(A)")
n_flv_born = 22; n_flv_real = 67
n_legs_born = 7; n_legs_real = 8
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:,1) = [11,-11,-4,-4,4,4,21]
flv_born (:,2) = [11,-11,-4,-2,2,4,21]
flv_born (:,3) = [11,-11,-4,4,21,21,21]
flv_born (:,4) = [11,-11,-4,-5,4,5,21]
flv_born (:,5) = [11,-11,-4,-3,4,3,21]
flv_born (:,6) = [11,-11,-4,-1,2,3,21]
flv_born (:,7) = [11,-11,-4,-1,4,1,21]
flv_born (:,8) = [11,-11,-2,-2,2,2,21]
flv_born (:,9) = [11,-11,-2,2,21,21,21]
flv_born (:,10) = [11,-11,-2,-5,2,5,21]
flv_born (:,11) = [11,-11,-2,-3,2,3,21]
flv_born (:,12) = [11,-11,-2,-3,4,1,21]
flv_born (:,13) = [11,-11,-2,-1,2,1,21]
flv_born (:,14) = [11,-11,-5,-5,5,5,21]
flv_born (:,15) = [11,-11,-5,-3,3,5,21]
flv_born (:,16) = [11,-11,-5,-1,1,5,21]
flv_born (:,17) = [11,-11,-5,5,21,21,21]
flv_born (:,18) = [11,-11,-3,-3,3,3,21]
flv_born (:,19) = [11,-11,-3,-1,1,3,21]
flv_born (:,20) = [11,-11,-3,3,21,21,21]
flv_born (:,21) = [11,-11,-1,-1,1,1,21]
flv_born (:,22) = [11,-11,-1,1,21,21,21]
flv_real (:,1) = [11,-11,-4,-4,-4,4,4,4]
flv_real (:,2) = [11,-11,-4,-4,-2,2,4,4]
flv_real (:,3) = [11,-11,-4,-4,4,4,21,21]
flv_real (:,4) = [11,-11,-4,-4,-5,4,4,5]
flv_real (:,5) = [11,-11,-4,-4,-3,4,4,3]
flv_real (:,6) = [11,-11,-4,-4,-1,2,4,3]
flv_real (:,7) = [11,-11,-4,-4,-1,4,4,1]
flv_real (:,8) = [11,-11,-4,-2,-2,2,2,4]
flv_real (:,9) = [11,-11,-4,-2,2,4,21,21]
flv_real (:,10) = [11,-11,-4,-2,-5,2,4,5]
flv_real (:,11) = [11,-11,-4,-2,-3,2,4,3]
flv_real (:,12) = [11,-11,-4,-2,-3,4,4,1]
flv_real (:,13) = [11,-11,-4,-2,-1,2,2,3]
flv_real (:,14) = [11,-11,-4,-2,-1,2,4,1]
flv_real (:,15) = [11,-11,-4,4,21,21,21,21]
flv_real (:,16) = [11,-11,-4,-5,4,5,21,21]
flv_real (:,17) = [11,-11,-4,-5,-5,4,5,5]
flv_real (:,18) = [11,-11,-4,-5,-3,4,3,5]
flv_real (:,19) = [11,-11,-4,-5,-1,2,3,5]
flv_real (:,20) = [11,-11,-4,-5,-1,4,1,5]
flv_real (:,21) = [11,-11,-4,-3,4,3,21,21]
flv_real (:,22) = [11,-11,-4,-3,-3,4,3,3]
flv_real (:,23) = [11,-11,-4,-3,-1,2,3,3]
flv_real (:,24) = [11,-11,-4,-3,-1,4,1,3]
flv_real (:,25) = [11,-11,-4,-1,2,3,21,21]
flv_real (:,26) = [11,-11,-4,-1,4,1,21,21]
flv_real (:,27) = [11,-11,-4,-1,-1,2,1,3]
flv_real (:,28) = [11,-11,-4,-1,-1,4,1,1]
flv_real (:,29) = [11,-11,-2,-2,-2,2,2,2]
flv_real (:,30) = [11,-11,-2,-2,2,2,21,21]
flv_real (:,31) = [11,-11,-2,-2,-5,2,2,5]
flv_real (:,32) = [11,-11,-2,-2,-3,2,2,3]
flv_real (:,33) = [11,-11,-2,-2,-3,2,4,1]
flv_real (:,34) = [11,-11,-2,-2,-1,2,2,1]
flv_real (:,35) = [11,-11,-2,2,21,21,21,21]
flv_real (:,36) = [11,-11,-2,-5,2,5,21,21]
flv_real (:,37) = [11,-11,-2,-5,-5,2,5,5]
flv_real (:,38) = [11,-11,-2,-5,-3,2,3,5]
flv_real (:,39) = [11,-11,-2,-5,-3,4,1,5]
flv_real (:,40) = [11,-11,-2,-5,-1,2,1,5]
flv_real (:,41) = [11,-11,-2,-3,2,3,21,21]
flv_real (:,42) = [11,-11,-2,-3,4,1,21,21]
flv_real (:,43) = [11,-11,-2,-3,-3,2,3,3]
flv_real (:,44) = [11,-11,-2,-3,-3,4,1,3]
flv_real (:,45) = [11,-11,-2,-3,-1,2,1,3]
flv_real (:,46) = [11,-11,-2,-3,-1,4,1,1]
flv_real (:,47) = [11,-11,-2,-1,2,1,21,21]
flv_real (:,48) = [11,-11,-2,-1,-1,2,1,1]
flv_real (:,49) = [11,-11,-5,-5,-5,5,5,5]
flv_real (:,50) = [11,-11,-5,-5,-3,3,5,5]
flv_real (:,51) = [11,-11,-5,-5,-1,1,5,5]
flv_real (:,52) = [11,-11,-5,-5,5,5,21,21]
flv_real (:,53) = [11,-11,-5,-3,-3,3,3,5]
flv_real (:,54) = [11,-11,-5,-3,-1,1,3,5]
flv_real (:,55) = [11,-11,-5,-3,3,5,21,21]
flv_real (:,56) = [11,-11,-5,-1,-1,1,1,5]
flv_real (:,57) = [11,-11,-5,-1,1,5,21,21]
flv_real (:,58) = [11,-11,-5,5,21,21,21,21]
flv_real (:,59) = [11,-11,-3,-3,-3,3,3,3]
flv_real (:,60) = [11,-11,-3,-3,-1,1,3,3]
flv_real (:,61) = [11,-11,-3,-3,3,3,21,21]
flv_real (:,62) = [11,-11,-3,-1,-1,1,1,3]
flv_real (:,63) = [11,-11,-3,-1,1,3,21,21]
flv_real (:,64) = [11,-11,-3,3,21,21,21,21]
flv_real (:,65) = [11,-11,-1,-1,-1,1,1,1]
flv_real (:,66) = [11,-11,-1,-1,1,1,21,21]
flv_real (:,67) = [11,-11,-1,1,21,21,21,21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
end subroutine fks_regions_5
@ %def fks_regions_5
@
<<fks regions: test declarations>>=
public :: fks_regions_6
<<fks regions: tests>>=
subroutine fks_regions_6 (u)
integer, intent(in) :: u
integer :: n_flv_born, n_flv_real
integer :: n_legs_born, n_legs_real
integer :: n_in
integer, dimension(:,:), allocatable :: flv_born, flv_real
type(region_data_t) :: reg_data
integer :: i, j
integer, dimension(10) :: flavors
write (u, "(A)") "* Test output: fks_regions_6"
write (u, "(A)") "* Create table of singular regions for Drell Yan"
write (u, "(A)")
n_flv_born = 10; n_flv_real = 30
n_legs_born = 4; n_legs_real = 5
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flavors = [-5, -4, -3, -2, -1, 1, 2, 3, 4, 5]
do i = 1, n_flv_born
flv_born (3:4, i) = [11, -11]
end do
do j = 1, n_flv_born
flv_born (1, j) = flavors (j)
flv_born (2, j) = -flavors (j)
end do
do i = 1, n_flv_real
flv_real (3:4, i) = [11, -11]
end do
i = 1
do j = 1, n_flv_real
if (mod (j, 3) == 1) then
flv_real (1, j) = flavors (i)
flv_real (2, j) = -flavors (i)
flv_real (5, j) = 21
else if (mod (j, 3) == 2) then
flv_real (1, j) = flavors (i)
flv_real (2, j) = 21
flv_real (5, j) = flavors (i)
else
flv_real (1, j) = 21
flv_real (2, j) = -flavors (i)
flv_real (5, j) = -flavors (i)
i = i + 1
end if
end do
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
call write_separator (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
write (u, "(A)") "* Create table of singular regions for hadronic top decay"
write (u, "(A)")
n_flv_born = 1; n_flv_real = 1
n_legs_born = 4; n_legs_real = 5
n_in = 1
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [6, -5, 2, -1]
flv_real (:, 1) = [6, -5, 2, -1, 21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
call write_separator (u)
deallocate (flv_born, flv_real)
call reg_data%final ()
write (u, "(A)") "* Create table of singular regions for dijet s sbar -> jet jet"
write (u, "(A)") "* With jet = u:d:gl"
write (u, "(A)")
n_flv_born = 3; n_flv_real = 3
n_legs_born = 4; n_legs_real = 5
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
do i = 1, n_flv_born
flv_born (1:2, i) = [3, -3]
end do
flv_born (3, :) = [1, 2, 21]
flv_born (4, :) = [-1, -2, 21]
do i = 1, n_flv_real
flv_real (1:2, i) = [3, -3]
end do
flv_real (3, :) = [1, 2, 21]
flv_real (4, :) = [-1, -2, 21]
flv_real (5, :) = [21, 21, 21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
call reg_data%final ()
end subroutine fks_regions_6
@ %def fks_regions_6
@
<<fks regions: test declarations>>=
public :: fks_regions_7
<<fks regions: tests>>=
subroutine fks_regions_7 (u)
integer, intent(in) :: u
integer :: n_flv_born, n_flv_real
integer :: n_legs_born, n_legs_real
integer :: n_in
integer, dimension(:,:), allocatable :: flv_born, flv_real
type(region_data_t) :: reg_data
write (u, "(A)") "* Test output: fks_regions_7"
write (u, "(A)") "* Create table of singular regions for ee -> qq"
write (u, "(A)")
n_flv_born = 1; n_flv_real = 1
n_legs_born = 4; n_legs_real = 5
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, 2, -2]
flv_real (:, 1) = [11, -11, 2, -2, 21]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QCD"))
call reg_data%write_latex (u)
call reg_data%final ()
end subroutine fks_regions_7
@ %def fks_regions_7
@
<<fks regions: test declarations>>=
public :: fks_regions_8
<<fks regions: tests>>=
subroutine fks_regions_8 (u)
integer, intent(in) :: u
integer :: n_flv_born, n_flv_real
integer :: n_legs_born, n_legs_real
integer :: n_in
integer, dimension(:,:), allocatable :: flv_born, flv_real
type(region_data_t) :: reg_data
integer :: i, j
integer, dimension(10) :: flavors
write (u, "(A)") "* Test output: fks_regions_8"
write (u, "(A)") "* Create table of singular regions for ee -> ee"
write (u, "(A)")
n_flv_born = 1; n_flv_real = 3
n_legs_born = 4; n_legs_real = 5
n_in = 2
allocate (flv_born (n_legs_born, n_flv_born))
allocate (flv_real (n_legs_real, n_flv_real))
flv_born (:, 1) = [11, -11, -11, 11]
flv_real (:, 1) = [11, -11, -11, 11, 22]
flv_real (:, 2) = [11, 22, -11, 11, 11]
flv_real (:, 3) = [22, -11, 11, -11, -11]
call setup_region_data_for_test (n_in, flv_born, flv_real, reg_data, var_str ("QED"))
call reg_data%check_consistency (.false., u)
call reg_data%write (u)
call reg_data%final ()
end subroutine fks_regions_8
@ %def fks_regions_8
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Virtual contribution to the cross section}
<<[[virtual.f90]]>>=
<<File header>>
module virtual
<<Use kinds>>
<<Use strings>>
<<Use debug>>
use numeric_utils
use constants
use diagnostics
use pdg_arrays
use models
use model_data, only: model_data_t
use physics_defs
use sm_physics
use lorentz
use flavors
use nlo_data, only: get_threshold_momenta, nlo_settings_t
use nlo_data, only: ASSOCIATED_LEG_PAIR
use fks_regions
<<Standard module head>>
<<virtual: public>>
<<virtual: parameters>>
<<virtual: types>>
contains
<<virtual: procedures>>
end module virtual
@ %def virtual
@
<<virtual: public>>=
public :: virtual_t
<<virtual: types>>=
type :: virtual_t
type(nlo_settings_t), pointer :: settings
real(default), dimension(:,:), allocatable :: gamma_0, gamma_p, c_flv
real(default) :: ren_scale2, fac_scale, es_scale2
integer, dimension(:), allocatable :: n_is_neutrinos
integer :: n_in, n_legs, n_flv
logical :: bad_point = .false.
type(string_t) :: selection
real(default), dimension(:), allocatable :: sqme_born
real(default), dimension(:), allocatable :: sqme_virt_fin
real(default), dimension(:,:,:), allocatable :: sqme_color_c
real(default), dimension(:,:,:), allocatable :: sqme_charge_c
logical :: has_pdfs = .false.
contains
<<virtual: virtual: TBP>>
end type virtual_t
@ %def virtual_t
@
<<virtual: virtual: TBP>>=
procedure :: init => virtual_init
<<virtual: procedures>>=
subroutine virtual_init (virt, flv_born, n_in, settings, &
nlo_corr_type, model, has_pdfs)
class(virtual_t), intent(inout) :: virt
integer, intent(in), dimension(:,:) :: flv_born
integer, intent(in) :: n_in
type(nlo_settings_t), intent(in), pointer :: settings
type(string_t), intent(in) :: nlo_corr_type
class(model_data_t), intent(in) :: model
logical, intent(in) :: has_pdfs
integer :: i_flv
virt%n_legs = size (flv_born, 1); virt%n_flv = size (flv_born, 2)
virt%n_in = n_in
allocate (virt%sqme_born (virt%n_flv))
allocate (virt%sqme_virt_fin (virt%n_flv))
allocate (virt%sqme_color_c (virt%n_legs, virt%n_legs, virt%n_flv))
allocate (virt%sqme_charge_c (virt%n_legs, virt%n_legs, virt%n_flv))
allocate (virt%gamma_0 (virt%n_legs, virt%n_flv), &
virt%gamma_p (virt%n_legs, virt%n_flv), &
virt%c_flv (virt%n_legs, virt%n_flv))
call virt%init_constants (flv_born, settings%fks_template%n_f, nlo_corr_type, model)
allocate (virt%n_is_neutrinos (virt%n_flv))
virt%n_is_neutrinos = 0
do i_flv = 1, virt%n_flv
if (is_neutrino (flv_born(1, i_flv))) &
virt%n_is_neutrinos(i_flv) = virt%n_is_neutrinos(i_flv) + 1
if (is_neutrino (flv_born(2, i_flv))) &
virt%n_is_neutrinos(i_flv) = virt%n_is_neutrinos(i_flv) + 1
end do
select case (char (settings%virtual_selection))
case ("Full", "OLP", "Subtraction")
virt%selection = settings%virtual_selection
case default
call msg_fatal ('Virtual selection: Possible values are "Full", "OLP" or "Subtraction')
end select
virt%settings => settings
virt%has_pdfs = has_pdfs
contains
function is_neutrino (flv) result (neutrino)
integer, intent(in) :: flv
logical :: neutrino
neutrino = (abs(flv) == 12 .or. abs(flv) == 14 .or. abs(flv) == 16)
end function is_neutrino
end subroutine virtual_init
@ %def virtual_init
@ The virtual subtraction terms contain Casimir operators and derived constants, listed
below:
\begin{align}
\label{eqn:C(q)}
C(q) = C(\bar{q}) &= C_F, \\
\label{eqn:C(g)}
C(g) &= C_A,\\
\label{eqn:gamma(q)}
\gamma(q) = \gamma(\bar{q}) &= \frac{3}{2} C_F,\\
\label{eqn:gamma(g)}
\gamma(g) &= \frac{11}{6} C_A - \frac{2}{3} T_F N_f,\\
\label{eqn:gammap(q)}
\gamma'(q) = \gamma'(\bar{q}) &= \left(\frac{13}{2} - \frac{2\pi^2}{3}\right) C_F, \\
\label{eqn:gammap(g)}
\gamma'(g) &= \left(\frac{67}{9} - \frac{2\pi^2}{3}\right) C_A - \frac{23}{9} T_F N_f.
\end{align}
For uncolored particles, [[virtual_init_constants]] sets $C$, $\gamma$ and $\gamma'$ to zero.
<<virtual: virtual: TBP>>=
procedure :: init_constants => virtual_init_constants
<<virtual: procedures>>=
subroutine virtual_init_constants (virt, flv_born, nf_input, nlo_corr_type, model)
class(virtual_t), intent(inout) :: virt
integer, intent(in), dimension(:,:) :: flv_born
integer, intent(in) :: nf_input
type(string_t), intent(in) :: nlo_corr_type
class(model_data_t), intent(in) :: model
integer :: i_part, i_flv
real(default) :: nf, CA_factor
real(default), dimension(:,:), allocatable :: CF_factor, TR_factor
type(flavor_t) :: flv
allocate (CF_factor (size (flv_born, 1), size (flv_born, 2)), &
TR_factor (size (flv_born, 1), size (flv_born, 2)))
if (nlo_corr_type == "QCD") then
CA_factor = CA; CF_factor = CF; TR_factor = TR
nf = real(nf_input, default)
else if (nlo_corr_type == "QED") then
CA_factor = zero
do i_flv = 1, size (flv_born, 2)
do i_part = 1, size (flv_born, 1)
call flv%init (flv_born(i_part, i_flv), model)
CF_factor(i_part, i_flv) = (flv%get_charge ())**2
TR_factor(i_part, i_flv) = (flv%get_charge ())**2
end do
end do
! TODO vincent_r fixed nf needs replacement !!! for testing only, needs dynamical treatment!
nf = real(4, default)
end if
do i_flv = 1, size (flv_born, 2)
do i_part = 1, size (flv_born, 1)
if (is_corresponding_vector (flv_born(i_part, i_flv), nlo_corr_type)) then
virt%gamma_0(i_part, i_flv) = 11._default / 6._default * CA_factor &
- two / three * TR_factor(i_part, i_flv) * nf
virt%gamma_p(i_part, i_flv) = (67._default / 9._default &
- two * pi**2 / three) * CA_factor &
- 23._default / 9._default * TR_factor(i_part, i_flv) * nf
virt%c_flv(i_part, i_flv) = CA_factor
else if (is_corresponding_fermion (flv_born(i_part, i_flv), nlo_corr_type)) then
virt%gamma_0(i_part, i_flv) = 1.5_default * CF_factor(i_part, i_flv)
virt%gamma_p(i_part, i_flv) = (6.5_default - two * pi**2 / three) * CF_factor(i_part, i_flv)
virt%c_flv(i_part, i_flv) = CF_factor(i_part, i_flv)
else
virt%gamma_0(i_part, i_flv) = zero
virt%gamma_p(i_part, i_flv) = zero
virt%c_flv(i_part, i_flv) = zero
end if
end do
end do
contains
function is_corresponding_vector (pdg_nr, nlo_corr_type)
logical :: is_corresponding_vector
integer, intent(in) :: pdg_nr
type(string_t), intent(in) :: nlo_corr_type
is_corresponding_vector = .false.
if (nlo_corr_type == "QCD") then
is_corresponding_vector = is_gluon (pdg_nr)
else if (nlo_corr_type == "QED") then
is_corresponding_vector = is_photon (pdg_nr)
end if
end function is_corresponding_vector
function is_corresponding_fermion (pdg_nr, nlo_corr_type)
logical :: is_corresponding_fermion
integer, intent(in) :: pdg_nr
type(string_t), intent(in) :: nlo_corr_type
is_corresponding_fermion = .false.
if (nlo_corr_type == "QCD") then
is_corresponding_fermion = is_quark (pdg_nr)
else if (nlo_corr_type == "QED") then
is_corresponding_fermion = is_fermion (pdg_nr)
end if
end function is_corresponding_fermion
end subroutine virtual_init_constants
@ %def virtual_init_constants
@ Set the renormalization scale. If the input is zero, use the
center-of-mass energy.
<<virtual: virtual: TBP>>=
procedure :: set_ren_scale => virtual_set_ren_scale
<<virtual: procedures>>=
subroutine virtual_set_ren_scale (virt, p, ren_scale)
class(virtual_t), intent(inout) :: virt
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(in) :: ren_scale
if (ren_scale > 0) then
virt%ren_scale2 = ren_scale**2
else
virt%ren_scale2 = (p(1) + p(2))**2
end if
end subroutine virtual_set_ren_scale
@ %def virtual_set_ren_scale
@
<<virtual: virtual: TBP>>=
procedure :: set_fac_scale => virtual_set_fac_scale
<<virtual: procedures>>=
subroutine virtual_set_fac_scale (virt, p, fac_scale)
class(virtual_t), intent(inout) :: virt
type(vector4_t), dimension(:), intent(in) :: p
real(default), optional :: fac_scale
if (present (fac_scale)) then
virt%fac_scale = fac_scale
else
virt%fac_scale = (p(1) + p(2))**1
end if
end subroutine virtual_set_fac_scale
@ %def virtual_set_fac_scale
<<virtual: virtual: TBP>>=
procedure :: set_ellis_sexton_scale => virtual_set_ellis_sexton_scale
<<virtual: procedures>>=
subroutine virtual_set_ellis_sexton_scale (virt, Q2)
class(virtual_t), intent(inout) :: virt
real(default), intent(in), optional :: Q2
if (present (Q2)) then
virt%es_scale2 = Q2
else
virt%es_scale2 = virt%ren_scale2
end if
end subroutine virtual_set_ellis_sexton_scale
@ %def virtual_set_ellis_sexton_scale
@ The virtual-subtracted matrix element is given by the equation
\begin{equation}
\label{eqn:virt_sub}
\mathcal{V} = \frac{\alpha_s}{2\pi}\left(\mathcal{Q}\mathcal{B} +
\sum \mathcal{I}_{ij}\mathcal{B}_{ij} + \mathcal{V}_{fin}\right),
\end{equation}
The expressions for $\mathcal{Q}$ can be found in equations \ref{eqn:virt_Q_isr}
and \ref{eqn:virt_Q_fsr}.
The expressions for $\mathcal{I}_{ij}$ can be found in equations
(\ref{I_00}), (\ref{I_mm}), (\ref{I_0m}), depending on whether the
particles involved in the radiation process are massive or massless.
<<virtual: virtual: TBP>>=
procedure :: evaluate => virtual_evaluate
<<virtual: procedures>>=
subroutine virtual_evaluate (virt, reg_data, alpha_coupling, &
p_born, separate_alrs, sqme_virt)
class(virtual_t), intent(inout) :: virt
type(region_data_t), intent(in) :: reg_data
real(default), intent(in) :: alpha_coupling
type(vector4_t), intent(in), dimension(:) :: p_born
logical, intent(in) :: separate_alrs
real(default), dimension(:), intent(inout) :: sqme_virt
real(default) :: s, s_o_Q2
real(default), dimension(reg_data%n_flv_born) :: QB, BI
integer :: i_flv, ii_flv
QB = zero; BI = zero
if (virt%bad_point) return
if (debug2_active (D_VIRTUAL)) then
print *, 'Compute virtual component using alpha = ', alpha_coupling
print *, 'Virtual selection: ', char (virt%selection)
print *, 'virt%es_scale2 = ', virt%es_scale2 !!! Debugging
end if
s = sum (p_born(1 : virt%n_in))**2
if (virt%settings%factorization_mode == FACTORIZATION_THRESHOLD) &
call set_s_for_threshold ()
s_o_Q2 = s / virt%es_scale2 * virt%settings%fks_template%xi_cut**2
do i_flv = 1, reg_data%n_flv_born
if (separate_alrs) then
ii_flv = i_flv
else
ii_flv = 1
end if
if (virt%selection == var_str ("Full") .or. virt%selection == var_str ("OLP")) then
!!! A factor of alpha_coupling/twopi is assumed to be included in vfin
sqme_virt(ii_flv) = sqme_virt(ii_flv) + virt%sqme_virt_fin(i_flv)
end if
if (virt%selection == var_str ("Full") .or. virt%selection == var_str ("Subtraction")) then
call virt%evaluate_initial_state (i_flv, QB)
call virt%compute_collinear_contribution (i_flv, p_born, sqrt(s), reg_data, QB)
select case (virt%settings%factorization_mode)
case (FACTORIZATION_THRESHOLD)
call virt%compute_eikonals_threshold (i_flv, p_born, s_o_Q2, QB, BI)
case default
call virt%compute_massive_self_eikonals (i_flv, p_born, s_o_Q2, reg_data, QB)
call virt%compute_eikonals (i_flv, p_born, s_o_Q2, reg_data, BI)
end select
if (debug2_active (D_VIRTUAL)) then
print *, 'Evaluate i_flv: ', i_flv
print *, 'sqme_born: ', virt%sqme_born (i_flv)
print *, 'Q * sqme_born: ', alpha_coupling / twopi * QB(i_flv)
print *, 'BI: ', alpha_coupling / twopi * BI(i_flv)
print *, 'vfin: ', virt%sqme_virt_fin (i_flv)
end if
sqme_virt(ii_flv) = &
sqme_virt(ii_flv) + alpha_coupling / twopi * (QB(i_flv) + BI(i_flv))
end if
end do
if (debug2_active (D_VIRTUAL)) then
call msg_debug2 (D_VIRTUAL, "virtual-subtracted matrix element(s): ")
print *, sqme_virt
end if
do i_flv = 1, reg_data%n_flv_born
if (virt%n_is_neutrinos(i_flv) > 0) &
sqme_virt = sqme_virt * virt%n_is_neutrinos(i_flv) * two
end do
contains
subroutine set_s_for_threshold ()
use ttv_formfactors, only: m1s_to_mpole
real(default) :: mtop2
mtop2 = m1s_to_mpole (sqrt(s))**2
if (s < four * mtop2) s = four * mtop2
end subroutine set_s_for_threshold
end subroutine virtual_evaluate
@ %def virtual_evaluate
@
<<virtual: virtual: TBP>>=
procedure :: compute_eikonals => virtual_compute_eikonals
<<virtual: procedures>>=
subroutine virtual_compute_eikonals (virtual, i_flv, &
p_born, s_o_Q2, reg_data, BI)
class(virtual_t), intent(inout) :: virtual
integer, intent(in) :: i_flv
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in) :: s_o_Q2
type(region_data_t), intent(in) :: reg_data
real(default), intent(inout), dimension(:) :: BI
integer :: i, j
real(default) :: I_ij, BI_tmp
BI_tmp = zero
! TODO vincent_r: Split the procedure into one computing QCD eikonals and one computing QED eikonals.
! TODO vincent_r: In the best case, remove the dependency on reg_data completely.
associate (flst_born => reg_data%flv_born(i_flv), &
nlo_corr_type => reg_data%regions(1)%nlo_correction_type)
do i = 1, virtual%n_legs
do j = 1, virtual%n_legs
if (i /= j) then
if (nlo_corr_type == "QCD") then
if (flst_born%colored(i) .and. flst_born%colored(j)) then
I_ij = compute_eikonal_factor (p_born, flst_born%massive, &
i, j, s_o_Q2)
BI_tmp = BI_tmp + virtual%sqme_color_c (i, j, i_flv) * I_ij
if (debug2_active (D_VIRTUAL)) &
print *, 'b_ij: ', i, j, virtual%sqme_color_c (i, j, i_flv), 'I_ij: ', I_ij
end if
else if (nlo_corr_type == "QED") then
I_ij = compute_eikonal_factor (p_born, flst_born%massive, &
i, j, s_o_Q2)
BI_tmp = BI_tmp + virtual%sqme_charge_c (i, j, i_flv) * I_ij
if (debug2_active (D_VIRTUAL)) &
print *, 'b_ij: ', virtual%sqme_charge_c (i, j, i_flv), 'I_ij: ', I_ij
end if
else if (debug2_active (D_VIRTUAL)) then
print *, 'b_ij: ', i, j, virtual%sqme_color_c (i, j, i_flv), 'I_ij: ', I_ij
end if
end do
end do
if (virtual%settings%use_internal_color_correlations .or. nlo_corr_type == "QED") &
BI_tmp = BI_tmp * virtual%sqme_born (i_flv)
end associate
BI(i_flv) = BI(i_flv) + BI_tmp
end subroutine virtual_compute_eikonals
@ %def virtual_compute_eikonals
@
<<virtual: virtual: TBP>>=
procedure :: compute_eikonals_threshold => virtual_compute_eikonals_threshold
<<virtual: procedures>>=
subroutine virtual_compute_eikonals_threshold (virtual, i_flv, &
p_born, s_o_Q2, QB, BI)
class(virtual_t), intent(in) :: virtual
integer, intent(in) :: i_flv
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in) :: s_o_Q2
real(default), intent(inout), dimension(:) :: QB
real(default), intent(inout), dimension(:) :: BI
type(vector4_t), dimension(4) :: p_thr
integer :: leg
BI = zero; p_thr = get_threshold_momenta (p_born)
call compute_massive_self_eikonals (virtual%sqme_born(i_flv), QB(i_flv))
do leg = 1, 2
BI(i_flv) = BI(i_flv) + evaluate_leg_pair (ASSOCIATED_LEG_PAIR(leg), i_flv)
end do
contains
subroutine compute_massive_self_eikonals (sqme_born, QB)
real(default), intent(in) :: sqme_born
real(default), intent(inout) :: QB
integer :: i
if (debug_on) call msg_debug2 (D_VIRTUAL, "compute_massive_self_eikonals")
if (debug_on) call msg_debug2 (D_VIRTUAL, "s_o_Q2", s_o_Q2)
if (debug_on) call msg_debug2 (D_VIRTUAL, "log (s_o_Q2)", log (s_o_Q2))
do i = 1, 4
QB = QB - (cf * (log (s_o_Q2) - 0.5_default * I_m_eps (p_thr(i)))) &
* sqme_born
end do
end subroutine compute_massive_self_eikonals
function evaluate_leg_pair (i_start, i_flv) result (b_ij_times_I)
real(default) :: b_ij_times_I
integer, intent(in) :: i_start, i_flv
real(default) :: I_ij
integer :: i, j
b_ij_times_I = zero
do i = i_start, i_start + 1
do j = i_start, i_start + 1
if (i /= j) then
I_ij = compute_eikonal_factor &
(p_thr, [.true., .true., .true., .true.], i, j, s_o_Q2)
b_ij_times_I = b_ij_times_I + &
virtual%sqme_color_c (i, j, i_flv) * I_ij
if (debug2_active (D_VIRTUAL)) &
print *, 'b_ij: ', virtual%sqme_color_c (i, j, i_flv), 'I_ij: ', I_ij
end if
end do
end do
if (virtual%settings%use_internal_color_correlations) &
b_ij_times_I = b_ij_times_I * virtual%sqme_born (i_flv)
if (debug2_active (D_VIRTUAL)) then
print *, 'internal color: ', virtual%settings%use_internal_color_correlations
print *, 'b_ij_times_I = ', b_ij_times_I
print *, 'QB = ', QB
end if
end function evaluate_leg_pair
end subroutine virtual_compute_eikonals_threshold
@ %def virtual_compute_eikonals_threshold
@
<<virtual: virtual: TBP>>=
procedure :: set_bad_point => virtual_set_bad_point
<<virtual: procedures>>=
subroutine virtual_set_bad_point (virt, value)
class(virtual_t), intent(inout) :: virt
logical, intent(in) :: value
virt%bad_point = value
end subroutine virtual_set_bad_point
@ %def virtual_set_bad_point
@ The collinear limit of $\tilde{\mathcal{R}}$ can be integrated over the radiation
degrees of freedom, giving the collinear contribution to the virtual component. Its
general structure is $\mathcal{Q} \cdot \mathcal{B}$. The initial-state contribution
to $\mathcal{Q}$ is simply given by
\begin{equation}
\label{eqn:virt_Q_isr}
\mathcal{Q} = -\log\frac{\mu_F^2}{Q^2} \left(\gamma(\mathcal{I}_1) + 2 C (\mathcal{I}_1) \log(\xi_{\text{cut}}) + \gamma(\mathcal{I}_2) + 2 C (\mathcal{I}_2) \log(\xi_{\text{cut}}) \right),
\end{equation}
where $Q^2$ is the Ellis-Sexton scale and $\gamma$ is as in eqns. \ref{eqn:gamma(q)}
and \ref{eqn:gamma(g)}.\\
[[virtual_evaluate_initial_state]] computes this quantity. The loop over the
initial-state particles is only executed if we are
dealing with a scattering process, because for decays there are no virtual
initial-initial interactions.
<<virtual: virtual: TBP>>=
procedure :: evaluate_initial_state => virtual_evaluate_initial_state
<<virtual: procedures>>=
subroutine virtual_evaluate_initial_state (virt, i_flv, QB)
class(virtual_t), intent(inout) :: virt
integer, intent(in) :: i_flv
real(default), intent(inout), dimension(:) :: QB
integer :: i
if (virt%n_in == 2) then
do i = 1, virt%n_in
QB(i_flv) = QB(i_flv) - (virt%gamma_0 (i, i_flv) + two * virt%c_flv(i, i_flv) &
* log (virt%settings%fks_template%xi_cut)) &
* log(virt%fac_scale**2 / virt%es_scale2) * virt%sqme_born (i_flv)
end do
end if
end subroutine virtual_evaluate_initial_state
@ %def virtual_evaluate_initial_state
@ Same as above, but for final-state particles. The collinear limit for final-state
particles follows from the integral
\begin{equation*}
I_{+,\alpha_r} = \int d\Phi_{n+1} \frac{\xi_+^{-1-2\epsilon}}{\xi^{-1-2\epsilon}} \mathcal{R}_{\alpha_r}.
\end{equation*}
We can distinguish three situations:
\begin{enumerate}
\item $\alpha_r$ contains a massive emitter. In this case, no collinear subtraction terms is required and
the integral above irrelevant.
\item $\alpha_r$ contains a massless emitter, but resonances are not taken into account in the subtraction.
Here, $\xi_{max} = \frac{2E_{em}}{\sqrt{s}}$ is the upper bound on $\xi$.
\item $\alpha_r$ contains a massless emitter and resonance-aware subtraction is used. Here,
$\xi_{max} = \frac{2E_{em}}{\sqrt{k_{res}^2}}$.
\end{enumerate}
Before version 2.4, only situations 1 and 2 were covered. The difference between situation 2 and 3 comes
from the expansion of the plus-distribution in the integral above,
\begin{equation*}
\xi_+^{-1-2\epsilon} = \xi^{-1-2\epsilon} + \frac{1}{2\epsilon}\delta(\xi)
= \xi_{max}^{-1-2\epsilon}\left[(1-z)^{-1-2\epsilon} + \frac{\xi_{max}^{2\epsilon}}{2\epsilon}\delta(1-z)\right].
\end{equation*}
The expression from the standard FKS literature is given by
$\mathcal{Q}$ is given by
\begin{equation}
\label{eqn:virt_Q_fsr_old}
\begin{split}
\mathcal{Q} = \sum_{k=n_{in}}^{n_L^{(B)}} \left[\gamma'(\mathcal{I}_k)
- \log\frac{s\delta_o}{2Q^2}\left(\gamma(\mathcal{I}_k)
- 2C(\mathcal{I}_k) \log\frac{2E_k}{\xi_{\text{cut}}\sqrt{s}}\right) \right.\\
+ \left. 2C(\mathcal{I}_k) \left( \log^2\frac{2E_k}{\sqrt{s}} - \log^2 \xi_{\text{cut}} \right)
- 2\gamma(\mathcal{I}_k)\log\frac{2E_k}{\sqrt{s}}\right].
\end{split}
\end{equation}
$n_L^{(B)}$ is the number of legs at Born level.
Here, $\xi_{max}$ is implicitly present in the ratios in the logarithms. Using the resonance-aware $\xi_{max}$ yields
\begin{equation}
\label{eqn:virt_Q_fsr}
\begin{split}
\mathcal{Q} = \sum_{k=n_{in}}^{n_L^{(B)}} \left[\gamma'(\mathcal{I}_k)
+ 2\left(\log\frac{\sqrt{s}}{2E_{em}} + \log\xi_{max}\right)
\left(\log\frac{\sqrt{s}}{2E_{em}} + \log\xi_{max} + \log\frac{Q^2}{s}\right) C(\mathcal{I}_k) \right.\\
+ \left. 2 \log\xi_{max} \left(\log\xi_{max} - \log\frac{Q^2}{k_{res}^2}\right) C(\mathcal{I}_k)
+ \left(\log\frac{Q^2}{k_{res}^2} - 2 \log\xi_{max}\right) \gamma(\mathcal{I}_k)\right].
\end{split}
\end{equation}
Equation \ref{eqn:virt_Q_fsr} leads to \ref{eqn:virt_Q_fsr_old} with the substitutions $\xi_{max} \rightarrow \frac{2E_{em}}{\sqrt{s}}$ and $k_{res}^2 \rightarrow s$.
[[virtual_compute_collinear_contribution]] only implements the second one.
<<virtual: virtual: TBP>>=
procedure :: compute_collinear_contribution &
=> virtual_compute_collinear_contribution
<<virtual: procedures>>=
subroutine virtual_compute_collinear_contribution (virt, i_flv, &
p_born, sqrts, reg_data, QB)
class(virtual_t), intent(inout) :: virt
integer, intent(in) :: i_flv
type(vector4_t), dimension(:), intent(in) :: p_born
real(default), intent(in) :: sqrts
type(region_data_t), intent(in) :: reg_data
real(default), intent(inout), dimension(:) :: QB
real(default) :: s1, s2, s3, s4, s5
integer :: alr, em
real(default) :: E_em, xi_max, log_xi_max, E_tot2
logical, dimension(virt%n_flv, virt%n_legs) :: evaluated
integer :: i_contr
type(vector4_t) :: k_res
type(lorentz_transformation_t) :: L_to_resonance
evaluated = .false.
do alr = 1, reg_data%n_regions
if (i_flv /= reg_data%regions(alr)%uborn_index) cycle
em = reg_data%regions(alr)%emitter
- if (em == 0) cycle
+ if (em <= virt%n_in) cycle
if (evaluated(i_flv, em)) cycle
!!! Collinear terms only for massless particles
if (reg_data%regions(alr)%flst_uborn%massive(em)) cycle
E_em = p_born(em)%p(0)
if (allocated (reg_data%alr_contributors)) then
i_contr = reg_data%alr_to_i_contributor (alr)
k_res = get_resonance_momentum (p_born, reg_data%alr_contributors(i_contr)%c)
E_tot2 = k_res%p(0)**2
L_to_resonance = inverse (boost (k_res, k_res**1))
xi_max = two * space_part_norm (L_to_resonance * p_born(em)) / k_res%p(0)
log_xi_max = log (xi_max)
else
E_tot2 = sqrts**2
xi_max = two * E_em / sqrts
log_xi_max = log (xi_max)
end if
associate (xi_cut => virt%settings%fks_template%xi_cut, delta_o => virt%settings%fks_template%delta_o)
if (virt%settings%virtual_resonance_aware_collinear) then
if (debug_active (D_VIRTUAL)) &
call msg_debug (D_VIRTUAL, "Using resonance-aware collinear subtraction")
s1 = virt%gamma_p(em, i_flv)
s2 = two * (log (sqrts / (two * E_em)) + log_xi_max) * &
(log (sqrts / (two * E_em)) + log_xi_max + log (virt%es_scale2 / sqrts**2)) &
* virt%c_flv(em, i_flv)
s3 = two * log_xi_max * &
(log_xi_max - log (virt%es_scale2 / E_tot2)) * virt%c_flv(em, i_flv)
s4 = (log (virt%es_scale2 / E_tot2) - two * log_xi_max) * virt%gamma_0(em, i_flv)
QB(i_flv) = QB(i_flv) + (s1 + s2 + s3 + s4) * virt%sqme_born(i_flv)
else
if (debug_active (D_VIRTUAL)) &
call msg_debug (D_VIRTUAL, "Using old-fashioned collinear subtraction")
s1 = virt%gamma_p(em, i_flv)
s2 = log (delta_o * sqrts**2 / (two * virt%es_scale2)) * virt%gamma_0(em,i_flv)
s3 = log (delta_o * sqrts**2 / (two * virt%es_scale2)) * two * virt%c_flv(em,i_flv) * &
log (two * E_em / (xi_cut * sqrts))
! s4 = two * virt%c_flv(em,i_flv) * (log (two * E_em / sqrts)**2 - log (xi_cut)**2)
s4 = two * virt%c_flv(em,i_flv) * & ! a**2 - b**2 = (a - b) * (a + b), for better numerical performance
(log (two * E_em / sqrts) + log (xi_cut)) * (log (two * E_em / sqrts) - log (xi_cut))
s5 = two * virt%gamma_0(em,i_flv) * log (two * E_em / sqrts)
QB(i_flv) = QB(i_flv) + (s1 - s2 + s3 + s4 - s5) * virt%sqme_born(i_flv)
end if
end associate
evaluated(i_flv, em) = .true.
end do
end subroutine virtual_compute_collinear_contribution
@ %def virtual_compute_collinear_contribution
@ For the massless-massive case and $i = j$ we get the massive self-eikonal of (A.10) in arXiv:0908.4272, given as
\begin{equation}
\mathcal{I}_{ii} = \log \frac{\xi^2_{\text{cut}}s}{Q^2} - \frac{1}{\beta} \log \frac{1 + \beta}{1 - \beta}.
\end{equation}
<<virtual: virtual: TBP>>=
procedure :: compute_massive_self_eikonals => virtual_compute_massive_self_eikonals
<<virtual: procedures>>=
subroutine virtual_compute_massive_self_eikonals (virt, i_flv, &
p_born, s_over_Q2, reg_data, QB)
class(virtual_t), intent(inout) :: virt
integer, intent(in) :: i_flv
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in) :: s_over_Q2
type(region_data_t), intent(in) :: reg_data
real(default), intent(inout), dimension(:) :: QB
integer :: i
logical :: massive
do i = 1, virt%n_legs
massive = reg_data%flv_born(i_flv)%massive(i)
if (massive) then
QB(i_flv) = QB(i_flv) - (virt%c_flv (i, i_flv) &
* (log (s_over_Q2) - 0.5_default * I_m_eps (p_born(i)))) &
* virt%sqme_born (i_flv)
end if
end do
end subroutine virtual_compute_massive_self_eikonals
@ %def virtual_compute_massive_self_eikonals
@ The following code implements the $\mathcal{I}_{ij}$-function.
-The complete formulas can be found in arXiv:0908.4272 (A.1-A.17).
+The complete formulas can be found in arXiv:0908.4272 (A.1-A.17) and are also discussed in arXiv:1002.2581 in Appendix A.
The implementation may differ in the detail from the formulas presented in the above paper.
The parameter $\xi_{\text{cut}}$ is unphysically and cancels with appropriate factors in the real subtraction.
We keep the additional parameter for debug usage.
The implemented formulas are then defined as follows:
\begin{itemize}
\item[massless-massless case]
$p^2 = 0, k^2 = 0,$
\begin{equation}
\begin{split}
\mathcal{I}_{ij} &= \frac{1}{2}\log^2\frac{\xi^2_{\text{cut}}s}{Q^2} + \log\frac{\xi^2_{\text{cut}}s}{Q^2}\log\frac{k_ik_j}{2E_iE_j} - \rm{Li}_2\left(\frac{k_ik_j}{2E_iE_j}\right) \\
&+ \frac{1}{2}\log^2\frac{k_ik_j}{2E_iE_j} - \log\left(1-\frac{k_ik_j}{2E_iE_j}\right) \log\frac{k_ik_j}{2E_iE_j}.
\end{split}
\label{I_00}
\end{equation}
\item[massive-massive case]
$p^2 \neq 0, k^2 \neq 0,$
\begin{equation}
\mathcal{I}_{ij} = \frac{1}{2}I_0(k_i, k_j)\log\frac{\xi^2_{\text{cut}}s}{Q^2} - \frac{1}{2}I_\epsilon(k_i,k_j)
\label{I_mm}
\end{equation}
with
\begin{equation}
I_0(k_i, k_j) = \frac{1}{\beta}\log\frac{1+\beta}{1-\beta}, \qquad \beta = \sqrt{1-\frac{k_i^2k_j^2}{(k_i \cdot k_j)^2}}
\end{equation}
and a rather involved expression for $I_\epsilon$:
\begin{align}
\allowdisplaybreaks
I_\epsilon(k_i, k_j) &= \left(K(z_j)-K(z_i)\right) \frac{1-\vec{\beta_i}\cdot\vec{\beta_j}}{\sqrt{a(1-b)}}, \\
\vec{\beta_i} &= \frac{\vec{k}_i}{k_i^0}, \\
a &= \beta_i^2 + \beta_j^2 - 2\vec{\beta}_i \cdot \vec{\beta}_j, \\
x_i &= \frac{\beta_i^2 -\vec{\beta}_i \cdot \vec{\beta}_j}{a}, \\
x_j &= \frac{\beta_j^2 -\vec{\beta}_i \cdot \vec{\beta}_j}{a} = 1-x_j, \\
b &= \frac{\beta_i^2\beta_j^2 - (\vec{\beta}_i\cdot\vec{\beta}_j)^2}{a}, \\
c &= \sqrt{\frac{b}{4a}}, \\
z_+ &= \frac{1+\sqrt{1-b}}{\sqrt{b}}, \\
z_- &= \frac{1-\sqrt{1-b}}{\sqrt{b}}, \\
z_i &= \frac{\sqrt{x_i^2 + 4c^2} - x_i}{2c}, \\
z_j &= \frac{\sqrt{x_j^2 + 4c^2} + x_j}{2c}, \\
K(z) = &-\frac{1}{2}\log^2\frac{(z-z_-)(z_+-z)}{(z_++z)(z_-+z)} - 2Li_2\left(\frac{2z_-(z_+-z)}{(z_+-z_-)(z_-+z)}\right) \\
&-2Li_2\left(-\frac{2z_+(z_-+z)}{(z_+-z_-)(z_+-z)}\right)
\end{align}
\item[massless-massive case]
$p^2 = 0, k^2 \neq 0,$
\begin{equation}
- \mathcal{I}_{ij} = \frac{1}{2}\left[\log^2\frac{\xi^2_{\text{cut}}s}{Q^2} - \frac{\pi^2}{6}\right] -\frac{1}{2}I_0(k_i,k_j)\log\frac{\xi^2_{\text{cut}}s}{Q^2} - \frac{1}{2}I_\epsilon(k_i,k_j)
+ \mathcal{I}_{ij} = \frac{1}{2}\left[\frac{1}{2}\log^2\frac{\xi^2_{\text{cut}}s}{Q^2} - \frac{\pi^2}{6}\right] + \frac{1}{2}I_0(k_i,k_j)\log\frac{\xi^2_{\text{cut}}s}{Q^2} - \frac{1}{2}I_\epsilon(k_i,k_j)
\label{I_0m}
\end{equation}
with
\begin{align}
I_0(p,k) &= \log\frac{(\hat{p}\cdot\hat{k})^2}{\hat{k}^2}, \\
I_\varepsilon(p,k) &= -2\left[\frac{1}{4}\log^2\frac{1-\beta}{1+\beta} + \log\frac{\hat{p}\cdot\hat{k}}{1+\beta}\log\frac{\hat{p}\cdot\hat{k}}{1-\beta} + \rm{Li}_2\left(1-\frac{\hat{p}\cdot\hat{k}}{1+\beta}\right) + \rm{Li}_2\left(1-\frac{\hat{p}\cdot\hat{k}}{1-\beta}\right)\right],
\end{align}
using
\begin{align}
\hat{p} = \frac{p}{p^0}, \quad \hat{k} = \frac{k}{k^0}, \quad \beta = \frac{|\vec{k}|}{k_0}, \\
\rm{Li}_2(1 - x) + \rm{Li}_2(1 - x^{-1}) = -\frac{1}{2} \log^2 x.
\end{align}
\end{itemize}
<<virtual: procedures>>=
function compute_eikonal_factor (p_born, massive, i, j, s_o_Q2) result (I_ij)
real(default) :: I_ij
type(vector4_t), intent(in), dimension(:) :: p_born
logical, dimension(:), intent(in) :: massive
integer, intent(in) :: i, j
real(default), intent(in) :: s_o_Q2
if (massive(i) .and. massive(j)) then
I_ij = compute_Imm (p_born(i), p_born(j), s_o_Q2)
else if (.not. massive(i) .and. massive(j)) then
I_ij = compute_I0m (p_born(i), p_born(j), s_o_Q2)
else if (massive(i) .and. .not. massive(j)) then
I_ij = compute_I0m (p_born(j), p_born(i), s_o_Q2)
else
I_ij = compute_I00 (p_born(i), p_born(j), s_o_Q2)
end if
end function compute_eikonal_factor
function compute_I00 (pi, pj, s_o_Q2) result (I)
type(vector4_t), intent(in) :: pi, pj
real(default), intent(in) :: s_o_Q2
real(default) :: I
real(default) :: Ei, Ej
real(default) :: pij, Eij
real(default) :: s1, s2, s3, s4, s5
real(default) :: arglog
real(default), parameter :: tiny_value = epsilon(1.0)
s1 = 0; s2 = 0; s3 = 0; s4 = 0; s5 = 0
Ei = pi%p(0); Ej = pj%p(0)
pij = pi * pj; Eij = Ei * Ej
s1 = 0.5_default * log(s_o_Q2)**2
s2 = log(s_o_Q2) * log(pij / (two * Eij))
s3 = Li2 (pij / (two * Eij))
s4 = 0.5_default * log (pij / (two * Eij))**2
arglog = one - pij / (two * Eij)
if (arglog > tiny_value) then
s5 = log(arglog) * log(pij / (two * Eij))
else
s5 = zero
end if
I = s1 + s2 - s3 + s4 - s5
end function compute_I00
function compute_I0m (ki, kj, s_o_Q2) result (I)
type(vector4_t), intent(in) :: ki, kj
real(default), intent(in) :: s_o_Q2
real(default) :: I
real(default) :: logsomu
real(default) :: s1, s2, s3
s1 = 0; s2 = 0; s3 = 0
logsomu = log(s_o_Q2)
s1 = 0.5 * (0.5 * logsomu**2 - pi**2 / 6)
s2 = 0.5 * I_0m_0 (ki, kj) * logsomu
s3 = 0.5 * I_0m_eps (ki, kj)
I = s1 + s2 - s3
end function compute_I0m
function compute_Imm (pi, pj, s_o_Q2) result (I)
type(vector4_t), intent(in) :: pi, pj
real(default), intent(in) :: s_o_Q2
real(default) :: I
real(default) :: s1, s2
s1 = 0.5 * log(s_o_Q2) * I_mm_0(pi, pj)
s2 = 0.5 * I_mm_eps(pi, pj)
I = s1 - s2
end function compute_Imm
function I_m_eps (p) result (I)
type(vector4_t), intent(in) :: p
real(default) :: I
real(default) :: beta
beta = space_part_norm (p)/p%p(0)
if (beta < tiny_07) then
I = four * (one + beta**2/3 + beta**4/5 + beta**6/7)
else
I = two * log((one + beta) / (one - beta)) / beta
end if
end function I_m_eps
function I_0m_eps (p, k) result (I)
type(vector4_t), intent(in) :: p, k
real(default) :: I
type(vector4_t) :: pp, kp
real(default) :: beta
pp = p / p%p(0); kp = k / k%p(0)
beta = sqrt (one - kp*kp)
I = -2*(log((one - beta) / (one + beta))**2/4 + log((pp*kp) / (one + beta))*log((pp*kp) / (one - beta)) &
+ Li2(one - (pp*kp) / (one + beta)) + Li2(one - (pp*kp) / (one - beta)))
end function I_0m_eps
function I_0m_0 (p, k) result (I)
type(vector4_t), intent(in) :: p, k
real(default) :: I
type(vector4_t) :: pp, kp
pp = p / p%p(0); kp = k / k%p(0)
I = log((pp*kp)**2 / kp**2)
end function I_0m_0
function I_mm_eps (p1, p2) result (I)
type(vector4_t), intent(in) :: p1, p2
real(default) :: I
type(vector3_t) :: beta1, beta2
real(default) :: a, b, b2
real(default) :: zp, zm, z1, z2, x1, x2
real(default) :: zmb, z1b
real(default) :: K1, K2
beta1 = space_part (p1) / energy(p1)
beta2 = space_part (p2) / energy(p2)
a = beta1**2 + beta2**2 - 2 * beta1 * beta2
b = beta1**2 * beta2**2 - (beta1 * beta2)**2
if (beta1**1 > beta2**1) call switch_beta (beta1, beta2)
if (beta1 == vector3_null) then
b2 = beta2**1
I = (-0.5 * log ((one - b2) / (one + b2))**2 - two * Li2 (-two * b2 / (one - b2))) &
* one / sqrt (a - b)
return
end if
x1 = beta1**2 - beta1 * beta2
x2 = beta2**2 - beta1 * beta2
zp = sqrt (a) + sqrt (a - b)
zm = sqrt (a) - sqrt (a - b)
zmb = one / zp
z1 = sqrt (x1**2 + b) - x1
z2 = sqrt (x2**2 + b) + x2
z1b = one / (sqrt (x1**2 + b) + x1)
K1 = - 0.5 * log (((z1b - zmb) * (zp - z1)) / ((zp + z1) * (z1b + zmb)))**2 &
- two * Li2 ((two * zmb * (zp - z1)) / ((zp - zm) * (zmb + z1b))) &
- two * Li2 ((-two * zp * (zm + z1)) / ((zp - zm) * (zp - z1)))
K2 = - 0.5 * log ((( z2 - zm) * (zp - z2)) / ((zp + z2) * (z2 + zm)))**2 &
- two * Li2 ((two * zm * (zp - z2)) / ((zp - zm) * (zm + z2))) &
- two * Li2 ((-two * zp * (zm + z2)) / ((zp - zm) * (zp - z2)))
I = (K2 - K1) * (one - beta1 * beta2) / sqrt (a - b)
contains
subroutine switch_beta (beta1, beta2)
type(vector3_t), intent(inout) :: beta1, beta2
type(vector3_t) :: beta_tmp
beta_tmp = beta1
beta1 = beta2
beta2 = beta_tmp
end subroutine switch_beta
end function I_mm_eps
function I_mm_0 (k1, k2) result (I)
type(vector4_t), intent(in) :: k1, k2
real(default) :: I
real(default) :: beta
beta = sqrt (one - k1**2 * k2**2 / (k1 * k2)**2)
I = log ((one + beta) / (one - beta)) / beta
end function I_mm_0
@ %def I_mm_0
@
<<virtual: virtual: TBP>>=
procedure :: final => virtual_final
<<virtual: procedures>>=
subroutine virtual_final (virtual)
class(virtual_t), intent(inout) :: virtual
if (allocated (virtual%gamma_0)) deallocate (virtual%gamma_0)
if (allocated (virtual%gamma_p)) deallocate (virtual%gamma_p)
if (allocated (virtual%c_flv)) deallocate (virtual%c_flv)
if (allocated (virtual%n_is_neutrinos)) deallocate (virtual%n_is_neutrinos)
end subroutine virtual_final
@ %def virtual_final
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Real Subtraction}
<<[[real_subtraction.f90]]>>=
<<File header>>
module real_subtraction
<<Use kinds with double>>
<<Use strings>>
<<Use debug>>
use io_units
use format_defs, only: FMT_15
use string_utils
use constants
use numeric_utils
use diagnostics
use pdg_arrays
use models
use physics_defs
use sm_physics
use lorentz
use flavors
use phs_fks, only: real_kinematics_t, isr_kinematics_t
use phs_fks, only: I_PLUS, I_MINUS
use phs_fks, only: SQRTS_VAR, SQRTS_FIXED
use phs_fks, only: phs_point_set_t
use ttv_formfactors, only: m1s_to_mpole
use fks_regions
use nlo_data
<<Standard module head>>
<<real subtraction: public>>
<<real subtraction: parameters>>
<<real subtraction: types>>
<<real subtraction: interfaces>>
contains
<<real subtraction: procedures>>
end module real_subtraction
@ %def real_subtraction
@
\subsubsection{Soft subtraction terms}
<<real subtraction: parameters>>=
integer, parameter, public :: INTEGRATION = 0
integer, parameter, public :: FIXED_ORDER_EVENTS = 1
integer, parameter, public :: POWHEG = 2
@ %def real subtraction parameters
@
<<real subtraction: public>>=
public :: this_purpose
<<real subtraction: procedures>>=
function this_purpose (purpose)
type(string_t) :: this_purpose
integer, intent(in) :: purpose
select case (purpose)
case (INTEGRATION)
this_purpose = var_str ("Integration")
case (FIXED_ORDER_EVENTS)
this_purpose = var_str ("Fixed order NLO events")
case (POWHEG)
this_purpose = var_str ("Powheg events")
case default
this_purpose = var_str ("Undefined!")
end select
end function this_purpose
@ %def this_purpose
@
In the soft limit, the real matrix element behaves as
\begin{equation*}
\mathcal{R}_{\rm{soft}} = 4\pi\alpha_s \left[\sum_{i \neq j}
\mathcal{B}_{ij} \frac{k_i \cdot k_j}{(k_i \cdot k)(k_j \cdot k)}
- \mathcal{B} \sum_{i} \frac{k_i^2}{(k_i \cdot k)^2}C_i\right],
\end{equation*}
where $k$ denotes the momentum of the emitted parton. The quantity $\mathcal{B}_{ij}$ is called the color-correlated Born matrix element defined as
\begin{equation*}
\mathcal{B}_{ij} = \frac{1}{2s} \sum_{\stackrel{colors}{spins}} \mathcal{M}_{\{c_k\}}\left(\mathcal{M}^\dagger_{\{c_k\}}\right)_{\stackrel{c_i \rightarrow c_i'}{c_j \rightarrow c_j'}} T^a_{c_i,c_i'} T^a_{c_j,c_j'}.
\end{equation*}
<<real subtraction: types>>=
type :: soft_subtraction_t
type(region_data_t), pointer :: reg_data => null ()
real(default), dimension(:,:), allocatable :: momentum_matrix
logical :: use_resonance_mappings = .false.
type(vector4_t) :: p_soft = vector4_null
logical :: use_internal_color_correlations = .true.
logical :: use_internal_spin_correlations = .false.
logical :: xi2_expanded = .true.
integer :: factorization_mode = NO_FACTORIZATION
contains
<<real subtraction: soft sub: TBP>>
end type soft_subtraction_t
@ %def soft_subtraction_t
@
<<real subtraction: soft sub: TBP>>=
procedure :: init => soft_subtraction_init
<<real subtraction: procedures>>=
subroutine soft_subtraction_init (sub_soft, reg_data)
class(soft_subtraction_t), intent(inout) :: sub_soft
type(region_data_t), intent(in), target :: reg_data
sub_soft%reg_data => reg_data
allocate (sub_soft%momentum_matrix (reg_data%n_legs_born, &
reg_data%n_legs_born))
end subroutine soft_subtraction_init
@ %def soft_subtraction_init
@
<<real subtraction: soft sub: TBP>>=
procedure :: requires_boost => soft_subtraction_requires_boost
<<real subtraction: procedures>>=
function soft_subtraction_requires_boost (sub_soft, sqrts) result (requires_boost)
logical :: requires_boost
class(soft_subtraction_t), intent(in) :: sub_soft
real(default), intent(in) :: sqrts
real(default) :: mtop
logical :: above_threshold
if (sub_soft%factorization_mode == FACTORIZATION_THRESHOLD) then
mtop = m1s_to_mpole (sqrts)
above_threshold = sqrts**2 - four * mtop**2 > zero
else
above_threshold = .false.
end if
requires_boost = sub_soft%use_resonance_mappings .or. above_threshold
end function soft_subtraction_requires_boost
@ %def soft_subtraction_requires_boost
@ The treatment of the momentum $k$ follows the discussion about the
soft limit of the partition functions (ref????). The parton momentum is
pulled out, $k = E \hat{k}$. In fact, we will substitute $\hat{k}$ for
$k$ throughout the code, because the energy will factor out of the
equation when the soft $\mathcal{S}$-function is multiplied. The soft
momentum is a unit vector, because $k^2 = \left(k^0\right)^2 -
\left(k^0\right)^2\hat{\vec{k}}^2 = 0$.
The soft momentum is constructed by first creating a unit vector
parallel to the emitter's Born momentum. This unit vector is then
rotated about the corresponding angles $y$ and $\phi$.
<<real subtraction: soft sub: TBP>>=
procedure :: create_softvec_fsr => soft_subtraction_create_softvec_fsr
<<real subtraction: procedures>>=
subroutine soft_subtraction_create_softvec_fsr &
(sub_soft, p_born, y, phi, emitter, xi_ref_momentum)
class(soft_subtraction_t), intent(inout) :: sub_soft
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in) :: y, phi
integer, intent(in) :: emitter
type(vector4_t), intent(in) :: xi_ref_momentum
type(vector3_t) :: dir
type(vector4_t) :: p_em
type(lorentz_transformation_t) :: rot
type(lorentz_transformation_t) :: boost_to_rest_frame
logical :: requires_boost
associate (p_soft => sub_soft%p_soft)
p_soft%p(0) = one
requires_boost = sub_soft%requires_boost (two * p_born(1)%p(0))
if (requires_boost) then
boost_to_rest_frame = inverse (boost (xi_ref_momentum, xi_ref_momentum**1))
p_em = boost_to_rest_frame * p_born(emitter)
else
p_em = p_born(emitter)
end if
p_soft%p(1:3) = p_em%p(1:3) / space_part_norm (p_em)
dir = create_orthogonal (space_part (p_em))
rot = rotation (y, sqrt(one - y**2), dir)
p_soft = rot * p_soft
if (.not. vanishes (phi)) then
dir = space_part (p_em) / space_part_norm (p_em)
rot = rotation (cos(phi), sin(phi), dir)
p_soft = rot * p_soft
end if
if (requires_boost) p_soft = inverse (boost_to_rest_frame) * p_soft
end associate
end subroutine soft_subtraction_create_softvec_fsr
@ %def soft_subtraction_create_softvec_fsr
@ For initial-state emissions, the soft vector is just a unit vector
with the same direction as the radiated particle.
<<real subtraction: soft sub: TBP>>=
procedure :: create_softvec_isr => soft_subtraction_create_softvec_isr
<<real subtraction: procedures>>=
subroutine soft_subtraction_create_softvec_isr (sub_soft, y, phi)
class(soft_subtraction_t), intent(inout) :: sub_soft
real(default), intent(in) :: y, phi
real(default) :: sin_theta
sin_theta = sqrt(one - y**2)
associate (p => sub_soft%p_soft%p)
p(0) = one
p(1) = sin_theta * sin(phi)
p(2) = sin_theta * cos(phi)
p(3) = y
end associate
end subroutine soft_subtraction_create_softvec_isr
@ %def soft_subtraction_create_softvec_isr
@ The soft vector for the real mismatch is basically the same as for usual FSR,
except for the scaling with the total gluon energy. Moreover, the resulting
vector is rotated into the frame where the 3-axis points along the direction
of the emitter. This is necessary because in the collinear limit, the approximation
\begin{equation*}
k_i = \frac{k_i^0}{\bar{k}_j^0} \bar{k}_j = \frac{\xi\sqrt{s}}{2\bar{k}_j^0}\bar{k}_j
\end{equation*}
is used. The collinear limit is not included in the soft mismatch yet, but we keep
the rotation for future usage here already (the performance loss is negligible).
<<real subtraction: soft sub: TBP>>=
procedure :: create_softvec_mismatch => &
soft_subtraction_create_softvec_mismatch
<<real subtraction: procedures>>=
subroutine soft_subtraction_create_softvec_mismatch (sub_soft, E, y, phi, p_em)
class(soft_subtraction_t), intent(inout) :: sub_soft
real(default), intent(in) :: E, phi, y
type(vector4_t), intent(in) :: p_em
real(default) :: sin_theta
type(lorentz_transformation_t) :: rot_em_off_3_axis
sin_theta = sqrt (one - y**2)
associate (p => sub_soft%p_soft%p)
p(0) = E
p(1) = E * sin_theta * sin(phi)
p(2) = E * sin_theta * cos(phi)
p(3) = E * y
end associate
rot_em_off_3_axis = rotation_to_2nd (3, space_part (p_em))
sub_soft%p_soft = rot_em_off_3_axis * sub_soft%p_soft
end subroutine soft_subtraction_create_softvec_mismatch
@ %def soft_subtraction_create_softvec_mismatch
@ Computation of the soft limit of $R_\alpha$. Note that what we are
actually integrating (in the case of final-state radiation) is the
quantity $f(0,y) / \xi$, where
\begin{equation*}
f(\xi,y) = \frac{J(\xi,y,\phi)}{\xi} \xi^2 R_\alpha.
\end{equation*}
$J/\xi$ is computed by the phase space generator. The additional factor
of $\xi^{-1}$ is supplied in the [[evaluate_region_fsr]]-routine. Thus,
we are left with a factor of $\xi^2$. A look on the expression for the
soft limit of $R_\alpha$ below reveals that we are factoring out the gluon
energy $E_i$ in the denominator. Therefore, we have a factor
$\xi^2 / E_i^2 = q^2 / 4$.\\
Note that the same routine is used also for the computation of the soft
mismatch. There, the gluon energy is not factored out from the soft vector,
so that we are left with the $\xi^2$-factor, which will eventually be
cancelled out again. So, we just multiply with 1. Both cases are
distinguished by the flag [[xi2_expanded]].
<<real subtraction: soft sub: TBP>>=
procedure :: compute => soft_subtraction_compute
<<real subtraction: procedures>>=
function soft_subtraction_compute (sub_soft, p_born, &
born_ij, y, q2, alpha_coupling, alr, emitter, i_res) result (sqme)
real(default) :: sqme
class(soft_subtraction_t), intent(inout) :: sub_soft
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in), dimension(:,:) :: born_ij
real(default), intent(in) :: y
real(default), intent(in) :: q2, alpha_coupling
integer, intent(in) :: alr, emitter, i_res
real(default) :: s_alpha_soft
real(default) :: kb
real(default) :: xi2_factor
if (.not. vector_set_is_cms (p_born, sub_soft%reg_data%n_in)) then
call vector4_write_set (p_born, show_mass = .true., &
check_conservation = .true.)
call msg_fatal ("Soft subtraction: phase space point must be in CMS")
end if
if (debug2_active (D_SUBTRACTION)) then
associate (nlo_corr_type => sub_soft%reg_data%regions(alr)%nlo_correction_type)
if (nlo_corr_type == "QCD") then
print *, 'Compute soft subtraction using alpha_s = ', alpha_coupling
else if (nlo_corr_type == "QED") then
print *, 'Compute soft subtraction using alpha_qed = ', alpha_coupling
end if
end associate
end if
s_alpha_soft = sub_soft%reg_data%get_svalue_soft (p_born, &
sub_soft%p_soft, alr, emitter, i_res)
if (s_alpha_soft > one + tiny_07) call msg_fatal ("s_alpha_soft > 1!")
if (debug2_active (D_SUBTRACTION)) &
call msg_print_color ('s_alpha_soft', s_alpha_soft, COL_YELLOW)
select case (sub_soft%factorization_mode)
case (NO_FACTORIZATION)
kb = sub_soft%evaluate_factorization_default (p_born, born_ij)
case (FACTORIZATION_THRESHOLD)
kb = sub_soft%evaluate_factorization_threshold (thr_leg(emitter), p_born, born_ij)
end select
if (debug_on) call msg_debug2 (D_SUBTRACTION, 'KB', kb)
sqme = four * pi * alpha_coupling * s_alpha_soft * kb
if (sub_soft%xi2_expanded) then
xi2_factor = four / q2
else
xi2_factor = one
end if
if (emitter <= sub_soft%reg_data%n_in) then
sqme = xi2_factor * (one - y**2) * sqme
else
sqme = xi2_factor * (one - y) * sqme
end if
if (sub_soft%reg_data%regions(alr)%double_fsr) sqme = sqme * two
end function soft_subtraction_compute
@ %def soft_subtraction_compute
@ We loop over all external legs and do not take care to leave out non-colored
ones because [[born_ij]] is constructed in such a way that it is only
non-zero for colored entries.
<<real subtraction: soft sub: TBP>>=
procedure :: evaluate_factorization_default => &
soft_subtraction_evaluate_factorization_default
<<real subtraction: procedures>>=
function soft_subtraction_evaluate_factorization_default &
(sub_soft, p, born_ij) result (kb)
real(default) :: kb
class(soft_subtraction_t), intent(inout) :: sub_soft
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(in), dimension(:,:) :: born_ij
integer :: i, j
kb = zero
call sub_soft%compute_momentum_matrix (p)
do i = 1, size (p)
do j = 1, size (p)
kb = kb + sub_soft%momentum_matrix (i, j) * born_ij (i, j)
end do
end do
end function soft_subtraction_evaluate_factorization_default
@ %def soft_subtraction_evaluate_factorization_default
@ We have to multiply this with $\xi^2(1-y)$. Further, when applying
the soft $\mathcal{S}$-function, the energy of the radiated particle
is factored out. Thus we have $\xi^2/E_{em}^2(1-y) = 4/q_0^2(1-y)$.
Computes the quantity $\mathcal{K}_{ij} = \frac{k_i \cdot
k_j}{(k_i\cdot k)(k_j\cdot k)}$.
<<real subtraction: soft sub: TBP>>=
procedure :: compute_momentum_matrix => &
soft_subtraction_compute_momentum_matrix
<<real subtraction: procedures>>=
subroutine soft_subtraction_compute_momentum_matrix &
(sub_soft, p_born)
class(soft_subtraction_t), intent(inout) :: sub_soft
type(vector4_t), intent(in), dimension(:) :: p_born
real(default) :: num, deno1, deno2
integer :: i, j
do i = 1, sub_soft%reg_data%n_legs_born
do j = 1, sub_soft%reg_data%n_legs_born
if (i <= j) then
num = p_born(i) * p_born(j)
deno1 = p_born(i) * sub_soft%p_soft
deno2 = p_born(j) * sub_soft%p_soft
sub_soft%momentum_matrix(i, j) = num / (deno1 * deno2)
else
!!! momentum matrix is symmetric.
sub_soft%momentum_matrix(i, j) = sub_soft%momentum_matrix(j, i)
end if
end do
end do
end subroutine soft_subtraction_compute_momentum_matrix
@ %def soft_subtraction_compute_momentum_matrx
@
<<real subtraction: soft sub: TBP>>=
procedure :: evaluate_factorization_threshold => &
soft_subtraction_evaluate_factorization_threshold
<<real subtraction: procedures>>=
function soft_subtraction_evaluate_factorization_threshold &
(sub_soft, leg, p_born, born_ij) result (kb)
real(default) :: kb
class(soft_subtraction_t), intent(inout) :: sub_soft
integer, intent(in) :: leg
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in), dimension(:,:) :: born_ij
type(vector4_t), dimension(4) :: p
p = get_threshold_momenta (p_born)
kb = evaluate_leg_pair (ASSOCIATED_LEG_PAIR (leg))
if (debug2_active (D_SUBTRACTION)) call show_debug ()
contains
function evaluate_leg_pair (i_start) result (kbb)
real(default) :: kbb
integer, intent(in) :: i_start
integer :: i1, i2
real(default) :: numerator, deno1, deno2
kbb = zero
do i1 = i_start, i_start + 1
do i2 = i_start, i_start + 1
numerator = p(i1) * p(i2)
deno1 = p(i1) * sub_soft%p_soft
deno2 = p(i2) * sub_soft%p_soft
kbb = kbb + numerator * born_ij (i1, i2) / deno1 / deno2
end do
end do
if (debug2_active (D_SUBTRACTION)) then
do i1 = i_start, i_start + 1
do i2 = i_start, i_start + 1
call msg_print_color('i1', i1, COL_PEACH)
call msg_print_color('i2', i2, COL_PEACH)
call msg_print_color('born_ij (i1,i2)', born_ij (i1,i2), COL_PINK)
print *, 'Top momentum: ', p(1)%p
end do
end do
end if
end function evaluate_leg_pair
subroutine show_debug ()
integer :: i
call msg_print_color ('soft_subtraction_evaluate_factorization_threshold', COL_GREEN)
do i = 1, 4
print *, 'sqrt(p(i)**2) = ', sqrt(p(i)**2)
end do
end subroutine show_debug
end function soft_subtraction_evaluate_factorization_threshold
@ %def soft_subtraction_evaluate_factorization_threshold
@
<<real subtraction: soft sub: TBP>>=
procedure :: i_xi_ref => soft_subtraction_i_xi_ref
<<real subtraction: procedures>>=
function soft_subtraction_i_xi_ref (sub_soft, alr, i_phs) result (i_xi_ref)
integer :: i_xi_ref
class(soft_subtraction_t), intent(in) :: sub_soft
integer, intent(in) :: alr, i_phs
if (sub_soft%use_resonance_mappings) then
i_xi_ref = sub_soft%reg_data%alr_to_i_contributor (alr)
else if (sub_soft%factorization_mode == FACTORIZATION_THRESHOLD) then
i_xi_ref = i_phs
else
i_xi_ref = 1
end if
end function soft_subtraction_i_xi_ref
@ %def soft_subtraction_i_xi_ref
@
<<real subtraction: soft sub: TBP>>=
procedure :: final => soft_subtraction_final
<<real subtraction: procedures>>=
subroutine soft_subtraction_final (sub_soft)
class(soft_subtraction_t), intent(inout) :: sub_soft
if (associated (sub_soft%reg_data)) nullify (sub_soft%reg_data)
if (allocated (sub_soft%momentum_matrix)) deallocate (sub_soft%momentum_matrix)
end subroutine soft_subtraction_final
@ %def soft_subtraction_final
@
\subsection{Soft mismatch}
<<real subtraction: public>>=
public :: soft_mismatch_t
<<real subtraction: types>>=
type :: soft_mismatch_t
type(region_data_t), pointer :: reg_data => null ()
real(default), dimension(:), allocatable :: sqme_born
real(default), dimension(:,:,:), allocatable :: sqme_born_color_c
real(default), dimension(:,:,:), allocatable :: sqme_born_charge_c
type(real_kinematics_t), pointer :: real_kinematics => null ()
type(soft_subtraction_t) :: sub_soft
contains
<<real subtraction: soft mismatch: TBP>>
end type soft_mismatch_t
@ %def soft_mismatch_t
@
<<real subtraction: soft mismatch: TBP>>=
procedure :: init => soft_mismatch_init
<<real subtraction: procedures>>=
subroutine soft_mismatch_init (soft_mismatch, reg_data, &
real_kinematics, factorization_mode)
class(soft_mismatch_t), intent(inout) :: soft_mismatch
type(region_data_t), intent(in), target :: reg_data
type(real_kinematics_t), intent(in), target :: real_kinematics
integer, intent(in) :: factorization_mode
soft_mismatch%reg_data => reg_data
allocate (soft_mismatch%sqme_born (reg_data%n_flv_born))
allocate (soft_mismatch%sqme_born_color_c (reg_data%n_legs_born, &
reg_data%n_legs_born, reg_data%n_flv_born))
allocate (soft_mismatch%sqme_born_charge_c (reg_data%n_legs_born, &
reg_data%n_legs_born, reg_data%n_flv_born))
call soft_mismatch%sub_soft%init (reg_data)
soft_mismatch%sub_soft%xi2_expanded = .false.
soft_mismatch%real_kinematics => real_kinematics
soft_mismatch%sub_soft%factorization_mode = factorization_mode
end subroutine soft_mismatch_init
@ %def soft_mismatch_init
@ Main routine to compute the soft mismatch. Loops over all singular regions.
There, it first creates the soft vector, then the necessary soft real matrix element.
These inputs are then used to get the numerical value of the soft mismatch.
<<real subtraction: soft mismatch: TBP>>=
procedure :: evaluate => soft_mismatch_evaluate
<<real subtraction: procedures>>=
function soft_mismatch_evaluate (soft_mismatch, alpha_s) result (sqme_mismatch)
real(default) :: sqme_mismatch
class(soft_mismatch_t), intent(inout) :: soft_mismatch
real(default), intent(in) :: alpha_s
integer :: alr, i_born, emitter, i_res, i_phs, i_con
real(default) :: xi, y, q2, s
real(default) :: E_gluon
type(vector4_t) :: p_em
real(default) :: sqme_alr, sqme_soft
type(vector4_t), dimension(:), allocatable :: p_born
sqme_mismatch = zero
associate (real_kinematics => soft_mismatch%real_kinematics)
xi = real_kinematics%xi_mismatch
y = real_kinematics%y_mismatch
s = real_kinematics%cms_energy2
E_gluon = sqrt (s) * xi / two
if (debug_active (D_MISMATCH)) then
print *, 'Evaluating soft mismatch: '
print *, 'Phase space: '
call vector4_write_set (real_kinematics%p_born_cms%get_momenta(1), &
show_mass = .true.)
print *, 'xi: ', xi, 'y: ', y, 's: ', s, 'E_gluon: ', E_gluon
end if
allocate (p_born (soft_mismatch%reg_data%n_legs_born))
do alr = 1, soft_mismatch%reg_data%n_regions
i_phs = real_kinematics%alr_to_i_phs (alr)
if (soft_mismatch%reg_data%has_pseudo_isr ()) then
i_con = 1
p_born = soft_mismatch%real_kinematics%p_born_onshell%get_momenta(1)
else
i_con = soft_mismatch%reg_data%alr_to_i_contributor (alr)
p_born = soft_mismatch%real_kinematics%p_born_cms%get_momenta(1)
end if
q2 = real_kinematics%xi_ref_momenta(i_con)**2
emitter = soft_mismatch%reg_data%regions(alr)%emitter
p_em = p_born (emitter)
i_res = soft_mismatch%reg_data%regions(alr)%i_res
i_born = soft_mismatch%reg_data%regions(alr)%uborn_index
call print_debug_alr ()
call soft_mismatch%sub_soft%create_softvec_mismatch &
(E_gluon, y, real_kinematics%phi, p_em)
if (debug_active (D_MISMATCH)) &
print *, 'Created soft vector: ', soft_mismatch%sub_soft%p_soft%p
select type (fks_mapping => soft_mismatch%reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
call fks_mapping%set_resonance_momentum &
(real_kinematics%xi_ref_momenta(i_con))
end select
sqme_soft = soft_mismatch%sub_soft%compute &
(p_born, soft_mismatch%sqme_born_color_c(:,:,i_born), y, &
q2, alpha_s, alr, emitter, i_res)
sqme_alr = soft_mismatch%compute (alr, xi, y, p_em, &
real_kinematics%xi_ref_momenta(i_con), soft_mismatch%sub_soft%p_soft, &
soft_mismatch%sqme_born(i_born), sqme_soft, &
alpha_s, s)
if (debug_on) call msg_debug (D_MISMATCH, 'sqme_alr: ', sqme_alr)
sqme_mismatch = sqme_mismatch + sqme_alr
end do
end associate
contains
subroutine print_debug_alr ()
if (debug_active (D_MISMATCH)) then
print *, 'alr: ', alr
print *, 'i_phs: ', i_phs, 'i_con: ', i_con, 'i_res: ', i_res
print *, 'emitter: ', emitter, 'i_born: ', i_born
print *, 'emitter momentum: ', p_em%p
print *, 'resonance momentum: ', &
soft_mismatch%real_kinematics%xi_ref_momenta(i_con)%p
print *, 'q2: ', q2
end if
end subroutine print_debug_alr
end function soft_mismatch_evaluate
@ %def soft_mismatch_evaluate
@ Computes the soft mismatch in a given $\alpha_r$,
\begin{align*}
I_{s+,\alpha_r} &= \int d\Phi_B \int_0^\infty d\xi \int_{-1}^1 dy \int_0^{2\pi} d\phi
\frac{s\xi}{(4\pi)^3} \\
&\times \left\lbrace\tilde{R}_{\alpha_r}
\left(e^{-\frac{2k_\gamma \cdot k_{res}}{k_{res}}^2} - e^{-\xi}\right)
- \frac{32 \pi \alpha_s C_{em}}{s\xi^2} B_{f_b(\alpha_r)} (1-y)^{-1}
\left[e^{-\frac{2\bar{k}_{em} \cdot k_{res}}{k_{res}^2} \frac{k_\gamma^0}{k_{em}^0}} - e^{-\xi}\right]\right\rbrace.
\end{align*}
<<real subtraction: soft mismatch: TBP>>=
procedure :: compute => soft_mismatch_compute
<<real subtraction: procedures>>=
function soft_mismatch_compute (soft_mismatch, alr, xi, y, p_em, p_res, p_soft, &
sqme_born, sqme_soft, alpha_s, s) result (sqme_mismatch)
real(default) :: sqme_mismatch
class(soft_mismatch_t), intent(in) :: soft_mismatch
integer, intent(in) :: alr
real(default), intent(in) :: xi, y
type(vector4_t), intent(in) :: p_em, p_res, p_soft
real(default), intent(in) :: sqme_born, sqme_soft
real(default), intent(in) :: alpha_s, s
real(default) :: q2, expo, sm1, sm2, jacobian
q2 = p_res**2
expo = - two * p_soft * p_res / q2
!!! Divide by 1 - y to factor out the corresponding
!!! factor in the soft matrix element
sm1 = sqme_soft / (one - y) * ( exp(expo) - exp(- xi) )
if (debug_on) call msg_debug2 (D_MISMATCH, 'sqme_soft in mismatch ', sqme_soft)
sm2 = zero
if (soft_mismatch%reg_data%regions(alr)%has_collinear_divergence ()) then
expo = - two * p_em * p_res / q2 * &
p_soft%p(0) / p_em%p(0)
sm2 = 32 * pi * alpha_s * cf / (s * xi**2) * sqme_born * &
( exp(expo) - exp(- xi) ) / (one - y)
end if
jacobian = soft_mismatch%real_kinematics%jac_mismatch * s * xi / (8 * twopi3)
sqme_mismatch = (sm1 - sm2) * jacobian
end function soft_mismatch_compute
@ %def soft_mismatch_compute
@
<<real subtraction: soft mismatch: TBP>>=
procedure :: final => soft_mismatch_final
<<real subtraction: procedures>>=
subroutine soft_mismatch_final (soft_mismatch)
class(soft_mismatch_t), intent(inout) :: soft_mismatch
call soft_mismatch%sub_soft%final ()
if (associated (soft_mismatch%reg_data)) nullify (soft_mismatch%reg_data)
if (allocated (soft_mismatch%sqme_born)) deallocate (soft_mismatch%sqme_born)
if (allocated (soft_mismatch%sqme_born_color_c)) deallocate (soft_mismatch%sqme_born_color_c)
if (allocated (soft_mismatch%sqme_born_charge_c)) deallocate (soft_mismatch%sqme_born_charge_c)
if (associated (soft_mismatch%real_kinematics)) nullify (soft_mismatch%real_kinematics)
end subroutine soft_mismatch_final
@ %def soft_mismatch_final
@
\subsection{Collinear and soft-collinear subtraction terms}
This data type deals with the calculation of the collinear and
soft-collinear contribution to the cross section.
<<real subtraction: public>>=
public :: coll_subtraction_t
<<real subtraction: types>>=
type :: coll_subtraction_t
integer :: n_in, n_alr
logical :: use_resonance_mappings = .false.
real(default) :: CA = 0, CF = 0, TR = 0
contains
<<real subtraction: coll sub: TBP>>
end type coll_subtraction_t
@ %def coll_subtraction_t
@
<<real subtraction: coll sub: TBP>>=
procedure :: init => coll_subtraction_init
<<real subtraction: procedures>>=
subroutine coll_subtraction_init (coll_sub, n_alr, n_in)
class(coll_subtraction_t), intent(inout) :: coll_sub
integer, intent(in) :: n_alr, n_in
coll_sub%n_in = n_in
coll_sub%n_alr = n_alr
end subroutine coll_subtraction_init
@ %def coll_subtraction_init
@ Set the corresponding algebra parameters of the underlying gauge group of the correction.
<<real subtraction: coll sub: TBP>>=
procedure :: set_parameters => coll_subtraction_set_parameters
<<real subtraction: procedures>>=
subroutine coll_subtraction_set_parameters (coll_sub, CA, CF, TR)
class(coll_subtraction_t), intent(inout) :: coll_sub
real(default), intent(in) :: CA, CF, TR
coll_sub%CA = CA
coll_sub%CF = CF
coll_sub%TR = TR
end subroutine coll_subtraction_set_parameters
@ %def coll_subtraction_set_parameters
@ This subroutine computes the collinear limit of $g^\alpha(\xi,y)$ introduced
in eq.~\ref{fks: sub: real}. Care is given to also enable the usage for
the soft-collinear limit. This, we write all formulas in terms of soft-finite
quantities.
We have to compute
\begin{equation*}
\frac{J(\Phi_n,\xi,y,\phi)}{\xi} \left[(1-y)\xi^2\mathcal{R}^\alpha(\Phi_{n+1})\right]|_{y = 1}.
\end{equation*}
-The Jacobian $j$ is proportional to $\xi$, due to the $d^3 k_{n+1} / k_{n+1}^0$ factor
+The Jacobian $J$ is proportional to $\xi$, due to the $d^3 k_{n+1} / k_{n+1}^0$ factor
in the integration measure. It cancels the factor of $\xi$ in the denominator.
The remaining part of the Jacobian is multiplied in [[evaluate_region_fsr]] and is
not relevant here.
Inserting the splitting functions exemplarily for $q \to qg$ yields
\begin{equation*}
g^\alpha = \frac{8\pi\alpha_s}{k_{\mathrm{em}}^2} C_F (1-y) \xi^2
\frac{1+(1-z)^2}{z} \mathcal{B},
\end{equation*}
where we have chosen $z = E_\mathrm{rad} / \bar{E}_\mathrm{em}$ and $\bar{E}_\mathrm{em}$ denotes the emitter energy in the Born frame.
-The collinear final state imposes $\bar{k}_n = k_{n} + k_{k + 1}$ for the
+The collinear final state imposes $\bar{k}_n = k_{n} + k_{n + 1}$ for the
connection between $\Phi_n$- and $\Phi_{n+1}$-phasepace and we get $1 - z = E_\mathrm{em} / \bar{E}_\mathrm{em}$.
The denominator can be rewritten by the constraint $\bar{k}_n^2 = (k_n +
k_{n+1})^2 = 0$ to
\begin{equation*}
k_{\mathrm{em}}^2 = 2 E_\mathrm{rad} E_\mathrm{em} (1-y)
\end{equation*}
which cancels the $(1-y)$ factor in the numerator, thus showing that
the whole expression is indeed collinear-finite. We can further transform
\begin{equation*}
E_\mathrm{rad} E_\mathrm{em} = z (1-z) \bar{E}_\mathrm{em}^2
\end{equation*}
so that in total we have
\begin{equation*}
g^\alpha = \frac{4\pi\alpha_s}{1-z} \frac{1}{\bar{k}_{\text{em}}^2} C_F \left(\frac{\xi}{z}\right)^2
(1 + (1-z)^2) \mathcal{B}
\end{equation*}
Follow up calculations give us
\begin{align*}
g^{\alpha, g \rightarrow gg} & = \frac{4\pi\alpha_s}{1-z}\frac{1}{\bar{k}_{\text{em}}^2}
C_{\mathrm{A}} \frac{\xi}{z} \left\lbrace 2 \left( \frac{z}{1 - z} \xi + \frac{1 - z}{\frac{z}{\xi}} \right) \mathcal{B} + 4\xi z(1 - z) \hat{k}_{\perp}^{\mu} \hat{k}_{\perp}^{\nu} \mathcal{B}_{\mu\nu} \right\rbrace, \\
g^{\alpha, g \rightarrow qq} & = \frac{4\pi\alpha_s}{1-z} \frac{1}{\bar{k}_{\text{em}}^2} T_{\mathrm{R}}
\frac{\xi}{z} \left\lbrace \xi \mathcal{B} - 4\xi z(1 - z) \hat{k}_{\perp}^{\mu} \hat{k}_{\perp}^{\nu} \mathcal{B}_{\mu\nu} \right\rbrace.
\end{align*}
The ratio $z / \xi$ is finite in the soft limit
\begin{equation*}
\frac{z}{\xi} = \frac{q^0}{2\bar{E}_\mathrm{em}}
\end{equation*}
so that $\xi$ does not appear explicitly in the computation.
The argumentation above is valid for $q \to qg$--splittings, but the general
factorization is valid for general splittings, also for those involving spin
correlations and QED splittings. Note that care has to be given to the definition
of $z$. Further, we have factored out a factor of $z$ to include in the
ratio $z/\xi$, which has to be taken into account in the implementation of
the splitting functions.
<<real subtraction: coll sub: TBP>>=
procedure :: compute_fsr => coll_subtraction_compute_fsr
<<real subtraction: procedures>>=
function coll_subtraction_compute_fsr &
(coll_sub, emitter, flst, p_res, p_born, sqme_born, mom_times_sqme_spin_c, &
xi, alpha_coupling, double_fsr) result (sqme)
real(default) :: sqme
class(coll_subtraction_t), intent(in) :: coll_sub
integer, intent(in) :: emitter
integer, dimension(:), intent(in) :: flst
type(vector4_t), intent(in) :: p_res
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in) :: sqme_born, mom_times_sqme_spin_c
real(default), intent(in) :: xi, alpha_coupling
logical, intent(in) :: double_fsr
real(default) :: q0, z, p0, z_o_xi, onemz
integer :: nlegs, flv_em, flv_rad
nlegs = size (flst)
flv_rad = flst(nlegs); flv_em = flst(emitter)
q0 = p_res**1
p0 = p_res * p_born(emitter) / q0
!!! Here, z corresponds to 1-z in the formulas of arXiv:1002.2581;
!!! the integrand is symmetric under this variable change
z_o_xi = q0 / (two * p0)
z = xi * z_o_xi; onemz = one - z
if (is_gluon (flv_em) .and. is_gluon (flv_rad)) then
sqme = coll_sub%CA * ( two * ( z / onemz * xi + onemz / z_o_xi ) * sqme_born &
+ four * xi * z * onemz * mom_times_sqme_spin_c )
else if (is_fermion (flv_em) .and. is_fermion (flv_rad)) then
sqme = coll_sub%TR * xi * (sqme_born - four * z * onemz * mom_times_sqme_spin_c)
else if (is_fermion (flv_em) .and. is_massless_vector (flv_rad)) then
sqme = sqme_born * coll_sub%CF * (one + onemz**2) / z_o_xi
else
sqme = zero
end if
sqme = sqme / (p0**2 * onemz * z_o_xi)
sqme = sqme * four * pi * alpha_coupling
if (double_fsr) sqme = sqme * onemz * two
end function coll_subtraction_compute_fsr
@ %def coll_subtraction_compute_fsr
@ Like in the context of [[coll_subtraction_compute_fsr]] we compute
the quantity
\begin{equation*}
\frac{J(\Phi_n,\xi,y,\phi)}{\xi} \left[(1-y)\xi^2\mathcal{R}^\alpha(\Phi_{n+1})\right]|_{y = 1},
\end{equation*}
and, additionally the anti-collinear case with $y = +1$, which, however,
is completely analogous. Again, the Jacobian is proportional to $\xi$, so we
drop the $J / \xi$ factor. Note that it is important to take into account this missing
factor of $\xi$ in the computation of the Jacobian during phase-space generation
both for fixed-beam and structure ISR. We consider only a $q \to qg$ splitting
arguing that other splittings are identical in terms of the
factors which cancel. It is given by
\begin{equation*}
g^\alpha = \frac{8\pi\alpha_s}{-k_{\mathrm{em}}^2} C_F (1-y) \xi^2
\frac{1+z^2}{1-z} \mathcal{B}.
\end{equation*}
Note the negative sign of $k_\mathrm{em}^2$ to compensate the negative
virtuality of the initial-state emitter.
For ISR, $z$ is defined with respect to the emitter energy entering the hard
interaction, i.e.
\begin{equation*}
z = \frac{E_\mathrm{beam} - E_\mathrm{rad}}{E_\mathrm{beam}} =
1 - \frac{E_\mathrm{rad}}{E_\mathrm{beam}}.
\end{equation*}
Because $E_\mathrm{rad} = E_\mathrm{beam} \cdot \xi$, it is
$z = 1 - \xi$. The factor $k_\mathrm{em}^2$ in the denonimator
is rewritten as
\begin{equation*}
k_\mathrm{em}^2 = \left(p_\mathrm{beam} - p_\mathrm{rad}\right)^2
= - 2 p_\mathrm{beam} \cdot p_\mathrm{rad}
= - 2 E_\mathrm{beam} E_\mathrm{rad} (1-y)
= -2 E_\mathrm{beam}^2 (1-z) (1-y).
\end{equation*}
This leads to the cancellation of the $(1-y)$ factors and one of
the two factors of $\xi$ in the numerator. Further rewriting to
\begin{equation*}
E_\mathrm{beam} E_\mathrm{rad} = E_\mathrm{beam}^2 (1-z)
\end{equation*}
cancels another factor of $\xi$. We thus end up with
\begin{equation*}
g^\alpha = \frac{4\pi\alpha_s}{E_\mathrm{beam}^2} C_F \left(1 + z^2\right)\mathcal{B},
\end{equation*}
which is soft-finite.
Now what about this boosting to the other beam?
+
+Note that here in [[compute_isr]], [[sqme_born]] is supposed to be
+the squared Born matrix element convoluted with the real PDF.
<<real subtraction: coll sub: TBP>>=
procedure :: compute_isr => coll_subtraction_compute_isr
<<real subtraction: procedures>>=
function coll_subtraction_compute_isr &
(coll_sub, emitter, flst, p_born, sqme_born, mom_times_sqme_spin_c, &
xi, alpha_coupling, isr_mode) result (sqme)
real(default) :: sqme
class(coll_subtraction_t), intent(in) :: coll_sub
integer, intent(in) :: emitter
integer, dimension(:), intent(in) :: flst
type(vector4_t), intent(in), dimension(:) :: p_born
real(default), intent(in) :: sqme_born
real(default), intent(in) :: mom_times_sqme_spin_c
real(default), intent(in) :: xi, alpha_coupling
integer, intent(in) :: isr_mode
real(default) :: z, onemz, p02
integer :: nlegs, flv_em, flv_rad
if (isr_mode == SQRTS_VAR .and. vector_set_is_cms (p_born, coll_sub%n_in)) then
call vector4_write_set (p_born, show_mass = .true., &
check_conservation = .true.)
call msg_fatal ("Collinear subtraction, ISR: Phase space point &
&must be in lab frame")
end if
nlegs = size (flst)
flv_rad = flst(nlegs); flv_em = flst(emitter)
!!! No need to pay attention to n_in = 1, because this case always has a
!!! massive initial-state particle and thus no collinear divergence.
p02 = p_born(1)%p(0) * p_born(2)%p(0) / two
z = one - xi; onemz = xi
if (is_massless_vector (flv_em) .and. is_massless_vector (flv_rad)) then
sqme = coll_sub%CA * (two * (z + z * onemz**2) * sqme_born + four * onemz**2 &
/ z * mom_times_sqme_spin_c)
else if (is_fermion (flv_em) .and. is_massless_vector (flv_rad)) then
sqme = coll_sub%CF * (one + z**2) * sqme_born
else if (is_fermion (flv_em) .and. is_fermion (flv_rad)) then
sqme = coll_sub%CF * (z * onemz * sqme_born + four * onemz**2 / z * mom_times_sqme_spin_c)
else if (is_massless_vector (flv_em) .and. is_fermion (flv_rad)) then
sqme = coll_sub%TR * (z**2 + onemz**2) * onemz * sqme_born
else
sqme = zero
end if
if (isr_mode == SQRTS_VAR) then
sqme = sqme / p02 * z
else
!!! We have no idea why this seems to work as there should be no factor
!!! of z for the fixed-beam settings. This should definitely be understood in the
!!! future!
sqme = sqme / p02 / z
end if
sqme = sqme * four * pi * alpha_coupling
end function coll_subtraction_compute_isr
@ %def coll_subtraction_compute_isr
@
<<real subtraction: coll sub: TBP>>=
procedure :: final => coll_subtraction_final
<<real subtraction: procedures>>=
subroutine coll_subtraction_final (sub_coll)
class(coll_subtraction_t), intent(inout) :: sub_coll
sub_coll%use_resonance_mappings = .false.
end subroutine coll_subtraction_final
@ %def coll_subtraction_final
@
\subsection{Real Subtraction}
We store a pointer to the [[nlo_settings_t]] object which holds tuning parameters, e.g. cutoffs for the subtraction terms.
<<real subtraction: public>>=
public :: real_subtraction_t
<<real subtraction: types>>=
type :: real_subtraction_t
type(nlo_settings_t), pointer :: settings => null ()
type(region_data_t), pointer :: reg_data => null ()
type(real_kinematics_t), pointer :: real_kinematics => null ()
type(isr_kinematics_t), pointer :: isr_kinematics => null ()
type(real_scales_t) :: scales
real(default), dimension(:,:), allocatable :: sqme_real_non_sub
real(default), dimension(:), allocatable :: sqme_born
- real(default), dimension(:,:,:), allocatable :: sqme_coll_isr
- real(default), dimension(:,:,:), allocatable :: sqme_born_color_c
- real(default), dimension(:,:,:), allocatable :: sqme_born_charge_c
- complex(default), dimension(:,:,:,:), allocatable :: sqme_born_spin_c
- type(soft_subtraction_t) :: sub_soft
- type(coll_subtraction_t) :: sub_coll
- logical, dimension(:), allocatable :: sc_required
- logical :: subtraction_deactivated = .false.
- integer :: purpose = INTEGRATION
- logical :: radiation_event = .true.
- logical :: subtraction_event = .false.
- integer, dimension(:), allocatable :: selected_alr
+ real(default), dimension(:,:), allocatable :: sf_factors
+ real(default), dimension(:,:,:), allocatable :: sqme_born_color_c
+ real(default), dimension(:,:,:), allocatable :: sqme_born_charge_c
+ complex(default), dimension(:,:,:,:), allocatable :: sqme_born_spin_c
+ type(soft_subtraction_t) :: sub_soft
+ type(coll_subtraction_t) :: sub_coll
+ logical, dimension(:), allocatable :: sc_required
+ logical :: subtraction_deactivated = .false.
+ integer :: purpose = INTEGRATION
+ logical :: radiation_event = .true.
+ logical :: subtraction_event = .false.
+ integer, dimension(:), allocatable :: selected_alr
contains
<<real subtraction: real subtraction: TBP>>
end type real_subtraction_t
@ %def real_subtraction_t
@ Initializer
<<real subtraction: real subtraction: TBP>>=
procedure :: init => real_subtraction_init
<<real subtraction: procedures>>=
subroutine real_subtraction_init (rsub, reg_data, settings)
class(real_subtraction_t), intent(inout), target :: rsub
type(region_data_t), intent(in), target :: reg_data
type(nlo_settings_t), intent(in), target :: settings
integer :: alr
if (debug_on) call msg_debug (D_SUBTRACTION, "real_subtraction_init")
if (debug_on) call msg_debug (D_SUBTRACTION, "n_in", reg_data%n_in)
if (debug_on) call msg_debug (D_SUBTRACTION, "nlegs_born", reg_data%n_legs_born)
if (debug_on) call msg_debug (D_SUBTRACTION, "nlegs_real", reg_data%n_legs_real)
if (debug_on) call msg_debug (D_SUBTRACTION, "reg_data%n_regions", reg_data%n_regions)
if (debug2_active (D_SUBTRACTION)) call reg_data%write ()
rsub%reg_data => reg_data
allocate (rsub%sqme_born (reg_data%n_flv_born))
rsub%sqme_born = zero
+ allocate (rsub%sf_factors (reg_data%n_regions, 0:reg_data%n_in))
+ rsub%sf_factors = zero
allocate (rsub%sqme_born_color_c (reg_data%n_legs_born, reg_data%n_legs_born, &
reg_data%n_flv_born))
rsub%sqme_born_color_c = zero
allocate (rsub%sqme_born_charge_c (reg_data%n_legs_born, reg_data%n_legs_born, &
reg_data%n_flv_born))
rsub%sqme_born_charge_c = zero
allocate (rsub%sqme_real_non_sub (reg_data%n_flv_real, reg_data%n_phs))
rsub%sqme_real_non_sub = zero
allocate (rsub%sc_required (reg_data%n_regions))
do alr = 1, reg_data%n_regions
rsub%sc_required(alr) = reg_data%regions(alr)%sc_required
end do
if (rsub%requires_spin_correlations ()) then
allocate (rsub%sqme_born_spin_c (0:3, 0:3, reg_data%n_legs_born, reg_data%n_flv_born))
rsub%sqme_born_spin_c = zero
end if
call rsub%sub_soft%init (reg_data)
call rsub%sub_coll%init (reg_data%n_regions, reg_data%n_in)
- allocate (rsub%sqme_coll_isr (2, 2, reg_data%n_flv_born))
- rsub%sqme_coll_isr = zero
rsub%settings => settings
rsub%sub_soft%use_resonance_mappings = settings%use_resonance_mappings
rsub%sub_coll%use_resonance_mappings = settings%use_resonance_mappings
rsub%sub_soft%factorization_mode = settings%factorization_mode
end subroutine real_subtraction_init
@ %def real_subtraction_init
@
<<real subtraction: real subtraction: TBP>>=
procedure :: set_real_kinematics => real_subtraction_set_real_kinematics
<<real subtraction: procedures>>=
subroutine real_subtraction_set_real_kinematics (rsub, real_kinematics)
class(real_subtraction_t), intent(inout) :: rsub
type(real_kinematics_t), intent(in), target :: real_kinematics
rsub%real_kinematics => real_kinematics
end subroutine real_subtraction_set_real_kinematics
@ %def real_subtraction_set_real_kinematics
@
<<real subtraction: real subtraction: TBP>>=
procedure :: set_isr_kinematics => real_subtraction_set_isr_kinematics
<<real subtraction: procedures>>=
subroutine real_subtraction_set_isr_kinematics (rsub, fractions)
class(real_subtraction_t), intent(inout) :: rsub
type(isr_kinematics_t), intent(in), target :: fractions
rsub%isr_kinematics => fractions
end subroutine real_subtraction_set_isr_kinematics
@ %def real_subtraction_set_isr_kinematics
@
<<real subtraction: real subtraction: TBP>>=
procedure :: get_i_res => real_subtraction_get_i_res
<<real subtraction: procedures>>=
function real_subtraction_get_i_res (rsub, alr) result (i_res)
integer :: i_res
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr
select type (fks_mapping => rsub%reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
i_res = fks_mapping%res_map%alr_to_i_res (alr)
class default
i_res = 0
end select
end function real_subtraction_get_i_res
@ %def real_subtraction_get_i_res
@
\subsection{The real contribution to the cross section}
In each singular region $\alpha$, the real contribution to $\sigma$ is
given by the second summand of eqn. \ref{fks: sub: complete},
\begin{equation}
\label{fks: sub: real}
\sigma^\alpha_{\text{real}} = \int d\Phi_n \int_0^{2\pi} d\phi
\int_{-1}^1 dy \int_0^{\xi_{\text{max}}} d\xi
\left(\frac{1}{\xi}\right)_+ \left(\frac{1}{1-y}\right)_+
\underbrace{\frac{J(\Phi_n, \xi, y, \phi)}{\xi}
\left[(1-y)\xi^2\mathcal{R}^\alpha(\Phi_{n+1})\right]}_{g^\alpha(\xi,y)}.
\end{equation}
Writing out the plus-distribution and introducing $\tilde{\xi} =
\xi/\xi_{\text{max}}$ to set the upper integration limit to 1, this
turns out to be equal to
\begin{equation}
\begin{split}
\sigma^\alpha_{\rm{real}} &= \int d\Phi_n \int_0^{2\pi}d\phi
\int_{-1}^1 \frac{dy}{1-y} \Bigg\{\int_0^1
d\tilde{\xi}\Bigg[\frac{g^\alpha(\tilde{\xi}\xi_{\rm{max}},y)}{\tilde{\xi}}
- \underbrace{\frac{g^\alpha(0,y)}{\tilde{\xi}}}_{\text{soft}} -
\underbrace{\frac{g^\alpha(\tilde{\xi}\xi_{\rm{max}},1)}{\tilde{\xi}}}_{\text{coll.}}
+
\underbrace{\frac{g^\alpha(0,1)}{\tilde{\xi}}}_{\text{coll.+soft}}\Bigg]
\\
&+ \left[\log\xi_{\rm{max}}(y)g^\alpha(0,y) - \log\xi_{\rm{max}}(1)g^\alpha(0,1)\right]\Bigg\}.
\end{split}
\end{equation}
This formula is implemented in \texttt{compute\_sqme\_real\_fin}
<<real subtraction: real subtraction: TBP>>=
procedure :: compute => real_subtraction_compute
<<real subtraction: procedures>>=
subroutine real_subtraction_compute (rsub, emitter, i_phs, alpha_s, &
alpha_qed, separate_alrs, sqme)
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: emitter, i_phs
logical, intent(in) :: separate_alrs
real(default), intent(inout), dimension(:) :: sqme
real(default), intent(in) :: alpha_s, alpha_qed
real(default) :: sqme_alr, alpha_coupling
integer :: alr, i_con, i_res, this_emitter
logical :: same_emitter
do alr = 1, rsub%reg_data%n_regions
if (allocated (rsub%selected_alr)) then
if (.not. any (rsub%selected_alr == alr)) cycle
end if
sqme_alr = zero
if (emitter > rsub%isr_kinematics%n_in) then
same_emitter = emitter == rsub%reg_data%regions(alr)%emitter
else
same_emitter = rsub%reg_data%regions(alr)%emitter <= rsub%isr_kinematics%n_in
end if
associate (nlo_corr_type => rsub%reg_data%regions(alr)%nlo_correction_type)
if (nlo_corr_type == "QCD") then
alpha_coupling = alpha_s
else if (nlo_corr_type == "QED") then
alpha_coupling = alpha_qed
end if
end associate
if (same_emitter .and. i_phs == rsub%real_kinematics%alr_to_i_phs (alr)) then
i_res = rsub%get_i_res (alr)
this_emitter = rsub%reg_data%regions(alr)%emitter
sqme_alr = rsub%evaluate_emitter_region (alr, this_emitter, i_phs, i_res, &
alpha_coupling)
if (rsub%purpose == INTEGRATION .or. rsub%purpose == FIXED_ORDER_EVENTS) then
i_con = rsub%get_i_contributor (alr)
sqme_alr = sqme_alr * rsub%get_phs_factor (i_con)
end if
end if
if (separate_alrs) then
sqme(alr) = sqme(alr) + sqme_alr
else
sqme(1) = sqme(1) + sqme_alr
end if
end do
if (debug2_active (D_SUBTRACTION)) call check_s_alpha_consistency ()
contains
subroutine check_s_alpha_consistency ()
real(default) :: sum_s_alpha, sum_s_alpha_soft
integer :: i_reg, i1, i2
if (debug_on) call msg_debug2 (D_SUBTRACTION, "Check consistency of s_alpha: ")
do i_reg = 1, rsub%reg_data%n_regions
sum_s_alpha = zero; sum_s_alpha_soft = zero
do alr = 1, rsub%reg_data%regions(i_reg)%nregions
call rsub%reg_data%regions(i_reg)%ftuples(alr)%get (i1, i2)
call rsub%evaluate_emitter_region_debug (i_reg, alr, i1, i2, i_phs, &
sum_s_alpha, sum_s_alpha_soft)
end do
end do
end subroutine check_s_alpha_consistency
end subroutine real_subtraction_compute
@ %def real_subtraction_compute
@ The emitter is fixed. We now have to decide whether we evaluate in ISR or FSR
region, and also if resonances are used.
<<real subtraction: real subtraction: TBP>>=
procedure :: evaluate_emitter_region => real_subtraction_evaluate_emitter_region
<<real subtraction: procedures>>=
function real_subtraction_evaluate_emitter_region (rsub, alr, emitter, &
i_phs, i_res, alpha_coupling) result (sqme)
real(default) :: sqme
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr, emitter, i_phs, i_res
real(default), intent(in) :: alpha_coupling
if (emitter <= rsub%isr_kinematics%n_in) then
sqme = rsub%evaluate_region_isr (alr, emitter, i_phs, i_res, alpha_coupling)
else
select type (fks_mapping => rsub%reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
call fks_mapping%set_resonance_momenta &
(rsub%real_kinematics%xi_ref_momenta)
end select
sqme = rsub%evaluate_region_fsr (alr, emitter, i_phs, i_res, alpha_coupling)
end if
end function real_subtraction_evaluate_emitter_region
@ %def real_subtraction_evaluate_emitter_region
@
<<real subtraction: real subtraction: TBP>>=
procedure :: evaluate_emitter_region_debug &
=> real_subtraction_evaluate_emitter_region_debug
<<real subtraction: procedures>>=
subroutine real_subtraction_evaluate_emitter_region_debug (rsub, i_reg, alr, i1, i2, &
i_phs, sum_s_alpha, sum_s_alpha_soft)
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: i_reg, alr, i1, i2, i_phs
real(default), intent(inout) :: sum_s_alpha, sum_s_alpha_soft
type(vector4_t), dimension(:), allocatable :: p_real, p_born
integer :: i_res
allocate (p_real (rsub%reg_data%n_legs_real))
allocate (p_born (rsub%reg_data%n_legs_born))
if (rsub%reg_data%has_pseudo_isr ()) then
p_real = rsub%real_kinematics%p_real_onshell(i_phs)%get_momenta (i_phs)
p_born = rsub%real_kinematics%p_born_onshell%get_momenta (1)
else
p_real = rsub%real_kinematics%p_real_cms%get_momenta (i_phs)
p_born = rsub%real_kinematics%p_born_cms%get_momenta (1)
end if
i_res = rsub%get_i_res (i_reg)
sum_s_alpha = sum_s_alpha + rsub%reg_data%get_svalue (p_real, i_reg, i1, i2, i_res)
associate (r => rsub%real_kinematics)
if (i1 > rsub%sub_soft%reg_data%n_in) then
call rsub%sub_soft%create_softvec_fsr (p_born, r%y_soft(i_phs), r%phi, &
i1, r%xi_ref_momenta(rsub%sub_soft%i_xi_ref (i_reg, i_phs)))
else
call rsub%sub_soft%create_softvec_isr (r%y_soft(i_phs), r%phi)
end if
end associate
sum_s_alpha_soft = sum_s_alpha_soft + rsub%reg_data%get_svalue_soft &
(p_born, rsub%sub_soft%p_soft, i_reg, i1, i_res)
end subroutine real_subtraction_evaluate_emitter_region_debug
@ %def real_subtraction_evaluate_emitter_region_debug
@ This subroutine computes the finite part of the real matrix element in
an individual singular region.
First, the radiation variables are fetched and $\mathcal{R}$ is
multiplied by the appropriate $S_\alpha$-factors,
region multiplicities and double-FSR factors.
Then, it computes the soft, collinear, soft-collinear and remnant matrix
elements and supplies the corresponding factor $1/\xi/(1-y)$ as well as
the corresponding jacobians.
<<real subtraction: real subtraction: TBP>>=
procedure :: evaluate_region_fsr => real_subtraction_evaluate_region_fsr
<<real subtraction: procedures>>=
function real_subtraction_evaluate_region_fsr (rsub, alr, emitter, i_phs, &
i_res, alpha_coupling) result (sqme_tot)
real(default) :: sqme_tot
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr, emitter, i_phs, i_res
real(default), intent(in) :: alpha_coupling
real(default) :: sqme_rad, sqme_soft, sqme_coll, sqme_cs, sqme_remn
sqme_rad = zero; sqme_soft = zero; sqme_coll = zero
sqme_cs = zero; sqme_remn = zero
associate (region => rsub%reg_data%regions(alr), template => rsub%settings%fks_template)
if (rsub%radiation_event) then
sqme_rad = rsub%sqme_real_non_sub (rsub%reg_data%get_matrix_element_index (alr), i_phs)
call evaluate_fks_factors (sqme_rad, rsub%reg_data, rsub%real_kinematics, &
alr, i_phs, emitter, i_res)
call apply_kinematic_factors_radiation (sqme_rad, rsub%purpose, &
rsub%real_kinematics, i_phs, .false., rsub%reg_data%has_pseudo_isr (), &
emitter)
end if
if (rsub%subtraction_event .and. .not. rsub%subtraction_deactivated) then
if (debug2_active (D_SUBTRACTION)) then
print *, "[real_subtraction_evaluate_region_fsr]"
print *, "xi: ", rsub%real_kinematics%xi_max(i_phs) * rsub%real_kinematics%xi_tilde
print *, "y: ", rsub%real_kinematics%y(i_phs)
end if
call rsub%evaluate_subtraction_terms_fsr (alr, emitter, i_phs, i_res, alpha_coupling, &
sqme_soft, sqme_coll, sqme_cs)
call apply_kinematic_factors_subtraction_fsr (sqme_soft, sqme_coll, sqme_cs, &
rsub%real_kinematics, i_phs)
associate (symm_factor_fs => rsub%reg_data%born_to_real_symm_factor_fs (alr))
sqme_soft = sqme_soft * symm_factor_fs
sqme_coll = sqme_coll * symm_factor_fs
sqme_cs = sqme_cs * symm_factor_fs
end associate
sqme_remn = compute_sqme_remnant_fsr (sqme_soft, sqme_cs, &
rsub%real_kinematics%xi_max(i_phs), template%xi_cut, rsub%real_kinematics%xi_tilde)
select case (rsub%purpose)
case (INTEGRATION)
sqme_tot = sqme_rad - sqme_soft - sqme_coll + sqme_cs + sqme_remn
case (FIXED_ORDER_EVENTS)
sqme_tot = - sqme_soft - sqme_coll + sqme_cs + sqme_remn
case default
sqme_tot = zero
call msg_bug ("real_subtraction_evaluate_region_fsr: " // &
"Undefined rsub%purpose")
end select
else
sqme_tot = sqme_rad
end if
sqme_tot = sqme_tot * rsub%real_kinematics%jac_rand(i_phs)
sqme_tot = sqme_tot * rsub%reg_data%regions(alr)%mult
end associate
if (debug_active (D_SUBTRACTION) .and. .not. debug2_active (D_SUBTRACTION)) then
call real_subtraction_register_debug_sqme (rsub, alr, emitter, i_phs, sqme_rad, sqme_soft, &
sqme_coll=sqme_coll, sqme_cs=sqme_cs)
else if (debug2_active (D_SUBTRACTION)) then
call write_computation_status_fsr ()
end if
contains
<<real subtraction: real subtraction evaluate region fsr: procedures>>
subroutine write_computation_status_fsr (passed, total, region_type, full)
integer, intent(in), optional :: passed, total
character(*), intent(in), optional :: region_type
integer :: i_born
integer :: u
real(default) :: xi
logical :: yorn
logical, intent(in), optional :: full
yorn = .true.
if (present (full)) yorn = full
if (debug_on) call msg_debug (D_SUBTRACTION, "real_subtraction_evaluate_region_fsr")
u = given_output_unit (); if (u < 0) return
i_born = rsub%reg_data%regions(alr)%uborn_index
xi = rsub%real_kinematics%xi_max (i_phs) * rsub%real_kinematics%xi_tilde
write (u,'(A,I2)') 'rsub%purpose: ', rsub%purpose
write (u,'(A,I3)') 'alr: ', alr
write (u,'(A,I3)') 'emitter: ', emitter
write (u,'(A,I3)') 'i_phs: ', i_phs
write (u,'(A,F6.4)') 'xi_max: ', rsub%real_kinematics%xi_max (i_phs)
write (u,'(A,F6.4)') 'xi_cut: ', rsub%real_kinematics%xi_max(i_phs) * rsub%settings%fks_template%xi_cut
write (u,'(A,F6.4,2X,A,F6.4)') 'xi: ', xi, 'y: ', rsub%real_kinematics%y (i_phs)
if (yorn) then
write (u,'(A,ES16.9)') 'sqme_born: ', rsub%sqme_born(i_born)
write (u,'(A,ES16.9)') 'sqme_real: ', sqme_rad
write (u,'(A,ES16.9)') 'sqme_soft: ', sqme_soft
write (u,'(A,ES16.9)') 'sqme_coll: ', sqme_coll
write (u,'(A,ES16.9)') 'sqme_coll-soft: ', sqme_cs
write (u,'(A,ES16.9)') 'sqme_remn: ', sqme_remn
write (u,'(A,ES16.9)') 'sqme_tot: ', sqme_tot
if (present (passed) .and. present (total) .and. &
present (region_type)) &
write (u,'(A)') char (str (passed) // " of " // str (total) // &
" " // region_type // " points passed in total")
end if
write (u,'(A,ES16.9)') 'jacobian - real: ', rsub%real_kinematics%jac(i_phs)%jac(1)
write (u,'(A,ES16.9)') 'jacobian - soft: ', rsub%real_kinematics%jac(i_phs)%jac(2)
write (u,'(A,ES16.9)') 'jacobian - coll: ', rsub%real_kinematics%jac(i_phs)%jac(3)
end subroutine write_computation_status_fsr
end function real_subtraction_evaluate_region_fsr
@ %def real_subtraction_evalute_region_fsr
@ Compares the real matrix element to the subtraction terms in the soft, the collinear
or the soft-collinear limits. Used for debug purposes if [[?test_anti_coll_limit]],
[[?test_coll_limit]] and/or [[?test_soft_limit]] are set in the Sindarin.
[[sqme_soft]] and [[sqme_cs]] need to be provided if called for FSR and [[sqme_coll_plus]],
[[sqme_coll_minus]], [[sqme_cs_plus]] as well as [[sqme_cs_minus]] need to be provided if called for ISR.
<<real subtraction: procedures>>=
subroutine real_subtraction_register_debug_sqme (rsub, alr, emitter, i_phs, sqme_rad, sqme_soft,&
sqme_coll, sqme_cs, sqme_coll_plus, sqme_coll_minus, sqme_cs_plus, sqme_cs_minus)
class(real_subtraction_t), intent(in) :: rsub
integer, intent(in) :: alr, emitter, i_phs
real(default), intent(in) :: sqme_rad, sqme_soft
real(default), intent(in), optional :: sqme_coll, sqme_cs, sqme_coll_plus, sqme_coll_minus, sqme_cs_plus, sqme_cs_minus
real(default), dimension(:), allocatable, save :: sqme_rad_store
logical :: is_soft, is_collinear_plus, is_collinear_minus, is_fsr
- real(default), parameter :: soft_threshold = 0.01_default
+ real(default), parameter :: soft_threshold = 0.001_default
real(default), parameter :: coll_threshold = 0.99_default
- real(default) :: sqme_dummy, this_sqme_rad, E_gluon, y
+ real(default), parameter :: rel_smallness = 0.01_default
+ real(default) :: sqme_dummy, this_sqme_rad, y, xi_tilde
logical, dimension(:), allocatable, save :: count_alr
if (.not. allocated (sqme_rad_store)) then
allocate (sqme_rad_store (rsub%reg_data%n_regions))
sqme_rad_store = zero
end if
if (rsub%radiation_event) then
sqme_rad_store(alr) = sqme_rad
else
if (.not. allocated (count_alr)) then
allocate (count_alr (rsub%reg_data%n_regions))
count_alr = .false.
end if
if (is_gluon (rsub%reg_data%regions(alr)%flst_real%flst(rsub%reg_data%n_legs_real))) then
- E_gluon = rsub%real_kinematics%p_real_cms%get_energy (i_phs, rsub%reg_data%n_legs_real)
- is_soft = E_gluon < soft_threshold
+ xi_tilde = rsub%real_kinematics%xi_tilde
+ is_soft = xi_tilde < soft_threshold
else
is_soft = .false.
end if
y = rsub%real_kinematics%y(i_phs)
- is_collinear_plus = y > coll_threshold
- is_collinear_minus = -y > coll_threshold
+ is_collinear_plus = y > coll_threshold .and. &
+ rsub%reg_data%regions(alr)%has_collinear_divergence()
+ is_collinear_minus = -y > coll_threshold .and. &
+ rsub%reg_data%regions(alr)%has_collinear_divergence()
is_fsr = emitter > rsub%isr_kinematics%n_in
if (is_fsr) then
if (.not. present(sqme_coll) .or. .not. present(sqme_cs)) &
call msg_error ("real_subtraction_register_debug_sqme: Wrong arguments for FSR")
else
if (.not. present(sqme_coll_plus) .or. .not. present(sqme_coll_minus) &
.or. .not. present(sqme_cs_plus) .or. .not. present(sqme_cs_minus)) &
call msg_error ("real_subtraction_register_debug_sqme: Wrong arguments for ISR")
end if
this_sqme_rad = sqme_rad_store(alr)
if (is_soft .and. .not. is_collinear_plus .and. .not. is_collinear_minus) then
if ( .not. nearly_equal (this_sqme_rad, sqme_soft, &
- abs_smallness=eps0, rel_smallness=tiny_07*10000)) then
+ abs_smallness=tiny(1._default), rel_smallness=rel_smallness)) then
call msg_print_color (char ("Soft MEs do not match in region " // str (alr)), COL_RED)
else
call msg_print_color (char ("sqme_soft OK in region " // str (alr)), COL_GREEN)
end if
print *, 'this_sqme_rad, sqme_soft = ', this_sqme_rad, sqme_soft
end if
if (is_collinear_plus .and. .not. is_soft) then
if (is_fsr) then
if ( .not. nearly_equal (this_sqme_rad, sqme_coll, &
- abs_smallness=eps0, rel_smallness=tiny_07*10000)) then
+ abs_smallness=tiny(1._default), rel_smallness=rel_smallness)) then
call msg_print_color (char ("Collinear MEs do not match in region " // str (alr)), COL_RED)
else
call msg_print_color (char ("sqme_coll OK in region " // str (alr)), COL_GREEN)
end if
print *, 'this_sqme_rad, sqme_coll = ', this_sqme_rad, sqme_coll
else
if ( .not. nearly_equal (this_sqme_rad, sqme_coll_plus, &
- abs_smallness=eps0, rel_smallness=tiny_07*10000)) then
+ abs_smallness=tiny(1._default), rel_smallness=rel_smallness)) then
call msg_print_color (char ("Collinear MEs do not match in region " // str (alr)), COL_RED)
else
call msg_print_color (char ("sqme_coll_plus OK in region " // str (alr)), COL_GREEN)
end if
print *, 'this_sqme_rad, sqme_coll_plus = ', this_sqme_rad, sqme_coll_plus
end if
end if
if (is_collinear_minus .and. .not. is_soft) then
if (.not. is_fsr) then
if ( .not. nearly_equal (this_sqme_rad, sqme_coll_minus, &
- abs_smallness=eps0, rel_smallness=tiny_07*10000)) then
+ abs_smallness=tiny(1._default), rel_smallness=rel_smallness)) then
call msg_print_color (char ("Collinear MEs do not match in region " // str (alr)), COL_RED)
else
call msg_print_color (char ("sqme_coll_minus OK in region " // str (alr)), COL_GREEN)
end if
print *, 'this_sqme_rad, sqme_coll_minus = ', this_sqme_rad, sqme_coll_minus
end if
end if
if (is_soft .and. is_collinear_plus) then
if (is_fsr) then
if ( .not. nearly_equal (this_sqme_rad, sqme_cs, &
- abs_smallness=eps0, rel_smallness=tiny_07*10000)) then
+ abs_smallness=tiny(1._default), rel_smallness=rel_smallness)) then
call msg_print_color (char ("Soft-collinear MEs do not match in region " // str (alr)), COL_RED)
else
call msg_print_color (char ("sqme_cs OK in region " // str (alr)), COL_GREEN)
end if
print *, 'this_sqme_rad, sqme_cs = ', this_sqme_rad, sqme_cs
else
if ( .not. nearly_equal (this_sqme_rad, sqme_cs_plus, &
- abs_smallness=eps0, rel_smallness=tiny_07*10000)) then
+ abs_smallness=tiny(1._default), rel_smallness=rel_smallness)) then
call msg_print_color (char ("Soft-collinear MEs do not match in region " // str (alr)), COL_RED)
else
call msg_print_color (char ("sqme_cs_plus OK in region " // str (alr)), COL_GREEN)
end if
print *, 'this_sqme_rad, sqme_cs_plus = ', this_sqme_rad, sqme_cs_plus
end if
end if
if (is_soft .and. is_collinear_minus) then
- if ( .not. nearly_equal (this_sqme_rad, sqme_cs_minus, &
- abs_smallness=eps0, rel_smallness=tiny_07*10000)) then
- call msg_print_color (char ("Soft-collinear MEs do not match in region " // str (alr)), COL_RED)
- else
- call msg_print_color (char ("sqme_cs_minus OK in region " // str (alr)), COL_GREEN)
+ if (.not. is_fsr) then
+ if ( .not. nearly_equal (this_sqme_rad, sqme_cs_minus, &
+ abs_smallness=tiny(1._default), rel_smallness=rel_smallness)) then
+ call msg_print_color (char ("Soft-collinear MEs do not match in region " // str (alr)), COL_RED)
+ else
+ call msg_print_color (char ("sqme_cs_minus OK in region " // str (alr)), COL_GREEN)
+ end if
+ print *, 'this_sqme_rad, sqme_cs_minus = ', this_sqme_rad, sqme_cs_minus
end if
- print *, 'this_sqme_rad, sqme_cs_minus = ', this_sqme_rad, sqme_cs_minus
end if
count_alr (alr) = .true.
if (all (count_alr)) then
deallocate (count_alr)
deallocate (sqme_rad_store)
end if
end if
end subroutine real_subtraction_register_debug_sqme
@ %def real_subtraction_register_debug_sqme
@ For final state radiation, the subtraction remnant cross section is
\begin{equation}
\sigma_{\text{remn}} = \left(\sigma_{\text{soft}} - \sigma_{\text{soft-coll}}\right)
\log (\xi_{\text{max}}\xi_{\text{cut}})) \cdot \tilde{\xi}.
\end{equation}
We use the already computed [[sqme_soft]] and [[sqme_cs]] with a factor of
$\tilde{\xi}$ which we have to compensate.
<<real subtraction: real subtraction evaluate region fsr: procedures>>=
function compute_sqme_remnant_fsr (sqme_soft, sqme_cs, xi_max, xi_cut, xi_tilde) result (sqme_remn)
real(default) :: sqme_remn
real(default), intent(in) :: sqme_soft, sqme_cs, xi_max, xi_cut, xi_tilde
if (debug_on) call msg_debug (D_SUBTRACTION, "compute_sqme_remnant_fsr")
sqme_remn = zero
sqme_remn = sqme_remn + (sqme_soft - sqme_cs) * log (xi_max * xi_cut) * xi_tilde
end function compute_sqme_remnant_fsr
@ %def compute_sqme_remnant_fsr
@
<<real subtraction: real subtraction: TBP>>=
procedure :: evaluate_region_isr => real_subtraction_evaluate_region_isr
<<real subtraction: procedures>>=
function real_subtraction_evaluate_region_isr (rsub, alr, emitter, i_phs, i_res, alpha_coupling) &
result (sqme_tot)
real(default) :: sqme_tot
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr, emitter, i_phs, i_res
real(default), intent(in) :: alpha_coupling
real(default) :: sqme_rad, sqme_soft, sqme_coll_plus, sqme_coll_minus
real(default) :: sqme_cs_plus, sqme_cs_minus
real(default) :: sqme_remn
sqme_rad = zero; sqme_soft = zero;
sqme_coll_plus = zero; sqme_coll_minus = zero
sqme_cs_plus = zero; sqme_cs_minus = zero
sqme_remn = zero
associate (region => rsub%reg_data%regions(alr), template => rsub%settings%fks_template)
if (rsub%radiation_event) then
sqme_rad = rsub%sqme_real_non_sub (rsub%reg_data%get_matrix_element_index (alr), i_phs)
call evaluate_fks_factors (sqme_rad, rsub%reg_data, rsub%real_kinematics, &
alr, i_phs, emitter, i_res)
call apply_kinematic_factors_radiation (sqme_rad, rsub%purpose, rsub%real_kinematics, &
i_phs, .true., .false.)
end if
if (rsub%subtraction_event .and. .not. rsub%subtraction_deactivated) then
call rsub%evaluate_subtraction_terms_isr (alr, emitter, i_phs, i_res, alpha_coupling, &
sqme_soft, sqme_coll_plus, sqme_coll_minus, sqme_cs_plus, sqme_cs_minus)
call apply_kinematic_factors_subtraction_isr (sqme_soft, sqme_coll_plus, &
sqme_coll_minus, sqme_cs_plus, sqme_cs_minus, rsub%real_kinematics, i_phs)
associate (symm_factor_fs => rsub%reg_data%born_to_real_symm_factor_fs (alr))
sqme_soft = sqme_soft * symm_factor_fs
sqme_coll_plus = sqme_coll_plus * symm_factor_fs
sqme_coll_minus = sqme_coll_minus * symm_factor_fs
sqme_cs_plus = sqme_cs_plus * symm_factor_fs
sqme_cs_minus = sqme_cs_minus * symm_factor_fs
end associate
sqme_remn = compute_sqme_remnant_isr (rsub%isr_kinematics%isr_mode, &
sqme_soft, sqme_cs_plus, sqme_cs_minus, &
rsub%isr_kinematics, rsub%real_kinematics, i_phs, template%xi_cut)
sqme_tot = sqme_rad - sqme_soft - sqme_coll_plus - sqme_coll_minus &
+ sqme_cs_plus + sqme_cs_minus + sqme_remn
else
sqme_tot = sqme_rad
end if
end associate
sqme_tot = sqme_tot * rsub%real_kinematics%jac_rand (i_phs)
sqme_tot = sqme_tot * rsub%reg_data%regions(alr)%mult
if (debug_active (D_SUBTRACTION) .and. .not. debug2_active (D_SUBTRACTION)) then
call real_subtraction_register_debug_sqme (rsub, alr, emitter, i_phs, sqme_rad,&
sqme_soft, sqme_coll_plus=sqme_coll_plus, sqme_coll_minus=sqme_coll_minus,&
sqme_cs_plus=sqme_cs_plus, sqme_cs_minus=sqme_cs_minus)
else if (debug2_active (D_SUBTRACTION)) then
call write_computation_status_isr ()
end if
contains
<<real subtraction: evaluate region isr: procedures>>
subroutine write_computation_status_isr (unit)
integer, intent(in), optional :: unit
integer :: i_born
integer :: u
real(default) :: xi
u = given_output_unit (unit); if (u < 0) return
i_born = rsub%reg_data%regions(alr)%uborn_index
xi = rsub%real_kinematics%xi_max (i_phs) * rsub%real_kinematics%xi_tilde
write (u,'(A,I2)') 'alr: ', alr
write (u,'(A,I2)') 'emitter: ', emitter
write (u,'(A,F4.2)') 'xi_max: ', rsub%real_kinematics%xi_max (i_phs)
print *, 'xi: ', xi, 'y: ', rsub%real_kinematics%y (i_phs)
print *, 'xb1: ', rsub%isr_kinematics%x(1), 'xb2: ', rsub%isr_kinematics%x(2)
print *, 'random jacobian: ', rsub%real_kinematics%jac_rand (i_phs)
write (u,'(A,ES16.9)') 'sqme_born: ', rsub%sqme_born(i_born)
write (u,'(A,ES16.9)') 'sqme_real: ', sqme_rad
write (u,'(A,ES16.9)') 'sqme_soft: ', sqme_soft
write (u,'(A,ES16.9)') 'sqme_coll_plus: ', sqme_coll_plus
write (u,'(A,ES16.9)') 'sqme_coll_minus: ', sqme_coll_minus
write (u,'(A,ES16.9)') 'sqme_cs_plus: ', sqme_cs_plus
write (u,'(A,ES16.9)') 'sqme_cs_minus: ', sqme_cs_minus
write (u,'(A,ES16.9)') 'sqme_remn: ', sqme_remn
write (u,'(A,ES16.9)') 'sqme_tot: ', sqme_tot
write (u,'(A,ES16.9)') 'jacobian - real: ', rsub%real_kinematics%jac(i_phs)%jac(1)
write (u,'(A,ES16.9)') 'jacobian - soft: ', rsub%real_kinematics%jac(i_phs)%jac(2)
write (u,'(A,ES16.9)') 'jacobian - collplus: ', rsub%real_kinematics%jac(i_phs)%jac(3)
write (u,'(A,ES16.9)') 'jacobian - collminus: ', rsub%real_kinematics%jac(i_phs)%jac(4)
end subroutine write_computation_status_isr
end function real_subtraction_evaluate_region_isr
@ %def real_subtraction_evaluate_region_isr
@
<<real subtraction: evaluate region isr: procedures>>=
function compute_sqme_remnant_isr (isr_mode, sqme_soft, sqme_cs_plus, sqme_cs_minus, &
isr_kinematics, real_kinematics, i_phs, xi_cut) result (sqme_remn)
real(default) :: sqme_remn
integer, intent(in) :: isr_mode
real(default), intent(in) :: sqme_soft, sqme_cs_plus, sqme_cs_minus
type(isr_kinematics_t), intent(in) :: isr_kinematics
type(real_kinematics_t), intent(in) :: real_kinematics
integer, intent(in) :: i_phs
real(default), intent(in) :: xi_cut
real(default) :: xi_tilde, xi_max, xi_max_plus, xi_max_minus
xi_max = real_kinematics%xi_max (i_phs)
select case (isr_mode)
case (SQRTS_VAR)
xi_max_plus = one - isr_kinematics%x(I_PLUS)
xi_max_minus = one - isr_kinematics%x(I_MINUS)
case (SQRTS_FIXED)
xi_max_plus = real_kinematics%xi_max (i_phs)
xi_max_minus = real_kinematics%xi_max (i_phs)
end select
xi_tilde = real_kinematics%xi_tilde
sqme_remn = log(xi_max * xi_cut) * xi_tilde * sqme_soft
sqme_remn = sqme_remn - log (xi_max_plus * xi_cut) * xi_tilde * sqme_cs_plus &
- log (xi_max_minus * xi_cut) * xi_tilde * sqme_cs_minus
end function compute_sqme_remnant_isr
@ %def compute_sqme_remnant_isr
@
<<real subtraction: real subtraction: TBP>>=
procedure :: evaluate_subtraction_terms_fsr => &
real_subtraction_evaluate_subtraction_terms_fsr
<<real subtraction: procedures>>=
subroutine real_subtraction_evaluate_subtraction_terms_fsr (rsub, &
alr, emitter, i_phs, i_res, alpha_coupling, sqme_soft, sqme_coll, sqme_cs)
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr, emitter, i_phs, i_res
real(default), intent(in) :: alpha_coupling
real(default), intent(out) :: sqme_soft, sqme_coll, sqme_cs
if (debug_on) call msg_debug (D_SUBTRACTION, "real_subtraction_evaluate_subtraction_terms_fsr")
sqme_soft = zero; sqme_coll = zero; sqme_cs = zero
associate (xi_tilde => rsub%real_kinematics%xi_tilde, &
y => rsub%real_kinematics%y(i_phs), template => rsub%settings%fks_template)
if (template%xi_cut > xi_tilde) &
sqme_soft = rsub%compute_sub_soft (alr, emitter, i_phs, i_res, alpha_coupling)
if (y - 1 + template%delta_o > 0) &
sqme_coll = rsub%compute_sub_coll (alr, emitter, i_phs, alpha_coupling)
if (template%xi_cut > xi_tilde .and. y - 1 + template%delta_o > 0) &
sqme_cs = rsub%compute_sub_coll_soft (alr, emitter, i_phs, alpha_coupling)
if (debug2_active (D_SUBTRACTION)) then
print *, "FSR Cutoff:"
print *, "sub_soft: ", template%xi_cut > xi_tilde, "(ME: ", sqme_soft, ")"
print *, "sub_coll: ", (y - 1 + template%delta_o) > 0, "(ME: ", sqme_coll, ")"
print *, "sub_coll_soft: ", template%xi_cut > xi_tilde .and. (y - 1 + template%delta_o) > 0, &
"(ME: ", sqme_cs, ")"
end if
end associate
end subroutine real_subtraction_evaluate_subtraction_terms_fsr
@ %def real_subtraction_evaluate_subtraction_terms_fsr
@
<<real subtraction: procedures>>=
subroutine evaluate_fks_factors (sqme, reg_data, real_kinematics, &
alr, i_phs, emitter, i_res)
real(default), intent(inout) :: sqme
type(region_data_t), intent(inout) :: reg_data
type(real_kinematics_t), intent(in), target :: real_kinematics
integer, intent(in) :: alr, i_phs, emitter, i_res
real(default) :: s_alpha
type(phs_point_set_t), pointer :: p_real => null ()
if (reg_data%has_pseudo_isr ()) then
p_real => real_kinematics%p_real_onshell (i_phs)
else
p_real => real_kinematics%p_real_cms
end if
s_alpha = reg_data%get_svalue (p_real%get_momenta(i_phs), alr, emitter, i_res)
if (debug2_active (D_SUBTRACTION)) call msg_print_color('s_alpha', s_alpha, COL_YELLOW)
if (s_alpha > one + tiny_07) call msg_fatal ("s_alpha > 1!")
sqme = sqme * s_alpha
associate (region => reg_data%regions(alr))
if (emitter > reg_data%n_in) then
if (debug2_active (D_SUBTRACTION)) &
print *, 'Double FSR: ', region%double_fsr_factor (p_real%get_momenta(i_phs))
sqme = sqme * region%double_fsr_factor (p_real%get_momenta(i_phs))
end if
end associate
end subroutine evaluate_fks_factors
@ %def evaluate_fks_factors
@
<<real subtraction: procedures>>=
subroutine apply_kinematic_factors_radiation (sqme, purpose, real_kinematics, &
i_phs, isr, threshold, emitter)
real(default), intent(inout) :: sqme
integer, intent(in) :: purpose
type(real_kinematics_t), intent(in) :: real_kinematics
integer, intent(in) :: i_phs
logical, intent(in) :: isr, threshold
integer, intent(in), optional :: emitter
real(default) :: xi, xi_tilde, s
xi_tilde = real_kinematics%xi_tilde
xi = xi_tilde * real_kinematics%xi_max (i_phs)
select case (purpose)
case (INTEGRATION, FIXED_ORDER_EVENTS)
sqme = sqme * xi**2 / xi_tilde * real_kinematics%jac(i_phs)%jac(1)
case (POWHEG)
if (.not. isr) then
s = real_kinematics%cms_energy2
sqme = sqme * real_kinematics%jac(i_phs)%jac(1) * s / (8 * twopi3) * xi
else
call msg_fatal ("POWHEG with initial-state radiation not implemented yet")
end if
end select
end subroutine apply_kinematic_factors_radiation
@ %def apply_kinematics_factors_radiation
@
<<real subtraction: procedures>>=
subroutine apply_kinematic_factors_subtraction_fsr &
(sqme_soft, sqme_coll, sqme_cs, real_kinematics, i_phs)
real(default), intent(inout) :: sqme_soft, sqme_coll, sqme_cs
type(real_kinematics_t), intent(in) :: real_kinematics
integer, intent(in) :: i_phs
real(default) :: xi_tilde, onemy
xi_tilde = real_kinematics%xi_tilde
onemy = one - real_kinematics%y(i_phs)
sqme_soft = sqme_soft / onemy / xi_tilde
sqme_coll = sqme_coll / onemy / xi_tilde
sqme_cs = sqme_cs / onemy / xi_tilde
associate (jac => real_kinematics%jac(i_phs)%jac)
sqme_soft = sqme_soft * jac(2)
sqme_coll = sqme_coll * jac(3)
sqme_cs = sqme_cs * jac(2)
end associate
end subroutine apply_kinematic_factors_subtraction_fsr
@ %def apply_kinematic_factors_subtraction_fsr
@
<<real subtraction: procedures>>=
subroutine apply_kinematic_factors_subtraction_isr &
(sqme_soft, sqme_coll_plus, sqme_coll_minus, sqme_cs_plus, &
sqme_cs_minus, real_kinematics, i_phs)
real(default), intent(inout) :: sqme_soft, sqme_coll_plus, sqme_coll_minus
real(default), intent(inout) :: sqme_cs_plus, sqme_cs_minus
type(real_kinematics_t), intent(in) :: real_kinematics
integer, intent(in) :: i_phs
real(default) :: xi_tilde, y, onemy, onepy
xi_tilde = real_kinematics%xi_tilde
y = real_kinematics%y (i_phs)
onemy = one - y; onepy = one + y
associate (jac => real_kinematics%jac(i_phs)%jac)
sqme_soft = sqme_soft / (one - y**2) / xi_tilde * jac(2)
sqme_coll_plus = sqme_coll_plus / onemy / xi_tilde / two * jac(3)
sqme_coll_minus = sqme_coll_minus / onepy / xi_tilde / two * jac(4)
sqme_cs_plus = sqme_cs_plus / onemy / xi_tilde / two * jac(2)
sqme_cs_minus = sqme_cs_minus / onepy / xi_tilde / two * jac(2)
end associate
end subroutine apply_kinematic_factors_subtraction_isr
@ %def apply_kinematic_factors_subtraction_isr
@ This subroutine evaluates the soft and collinear subtraction terms for ISR.
References:
\begin{itemize}
\item arXiv:0709.2092, sec. 2.4.2
\item arXiv:0908.4272, sec. 4.2
\end{itemize}
For the collinear terms, the procedure is as follows:
If the emitter is 0, then a gluon was radiated from one of the incoming partons.
Gluon emissions require two counter terms:
One for emission in the direction of the first incoming parton $\oplus$
and a second for emission in the direction of the second incoming parton $\ominus$
because in both cases, there are divergent diagrams contributing to the matrix element.
So in this case both, [[sqme_coll_plus]] and [[sqme_coll_minus]], are non-zero.
If the emitter is 1 or 2, then a quark was emitted instead of a gluon.
This only leads to a divergence collinear to the emitter because for anti-collinear
quark emission, there are simply no divergent diagrams in the same region as two
collinear quarks that cannot originate in the same splitting are non-divergent.
This means that in case the emitter is 1, we need non-zero [[sqme_coll_plus]]
and in case the emitter is 2, we need non-zero [[sqme_coll_minus]].
At this point, we want to remind ourselves that in case of initial state divergences,
$y$ is just the polar angle, so the [[sqme_coll_minus]] terms are there to counter emissions in
the direction of the second incoming parton $\ominus$ and \textbf{not} to counter in general
anti-collinear divergences.
<<real subtraction: real subtraction: TBP>>=
procedure :: evaluate_subtraction_terms_isr => &
real_subtraction_evaluate_subtraction_terms_isr
<<real subtraction: procedures>>=
subroutine real_subtraction_evaluate_subtraction_terms_isr (rsub, &
alr, emitter, i_phs, i_res, alpha_coupling, sqme_soft, sqme_coll_plus, &
sqme_coll_minus, sqme_cs_plus, sqme_cs_minus)
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr, emitter, i_phs, i_res
real(default), intent(in) :: alpha_coupling
real(default), intent(out) :: sqme_soft
real(default), intent(out) :: sqme_coll_plus, sqme_coll_minus
real(default), intent(out) :: sqme_cs_plus, sqme_cs_minus
sqme_coll_plus = zero; sqme_cs_plus = zero
sqme_coll_minus = zero; sqme_cs_minus = zero
associate (xi_tilde => rsub%real_kinematics%xi_tilde, &
y => rsub%real_kinematics%y(i_phs), template => rsub%settings%fks_template)
if (template%xi_cut > xi_tilde) &
sqme_soft = rsub%compute_sub_soft (alr, emitter, i_phs, i_res, alpha_coupling)
if (emitter /= 2) then
if (y - 1 + template%delta_i > 0) then
sqme_coll_plus = rsub%compute_sub_coll (alr, 1, i_phs, alpha_coupling)
if (template%xi_cut > xi_tilde) then
sqme_cs_plus = rsub%compute_sub_coll_soft (alr, 1, i_phs, alpha_coupling)
end if
end if
end if
if (emitter /= 1) then
if (-y - 1 + template%delta_i > 0) then
sqme_coll_minus = rsub%compute_sub_coll (alr, 2, i_phs, alpha_coupling)
if (template%xi_cut > xi_tilde) then
sqme_cs_minus = rsub%compute_sub_coll_soft (alr, 2, i_phs, alpha_coupling)
end if
end if
end if
if (debug2_active (D_SUBTRACTION)) then
print *, "ISR Cutoff:"
print *, "y: ", y
print *, "delta_i: ", template%delta_i
print *, "emitter: ", emitter
print *, "sub_soft: ", template%xi_cut > xi_tilde, "(ME: ", sqme_soft, ")"
print *, "sub_coll_plus: ", (y - 1 + template%delta_i) > 0, "(ME: ", sqme_coll_plus, ")"
print *, "sub_coll_minus: ", (-y - 1 + template%delta_i) > 0, "(ME: ", sqme_coll_minus, ")"
print *, "sub_coll_soft_plus: ", template%xi_cut > xi_tilde .and. (y - 1 + template%delta_i) > 0, &
"(ME: ", sqme_cs_plus, ")"
print *, "sub_coll_soft_minus: ", template%xi_cut > xi_tilde .and. (-y - 1 + template%delta_i) > 0, &
"(ME: ", sqme_cs_minus, ")"
end if
end associate
end subroutine real_subtraction_evaluate_subtraction_terms_isr
@ %def real_subtraction_evaluate_subtraction_terms_isr
@ This is basically the part of the real Jacobian corresponding to
\begin{equation*}
\frac{q^2}{8 (2\pi)^3}.
\end{equation*}
We interpret it as the additional phase space factor of the real component,
to be more consistent with the evaluation of the Born phase space.
<<real subtraction: real subtraction: TBP>>=
procedure :: get_phs_factor => real_subtraction_get_phs_factor
<<real subtraction: procedures>>=
function real_subtraction_get_phs_factor (rsub, i_con) result (factor)
real(default) :: factor
class(real_subtraction_t), intent(in) :: rsub
integer, intent(in) :: i_con
real(default) :: s
s = rsub%real_kinematics%xi_ref_momenta (i_con)**2
factor = s / (8 * twopi3)
end function real_subtraction_get_phs_factor
@ %def real_subtraction_get_phs_factor
@
<<real subtraction: real subtraction: TBP>>=
procedure :: get_i_contributor => real_subtraction_get_i_contributor
<<real subtraction: procedures>>=
function real_subtraction_get_i_contributor (rsub, alr) result (i_con)
integer :: i_con
class(real_subtraction_t), intent(in) :: rsub
integer, intent(in) :: alr
if (allocated (rsub%reg_data%alr_to_i_contributor)) then
i_con = rsub%reg_data%alr_to_i_contributor (alr)
else
i_con = 1
end if
end function real_subtraction_get_i_contributor
@ %def real_subtraction_get_i_contributor
-@
+@ Computes the soft subtraction term.
+If there is an initial state emission having a soft divergence, then a gluon
+has to have been emitted. A gluon can always be emitted from both IS partons
+and thus, we can take the [[sf_factor]] for emitter $0$ in this case.
+Be aware that this approach will not work for $pe$ collisions.
<<real subtraction: real subtraction: TBP>>=
procedure :: compute_sub_soft => real_subtraction_compute_sub_soft
<<real subtraction: procedures>>=
function real_subtraction_compute_sub_soft (rsub, alr, emitter, &
i_phs, i_res, alpha_coupling) result (sqme_soft)
real(default) :: sqme_soft
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr, emitter, i_phs, i_res
real(default), intent(in) :: alpha_coupling
integer :: i_xi_ref, i_born
- real(default) :: q2
+ real(default) :: q2, sf_factor
type(vector4_t), dimension(:), allocatable :: p_born
associate (real_kinematics => rsub%real_kinematics, &
- nlo_corr_type => rsub%reg_data%regions(alr)%nlo_correction_type)
+ nlo_corr_type => rsub%reg_data%regions(alr)%nlo_correction_type, &
+ sregion => rsub%reg_data%regions(alr))
sqme_soft = zero
- if (rsub%reg_data%regions(alr)%has_soft_divergence ()) then
+ if (sregion%has_soft_divergence ()) then
i_xi_ref = rsub%sub_soft%i_xi_ref (alr, i_phs)
q2 = real_kinematics%xi_ref_momenta (i_xi_ref)**2
allocate (p_born (rsub%reg_data%n_legs_born))
if (rsub%reg_data%has_pseudo_isr ()) then
p_born = real_kinematics%p_born_onshell%get_momenta(1)
else
- p_born = real_kinematics%p_born_cms%get_momenta(1)
+ p_born = real_kinematics%p_born_cms%get_momenta(1) ! TODO: cms or lab?
end if
if (emitter > rsub%sub_soft%reg_data%n_in) then
call rsub%sub_soft%create_softvec_fsr &
(p_born, real_kinematics%y_soft(i_phs), &
real_kinematics%phi, emitter, &
real_kinematics%xi_ref_momenta(i_xi_ref))
+ sf_factor = one
else
call rsub%sub_soft%create_softvec_isr &
(real_kinematics%y_soft(i_phs), real_kinematics%phi)
+ sf_factor = rsub%sf_factors(alr, 0)
end if
- i_born = rsub%reg_data%regions(alr)%uborn_index
+ i_born = sregion%uborn_index
if (nlo_corr_type == "QCD") then
sqme_soft = rsub%sub_soft%compute &
- (p_born, rsub%sqme_born_color_c(:,:,i_born), &
- real_kinematics%y(i_phs), &
+ (p_born, rsub%sqme_born_color_c(:,:,i_born) * &
+ sf_factor, real_kinematics%y(i_phs), &
q2, alpha_coupling, alr, emitter, i_res)
else if (nlo_corr_type == "QED") then
sqme_soft = rsub%sub_soft%compute &
- (p_born, rsub%sqme_born_charge_c(:,:,i_born), &
- real_kinematics%y(i_phs), &
+ (p_born, rsub%sqme_born_charge_c(:,:,i_born) * &
+ sf_factor, real_kinematics%y(i_phs), &
q2, alpha_coupling, alr, emitter, i_res)
end if
end if
end associate
if (debug2_active (D_SUBTRACTION)) call check_soft_vector ()
contains
subroutine check_soft_vector ()
type(vector4_t) :: p_gluon
if (debug_on) call msg_debug2 (D_SUBTRACTION, "Compare soft vector: ")
print *, 'p_soft: ', rsub%sub_soft%p_soft%p
print *, 'Normalized gluon momentum: '
if (rsub%reg_data%has_pseudo_isr ()) then
p_gluon = rsub%real_kinematics%p_real_onshell(thr_leg(emitter))%get_momentum &
(i_phs, rsub%reg_data%n_legs_real)
else
p_gluon = rsub%real_kinematics%p_real_cms%get_momentum &
(i_phs, rsub%reg_data%n_legs_real)
end if
call vector4_write (p_gluon / p_gluon%p(0), show_mass = .true.)
end subroutine check_soft_vector
end function real_subtraction_compute_sub_soft
@ %def real_subtraction_compute_sub_soft
@
<<real subtraction: real subtraction: TBP>>=
procedure :: get_spin_correlation_term => real_subtraction_get_spin_correlation_term
<<real subtraction: procedures>>=
function real_subtraction_get_spin_correlation_term (rsub, alr, i_born, emitter) &
result (mom_times_sqme)
real(default) :: mom_times_sqme
class(real_subtraction_t), intent(in) :: rsub
integer, intent(in) :: alr, i_born, emitter
real(default), dimension(0:3) :: k_perp
integer :: mu, nu
if (rsub%sc_required(alr)) then
if (debug2_active(D_SUBTRACTION)) call check_me_consistency ()
associate (real_kin => rsub%real_kinematics)
if (emitter > rsub%reg_data%n_in) then
k_perp = real_subtraction_compute_k_perp_fsr ( &
real_kin%p_born_lab%get_momentum(1, emitter), &
rsub%real_kinematics%phi)
else
k_perp = real_subtraction_compute_k_perp_isr ( &
real_kin%p_born_lab%get_momentum(1, emitter), &
rsub%real_kinematics%phi)
end if
end associate
mom_times_sqme = zero
do mu = 0, 3
do nu = 0, 3
mom_times_sqme = mom_times_sqme + &
k_perp(mu) * k_perp(nu) * rsub%sqme_born_spin_c (mu, nu, emitter, i_born)
end do
end do
else
mom_times_sqme = zero
end if
contains
subroutine check_me_consistency ()
real(default) :: sqme_sum
if (debug_on) call msg_debug2 (D_SUBTRACTION, "Spin-correlation: Consistency check")
sqme_sum = rsub%sqme_born_spin_c(0,0,emitter,i_born) &
- rsub%sqme_born_spin_c(1,1,emitter,i_born) &
- rsub%sqme_born_spin_c(2,2,emitter,i_born) &
- rsub%sqme_born_spin_c(3,3,emitter,i_born)
if (.not. nearly_equal (sqme_sum, -rsub%sqme_born(i_born), 0.0001_default)) then
print *, 'Spin-correlated matrix elements are not consistent: '
print *, 'emitter: ', emitter
print *, 'g^{mu,nu} B_{mu,nu}: ', -sqme_sum
print *, 'all Born matrix elements: ', rsub%sqme_born
call msg_fatal ("FAIL")
else
call msg_print_color ("Success", COL_GREEN)
end if
end subroutine check_me_consistency
end function real_subtraction_get_spin_correlation_term
@ %def real_subtraction_get_spin_correlation_term
@ Construct a normalised momentum perpendicular to momentum [[p]] and rotate by
-an arbitrary angle [[phi]].
+an arbitrary angle [[phi]]. The angular conventions we use here are
+equivalent to those used by POWHEG.
<<real subtraction: public>>=
public :: real_subtraction_compute_k_perp_fsr, &
real_subtraction_compute_k_perp_isr
<<real subtraction: procedures>>=
function real_subtraction_compute_k_perp_fsr (p, phi) result (k_perp_fsr)
real(default), dimension(0:3) :: k_perp_fsr
type(vector4_t), intent(in) :: p
real(default), intent(in) :: phi
type(vector4_t) :: k
type(vector3_t) :: vec
type(lorentz_transformation_t) :: rot
vec = p%p(1:3) / p%p(0)
k%p(0) = zero
k%p(1) = p%p(1); k%p(2) = p%p(2)
k%p(3) = - (p%p(1)**2 + p%p(2)**2) / p%p(3)
rot = rotation (cos(phi), sin(phi), vec)
k = rot * k
k%p(1:3) = k%p(1:3) / space_part_norm (k)
k_perp_fsr = k%p
end function real_subtraction_compute_k_perp_fsr
function real_subtraction_compute_k_perp_isr (p, phi) result (k_perp_isr)
real(default), dimension(0:3) :: k_perp_isr
type(vector4_t), intent(in) :: p
real(default), intent(in) :: phi
k_perp_isr(0) = zero
- k_perp_isr(1) = cos(phi)
- k_perp_isr(2) = sin(phi)
+ k_perp_isr(1) = sin(phi)
+ k_perp_isr(2) = cos(phi)
k_perp_isr(3) = zero
end function real_subtraction_compute_k_perp_isr
@ %def real_subtraction_compute_k_perp_fsr, real_subtraction_compute_k_perp_isr
@
<<real subtraction: real subtraction: TBP>>=
procedure :: compute_sub_coll => real_subtraction_compute_sub_coll
<<real subtraction: procedures>>=
function real_subtraction_compute_sub_coll (rsub, alr, em, i_phs, alpha_coupling) &
result (sqme_coll)
real(default) :: sqme_coll
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr, em, i_phs
real(default), intent(in) :: alpha_coupling
real(default) :: xi, xi_max
real(default) :: mom_times_sqme_spin_c
- integer :: i_con, pdf_type
+ integer :: i_con
real(default) :: pfr
associate (sregion => rsub%reg_data%regions(alr))
sqme_coll = zero
if (sregion%has_collinear_divergence ()) then
xi = rsub%real_kinematics%xi_tilde * rsub%real_kinematics%xi_max(i_phs)
if (rsub%sub_coll%use_resonance_mappings) then
i_con = rsub%reg_data%alr_to_i_contributor (alr)
else
i_con = 1
end if
mom_times_sqme_spin_c = rsub%get_spin_correlation_term (alr, sregion%uborn_index, em)
if (em <= rsub%sub_coll%n_in) then
select case (rsub%isr_kinematics%isr_mode)
case (SQRTS_FIXED)
xi_max = rsub%real_kinematics%xi_max(i_phs)
case (SQRTS_VAR)
xi_max = one - rsub%isr_kinematics%x(em)
end select
xi = rsub%real_kinematics%xi_tilde * xi_max
- ! TODO sbrass introduce overall PDF/PDF_SINGLET parameter
- if (rsub%reg_data%regions(alr)%flst_real%flst(em) == GLUON) then
- pdf_type = 2
- else
- pdf_type = 1
- end if
if (sregion%nlo_correction_type == "QCD") then
call rsub%sub_coll%set_parameters (CA = CA, CF = CF, TR = TR)
else if (sregion%nlo_correction_type == "QED") then
call rsub%sub_coll%set_parameters (CA = zero, &
CF = sregion%flst_real%charge(em)**2, &
TR = sregion%flst_real%charge(size(sregion%flst_real%flst))**2)
end if
sqme_coll = rsub%sub_coll%compute_isr (em, sregion%flst_real%flst, &
rsub%real_kinematics%p_born_lab%phs_point(1)%p, &
- rsub%sqme_coll_isr(em, pdf_type, sregion%uborn_index), &
- mom_times_sqme_spin_c, &
+ rsub%sqme_born(sregion%uborn_index) * rsub%sf_factors(alr, em), &
+ mom_times_sqme_spin_c * rsub%sf_factors(alr, em), &
xi, alpha_coupling, rsub%isr_kinematics%isr_mode)
else
if (sregion%nlo_correction_type == "QCD") then
call rsub%sub_coll%set_parameters (CA = CA, CF = CF, TR = TR)
else if (sregion%nlo_correction_type == "QED") then
call rsub%sub_coll%set_parameters (CA = zero, &
CF = sregion%flst_real%charge(sregion%emitter)**2, &
TR = sregion%flst_real%charge(sregion%emitter)**2)
end if
sqme_coll = rsub%sub_coll%compute_fsr (sregion%emitter, sregion%flst_real%flst, &
rsub%real_kinematics%xi_ref_momenta (i_con), &
rsub%real_kinematics%p_born_lab%get_momenta(1), &
- rsub%sqme_born(sregion%uborn_index), mom_times_sqme_spin_c, &
+ rsub%sqme_born(sregion%uborn_index), &
+ mom_times_sqme_spin_c, &
xi, alpha_coupling, sregion%double_fsr)
if (rsub%sub_coll%use_resonance_mappings) then
select type (fks_mapping => rsub%reg_data%fks_mapping)
type is (fks_mapping_resonances_t)
pfr = fks_mapping%get_resonance_weight (alr, &
rsub%real_kinematics%p_born_cms%get_momenta(1))
end select
sqme_coll = sqme_coll * pfr
end if
end if
end if
end associate
end function real_subtraction_compute_sub_coll
@ %def real_subtraction_compute_sub_coll
-@
+@ Computes the soft-collinear subtraction term. For alpha regions with emitter
+$0$, this routine is called with [[em == 1]] and [[em == 2]] separately.
+To still be able to use the unrescaled pdf factors stored in [[sf_factors(alr, 0)]]
+in this case, we need to differentiate between [[em]] and [[em_pdf]].
<<real subtraction: real subtraction: TBP>>=
procedure :: compute_sub_coll_soft => real_subtraction_compute_sub_coll_soft
<<real subtraction: procedures>>=
function real_subtraction_compute_sub_coll_soft (rsub, alr, em, i_phs, alpha_coupling) &
result (sqme_cs)
real(default) :: sqme_cs
class(real_subtraction_t), intent(inout) :: rsub
integer, intent(in) :: alr, em, i_phs
real(default), intent(in) :: alpha_coupling
real(default) :: mom_times_sqme_spin_c
- integer :: i_con
+ integer :: i_con, em_pdf
associate (sregion => rsub%reg_data%regions(alr))
sqme_cs = zero
if (sregion%has_collinear_divergence ()) then
if (rsub%sub_coll%use_resonance_mappings) then
i_con = rsub%reg_data%alr_to_i_contributor (alr)
else
i_con = 1
end if
mom_times_sqme_spin_c = rsub%get_spin_correlation_term (alr, sregion%uborn_index, em)
if (em <= rsub%sub_coll%n_in) then
+ em_pdf = sregion%emitter
if (sregion%nlo_correction_type == "QCD") then
call rsub%sub_coll%set_parameters (CA = CA, CF = CF, TR = TR)
else if (sregion%nlo_correction_type == "QED") then
call rsub%sub_coll%set_parameters (CA = zero, &
CF = sregion%flst_real%charge(em)**2, &
TR = sregion%flst_real%charge(size(sregion%flst_real%flst))**2)
end if
sqme_cs = rsub%sub_coll%compute_isr (em, sregion%flst_real%flst, &
rsub%real_kinematics%p_born_lab%phs_point(1)%p, &
- rsub%sqme_born(sregion%uborn_index), mom_times_sqme_spin_c, &
+ rsub%sqme_born(sregion%uborn_index) * rsub%sf_factors(alr, em_pdf), &
+ mom_times_sqme_spin_c * rsub%sf_factors(alr, em_pdf), &
zero, alpha_coupling, rsub%isr_kinematics%isr_mode)
else
if (sregion%nlo_correction_type == "QCD") then
call rsub%sub_coll%set_parameters (CA = CA, CF = CF, TR = TR)
else if (sregion%nlo_correction_type == "QED") then
call rsub%sub_coll%set_parameters (CA = zero, &
CF = sregion%flst_real%charge(sregion%emitter)**2, &
TR = sregion%flst_real%charge(sregion%emitter)**2)
end if
sqme_cs = rsub%sub_coll%compute_fsr (sregion%emitter, sregion%flst_real%flst, &
rsub%real_kinematics%xi_ref_momenta(i_con), &
rsub%real_kinematics%p_born_lab%phs_point(1)%p, &
- rsub%sqme_born(sregion%uborn_index), mom_times_sqme_spin_c, &
+ rsub%sqme_born(sregion%uborn_index), &
+ mom_times_sqme_spin_c, &
zero, alpha_coupling, sregion%double_fsr)
end if
end if
end associate
end function real_subtraction_compute_sub_coll_soft
@ %def real_subtraction_compute_sub_coll_soft
<<real subtraction: real subtraction: TBP>>=
procedure :: requires_spin_correlations => &
real_subtraction_requires_spin_correlations
<<real subtraction: procedures>>=
function real_subtraction_requires_spin_correlations (rsub) result (val)
logical :: val
class(real_subtraction_t), intent(in) :: rsub
val = any (rsub%sc_required)
end function real_subtraction_requires_spin_correlations
@ %def real_subtraction_requires_spin_correlations
@
<<real subtraction: real subtraction: TBP>>=
procedure :: final => real_subtraction_final
<<real subtraction: procedures>>=
subroutine real_subtraction_final (rsub)
class(real_subtraction_t), intent(inout) :: rsub
call rsub%sub_soft%final ()
call rsub%sub_coll%final ()
!!! Finalization of region data is done in pcm_nlo_final
if (associated (rsub%reg_data)) nullify (rsub%reg_data)
!!! Finalization of real kinematics is done in pcm_instance_nlo_final
if (associated (rsub%real_kinematics)) nullify (rsub%real_kinematics)
if (associated (rsub%isr_kinematics)) nullify (rsub%isr_kinematics)
if (allocated (rsub%sqme_real_non_sub)) deallocate (rsub%sqme_real_non_sub)
if (allocated (rsub%sqme_born)) deallocate (rsub%sqme_born)
+ if (allocated (rsub%sf_factors)) deallocate (rsub%sf_factors)
if (allocated (rsub%sqme_born_color_c)) deallocate (rsub%sqme_born_color_c)
if (allocated (rsub%sqme_born_charge_c)) deallocate (rsub%sqme_born_charge_c)
if (allocated (rsub%sc_required)) deallocate (rsub%sc_required)
if (allocated (rsub%selected_alr)) deallocate (rsub%selected_alr)
end subroutine real_subtraction_final
@ %def real_subtraction_final
@ \subsubsection{Partitions of the real matrix element and Powheg damping}
<<real subtraction: public>>=
public :: real_partition_t
<<real subtraction: types>>=
type, abstract :: real_partition_t
contains
<<real subtraction: real partition: TBP>>
end type real_partition_t
@ %def real partition_t
@
<<real subtraction: real partition: TBP>>=
procedure (real_partition_init), deferred :: init
<<real subtraction: interfaces>>=
abstract interface
subroutine real_partition_init (partition, scale, reg_data)
import
class(real_partition_t), intent(out) :: partition
real(default), intent(in) :: scale
type(region_data_t), intent(in) :: reg_data
end subroutine real_partition_init
end interface
@ %def real_partition_init
@
<<real subtraction: real partition: TBP>>=
procedure (real_partition_write), deferred :: write
<<real subtraction: interfaces>>=
abstract interface
subroutine real_partition_write (partition, unit)
import
class(real_partition_t), intent(in) :: partition
integer, intent(in), optional :: unit
end subroutine real_partition_write
end interface
@ %def real_partition_write
@ To allow really arbitrary damping functions, [[get_f]] should get the
full real phase space as argument and not just some [[pt2]] that is
extracted higher up.
<<real subtraction: real partition: TBP>>=
procedure (real_partition_get_f), deferred :: get_f
<<real subtraction: interfaces>>=
abstract interface
function real_partition_get_f (partition, p) result (f)
import
real(default) :: f
class(real_partition_t), intent(in) :: partition
type(vector4_t), intent(in), dimension(:) :: p
end function real_partition_get_f
end interface
@ %def real_partition_get_f
@
<<real subtraction: public>>=
public :: powheg_damping_simple_t
<<real subtraction: types>>=
type, extends (real_partition_t) :: powheg_damping_simple_t
real(default) :: h2 = 5._default
integer :: emitter
contains
<<real subtraction: powheg damping simple: TBP>>
end type powheg_damping_simple_t
@ %def powheg_damping_simple_t
@
<<real subtraction: powheg damping simple: TBP>>=
procedure :: get_f => powheg_damping_simple_get_f
<<real subtraction: procedures>>=
function powheg_damping_simple_get_f (partition, p) result (f)
real(default) :: f
class(powheg_damping_simple_t), intent(in) :: partition
type(vector4_t), intent(in), dimension(:) :: p
!!! real(default) :: pt2
f = 1
call msg_bug ("Simple damping currently not available")
!!! TODO (cw-2017-03-01) Compute pt2 from emitter)
!!! f = partition%h2 / (pt2 + partition%h2)
end function powheg_damping_simple_get_f
@ %def powheg_damping_simple_get_f
@
<<real subtraction: powheg damping simple: TBP>>=
procedure :: init => powheg_damping_simple_init
<<real subtraction: procedures>>=
subroutine powheg_damping_simple_init (partition, scale, reg_data)
class(powheg_damping_simple_t), intent(out) :: partition
real(default), intent(in) :: scale
type(region_data_t), intent(in) :: reg_data
partition%h2 = scale**2
end subroutine powheg_damping_simple_init
@ %def powheg_damping_simple_init
@
<<real subtraction: powheg damping simple: TBP>>=
procedure :: write => powheg_damping_simple_write
<<real subtraction: procedures>>=
subroutine powheg_damping_simple_write (partition, unit)
class(powheg_damping_simple_t), intent(in) :: partition
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "Powheg damping simple: "
write (u, "(1x,A, "// FMT_15 // ")") "scale h2: ", partition%h2
end subroutine powheg_damping_simple_write
@ %def powheg_damping_simple_write
@
<<real subtraction: public>>=
public :: real_partition_fixed_order_t
<<real subtraction: types>>=
type, extends (real_partition_t) :: real_partition_fixed_order_t
real(default) :: scale
type(ftuple_t), dimension(:), allocatable :: fks_pairs
contains
<<real subtraction: real partition fixed order: TBP>>
end type real_partition_fixed_order_t
@ %def real_partition_fixed_order_t
@
<<real subtraction: real partition fixed order: TBP>>=
procedure :: init => real_partition_fixed_order_init
<<real subtraction: procedures>>=
subroutine real_partition_fixed_order_init (partition, scale, reg_data)
class(real_partition_fixed_order_t), intent(out) :: partition
real(default), intent(in) :: scale
type(region_data_t), intent(in) :: reg_data
end subroutine real_partition_fixed_order_init
@ %def real_partition_fixed_order_init
@
<<real subtraction: real partition fixed order: TBP>>=
procedure :: write => real_partition_fixed_order_write
<<real subtraction: procedures>>=
subroutine real_partition_fixed_order_write (partition, unit)
class(real_partition_fixed_order_t), intent(in) :: partition
integer, intent(in), optional :: unit
end subroutine real_partition_fixed_order_write
@ %def real_partition_fixed_order_write
@
<<real subtraction: real partition fixed order: TBP>>=
procedure :: get_f => real_partition_fixed_order_get_f
<<real subtraction: procedures>>=
function real_partition_fixed_order_get_f (partition, p) result (f)
real(default) :: f
class(real_partition_fixed_order_t), intent(in) :: partition
type(vector4_t), intent(in), dimension(:) :: p
integer :: i
f = zero
do i = 1, size (partition%fks_pairs)
associate (ii => partition%fks_pairs(i)%ireg)
if ((p(ii(1)) + p(ii(2)))**1 < p(ii(1))**1 + p(ii(2))**1 + partition%scale) then
f = one
exit
end if
end associate
end do
end function real_partition_fixed_order_get_f
@ %def real_partition_fixed_order_get_f
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[real_subtraction_ut.f90]]>>=
<<File header>>
module real_subtraction_ut
use unit_tests
use real_subtraction_uti
<<Standard module head>>
<<Real subtraction: public test>>
contains
<<Real subtraction: test driver>>
end module real_subtraction_ut
@ %def real_subtraction_ut
@
<<[[real_subtraction_uti.f90]]>>=
<<File header>>
module real_subtraction_uti
<<Use kinds>>
use physics_defs
use lorentz
use numeric_utils
use real_subtraction
<<Standard module head>>
<<Real subtraction: test declarations>>
contains
<<Real subtraction: tests>>
end module real_subtraction_uti
@ %def real_subtraction_ut
@ API: driver for the unit tests below.
<<Real subtraction: public test>>=
public :: real_subtraction_test
<<Real subtraction: test driver>>=
subroutine real_subtraction_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Real subtraction: execute tests>>
end subroutine real_subtraction_test
@ %def real_subtraction_test
@ Test the final-state collinear subtraction.
<<Real subtraction: execute tests>>=
call test (real_subtraction_1, "real_subtraction_1", &
"final-state collinear subtraction", &
u, results)
<<Real subtraction: test declarations>>=
public :: real_subtraction_1
<<Real subtraction: tests>>=
subroutine real_subtraction_1 (u)
integer, intent(in) :: u
type(coll_subtraction_t) :: coll_sub
real(default) :: sqme_coll
type(vector4_t) :: p_res
type(vector4_t), dimension(5) :: p_born
real(default), dimension(4) :: k_perp
real(default), dimension(4,4) :: b_munu
integer :: mu, nu
real(default) :: born, born_c
integer, dimension(6) :: flst
p_born(1)%p = [500, 0, 0, 500]
p_born(2)%p = [500, 0, 0, -500]
p_born(3)%p = [3.7755E+02, 2.2716E+02, -95.4172, 2.8608E+02]
p_born(4)%p = [4.9529E+02, -2.739E+02, 84.8535, -4.0385E+02]
p_born(5)%p = [1.2715E+02, 46.7375, 10.5637, 1.1778E+02]
p_res = p_born(1) + p_born(2)
flst = [11, -11 , -2, 2, -2, 2]
b_munu(1, :) = [0., 0., 0., 0.]
b_munu(2, :) = [0., 1., 1., 1.]
b_munu(3, :) = [0., 1., 1., 1.]
b_munu(4, :) = [0., 1., 1., 1.]
k_perp = real_subtraction_compute_k_perp_fsr (p = p_born(5), phi = 0.5_default)
born = - b_munu(1, 1) + b_munu(2, 2) + b_munu(3, 3) + b_munu(4, 4)
born_c = 0.
do mu = 1, 4
do nu = 1, 4
born_c = born_c + k_perp(mu) * k_perp(nu) * b_munu(mu, nu)
end do
end do
write (u, "(A)") "* Test output: real_subtraction_1"
write (u, "(A)") "* Purpose: final-state collinear subtraction"
write (u, "(A)")
write (u, "(A, L1)") "* vanishing scalar-product of 3-momenta k_perp and p_born(emitter): ", &
nearly_equal (dot_product (p_born(5)%p(1:3), k_perp(2:4)), 0._default)
call coll_sub%init (n_alr = 1, n_in = 2)
call coll_sub%set_parameters (CA, CF, TR)
write (u, "(A)")
write (u, "(A)") "* g -> qq splitting"
write (u, "(A)")
sqme_coll = coll_sub%compute_fsr(5, flst, p_res, p_born, &
born, born_c, 0.5_default, 0.25_default, .false.)
write (u, "(A,F15.12)") "ME: ", sqme_coll
write (u, "(A)")
write (u, "(A)") "* g -> gg splitting"
write (u, "(A)")
b_munu(1, :) = [0., 0., 0., 0.]
b_munu(2, :) = [0., 0., 0., 1.]
b_munu(3, :) = [0., 0., 1., 1.]
b_munu(4, :) = [0., 0., 1., 1.]
k_perp = real_subtraction_compute_k_perp_fsr (p = p_born(5), phi = 0.5_default)
born = - b_munu(1, 1) + b_munu(2, 2) + b_munu(3, 3) + b_munu(4, 4)
born_c = 0.
do mu = 1, 4
do nu = 1, 4
born_c = born_c + k_perp(mu) * k_perp(nu) * b_munu(mu, nu)
end do
end do
flst = [11, -11, 2, -2, 21, 21]
sqme_coll = coll_sub%compute_fsr(5, flst, p_res, p_born, &
born, born_c, 0.5_default, 0.25_default, .true.)
write (u, "(A,F15.12)") "ME: ", sqme_coll
write (u, "(A)")
write (u, "(A)") "* Test output end: real_subtraction_1"
write (u, "(A)")
end subroutine real_subtraction_1
@ %def real_subtraction_1
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Combining the FKS Pieces}
<<[[nlo_data.f90]]>>=
<<File header>>
module nlo_data
<<Use kinds>>
<<Use strings>>
use diagnostics
use constants, only: zero
use string_utils, only: split_string, read_ival, string_contains_word
use io_units
use lorentz
use variables, only: var_list_t
use format_defs, only: FMT_15
use physics_defs, only: THR_POS_WP, THR_POS_WM
use physics_defs, only: THR_POS_B, THR_POS_BBAR
use physics_defs, only: NO_FACTORIZATION, FACTORIZATION_THRESHOLD
<<Standard module head>>
<<nlo data: public>>
<<nlo data: parameters>>
<<nlo data: types>>
<<nlo data: interfaces>>
contains
<<nlo data: procedures>>
end module nlo_data
@ %def nlo_data
@
<<nlo data: parameters>>=
integer, parameter, public :: FKS_DEFAULT = 1
integer, parameter, public :: FKS_RESONANCES = 2
integer, dimension(2), parameter, public :: ASSOCIATED_LEG_PAIR = [1, 3]
@ %def parameters
@
<<nlo data: public>>=
public :: fks_template_t
<<nlo data: types>>=
type :: fks_template_t
logical :: subtraction_disabled = .false.
integer :: mapping_type = FKS_DEFAULT
logical :: count_kinematics = .false.
real(default) :: fks_dij_exp1
real(default) :: fks_dij_exp2
real(default) :: xi_min
real(default) :: y_max
real(default) :: xi_cut, delta_o, delta_i
type(string_t), dimension(:), allocatable :: excluded_resonances
integer :: n_f
contains
<<nlo data: fks template: TBP>>
end type fks_template_t
@ %def fks_template_t
@
<<nlo data: fks template: TBP>>=
procedure :: write => fks_template_write
<<nlo data: procedures>>=
subroutine fks_template_write (template, unit)
class(fks_template_t), intent(in) :: template
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u,'(1x,A)') 'FKS Template: '
write (u,'(1x,A)', advance = 'no') 'Mapping Type: '
select case (template%mapping_type)
case (FKS_DEFAULT)
write (u,'(A)') 'Default'
case (FKS_RESONANCES)
write (u,'(A)') 'Resonances'
case default
write (u,'(A)') 'Unkown'
end select
write (u,'(1x,A,ES4.3,ES4.3)') 'd_ij exponentials: ', &
template%fks_dij_exp1, template%fks_dij_exp2
write (u, '(1x,A,ES4.3,ES4.3)') 'xi_cut: ', &
template%xi_cut
write (u, '(1x,A,ES4.3,ES4.3)') 'delta_o: ', &
template%delta_o
write (u, '(1x,A,ES4.3,ES4.3)') 'delta_i: ', &
template%delta_i
end subroutine fks_template_write
@ %def fks_template_write
@ Set FKS parameters. $\xi_{\text{cut}}, \delta_o$ and $\delta_{\mathrm{I}}$ steer the ratio of the integrated and real subtraction.
<<nlo data: fks template: TBP>>=
procedure :: set_parameters => fks_template_set_parameters
<<nlo data: procedures>>=
subroutine fks_template_set_parameters (template, exp1, exp2, xi_min, &
y_max, xi_cut, delta_o, delta_i)
class(fks_template_t), intent(inout) :: template
real(default), intent(in) :: exp1, exp2
real(default), intent(in) :: xi_min, y_max, &
xi_cut, delta_o, delta_i
template%fks_dij_exp1 = exp1
template%fks_dij_exp2 = exp2
template%xi_min = xi_min
template%y_max = y_max
template%xi_cut = xi_cut
template%delta_o = delta_o
template%delta_i = delta_i
end subroutine fks_template_set_parameters
@ %def fks_template_set_parameters
<<nlo data: fks template: TBP>>=
procedure :: set_mapping_type => fks_template_set_mapping_type
<<nlo data: procedures>>=
subroutine fks_template_set_mapping_type (template, val)
class(fks_template_t), intent(inout) :: template
integer, intent(in) :: val
template%mapping_type = val
end subroutine fks_template_set_mapping_type
@ %def fks_template_set_mapping_type
@
<<nlo data: fks template: TBP>>=
procedure :: set_counter => fks_template_set_counter
<<nlo data: procedures>>=
subroutine fks_template_set_counter (template)
class(fks_template_t), intent(inout) :: template
template%count_kinematics = .true.
end subroutine fks_template_set_counter
@ %def fks_template_set_counter
@
<<nlo data: public>>=
public :: real_scales_t
<<nlo data: types>>=
type :: real_scales_t
real(default) :: scale
real(default) :: ren_scale
real(default) :: fac_scale
real(default) :: scale_born
real(default) :: fac_scale_born
real(default) :: ren_scale_born
end type real_scales_t
@ %def real_scales_t
@
<<nlo data: public>>=
public :: get_threshold_momenta
<<nlo data: procedures>>=
function get_threshold_momenta (p) result (p_thr)
type(vector4_t), dimension(4) :: p_thr
type(vector4_t), intent(in), dimension(:) :: p
p_thr(1) = p(THR_POS_WP) + p(THR_POS_B)
p_thr(2) = p(THR_POS_B)
p_thr(3) = p(THR_POS_WM) + p(THR_POS_BBAR)
p_thr(4) = p(THR_POS_BBAR)
end function get_threshold_momenta
@ %def get_threshold_momenta
@
\subsection{Putting it together}
<<nlo data: public>>=
public :: nlo_settings_t
<<nlo data: types>>=
type :: nlo_settings_t
logical :: use_internal_color_correlations = .true.
logical :: use_internal_spin_correlations = .false.
logical :: use_resonance_mappings = .false.
logical :: combined_integration = .false.
logical :: fixed_order_nlo = .false.
logical :: test_soft_limit = .false.
logical :: test_coll_limit = .false.
logical :: test_anti_coll_limit = .false.
integer, dimension(:), allocatable :: selected_alr
integer :: factorization_mode = NO_FACTORIZATION
!!! Probably not the right place for this. Revisit after refactoring
real(default) :: powheg_damping_scale = zero
type(fks_template_t) :: fks_template
type(string_t) :: virtual_selection
logical :: virtual_resonance_aware_collinear = .true.
logical :: use_born_scale = .true.
logical :: cut_all_sqmes = .true.
type(string_t) :: nlo_correction_type
contains
<<nlo data: nlo settings: TBP>>
end type nlo_settings_t
@ %def nlo_settings_t
@
<<nlo data: nlo settings: TBP>>=
procedure :: init => nlo_settings_init
<<nlo data: procedures>>=
subroutine nlo_settings_init (nlo_settings, var_list, fks_template)
class(nlo_settings_t), intent(inout) :: nlo_settings
type(var_list_t), intent(in) :: var_list
type(fks_template_t), intent(in), optional :: fks_template
type(string_t) :: color_method
if (present (fks_template)) nlo_settings%fks_template = fks_template
color_method = var_list%get_sval (var_str ('$correlation_me_method'))
if (color_method == "") color_method = var_list%get_sval (var_str ('$method'))
nlo_settings%use_internal_color_correlations = color_method == 'omega' &
.or. color_method == 'threshold'
nlo_settings%combined_integration = var_list%get_lval &
(var_str ("?combined_nlo_integration"))
nlo_settings%fixed_order_nlo = var_list%get_lval &
(var_str ("?fixed_order_nlo_events"))
nlo_settings%test_soft_limit = var_list%get_lval (var_str ('?test_soft_limit'))
nlo_settings%test_coll_limit = var_list%get_lval (var_str ('?test_coll_limit'))
nlo_settings%test_anti_coll_limit = var_list%get_lval (var_str ('?test_anti_coll_limit'))
call setup_alr_selection ()
nlo_settings%virtual_selection = var_list%get_sval (var_str ('$virtual_selection'))
nlo_settings%virtual_resonance_aware_collinear = &
var_list%get_lval (var_str ('?virtual_collinear_resonance_aware'))
nlo_settings%powheg_damping_scale = &
var_list%get_rval (var_str ('powheg_damping_scale'))
nlo_settings%use_born_scale = &
var_list%get_lval (var_str ("?nlo_use_born_scale"))
nlo_settings%cut_all_sqmes = &
var_list%get_lval (var_str ("?nlo_cut_all_sqmes"))
nlo_settings%nlo_correction_type = var_list%get_sval (var_str ('$nlo_correction_type'))
contains
subroutine setup_alr_selection ()
type(string_t) :: alr_selection
type(string_t), dimension(:), allocatable :: alr_split
integer :: i, i1, i2
alr_selection = var_list%get_sval (var_str ('$select_alpha_regions'))
if (string_contains_word (alr_selection, var_str (","))) then
call split_string (alr_selection, var_str (","), alr_split)
allocate (nlo_settings%selected_alr (size (alr_split)))
do i = 1, size (alr_split)
nlo_settings%selected_alr(i) = read_ival(alr_split(i))
end do
else if (string_contains_word (alr_selection, var_str (":"))) then
call split_string (alr_selection, var_str (":"), alr_split)
if (size (alr_split) == 2) then
i1 = read_ival (alr_split(1))
i2 = read_ival (alr_split(2))
allocate (nlo_settings%selected_alr (i2 - i1 + 1))
do i = 1, i2 - i1 + 1
nlo_settings%selected_alr(i) = read_ival (alr_split(i))
end do
else
call msg_fatal ("select_alpha_regions: ':' specifies a range!")
end if
else if (len(alr_selection) == 1) then
allocate (nlo_settings%selected_alr (1))
nlo_settings%selected_alr(1) = read_ival (alr_selection)
end if
if (allocated (alr_split)) deallocate (alr_split)
end subroutine setup_alr_selection
end subroutine nlo_settings_init
@ %def nlo_settings_init
@
<<nlo data: nlo settings: TBP>>=
procedure :: write => nlo_settings_write
<<nlo data: procedures>>=
subroutine nlo_settings_write (nlo_settings, unit)
class(nlo_settings_t), intent(in) :: nlo_settings
integer, intent(in), optional :: unit
integer :: i, u
u = given_output_unit (unit); if (u < 0) return
write (u, '(A)') 'nlo_settings:'
write (u, '(3X,A,L1)') 'internal_color_correlations = ', &
nlo_settings%use_internal_color_correlations
write (u, '(3X,A,L1)') 'internal_spin_correlations = ', &
nlo_settings%use_internal_spin_correlations
write (u, '(3X,A,L1)') 'use_resonance_mappings = ', &
nlo_settings%use_resonance_mappings
write (u, '(3X,A,L1)') 'combined_integration = ', &
nlo_settings%combined_integration
write (u, '(3X,A,L1)') 'test_soft_limit = ', &
nlo_settings%test_soft_limit
write (u, '(3X,A,L1)') 'test_coll_limit = ', &
nlo_settings%test_coll_limit
write (u, '(3X,A,L1)') 'test_anti_coll_limit = ', &
nlo_settings%test_anti_coll_limit
if (allocated (nlo_settings%selected_alr)) then
write (u, '(3x,A)', advance = "no") 'selected alpha regions = ['
do i = 1, size (nlo_settings%selected_alr)
write (u, '(A,I0)', advance = "no") ",", nlo_settings%selected_alr(i)
end do
write (u, '(A)') "]"
end if
write (u, '(3X,A,' // FMT_15 // ')') 'powheg_damping_scale = ', &
nlo_settings%powheg_damping_scale
write (u, '(3X,A,A)') 'virtual_selection = ', &
char (nlo_settings%virtual_selection)
write (u, '(3X,A,A)') 'Real factorization mode = ', &
char (factorization_mode (nlo_settings%factorization_mode))
contains
function factorization_mode (fm)
type(string_t) :: factorization_mode
integer, intent(in) :: fm
select case (fm)
case (NO_FACTORIZATION)
factorization_mode = var_str ("None")
case (FACTORIZATION_THRESHOLD)
factorization_mode = var_str ("Threshold")
case default
factorization_mode = var_str ("Undefined!")
end select
end function factorization_mode
end subroutine nlo_settings_write
@ %def nlo_settings_write
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Contribution of divergencies due to PDF Evolution}
References:
\begin{itemize}
\item arXiv:hep-ph/9512328, (2.1)-(2.5), (4.29)-(4.53)
\item arXiv:0709.2092, (2.102)-(2.106)
\end{itemize}
The parton distrubition densities have to be evaluated at NLO, too.
The NLO PDF evolution is given by
\begin{equation}
\label{eqn:pdf_nlo}
f (\bar{x}) = \int_0^1 \int_0^1 dx dz f(x) \Gamma(z) \delta (\bar{x} - x z),
\end{equation}
where $\Gamma$ are the DGLAP evolution kernels for an $a \to d$ splitting,
\begin{equation}
\label{eqn:dglap}
\Gamma_a^{(d)} = \delta_{ad}\delta(1-x) - \frac{\alpha_s}{2\pi} \left(\frac{1}{\epsilon} P_{ad}(x,0) - K_{ad}(x)\right) + \mathcal{O}(\alpha_s).
\end{equation}
$K_{ad}$ is a renormalization scheme matching factor, which is exactly zero in $\overline{\text{MS}}$.
Let the leading-order hadronic cross section be given by
\begin{equation}
\label{eqn:xsec_hadro_lo}
d\sigma^{(0)}(s) = \int dx_\oplus dx_\ominus f_\oplus (x_\oplus) f_\ominus (x_\ominus) d\tilde{\sigma}^{(0)} (x_\oplus x_\ominus s),
\end{equation}
then the NLO hadronic cross section is
\begin{equation}
\label{eqn:xsec_hadro_nlo}
d\sigma^{(1)}(s) = \int dx_\oplus dx_\ominus dz_\oplus dz_\ominus f_\oplus (x_\oplus) f_\ominus (x_\ominus)
\underbrace{\Gamma_\oplus (z_\oplus) \Gamma_\ominus (z_\ominus) d\tilde{\sigma}^{(1)} (z_\oplus z_\ominus s)}_{d\hat{\sigma}^{(1)}}.
\end{equation}
$d\hat{\sigma}$ is called the subtracted partonic cross section. Expanding in $\alpha_s$ we find
\begin{align}
d\hat{\sigma}^{(0)}_{ab}(k_1, k_2) &= d\tilde{\sigma}_{ab}^{(0)} (k_1, k_2), \\
d\hat{\sigma}^{(1)}_{ab}(k_1, k_2) &= d\tilde{\sigma}_{ab}^{(1)} (k_1, k_2) \\
&+ \frac{\alpha_s}{2\pi} \sum_d \int dx \left (\frac{1}{\epsilon} P_{da}(x,0) - K_{da}(x)\right) d\tilde{\sigma}_{db}^{(0)}(xk_1, k_2)\\
&+ \frac{\alpha_s}{2\pi} \sum_d \int \left (\frac{1}{\epsilon} P_{db} (x, 0) - K_{db}(x)\right) d\tilde{\sigma}_{ad}^{(0)}(k_1, xk_2).\\
&= d\tilde{\sigma}_{ab}^{(1)} + d\tilde{\sigma}_{ab}^{(cnt,+)} + d\tilde{\sigma}_{ab}^{(cnt,-)}
\end{align}
Let us now turn the soft-subtracted real part of the cross section. For ease of notation, it is constrained to one singular region,
\begin{align*}
\label{eqn:R-in}
d\sigma^{(in)}_\alpha &= \left[\left(\frac{1}{\xi}\right)_{c} - 2\epsilon\left(\frac{\log \xi}{\xi}\right)_{c}\right] (1-y^2)\xi^2 \mathcal{R}_\alpha \mathcal{S}_\alpha \\
&\times \frac{1}{2(2\pi)^{3-2\epsilon}} \left(\frac{\sqrt{s}}{2}\right)^{2-2\epsilon} \left( 1 - y^2\right)^{-1-\epsilon} d\phi d\xi dy d\Omega^{2-2\epsilon},
\end{align*}
where we regularize collinear divergencies using the identity
\begin{equation*}
\left (1 - y^2 \right)^{-1-\epsilon} = -\frac{2^{-\epsilon}}{\epsilon} \left (\delta(1-y) + \delta(1+y)\right)
+ \underbrace{\frac{1}{2} \left[ \left (\frac {1}{1-y}\right)_{c} + \left (\frac{1}{1+y}\right)_{c} \right]}_{\mathcal{P}(y)}.
\end{equation*}
This enables us to split the cross section into a finite and a singular part. The latter can further be separated into a contribution of the incoming and of the outgoing particles,
\begin{equation*}
d\sigma^{(in)}_\alpha = d\sigma^{(in,+)}_\alpha + d\sigma^{(in,-)}_\alpha + d\sigma^{(in,f)}_\alpha.
\end{equation*}
They are given by
\begin{align}
\label{eqn:sigma-f}
d\sigma^{(in,f)}_\alpha = & \mathcal{P}(y) \left[\left(\frac{1}{\xi}\right)_{c} - 2\epsilon \left(\frac{\log\xi}{\xi}\right)_{c}\right] \frac{1}{2(2\pi)^{3-2\epsilon}}
\left(\frac{\sqrt{s}}{2}\right)^{2-2\epsilon} \\
& \times (1-y^2)\xi^2 \mathcal{R}_\alpha \mathcal{S}_\alpha d\phi d\xi dy d\Omega^{2-2\epsilon}
\end{align}
and
\begin{align}
\label{eqn:sigma-pm}
d\sigma^{(in,\pm)}_\alpha &= -\frac{2^{-\epsilon}}{\epsilon} \delta (1 \mp y) \left[ \left( \frac{1}{\xi}\right)_{c} - 2\epsilon \left(\frac{\log\xi}{\xi}\right)_{c}\right] \\
& \times \frac{1}{2(2\pi)^{3-2\epsilon}} \left( \frac{\sqrt{s}}{2}\right)^{2-2\epsilon} (1-y^2)\xi^2 \mathcal{R}_\alpha \mathcal{S}_\alpha d\phi d\xi dy d\Omega^{2-2\epsilon}.
\end{align}
Equation \ref{eqn:sigma-f} is the contribution to the real cross section which is computed in [[evaluate_region_isr]]. It is regularized both in the soft and collinear limit via the plus distributions.
Equation \ref{eqn:sigma-pm} is a different contribution. It is only present exactly in the collinear limit, due to the delta function. The divergences present in this term do not completely cancel out
divergences in the virtual matrix element, because the beam axis is distinguished. Thus, the conditions in which the KLM theorem applies are not met. To see this, we carry out the collinear limit, obtaining
\begin{equation*}
\lim_{y \to 1} (1-y^2)\xi^2\mathcal{R}_\alpha = 8\pi\alpha_s \mu^{2\epsilon} \left(\frac{2}{\sqrt{s}}\right)^2 \xi P^<(1-\xi, \epsilon) \mathcal{R}_\alpha,
\end{equation*}
with the Altarelli-Parisi splitting kernel for $z < 1$, $P^<(z,\epsilon)$. Moreover, $\lim_{\vec{k} \parallel \vec{k}_1} d\phi = d\phi_3$ violates spatial averaging. The integration over the spherical angle $d\Omega$ can be carried out easily, yielding a factor of $2\pi^{1-\epsilon} / \Gamma(1-\epsilon)$.
This allows us to redefine $\epsilon$,
\begin{equation}
\frac{1}{\epsilon} - \gamma_E + \log(4\pi) \to \frac{1}{\epsilon}.
\end{equation}
In order to make a connection to $d\tilde{\sigma}^{(cnt,\pm)}$, we relate $P_{ab}(z,0)$ to $P^<_{ab}(z,0)$ via the equation
\begin{equation*}
P_{ab}(z,0) = (1-z)P_{ab}^<(z,0)\left(\frac{1}{1-z}\right)_+ + \gamma(a)\delta_{ab}\delta(1-z),
\end{equation*}
which yields
\begin{equation} \label{eqn:sigma-cnt}
d\tilde{\sigma}^{(cnt,+)} = \frac{\alpha_s}{2\pi} \sum_d \left\lbrace -K_{da}(1-\xi) + \frac{1}{\epsilon} \left[\left(\frac{1}{\xi}\right)_+ \xi P_{da}^<(1-\xi,0)
+ \delta_{da}\delta(\xi)\gamma(d)\right]\right\rbrace \mathcal{R}_\alpha \mathcal{S}_\alpha.
\end{equation}
This term has the same pole structure as eqn. \ref{eqn:sigma-pm}. This makes clear that the quantity
\begin{equation}
d\hat{\sigma}^{(in,+)} = d\tilde{\sigma}^{(in,+)} + \frac{1}{4} d\tilde{\sigma}^{(cnt,+)}
\end{equation}
has no collinear poles. Therefore, our task is to add up eqns. \ref{eqn:sigma-pm} and \ref{eqn:sigma-cnt} in order to compute the finite remainder. This is the integrand which is evaluated in the [[dglap_remnant]] component.\\
So, we have to perform an expansion of $d\hat{\sigma}^{(in,+)}$ in $\epsilon$. Hereby, we must not neglect the implicit $\epsilon$-dependence of $P^<$, which leads to additional terms involving the
first derivative,
\begin{equation*}
P_{ab}^<(z,\epsilon) = P_{ab}^<(z,0) + \epsilon \frac{\partial P_{ab}^<(z,\epsilon)}{\partial \epsilon}|_{\epsilon = 0} + \mathcal{O}(\alpha_s^2).
\end{equation*}
This finally gives us the equation for the collinear remnant. Note that there is still one soft $1/\epsilon$-pole, which cancels out with the corresponding expression in the soft-virtual terms.
-\begin{align}
+\begin{align} \label{eqn:sigma-in-p-final}
d\hat{\sigma}^{(in,+)} &= \frac{\alpha_s}{2\pi} \frac{1}{\epsilon} \gamma(a) \mathcal{R}_\alpha \mathcal{S}_\alpha \nonumber\\
&+ \frac{\alpha_s}{2\pi} \sum_d \left\lbrace (1-z) P_{da}^<(z,0)\left[\left(\frac{1}{1-z}\right)_{c} \log\frac{s\delta_{\mathrm{I}}}{2\mu^2} + 2 \left(\frac{\log(1-z)}{1-z}\right)_{c}\right] \right. \nonumber\\
&\left . -(1-z)\frac{\partial P_{da}^<(z,\epsilon)}{\partial \epsilon} \left(\frac{1}{1-z}\right)_{c} - K_{da}(z)\right\rbrace \mathcal{R}_\alpha \mathcal{S}_\alpha
\end{align}
<<[[dglap_remnant.f90]]>>=
<<File header>>
module dglap_remnant
<<Use kinds with double>>
<<Use strings>>
use numeric_utils
use diagnostics
use constants
use physics_defs
use pdg_arrays
use phs_fks, only: isr_kinematics_t
+ use fks_regions, only: region_data_t
use nlo_data
<<Standard module head>>
<<dglap remnant: public>>
<<dglap remnant: types>>
contains
<<dglap remnant: procedures>>
end module dglap_remnant
@ %def module dglap_remnant
@
<<dglap remnant: public>>=
public :: dglap_remnant_t
<<dglap remnant: types>>=
type :: dglap_remnant_t
type(nlo_settings_t), pointer :: settings => null ()
+ type(region_data_t), pointer :: reg_data => null ()
type(isr_kinematics_t), pointer :: isr_kinematics => null ()
- integer, dimension(:), allocatable :: light_quark_flv
- integer, dimension(:,:), allocatable :: flv_in
real(default), dimension(:), allocatable :: sqme_born
- real(default), dimension(:,:,:), allocatable :: sqme_coll_isr
- integer :: n_flv
+ real(default), dimension(:,:), allocatable :: sf_factors
contains
<<dglap remnant: dglap remnant: TBP>>
end type dglap_remnant_t
@ %def dglap_remnant_t
@
<<dglap remnant: dglap remnant: TBP>>=
procedure :: init => dglap_remnant_init
<<dglap remnant: procedures>>=
- subroutine dglap_remnant_init (dglap, settings, n_flv_born, isr_kinematics, flv, n_alr)
+ subroutine dglap_remnant_init (dglap, settings, reg_data, isr_kinematics)
class(dglap_remnant_t), intent(inout) :: dglap
type(nlo_settings_t), intent(in), target :: settings
- integer, intent(in) :: n_flv_born
+ type(region_data_t), intent(in), target :: reg_data
+ integer :: n_flv_born
type(isr_kinematics_t), intent(in), target :: isr_kinematics
- integer, dimension(:,:), intent(in) :: flv
- integer, intent(in) :: n_alr
- integer :: i, j, n_quarks
- logical, dimension(-6:6) :: quark_checked
+ dglap%reg_data => reg_data
+ n_flv_born = reg_data%get_n_flv_born ()
+ allocate (dglap%sf_factors (reg_data%n_regions, 0:reg_data%n_in))
+ dglap%sf_factors = zero
dglap%settings => settings
- quark_checked = .false.
allocate (dglap%sqme_born(n_flv_born))
dglap%sqme_born = zero
- allocate (dglap%sqme_coll_isr(2, 2, n_flv_born))
- dglap%sqme_coll_isr = zero
dglap%isr_kinematics => isr_kinematics
- dglap%n_flv = size (flv, dim=2)
- allocate (dglap%flv_in (2, dglap%n_flv))
- dglap%flv_in = flv
- n_quarks = 0
- do i = 1, size (flv, dim = 1)
- if (is_quark(flv(i,1))) then
- n_quarks = n_quarks + 1
- quark_checked(flv(i, 1)) = .true.
- end if
- end do
- allocate (dglap%light_quark_flv (n_quarks))
- j = 1
- do i = -6, 6
- if (quark_checked(i)) then
- dglap%light_quark_flv(j) = i
- j = j + 1
- end if
- end do
end subroutine dglap_remnant_init
@ %def dglap_remnant_init
-@
-<<dglap remnant: dglap remnant: TBP>>=
- procedure :: get_pdf_singlet => dglap_remnant_get_pdf_singlet
-<<dglap remnant: procedures>>=
- function dglap_remnant_get_pdf_singlet (dglap, emitter) result (sum_sqme)
- real(default) :: sum_sqme
- class(dglap_remnant_t), intent(in) :: dglap
- integer, intent(in) :: emitter
- integer :: i_flv
- integer, parameter :: PDF_SINGLET = 2
- sum_sqme = zero
- do i_flv = 1, size (dglap%sqme_coll_isr, dim=3)
- if (any (dglap%flv_in(emitter, i_flv) == dglap%light_quark_flv)) &
- sum_sqme = sum_sqme + dglap%sqme_coll_isr (emitter, PDF_SINGLET, i_flv)
- end do
- end function dglap_remnant_get_pdf_singlet
-
-@ %def dglap_remnant_get_summed_quark_sqmes
-@ Evaluates formula (...). Note that, as also is the case for the real subtraction,
-we have to take into account an additional term, occuring because the integral the
-plus distribution is evaluated over is not constrained on the interval $[0,1]$.
+@ Evaluates formula \ref{eqn:sigma-in-p-final}. Note that, as also in the case for the
+real subtraction, we have to take into account an additional term, occuring because the
+integral the plus distribution is evaluated over is not constrained on the interval $[0,1]$.
Explicitly, this means (see JHEP 06(2010)043, (4.11)-(4.12))
\begin{align}
\int_{\bar{x}_\oplus}^1 dz \left( \frac{1}{1-z} \right)_{\xi_{\text{cut}}} & = \log \frac{1-\bar{x}_\oplus}{\xi_{\text{cut}}} f(1) + \int_{\bar{x}_\oplus}^1 \frac{f(z) - f(1)}{1-z}, \\
\int_{\bar{x}_\oplus}^1 dz \left(\frac{\log(1-z)}{1-z}\right)_{\xi_{\text{cut}}} f(z) & = \frac{1}{2}\left( \log^2(1-\bar{x}_\oplus) - \log^2 (\xi_{\text{cut}}) \right)f(1) + \int_{\bar{x}_\oplus}^1 \frac{\log(1-z)[f(z) - f(1)]}{1-z},
\end{align}
and the same of course for $\bar{x}_\ominus$. These two terms are stored in the [[plus_dist_remnant]] variable below.
<<dglap remnant: dglap remnant: TBP>>=
procedure :: evaluate => dglap_remnant_evaluate
<<dglap remnant: procedures>>=
subroutine dglap_remnant_evaluate (dglap, alpha_s, separate_alrs, sqme_dglap)
class(dglap_remnant_t), intent(inout) :: dglap
real(default), intent(in) :: alpha_s
logical, intent(in) :: separate_alrs
real(default), intent(inout), dimension(:) :: sqme_dglap
- real(default) :: factor, factor_soft, plus_dist_remnant
- integer :: i_flv, ii_flv, emitter
- real(default), dimension(2) :: tmp
- real(default) :: sb, xb, onemz
- real(default) :: fac_scale2, jac
- real(default) :: sqme_scaled
- integer, parameter :: PDF = 1, PDF_SINGLET = 2
+ integer :: alr, emitter
+ real(default) :: sqme_alr
+ logical, dimension(:,:,:), allocatable :: evaluated
+ real(default) :: sb, fac_scale2
sb = dglap%isr_kinematics%sqrts_born**2
fac_scale2 = dglap%isr_kinematics%fac_scale**2
- do i_flv = 1, dglap%n_flv
+ allocate (evaluated(dglap%reg_data%get_n_flv_born (), dglap%reg_data%get_n_flv_real (), &
+ dglap%reg_data%n_in))
+ evaluated = .false.
+ do alr = 1, dglap%reg_data%n_regions
+ sqme_alr = zero
+ emitter = dglap%reg_data%regions(alr)%emitter
+ if (emitter > dglap%reg_data%n_in) cycle
+ associate (i_flv_born => dglap%reg_data%regions(alr)%uborn_index, &
+ i_flv_real => dglap%reg_data%regions(alr)%real_index)
+ if (emitter == 0) then
+ do emitter = 1, 2
+ if (evaluated(i_flv_born, i_flv_real, emitter)) cycle
+ call evaluate_alr (alr, emitter, i_flv_born, i_flv_real, sqme_alr, evaluated)
+ end do
+ else if (emitter > 0) then
+ if (evaluated(i_flv_born, i_flv_real, emitter)) cycle
+ call evaluate_alr (alr, emitter, i_flv_born, i_flv_real, sqme_alr, evaluated)
+ end if
+ end associate
if (separate_alrs) then
- ii_flv = i_flv
+ sqme_dglap(alr) = sqme_dglap(alr) + alpha_s / twopi * sqme_alr
else
- ii_flv = 1
+ sqme_dglap(1) = sqme_dglap(1) + alpha_s / twopi * sqme_alr
end if
- tmp = zero
- do emitter = 1, 2
- associate (z => dglap%isr_kinematics%z(emitter), template => dglap%settings%fks_template)
- jac = dglap%isr_kinematics%jacobian(emitter)
- onemz = one - z
- factor = log (sb * template%delta_i / two / z / fac_scale2) / &
- onemz + two * log (onemz) / onemz
- factor_soft = log (sb * template%delta_i / two / fac_scale2) / &
- onemz + two * log (onemz) / onemz
- xb = dglap%isr_kinematics%x(emitter)
- plus_dist_remnant = log ((one - xb) / template%xi_cut) * log (sb * template%delta_i / &
- two / fac_scale2) + (log (one - xb)**2 - log (template%xi_cut)**2)
- if (is_gluon(dglap%flv_in(emitter, i_flv))) then
- sqme_scaled = dglap%sqme_coll_isr(emitter, PDF, i_flv)
- tmp(emitter) = p_hat_gg(z) * factor / z * sqme_scaled * jac &
- - p_hat_gg(one) * factor_soft * dglap%sqme_born(i_flv) * jac &
- + p_hat_gg(one) * plus_dist_remnant * dglap%sqme_born(i_flv)
- tmp(emitter) = tmp(emitter) + &
- (p_hat_qg(z) * factor - p_derived_qg(z)) / z * jac * &
- dglap%get_pdf_singlet (emitter)
- else if (is_quark(dglap%flv_in(emitter, i_flv))) then
- sqme_scaled = dglap%sqme_coll_isr(emitter, PDF, i_flv)
- tmp(emitter) = p_hat_qq(z) * factor / z * sqme_scaled * jac &
- - p_derived_qq(z) / z * sqme_scaled * jac &
- - p_hat_qq(one) * factor_soft * dglap%sqme_born(i_flv) * jac &
- + p_hat_qq(one) * plus_dist_remnant * dglap%sqme_born(i_flv)
- sqme_scaled = dglap%sqme_coll_isr(emitter, PDF_SINGLET, i_flv)
- tmp(emitter) = tmp(emitter) + &
- (p_hat_gq(z) * factor - p_derived_gq(z)) / z * sqme_scaled * jac
- end if
- end associate
- end do
- sqme_dglap(ii_flv) = sqme_dglap(ii_flv) + alpha_s / twopi * (tmp(1) + tmp(2))
end do
+
contains
<<dglap remnant: dglap remnant evaluate: procedures>>
end subroutine dglap_remnant_evaluate
@ %def dglap_remnant_evaluate
@ We introduce $\hat{P}(z, \epsilon) = (1 - z) P(z, \epsilon)$ and have
\begin{align}
\hat{P}^{gg}(z) & = 2C_A \left[z + \frac{(1-z)^2}{z} + z(1-z)^2\right], \\
\hat{P}^{qg}(z) & = C_F (1-z) \frac{1 + (1-z)^2}{z}, \\
\hat{P}^{gq}(z) & = T_F (1 - z - 2z(1-z)^2), \\
\hat{P}^{qq}(z) & = C_F (1 + z^2).
\end{align}
<<dglap remnant: dglap remnant evaluate: procedures>>=
function p_hat_gg (z)
real(default) :: p_hat_gg
<<p variables>>
p_hat_gg = two * CA * (z + onemz**2 / z + z * onemz**2)
end function p_hat_gg
function p_hat_qg (z)
real(default) :: p_hat_qg
<<p variables>>
p_hat_qg = CF * onemz / z * (one + onemz**2)
end function p_hat_qg
function p_hat_gq (z)
real(default) :: p_hat_gq
<<p variables>>
p_hat_gq = TR * (onemz - two * z * onemz**2)
end function p_hat_gq
function p_hat_qq (z)
real(default) :: p_hat_qq
real(default), intent(in) :: z
p_hat_qq = CF * (one + z**2)
end function p_hat_qq
@ %def p_hat_qq, p_hat_gq, p_hat_qg, p_hat_gg
@
\begin{align}
\frac{\partial P^{gg}(z,\epsilon)}{\partial \epsilon}|_{\epsilon = 0} & = 0, \\
\frac{\partial P^{qg}(z,\epsilon)}{\partial \epsilon}|_{\epsilon = 0} & = -C_F z, \\
\frac{\partial P^{gq}(z,\epsilon)}{\partial \epsilon}|_{\epsilon = 0} & = -
2 T_F z (1-z), \\
\frac{\partial P^{gq}(z,\epsilon)}{\partial \epsilon}|_{\epsilon = 0} & = -C_F (1-z).\\
\end{align}
<<dglap remnant: dglap remnant evaluate: procedures>>=
function p_derived_gg (z)
real(default) :: p_derived_gg
real(default), intent(in) :: z
p_derived_gg = zero
end function p_derived_gg
function p_derived_qg (z)
real(default) :: p_derived_qg
real(default), intent(in) :: z
p_derived_qg = -CF * z
end function p_derived_qg
function p_derived_gq (z)
real(default) :: p_derived_gq
<<p variables>>
p_derived_gq = -two * TR * z * onemz
end function p_derived_gq
function p_derived_qq (z)
real(default) :: p_derived_qq
<<p variables>>
p_derived_qq = -CF * onemz
end function p_derived_qq
@ %def p_derived_gg, p_derived_qg, p_derived_gq, p_derived_qq
@
+<<dglap remnant: dglap remnant evaluate: procedures>>=
+subroutine evaluate_alr (alr, emitter, i_flv_born, i_flv_real, sqme_alr, evaluated)
+ integer, intent(in) :: alr, emitter, i_flv_born, i_flv_real
+ real(default), intent(inout) :: sqme_alr
+ logical, intent(inout), dimension(:,:,:) :: evaluated
+ real(default) :: z, jac
+ real(default) :: factor, factor_soft, plus_dist_remnant
+ real(default) :: xb, onemz
+ real(default) :: sqme_scaled
+ integer :: flv_em, flv_rad
+ associate (template => dglap%settings%fks_template)
+ z = dglap%isr_kinematics%z(emitter)
+ flv_rad = dglap%reg_data%regions(alr)%flst_real%flst(dglap%reg_data%n_legs_real)
+ flv_em = dglap%reg_data%regions(alr)%flst_real%flst(emitter)
+ jac = dglap%isr_kinematics%jacobian(emitter)
+ onemz = one - z
+ factor = log (sb * template%delta_i / two / z / fac_scale2) / &
+ onemz + two * log (onemz) / onemz
+ factor_soft = log (sb * template%delta_i / two / fac_scale2) / &
+ onemz + two * log (onemz) / onemz
+ xb = dglap%isr_kinematics%x(emitter)
+ plus_dist_remnant = log ((one - xb) / template%xi_cut) * log (sb * template%delta_i / &
+ two / fac_scale2) + (log (one - xb)**2 - log (template%xi_cut)**2)
+ end associate
+ if (is_massless_vector (flv_em) .and. is_massless_vector (flv_rad)) then
+ sqme_scaled = dglap%sqme_born(i_flv_born) * dglap%sf_factors(alr, emitter)
+ sqme_alr = sqme_alr + p_hat_gg(z) * factor / z * sqme_scaled * jac &
+ - p_hat_gg(one) * factor_soft * dglap%sqme_born(i_flv_born) * jac &
+ + p_hat_gg(one) * plus_dist_remnant * dglap%sqme_born(i_flv_born)
+ else if (is_fermion (flv_em) .and. is_massless_vector (flv_rad)) then
+ sqme_scaled = dglap%sqme_born(i_flv_born) * dglap%sf_factors(alr, emitter)
+ sqme_alr = sqme_alr + p_hat_qq(z) * factor / z * sqme_scaled * jac &
+ - p_derived_qq(z) / z * sqme_scaled * jac &
+ - p_hat_qq(one) * factor_soft * dglap%sqme_born(i_flv_born) * jac &
+ + p_hat_qq(one) * plus_dist_remnant * dglap%sqme_born(i_flv_born)
+ else if (is_fermion (flv_em) .and. is_fermion (flv_rad)) then
+ sqme_alr = sqme_alr + (p_hat_qg(z) * factor - p_derived_qg(z)) / z * jac * &
+ dglap%sqme_born(i_flv_born) * dglap%sf_factors(alr, emitter)
+ else if (is_massless_vector (flv_em) .and. is_fermion (flv_rad)) then
+ sqme_scaled = dglap%sqme_born(i_flv_born) * dglap%sf_factors(alr, emitter)
+ sqme_alr = sqme_alr + (p_hat_gq(z) * factor - p_derived_gq(z)) / z * sqme_scaled * jac
+ else
+ sqme_alr = sqme_alr + zero
+ end if
+ evaluated(i_flv_born, i_flv_real, emitter) = .true.
+end subroutine evaluate_alr
+@ %dglap_remnant_evaluate_alr
+@
<<p variables>>=
real(default), intent(in) :: z
real(default) :: onemz
onemz = one - z
@ %def variables
@
<<dglap remnant: dglap remnant: TBP>>=
procedure :: final => dglap_remnant_final
<<dglap remnant: procedures>>=
subroutine dglap_remnant_final (dglap)
class(dglap_remnant_t), intent(inout) :: dglap
if (associated (dglap%isr_kinematics)) nullify (dglap%isr_kinematics)
- if (allocated (dglap%light_quark_flv)) deallocate (dglap%light_quark_flv)
+ if (associated (dglap%reg_data)) nullify (dglap%reg_data)
+ if (associated (dglap%settings)) nullify (dglap%settings)
if (allocated (dglap%sqme_born)) deallocate (dglap%sqme_born)
- if (allocated (dglap%sqme_coll_isr)) deallocate (dglap%sqme_coll_isr)
+ if (allocated (dglap%sf_factors)) deallocate (dglap%sf_factors)
end subroutine dglap_remnant_final
@ %def dglap_remnant_final
@
-\subsection{Rescaling function}
-
-NLO applications require that the beam energy fractions
-can be recomputed flexibly for different components of
-the calculation, e.g. in the collinear subtraction. To
-deal with this, we use a rescaling function which is given
-to [[sf_int_apply]] as an optional argument to use a different
-set of [[x]] values.
-
-<<[[isr_collinear.f90]]>>=
-<<File header>>
-
-module isr_collinear
-
-<<Use kinds with double>>
-<<Use strings>>
- use diagnostics
- use constants, only: one, two
- use physics_defs, only: n_beam_structure_int
- use sf_base, only: sf_rescale_t
-
-<<Standard module head>>
-
-<<isr collinear: public>>
-
-<<isr collinear: types>>
-
-contains
-
-<<isr collinear: procedures>>
-
-end module isr_collinear
-
-@ %def module isr_collinear
-
-<<isr collinear: public>>=
- public :: sf_rescale_collinear_t
-<<isr collinear: types>>=
- type, extends (sf_rescale_t) :: sf_rescale_collinear_t
- real(default) :: xi_tilde
- contains
- <<isr collinear: rescale collinear: TBP>>
- end type sf_rescale_collinear_t
-
-@ %def sf_rescale_collinear_t
-@
-<<isr collinear: rescale collinear: TBP>>=
- procedure :: apply => sf_rescale_collinear_apply
-<<isr collinear: procedures>>=
- subroutine sf_rescale_collinear_apply (func, x)
- class(sf_rescale_collinear_t), intent(in) :: func
- real(default), intent(inout) :: x
- real(default) :: xi
- if (debug2_active (D_BEAMS)) then
- print *, 'Rescaling function - Collinear: '
- print *, 'Input: ', x
- print *, 'xi_tilde: ', func%xi_tilde
- end if
- xi = func%xi_tilde * (one - x)
- x = x / (one - xi)
- if (debug2_active (D_BEAMS)) print *, 'scaled x: ', x
- end subroutine sf_rescale_collinear_apply
-
-@ %def sf_rescale_collinear_apply
-@
-<<isr collinear: rescale collinear: TBP>>=
- procedure :: set => sf_rescale_collinear_set
-<<isr collinear: procedures>>=
- subroutine sf_rescale_collinear_set (func, xi_tilde)
- class(sf_rescale_collinear_t), intent(inout) :: func
- real(default), intent(in) :: xi_tilde
- func%xi_tilde = xi_tilde
- end subroutine sf_rescale_collinear_set
-
-@ %def sf_rescale_collinear_set
-@
-<<isr collinear: public>>=
- public :: sf_rescale_real_t
-<<isr collinear: types>>=
- type, extends (sf_rescale_t) :: sf_rescale_real_t
- real(default) :: xi, y
- contains
- <<isr collinear: rescale real: TBP>>
- end type sf_rescale_real_t
-
-@ %def sf_rescale_real_t
-@
-<<isr collinear: rescale real: TBP>>=
- procedure :: apply => sf_rescale_real_apply
-<<isr collinear: procedures>>=
- subroutine sf_rescale_real_apply (func, x)
- class(sf_rescale_real_t), intent(in) :: func
- real(default), intent(inout) :: x
- real(default) :: onepy, onemy
- if (debug2_active (D_BEAMS)) then
- print *, 'Rescaling function - Real: '
- print *, 'Input: ', x
- print *, 'Beam index: ', func%i_beam
- print *, 'xi: ', func%xi, 'y: ', func%y
- end if
- x = x / sqrt (one - func%xi)
- onepy = one + func%y; onemy = one - func%y
- if (func%i_beam == 1) then
- x = x * sqrt ((two - func%xi * onemy) / (two - func%xi * onepy))
- else if (func%i_beam == 2) then
- x = x * sqrt ((two - func%xi * onepy) / (two - func%xi * onemy))
- else
- call msg_fatal ("sf_rescale_real_apply - invalid beam index")
- end if
- if (debug2_active (D_BEAMS)) print *, 'scaled x: ', x
- end subroutine sf_rescale_real_apply
-
-@ %def sf_rescale_real_apply
-@
-<<isr collinear: rescale real: TBP>>=
- procedure :: set => sf_rescale_real_set
-<<isr collinear: procedures>>=
- subroutine sf_rescale_real_set (func, xi, y)
- class(sf_rescale_real_t), intent(inout) :: func
- real(default), intent(in) :: xi, y
- func%xi = xi; func%y = y
- end subroutine sf_rescale_real_set
-
-@ %def sf_rescale_real_set
-<<isr collinear: public>>=
- public :: sf_rescale_dglap_t
-<<isr collinear: types>>=
- type, extends(sf_rescale_t) :: sf_rescale_dglap_t
- real(default), dimension(:), allocatable :: z
- contains
- <<isr collinear: rescale dglap: TBP>>
- end type sf_rescale_dglap_t
-
-@ %def sf_rescale_dglap_t
-@
-<<isr collinear: rescale dglap: TBP>>=
- procedure :: apply => sf_rescale_dglap_apply
-<<isr collinear: procedures>>=
- subroutine sf_rescale_dglap_apply (func, x)
- class(sf_rescale_dglap_t), intent(in) :: func
- real(default), intent(inout) :: x
- if (debug2_active (D_BEAMS)) then
- print *, "Rescaling function - DGLAP:"
- print *, "Input: ", x
- print *, "Beam index: ", func%i_beam
- print *, "z: ", func%z
- end if
- x = x / func%z(func%i_beam)
- if (debug2_active (D_BEAMS)) print *, "scaled x: ", x
- end subroutine sf_rescale_dglap_apply
-
-@ %def sf_rescale_dglap_apply
-@
-<<isr collinear: rescale dglap: TBP>>=
- procedure :: set => sf_rescale_dglap_set
-<<isr collinear: procedures>>=
- subroutine sf_rescale_dglap_set (func, z)
- class(sf_rescale_dglap_t), intent(inout) :: func
- real(default), dimension(:), intent(in) :: z
- ! allocate-on-assginment
- func%z = z
- end subroutine sf_rescale_dglap_set
-
-@ %def sf_rescale_dglap_set
-@
\section{Dispatch}
@
<<[[dispatch_fks.f90]]>>=
<<File header>>
module dispatch_fks
<<Use kinds>>
<<Use strings>>
use string_utils, only: split_string
use variables, only: var_list_t
use nlo_data, only: fks_template_t, FKS_DEFAULT, FKS_RESONANCES
<<Standard module head>>
<<Dispatch fks: public>>
contains
<<Dispatch fks: procedures>>
end module dispatch_fks
@ %def dispatch_fks
@ Initialize parameters used to optimize FKS calculations.
<<Dispatch fks: public>>=
public :: dispatch_fks_s
<<Dispatch fks: procedures>>=
subroutine dispatch_fks_s (fks_template, var_list)
type(fks_template_t), intent(inout) :: fks_template
type(var_list_t), intent(in) :: var_list
real(default) :: fks_dij_exp1, fks_dij_exp2
type(string_t) :: fks_mapping_type
logical :: subtraction_disabled
type(string_t) :: exclude_from_resonance
fks_dij_exp1 = &
var_list%get_rval (var_str ("fks_dij_exp1"))
fks_dij_exp2 = &
var_list%get_rval (var_str ("fks_dij_exp2"))
fks_mapping_type = &
var_list%get_sval (var_str ("$fks_mapping_type"))
subtraction_disabled = &
var_list%get_lval (var_str ("?disable_subtraction"))
exclude_from_resonance = &
var_list%get_sval (var_str ("$resonances_exclude_particles"))
if (exclude_from_resonance /= var_str ("default")) &
call split_string (exclude_from_resonance, var_str (":"), &
fks_template%excluded_resonances)
call fks_template%set_parameters ( &
exp1 = fks_dij_exp1, exp2 = fks_dij_exp2, &
xi_min = var_list%get_rval (var_str ("fks_xi_min")), &
y_max = var_list%get_rval (var_str ("fks_y_max")), &
xi_cut = var_list%get_rval (var_str ("fks_xi_cut")), &
delta_o = var_list%get_rval (var_str ("fks_delta_o")), &
delta_i = var_list%get_rval (var_str ("fks_delta_i")))
select case (char (fks_mapping_type))
case ("default")
call fks_template%set_mapping_type (FKS_DEFAULT)
case ("resonances")
call fks_template%set_mapping_type (FKS_RESONANCES)
end select
fks_template%subtraction_disabled = subtraction_disabled
fks_template%n_f = var_list%get_ival (var_str ("alphas_nf"))
end subroutine dispatch_fks_s
@ %def dispatch_fks_s
@
Index: trunk/src/fks/Makefile.am
===================================================================
--- trunk/src/fks/Makefile.am (revision 8293)
+++ trunk/src/fks/Makefile.am (revision 8294)
@@ -1,206 +1,205 @@
## Makefile.am -- Makefile for WHIZARD
##
## Process this file with automake to produce Makefile.in
#
# Copyright (C) 1999-2019 by
# Wolfgang Kilian <kilian@physik.uni-siegen.de>
# Thorsten Ohl <ohl@physik.uni-wuerzburg.de>
# Juergen Reuter <juergen.reuter@desy.de>
# with contributions from
# cf. main AUTHORS file
#
# WHIZARD is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# WHIZARD is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
########################################################################
## The files in this directory implement the FKS subtraction scheme.
## We create a library which is still to be combined with auxiliary libs.
noinst_LTLIBRARIES = libfks.la
check_LTLIBRARIES = libfks_ut.la
libfks_la_SOURCES = \
fks_regions.f90 \
nlo_data.f90 \
virtual.f90 \
real_subtraction.f90 \
- isr_collinear.f90 \
dglap_remnant.f90 \
dispatch_fks.f90
libfks_ut_la_SOURCES = \
fks_regions_uti.f90 fks_regions_ut.f90 \
real_subtraction_uti.f90 real_subtraction_ut.f90
## Omitting this would exclude it from the distribution
dist_noinst_DATA = fks.nw
# Dump module names into file Modules
libfks_Modules = ${libfks_la_SOURCES:.f90=} ${libfks_ut_la_SOURCES:.f90=}
Modules: Makefile
@for module in $(libfks_Modules); do \
echo $$module >> $@.new; \
done
@if diff $@ $@.new -q >/dev/null; then \
rm $@.new; \
else \
mv $@.new $@; echo "Modules updated"; \
fi
BUILT_SOURCES = Modules
## Fortran module dependencies
# Get module lists from other directories
module_lists = \
../basics/Modules \
../utilities/Modules \
../testing/Modules \
../system/Modules \
../parsing/Modules \
../user/Modules \
../combinatorics/Modules \
../physics/Modules \
../qft/Modules \
../model_features/Modules \
../types/Modules \
../particles/Modules \
../threshold/Modules \
../matrix_elements/Modules \
../beams/Modules \
../me_methods/Modules \
../phase_space/Modules \
../variables/Modules \
../blha/Modules
$(module_lists):
$(MAKE) -C `dirname $@` Modules
Module_dependencies.sed: $(libfks_la_SOURCES) $(libfks_ut_la_SOURCES)
Module_dependencies.sed: $(module_lists)
@rm -f $@
echo 's/, *only:.*//' >> $@
echo 's/, *&//' >> $@
echo 's/, *.*=>.*//' >> $@
echo 's/$$/.lo/' >> $@
for list in $(module_lists); do \
dir="`dirname $$list`"; \
for mod in `cat $$list`; do \
echo 's!: '$$mod'.lo$$!': $$dir/$$mod'.lo!' >> $@; \
done \
done
DISTCLEANFILES = Module_dependencies.sed
# The following line just says
# include Makefile.depend
# but in a portable fashion (depending on automake's AM_MAKE_INCLUDE
@am__include@ @am__quote@Makefile.depend@am__quote@
Makefile.depend: Module_dependencies.sed
Makefile.depend: $(libfks_la_SOURCES) ${libfks_ut_la_SOURCES}
@rm -f $@
for src in $^; do \
module="`basename $$src | sed 's/\.f[90][0358]//'`"; \
grep '^ *use ' $$src \
| grep -v '!NODEP!' \
| sed -e 's/^ *use */'$$module'.lo: /' \
-f Module_dependencies.sed; \
done > $@
DISTCLEANFILES += Makefile.depend
# Fortran90 module files are generated at the same time as object files
.lo.$(FC_MODULE_EXT):
@:
# touch $@
AM_FCFLAGS = -I../basics -I../utilities -I../testing -I../system -I../combinatorics -I../parsing -I../user -I../rng -I../combinatorics -I../physics -I../qft -I../model_features -I../expr_base -I../types -I../particles -I../matrix_elements -I../beams -I../me_methods -I../phase_space -I../variables -I../blha -I../threshold -I../lhapdf -I../pdf_builtin -I../fastjet
########################################################################
## Default Fortran compiler options
## Profiling
if FC_USE_PROFILING
AM_FCFLAGS += $(FCFLAGS_PROFILING)
endif
## OpenMP
if FC_USE_OPENMP
AM_FCFLAGS += $(FCFLAGS_OPENMP)
endif
# MPI
if FC_USE_MPI
AM_FCFLAGS += $(FCFLAGS_MPI)
endif
########################################################################
## Non-standard targets and dependencies
## (Re)create F90 sources from NOWEB source.
if NOWEB_AVAILABLE
PRELUDE = $(top_srcdir)/src/noweb-frame/whizard-prelude.nw
POSTLUDE = $(top_srcdir)/src/noweb-frame/whizard-postlude.nw
fks.stamp: $(PRELUDE) $(srcdir)/fks.nw $(POSTLUDE)
@rm -f fks.tmp
@touch fks.tmp
for src in $(libfks_la_SOURCES) $(libfks_ut_la_SOURCES); do \
$(NOTANGLE) -R[[$$src]] $^ | $(CPIF) $$src; \
done
@mv -f fks.tmp fks.stamp
$(libfks_la_SOURCES) $(libfks_ut_la_SOURCES): fks.stamp
## Recover from the removal of $@
@if test -f $@; then :; else \
rm -f fks.stamp; \
$(MAKE) $(AM_MAKEFLAGS) fks.stamp; \
fi
endif
########################################################################
## Non-standard cleanup tasks
## Remove sources that can be recreated using NOWEB
if NOWEB_AVAILABLE
maintainer-clean-noweb:
-rm -f *.f90 *.c
endif
.PHONY: maintainer-clean-noweb
## Remove those sources also if builddir and srcdir are different
if NOWEB_AVAILABLE
clean-noweb:
test "$(srcdir)" != "." && rm -f *.f90 *.c || true
endif
.PHONY: clean-noweb
## Remove F90 module files
clean-local: clean-noweb
-rm -f fks.stamp fks.tmp
-rm -f *.$(FC_MODULE_EXT)
if FC_SUBMODULES
-rm -f *.smod
endif
## Remove backup files
maintainer-clean-backup:
-rm -f *~
.PHONY: maintainer-clean-backup
## Register additional clean targets
maintainer-clean-local: maintainer-clean-noweb maintainer-clean-backup
Index: trunk/src/openloops/openloops.nw
===================================================================
--- trunk/src/openloops/openloops.nw (revision 8293)
+++ trunk/src/openloops/openloops.nw (revision 8294)
@@ -1,685 +1,691 @@
% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: interface to OpenLoops 1-loop library
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{OpenLoops Interface}
\includemodulegraph{openloops}
The interface to OpenLoops.
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
<<[[prc_openloops.f90]]>>=
<<File header>>
module prc_openloops
use, intrinsic :: iso_c_binding !NODEP!
use kinds
use io_units
<<Use strings>>
use string_utils, only: str
use constants
use numeric_utils
use diagnostics
+<<Use debug>>
use system_dependencies
use physics_defs
use variables
use os_interface
use lorentz
use interactions
use sm_qcd
use sm_physics, only: top_width_sm_lo, top_width_sm_qcd_nlo_jk
use model_data
use prclib_interfaces
use prc_core_def
use prc_core
use blha_config
use blha_olp_interfaces
<<Use mpi f08>>
<<Standard module head>>
<<prc openloops: public>>
<<prc openloops: parameters>>
<<prc openloops: types>>
<<prc openloops: interfaces>>
contains
<<prc openloops: procedures>>
end module prc_openloops
@ %def module prc_openloops
@
<<prc openloops: parameters>>=
real(default), parameter :: openloops_default_bmass = 0._default
real(default), parameter :: openloops_default_topmass = 172._default
real(default), parameter :: openloops_default_topwidth = 0._default
real(default), parameter :: openloops_default_wmass = 80.399_default
real(default), parameter :: openloops_default_wwidth = 0._default
real(default), parameter :: openloops_default_zmass = 91.1876_default
real(default), parameter :: openloops_default_zwidth = 0._default
real(default), parameter :: openloops_default_higgsmass = 125._default
real(default), parameter :: openloops_default_higgswidth = 0._default
integer :: N_EXTERNAL = 0
@ %def openloops default parameter
@
<<prc openloops: interfaces>>=
abstract interface
subroutine ol_evaluate_scpowheg (id, pp, emitter, res, resmunu) bind(C)
import
integer(kind = c_int), value :: id, emitter
real(kind = c_double), intent(in) :: pp(5 * N_EXTERNAL)
real(kind = c_double), intent(out) :: res, resmunu(16)
end subroutine ol_evaluate_scpowheg
end interface
@ %def ol_evaluate_scpowheg interface
@
<<prc openloops: types>>=
type, extends (prc_blha_writer_t) :: openloops_writer_t
contains
<<prc openloops: openloops writer: TBP>>
end type openloops_writer_t
@ %def openloops_writer_t
@
<<prc openloops: public>>=
public :: openloops_def_t
<<prc openloops: types>>=
type, extends (blha_def_t) :: openloops_def_t
integer :: verbosity
contains
<<prc openloops: openloops def: TBP>>
end type openloops_def_t
@ %def openloops_def_t
@
<<prc openloops: types>>=
type, extends (blha_driver_t) :: openloops_driver_t
integer :: n_external = 0
type(string_t) :: olp_file
procedure(ol_evaluate_scpowheg), nopass, pointer :: &
evaluate_spin_correlations_powheg => null ()
contains
<<prc openloops: openloops driver: TBP>>
end type openloops_driver_t
@ %def openloops_driver_t
@
<<prc openloops: types>>=
type :: openloops_threshold_data_t
logical :: nlo = .true.
real(default) :: alpha_ew
real(default) :: sinthw
real(default) :: m_b, m_W
real(default) :: vtb
contains
<<prc openloops: openloops threshold data: TBP>>
end type openloops_threshold_data_t
@ %def openloops_threshold_data_t
@
<<prc openloops: openloops threshold data: TBP>>=
procedure :: compute_top_width => &
openloops_threshold_data_compute_top_width
<<prc openloops: procedures>>=
function openloops_threshold_data_compute_top_width &
(data, mtop, alpha_s) result (wtop)
real(default) :: wtop
class(openloops_threshold_data_t), intent(in) :: data
real(default), intent(in) :: mtop, alpha_s
if (data%nlo) then
wtop = top_width_sm_qcd_nlo_jk (data%alpha_ew, data%sinthw, &
data%vtb, mtop, data%m_W, data%m_b, alpha_s)
else
wtop = top_width_sm_lo (data%alpha_ew, data%sinthw, data%vtb, &
mtop, data%m_W, data%m_b)
end if
end function openloops_threshold_data_compute_top_width
@ %def openloops_threshold_data_compute_top_width
@
<<prc openloops: public>>=
public :: openloops_state_t
<<prc openloops: types>>=
type, extends (blha_state_t) :: openloops_state_t
type(openloops_threshold_data_t), allocatable :: threshold_data
contains
<<prc openloops: openloops state: TBP>>
end type openloops_state_t
@ %def openloops_state_t
@
<<prc openloops: openloops state: TBP>>=
procedure :: init_threshold => openloops_state_init_threshold
<<prc openloops: procedures>>=
subroutine openloops_state_init_threshold (object, model)
class(openloops_state_t), intent(inout) :: object
type(model_data_t), intent(in) :: model
if (model%get_name () == "SM_tt_threshold") then
allocate (object%threshold_data)
associate (data => object%threshold_data)
data%nlo = btest (int (model%get_real (var_str ('offshell_strategy'))), 0)
data%alpha_ew = one / model%get_real (var_str ('alpha_em_i'))
data%sinthw = model%get_real (var_str ('sw'))
data%m_b = model%get_real (var_str ('mb'))
data%m_W = model%get_real (var_str ('mW'))
data%vtb = model%get_real (var_str ('Vtb'))
end associate
end if
end subroutine openloops_state_init_threshold
@ %def openloops_state_init_threshold
@
<<prc openloops: public>>=
public :: prc_openloops_t
<<prc openloops: types>>=
type, extends (prc_blha_t) :: prc_openloops_t
contains
<<prc openloops: prc openloops: TBP>>
end type prc_openloops_t
@ %def prc_openloops_t
@
<<prc openloops: openloops writer: TBP>>=
procedure, nopass :: type_name => openloops_writer_type_name
<<prc openloops: procedures>>=
function openloops_writer_type_name () result (string)
type(string_t) :: string
string = "openloops"
end function openloops_writer_type_name
@
@ %def openloops_writer_type_name
<<prc openloops: openloops def: TBP>>=
procedure :: init => openloops_def_init
<<prc openloops: procedures>>=
subroutine openloops_def_init (object, basename, model_name, &
prt_in, prt_out, nlo_type, restrictions, var_list)
class(openloops_def_t), intent(inout) :: object
type(string_t), intent(in) :: basename, model_name
type(string_t), dimension(:), intent(in) :: prt_in, prt_out
integer, intent(in) :: nlo_type
type(string_t), intent(in), optional :: restrictions
type(var_list_t), intent(in) :: var_list
<<prc openloops: openloops def init: variables>>
object%basename = basename
allocate (openloops_writer_t :: object%writer)
select case (nlo_type)
case (BORN)
object%suffix = '_BORN'
case (NLO_REAL)
object%suffix = '_REAL'
case (NLO_VIRTUAL)
object%suffix = '_LOOP'
case (NLO_SUBTRACTION, NLO_MISMATCH)
object%suffix = '_SUB'
case (NLO_DGLAP)
object%suffix = '_DGLAP'
end select
<<prc openloops: openloops def init: suffix>>
select type (writer => object%writer)
class is (prc_blha_writer_t)
call writer%init (model_name, prt_in, prt_out, restrictions)
end select
object%verbosity = var_list%get_ival (var_str ("openloops_verbosity"))
end subroutine openloops_def_init
@ %def openloops_def_init
@ Add additional suffix for each rank of the communicator, such that the
filenames do not clash.
<<MPI: prc openloops: openloops def init: variables>>=
integer :: n_size, rank
<<MPI: prc openloops: openloops def init: suffix>>=
call MPI_comm_rank (MPI_COMM_WORLD, rank)
call MPI_Comm_size (MPI_COMM_WORLD, n_size)
if (n_size > 1) then
object%suffix = object%suffix // var_str ("_") // str (rank)
end if
@
<<prc openloops: openloops def: TBP>>=
procedure, nopass :: type_string => openloops_def_type_string
<<prc openloops: procedures>>=
function openloops_def_type_string () result (string)
type(string_t) :: string
string = "openloops"
end function openloops_def_type_string
@
@ %def openloops_def_type_string
<<prc openloops: openloops def: TBP>>=
procedure :: write => openloops_def_write
<<prc openloops: procedures>>=
subroutine openloops_def_write (object, unit)
class(openloops_def_t), intent(in) :: object
integer, intent(in) :: unit
select type (writer => object%writer)
type is (openloops_writer_t)
call writer%write (unit)
end select
end subroutine openloops_def_write
@
@ %def openloops_def_write
<<prc openloops: openloops driver: TBP>>=
procedure :: init_dlaccess_to_library => openloops_driver_init_dlaccess_to_library
<<prc openloops: procedures>>=
subroutine openloops_driver_init_dlaccess_to_library &
(object, os_data, dlaccess, success)
class(openloops_driver_t), intent(in) :: object
type(os_data_t), intent(in) :: os_data
type(dlaccess_t), intent(out) :: dlaccess
logical, intent(out) :: success
type(string_t) :: ol_library, msg_buffer
ol_library = OPENLOOPS_DIR // '/lib/libopenloops.' // &
os_data%shrlib_ext
msg_buffer = "One-Loop-Provider: Using OpenLoops"
call msg_message (char(msg_buffer))
msg_buffer = "Loading library: " // ol_library
call msg_message (char(msg_buffer))
if (os_file_exist (ol_library)) then
call dlaccess_init (dlaccess, var_str (""), ol_library, os_data)
else
call msg_fatal ("Link OpenLoops: library not found")
end if
success = .not. dlaccess_has_error (dlaccess)
end subroutine openloops_driver_init_dlaccess_to_library
@ %def openloops_driver_init_dlaccess_to_library
@
<<prc openloops: openloops driver: TBP>>=
procedure :: set_alpha_s => openloops_driver_set_alpha_s
<<prc openloops: procedures>>=
subroutine openloops_driver_set_alpha_s (driver, alpha_s)
class(openloops_driver_t), intent(in) :: driver
real(default), intent(in) :: alpha_s
integer :: ierr
if (associated (driver%blha_olp_set_parameter)) then
call driver%blha_olp_set_parameter &
(c_char_'alphas'//c_null_char, &
dble (alpha_s), 0._double, ierr)
else
call msg_fatal ("blha_olp_set_parameter not associated!")
end if
if (ierr == 0) call parameter_error_message (var_str ('alphas'))
end subroutine openloops_driver_set_alpha_s
@ %def openloops_driver_set_alpha_s
@
<<prc openloops: openloops driver: TBP>>=
procedure :: set_alpha_qed => openloops_driver_set_alpha_qed
<<prc openloops: procedures>>=
subroutine openloops_driver_set_alpha_qed (driver, alpha)
class(openloops_driver_t), intent(inout) :: driver
real(default), intent(in) :: alpha
integer :: ierr
call driver%blha_olp_set_parameter &
(c_char_'alpha_qed'//c_null_char, &
dble (alpha), 0._double, ierr)
if (ierr == 0) call parameter_error_message (var_str ('alpha_qed'))
end subroutine openloops_driver_set_alpha_qed
@ %def openloops_driver_set_alpha_qed
@
<<prc openloops: openloops driver: TBP>>=
procedure :: set_GF => openloops_driver_set_GF
<<prc openloops: procedures>>=
subroutine openloops_driver_set_GF (driver, GF)
class(openloops_driver_t), intent(inout) :: driver
real(default), intent(in) :: GF
integer :: ierr
call driver%blha_olp_set_parameter &
(c_char_'Gmu'//c_null_char, &
dble(GF), 0._double, ierr)
if (ierr == 0) call parameter_error_message (var_str ('Gmu'))
end subroutine openloops_driver_set_GF
@ %def openloops_driver_set_GF
@
<<prc openloops: openloops driver: TBP>>=
procedure :: set_weinberg_angle => openloops_driver_set_weinberg_angle
<<prc openloops: procedures>>=
subroutine openloops_driver_set_weinberg_angle (driver, sw2)
class(openloops_driver_t), intent(inout) :: driver
real(default), intent(in) :: sw2
integer :: ierr
call driver%blha_olp_set_parameter &
(c_char_'sw2'//c_null_char, &
dble(sw2), 0._double, ierr)
if (ierr == 0) call parameter_error_message (var_str ('sw2'))
end subroutine openloops_driver_set_weinberg_angle
@ %def openloops_driver_set_weinberg_angle
@
<<prc openloops: openloops driver: TBP>>=
procedure :: print_alpha_s => openloops_driver_print_alpha_s
<<prc openloops: procedures>>=
subroutine openloops_driver_print_alpha_s (object)
class(openloops_driver_t), intent(in) :: object
call object%blha_olp_print_parameter (c_char_'alphas'//c_null_char)
end subroutine openloops_driver_print_alpha_s
@ %def openloops_driver_print_alpha_s
@
<<prc openloops: openloops driver: TBP>>=
procedure, nopass :: type_name => openloops_driver_type_name
<<prc openloops: procedures>>=
function openloops_driver_type_name () result (type)
type(string_t) :: type
type = "OpenLoops"
end function openloops_driver_type_name
@ %def openloops_driver_type_name
@
<<prc openloops: openloops driver: TBP>>=
procedure :: load_sc_procedure => openloops_driver_load_sc_procedure
<<prc openloops: procedures>>=
subroutine openloops_driver_load_sc_procedure (object, os_data, success)
class(openloops_driver_t), intent(inout) :: object
type(os_data_t), intent(in) :: os_data
logical, intent(out) :: success
type(dlaccess_t) :: dlaccess
type(c_funptr) :: c_fptr
logical :: init_success
call object%init_dlaccess_to_library (os_data, dlaccess, init_success)
c_fptr = dlaccess_get_c_funptr (dlaccess, var_str ("ol_evaluate_scpowheg"))
call c_f_procpointer (c_fptr, object%evaluate_spin_correlations_powheg)
if (dlaccess_has_error (dlaccess)) then
call msg_fatal ("Could not load Openloops-powheg spin correlations!")
else
success = .true.
end if
end subroutine openloops_driver_load_sc_procedure
@ %def openloops_driver_load_sc_procedure
@
<<prc openloops: openloops def: TBP>>=
procedure :: read => openloops_def_read
<<prc openloops: procedures>>=
subroutine openloops_def_read (object, unit)
class(openloops_def_t), intent(out) :: object
integer, intent(in) :: unit
end subroutine openloops_def_read
@ %def openloops_def_read
@
<<prc openloops: openloops def: TBP>>=
procedure :: allocate_driver => openloops_def_allocate_driver
<<prc openloops: procedures>>=
subroutine openloops_def_allocate_driver (object, driver, basename)
class(openloops_def_t), intent(in) :: object
class(prc_core_driver_t), intent(out), allocatable :: driver
type(string_t), intent(in) :: basename
if (.not. allocated (driver)) allocate (openloops_driver_t :: driver)
end subroutine openloops_def_allocate_driver
@
@ %def openloops_def_allocate_driver
<<prc openloops: openloops state: TBP>>=
procedure :: write => openloops_state_write
<<prc openloops: procedures>>=
subroutine openloops_state_write (object, unit)
class(openloops_state_t), intent(in) :: object
integer, intent(in), optional :: unit
end subroutine openloops_state_write
@ %def prc_openloops_state_write
@
<<prc openloops: prc openloops: TBP>>=
procedure :: allocate_workspace => prc_openloops_allocate_workspace
<<prc openloops: procedures>>=
subroutine prc_openloops_allocate_workspace (object, core_state)
class(prc_openloops_t), intent(in) :: object
class(prc_core_state_t), intent(inout), allocatable :: core_state
allocate (openloops_state_t :: core_state)
end subroutine prc_openloops_allocate_workspace
@ %def prc_openloops_allocate_workspace
@
<<prc openloops: prc openloops: TBP>>=
procedure :: init_driver => prc_openloops_init_driver
<<prc openloops: procedures>>=
subroutine prc_openloops_init_driver (object, os_data)
class(prc_openloops_t), intent(inout) :: object
type(os_data_t), intent(in) :: os_data
type(string_t) :: olp_file, olc_file
type(string_t) :: suffix
select type (def => object%def)
type is (openloops_def_t)
suffix = def%suffix
olp_file = def%basename // suffix // '.olp'
olc_file = def%basename // suffix // '.olc'
class default
call msg_bug ("prc_openloops_init_driver: core_def should be openloops-type")
end select
select type (driver => object%driver)
type is (openloops_driver_t)
driver%olp_file = olp_file
driver%contract_file = olc_file
driver%nlo_suffix = suffix
end select
end subroutine prc_openloops_init_driver
@ %def prc_openloops_init_driver
@
<<prc openloops: prc openloops: TBP>>=
procedure :: write => prc_openloops_write
<<prc openloops: procedures>>=
subroutine prc_openloops_write (object, unit)
class(prc_openloops_t), intent(in) :: object
integer, intent(in), optional :: unit
call msg_message (unit = unit, string = "OpenLoops")
end subroutine prc_openloops_write
@
@ %def prc_openloops_write
<<prc openloops: prc openloops: TBP>>=
procedure :: write_name => prc_openloops_write_name
<<prc openloops: procedures>>=
subroutine prc_openloops_write_name (object, unit)
class(prc_openloops_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u,"(1x,A)") "Core: OpenLoops"
end subroutine prc_openloops_write_name
@
@ %def prc_openloops_write_name
<<prc openloops: prc openloops: TBP>>=
procedure :: prepare_library => prc_openloops_prepare_library
<<prc openloops: procedures>>=
subroutine prc_openloops_prepare_library (object, os_data, model)
class(prc_openloops_t), intent(inout) :: object
type(os_data_t), intent(in) :: os_data
type(model_data_t), intent(in), target :: model
call object%load_driver (os_data)
call object%reset_parameters ()
call object%set_particle_properties (model)
call object%set_electroweak_parameters (model)
select type(def => object%def)
type is (openloops_def_t)
call object%set_verbosity (def%verbosity)
end select
end subroutine prc_openloops_prepare_library
@ %def prc_openloops_prepare_library
@
<<prc openloops: prc openloops: TBP>>=
procedure :: load_driver => prc_openloops_load_driver
<<prc openloops: procedures>>=
subroutine prc_openloops_load_driver (object, os_data)
class(prc_openloops_t), intent(inout) :: object
type(os_data_t), intent(in) :: os_data
logical :: success
select type (driver => object%driver)
type is (openloops_driver_t)
call driver%load (os_data, success)
call driver%load_sc_procedure (os_data, success)
end select
end subroutine prc_openloops_load_driver
@ %def prc_openloops_load_driver
@
<<prc openloops: prc openloops: TBP>>=
procedure :: start => prc_openloops_start
<<prc openloops: procedures>>=
subroutine prc_openloops_start (object)
class(prc_openloops_t), intent(inout) :: object
integer :: ierr
select type (driver => object%driver)
type is (openloops_driver_t)
call driver%blha_olp_start (char (driver%olp_file)//c_null_char, ierr)
end select
end subroutine prc_openloops_start
@ %def prc_openloops_start
@
<<prc openloops: prc openloops: TBP>>=
procedure :: set_n_external => prc_openloops_set_n_external
<<prc openloops: procedures>>=
subroutine prc_openloops_set_n_external (object, n)
class(prc_openloops_t), intent(inout) :: object
integer, intent(in) :: n
N_EXTERNAL = n
end subroutine prc_openloops_set_n_external
@ %def prc_openloops_set_n_external
@
<<prc openloops: prc openloops: TBP>>=
procedure :: reset_parameters => prc_openloops_reset_parameters
<<prc openloops: procedures>>=
subroutine prc_openloops_reset_parameters (object)
class(prc_openloops_t), intent(inout) :: object
integer :: ierr
select type (driver => object%driver)
type is (openloops_driver_t)
call driver%blha_olp_set_parameter ('mass(5)'//c_null_char, &
dble(openloops_default_bmass), 0._double, ierr)
call driver%blha_olp_set_parameter ('mass(6)'//c_null_char, &
dble(openloops_default_topmass), 0._double, ierr)
call driver%blha_olp_set_parameter ('width(6)'//c_null_char, &
dble(openloops_default_topwidth), 0._double, ierr)
call driver%blha_olp_set_parameter ('mass(23)'//c_null_char, &
dble(openloops_default_zmass), 0._double, ierr)
call driver%blha_olp_set_parameter ('width(23)'//c_null_char, &
dble(openloops_default_zwidth), 0._double, ierr)
call driver%blha_olp_set_parameter ('mass(24)'//c_null_char, &
dble(openloops_default_wmass), 0._double, ierr)
call driver%blha_olp_set_parameter ('width(24)'//c_null_char, &
dble(openloops_default_wwidth), 0._double, ierr)
call driver%blha_olp_set_parameter ('mass(25)'//c_null_char, &
dble(openloops_default_higgsmass), 0._double, ierr)
call driver%blha_olp_set_parameter ('width(25)'//c_null_char, &
dble(openloops_default_higgswidth), 0._double, ierr)
end select
end subroutine prc_openloops_reset_parameters
@ %def prc_openloops_reset_parameters
@ Set the verbosity level for openloops. The different levels are as follows:
\begin{itemize}
\item[0] minimal output (startup message et.al.)
\item[1] show which libraries are loaded
\item[2] show debug information of the library loader, but not during run time
\item[3] show debug information during run time
\item[4] output for each call of [[set_parameters]].
\end{itemize}
<<prc openloops: prc openloops: TBP>>=
procedure :: set_verbosity => prc_openloops_set_verbosity
<<prc openloops: procedures>>=
subroutine prc_openloops_set_verbosity (object, verbose)
class(prc_openloops_t), intent(inout) :: object
integer, intent(in) :: verbose
integer :: ierr
select type (driver => object%driver)
type is (openloops_driver_t)
call driver%blha_olp_set_parameter ('verbose'//c_null_char, &
dble(verbose), 0._double, ierr)
end select
end subroutine prc_openloops_set_verbosity
@ %def prc_openloops_set_verbosity
@
<<prc openloops: prc openloops: TBP>>=
procedure :: prepare_external_code => &
prc_openloops_prepare_external_code
<<prc openloops: procedures>>=
subroutine prc_openloops_prepare_external_code &
(core, flv_states, var_list, os_data, libname, model, i_core, is_nlo)
class(prc_openloops_t), intent(inout) :: core
integer, intent(in), dimension(:,:), allocatable :: flv_states
type(var_list_t), intent(in) :: var_list
type(os_data_t), intent(in) :: os_data
type(string_t), intent(in) :: libname
type(model_data_t), intent(in), target :: model
integer, intent(in) :: i_core
logical, intent(in) :: is_nlo
core%sqme_tree_pos = 1
call core%set_n_external (core%data%get_n_tot ())
call core%prepare_library (os_data, model)
call core%start ()
call core%read_contract_file (flv_states)
call core%print_parameter_file (i_core)
end subroutine prc_openloops_prepare_external_code
@ %def prc_openloops_prepare_external_code
@ Computes a spin-correlated matrix element from an interface to an
external one-loop provider. The output of [[blha_olp_eval2]] is
an array of [[dimension(16)]]. The current interface does not
give out an accuracy, so that [[bad_point]] is always [[.false.]].
OpenLoops includes a factor of 1 / [[n_hel]] in the
amplitudes, which we have to undo if polarized matrix elements
are requested.
<<prc openloops: prc openloops: TBP>>=
procedure :: compute_sqme_spin_c => prc_openloops_compute_sqme_spin_c
<<prc openloops: procedures>>=
subroutine prc_openloops_compute_sqme_spin_c (object, &
i_flv, i_hel, em, p, ren_scale, sqme_spin_c, bad_point)
class(prc_openloops_t), intent(inout) :: object
integer, intent(in) :: i_flv, i_hel
integer, intent(in) :: em
type(vector4_t), intent(in), dimension(:) :: p
real(default), intent(in) :: ren_scale
real(default), intent(out), dimension(16) :: sqme_spin_c
logical, intent(out) :: bad_point
real(double), dimension(5*N_EXTERNAL) :: mom
real(double) :: res
real(double), dimension(16) :: res_munu
real(default) :: alpha_s
if (object%i_spin_c(i_flv, i_hel) >= 0) then
mom = object%create_momentum_array (p)
if (vanishes (ren_scale)) call msg_fatal &
("prc_openloops_compute_sqme_spin_c: ren_scale vanishes")
alpha_s = object%qcd%alpha%get (ren_scale)
select type (driver => object%driver)
type is (openloops_driver_t)
call driver%set_alpha_s (alpha_s)
call driver%evaluate_spin_correlations_powheg &
(object%i_spin_c(i_flv, i_hel), mom, em, res, res_munu)
end select
sqme_spin_c = res_munu
bad_point = .false.
if (object%includes_polarization ()) &
sqme_spin_c = object%n_hel * sqme_spin_c
+ if (debug_on) then
+ if (sum(sqme_spin_c) == 0) then
+ call msg_debug(D_SUBTRACTION,'Spin-correlated matrix elements provided by OpenLoops are zero!')
+ end if
+ end if
else
sqme_spin_c = zero
end if
end subroutine prc_openloops_compute_sqme_spin_c
@ %def prc_openloops_compute_sqme_spin_c
@
Index: trunk/src/user/user.nw
===================================================================
--- trunk/src/user/user.nw (revision 8293)
+++ trunk/src/user/user.nw (revision 8294)
@@ -1,933 +1,932 @@
% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: user plugins for cuts and structure functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{User Plugin Support}
\includemodulegraph{user}
Here we collect interface code that enables the user to inject his own
code into the WHIZARD workflow. The code uses data types defined elsewhere, and
is referenced in the [[eval_trees]] module.
These are the modules:
\begin{description}
\item[user\_code\_interface]
Generic support and specific additions.
\item[sf\_user]
Handle user-defined structure functions.
\end{description}
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{User Code Interface}
<<[[user_code_interface.f90]]>>=
<<File header>>
module user_code_interface
use iso_c_binding !NODEP!
<<Use kinds>>
<<Use strings>>
use diagnostics
use c_particles
use os_interface
<<Standard module head>>
<<User Code: public>>
<<User Code: variables>>
<<User Code: interfaces>>
contains
<<User Code: procedures>>
end module user_code_interface
@ %def user_code_interface
@
\subsection{User Code Management}
@
This data structure globally holds the filehandle of the user-code
library:
<<User Code: public>>=
public :: has_user_lib
<<User Code: variables>>=
type(dlaccess_t), save :: user_lib_handle
logical, save :: has_user_lib = .false.
type(string_t), save :: user
@ %def user_lib_handle has_user_lib user
@
Compile, link and load user code files. Dlopen all user-provided
libraries, included the one just compiled (if any).
By default, we are looking for a library [[user.so/dylib]]. If this is not
present, try [[user.f90]] and compile it. This can be overridden.
In detail: First, compile all sources explicitly specified on the
command line. Then collect all libraries specified on the command
line, including [[user.so]] if it was generated. If there is still no
code, check for an existing [[user.f90]] and compile this. Link
everything into a [[user.la]] libtool library. When
done, dlopen all libraries that we have so far.
<<User Code: public>>=
public :: user_code_init
<<User Code: procedures>>=
subroutine user_code_init (user_src, user_lib, user_target, rebuild, os_data)
type(string_t), dimension(:), intent(in) :: user_src, user_lib
type(string_t), intent(in) :: user_target
logical, intent(in) :: rebuild
type(os_data_t), intent(in) :: os_data
type(string_t) :: user_src_file, user_obj_files, user_lib_file
logical :: exist
type(c_funptr) :: fptr
integer :: i
call msg_message ("Initializing user code")
user = user_target; if (user == "") user = "user"
user_obj_files = ""
inquire (file = char (user) // ".la", exist = exist)
if (rebuild .or. .not. exist) then
do i = 1, size (user_src)
user_src_file = user_src(i) // os_data%fc_src_ext
inquire (file = char (user_src_file), exist = exist)
if (exist) then
call msg_message ("Found user-code source '" &
// char (user_src_file) // "'.")
call compile_user_src (user_src_file, user_obj_files)
else
call msg_fatal ("User-code source '" // char (user_src_file) &
// "' not found")
end if
end do
do i = 1, size (user_lib)
user_lib_file = user_lib(i) // ".la"
inquire (file = char (user_lib_file), exist = exist)
if (exist) then
call msg_message ("Found user-code library '" &
// char (user_lib_file) // "'.")
else
user_lib_file = user_lib(i) // os_data%fc_shrlib_ext
inquire (file = char (user_lib_file), exist = exist)
if (exist) then
call msg_message ("Found user-code library '" &
// char (user_lib_file) // "'.")
else
call msg_fatal ("User-code library '" // char (user_lib(i)) &
// "' not found")
end if
end if
user_obj_files = user_obj_files // " " // user_lib_file
end do
if (user_obj_files == "") then
user_src_file = user // os_data%fc_src_ext
inquire (file = char (user_src_file), exist = exist)
if (exist) then
call msg_message ("Found user-code source '" &
// char (user_src_file) // "'.")
call compile_user_src (user_src_file, user_obj_files)
else
call msg_fatal ("User-code source '" // char (user_src_file) &
// "' not found")
end if
end if
if (user_obj_files /= "") then
call link_user (char (user), user_obj_files)
end if
end if
call dlaccess_init &
(user_lib_handle, var_str ("."), &
user // os_data%fc_shrlib_ext, os_data)
if (dlaccess_has_error (user_lib_handle)) then
call msg_error (char (dlaccess_get_error (user_lib_handle)))
call msg_fatal ("Loading user code library '" // char (user) &
// ".la' failed")
else
call msg_message ("User code library '" // char (user) &
// ".la' successfully loaded")
has_user_lib = .true.
end if
contains
subroutine compile_user_src (user_src_file, user_obj_files)
type(string_t), intent(in) :: user_src_file
type(string_t), intent(inout) :: user_obj_files
type(string_t) :: basename, ext
logical :: exist
basename = user_src_file
call split (basename, ext, ".", back=.true.)
if ("." // ext == os_data%fc_src_ext) then
inquire (file = char (user_src_file), exist = exist)
if (exist) then
call msg_message ("Compiling user code file '" &
// char (user_src_file) // "'")
call os_compile_shared (basename, os_data)
user_obj_files = user_obj_files // " " // basename // ".lo"
else
call msg_error ("User code file '" // char (user_src_file) &
// "' not found.")
end if
else
call msg_error ("User code file '" // char (user_src_file) &
// "' should have file extension '" &
// char (os_data%fc_src_ext) // "'")
end if
end subroutine compile_user_src
subroutine link_user (user_lib, user_obj_files)
character(*), intent(in) :: user_lib
type(string_t), intent(in) :: user_obj_files
call msg_message ("Linking user code library '" &
// user_lib // char (os_data%fc_shrlib_ext) // "'")
call os_link_shared (user_obj_files, var_str (user_lib), os_data)
end subroutine link_user
end subroutine user_code_init
@ %def user_code_init
@ Unload all user-code libraries.
<<User Code: public>>=
public :: user_code_final
<<User Code: procedures>>=
subroutine user_code_final ()
if (has_user_lib) then
call dlaccess_final (user_lib_handle)
has_user_lib = .false.
end if
end subroutine user_code_final
@ %def user_code_final
@ Try to load the possible user-defined procedures from the dlopened
libraries. If a procedure is not found, do nothing.
<<User Code: public>>=
public :: user_code_find_proc
<<User Code: procedures>>=
function user_code_find_proc (name) result (fptr)
type(string_t), intent(in) :: name
type(c_funptr) :: fptr
integer :: i
fptr = c_null_funptr
!!! Ticket #529
! fptr = libmanager_get_c_funptr (char (user), char (name))
if (.not. c_associated (fptr)) then
if (has_user_lib) then
fptr = dlaccess_get_c_funptr (user_lib_handle, name)
if (.not. c_associated (fptr)) then
call msg_fatal ("User procedure '" // char (name) // "' not found")
end if
else
call msg_fatal ("User procedure '" // char (name) &
// "' called without user library (missing -u flag?)")
end if
end if
end function user_code_find_proc
@ %def user_code_find_proc
@
\subsection{Interfaces for user-defined functions}
The following functions represent user-defined real observables. There may
be one or two particles as argument, the result is a real value.
<<User Code: public>>=
public :: user_obs_int_unary
public :: user_obs_int_binary
public :: user_obs_real_unary
public :: user_obs_real_binary
<<User Code: interfaces>>=
abstract interface
function user_obs_int_unary (prt1) result (ival) bind(C)
use iso_c_binding !NODEP!
use c_particles !NODEP!
type(c_prt_t), intent(in) :: prt1
integer(c_int) :: ival
end function user_obs_int_unary
end interface
abstract interface
function user_obs_int_binary (prt1, prt2) result (ival) bind(C)
use iso_c_binding !NODEP!
use c_particles !NODEP!
type(c_prt_t), intent(in) :: prt1, prt2
integer(c_int) :: ival
end function user_obs_int_binary
end interface
abstract interface
function user_obs_real_unary (prt1) result (rval) bind(C)
use iso_c_binding !NODEP!
use c_particles !NODEP!
type(c_prt_t), intent(in) :: prt1
real(c_double) :: rval
end function user_obs_real_unary
end interface
abstract interface
function user_obs_real_binary (prt1, prt2) result (rval) bind(C)
use iso_c_binding !NODEP!
use c_particles !NODEP!
type(c_prt_t), intent(in) :: prt1, prt2
real(c_double) :: rval
end function user_obs_real_binary
end interface
@ %def user_obs_real_unary
@ %def user_obs_real_binary
@
The following function takes an array of C-compatible particles and
return a single value. The particle array represents a subevent. For
C interoperability, we have to use an assumed-size array, hence the
array size has to be transferred explicitly.
The cut function returns an [[int]], which we should interpret as a
logical value (nonzero=true).
<<User Code: public>>=
public :: user_cut_fun
<<User Code: interfaces>>=
abstract interface
function user_cut_fun (prt, n_prt) result (iflag) bind(C)
use iso_c_binding !NODEP!
use c_particles !NODEP!
type(c_prt_t), dimension(*), intent(in) :: prt
integer(c_int), intent(in) :: n_prt
integer(c_int) :: iflag
end function user_cut_fun
end interface
@ %def user_cut_fun
@ The event-shape function returns a real value.
<<User Code: public>>=
public :: user_event_shape_fun
<<User Code: interfaces>>=
abstract interface
function user_event_shape_fun (prt, n_prt) result (rval) bind(C)
use iso_c_binding !NODEP!
use c_particles !NODEP!
type(c_prt_t), dimension(*), intent(in) :: prt
integer(c_int), intent(in) :: n_prt
real(c_double) :: rval
end function user_event_shape_fun
end interface
@ %def user_event_shape_fun
@
\subsection{Interfaces for user-defined interactions}
The following procedure interfaces pertain to user-defined
interactions, e.g., spectra or structure functions.
This subroutine retrieves the basic information for setting up the
interaction and event generation. All parameters are
[[intent(inout)]], so we can provide default values. [[n_in]] and
[[n_out]] are the number of incoming and outgoing particles,
respectively. [[n_states]] is the total number of distinct states
that should be generated (counting all states of the incoming
particles). [[n_col]] is the maximal number of color entries a
particle can have. [[n_dim]] is the number of input parameters, i.e.,
integration dimensions, that the structure function call requires for
computing kinematics and matrix elements.
[[n_var]] is the number of variables (e.g., momentum fractions) that
the structure function call has to transfer from the kinematics to the
dynamics evaluation.
<<User Code: public>>=
public :: user_int_info
<<User Code: interfaces>>=
abstract interface
subroutine user_int_info (n_in, n_out, n_states, n_col, n_dim, n_var) &
bind(C)
use iso_c_binding !NODEP!
integer(c_int), intent(inout) :: n_in, n_out, n_states, n_col
integer(c_int), intent(inout) :: n_dim, n_var
end subroutine user_int_info
end interface
@ %def user_int_info
@ This subroutine retrieves the settings for the quantum number mask
of a given particle index in the interaction. A nonzero value
indicates that the corresponding quantum number is to be ignored. The
lock index is the index of a particle that the current particle is
related to. The relation applies if quantum numbers of one of the
locked particles are summed over. (This is intended for helicity.)
<<User Code: public>>=
public :: user_int_mask
<<User Code: interfaces>>=
abstract interface
subroutine user_int_mask (i_prt, m_flv, m_hel, m_col, i_lock) bind(C)
use iso_c_binding !NODEP!
integer(c_int), intent(in) :: i_prt
integer(c_int), intent(inout) :: m_flv, m_hel, m_col, i_lock
end subroutine user_int_mask
end interface
@ %def user_int_mask
@ This subroutine retrieves the quantum numbers for the particle
index [[i_prt]] in state [[i_state]]. The [[flv]] value is a PDG
code. The [[hel]] value is an integer helicity (twice the helicity
for fermions). The [[col]] array is an array which has at most
[[n_col]] entries (see above). All parameters are [[intent(inout)]]
since default values exist. In particular, if a mask entry is set by
the previous procedure, the corresponding quantum number is ignored
anyway.
<<User Code: public>>=
public :: user_int_state
<<User Code: interfaces>>=
abstract interface
subroutine user_int_state (i_state, i_prt, flv, hel, col) bind(C)
use iso_c_binding !NODEP!
integer(c_int), intent(in) :: i_state, i_prt
integer(c_int), intent(inout) :: flv, hel
integer(c_int), dimension(*), intent(inout) :: col
end subroutine user_int_state
end interface
@ %def user_int_state
@ This subroutine takes an array of particle objects
with array length [[n_in]] and an array of input parameters between 0 and 1
with array length [[n_dim]]. It returns an array of particle objects
with array length [[n_out]]. In addition, it returns an array of
internal variables (e.g., momentum fractions, Jacobian) with array
length [[n_var]] that is used by the
following subroutine for evaluating the dynamics, i.e., the matrix
elements.
<<User Code: public>>=
public :: user_int_kinematics
<<User Code: interfaces>>=
abstract interface
subroutine user_int_kinematics (prt_in, rval, prt_out, xval) bind(C)
use iso_c_binding !NODEP!
use c_particles !NODEP!
type(c_prt_t), dimension(*), intent(in) :: prt_in
real(c_double), dimension(*), intent(in) :: rval
type(c_prt_t), dimension(*), intent(inout) :: prt_out
real(c_double), dimension(*), intent(out) :: xval
end subroutine user_int_kinematics
end interface
@ %def user_int_kinematics
@ This subroutine takes the array of variables (e.g., momentum
fractions) with length [[n_var]] which has been generated by the
previous subroutine and a real variable, the energy scale of the
event. It returns an array of matrix-element values, one entry for
each quantum state [[n_states]]. The ordering of matrix elements must
correspond to the ordering of states.
<<User Code: public>>=
public :: user_int_evaluate
<<User Code: interfaces>>=
abstract interface
subroutine user_int_evaluate (xval, scale, fval) bind(C)
use iso_c_binding !NODEP!
real(c_double), dimension(*), intent(in) :: xval
real(c_double), intent(in) :: scale
real(c_double), dimension(*), intent(out) :: fval
end subroutine user_int_evaluate
end interface
@ %def user_int_evaluate
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{User Plugin for Structure Functions}
This variant gives access to user-defined structure functions or spectra.
\subsection{The module}
<<[[sf_user.f90]]>>=
<<File header>>
module sf_user
use, intrinsic :: iso_c_binding !NODEP!
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_17
use diagnostics
use c_particles
use lorentz
use subevents
use user_code_interface
use pdg_arrays
use model_data
use flavors
use helicities
use colors
use quantum_numbers
use state_matrices
use polarizations
use interactions
use sf_aux
use sf_base
<<Standard module head>>
<<SF user: public>>
<<SF user: types>>
contains
<<SF user: procedures>>
end module sf_user
@ %def sf_user
@
\subsection{The user structure function data block}
The data block holds the procedure pointers that are used for retrieving
static information, as well as the actual evaluation.
<<SF user: public>>=
public :: user_data_t
<<SF user: types>>=
type, extends(sf_data_t) :: user_data_t
private
type(string_t) :: name
integer :: n_in
integer :: n_out
integer :: n_tot
integer :: n_states
integer :: n_col
integer :: n_dim
integer :: n_var
integer, dimension(2) :: pdg_in
class(model_data_t), pointer :: model => null ()
procedure(user_int_info), nopass, pointer :: info => null ()
procedure(user_int_mask), nopass, pointer :: mask => null ()
procedure(user_int_state), nopass, pointer :: state => null ()
procedure(user_int_kinematics), nopass, pointer :: kinematics => null ()
procedure(user_int_evaluate), nopass, pointer :: evaluate => null ()
contains
<<SF user: user data: TBP>>
end type user_data_t
@ %def user_data_t
@ Assign procedure pointers from a dynamically loaded library, given the
specified [[name]].
We have to distinguish three cases: (1) Both beams are affected, and
the user spectrum implements both beams. There is a single data
object. (2) Both beams are
affected, and the user spectrum applies to single beams. Fill two
different objects. (3) A single beam is affected.
<<SF User: public>>=
public :: sf_user_data_init
<<SF User: procedures>>=
subroutine sf_user_data_init (data, name, flv, model)
type(sf_user_data_t), intent(out) :: data
type(string_t), intent(in) :: name
type(flavor_t), dimension(2), intent(in) :: flv
class(model_data_t), intent(in), target :: model
integer(c_int) :: n_in
integer(c_int) :: n_out
integer(c_int) :: n_states
integer(c_int) :: n_col
integer(c_int) :: n_dim
integer(c_int) :: n_var
data%name = name
data%pdg_in = flavor_get_pdg (flv)
data%model => model
call c_f_procpointer (user_code_find_proc (name // "_info"), data%info)
call c_f_procpointer (user_code_find_proc (name // "_mask"), data%mask)
call c_f_procpointer (user_code_find_proc (name // "_state"), data%state)
call c_f_procpointer &
(user_code_find_proc (name // "_kinematics"), data%kinematics)
call c_f_procpointer &
(user_code_find_proc (name // "_evaluate"), data%evaluate)
n_in = 1
n_out = 2
n_states = 1
n_col = 2
n_dim = 1
n_var = 1
call data%info (n_in, n_out, n_states, n_col, n_dim, n_var)
data%n_in = n_in
data%n_out = n_out
data%n_tot = n_in + n_out
data%n_states = n_states
data%n_col = n_col
data%n_dim = n_dim
data%n_var = n_var
end subroutine sf_user_data_init
@ %def sf_user_data_init
@ Output
<<SF user: user data: TBP>>=
procedure :: write => user_data_write
<<SF user: procedures>>=
subroutine user_data_write (data, unit, verbose)
class(user_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A,A)") "User structure function: ", char (data%name)
end subroutine user_data_write
@ %def user_data_write
@ Retrieving contents
<<SF User: public>>=
public :: sf_user_data_get_name
<<SF User: procedures>>=
function sf_user_data_get_name (data) result (name)
type(string_t) :: name
type(sf_user_data_t), intent(in) :: data
name = data%name
end function sf_user_data_get_name
@ %def sf_user_data_get_name
<<SF User: public>>=
public :: sf_user_data_get_n_in
public :: sf_user_data_get_n_out
public :: sf_user_data_get_n_tot
public :: sf_user_data_get_n_dim
public :: sf_user_data_get_n_var
<<SF User: procedures>>=
function sf_user_data_get_n_in (data) result (n_in)
integer :: n_in
type(sf_user_data_t), intent(in) :: data
n_in = data%n_in
end function sf_user_data_get_n_in
function sf_user_data_get_n_out (data) result (n_out)
integer :: n_out
type(sf_user_data_t), intent(in) :: data
n_out = data%n_out
end function sf_user_data_get_n_out
function sf_user_data_get_n_tot (data) result (n_tot)
integer :: n_tot
type(sf_user_data_t), intent(in) :: data
n_tot = data%n_tot
end function sf_user_data_get_n_tot
function sf_user_data_get_n_dim (data) result (n_dim)
integer :: n_dim
type(sf_user_data_t), intent(in) :: data
n_dim = data%n_dim
end function sf_user_data_get_n_dim
function sf_user_data_get_n_var (data) result (n_var)
integer :: n_var
type(sf_user_data_t), intent(in) :: data
n_var = data%n_var
end function sf_user_data_get_n_var
@ %def sf_user_data_get_n_in
@ %def sf_user_data_get_n_out
@ %def sf_user_data_get_n_tot
@ %def sf_user_data_get_n_dim
@ %def sf_user_data_get_n_var
@
\subsection{The interaction}
We fill the interaction by looking up the table of states using the interface
functions.
For particles which have a known flavor (as indicated by the mask), we
compute the mass squared, so we can use it for the invariant mass of
the particle objects.
<<SF user: user: TBP>>=
procedure :: init => user_init
<<SF user: procedures>>=
subroutine user_init (sf_int, data)
!!! JRR: WK please check (#529)
class(user_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask
integer, dimension(:), allocatable :: hel_lock
integer(c_int) :: m_flv, m_hel, m_col, i_lock
type(quantum_numbers_t), dimension(:), allocatable :: qn
integer(c_int) :: f, h
integer(c_int), dimension(:), allocatable :: c
type(flavor_t) :: flv
type(helicity_t) :: hel
type(color_t) :: col
integer :: i, s
integer(c_int) :: i_prt, i_state
select type (data)
type is (user_data_t)
allocate (mask (data%n_tot))
allocate (hel_lock (data%n_tot))
allocate (qn (data%n_tot))
allocate (c (data%n_col))
do i = 1, size (mask)
i_prt = i
m_flv = 0; m_col = 0; m_hel = 0; i_lock = 0
call data%mask (i_prt, m_flv, m_col, m_hel, i_lock)
mask(i) = &
quantum_numbers_mask (m_flv /= 0, m_col /= 0, m_hel /= 0)
hel_lock(i) = i_lock
end do
!!! JRR: WK please check (#529)
!!! Will have to be filled in later.
! call sf_int%base_init (mask, &
! hel_lock = hel_lock)
call sf_int%basic_init &
(data%n_in, 0, data%n_out, mask=mask, &
hel_lock=hel_lock, set_relations=.true.)
do s = 1, data%n_states
i_state = s
do i = 1, data%n_tot
i_prt = i
f = 0; h = 0; c = 0
call data%state (i_state, i_prt, f, h, c)
if (m_flv == 0) then
call flv%init (int (f), data%model)
else
call flv%init ()
end if
if (m_hel == 0) then
call hel%init (int (h))
else
call hel%init ()
end if
if (m_col == 0) then
call color_init_from_array (col, int (c))
else
call col%init ()
end if
call qn(i)%init (flv, col, hel)
end do
call sf_int%add_state (qn)
end do
call sf_int%freeze ()
!!! JRR: WK please check (#529)
!!! What has to be inserted here?
! call sf_int%set_incoming (??)
! call sf_int%set_radiated (??)
! call sf_int%set_outgoing (??)
sf_int%status = SF_INITIAL
end select
end subroutine user_init
@ %def user_init
@
@ Allocate the interaction record.
<<SF user: user data: TBP>>=
procedure :: allocate_sf_int => user_data_allocate_sf_int
<<SF user: procedures>>=
subroutine user_data_allocate_sf_int (data, sf_int)
class(user_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (user_t :: sf_int)
end subroutine user_data_allocate_sf_int
@ %def user_data_allocate_sf_int
@ The number of parameters is one. We do not generate transverse momentum.
<<SF user: user data: TBP>>=
procedure :: get_n_par => user_data_get_n_par
<<SF user: procedures>>=
function user_data_get_n_par (data) result (n)
class(user_data_t), intent(in) :: data
integer :: n
n = data%n_var
end function user_data_get_n_par
@ %def user_data_get_n_par
@
@ Return the outgoing particle PDG codes. This has to be inferred from
the states (right?). JRR: WK please check.
<<SF user: user data: TBP>>=
procedure :: get_pdg_out => user_data_get_pdg_out
<<SF user: procedures>>=
subroutine user_data_get_pdg_out (data, pdg_out)
class(user_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
!!! JRR: WK please check (#529)
!!! integer :: n, np, i
!!! n = count (data%mask)
!!! np = 0; if (data%has_photon .and. data%mask_photon) np = 1
!!! allocate (pdg_out (n + np))
!!! pdg_out(1:n) = pack ([(i, i = -6, 6)], data%mask)
!!! if (np == 1) pdg_out(n+np) = PHOTON
end subroutine user_data_get_pdg_out
@ %def user_data_get_pdg_out
\subsection{The user structure function}
For maximal flexibility, user structure functions separate kinematics from
dynamics just as the PDF interface does. (JRR: Ok, I guess this now
done for all structure functions, right?) We create [[c_prt_t]]
particle objects from the incoming momenta (all other quantum numbers
are irrelevant) and call the user-supplied kinematics function to
compute the outgoing momenta, along with other variables that will be
needed for matrix element evaluation. If known, we use the mass
squared computed above.
!!! JRR: WK please check (\#529)
I don't know actually whether this really fits into the setup done by
WK.
<<SF user: types>>=
!!! JRR: WK please check (#529)
type, extends (sf_int_t) :: user_t
type(user_data_t), pointer :: data => null ()
real(default) :: x = 0
real(default) :: q = 0
contains
<<SF user: user: TBP>>
end type user_t
@ %def user_t
@ Type string: display the name of the user structure function.
<<SF user: user: TBP>>=
procedure :: type_string => user_type_string
<<SF user: procedures>>=
function user_type_string (object) result (string)
class(user_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "User structure function: " // object%data%name
else
string = "User structure function: [undefined]"
end if
end function user_type_string
@ %def user_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF user: user: TBP>>=
procedure :: write => user_write
<<SF user: procedures>>=
subroutine user_write (object, unit, testflag)
!!! JRR: WK please check (#529)
!!! Guess these variables do not exist for user strfun (?)
class(user_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
if (object%status >= SF_DONE_KINEMATICS) then
write (u, "(1x,A)") "SF parameters:"
write (u, "(3x,A," // FMT_17 // ")") "x =", object%x
if (object%status >= SF_FAILED_EVALUATION) then
write (u, "(3x,A," // FMT_17 // ")") "Q =", object%q
end if
end if
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "User structure function data: [undefined]"
end if
end subroutine user_write
@ %def user_write
@
\subsection{Kinematics}
Set kinematics. If [[map]] is unset, the $r$ and $x$ values
coincide, and the Jacobian $f(r)$ is trivial.
If [[map]] is set, we are asked to provide an efficient mapping.
For the test case, we set $x=r^2$ and consequently $f(r)=2r$.
<<SF user: user: TBP>>=
procedure :: complete_kinematics => user_complete_kinematics
<<SF user: procedures>>=
subroutine user_complete_kinematics (sf_int, x, xb, f, r, rb, map)
!!! JRR: WK please check (#529)
!!! This cannot be correct, as the CIRCE1 structure function has
!!! twice the variables (2->4 instead of 1->2 splitting)
class(user_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
if (map) then
call msg_fatal ("User structure function: map flag not supported")
else
x(1) = r(1)
xb(1)= rb(1)
f = 1
end if
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_DONE_KINEMATICS)
sf_int%x = x(1)
case (SF_FAILED_KINEMATICS)
sf_int%x = 0
f = 0
end select
end subroutine user_complete_kinematics
@ %def user_complete_kinematics
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
<<SF user: user: TBP>>=
procedure :: inverse_kinematics => user_inverse_kinematics
<<SF user: procedures>>=
subroutine user_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
!!! JRR: WK please check (#529)
!!! This cannot be correct, as the CIRCE1 structure function has
!!! twice the variables (2->4 instead of 1->2 splitting)
class(user_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
call msg_fatal ("User structure function: map flag not supported")
else
r(1) = x(1)
rb(1)= xb(1)
f = 1
end if
if (set_mom) then
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_DONE_KINEMATICS)
sf_int%x = x(1)
case (SF_FAILED_KINEMATICS)
sf_int%x = 0
f = 0
end select
end if
end subroutine user_inverse_kinematics
@ %def user_inverse_kinematics
@
<<SF User: public>>=
public :: interaction_set_kinematics_sf_user
<<SF User: procedures>>=
subroutine interaction_set_kinematics_sf_user (int, x, r, data)
type(interaction_t), intent(inout) :: int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(in) :: r
type(sf_user_data_t), intent(in) :: data
type(vector4_t), dimension(data%n_in) :: p_in
type(vector4_t), dimension(data%n_out) :: p_out
type(c_prt_t), dimension(data%n_in) :: prt_in
type(c_prt_t), dimension(data%n_out) :: prt_out
real(c_double), dimension(data%n_var) :: xval
call int%get_momenta_sub (p_in, outgoing=.false.)
prt_in = vector4_to_c_prt (p_in)
prt_in%type = PRT_INCOMING
call data%kinematics (prt_in, real (r, c_double), prt_out, xval)
x = xval
p_out = vector4_from_c_prt (prt_out)
call int%set_momenta (p_out, outgoing=.true.)
end subroutine interaction_set_kinematics_sf_user
@ %def interaction_set_kinematics_sf_user
@ The matrix-element evaluation may require a scale parameter, therefore this
routine is separate. We take the variables computed above together
with the event energy scale and call the user function that computes
the matrix elements.
<<SF user: user: TBP>>=
procedure :: apply => user_apply
<<SF user: procedures>>=
- subroutine user_apply (sf_int, scale, rescale, i_sub, fill_sub) !, x, data)
+ subroutine user_apply (sf_int, scale, rescale, i_sub) !, x, data)
!!! JRR: WK please check (#529)
class(user_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default), dimension(:), allocatable :: x
real(c_double), dimension(sf_int%data%n_states) :: fval
complex(default), dimension(sf_int%data%n_states) :: fc
associate (data => sf_int%data)
!!! This is wrong, has to be replaced
! allocate (x, size (sf_int%x)))
x = sf_int%x
call data%evaluate (real (x, c_double), real (scale, c_double), fval)
fc = fval
call sf_int%set_matrix_element (fc)
end associate
sf_int%status = SF_EVALUATED
end subroutine user_apply
@ %def user_apply
Index: trunk/src/physics/physics.nw
===================================================================
--- trunk/src/physics/physics.nw (revision 8293)
+++ trunk/src/physics/physics.nw (revision 8294)
@@ -1,5310 +1,5312 @@
% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: physics and such
\chapter{Physics}
\includemodulegraph{physics}
Here we collect definitions and functions that we need for (particle)
physics in general, to make them available for the more specific needs
of WHIZARD.
\begin{description}
\item[physics\_defs]
Physical constants.
\item[c\_particles]
A simple data type for particles which is C compatible.
\item[lorentz]
Define three-vectors, four-vectors and Lorentz
transformations and common operations for them.
\item[sm\_physics]
Here, running functions are stored for special kinematical setup like
running coupling constants, Catani-Seymour dipoles, or Sudakov factors.
\item[sm\_qcd]
Definitions and methods for dealing with the running QCD coupling.
\item[shower\_algorithms]
Algorithms typically used in Parton Showers as well as in their
matching to NLO computations, e.g. with the POWHEG method.
\end{description}
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Physics Constants}
There is also the generic [[constants]] module. The constants listed
here are more specific for particle physics.
<<[[physics_defs.f90]]>>=
<<File header>>
module physics_defs
<<Use kinds>>
<<Use strings>>
use constants, only: one, two, three
<<Standard module head>>
<<Physics defs: public parameters>>
<<Physics defs: public>>
<<Physics defs: interfaces>>
contains
<<Physics defs: procedures>>
end module physics_defs
@ %def physics_defs
@
\subsection{Units}
Conversion from energy units to cross-section units.
<<Physics defs: public parameters>>=
real(default), parameter, public :: &
conv = 0.38937966e12_default
@
Conversion from millimeter to nanoseconds for lifetimes.
<<Physics defs: public parameters>>=
real(default), parameter, public :: &
ns_per_mm = 1.e6_default / 299792458._default
-@
+@
Rescaling factor.
<<Physics defs: public parameters>>=
real(default), parameter, public :: &
pb_per_fb = 1.e-3_default
@
String for the default energy and cross-section units.
<<Physics defs: public parameters>>=
character(*), parameter, public :: &
energy_unit = "GeV"
character(*), parameter, public :: &
cross_section_unit = "fb"
@
\subsection{SM and QCD constants}
<<Physics defs: public parameters>>=
real(default), parameter, public :: &
NC = three, &
CF = (NC**2 - one) / two / NC, &
CA = NC, &
TR = one / two
@
\subsection{Parameter Reference values}
These are used exclusively in the context of
running QCD parameters. In other contexts, we rely on the uniform
parameter set as provided by the model definition, modifiable by the
user.
<<Physics defs: public parameters>>=
real(default), public, parameter :: MZ_REF = 91.188_default
real(default), public, parameter :: ALPHA_QCD_MZ_REF = 0.1178_default
real(default), public, parameter :: LAMBDA_QCD_REF = 200.e-3_default
@ %def alpha_s_mz_ref mz_ref lambda_qcd_ref
@
\subsection{Particle codes}
Let us define a few particle codes independent of the model.
We need an UNDEFINED value:
<<Physics defs: public parameters>>=
integer, parameter, public :: UNDEFINED = 0
@ %def UNDEFINED
@ SM fermions:
<<Physics defs: public parameters>>=
integer, parameter, public :: ELECTRON = 11
integer, parameter, public :: ELECTRON_NEUTRINO = 12
integer, parameter, public :: MUON = 13
integer, parameter, public :: MUON_NEUTRINO = 14
integer, parameter, public :: TAU = 15
integer, parameter, public :: TAU_NEUTRINO = 16
@ %def ELECTRON MUON TAU
@ Gauge bosons:
<<Physics defs: public parameters>>=
integer, parameter, public :: GLUON = 21
integer, parameter, public :: PHOTON = 22
integer, parameter, public :: Z_BOSON = 23
integer, parameter, public :: W_BOSON = 24
@ %def GLUON PHOTON Z_BOSON W_BOSON
@ Light mesons:
<<Physics defs: public parameters>>=
integer, parameter, public :: PION = 111
integer, parameter, public :: PIPLUS = 211
integer, parameter, public :: PIMINUS = - PIPLUS
@ %def PION PIPLUS PIMINUS
@ Di-Quarks:
<<Physics defs: public parameters>>=
integer, parameter, public :: UD0 = 2101
integer, parameter, public :: UD1 = 2103
integer, parameter, public :: UU1 = 2203
@ %def UD0 UD1 UU1
@ Mesons:
<<Physics defs: public parameters>>=
integer, parameter, public :: K0L = 130
integer, parameter, public :: K0S = 310
integer, parameter, public :: K0 = 311
integer, parameter, public :: KPLUS = 321
integer, parameter, public :: DPLUS = 411
integer, parameter, public :: D0 = 421
integer, parameter, public :: B0 = 511
integer, parameter, public :: BPLUS = 521
@ %def K0L K0S K0 KPLUS DPLUS D0 B0 BPLUS
@ Light baryons:
<<Physics defs: public parameters>>=
integer, parameter, public :: PROTON = 2212
integer, parameter, public :: NEUTRON = 2112
integer, parameter, public :: DELTAPLUSPLUS = 2224
integer, parameter, public :: DELTAPLUS = 2214
integer, parameter, public :: DELTA0 = 2114
integer, parameter, public :: DELTAMINUS = 1114
@ %def PROTON NEUTRON DELTAPLUSPLUS DELTAPLUS DELTA0 DELTAMINUS
@ Strange baryons:
<<Physics defs: public parameters>>=
integer, parameter, public :: SIGMAPLUS = 3222
integer, parameter, public :: SIGMA0 = 3212
integer, parameter, public :: SIGMAMINUS = 3112
@ %def SIGMAPLUS SIGMA0 SIGMAMINUS
@ Charmed baryons:
<<Physics defs: public parameters>>=
integer, parameter, public :: SIGMACPLUSPLUS = 4222
integer, parameter, public :: SIGMACPLUS = 4212
integer, parameter, public :: SIGMAC0 = 4112
@ %def SIGMACPLUSPLUS SIGMACPLUS SIGMAC0
@ Bottom baryons:
<<Physics defs: public parameters>>=
integer, parameter, public :: SIGMAB0 = 5212
integer, parameter, public :: SIGMABPLUS = 5222
@ %def SIGMAB0 SIGMABPLUS
@ 81-100 are reserved for internal codes. Hadron and beam remnants:
<<Physics defs: public parameters>>=
integer, parameter, public :: BEAM_REMNANT = 9999
integer, parameter, public :: HADRON_REMNANT = 90
integer, parameter, public :: HADRON_REMNANT_SINGLET = 91
integer, parameter, public :: HADRON_REMNANT_TRIPLET = 92
integer, parameter, public :: HADRON_REMNANT_OCTET = 93
@ %def BEAM_REMNANT HADRON_REMNANT
@ %def HADRON_REMNANT_SINGLET HADRON_REMNANT_TRIPLET HADRON_REMNANT_OCTET
@
Further particle codes for internal use:
<<Physics defs: public parameters>>=
integer, parameter, public :: INTERNAL = 94
integer, parameter, public :: INVALID = 97
integer, parameter, public :: COMPOSITE = 99
@ %def INTERNAL INVALID COMPOSITE
@
\subsection{Spin codes}
Somewhat redundant, but for better readability we define named
constants for spin types. If the mass is nonzero, this is equal to
the number of degrees of freedom.
<<Physics defs: public parameters>>=
integer, parameter, public:: UNKNOWN = 0
integer, parameter, public :: SCALAR = 1, SPINOR = 2, VECTOR = 3, &
VECTORSPINOR = 4, TENSOR = 5
@ %def UNKNOWN SCALAR SPINOR VECTOR VECTORSPINOR TENSOR
@ Isospin types and charge types are counted in an analogous way,
where charge type 1 is charge 0, 2 is charge 1/3, and so on. Zero
always means unknown. Note that charge and isospin types have an
explicit sign.
Color types are defined as the dimension of the representation.
\subsection{NLO status codes}
Used to specify whether a [[term_instance_t]] of a
[[process_instance_t]] is associated with a Born, real-subtracted,
virtual-subtracted or subtraction-dummy matrix element.
<<Physics defs: public parameters>>=
integer, parameter, public :: BORN = 0
integer, parameter, public :: NLO_REAL = 1
integer, parameter, public :: NLO_VIRTUAL = 2
integer, parameter, public :: NLO_MISMATCH = 3
integer, parameter, public :: NLO_DGLAP = 4
integer, parameter, public :: NLO_SUBTRACTION = 5
integer, parameter, public :: NLO_FULL = 6
integer, parameter, public :: GKS = 7
integer, parameter, public :: COMPONENT_UNDEFINED = 99
@ % def BORN, NLO_REAL, NLO_VIRTUAL, NLO_SUBTRACTION, GKS
@ [[NLO_FULL]] is not strictly a component status code but having it is
convenient.
We define the number of additional subtractions for beam-involved NLO calculations.
Each subtraction refers to a rescaling of one of two beams.
+Obviously, this approach is not flexible enough to support setups with just a
+single beam described by a structure function.
<<Physics defs: public parameters>>=
- integer, parameter, public :: n_beam_structure_int = 4
- integer, parameter, public :: n_beam_gluon_offset = 2
+ integer, parameter, public :: n_beams_rescaled = 2
-@ %def n_beam_structure_int
+@ %def n_beams_rescaled
@
<<Physics defs: public>>=
public :: component_status
<<Physics defs: interfaces>>=
interface component_status
module procedure component_status_of_string
module procedure component_status_to_string
end interface
<<Physics defs: procedures>>=
elemental function component_status_of_string (string) result (i)
integer :: i
type(string_t), intent(in) :: string
select case (char(string))
case ("born")
i = BORN
case ("real")
i = NLO_REAL
case ("virtual")
i = NLO_VIRTUAL
case ("mismatch")
i = NLO_MISMATCH
case ("dglap")
i = NLO_DGLAP
case ("subtraction")
i = NLO_SUBTRACTION
case ("full")
i = NLO_FULL
case ("GKS")
i = GKS
case default
i = COMPONENT_UNDEFINED
end select
end function component_status_of_string
elemental function component_status_to_string (i) result (string)
type(string_t) :: string
integer, intent(in) :: i
select case (i)
case (BORN)
string = "born"
case (NLO_REAL)
string = "real"
case (NLO_VIRTUAL)
string = "virtual"
case (NLO_MISMATCH)
string = "mismatch"
case (NLO_DGLAP)
string = "dglap"
case (NLO_SUBTRACTION)
string = "subtraction"
case (NLO_FULL)
string = "full"
case (GKS)
string = "GKS"
case default
string = "undefined"
end select
end function component_status_to_string
@ %def component_status
@
<<Physics defs: public>>=
public :: is_nlo_component
<<Physics defs: procedures>>=
elemental function is_nlo_component (comp) result (is_nlo)
logical :: is_nlo
integer, intent(in) :: comp
select case (comp)
case (BORN : GKS)
is_nlo = .true.
case default
is_nlo = .false.
end select
end function is_nlo_component
@ %def is_nlo_component
@
<<Physics defs: public>>=
public :: is_subtraction_component
<<Physics defs: procedures>>=
function is_subtraction_component (emitter, nlo_type) result (is_subtraction)
logical :: is_subtraction
integer, intent(in) :: emitter, nlo_type
is_subtraction = nlo_type == NLO_REAL .and. emitter < 0
end function is_subtraction_component
@ %def is_subtraction_component
@
\subsection{Threshold}
Some commonly used variables for the threshold computation
<<Physics defs: public parameters>>=
integer, parameter, public :: THR_POS_WP = 3
integer, parameter, public :: THR_POS_WM = 4
integer, parameter, public :: THR_POS_B = 5
integer, parameter, public :: THR_POS_BBAR = 6
integer, parameter, public :: THR_POS_GLUON = 7
integer, parameter, public :: THR_EMITTER_OFFSET = 4
integer, parameter, public :: NO_FACTORIZATION = 0
integer, parameter, public :: FACTORIZATION_THRESHOLD = 1
integer, dimension(2), parameter, public :: ass_quark = [5, 6]
integer, dimension(2), parameter, public :: ass_boson = [3, 4]
integer, parameter, public :: PROC_MODE_UNDEFINED = 0
integer, parameter, public :: PROC_MODE_TT = 1
integer, parameter, public :: PROC_MODE_WBWB = 2
@
@
<<Physics defs: public>>=
public :: thr_leg
<<Physics defs: procedures>>=
function thr_leg (emitter) result (leg)
integer :: leg
integer, intent(in) :: emitter
leg = emitter - THR_EMITTER_OFFSET
end function thr_leg
@ %def thr_leg
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{C-compatible Particle Type}
For easy communication with C code, we introduce a simple C-compatible
type for particles. The components are either default C integers or
default C doubles.
The [[c_prt]] type is transparent, and its contents should be regarded
as part of the interface.
<<[[c_particles.f90]]>>=
<<File header>>
module c_particles
use, intrinsic :: iso_c_binding !NODEP!
use io_units
use format_defs, only: FMT_14, FMT_19
<<Standard module head>>
<<C Particles: public>>
<<C Particles: types>>
contains
<<C Particles: procedures>>
end module c_particles
@ %def c_particles
@
<<C Particles: public>>=
public :: c_prt_t
<<C Particles: types>>=
type, bind(C) :: c_prt_t
integer(c_int) :: type = 0
integer(c_int) :: pdg = 0
integer(c_int) :: polarized = 0
integer(c_int) :: h = 0
real(c_double) :: pe = 0
real(c_double) :: px = 0
real(c_double) :: py = 0
real(c_double) :: pz = 0
real(c_double) :: p2 = 0
end type c_prt_t
@ %def c_prt_t
@ This is for debugging only, there is no C binding. It is a
simplified version of [[prt_write]].
<<C Particles: public>>=
public :: c_prt_write
<<C Particles: procedures>>=
subroutine c_prt_write (prt, unit)
type(c_prt_t), intent(in) :: prt
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)", advance="no") "prt("
write (u, "(I0,':')", advance="no") prt%type
if (prt%polarized /= 0) then
write (u, "(I0,'/',I0,'|')", advance="no") prt%pdg, prt%h
else
write (u, "(I0,'|')", advance="no") prt%pdg
end if
write (u, "(" // FMT_14 // ",';'," // FMT_14 // ",','," // &
FMT_14 // ",','," // FMT_14 // ")", advance="no") &
prt%pe, prt%px, prt%py, prt%pz
write (u, "('|'," // FMT_19 // ")", advance="no") prt%p2
write (u, "(A)") ")"
end subroutine c_prt_write
@ %def c_prt_write
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Lorentz algebra}
Define Lorentz vectors, three-vectors, boosts, and some functions to
manipulate them.
To make maximum use of this, all functions, if possible, are declared
elemental (or pure, if this is not possible).
<<[[lorentz.f90]]>>=
<<File header>>
module lorentz
<<Use kinds with double>>
use numeric_utils
use io_units
use constants, only: pi, twopi, degree, zero, one, two, eps0, tiny_07
use format_defs, only: FMT_11, FMT_13, FMT_15, FMT_19
use format_utils, only: pac_fmt
use diagnostics
use c_particles
<<Standard module head>>
<<Lorentz: public>>
<<Lorentz: public operators>>
<<Lorentz: public functions>>
<<Lorentz: types>>
<<Lorentz: parameters>>
<<Lorentz: interfaces>>
contains
<<Lorentz: procedures>>
end module lorentz
@ %def lorentz
@
\subsection{Three-vectors}
First of all, let us introduce three-vectors in a trivial way. The
functions and overloaded elementary operations clearly are too much
overhead, but we like to keep the interface for three-vectors and
four-vectors exactly parallel. By the way, we might attach a label to
a vector by extending the type definition later.
<<Lorentz: public>>=
public :: vector3_t
<<Lorentz: types>>=
type :: vector3_t
real(default), dimension(3) :: p
end type vector3_t
@ %def vector3_t
@ Output a vector
<<Lorentz: public>>=
public :: vector3_write
<<Lorentz: procedures>>=
subroutine vector3_write (p, unit, testflag)
type(vector3_t), intent(in) :: p
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
character(len=7) :: fmt
integer :: u
u = given_output_unit (unit); if (u < 0) return
call pac_fmt (fmt, FMT_19, FMT_15, testflag)
write(u, "(1x,A,3(1x," // fmt // "))") 'P = ', p%p
end subroutine vector3_write
@ %def vector3_write
@ This is a three-vector with zero components
<<Lorentz: public>>=
public :: vector3_null
<<Lorentz: parameters>>=
type(vector3_t), parameter :: vector3_null = &
vector3_t ([ zero, zero, zero ])
@ %def vector3_null
@ Canonical three-vector:
<<Lorentz: public>>=
public :: vector3_canonical
<<Lorentz: procedures>>=
elemental function vector3_canonical (k) result (p)
type(vector3_t) :: p
integer, intent(in) :: k
p = vector3_null
p%p(k) = 1
end function vector3_canonical
@ %def vector3_canonical
@ A moving particle ($k$-axis, or arbitrary axis). Note that the
function for the generic momentum cannot be elemental.
<<Lorentz: public>>=
public :: vector3_moving
<<Lorentz: interfaces>>=
interface vector3_moving
module procedure vector3_moving_canonical
module procedure vector3_moving_generic
end interface
<<Lorentz: procedures>>=
elemental function vector3_moving_canonical (p, k) result(q)
type(vector3_t) :: q
real(default), intent(in) :: p
integer, intent(in) :: k
q = vector3_null
q%p(k) = p
end function vector3_moving_canonical
pure function vector3_moving_generic (p) result(q)
real(default), dimension(3), intent(in) :: p
type(vector3_t) :: q
q%p = p
end function vector3_moving_generic
@ %def vector3_moving
@ Equality and inequality
<<Lorentz: public operators>>=
public :: operator(==), operator(/=)
<<Lorentz: interfaces>>=
interface operator(==)
module procedure vector3_eq
end interface
interface operator(/=)
module procedure vector3_neq
end interface
<<Lorentz: procedures>>=
elemental function vector3_eq (p, q) result (r)
logical :: r
type(vector3_t), intent(in) :: p,q
r = all (abs (p%p - q%p) < eps0)
end function vector3_eq
elemental function vector3_neq (p, q) result (r)
logical :: r
type(vector3_t), intent(in) :: p,q
r = any (abs(p%p - q%p) > eps0)
end function vector3_neq
@ %def == /=
@ Define addition and subtraction
<<Lorentz: public operators>>=
public :: operator(+), operator(-)
<<Lorentz: interfaces>>=
interface operator(+)
module procedure add_vector3
end interface
interface operator(-)
module procedure sub_vector3
end interface
<<Lorentz: procedures>>=
elemental function add_vector3 (p, q) result (r)
type(vector3_t) :: r
type(vector3_t), intent(in) :: p,q
r%p = p%p + q%p
end function add_vector3
elemental function sub_vector3 (p, q) result (r)
type(vector3_t) :: r
type(vector3_t), intent(in) :: p,q
r%p = p%p - q%p
end function sub_vector3
@ %def + -
@ The multiplication sign is overloaded with scalar multiplication;
similarly division:
<<Lorentz: public operators>>=
public :: operator(*), operator(/)
<<Lorentz: interfaces>>=
interface operator(*)
module procedure prod_integer_vector3, prod_vector3_integer
module procedure prod_real_vector3, prod_vector3_real
end interface
interface operator(/)
module procedure div_vector3_real, div_vector3_integer
end interface
<<Lorentz: procedures>>=
elemental function prod_real_vector3 (s, p) result (q)
type(vector3_t) :: q
real(default), intent(in) :: s
type(vector3_t), intent(in) :: p
q%p = s * p%p
end function prod_real_vector3
elemental function prod_vector3_real (p, s) result (q)
type(vector3_t) :: q
real(default), intent(in) :: s
type(vector3_t), intent(in) :: p
q%p = s * p%p
end function prod_vector3_real
elemental function div_vector3_real (p, s) result (q)
type(vector3_t) :: q
real(default), intent(in) :: s
type(vector3_t), intent(in) :: p
q%p = p%p/s
end function div_vector3_real
elemental function prod_integer_vector3 (s, p) result (q)
type(vector3_t) :: q
integer, intent(in) :: s
type(vector3_t), intent(in) :: p
q%p = s * p%p
end function prod_integer_vector3
elemental function prod_vector3_integer (p, s) result (q)
type(vector3_t) :: q
integer, intent(in) :: s
type(vector3_t), intent(in) :: p
q%p = s * p%p
end function prod_vector3_integer
elemental function div_vector3_integer (p, s) result (q)
type(vector3_t) :: q
integer, intent(in) :: s
type(vector3_t), intent(in) :: p
q%p = p%p/s
end function div_vector3_integer
@ %def * /
@ The multiplication sign can also indicate scalar products:
<<Lorentz: interfaces>>=
interface operator(*)
module procedure prod_vector3
end interface
<<Lorentz: procedures>>=
elemental function prod_vector3 (p, q) result (s)
real(default) :: s
type(vector3_t), intent(in) :: p,q
s = dot_product (p%p, q%p)
end function prod_vector3
@ %def *
<<Lorentz: public functions>>=
public :: cross_product
<<Lorentz: interfaces>>=
interface cross_product
module procedure vector3_cross_product
end interface
<<Lorentz: procedures>>=
elemental function vector3_cross_product (p, q) result (r)
type(vector3_t) :: r
type(vector3_t), intent(in) :: p,q
integer :: i
do i=1,3
r%p(i) = dot_product (p%p, matmul(epsilon_three(i,:,:), q%p))
end do
end function vector3_cross_product
@ %def cross_product
@ Exponentiation is defined only for integer powers. Odd powers mean
take the square root; so [[p**1]] is the length of [[p]].
<<Lorentz: public operators>>=
public :: operator(**)
<<Lorentz: interfaces>>=
interface operator(**)
module procedure power_vector3
end interface
<<Lorentz: procedures>>=
elemental function power_vector3 (p, e) result (s)
real(default) :: s
type(vector3_t), intent(in) :: p
integer, intent(in) :: e
s = dot_product (p%p, p%p)
if (e/=2) then
if (mod(e,2)==0) then
s = s**(e/2)
else
s = sqrt(s)**e
end if
end if
end function power_vector3
@ %def **
@ Finally, we need a negation.
<<Lorentz: interfaces>>=
interface operator(-)
module procedure negate_vector3
end interface
<<Lorentz: procedures>>=
elemental function negate_vector3 (p) result (q)
type(vector3_t) :: q
type(vector3_t), intent(in) :: p
integer :: i
do i = 1, 3
if (abs (p%p(i)) < eps0) then
q%p(i) = 0
else
q%p(i) = -p%p(i)
end if
end do
end function negate_vector3
@ %def -
@ The sum function can be useful:
<<Lorentz: public functions>>=
public :: sum
<<Lorentz: interfaces>>=
interface sum
module procedure sum_vector3
end interface
@ %def sum
@
<<Lorentz: public>>=
public :: vector3_set_component
<<Lorentz: procedures>>=
subroutine vector3_set_component (p, i, value)
type(vector3_t), intent(inout) :: p
integer, intent(in) :: i
real(default), intent(in) :: value
p%p(i) = value
end subroutine vector3_set_component
@ %def vector3_set_component
@
<<Lorentz: procedures>>=
pure function sum_vector3 (p) result (q)
type(vector3_t) :: q
type(vector3_t), dimension(:), intent(in) :: p
integer :: i
do i=1, 3
q%p(i) = sum (p%p(i))
end do
end function sum_vector3
@ %def sum
@ Any component:
<<Lorentz: public>>=
public :: vector3_get_component
@ %def component
<<Lorentz: procedures>>=
elemental function vector3_get_component (p, k) result (c)
type(vector3_t), intent(in) :: p
integer, intent(in) :: k
real(default) :: c
c = p%p(k)
end function vector3_get_component
@ %def vector3_get_component
@ Extract all components. This is not elemental.
<<Lorentz: public>>=
public :: vector3_get_components
<<Lorentz: procedures>>=
pure function vector3_get_components (p) result (a)
type(vector3_t), intent(in) :: p
real(default), dimension(3) :: a
a = p%p
end function vector3_get_components
@ %def vector3_get_components
@ This function returns the direction of a three-vector, i.e., a
normalized three-vector. If the vector is null, we return a null vector.
<<Lorentz: public functions>>=
public :: direction
<<Lorentz: interfaces>>=
interface direction
module procedure vector3_get_direction
end interface
<<Lorentz: procedures>>=
elemental function vector3_get_direction (p) result (q)
type(vector3_t) :: q
type(vector3_t), intent(in) :: p
real(default) :: pp
pp = p**1
if (pp > eps0) then
q%p = p%p / pp
else
q%p = 0
end if
end function vector3_get_direction
@ %def direction
@
\subsection{Four-vectors}
In four-vectors the zero-component needs special treatment, therefore
we do not use the standard operations. Sure, we pay for the extra
layer of abstraction by losing efficiency; so we have to assume that
the time-critical applications do not involve four-vector operations.
<<Lorentz: public>>=
public :: vector4_t
<<Lorentz: types>>=
type :: vector4_t
real(default), dimension(0:3) :: p = &
[zero, zero, zero, zero]
contains
<<Lorentz: vector4: TBP>>
end type vector4_t
@ %def vector4_t
@ Output a vector
<<Lorentz: public>>=
public :: vector4_write
<<Lorentz: vector4: TBP>>=
procedure :: write => vector4_write
<<Lorentz: procedures>>=
subroutine vector4_write &
(p, unit, show_mass, testflag, compressed, ultra)
class(vector4_t), intent(in) :: p
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_mass, testflag, compressed, ultra
logical :: comp, sm, tf, extreme
integer :: u
character(len=7) :: fmt
real(default) :: m
comp = .false.; if (present (compressed)) comp = compressed
sm = .false.; if (present (show_mass)) sm = show_mass
tf = .false.; if (present (testflag)) tf = testflag
extreme = .false.; if (present (ultra)) extreme = ultra
if (extreme) then
call pac_fmt (fmt, FMT_19, FMT_11, testflag)
else
call pac_fmt (fmt, FMT_19, FMT_13, testflag)
end if
u = given_output_unit (unit); if (u < 0) return
if (comp) then
write (u, "(4(F12.3,1X))", advance="no") p%p(0:3)
else
write (u, "(1x,A,1x," // fmt // ")") 'E = ', p%p(0)
write (u, "(1x,A,3(1x," // fmt // "))") 'P = ', p%p(1:)
if (sm) then
m = p**1
if (tf) call pacify (m, tolerance = 1E-6_default)
write (u, "(1x,A,1x," // fmt // ")") 'M = ', m
end if
end if
end subroutine vector4_write
@ %def vector4_write
@ Binary I/O
<<Lorentz: public>>=
public :: vector4_write_raw
public :: vector4_read_raw
<<Lorentz: procedures>>=
subroutine vector4_write_raw (p, u)
type(vector4_t), intent(in) :: p
integer, intent(in) :: u
write (u) p%p
end subroutine vector4_write_raw
subroutine vector4_read_raw (p, u, iostat)
type(vector4_t), intent(out) :: p
integer, intent(in) :: u
integer, intent(out), optional :: iostat
read (u, iostat=iostat) p%p
end subroutine vector4_read_raw
@ %def vector4_write_raw vector4_read_raw
@ This is a four-vector with zero components
<<Lorentz: public>>=
public :: vector4_null
<<Lorentz: parameters>>=
type(vector4_t), parameter :: vector4_null = &
vector4_t ([ zero, zero, zero, zero ])
@ %def vector4_null
@ Canonical four-vector:
<<Lorentz: public>>=
public :: vector4_canonical
<<Lorentz: procedures>>=
elemental function vector4_canonical (k) result (p)
type(vector4_t) :: p
integer, intent(in) :: k
p = vector4_null
p%p(k) = 1
end function vector4_canonical
@ %def vector4_canonical
@ A particle at rest:
<<Lorentz: public>>=
public :: vector4_at_rest
<<Lorentz: procedures>>=
elemental function vector4_at_rest (m) result (p)
type(vector4_t) :: p
real(default), intent(in) :: m
p = vector4_t ([ m, zero, zero, zero ])
end function vector4_at_rest
@ %def vector4_at_rest
@ A moving particle ($k$-axis, or arbitrary axis)
<<Lorentz: public>>=
public :: vector4_moving
<<Lorentz: interfaces>>=
interface vector4_moving
module procedure vector4_moving_canonical
module procedure vector4_moving_generic
end interface
<<Lorentz: procedures>>=
elemental function vector4_moving_canonical (E, p, k) result (q)
type(vector4_t) :: q
real(default), intent(in) :: E, p
integer, intent(in) :: k
q = vector4_at_rest(E)
q%p(k) = p
end function vector4_moving_canonical
elemental function vector4_moving_generic (E, p) result (q)
type(vector4_t) :: q
real(default), intent(in) :: E
type(vector3_t), intent(in) :: p
q%p(0) = E
q%p(1:) = p%p
end function vector4_moving_generic
@ %def vector4_moving
@ Equality and inequality
<<Lorentz: interfaces>>=
interface operator(==)
module procedure vector4_eq
end interface
interface operator(/=)
module procedure vector4_neq
end interface
<<Lorentz: procedures>>=
elemental function vector4_eq (p, q) result (r)
logical :: r
type(vector4_t), intent(in) :: p,q
r = all (abs (p%p - q%p) < eps0)
end function vector4_eq
elemental function vector4_neq (p, q) result (r)
logical :: r
type(vector4_t), intent(in) :: p,q
r = any (abs (p%p - q%p) > eps0)
end function vector4_neq
@ %def == /=
@ Addition and subtraction:
<<Lorentz: interfaces>>=
interface operator(+)
module procedure add_vector4
end interface
interface operator(-)
module procedure sub_vector4
end interface
<<Lorentz: procedures>>=
elemental function add_vector4 (p,q) result (r)
type(vector4_t) :: r
type(vector4_t), intent(in) :: p,q
r%p = p%p + q%p
end function add_vector4
elemental function sub_vector4 (p,q) result (r)
type(vector4_t) :: r
type(vector4_t), intent(in) :: p,q
r%p = p%p - q%p
end function sub_vector4
@ %def + -
@ We also need scalar multiplication and division:
<<Lorentz: interfaces>>=
interface operator(*)
module procedure prod_real_vector4, prod_vector4_real
module procedure prod_integer_vector4, prod_vector4_integer
end interface
interface operator(/)
module procedure div_vector4_real
module procedure div_vector4_integer
end interface
<<Lorentz: procedures>>=
elemental function prod_real_vector4 (s, p) result (q)
type(vector4_t) :: q
real(default), intent(in) :: s
type(vector4_t), intent(in) :: p
q%p = s * p%p
end function prod_real_vector4
elemental function prod_vector4_real (p, s) result (q)
type(vector4_t) :: q
real(default), intent(in) :: s
type(vector4_t), intent(in) :: p
q%p = s * p%p
end function prod_vector4_real
elemental function div_vector4_real (p, s) result (q)
type(vector4_t) :: q
real(default), intent(in) :: s
type(vector4_t), intent(in) :: p
q%p = p%p/s
end function div_vector4_real
elemental function prod_integer_vector4 (s, p) result (q)
type(vector4_t) :: q
integer, intent(in) :: s
type(vector4_t), intent(in) :: p
q%p = s * p%p
end function prod_integer_vector4
elemental function prod_vector4_integer (p, s) result (q)
type(vector4_t) :: q
integer, intent(in) :: s
type(vector4_t), intent(in) :: p
q%p = s * p%p
end function prod_vector4_integer
elemental function div_vector4_integer (p, s) result (q)
type(vector4_t) :: q
integer, intent(in) :: s
type(vector4_t), intent(in) :: p
q%p = p%p/s
end function div_vector4_integer
@ %def * /
@ Scalar products and squares in the Minkowski sense:
<<Lorentz: interfaces>>=
interface operator(*)
module procedure prod_vector4
end interface
interface operator(**)
module procedure power_vector4
end interface
<<Lorentz: procedures>>=
elemental function prod_vector4 (p, q) result (s)
real(default) :: s
type(vector4_t), intent(in) :: p,q
s = p%p(0)*q%p(0) - dot_product(p%p(1:), q%p(1:))
end function prod_vector4
@ %def *
@ The power operation for four-vectors is signed, i.e., [[p**1]] is
positive for timelike and negative for spacelike vectors. Note that
[[(p**1)**2]] is not necessarily equal to [[p**2]].
<<Lorentz: procedures>>=
elemental function power_vector4 (p, e) result (s)
real(default) :: s
type(vector4_t), intent(in) :: p
integer, intent(in) :: e
s = p * p
if (e /= 2) then
if (mod(e, 2) == 0) then
s = s**(e / 2)
else if (s >= 0) then
s = sqrt(s)**e
else
s = -(sqrt(abs(s))**e)
end if
end if
end function power_vector4
@ %def **
@ Finally, we introduce a negation
<<Lorentz: interfaces>>=
interface operator(-)
module procedure negate_vector4
end interface
<<Lorentz: procedures>>=
elemental function negate_vector4 (p) result (q)
type(vector4_t) :: q
type(vector4_t), intent(in) :: p
integer :: i
do i = 0, 3
if (abs (p%p(i)) < eps0) then
q%p(i) = 0
else
q%p(i) = -p%p(i)
end if
end do
end function negate_vector4
@ %def -
@ The sum function can be useful:
<<Lorentz: interfaces>>=
interface sum
module procedure sum_vector4, sum_vector4_mask
end interface
@ %def sum
@
<<Lorentz: procedures>>=
pure function sum_vector4 (p) result (q)
type(vector4_t) :: q
type(vector4_t), dimension(:), intent(in) :: p
integer :: i
do i = 0, 3
q%p(i) = sum (p%p(i))
end do
end function sum_vector4
pure function sum_vector4_mask (p, mask) result (q)
type(vector4_t) :: q
type(vector4_t), dimension(:), intent(in) :: p
logical, dimension(:), intent(in) :: mask
integer :: i
do i = 0, 3
q%p(i) = sum (p%p(i), mask=mask)
end do
end function sum_vector4_mask
@ %def sum
@
\subsection{Conversions}
Manually set a component of the four-vector:
<<Lorentz: public>>=
public :: vector4_set_component
<<Lorentz: procedures>>=
subroutine vector4_set_component (p, k, c)
type(vector4_t), intent(inout) :: p
integer, intent(in) :: k
real(default), intent(in) :: c
p%p(k) = c
end subroutine vector4_set_component
@ %def vector4_get_component
Any component:
<<Lorentz: public>>=
public :: vector4_get_component
<<Lorentz: procedures>>=
elemental function vector4_get_component (p, k) result (c)
real(default) :: c
type(vector4_t), intent(in) :: p
integer, intent(in) :: k
c = p%p(k)
end function vector4_get_component
@ %def vector4_get_component
@ Extract all components. This is not elemental.
<<Lorentz: public>>=
public :: vector4_get_components
<<Lorentz: procedures>>=
pure function vector4_get_components (p) result (a)
real(default), dimension(0:3) :: a
type(vector4_t), intent(in) :: p
a = p%p
end function vector4_get_components
@ %def vector4_get_components
@ This function returns the space part of a four-vector, such that we
can apply three-vector operations on it:
<<Lorentz: public functions>>=
public :: space_part
<<Lorentz: interfaces>>=
interface space_part
module procedure vector4_get_space_part
end interface
<<Lorentz: procedures>>=
elemental function vector4_get_space_part (p) result (q)
type(vector3_t) :: q
type(vector4_t), intent(in) :: p
q%p = p%p(1:)
end function vector4_get_space_part
@ %def space_part
@ This function returns the direction of a four-vector, i.e., a
normalized three-vector. If the four-vector has zero space part, we
return a null vector.
<<Lorentz: interfaces>>=
interface direction
module procedure vector4_get_direction
end interface
<<Lorentz: procedures>>=
elemental function vector4_get_direction (p) result (q)
type(vector3_t) :: q
type(vector4_t), intent(in) :: p
real(default) :: qq
q%p = p%p(1:)
qq = q**1
if (abs(qq) > eps0) then
q%p = q%p / qq
else
q%p = 0
end if
end function vector4_get_direction
@ %def direction
@ Change the sign of the spatial part of a four-vector
<<Lorentz: public>>=
public :: vector4_invert_direction
<<Lorentz: procedures>>=
elemental subroutine vector4_invert_direction (p)
type(vector4_t), intent(inout) :: p
p%p(1:3) = -p%p(1:3)
end subroutine vector4_invert_direction
@ %def vector4_invert_direction
@ This function returns the four-vector as an ordinary array. A
second version for an array of four-vectors.
<<Lorentz: public>>=
public :: assignment (=)
<<Lorentz: interfaces>>=
interface assignment (=)
module procedure array_from_vector4_1, array_from_vector4_2, &
array_from_vector3_1, array_from_vector3_2, &
vector4_from_array, vector3_from_array
end interface
<<Lorentz: procedures>>=
pure subroutine array_from_vector4_1 (a, p)
real(default), dimension(:), intent(out) :: a
type(vector4_t), intent(in) :: p
a = p%p
end subroutine array_from_vector4_1
pure subroutine array_from_vector4_2 (a, p)
type(vector4_t), dimension(:), intent(in) :: p
real(default), dimension(:,:), intent(out) :: a
integer :: i
forall (i=1:size(p))
a(:,i) = p(i)%p
end forall
end subroutine array_from_vector4_2
pure subroutine array_from_vector3_1 (a, p)
real(default), dimension(:), intent(out) :: a
type(vector3_t), intent(in) :: p
a = p%p
end subroutine array_from_vector3_1
pure subroutine array_from_vector3_2 (a, p)
type(vector3_t), dimension(:), intent(in) :: p
real(default), dimension(:,:), intent(out) :: a
integer :: i
forall (i=1:size(p))
a(:,i) = p(i)%p
end forall
end subroutine array_from_vector3_2
pure subroutine vector4_from_array (p, a)
type(vector4_t), intent(out) :: p
real(default), dimension(:), intent(in) :: a
p%p(0:3) = a
end subroutine vector4_from_array
pure subroutine vector3_from_array (p, a)
type(vector3_t), intent(out) :: p
real(default), dimension(:), intent(in) :: a
p%p(1:3) = a
end subroutine vector3_from_array
@ %def array_from_vector4 array_from_vector3
@
<<Lorentz: public>>=
public :: vector4
<<Lorentz: procedures>>=
pure function vector4 (a) result (p)
type(vector4_t) :: p
real(default), intent(in), dimension(4) :: a
p%p = a
end function vector4
@ %def vector4
@
<<Lorentz: vector4: TBP>>=
procedure :: to_pythia6 => vector4_to_pythia6
<<Lorentz: procedures>>=
pure function vector4_to_pythia6 (vector4, m) result (p)
real(double), dimension(1:5) :: p
class(vector4_t), intent(in) :: vector4
real(default), intent(in), optional :: m
p(1:3) = vector4%p(1:3)
p(4) = vector4%p(0)
if (present (m)) then
p(5) = m
else
p(5) = vector4 ** 1
end if
end function vector4_to_pythia6
@ %def vector4_to_pythia6
@ Transform the momentum of a [[c_prt]] object into a four-vector and
vice versa:
<<Lorentz: interfaces>>=
interface assignment (=)
module procedure vector4_from_c_prt, c_prt_from_vector4
end interface
<<Lorentz: procedures>>=
pure subroutine vector4_from_c_prt (p, c_prt)
type(vector4_t), intent(out) :: p
type(c_prt_t), intent(in) :: c_prt
p%p(0) = c_prt%pe
p%p(1) = c_prt%px
p%p(2) = c_prt%py
p%p(3) = c_prt%pz
end subroutine vector4_from_c_prt
pure subroutine c_prt_from_vector4 (c_prt, p)
type(c_prt_t), intent(out) :: c_prt
type(vector4_t), intent(in) :: p
c_prt%pe = p%p(0)
c_prt%px = p%p(1)
c_prt%py = p%p(2)
c_prt%pz = p%p(3)
c_prt%p2 = p ** 2
end subroutine c_prt_from_vector4
@ %def vector4_from_c_prt c_prt_from_vector4
@ Initialize a [[c_prt_t]] object with the components of a four-vector
as its kinematical entries. Compute the invariant mass, or use the
optional mass-squared value instead.
<<Lorentz: public>>=
public :: vector4_to_c_prt
<<Lorentz: procedures>>=
elemental function vector4_to_c_prt (p, p2) result (c_prt)
type(c_prt_t) :: c_prt
type(vector4_t), intent(in) :: p
real(default), intent(in), optional :: p2
c_prt%pe = p%p(0)
c_prt%px = p%p(1)
c_prt%py = p%p(2)
c_prt%pz = p%p(3)
if (present (p2)) then
c_prt%p2 = p2
else
c_prt%p2 = p ** 2
end if
end function vector4_to_c_prt
@ %def vector4_to_c_prt
@
<<Lorentz: public>>=
public :: phs_point_t
<<Lorentz: types>>=
type :: phs_point_t
type(vector4_t), dimension(:), allocatable :: p
integer :: n_momenta = 0
contains
<<Lorentz: phs point: TBP>>
end type phs_point_t
@ %def phs_point_t
@
<<Lorentz: interfaces>>=
interface operator(==)
module procedure phs_point_eq
end interface
<<Lorentz: procedures>>=
elemental function phs_point_eq (phs_point_1, phs_point_2) result (eq)
logical :: eq
type(phs_point_t), intent(in) :: phs_point_1, phs_point_2
eq = all (phs_point_1%p == phs_point_2%p)
end function phs_point_eq
@ %def phs_point_eq
@
<<Lorentz: interfaces>>=
interface operator(*)
module procedure prod_LT_phs_point
end interface
<<Lorentz: procedures>>=
elemental function prod_LT_phs_point (L, phs_point) result (phs_point_LT)
type(phs_point_t) :: phs_point_LT
type(lorentz_transformation_t), intent(in) :: L
type(phs_point_t), intent(in) :: phs_point
phs_point_LT = size (phs_point%p)
phs_point_LT%p = L * phs_point%p
end function prod_LT_phs_point
@ %def prod_LT_phs_point
@
<<Lorentz: interfaces>>=
interface assignment(=)
module procedure phs_point_from_n, phs_point_from_vector4, &
phs_point_from_phs_point
end interface
<<Lorentz: procedures>>=
pure subroutine phs_point_from_n (phs_point, n_particles)
type(phs_point_t), intent(out) :: phs_point
integer, intent(in) :: n_particles
allocate (phs_point%p (n_particles))
phs_point%n_momenta = n_particles
phs_point%p = vector4_null
end subroutine phs_point_from_n
@ %def phs_point_init_from_n
@
<<Lorentz: phs point: TBP>>=
<<Lorentz: procedures>>=
pure subroutine phs_point_from_vector4 (phs_point, p)
type(phs_point_t), intent(out) :: phs_point
type(vector4_t), intent(in), dimension(:) :: p
phs_point%n_momenta = size (p)
allocate (phs_point%p (phs_point%n_momenta), source = p)
end subroutine phs_point_from_vector4
@ %def phs_point_init_from_p
@
<<Lorentz: procedures>>=
pure subroutine phs_point_from_phs_point (phs_point, phs_point_in)
type(phs_point_t), intent(out) :: phs_point
type(phs_point_t), intent(in) :: phs_point_in
phs_point%n_momenta = phs_point_in%n_momenta
allocate (phs_point%p (phs_point%n_momenta))
phs_point%p = phs_point_in%p
end subroutine phs_point_from_phs_point
@ %def phs_point_from_phs_point
@
<<Lorentz: phs point: TBP>>=
procedure :: get_sqrts_in => phs_point_get_sqrts_in
<<Lorentz: procedures>>=
function phs_point_get_sqrts_in (phs_point, n_in) result (msq)
real(default) :: msq
class(phs_point_t), intent(in) :: phs_point
integer, intent(in) :: n_in
msq = (sum (phs_point%p(1:n_in)))**2
end function phs_point_get_sqrts_in
@ %def phs_point_get_sqrts_in
@
<<Lorentz: phs point: TBP>>=
procedure :: final => phs_point_final
<<Lorentz: procedures>>=
subroutine phs_point_final (phs_point)
class(phs_point_t), intent(inout) :: phs_point
deallocate (phs_point%p)
phs_point%n_momenta = 0
end subroutine phs_point_final
@ %def phs_point_final
@
<<Lorentz: phs point: TBP>>=
procedure :: write => phs_point_write
<<Lorentz: procedures>>=
subroutine phs_point_write (phs_point, unit, show_mass, testflag, &
check_conservation, ultra, n_in)
class(phs_point_t), intent(in) :: phs_point
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_mass
logical, intent(in), optional :: testflag, ultra
logical, intent(in), optional :: check_conservation
integer, intent(in), optional :: n_in
call vector4_write_set (phs_point%p, unit = unit, show_mass = show_mass, &
testflag = testflag, check_conservation = check_conservation, &
ultra = ultra, n_in = n_in)
end subroutine phs_point_write
@ %def phs_point_write
@
<<Lorentz: phs point: TBP>>=
procedure :: get_x => phs_point_get_x
<<Lorentz: procedures>>=
function phs_point_get_x (phs_point, E_beam) result (x)
real(default), dimension(2) :: x
class(phs_point_t), intent(in) :: phs_point
real(default), intent(in) :: E_beam
x = phs_point%p(1:2)%p(0) / E_beam
end function phs_point_get_x
@ %def phs_point_get_x
@
\subsection{Angles}
Return the angles in a canonical system. The angle $\phi$ is defined
between $0\leq\phi<2\pi$. In degenerate cases, return zero.
<<Lorentz: public functions>>=
public :: azimuthal_angle
<<Lorentz: interfaces>>=
interface azimuthal_angle
module procedure vector3_azimuthal_angle
module procedure vector4_azimuthal_angle
end interface
<<Lorentz: procedures>>=
elemental function vector3_azimuthal_angle (p) result (phi)
real(default) :: phi
type(vector3_t), intent(in) :: p
if (any (abs (p%p(1:2)) > 0)) then
phi = atan2(p%p(2), p%p(1))
if (phi < 0) phi = phi + twopi
else
phi = 0
end if
end function vector3_azimuthal_angle
elemental function vector4_azimuthal_angle (p) result (phi)
real(default) :: phi
type(vector4_t), intent(in) :: p
phi = vector3_azimuthal_angle (space_part (p))
end function vector4_azimuthal_angle
@ %def azimuthal_angle
@ Azimuthal angle in degrees
<<Lorentz: public functions>>=
public :: azimuthal_angle_deg
<<Lorentz: interfaces>>=
interface azimuthal_angle_deg
module procedure vector3_azimuthal_angle_deg
module procedure vector4_azimuthal_angle_deg
end interface
<<Lorentz: procedures>>=
elemental function vector3_azimuthal_angle_deg (p) result (phi)
real(default) :: phi
type(vector3_t), intent(in) :: p
phi = vector3_azimuthal_angle (p) / degree
end function vector3_azimuthal_angle_deg
elemental function vector4_azimuthal_angle_deg (p) result (phi)
real(default) :: phi
type(vector4_t), intent(in) :: p
phi = vector4_azimuthal_angle (p) / degree
end function vector4_azimuthal_angle_deg
@ %def azimuthal_angle_deg
@ The azimuthal distance of two vectors. This is the difference of
the azimuthal angles, but cannot be larger than $\pi$: The result is
between $-\pi<\Delta\phi\leq\pi$.
<<Lorentz: public functions>>=
public :: azimuthal_distance
<<Lorentz: interfaces>>=
interface azimuthal_distance
module procedure vector3_azimuthal_distance
module procedure vector4_azimuthal_distance
end interface
<<Lorentz: procedures>>=
elemental function vector3_azimuthal_distance (p, q) result (dphi)
real(default) :: dphi
type(vector3_t), intent(in) :: p,q
dphi = vector3_azimuthal_angle (q) - vector3_azimuthal_angle (p)
if (dphi <= -pi) then
dphi = dphi + twopi
else if (dphi > pi) then
dphi = dphi - twopi
end if
end function vector3_azimuthal_distance
elemental function vector4_azimuthal_distance (p, q) result (dphi)
real(default) :: dphi
type(vector4_t), intent(in) :: p,q
dphi = vector3_azimuthal_distance &
(space_part (p), space_part (q))
end function vector4_azimuthal_distance
@ %def azimuthal_distance
@ The same in degrees:
<<Lorentz: public functions>>=
public :: azimuthal_distance_deg
<<Lorentz: interfaces>>=
interface azimuthal_distance_deg
module procedure vector3_azimuthal_distance_deg
module procedure vector4_azimuthal_distance_deg
end interface
<<Lorentz: procedures>>=
elemental function vector3_azimuthal_distance_deg (p, q) result (dphi)
real(default) :: dphi
type(vector3_t), intent(in) :: p,q
dphi = vector3_azimuthal_distance (p, q) / degree
end function vector3_azimuthal_distance_deg
elemental function vector4_azimuthal_distance_deg (p, q) result (dphi)
real(default) :: dphi
type(vector4_t), intent(in) :: p,q
dphi = vector4_azimuthal_distance (p, q) / degree
end function vector4_azimuthal_distance_deg
@ %def azimuthal_distance_deg
@ The polar angle is defined $0\leq\theta\leq\pi$. Note that
[[ATAN2]] has the reversed order of arguments: [[ATAN2(Y,X)]]. Here,
$x$ is the 3-component while $y$ is the transverse momentum which is
always nonnegative. Therefore, the result is nonnegative as well.
<<Lorentz: public functions>>=
public :: polar_angle
<<Lorentz: interfaces>>=
interface polar_angle
module procedure polar_angle_vector3
module procedure polar_angle_vector4
end interface
<<Lorentz: procedures>>=
elemental function polar_angle_vector3 (p) result (theta)
real(default) :: theta
type(vector3_t), intent(in) :: p
if (any (abs (p%p) > 0)) then
theta = atan2 (sqrt(p%p(1)**2 + p%p(2)**2), p%p(3))
else
theta = 0
end if
end function polar_angle_vector3
elemental function polar_angle_vector4 (p) result (theta)
real(default) :: theta
type(vector4_t), intent(in) :: p
theta = polar_angle (space_part (p))
end function polar_angle_vector4
@ %def polar_angle
@ This is the cosine of the polar angle: $-1\leq\cos\theta\leq 1$.
<<Lorentz: public functions>>=
public :: polar_angle_ct
<<Lorentz: interfaces>>=
interface polar_angle_ct
module procedure polar_angle_ct_vector3
module procedure polar_angle_ct_vector4
end interface
<<Lorentz: procedures>>=
elemental function polar_angle_ct_vector3 (p) result (ct)
real(default) :: ct
type(vector3_t), intent(in) :: p
if (any (abs (p%p) > 0)) then
ct = p%p(3) / p**1
else
ct = 1
end if
end function polar_angle_ct_vector3
elemental function polar_angle_ct_vector4 (p) result (ct)
real(default) :: ct
type(vector4_t), intent(in) :: p
ct = polar_angle_ct (space_part (p))
end function polar_angle_ct_vector4
@ %def polar_angle_ct
@ The polar angle in degrees.
<<Lorentz: public functions>>=
public :: polar_angle_deg
<<Lorentz: interfaces>>=
interface polar_angle_deg
module procedure polar_angle_deg_vector3
module procedure polar_angle_deg_vector4
end interface
<<Lorentz: procedures>>=
elemental function polar_angle_deg_vector3 (p) result (theta)
real(default) :: theta
type(vector3_t), intent(in) :: p
theta = polar_angle (p) / degree
end function polar_angle_deg_vector3
elemental function polar_angle_deg_vector4 (p) result (theta)
real(default) :: theta
type(vector4_t), intent(in) :: p
theta = polar_angle (p) / degree
end function polar_angle_deg_vector4
@ %def polar_angle_deg
@ This is the angle enclosed between two three-momenta. If one of the
momenta is zero, we return an angle of zero. The range of the result
is $0\leq\theta\leq\pi$. If there is only one argument, take the
positive $z$ axis as reference.
<<Lorentz: public functions>>=
public :: enclosed_angle
<<Lorentz: interfaces>>=
interface enclosed_angle
module procedure enclosed_angle_vector3
module procedure enclosed_angle_vector4
end interface
<<Lorentz: procedures>>=
elemental function enclosed_angle_vector3 (p, q) result (theta)
real(default) :: theta
type(vector3_t), intent(in) :: p, q
theta = acos (enclosed_angle_ct (p, q))
end function enclosed_angle_vector3
elemental function enclosed_angle_vector4 (p, q) result (theta)
real(default) :: theta
type(vector4_t), intent(in) :: p, q
theta = enclosed_angle (space_part (p), space_part (q))
end function enclosed_angle_vector4
@ %def enclosed_angle
@ The cosine of the enclosed angle.
<<Lorentz: public functions>>=
public :: enclosed_angle_ct
<<Lorentz: interfaces>>=
interface enclosed_angle_ct
module procedure enclosed_angle_ct_vector3
module procedure enclosed_angle_ct_vector4
end interface
<<Lorentz: procedures>>=
elemental function enclosed_angle_ct_vector3 (p, q) result (ct)
real(default) :: ct
type(vector3_t), intent(in) :: p, q
if (any (abs (p%p) > 0) .and. any (abs (q%p) > 0)) then
ct = p*q / (p**1 * q**1)
if (ct>1) then
ct = 1
else if (ct<-1) then
ct = -1
end if
else
ct = 1
end if
end function enclosed_angle_ct_vector3
elemental function enclosed_angle_ct_vector4 (p, q) result (ct)
real(default) :: ct
type(vector4_t), intent(in) :: p, q
ct = enclosed_angle_ct (space_part (p), space_part (q))
end function enclosed_angle_ct_vector4
@ %def enclosed_angle_ct
@ The enclosed angle in degrees.
<<Lorentz: public functions>>=
public :: enclosed_angle_deg
<<Lorentz: interfaces>>=
interface enclosed_angle_deg
module procedure enclosed_angle_deg_vector3
module procedure enclosed_angle_deg_vector4
end interface
<<Lorentz: procedures>>=
elemental function enclosed_angle_deg_vector3 (p, q) result (theta)
real(default) :: theta
type(vector3_t), intent(in) :: p, q
theta = enclosed_angle (p, q) / degree
end function enclosed_angle_deg_vector3
elemental function enclosed_angle_deg_vector4 (p, q) result (theta)
real(default) :: theta
type(vector4_t), intent(in) :: p, q
theta = enclosed_angle (p, q) / degree
end function enclosed_angle_deg_vector4
@ %def enclosed_angle
@ The polar angle of the first momentum w.r.t.\ the second momentum,
evaluated in the rest frame of the second momentum. If the second
four-momentum is not timelike, return zero.
<<Lorentz: public functions>>=
public :: enclosed_angle_rest_frame
public :: enclosed_angle_ct_rest_frame
public :: enclosed_angle_deg_rest_frame
<<Lorentz: interfaces>>=
interface enclosed_angle_rest_frame
module procedure enclosed_angle_rest_frame_vector4
end interface
interface enclosed_angle_ct_rest_frame
module procedure enclosed_angle_ct_rest_frame_vector4
end interface
interface enclosed_angle_deg_rest_frame
module procedure enclosed_angle_deg_rest_frame_vector4
end interface
<<Lorentz: procedures>>=
elemental function enclosed_angle_rest_frame_vector4 (p, q) result (theta)
type(vector4_t), intent(in) :: p, q
real(default) :: theta
theta = acos (enclosed_angle_ct_rest_frame (p, q))
end function enclosed_angle_rest_frame_vector4
elemental function enclosed_angle_ct_rest_frame_vector4 (p, q) result (ct)
type(vector4_t), intent(in) :: p, q
real(default) :: ct
if (invariant_mass(q) > 0) then
ct = enclosed_angle_ct ( &
space_part (boost(-q, invariant_mass (q)) * p), &
space_part (q))
else
ct = 1
end if
end function enclosed_angle_ct_rest_frame_vector4
elemental function enclosed_angle_deg_rest_frame_vector4 (p, q) &
result (theta)
type(vector4_t), intent(in) :: p, q
real(default) :: theta
theta = enclosed_angle_rest_frame (p, q) / degree
end function enclosed_angle_deg_rest_frame_vector4
@ %def enclosed_angle_rest_frame
@ %def enclosed_angle_ct_rest_frame
@ %def enclosed_angle_deg_rest_frame
@
\subsection{More kinematical functions (some redundant)}
The scalar transverse momentum (assuming the $z$ axis is longitudinal)
<<Lorentz: public functions>>=
public :: transverse_part
<<Lorentz: interfaces>>=
interface transverse_part
module procedure transverse_part_vector4_beam_axis
module procedure transverse_part_vector4_vector4
end interface
<<Lorentz: procedures>>=
elemental function transverse_part_vector4_beam_axis (p) result (pT)
real(default) :: pT
type(vector4_t), intent(in) :: p
pT = sqrt(p%p(1)**2 + p%p(2)**2)
end function transverse_part_vector4_beam_axis
elemental function transverse_part_vector4_vector4 (p1, p2) result (pT)
real(default) :: pT
type(vector4_t), intent(in) :: p1, p2
real(default) :: p1_norm, p2_norm, p1p2, pT2
p1_norm = space_part_norm(p1)**2
p2_norm = space_part_norm(p2)**2
! p1p2 = p1%p(1:3)*p2%p(1:3)
p1p2 = vector4_get_space_part(p1) * vector4_get_space_part(p2)
pT2 = (p1_norm*p2_norm - p1p2)/p1_norm
pT = sqrt (pT2)
end function transverse_part_vector4_vector4
@ %def transverse_part
@ The scalar longitudinal momentum (assuming the $z$ axis is
longitudinal). Identical to [[momentum_z_component]].
<<Lorentz: public functions>>=
public :: longitudinal_part
<<Lorentz: interfaces>>=
interface longitudinal_part
module procedure longitudinal_part_vector4
end interface
<<Lorentz: procedures>>=
elemental function longitudinal_part_vector4 (p) result (pL)
real(default) :: pL
type(vector4_t), intent(in) :: p
pL = p%p(3)
end function longitudinal_part_vector4
@ %def longitudinal_part
@ Absolute value of three-momentum
<<Lorentz: public functions>>=
public :: space_part_norm
<<Lorentz: interfaces>>=
interface space_part_norm
module procedure space_part_norm_vector4
end interface
<<Lorentz: procedures>>=
elemental function space_part_norm_vector4 (p) result (p3)
real(default) :: p3
type(vector4_t), intent(in) :: p
p3 = sqrt (p%p(1)**2 + p%p(2)**2 + p%p(3)**2)
end function space_part_norm_vector4
@ %def momentum
@ The energy (the zeroth component)
<<Lorentz: public functions>>=
public :: energy
<<Lorentz: interfaces>>=
interface energy
module procedure energy_vector4
module procedure energy_vector3
module procedure energy_real
end interface
<<Lorentz: procedures>>=
elemental function energy_vector4 (p) result (E)
real(default) :: E
type(vector4_t), intent(in) :: p
E = p%p(0)
end function energy_vector4
@ Alternative: The energy corresponding to a given momentum and mass.
If the mass is omitted, it is zero
<<Lorentz: procedures>>=
elemental function energy_vector3 (p, mass) result (E)
real(default) :: E
type(vector3_t), intent(in) :: p
real(default), intent(in), optional :: mass
if (present (mass)) then
E = sqrt (p**2 + mass**2)
else
E = p**1
end if
end function energy_vector3
elemental function energy_real (p, mass) result (E)
real(default) :: E
real(default), intent(in) :: p
real(default), intent(in), optional :: mass
if (present (mass)) then
E = sqrt (p**2 + mass**2)
else
E = abs (p)
end if
end function energy_real
@ %def energy
@ The invariant mass of four-momenta. Zero for lightlike, negative for
spacelike momenta.
<<Lorentz: public functions>>=
public :: invariant_mass
<<Lorentz: interfaces>>=
interface invariant_mass
module procedure invariant_mass_vector4
end interface
<<Lorentz: procedures>>=
elemental function invariant_mass_vector4 (p) result (m)
real(default) :: m
type(vector4_t), intent(in) :: p
real(default) :: msq
msq = p*p
if (msq >= 0) then
m = sqrt (msq)
else
m = - sqrt (abs (msq))
end if
end function invariant_mass_vector4
@ %def invariant_mass
@ The invariant mass squared. Zero for lightlike, negative for
spacelike momenta.
<<Lorentz: public functions>>=
public :: invariant_mass_squared
<<Lorentz: interfaces>>=
interface invariant_mass_squared
module procedure invariant_mass_squared_vector4
end interface
<<Lorentz: procedures>>=
elemental function invariant_mass_squared_vector4 (p) result (msq)
real(default) :: msq
type(vector4_t), intent(in) :: p
msq = p*p
end function invariant_mass_squared_vector4
@ %def invariant_mass_squared
@ The transverse mass. If the mass squared is negative, this value
also is negative.
<<Lorentz: public functions>>=
public :: transverse_mass
<<Lorentz: interfaces>>=
interface transverse_mass
module procedure transverse_mass_vector4
end interface
<<Lorentz: procedures>>=
elemental function transverse_mass_vector4 (p) result (m)
real(default) :: m
type(vector4_t), intent(in) :: p
real(default) :: msq
msq = p%p(0)**2 - p%p(1)**2 - p%p(2)**2
if (msq >= 0) then
m = sqrt (msq)
else
m = - sqrt (abs (msq))
end if
end function transverse_mass_vector4
@ %def transverse_mass
@ The rapidity (defined if particle is massive or $p_\perp>0$)
<<Lorentz: public functions>>=
public :: rapidity
<<Lorentz: interfaces>>=
interface rapidity
module procedure rapidity_vector4
end interface
<<Lorentz: procedures>>=
elemental function rapidity_vector4 (p) result (y)
real(default) :: y
type(vector4_t), intent(in) :: p
y = .5 * log( (energy (p) + longitudinal_part (p)) &
& /(energy (p) - longitudinal_part (p)))
end function rapidity_vector4
@ %def rapidity
@ The pseudorapidity (defined if $p_\perp>0$)
<<Lorentz: public functions>>=
public :: pseudorapidity
<<Lorentz: interfaces>>=
interface pseudorapidity
module procedure pseudorapidity_vector4
end interface
<<Lorentz: procedures>>=
elemental function pseudorapidity_vector4 (p) result (eta)
real(default) :: eta
type(vector4_t), intent(in) :: p
eta = -log( tan (.5 * polar_angle (p)))
end function pseudorapidity_vector4
@ %def pseudorapidity
@ The rapidity distance (defined if both $p_\perp>0$)
<<Lorentz: public functions>>=
public :: rapidity_distance
<<Lorentz: interfaces>>=
interface rapidity_distance
module procedure rapidity_distance_vector4
end interface
<<Lorentz: procedures>>=
elemental function rapidity_distance_vector4 (p, q) result (dy)
type(vector4_t), intent(in) :: p, q
real(default) :: dy
dy = rapidity (q) - rapidity (p)
end function rapidity_distance_vector4
@ %def rapidity_distance
@ The pseudorapidity distance (defined if both $p_\perp>0$)
<<Lorentz: public functions>>=
public :: pseudorapidity_distance
<<Lorentz: interfaces>>=
interface pseudorapidity_distance
module procedure pseudorapidity_distance_vector4
end interface
<<Lorentz: procedures>>=
elemental function pseudorapidity_distance_vector4 (p, q) result (deta)
real(default) :: deta
type(vector4_t), intent(in) :: p, q
deta = pseudorapidity (q) - pseudorapidity (p)
end function pseudorapidity_distance_vector4
@ %def pseudorapidity_distance
@ The distance on the $\eta-\phi$ cylinder:
<<Lorentz: public functions>>=
public :: eta_phi_distance
<<Lorentz: interfaces>>=
interface eta_phi_distance
module procedure eta_phi_distance_vector4
end interface
<<Lorentz: procedures>>=
elemental function eta_phi_distance_vector4 (p, q) result (dr)
type(vector4_t), intent(in) :: p, q
real(default) :: dr
dr = sqrt ( &
pseudorapidity_distance (p, q)**2 &
+ azimuthal_distance (p, q)**2)
end function eta_phi_distance_vector4
@ %def eta_phi_distance
@
\subsection{Lorentz transformations}
<<Lorentz: public>>=
public :: lorentz_transformation_t
<<Lorentz: types>>=
type :: lorentz_transformation_t
private
real(default), dimension(0:3, 0:3) :: L
contains
<<Lorentz: lorentz transformation: TBP>>
end type lorentz_transformation_t
@ %def lorentz_transformation_t
@ Output:
<<Lorentz: public>>=
public :: lorentz_transformation_write
<<Lorentz: lorentz transformation: TBP>>=
procedure :: write => lorentz_transformation_write
<<Lorentz: procedures>>=
subroutine lorentz_transformation_write (L, unit, testflag, ultra)
class(lorentz_transformation_t), intent(in) :: L
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag, ultra
integer :: u, i
logical :: ult
character(len=7) :: fmt
ult = .false.; if (present (ultra)) ult = ultra
if (ult) then
call pac_fmt (fmt, FMT_19, FMT_11, ultra)
else
call pac_fmt (fmt, FMT_19, FMT_13, testflag)
end if
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A,3(1x," // fmt // "))") "L00 = ", L%L(0,0)
write (u, "(1x,A,3(1x," // fmt // "))") "L0j = ", L%L(0,1:3)
do i = 1, 3
write (u, "(1x,A,I0,A,3(1x," // fmt // "))") &
"L", i, "0 = ", L%L(i,0)
write (u, "(1x,A,I0,A,3(1x," // fmt // "))") &
"L", i, "j = ", L%L(i,1:3)
end do
end subroutine lorentz_transformation_write
@ %def lorentz_transformation_write
@ Extract all components:
<<Lorentz: public>>=
public :: lorentz_transformation_get_components
<<Lorentz: procedures>>=
pure function lorentz_transformation_get_components (L) result (a)
type(lorentz_transformation_t), intent(in) :: L
real(default), dimension(0:3,0:3) :: a
a = L%L
end function lorentz_transformation_get_components
@ %def lorentz_transformation_get_components
@
\subsection{Functions of Lorentz transformations}
For the inverse, we make use of the fact that
$\Lambda^{\mu\nu}\Lambda_{\mu\rho}=\delta^\nu_\rho$. So, lowering the
indices and transposing is sufficient.
<<Lorentz: public functions>>=
public :: inverse
<<Lorentz: interfaces>>=
interface inverse
module procedure lorentz_transformation_inverse
end interface
<<Lorentz: procedures>>=
elemental function lorentz_transformation_inverse (L) result (IL)
type(lorentz_transformation_t) :: IL
type(lorentz_transformation_t), intent(in) :: L
IL%L(0,0) = L%L(0,0)
IL%L(0,1:) = -L%L(1:,0)
IL%L(1:,0) = -L%L(0,1:)
IL%L(1:,1:) = transpose(L%L(1:,1:))
end function lorentz_transformation_inverse
@ %def lorentz_transformation_inverse
@ %def inverse
@
\subsection{Invariants}
These are used below. The first array index is varying fastest in
[[FORTRAN]]; therefore the extra minus in the odd-rank tensor
epsilon.
<<Lorentz: parameters>>=
integer, dimension(3,3), parameter :: delta_three = &
& reshape( source = [ 1,0,0, 0,1,0, 0,0,1 ], &
& shape = [3,3] )
integer, dimension(3,3,3), parameter :: epsilon_three = &
& reshape( source = [ 0, 0,0, 0,0,-1, 0,1,0, &
& 0, 0,1, 0,0, 0, -1,0,0, &
& 0,-1,0, 1,0, 0, 0,0,0 ],&
& shape = [3,3,3] )
@ %def delta_three epsilon_three
@ This could be of some use:
<<Lorentz: public>>=
public :: identity
<<Lorentz: parameters>>=
type(lorentz_transformation_t), parameter :: &
& identity = &
& lorentz_transformation_t ( &
& reshape( source = [ one, zero, zero, zero, &
& zero, one, zero, zero, &
& zero, zero, one, zero, &
& zero, zero, zero, one ],&
& shape = [4,4] ) )
@ %def identity
<<Lorentz: public>>=
public :: space_reflection
<<Lorentz: parameters>>=
type(lorentz_transformation_t), parameter :: &
& space_reflection = &
& lorentz_transformation_t ( &
& reshape( source = [ one, zero, zero, zero, &
& zero,-one, zero, zero, &
& zero, zero,-one, zero, &
& zero, zero, zero,-one ],&
& shape = [4,4] ) )
@ %def space_reflection
@ Builds a unit vector orthogal to the input vector in the xy-plane.
<<Lorentz: public functions>>=
public :: create_orthogonal
<<Lorentz: procedures>>=
function create_orthogonal (p_in) result (p_out)
type(vector3_t), intent(in) :: p_in
type(vector3_t) :: p_out
real(default) :: ab
ab = sqrt (p_in%p(1)**2 + p_in%p(2)**2)
if (abs (ab) < eps0) then
p_out%p(1) = 1
p_out%p(2) = 0
p_out%p(3) = 0
else
p_out%p(1) = p_in%p(2)
p_out%p(2) = -p_in%p(1)
p_out%p(3) = 0
p_out = p_out / ab
end if
end function create_orthogonal
@ %def create_orthogonal
@
<<Lorentz: public functions>>=
public :: create_unit_vector
<<Lorentz: procedures>>=
function create_unit_vector (p_in) result (p_out)
type(vector4_t), intent(in) :: p_in
type(vector3_t) :: p_out
p_out%p = p_in%p(1:3) / space_part_norm (p_in)
end function create_unit_vector
@ %def create_unit_vector
@
<<Lorentz: public functions>>=
public :: normalize
<<Lorentz: procedures>>=
function normalize(p) result (p_norm)
type(vector3_t) :: p_norm
type(vector3_t), intent(in) :: p
real(default) :: abs
abs = sqrt (p%p(1)**2 + p%p(2)**2 + p%p(3)**2)
p_norm = p / abs
end function normalize
@ %def normalize
@ Computes the invariant mass of the momenta sum given by the indices in
[[i_res_born]] and the optional argument [[i_emitter]].
<<Lorentz: public>>=
public :: compute_resonance_mass
<<Lorentz: procedures>>=
pure function compute_resonance_mass (p, i_res_born, i_gluon) result (m)
real(default) :: m
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in), dimension(:) :: i_res_born
integer, intent(in), optional :: i_gluon
type(vector4_t) :: p_res
p_res = get_resonance_momentum (p, i_res_born, i_gluon)
m = p_res**1
end function compute_resonance_mass
@ %def compute_resonance_mass
@
<<Lorentz: public>>=
public :: get_resonance_momentum
<<Lorentz: procedures>>=
pure function get_resonance_momentum (p, i_res_born, i_gluon) result (p_res)
type(vector4_t) :: p_res
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in), dimension(:) :: i_res_born
integer, intent(in), optional :: i_gluon
integer :: i
p_res = vector4_null
do i = 1, size (i_res_born)
p_res = p_res + p (i_res_born(i))
end do
if (present (i_gluon)) p_res = p_res + p (i_gluon)
end function get_resonance_momentum
@ %def get_resonance_momentum
@
<<Lorentz: public>>=
public :: create_two_particle_decay
<<Lorentz: procedures>>=
function create_two_particle_decay (s, p1, p2) result (p_rest)
type(vector4_t), dimension(3) :: p_rest
real(default), intent(in) :: s
type(vector4_t), intent(in) :: p1, p2
real(default) :: m1_sq, m2_sq
real(default) :: E1, E2, p
m1_sq = p1**2; m2_sq = p2**2
p = sqrt (lambda (s, m1_sq, m2_sq)) / (two * sqrt (s))
E1 = sqrt (m1_sq + p**2); E2 = sqrt (m2_sq + p**2)
p_rest(1)%p = [sqrt (s), zero, zero, zero]
p_rest(2)%p(0) = E1
p_rest(2)%p(1:3) = p * p1%p(1:3) / space_part_norm (p1)
p_rest(3)%p(0) = E2; p_rest(3)%p(1:3) = -p_rest(2)%p(1:3)
end function create_two_particle_decay
@ %def create_two_particle_decay
@ This function creates a phase-space point for a $1 \to 3$ decay in
the decaying particle's rest frame. There are three rest frames for
this system, corresponding to $s$-, $t$,- and $u$-channel momentum
exchange, also referred to as Gottfried-Jackson frames. Below, we choose
the momentum with index 1 to be aligned along the $z$-axis. We then
have
\begin{align*}
s_1 &= \left(p_1 + p_2\right)^2, \\
s_2 &= \left(p_2 + p_3\right)^2, \\
s_3 &= \left(p_1 + p_3\right)^2, \\
s_1 + s_2 + s_3 &= s + m_1^2 + m_2^2 + m_3^2.
\end{align*}
From these we can construct
\begin{align*}
E_1^{R23} = \frac{s - s_2 - m_1^2}{2\sqrt{s_2}} &\quad P_1^{R23} = \frac{\lambda^{1/2}(s, s_2, m_1^2)}{2\sqrt{s_2}},\\
E_2^{R23} = \frac{s_2 + m_2^2 - m_3^2}{2\sqrt{s_2}} &\quad P_2^{R23} = \frac{\lambda^{1/2}(s_2, m_2^2, m_3^2)}{2\sqrt{s_2}},\\
E_3^{R23} = \frac{s_2 + m_3^2 - m_2^2}{2\sqrt{s_2}} &\quad P_3^{R23} = P_2^{R23},
\end{align*}
where $R23$ denotes the Gottfried-Jackson frame of our choice. Finally, the scattering angle $\theta_{12}^{R23}$ between
momentum $1$ and $2$ can be determined to be
\begin{equation*}
\cos\theta_{12}^{R23} = \frac{(s - s_2 - m_1^2)(s_2 + m_2^2 - m_3^2) + 2s_2 (m_1^2 + m_2^2 - s_1)}
{\lambda^{1/2}(s, s_2, m_1^2) \lambda^{1/2}(s_2, m_2^2, m_3^2)}
\end{equation*}
<<Lorentz: public>>=
public :: create_three_particle_decay
<<Lorentz: procedures>>=
function create_three_particle_decay (p1, p2, p3) result (p_rest)
type(vector4_t), dimension(4) :: p_rest
type(vector4_t), intent(in) :: p1, p2, p3
real(default) :: E1, E2, E3
real(default) :: pr1, pr2, pr3
real(default) :: s, s1, s2, s3
real(default) :: m1_sq, m2_sq, m3_sq
real(default) :: cos_theta_12
type(vector3_t) :: v3_unit
type(lorentz_transformation_t) :: rot
m1_sq = p1**2
m2_sq = p2**2
m3_sq = p3**2
s1 = (p1 + p2)**2
s2 = (p2 + p3)**2
s3 = (p3 + p1)**2
s = s1 + s2 + s3 - m1_sq - m2_sq - m3_sq
E1 = (s - s2 - m1_sq) / (two * sqrt (s2))
E2 = (s2 + m2_sq - m3_sq) / (two * sqrt (s2))
E3 = (s2 + m3_sq - m2_sq) / (two * sqrt (s2))
pr1 = sqrt (lambda (s, s2, m1_sq)) / (two * sqrt (s2))
pr2 = sqrt (lambda (s2, m2_sq, m3_sq)) / (two * sqrt(s2))
pr3 = pr2
cos_theta_12 = ((s - s2 - m1_sq) * (s2 + m2_sq - m3_sq) + two * s2 * (m1_sq + m2_sq - s1)) / &
sqrt (lambda (s, s2, m1_sq) * lambda (s2, m2_sq, m3_sq))
v3_unit%p = [zero, zero, one]
p_rest(1)%p(0) = E1
p_rest(1)%p(1:3) = v3_unit%p * pr1
p_rest(2)%p(0) = E2
p_rest(2)%p(1:3) = v3_unit%p * pr2
p_rest(3)%p(0) = E3
p_rest(3)%p(1:3) = v3_unit%p * pr3
p_rest(4)%p(0) = (s + s2 - m1_sq) / (2 * sqrt (s2))
p_rest(4)%p(1:3) = - p_rest(1)%p(1:3)
rot = rotation (cos_theta_12, sqrt (one - cos_theta_12**2), 2)
p_rest(2) = rot * p_rest(2)
p_rest(3)%p(1:3) = - p_rest(2)%p(1:3)
end function create_three_particle_decay
@ %def create_three_particle_decay
@
<<Lorentz: public>>=
public :: evaluate_one_to_two_splitting_special
<<Lorentz: interfaces>>=
abstract interface
subroutine evaluate_one_to_two_splitting_special (p_origin, &
p1_in, p2_in, p1_out, p2_out, msq_in, jac)
import
type(vector4_t), intent(in) :: p_origin
type(vector4_t), intent(in) :: p1_in, p2_in
type(vector4_t), intent(inout) :: p1_out, p2_out
real(default), intent(in), optional :: msq_in
real(default), intent(inout), optional :: jac
end subroutine evaluate_one_to_two_splitting_special
end interface
@ %def evaluate_one_to_two_splitting_special
@
<<Lorentz: public>>=
public :: generate_on_shell_decay
<<Lorentz: procedures>>=
recursive subroutine generate_on_shell_decay (p_dec, &
p_in, p_out, i_real, msq_in, jac, evaluate_special)
type(vector4_t), intent(in) :: p_dec
type(vector4_t), intent(in), dimension(:) :: p_in
type(vector4_t), intent(inout), dimension(:) :: p_out
integer, intent(in) :: i_real
real(default), intent(in), optional :: msq_in
real(default), intent(inout), optional :: jac
procedure(evaluate_one_to_two_splitting_special), intent(in), &
pointer, optional :: evaluate_special
type(vector4_t) :: p_dec_new
integer :: n_recoil
n_recoil = size (p_in) - 1
if (n_recoil > 1) then
if (present (evaluate_special)) then
call evaluate_special (p_dec, p_in(1), sum (p_in (2 : n_recoil + 1)), &
p_out(i_real), p_dec_new)
call generate_on_shell_decay (p_dec_new, p_in (2 : ), p_out, &
i_real + 1, msq_in, jac, evaluate_special)
else
call evaluate_one_to_two_splitting (p_dec, p_in(1), &
sum (p_in (2 : n_recoil + 1)), p_out(i_real), p_dec_new, msq_in, jac)
call generate_on_shell_decay (p_dec_new, p_in (2 : ), p_out, &
i_real + 1, msq_in, jac)
end if
else
call evaluate_one_to_two_splitting (p_dec, p_in(1), p_in(2), &
p_out(i_real), p_out(i_real + 1), msq_in, jac)
end if
end subroutine generate_on_shell_decay
subroutine evaluate_one_to_two_splitting (p_origin, &
p1_in, p2_in, p1_out, p2_out, msq_in, jac)
type(vector4_t), intent(in) :: p_origin
type(vector4_t), intent(in) :: p1_in, p2_in
type(vector4_t), intent(inout) :: p1_out, p2_out
real(default), intent(in), optional :: msq_in
real(default), intent(inout), optional :: jac
type(lorentz_transformation_t) :: L
type(vector4_t) :: p1_rest, p2_rest
real(default) :: m, msq, msq1, msq2
real(default) :: E1, E2, p
real(default) :: lda, rlda_soft
call get_rest_frame (p1_in, p2_in, p1_rest, p2_rest)
msq = p_origin**2; m = sqrt(msq)
msq1 = p1_in**2; msq2 = p2_in**2
lda = lambda (msq, msq1, msq2)
if (lda < zero) then
print *, 'Encountered lambda < 0 in 1 -> 2 splitting! '
print *, 'lda: ', lda
print *, 'm: ', m, 'msq: ', msq
print *, 'm1: ', sqrt (msq1), 'msq1: ', msq1
print *, 'm2: ', sqrt (msq2), 'msq2: ', msq2
stop
end if
p = sqrt (lda) / (two * m)
E1 = sqrt (msq1 + p**2)
E2 = sqrt (msq2 + p**2)
p1_out = shift_momentum (p1_rest, E1, p)
p2_out = shift_momentum (p2_rest, E2, p)
L = boost (p_origin, p_origin**1)
p1_out = L * p1_out
p2_out = L * p2_out
if (present (jac) .and. present (msq_in)) then
jac = jac * sqrt(lda) / msq
rlda_soft = sqrt (lambda (msq_in, msq1, msq2))
!!! We have to undo the Jacobian which has already been
!!! supplied by the Born phase space.
jac = jac * msq_in / rlda_soft
end if
contains
subroutine get_rest_frame (p1_in, p2_in, p1_out, p2_out)
type(vector4_t), intent(in) :: p1_in, p2_in
type(vector4_t), intent(out) :: p1_out, p2_out
type(lorentz_transformation_t) :: L
L = inverse (boost (p1_in + p2_in, (p1_in + p2_in)**1))
p1_out = L * p1_in; p2_out = L * p2_in
end subroutine get_rest_frame
function shift_momentum (p_in, E, p) result (p_out)
type(vector4_t) :: p_out
type(vector4_t), intent(in) :: p_in
real(default), intent(in) :: E, p
type(vector3_t) :: vec
vec = p_in%p(1:3) / space_part_norm (p_in)
p_out = vector4_moving (E, p * vec)
end function shift_momentum
end subroutine evaluate_one_to_two_splitting
@ %def generate_on_shell_decay
@
\subsection{Boosts}
We build Lorentz transformations from boosts and rotations. In both
cases we can supply a three-vector which defines the axis and (hyperbolic)
angle. For a boost, this is the vector $\vec\beta=\vec p/E$,
such that a particle at rest with mass $m$ is boosted to a particle
with three-vector $\vec p$. Here, we have
\begin{equation}
\beta = \tanh\chi = p/E, \qquad
\gamma = \cosh\chi = E/m, \qquad
\beta\gamma = \sinh\chi = p/m
\end{equation}
<<Lorentz: public functions>>=
public :: boost
<<Lorentz: interfaces>>=
interface boost
module procedure boost_from_rest_frame
module procedure boost_from_rest_frame_vector3
module procedure boost_generic
module procedure boost_canonical
end interface
@ %def boost
@ In the first form, the argument is some four-momentum, the space
part of which determines a direction, and the associated mass (which
is not checked against the four-momentum). The boost vector
$\gamma\vec\beta$ is then given by $\vec p/m$. This boosts from the
rest frame of a particle to the current frame. To be explicit, if
$\vec p$ is the momentum of a particle and $m$ its mass, $L(\vec p/m)$
is the transformation that turns $(m;\vec 0)$ into $(E;\vec p)$.
Conversely, the inverse transformation boosts a vector \emph{into} the
rest frame of a particle, in particular $(E;\vec p)$ into $(m;\vec
0)$.
<<Lorentz: procedures>>=
elemental function boost_from_rest_frame (p, m) result (L)
type(lorentz_transformation_t) :: L
type(vector4_t), intent(in) :: p
real(default), intent(in) :: m
L = boost_from_rest_frame_vector3 (space_part (p), m)
end function boost_from_rest_frame
elemental function boost_from_rest_frame_vector3 (p, m) result (L)
type(lorentz_transformation_t) :: L
type(vector3_t), intent(in) :: p
real(default), intent(in) :: m
type(vector3_t) :: beta_gamma
real(default) :: bg2, g, c
integer :: i,j
if (m > eps0) then
beta_gamma = p / m
bg2 = beta_gamma**2
else
bg2 = 0
L = identity
return
end if
if (bg2 > eps0) then
g = sqrt(1 + bg2); c = (g-1)/bg2
else
g = one + bg2 / two
c = one / two
end if
L%L(0,0) = g
L%L(0,1:) = beta_gamma%p
L%L(1:,0) = L%L(0,1:)
do i=1,3
do j=1,3
L%L(i,j) = delta_three(i,j) + c*beta_gamma%p(i)*beta_gamma%p(j)
end do
end do
end function boost_from_rest_frame_vector3
@ %def boost_from_rest_frame
@ A canonical boost is a boost along one of the coordinate axes, which
we may supply as an integer argument. Here, $\gamma\beta$ is scalar.
<<Lorentz: procedures>>=
elemental function boost_canonical (beta_gamma, k) result (L)
type(lorentz_transformation_t) :: L
real(default), intent(in) :: beta_gamma
integer, intent(in) :: k
real(default) :: g
g = sqrt(1 + beta_gamma**2)
L = identity
L%L(0,0) = g
L%L(0,k) = beta_gamma
L%L(k,0) = L%L(0,k)
L%L(k,k) = L%L(0,0)
end function boost_canonical
@ %def boost_canonical
@ Instead of a canonical axis, we can supply an arbitrary axis which
need not be normalized. If it is zero, return the unit matrix.
<<Lorentz: procedures>>=
elemental function boost_generic (beta_gamma, axis) result (L)
type(lorentz_transformation_t) :: L
real(default), intent(in) :: beta_gamma
type(vector3_t), intent(in) :: axis
if (any (abs (axis%p) > 0)) then
L = boost_from_rest_frame_vector3 (beta_gamma * axis, axis**1)
else
L = identity
end if
end function boost_generic
@ %def boost_generic
@
\subsection{Rotations}
For a rotation, the vector defines the rotation axis, and its length
-the rotation angle.
+the rotation angle. All of these rotations rotate counterclockwise
+in a right-handed coordinate system.
<<Lorentz: public functions>>=
public :: rotation
<<Lorentz: interfaces>>=
interface rotation
module procedure rotation_generic
module procedure rotation_canonical
module procedure rotation_generic_cs
module procedure rotation_canonical_cs
end interface
@ %def rotation
@ If $\cos\phi$ and $\sin\phi$ is already known, we do not have to
calculate them. Of course, the user has to ensure that
$\cos^2\phi+\sin^2\phi=1$, and that the given axis [[n]] is normalized to
one. In the second form, the length of [[axis]] is the rotation
angle.
<<Lorentz: procedures>>=
elemental function rotation_generic_cs (cp, sp, axis) result (R)
type(lorentz_transformation_t) :: R
real(default), intent(in) :: cp, sp
type(vector3_t), intent(in) :: axis
integer :: i,j
R = identity
do i=1,3
do j=1,3
R%L(i,j) = cp*delta_three(i,j) + (1-cp)*axis%p(i)*axis%p(j) &
& - sp*dot_product(epsilon_three(i,j,:), axis%p)
end do
end do
end function rotation_generic_cs
elemental function rotation_generic (axis) result (R)
type(lorentz_transformation_t) :: R
type(vector3_t), intent(in) :: axis
real(default) :: phi
if (any (abs(axis%p) > 0)) then
phi = abs(axis**1)
R = rotation_generic_cs (cos(phi), sin(phi), axis/phi)
else
R = identity
end if
end function rotation_generic
@ %def rotation_generic_cs rotation_generic
@ Alternatively, give just the angle and label the coordinate axis by
an integer.
<<Lorentz: procedures>>=
elemental function rotation_canonical_cs (cp, sp, k) result (R)
type(lorentz_transformation_t) :: R
real(default), intent(in) :: cp, sp
integer, intent(in) :: k
integer :: i,j
R = identity
do i=1,3
do j=1,3
R%L(i,j) = -sp*epsilon_three(i,j,k)
end do
R%L(i,i) = cp
end do
R%L(k,k) = 1
end function rotation_canonical_cs
elemental function rotation_canonical (phi, k) result (R)
type(lorentz_transformation_t) :: R
real(default), intent(in) :: phi
integer, intent(in) :: k
R = rotation_canonical_cs(cos(phi), sin(phi), k)
end function rotation_canonical
@ %def rotation_canonical_cs rotation_canonical
@
This is viewed as a method for the first argument (three-vector):
Reconstruct the rotation that rotates it into the second three-vector.
<<Lorentz: public functions>>=
public :: rotation_to_2nd
<<Lorentz: interfaces>>=
interface rotation_to_2nd
module procedure rotation_to_2nd_generic
module procedure rotation_to_2nd_canonical
end interface
<<Lorentz: procedures>>=
elemental function rotation_to_2nd_generic (p, q) result (R)
type(lorentz_transformation_t) :: R
type(vector3_t), intent(in) :: p, q
type(vector3_t) :: a, b, ab
real(default) :: ct, st
if (any (abs (p%p) > 0) .and. any (abs (q%p) > 0)) then
a = direction (p)
b = direction (q)
ab = cross_product(a,b)
ct = a * b; st = ab**1
if (abs(st) > eps0) then
R = rotation_generic_cs (ct, st, ab / st)
else if (ct < 0) then
R = space_reflection
else
R = identity
end if
else
R = identity
end if
end function rotation_to_2nd_generic
@ %def rotation_to_2nd_generic
@
The same for a canonical axis: The function returns the transformation that
rotates the $k$-axis into the direction of $p$.
<<Lorentz: procedures>>=
elemental function rotation_to_2nd_canonical (k, p) result (R)
type(lorentz_transformation_t) :: R
integer, intent(in) :: k
type(vector3_t), intent(in) :: p
type(vector3_t) :: b, ab
real(default) :: ct, st
integer :: i, j
if (any (abs (p%p) > 0)) then
b = direction (p)
ab%p = 0
do i = 1, 3
do j = 1, 3
ab%p(j) = ab%p(j) + b%p(i) * epsilon_three(i,j,k)
end do
end do
ct = b%p(k); st = ab**1
if (abs(st) > eps0) then
R = rotation_generic_cs (ct, st, ab / st)
else if (ct < 0) then
R = space_reflection
else
R = identity
end if
else
R = identity
end if
end function rotation_to_2nd_canonical
@ %def rotation_to_2nd_canonical
@
\subsection{Composite Lorentz transformations}
This function returns the transformation that, given a pair of vectors
$p_{1,2}$, (a) boosts from the rest frame of the c.m. system (with
invariant mass $m$) into the lab frame where $p_i$ are defined, and
(b) turns the given axis (or the canonical vectors $\pm
e_k$) in the rest frame into the directions of $p_{1,2}$ in the lab frame.
Note that the energy components are not used; for a
consistent result one should have $(p_1+p_2)^2 = m^2$.
<<Lorentz: public functions>>=
public :: transformation
<<Lorentz: interfaces>>=
interface transformation
module procedure transformation_rec_generic
module procedure transformation_rec_canonical
end interface
@ %def transformation
<<Lorentz: procedures>>=
elemental function transformation_rec_generic (axis, p1, p2, m) result (L)
type(vector3_t), intent(in) :: axis
type(vector4_t), intent(in) :: p1, p2
real(default), intent(in) :: m
type(lorentz_transformation_t) :: L
L = boost (p1 + p2, m)
L = L * rotation_to_2nd (axis, space_part (inverse (L) * p1))
end function transformation_rec_generic
elemental function transformation_rec_canonical (k, p1, p2, m) result (L)
integer, intent(in) :: k
type(vector4_t), intent(in) :: p1, p2
real(default), intent(in) :: m
type(lorentz_transformation_t) :: L
L = boost (p1 + p2, m)
L = L * rotation_to_2nd (k, space_part (inverse (L) * p1))
end function transformation_rec_canonical
@ %def transformation_rec_generic transformation_rec_canonical
@
\subsection{Applying Lorentz transformations}
Multiplying vectors and Lorentz transformations is straightforward.
<<Lorentz: interfaces>>=
interface operator(*)
module procedure prod_LT_vector4
module procedure prod_LT_LT
module procedure prod_vector4_LT
end interface
<<Lorentz: procedures>>=
elemental function prod_LT_vector4 (L, p) result (np)
type(vector4_t) :: np
type(lorentz_transformation_t), intent(in) :: L
type(vector4_t), intent(in) :: p
np%p = matmul (L%L, p%p)
end function prod_LT_vector4
elemental function prod_LT_LT (L1, L2) result (NL)
type(lorentz_transformation_t) :: NL
type(lorentz_transformation_t), intent(in) :: L1,L2
NL%L = matmul (L1%L, L2%L)
end function prod_LT_LT
elemental function prod_vector4_LT (p, L) result (np)
type(vector4_t) :: np
type(vector4_t), intent(in) :: p
type(lorentz_transformation_t), intent(in) :: L
np%p = matmul (p%p, L%L)
end function prod_vector4_LT
@ %def *
@
\subsection{Special Lorentz transformations}
These routines have their application in the generation and extraction
of angles in the phase-space sampling routine. Since this part of the
program is time-critical, we calculate the composition of
transformations directly instead of multiplying rotations and boosts.
This Lorentz transformation is the composition of a rotation by $\phi$
around the $3$ axis, a rotation by $\theta$ around the $2$ axis, and a
boost along the $3$ axis:
\begin{equation}
L = B_3(\beta\gamma)\,R_2(\theta)\,R_3(\phi)
\end{equation}
Instead of the angles we provide sine and cosine.
<<Lorentz: public functions>>=
public :: LT_compose_r3_r2_b3
<<Lorentz: procedures>>=
elemental function LT_compose_r3_r2_b3 &
(cp, sp, ct, st, beta_gamma) result (L)
type(lorentz_transformation_t) :: L
real(default), intent(in) :: cp, sp, ct, st, beta_gamma
real(default) :: gamma
if (abs(beta_gamma) < eps0) then
L%L(0,0) = 1
L%L(1:,0) = 0
L%L(0,1:) = 0
L%L(1,1:) = [ ct*cp, -ct*sp, st ]
L%L(2,1:) = [ sp, cp, zero ]
L%L(3,1:) = [ -st*cp, st*sp, ct ]
else
gamma = sqrt(1 + beta_gamma**2)
L%L(0,0) = gamma
L%L(1,0) = 0
L%L(2,0) = 0
L%L(3,0) = beta_gamma
L%L(0,1:) = beta_gamma * [ -st*cp, st*sp, ct ]
L%L(1,1:) = [ ct*cp, -ct*sp, st ]
L%L(2,1:) = [ sp, cp, zero ]
L%L(3,1:) = gamma * [ -st*cp, st*sp, ct ]
end if
end function LT_compose_r3_r2_b3
@ %def LT_compose_r3_r2_b3
@ Different ordering:
\begin{equation}
L = B_3(\beta\gamma)\,R_3(\phi)\,R_2(\theta)
\end{equation}
<<Lorentz: public functions>>=
public :: LT_compose_r2_r3_b3
<<Lorentz: procedures>>=
elemental function LT_compose_r2_r3_b3 &
(ct, st, cp, sp, beta_gamma) result (L)
type(lorentz_transformation_t) :: L
real(default), intent(in) :: ct, st, cp, sp, beta_gamma
real(default) :: gamma
if (abs(beta_gamma) < eps0) then
L%L(0,0) = 1
L%L(1:,0) = 0
L%L(0,1:) = 0
L%L(1,1:) = [ ct*cp, -sp, st*cp ]
L%L(2,1:) = [ ct*sp, cp, st*sp ]
L%L(3,1:) = [ -st , zero, ct ]
else
gamma = sqrt(1 + beta_gamma**2)
L%L(0,0) = gamma
L%L(1,0) = 0
L%L(2,0) = 0
L%L(3,0) = beta_gamma
L%L(0,1:) = beta_gamma * [ -st , zero, ct ]
L%L(1,1:) = [ ct*cp, -sp, st*cp ]
L%L(2,1:) = [ ct*sp, cp, st*sp ]
L%L(3,1:) = gamma * [ -st , zero, ct ]
end if
end function LT_compose_r2_r3_b3
@ %def LT_compose_r2_r3_b3
@ This function returns the previous Lorentz transformation applied to
an arbitrary four-momentum and extracts the space part of the result:
\begin{equation}
\vec n = [B_3(\beta\gamma)\,R_2(\theta)\,R_3(\phi)\,p]_{\rm space\ part}
\end{equation}
The second variant applies if there is no rotation
<<Lorentz: public functions>>=
public :: axis_from_p_r3_r2_b3, axis_from_p_b3
<<Lorentz: procedures>>=
elemental function axis_from_p_r3_r2_b3 &
(p, cp, sp, ct, st, beta_gamma) result (n)
type(vector3_t) :: n
type(vector4_t), intent(in) :: p
real(default), intent(in) :: cp, sp, ct, st, beta_gamma
real(default) :: gamma, px, py
px = cp * p%p(1) - sp * p%p(2)
py = sp * p%p(1) + cp * p%p(2)
n%p(1) = ct * px + st * p%p(3)
n%p(2) = py
n%p(3) = -st * px + ct * p%p(3)
if (abs(beta_gamma) > eps0) then
gamma = sqrt(1 + beta_gamma**2)
n%p(3) = n%p(3) * gamma + p%p(0) * beta_gamma
end if
end function axis_from_p_r3_r2_b3
elemental function axis_from_p_b3 (p, beta_gamma) result (n)
type(vector3_t) :: n
type(vector4_t), intent(in) :: p
real(default), intent(in) :: beta_gamma
real(default) :: gamma
n%p = p%p(1:3)
if (abs(beta_gamma) > eps0) then
gamma = sqrt(1 + beta_gamma**2)
n%p(3) = n%p(3) * gamma + p%p(0) * beta_gamma
end if
end function axis_from_p_b3
@ %def axis_from_p_r3_r2_b3 axis_from_p_b3
@
\subsection{Special functions}
The K\"all\'en function, mostly used for the phase space.
This is equivalent to $\lambda(x,y,z)=x^2+y^2+z^2-2xy-2xz-2yz$.
<<Lorentz: public functions>>=
public :: lambda
<<Lorentz: procedures>>=
elemental function lambda (m1sq, m2sq, m3sq)
real(default) :: lambda
real(default), intent(in) :: m1sq, m2sq, m3sq
lambda = (m1sq - m2sq - m3sq)**2 - 4*m2sq*m3sq
end function lambda
@ %def lambda
@ Return a pair of head-to-head colliding momenta, given the collider
energy, particle masses, and optionally the momentum of the
c.m. system.
<<Lorentz: public functions>>=
public :: colliding_momenta
<<Lorentz: procedures>>=
function colliding_momenta (sqrts, m, p_cm) result (p)
type(vector4_t), dimension(2) :: p
real(default), intent(in) :: sqrts
real(default), dimension(2), intent(in), optional :: m
real(default), intent(in), optional :: p_cm
real(default), dimension(2) :: dmsq
real(default) :: ch, sh
real(default), dimension(2) :: E0, p0
integer, dimension(2), parameter :: sgn = [1, -1]
if (abs(sqrts) < eps0) then
call msg_fatal (" Colliding beams: sqrts is zero (please set sqrts)")
p = vector4_null; return
else if (sqrts <= 0) then
call msg_fatal (" Colliding beams: sqrts is negative")
p = vector4_null; return
end if
if (present (m)) then
dmsq = sgn * (m(1)**2-m(2)**2)
E0 = (sqrts + dmsq/sqrts) / 2
if (any (E0 < m)) then
call msg_fatal &
(" Colliding beams: beam energy is less than particle mass")
p = vector4_null; return
end if
p0 = sgn * sqrt (E0**2 - m**2)
else
E0 = sqrts / 2
p0 = sgn * E0
end if
if (present (p_cm)) then
sh = p_cm / sqrts
ch = sqrt (1 + sh**2)
p = vector4_moving (E0 * ch + p0 * sh, E0 * sh + p0 * ch, 3)
else
p = vector4_moving (E0, p0, 3)
end if
end function colliding_momenta
@ %def colliding_momenta
@ This subroutine is for the purpose of numerical checks and
comparisons. The idea is to set a number to zero if it is numerically
equivalent with zero. The equivalence is established by comparing
with a [[tolerance]] argument. We implement this for vectors and
transformations.
<<Lorentz: public functions>>=
public :: pacify
<<Lorentz: interfaces>>=
interface pacify
module procedure pacify_vector3
module procedure pacify_vector4
module procedure pacify_LT
end interface pacify
<<Lorentz: procedures>>=
elemental subroutine pacify_vector3 (p, tolerance)
type(vector3_t), intent(inout) :: p
real(default), intent(in) :: tolerance
where (abs (p%p) < tolerance) p%p = zero
end subroutine pacify_vector3
elemental subroutine pacify_vector4 (p, tolerance)
type(vector4_t), intent(inout) :: p
real(default), intent(in) :: tolerance
where (abs (p%p) < tolerance) p%p = zero
end subroutine pacify_vector4
elemental subroutine pacify_LT (LT, tolerance)
type(lorentz_transformation_t), intent(inout) :: LT
real(default), intent(in) :: tolerance
where (abs (LT%L) < tolerance) LT%L = zero
end subroutine pacify_LT
@ %def pacify
@
<<Lorentz: public>>=
public :: vector_set_reshuffle
<<Lorentz: procedures>>=
subroutine vector_set_reshuffle (p1, list, p2)
type(vector4_t), intent(in), dimension(:), allocatable :: p1
integer, intent(in), dimension(:), allocatable :: list
type(vector4_t), intent(out), dimension(:), allocatable :: p2
integer :: n, n_p
n_p = size (p1)
if (size (list) /= n_p) return
allocate (p2 (n_p))
do n = 1, n_p
p2(n) = p1(list(n))
end do
end subroutine vector_set_reshuffle
@ %def vector_set_reshuffle
@
<<Lorentz: public>>=
public :: vector_set_is_cms
<<Lorentz: procedures>>=
function vector_set_is_cms (p, n_in) result (is_cms)
logical :: is_cms
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: n_in
integer :: i
type(vector4_t) :: p_sum
p_sum%p = 0._default
do i = 1, n_in
p_sum = p_sum + p(i)
end do
is_cms = all (abs (p_sum%p(1:3)) < tiny_07)
end function vector_set_is_cms
@ %def vector_set_is_cms
@
<<Lorentz: public>>=
public :: vector_set_is_lab
<<Lorentz: procedures>>=
function vector_set_is_lab (p, n_in) result (is_lab)
logical :: is_lab
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in) :: n_in
is_lab = .not. vector_set_is_cms (p, n_in)
end function vector_set_is_lab
@ %def vector_set_is_lab
@
<<Lorentz: public>>=
public :: vector4_write_set
<<Lorentz: procedures>>=
subroutine vector4_write_set (p, unit, show_mass, testflag, &
check_conservation, ultra, n_in)
type(vector4_t), intent(in), dimension(:) :: p
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_mass
logical, intent(in), optional :: testflag, ultra
logical, intent(in), optional :: check_conservation
integer, intent(in), optional :: n_in
logical :: extreme
integer :: i, j
real(default), dimension(0:3) :: p_tot
character(len=7) :: fmt
integer :: u
logical :: yorn, is_test
integer :: n
extreme = .false.; if (present (ultra)) extreme = ultra
is_test = .false.; if (present (testflag)) is_test = testflag
u = given_output_unit (unit); if (u < 0) return
n = 2; if (present (n_in)) n = n_in
p_tot = 0
yorn = .false.; if (present (check_conservation)) yorn = check_conservation
do i = 1, size (p)
if (yorn .and. i > n) then
forall (j=0:3) p_tot(j) = p_tot(j) - p(i)%p(j)
else
forall (j=0:3) p_tot(j) = p_tot(j) + p(i)%p(j)
end if
call vector4_write (p(i), u, show_mass=show_mass, &
testflag=testflag, ultra=ultra)
end do
if (extreme) then
call pac_fmt (fmt, FMT_19, FMT_11, testflag)
else
call pac_fmt (fmt, FMT_19, FMT_15, testflag)
end if
if (is_test) call pacify (p_tot, 1.E-9_default)
if (.not. is_test) then
write (u, "(A5)") 'Total: '
write (u, "(1x,A,1x," // fmt // ")") "E = ", p_tot(0)
write (u, "(1x,A,3(1x," // fmt // "))") "P = ", p_tot(1:)
end if
end subroutine vector4_write_set
@ %def vector4_write_set
@
<<Lorentz: public>>=
public :: vector4_check_momentum_conservation
<<Lorentz: procedures>>=
subroutine vector4_check_momentum_conservation (p, n_in, unit, &
abs_smallness, rel_smallness, verbose)
type(vector4_t), dimension(:), intent(in) :: p
integer, intent(in) :: n_in
integer, intent(in), optional :: unit
real(default), intent(in), optional :: abs_smallness, rel_smallness
logical, intent(in), optional :: verbose
integer :: u, i
type(vector4_t) :: psum_in, psum_out
logical, dimension(0:3) :: p_diff
logical :: verb
u = given_output_unit (unit); if (u < 0) return
verb = .false.; if (present (verbose)) verb = verbose
psum_in = vector4_null
do i = 1, n_in
psum_in = psum_in + p(i)
end do
psum_out = vector4_null
do i = n_in + 1, size (p)
psum_out = psum_out + p(i)
end do
!!! !!! !!! Workaround for gfortran-4.8.4 bug
do i = 0, 3
p_diff(i) = vanishes (psum_in%p(i) - psum_out%p(i), &
abs_smallness = abs_smallness, rel_smallness = rel_smallness)
end do
if (.not. all (p_diff)) then
call msg_warning ("Momentum conservation: FAIL", unit = u)
if (verb) then
write (u, "(A)") "Incoming:"
call vector4_write (psum_in, u)
write (u, "(A)") "Outgoing:"
call vector4_write (psum_out, u)
end if
else
if (verb) then
write (u, "(A)") "Momentum conservation: CHECK"
end if
end if
end subroutine vector4_check_momentum_conservation
@ %def vector4_check_momentum_conservation
@ This computes the quantities
\begin{align*}
\langle ij \rangle &= \sqrt{|S_{ij}|} e^{i\phi_{ij}},
[ij] &= \sqrt{|S_{ij}|} e^{\i\tilde{\phi}_{ij}},
\end{align*}
with $S_{ij} = \left(p_i + p_j\right)^2$. The phase space factor
$\phi_{ij}$ is determined by
\begin{align*}
\cos\phi_{ij} &= \frac{p_i^1p_j^+ - p_j^1p_i^+}{\sqrt{p_i^+p_j^+S_{ij}}},
\sin\phi_{ij} &= \frac{p_i^2p_j^+ - p_j^2p_i^+}{\sqrt{p_i^+p_j^+S_{ij}}}.
\end{align*}
After $\langle ij \rangle$ has been computed according to these
formulae, $[ij]$ can be obtained by using the relation $S_{ij} =
\langle ij \rangle [ji]$ and taking into account that $[ij] =
-[ji]$. Thus, a minus-sign has to be applied.
<<Lorentz: public>>=
public :: spinor_product
<<Lorentz: procedures>>=
subroutine spinor_product (p1, p2, prod1, prod2)
type(vector4_t), intent(in) :: p1, p2
complex(default), intent(out) :: prod1, prod2
real(default) :: sij
complex(default) :: phase
real(default) :: pp_1, pp_2
pp_1 = p1%p(0) + p1%p(3)
pp_2 = p2%p(0) + p2%p(3)
sij = (p1+p2)**2
phase = cmplx ((p1%p(1)*pp_2 - p2%p(1)*pp_1)/sqrt (sij*pp_1*pp_2), &
(p1%p(2)*pp_2 - p2%p(2)*pp_1)/sqrt (sij*pp_1*pp_2), &
default)
!!! <ij>
prod1 = sqrt (sij) * phase
!!! [ij]
if (abs(prod1) > 0) then
prod2 = - sij / prod1
else
prod2 = 0
end if
end subroutine spinor_product
@ %def spinor_product
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Special Physics functions}
Here, we declare functions that are specific for the Standard Model,
including QCD: fixed and running $\alpha_s$, Catani-Seymour
dipole terms, loop functions, etc.
To make maximum use of this, all functions, if possible, are declared
elemental (or pure, if this is not possible).
<<[[sm_physics.f90]]>>=
<<File header>>
module sm_physics
<<Use kinds>>
use io_units
use constants
use numeric_utils
use diagnostics
use physics_defs
use lorentz
<<Standard module head>>
<<SM physics: public>>
<<SM physics: parameters>>
contains
<<SM physics: procedures>>
end module sm_physics
@ %def sm_physics
@
\subsection{Running $\alpha_s$}
@ Then we define the coefficients of the beta function of QCD (as a
reference cf. the Particle Data Group), where $n_f$ is the number of
active flavors in two different schemes:
\begin{align}
\beta_0 &=\; 11 - \frac23 n_f \\
\beta_1 &=\; 51 - \frac{19}{3} n_f \\
\beta_2 &=\; 2857 - \frac{5033}{9} n_f + \frac{325}{27} n_f^2
\end{align}
\begin{align}
b_0 &=\; \frac{1}{12 \pi} \left( 11 C_A - 2 n_f \right) \\
b_1 &=\; \frac{1}{24 \pi^2} \left( 17 C_A^2 - 5 C_A n_f - 3 C_F n_f \right) \\
b_2 &=\; \frac{1}{(4\pi)^3} \biggl( \frac{2857}{54} C_A^3 -
\frac{1415}{54} * C_A^2 n_f - \frac{205}{18} C_A C_F n_f + C_F^2 n_f
+ \frac{79}{54} C_A n_f**2 + \frac{11}{9} C_F n_f**2 \biggr)
\end{align}
<<SM physics: public>>=
public :: beta0, beta1, beta2, coeff_b0, coeff_b1, coeff_b2
<<SM physics: procedures>>=
pure function beta0 (nf)
real(default), intent(in) :: nf
real(default) :: beta0
beta0 = 11.0_default - two/three * nf
end function beta0
pure function beta1 (nf)
real(default), intent(in) :: nf
real(default) :: beta1
beta1 = 51.0_default - 19.0_default/three * nf
end function beta1
pure function beta2 (nf)
real(default), intent(in) :: nf
real(default) :: beta2
beta2 = 2857.0_default - 5033.0_default / 9.0_default * &
nf + 325.0_default/27.0_default * nf**2
end function beta2
pure function coeff_b0 (nf)
real(default), intent(in) :: nf
real(default) :: coeff_b0
coeff_b0 = (11.0_default * CA - two * nf) / (12.0_default * pi)
end function coeff_b0
pure function coeff_b1 (nf)
real(default), intent(in) :: nf
real(default) :: coeff_b1
coeff_b1 = (17.0_default * CA**2 - five * CA * nf - three * CF * nf) / &
(24.0_default * pi**2)
end function coeff_b1
pure function coeff_b2 (nf)
real(default), intent(in) :: nf
real(default) :: coeff_b2
coeff_b2 = (2857.0_default/54.0_default * CA**3 - &
1415.0_default/54.0_default * &
CA**2 * nf - 205.0_default/18.0_default * CA*CF*nf &
+ 79.0_default/54.0_default * CA*nf**2 + &
11.0_default/9.0_default * CF * nf**2) / (four*pi)**3
end function coeff_b2
@ %def beta0 beta1 beta2
@ %def coeff_b0 coeff_b1 coeff_b2
@ There should be two versions of running $\alpha_s$, one which takes
the scale and $\Lambda_{\text{QCD}}$ as input, and one which takes the
scale and e.g. $\alpha_s(m_Z)$ as input. Here, we take the one which
takes the QCD scale and scale as inputs from the PDG book.
<<SM physics: public>>=
public :: running_as, running_as_lam
<<SM physics: procedures>>=
pure function running_as (scale, al_mz, mz, order, nf) result (ascale)
real(default), intent(in) :: scale
real(default), intent(in), optional :: al_mz, nf, mz
integer, intent(in), optional :: order
integer :: ord
real(default) :: az, m_z, as_log, n_f, b0, b1, b2, ascale
real(default) :: as0, as1
if (present (mz)) then
m_z = mz
else
m_z = MZ_REF
end if
if (present (order)) then
ord = order
else
ord = 0
end if
if (present (al_mz)) then
az = al_mz
else
az = ALPHA_QCD_MZ_REF
end if
if (present (nf)) then
n_f = nf
else
n_f = 5
end if
b0 = coeff_b0 (n_f)
b1 = coeff_b1 (n_f)
b2 = coeff_b2 (n_f)
as_log = one + b0 * az * log(scale**2/m_z**2)
as0 = az / as_log
as1 = as0 - as0**2 * b1/b0 * log(as_log)
select case (ord)
case (0)
ascale = as0
case (1)
ascale = as1
case (2)
ascale = as1 + as0**3 * (b1**2/b0**2 * ((log(as_log))**2 - &
log(as_log) + as_log - one) - b2/b0 * (as_log - one))
case default
ascale = as0
end select
end function running_as
pure function running_as_lam (nf, scale, lambda, order) result (ascale)
real(default), intent(in) :: nf, scale
real(default), intent(in), optional :: lambda
integer, intent(in), optional :: order
real(default) :: lambda_qcd
real(default) :: as0, as1, logmul, b0, b1, b2, ascale
integer :: ord
if (present (lambda)) then
lambda_qcd = lambda
else
lambda_qcd = LAMBDA_QCD_REF
end if
if (present (order)) then
ord = order
else
ord = 0
end if
b0 = beta0(nf)
logmul = log(scale**2/lambda_qcd**2)
as0 = four*pi / b0 / logmul
if (ord > 0) then
b1 = beta1(nf)
as1 = as0 * (one - two* b1 / b0**2 * log(logmul) / logmul)
end if
select case (ord)
case (0)
ascale = as0
case (1)
ascale = as1
case (2)
b2 = beta2(nf)
ascale = as1 + as0 * four * b1**2/b0**4/logmul**2 * &
((log(logmul) - 0.5_default)**2 + &
b2*b0/8.0_default/b1**2 - five/four)
case default
ascale = as0
end select
end function running_as_lam
@ %def running_as
@ %def running_as_lam
@
\subsection{Catani-Seymour Parameters}
These are fundamental constants of the Catani-Seymour dipole formalism.
Since the corresponding parameters for the gluon case depend on the
number of flavors which is treated as an argument, there we do have
functions and not parameters.
\begin{equation}
\gamma_q = \gamma_{\bar q} = \frac{3}{2} C_F \qquad \gamma_g =
\frac{11}{6} C_A - \frac{2}{3} T_R N_f
\end{equation}
\begin{equation}
K_q = K_{\bar q} = \left( \frac{7}{2} - \frac{\pi^2}{6} \right) C_F \qquad
K_g = \left( \frac{67}{18} - \frac{\pi^2}{6} \right) C_A -
\frac{10}{9} T_R N_f
\end{equation}
<<SM physics: parameters>>=
real(kind=default), parameter, public :: gamma_q = three/two * CF, &
k_q = (7.0_default/two - pi**2/6.0_default) * CF
@ %def gamma_q
@
<<SM physics: public>>=
public :: gamma_g, k_g
<<SM physics: procedures>>=
elemental function gamma_g (nf) result (gg)
real(kind=default), intent(in) :: nf
real(kind=default) :: gg
gg = 11.0_default/6.0_default * CA - two/three * TR * nf
end function gamma_g
elemental function k_g (nf) result (kg)
real(kind=default), intent(in) :: nf
real(kind=default) :: kg
kg = (67.0_default/18.0_default - pi**2/6.0_default) * CA - &
10.0_default/9.0_default * TR * nf
end function k_g
@ %def gamma_g
@ %def k_g
@
\subsection{Mathematical Functions}
The dilogarithm. This simplified version is bound to double
precision, and restricted to argument values less or equal to unity,
so we do not need complex algebra. The wrapper converts it to default
precision (which is, of course, a no-op if double=default).
The routine calculates the dilogarithm through mapping on the area
where there is a quickly convergent series (adapted from an F77
routine by Hans Kuijf, 1988): Map $x$ such that $x$ is not in the
neighbourhood of $1$. Note that $|z|=-\ln(1-x)$ is always smaller
than $1.10$, but $\frac{1.10^{19}}{19!}{\rm Bernoulli}_{19}=2.7\times
10^{-15}$.
<<SM physics: public>>=
public :: Li2
<<SM physics: procedures>>=
elemental function Li2 (x)
use kinds, only: double
real(default), intent(in) :: x
real(default) :: Li2
Li2 = real( Li2_double (real(x, kind=double)), kind=default)
end function Li2
@ %def: Li2
@
<<SM physics: procedures>>=
elemental function Li2_double (x) result (Li2)
use kinds, only: double
real(kind=double), intent(in) :: x
real(kind=double) :: Li2
real(kind=double), parameter :: pi2_6 = pi**2/6
if (abs(1-x) < tiny_07) then
Li2 = pi2_6
else if (abs(1-x) < 0.5_double) then
Li2 = pi2_6 - log(1-x) * log(x) - Li2_restricted (1-x)
else if (abs(x) > 1.d0) then
! Li2 = 0
! call msg_bug (" Dilogarithm called outside of defined range.")
!!! Reactivate Dilogarithm identity
Li2 = -pi2_6 - 0.5_default * log(-x) * log(-x) - Li2_restricted (1/x)
else
Li2 = Li2_restricted (x)
end if
contains
elemental function Li2_restricted (x) result (Li2)
real(kind=double), intent(in) :: x
real(kind=double) :: Li2
real(kind=double) :: tmp, z, z2
z = - log (1-x)
z2 = z**2
! Horner's rule for the powers z^3 through z^19
tmp = 43867._double/798._double
tmp = tmp * z2 /342._double - 3617._double/510._double
tmp = tmp * z2 /272._double + 7._double/6._double
tmp = tmp * z2 /210._double - 691._double/2730._double
tmp = tmp * z2 /156._double + 5._double/66._double
tmp = tmp * z2 /110._double - 1._double/30._double
tmp = tmp * z2 / 72._double + 1._double/42._double
tmp = tmp * z2 / 42._double - 1._double/30._double
tmp = tmp * z2 / 20._double + 1._double/6._double
! The first three terms of the power series
Li2 = z2 * z * tmp / 6._double - 0.25_double * z2 + z
end function Li2_restricted
end function Li2_double
@ %def Li2_double
@
\subsection{Loop Integrals}
These functions appear in the calculation of the effective one-loop coupling of
a (pseudo)scalar to a vector boson pair.
<<SM physics: public>>=
public :: faux
<<SM physics: procedures>>=
elemental function faux (x) result (y)
real(default), intent(in) :: x
complex(default) :: y
if (1 <= x) then
y = asin(sqrt(1/x))**2
else
y = - 1/4.0_default * (log((1 + sqrt(1 - x))/ &
(1 - sqrt(1 - x))) - cmplx (0.0_default, pi, kind=default))**2
end if
end function faux
@ %def faux
@
<<SM physics: public>>=
public :: fonehalf
<<SM physics: procedures>>=
elemental function fonehalf (x) result (y)
real(default), intent(in) :: x
complex(default) :: y
if (abs(x) < eps0) then
y = 0
else
y = - 2.0_default * x * (1 + (1 - x) * faux(x))
end if
end function fonehalf
@ %def fonehalf
@
<<SM physics: public>>=
public :: fonehalf_pseudo
<<SM physics: procedures>>=
function fonehalf_pseudo (x) result (y)
real(default), intent(in) :: x
complex(default) :: y
if (abs(x) < eps0) then
y = 0
else
y = - 2.0_default * x * faux(x)
end if
end function fonehalf_pseudo
@ %def fonehalf_pseudo
@
<<SM physics: public>>=
public :: fone
<<SM physics: procedures>>=
elemental function fone (x) result (y)
real(default), intent(in) :: x
complex(default) :: y
if (abs(x) < eps0) then
y = 2.0_default
else
y = 2.0_default + 3.0_default * x + &
3.0_default * x * (2.0_default - x) * &
faux(x)
end if
end function fone
@ %def fone
@
<<SM physics: public>>=
public :: gaux
<<SM physics: procedures>>=
elemental function gaux (x) result (y)
real(default), intent(in) :: x
complex(default) :: y
if (1 <= x) then
y = sqrt(x - 1) * asin(sqrt(1/x))
else
y = sqrt(1 - x) * (log((1 + sqrt(1 - x)) / &
(1 - sqrt(1 - x))) - &
cmplx (0.0_default, pi, kind=default)) / 2.0_default
end if
end function gaux
@ %def gaux
@
<<SM physics: public>>=
public :: tri_i1
<<SM physics: procedures>>=
elemental function tri_i1 (a,b) result (y)
real(default), intent(in) :: a,b
complex(default) :: y
if (a < eps0 .or. b < eps0) then
y = 0
else
y = a*b/2.0_default/(a-b) + a**2 * b**2/2.0_default/(a-b)**2 * &
(faux(a) - faux(b)) + &
a**2 * b/(a-b)**2 * (gaux(a) - gaux(b))
end if
end function tri_i1
@ %def tri_i1
@
<<SM physics: public>>=
public :: tri_i2
<<SM physics: procedures>>=
elemental function tri_i2 (a,b) result (y)
real(default), intent(in) :: a,b
complex(default) :: y
if (a < eps0 .or. b < eps0) then
y = 0
else
y = - a * b / 2.0_default / (a-b) * (faux(a) - faux(b))
end if
end function tri_i2
@ %def tri_i2
@
\subsection{More on $\alpha_s$}
These functions are for the running of the strong coupling constants,
$\alpha_s$.
<<SM physics: public>>=
public :: run_b0
<<SM physics: procedures>>=
elemental function run_b0 (nf) result (bnull)
integer, intent(in) :: nf
real(default) :: bnull
bnull = 33.0_default - 2.0_default * nf
end function run_b0
@ %def run_b0
@
<<SM physics: public>>=
public :: run_b1
<<SM physics: procedures>>=
elemental function run_b1 (nf) result (bone)
integer, intent(in) :: nf
real(default) :: bone
bone = 6.0_default * (153.0_default - 19.0_default * nf)/run_b0(nf)**2
end function run_b1
@ %def run_b1
@
<<SM physics: public>>=
public :: run_aa
<<SM physics: procedures>>=
elemental function run_aa (nf) result (aaa)
integer, intent(in) :: nf
real(default) :: aaa
aaa = 12.0_default * PI / run_b0(nf)
end function run_aa
@ %def run_aa
@
<<SM physics: pubic functions>>=
public :: run_bb
<<SM physics: procedures>>=
elemental function run_bb (nf) result (bbb)
integer, intent(in) :: nf
real(default) :: bbb
bbb = run_b1(nf) / run_aa(nf)
end function run_bb
@ %def run_bb
@
\subsection{Functions for Catani-Seymour dipoles}
For the automated Catani-Seymour dipole subtraction, we need the
following functions.
<<SM physics: public>>=
public :: ff_dipole
<<SM physics: procedures>>=
pure subroutine ff_dipole (v_ijk,y_ijk,p_ij,pp_k,p_i,p_j,p_k)
type(vector4_t), intent(in) :: p_i, p_j, p_k
type(vector4_t), intent(out) :: p_ij, pp_k
real(kind=default), intent(out) :: y_ijk
real(kind=default) :: z_i
real(kind=default), intent(out) :: v_ijk
z_i = (p_i*p_k) / ((p_k*p_j) + (p_k*p_i))
y_ijk = (p_i*p_j) / ((p_i*p_j) + (p_i*p_k) + (p_j*p_k))
p_ij = p_i + p_j - y_ijk/(1.0_default - y_ijk) * p_k
pp_k = (1.0/(1.0_default - y_ijk)) * p_k
!!! We don't multiply by alpha_s right here:
v_ijk = 8.0_default * PI * CF * &
(2.0 / (1.0 - z_i*(1.0 - y_ijk)) - (1.0 + z_i))
end subroutine ff_dipole
@ %def ff_dipole
@
<<SM physics: public>>=
public :: fi_dipole
<<SM physics: procedures>>=
pure subroutine fi_dipole (v_ija,x_ija,p_ij,pp_a,p_i,p_j,p_a)
type(vector4_t), intent(in) :: p_i, p_j, p_a
type(vector4_t), intent(out) :: p_ij, pp_a
real(kind=default), intent(out) :: x_ija
real(kind=default) :: z_i
real(kind=default), intent(out) :: v_ija
z_i = (p_i*p_a) / ((p_a*p_j) + (p_a*p_i))
x_ija = ((p_i*p_a) + (p_j*p_a) - (p_i*p_j)) &
/ ((p_i*p_a) + (p_j*p_a))
p_ij = p_i + p_j - (1.0_default - x_ija) * p_a
pp_a = x_ija * p_a
!!! We don't not multiply by alpha_s right here:
v_ija = 8.0_default * PI * CF * &
(2.0 / (1.0 - z_i + (1.0 - x_ija)) - (1.0 + z_i)) / x_ija
end subroutine fi_dipole
@ %def fi_dipole
@
<<SM physics: public>>=
public :: if_dipole
<<SM physics: procedures>>=
pure subroutine if_dipole (v_kja,u_j,p_aj,pp_k,p_k,p_j,p_a)
type(vector4_t), intent(in) :: p_k, p_j, p_a
type(vector4_t), intent(out) :: p_aj, pp_k
real(kind=default), intent(out) :: u_j
real(kind=default) :: x_kja
real(kind=default), intent(out) :: v_kja
u_j = (p_a*p_j) / ((p_a*p_j) + (p_a*p_k))
x_kja = ((p_a*p_k) + (p_a*p_j) - (p_j*p_k)) &
/ ((p_a*p_j) + (p_a*p_k))
p_aj = x_kja * p_a
pp_k = p_k + p_j - (1.0_default - x_kja) * p_a
v_kja = 8.0_default * PI * CF * &
(2.0 / (1.0 - x_kja + u_j) - (1.0 + x_kja)) / x_kja
end subroutine if_dipole
@ %def if_dipole
@ This function depends on a variable number of final state particles
whose kinematics all get changed by the initial-initial dipole insertion.
<<SM physics: public>>=
public :: ii_dipole
<<SM physics: procedures>>=
pure subroutine ii_dipole (v_jab,v_j,p_in,p_out,flag_1or2)
type(vector4_t), dimension(:), intent(in) :: p_in
type(vector4_t), dimension(size(p_in)-1), intent(out) :: p_out
logical, intent(in) :: flag_1or2
real(kind=default), intent(out) :: v_j
real(kind=default), intent(out) :: v_jab
type(vector4_t) :: p_a, p_b, p_j
type(vector4_t) :: k, kk
type(vector4_t) :: p_aj
real(kind=default) :: x_jab
integer :: i
!!! flag_1or2 decides whether this a 12 or 21 dipole
if (flag_1or2) then
p_a = p_in(1)
p_b = p_in(2)
else
p_b = p_in(1)
p_a = p_in(2)
end if
!!! We assume that the unresolved particle has always the last
!!! momentum
p_j = p_in(size(p_in))
x_jab = ((p_a*p_b) - (p_a*p_j) - (p_b*p_j)) / (p_a*p_b)
v_j = (p_a*p_j) / (p_a * p_b)
p_aj = x_jab * p_a
k = p_a + p_b - p_j
kk = p_aj + p_b
do i = 3, size(p_in)-1
p_out(i) = p_in(i) - 2.0*((k+kk)*p_in(i))/((k+kk)*(k+kk)) * (k+kk) + &
(2.0 * (k*p_in(i)) / (k*k)) * kk
end do
if (flag_1or2) then
p_out(1) = p_aj
p_out(2) = p_b
else
p_out(1) = p_b
p_out(2) = p_aj
end if
v_jab = 8.0_default * PI * CF * &
(2.0 / (1.0 - x_jab) - (1.0 + x_jab)) / x_jab
end subroutine ii_dipole
@ %def ii_dipole
@
\subsection{Distributions for integrated dipoles and such}
Note that the following formulae are only meaningful for
$0 \leq x \leq 1$.
The Dirac delta distribution, modified for Monte-Carlo sampling,
centered at $x=1-\frac{\epsilon}{2}$:
<<SM physics: public>>=
public :: delta
<<SM physics: procedures>>=
elemental function delta (x,eps) result (z)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: z
if (x > one - eps) then
z = one / eps
else
z = 0
end if
end function delta
@ %def delta
@ The $+$-distribution, $P_+(x) = \left( \frac{1}{1-x}\right)_+$, for
the regularization of soft-collinear singularities. The constant part
for the Monte-Carlo sampling is the integral over the splitting
function divided by the weight for the WHIZARD numerical integration
over the interval.
<<SM physics: public>>=
public :: plus_distr
<<SM physics: procedures>>=
elemental function plus_distr (x,eps) result (plusd)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: plusd
if (x > one - eps) then
plusd = log(eps) / eps
else
plusd = one / (one - x)
end if
end function plus_distr
@ %def plus_distr
@ The splitting function in $D=4$ dimensions, regularized as
$+$-distributions if necessary:
\begin{align}
P^{qq} (x) = P^{\bar q\bar q} (x) &= \; C_F \cdot \left( \frac{1 +
x^2}{1-x} \right)_+ \\
P^{qg} (x) = P^{\bar q g} (x) &= \; C_F \cdot \frac{1 + (1-x)^2}{x}\\
P^{gq} (x) = P^{g \bar q} (x) &= \; T_R \cdot \left[ x^2 + (1-x)^2
\right] \\
P^{gg} (x) &= \; 2 C_A \biggl[ \left( \frac{1}{1-x} \right)_+ +
\frac{1-x}{x} - 1 + x(1-x) \biggl] \notag{}\\
&\quad + \delta(1-x) \left( \frac{11}{6} C_A -
\frac{2}{3} N_f T_R \right)
\end{align}
Since the number of flavors summed over in the gluon splitting
function might depend on the physics case under consideration, it is
implemented as an input variable.
<<SM physics: public>>=
public :: pqq
<<SM physics: procedures>>=
elemental function pqq (x,eps) result (pqqx)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: pqqx
if (x > (1.0_default - eps)) then
pqqx = (eps - one) / two + two * log(eps) / eps - &
three * (eps - one) / eps / two
else
pqqx = (one + x**2) / (one - x)
end if
pqqx = CF * pqqx
end function pqq
@ %def pqq
@
<<SM physics: public>>=
public :: pgq
<<SM physics: procedures>>=
elemental function pgq (x) result (pgqx)
real(kind=default), intent(in) :: x
real(kind=default) :: pgqx
pgqx = TR * (x**2 + (one - x)**2)
end function pgq
@ %def pgq
@
<<SM physics: public>>=
public :: pqg
<<SM physics: procedures>>=
elemental function pqg (x) result (pqgx)
real(kind=default), intent(in) :: x
real(kind=default) :: pqgx
pqgx = CF * (one + (one - x)**2) / x
end function pqg
@ %def pqg
@
<<SM physics: public>>=
public :: pgg
<<SM physics: procedures>>=
elemental function pgg (x, nf, eps) result (pggx)
real(kind=default), intent(in) :: x, nf, eps
real(kind=default) :: pggx
pggx = two * CA * ( plus_distr (x, eps) + (one-x)/x - one + &
x*(one-x)) + delta (x, eps) * gamma_g(nf)
end function pgg
@ %def pgg
@ For the $qq$ and $gg$ cases, there exist ``regularized'' versions of
the splitting functions:
\begin{align}
P^{qq}_{\text{reg}} (x) &= - C_F \cdot (1 + x) \\
P^{gg}_{\text{reg}} (x) &= 2 C_A \left[ \frac{1-x}{x} - 1 + x(1-x) \right]
\end{align}
<<SM physics: public>>=
public :: pqq_reg
<<SM physics: procedures>>=
elemental function pqq_reg (x) result (pqqregx)
real(kind=default), intent(in) :: x
real(kind=default) :: pqqregx
pqqregx = - CF * (one + x)
end function pqq_reg
@ %def pqq_reg
@
<<SM physics: public>>=
public :: pgg_reg
<<SM physics: procedures>>=
elemental function pgg_reg (x) result (pggregx)
real(kind=default), intent(in) :: x
real(kind=default) :: pggregx
pggregx = two * CA * ((one - x)/x - one + x*(one - x))
end function pgg_reg
@ %def pgg_reg
@ Here, we collect the expressions needed for integrated
Catani-Seymour dipoles, and the so-called flavor kernels. We always
distinguish between the ``ordinary'' Catani-Seymour version, and the
one including a phase-space slicing parameter, $\alpha$.
The standard flavor kernels $\overline{K}^{ab}$ are:
\begin{align}
\overline{K}^{qg} (x) = \overline{K}^{\bar q g} (x) & = \;
P^{qg} (x) \log ((1-x)/x) + CF \times x \\
%%%
\overline{K}^{gq} (x) = \overline{K}^{g \bar q} (x) & = \;
P^{gq} (x) \log ((1-x)/x) + TR \times 2x(1-x) \\
%%%
\overline{K}^{qq} &=\; C_F \biggl[ \left( \frac{2}{1-x} \log
\frac{1-x}{x} \right)_+ - (1+x) \log ((1-x)/x) +
(1-x) \biggr] \notag{}\\
&\quad - (5 - \pi^2) \cdot C_F \cdot \delta(1-x) \\
%%%
\overline{K}^{gg} &=\; 2 C_A \biggl[ \left( \frac{1}{1-x} \log
\frac{1-x}{x} \right)_+ + \left( \frac{1-x}{x} - 1 + x(1-x)
\right) \log((1-x)/x) \biggr] \notag{}\\
&\quad - \delta(1-x) \biggl[ \left(
\frac{50}{9} - \pi^2 \right) C_A - \frac{16}{9} T_R N_f \biggr]
\end{align}
<<SM physics: public>>=
public :: kbarqg
<<SM physics: procedures>>=
function kbarqg (x) result (kbarqgx)
real(kind=default), intent(in) :: x
real(kind=default) :: kbarqgx
kbarqgx = pqg(x) * log((one-x)/x) + CF * x
end function kbarqg
@ %def kbarqg
@
<<SM physics: public>>=
public :: kbargq
<<SM physics: procedures>>=
function kbargq (x) result (kbargqx)
real(kind=default), intent(in) :: x
real(kind=default) :: kbargqx
kbargqx = pgq(x) * log((one-x)/x) + two * TR * x * (one - x)
end function kbargq
@ %def kbarqg
@
<<SM physics: public>>=
public :: kbarqq
<<SM physics: procedures>>=
function kbarqq (x,eps) result (kbarqqx)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: kbarqqx
kbarqqx = CF*(log_plus_distr(x,eps) - (one+x) * log((one-x)/x) + (one - &
x) - (five - pi**2) * delta(x,eps))
end function kbarqq
@ %def kbarqq
@
<<SM physics: public>>=
public :: kbargg
<<SM physics: procedures>>=
function kbargg (x,eps,nf) result (kbarggx)
real(kind=default), intent(in) :: x, eps, nf
real(kind=default) :: kbarggx
kbarggx = CA * (log_plus_distr(x,eps) + two * ((one-x)/x - one + &
x*(one-x) * log((1-x)/x))) - delta(x,eps) * &
((50.0_default/9.0_default - pi**2) * CA - &
16.0_default/9.0_default * TR * nf)
end function kbargg
@ %def kbargg
@ The $\tilde{K}$ are used when two identified hadrons participate:
\begin{equation}
\tilde{K}^{ab} (x) = P^{ab}_{\text{reg}} (x) \cdot \log (1-x) +
\delta^{ab} \mathbf{T}_a^2 \biggl[ \left( \frac{2}{1-x} \log (1-x)
\right)_+ - \frac{\pi^2}{3} \delta(1-x) \biggr]
\end{equation}
<<SM physics: public>>=
public :: ktildeqq
<<SM physics: procedures>>=
function ktildeqq (x,eps) result (ktildeqqx)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: ktildeqqx
ktildeqqx = pqq_reg (x) * log(one-x) + CF * ( - log2_plus_distr (x,eps) &
- pi**2/three * delta(x,eps))
end function ktildeqq
@ %def ktildeqq
@
<<SM physics: public>>=
public :: ktildeqg
<<SM physics: procedures>>=
function ktildeqg (x,eps) result (ktildeqgx)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: ktildeqgx
ktildeqgx = pqg (x) * log(one-x)
end function ktildeqg
@ %def ktildeqg
@
<<SM physics: public>>=
public :: ktildegq
<<SM physics: procedures>>=
function ktildegq (x,eps) result (ktildegqx)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: ktildegqx
ktildegqx = pgq (x) * log(one-x)
end function ktildegq
@ %def ktildeqg
@
<<SM physics: public>>=
public :: ktildegg
<<SM physics: procedures>>=
function ktildegg (x,eps) result (ktildeggx)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: ktildeggx
ktildeggx = pgg_reg (x) * log(one-x) + CA * ( - &
log2_plus_distr (x,eps) - pi**2/three * delta(x,eps))
end function ktildegg
@ %def ktildegg
@ The insertion operator might not be necessary for a GOLEM interface
but is demanded by the Les Houches NLO accord. It is a
three-dimensional array, where the index always gives the inverse
power of the DREG expansion parameter, $\epsilon$.
<<SM physics: public>>=
public :: insert_q
<<SM physics: procedures>>=
pure function insert_q ()
real(kind=default), dimension(0:2) :: insert_q
insert_q(0) = gamma_q + k_q - pi**2/three * CF
insert_q(1) = gamma_q
insert_q(2) = CF
end function insert_q
@ %def insert_q
@
<<SM physics: public>>=
public :: insert_g
<<SM physics: procedures>>=
pure function insert_g (nf)
real(kind=default), intent(in) :: nf
real(kind=default), dimension(0:2) :: insert_g
insert_g(0) = gamma_g (nf) + k_g (nf) - pi**2/three * CA
insert_g(1) = gamma_g (nf)
insert_g(2) = CA
end function insert_g
@ %def insert_g
@ For better convergence, one can exclude regions of phase space with
a slicing parameter from the dipole subtraction procedure. First of
all, the $K$ functions get modified:
\begin{equation}
K_i (\alpha) = K_i - \mathbf{T}_i^2 \log^2 \alpha + \gamma_i (
\alpha - 1 - \log\alpha)
\end{equation}
<<SM physics: public>>=
public :: k_q_al, k_g_al
<<SM physics: procedures>>=
pure function k_q_al (alpha)
real(kind=default), intent(in) :: alpha
real(kind=default) :: k_q_al
k_q_al = k_q - CF * (log(alpha))**2 + gamma_q * &
(alpha - one - log(alpha))
end function k_q_al
pure function k_g_al (alpha, nf)
real(kind=default), intent(in) :: alpha, nf
real(kind=default) :: k_g_al
k_g_al = k_g (nf) - CA * (log(alpha))**2 + gamma_g (nf) * &
(alpha - one - log(alpha))
end function k_g_al
@ %def k_q_al
@ %def k_g_al
@ The $+$-distribution, but with a phase-space slicing parameter,
$\alpha$, $P_{1-\alpha}(x) = \left( \frac{1}{1-x}
\right)_{1-x}$. Since we need the fatal error message here, this
function cannot be elemental.
<<SM physics: public>>=
public :: plus_distr_al
<<SM physics: procedures>>=
function plus_distr_al (x,alpha,eps) result (plusd_al)
real(kind=default), intent(in) :: x, eps, alpha
real(kind=default) :: plusd_al
if ((one - alpha) >= (one - eps)) then
plusd_al = zero
call msg_fatal ('sm_physics, plus_distr_al: alpha and epsilon chosen wrongly')
elseif (x < (1.0_default - alpha)) then
plusd_al = 0
else if (x > (1.0_default - eps)) then
plusd_al = log(eps/alpha)/eps
else
plusd_al = one/(one-x)
end if
end function plus_distr_al
@ %def plus_distr_al
@ Introducing phase-space slicing parameters, these standard flavor
kernels $\overline{K}^{ab}$ become:
\begin{align}
\overline{K}^{qg}_\alpha (x) = \overline{K}^{\bar q g}_\alpha (x) & = \;
P^{qg} (x) \log (\alpha (1-x)/x) + C_F \times x \\
%%%
\overline{K}^{gq}_\alpha (x) = \overline{K}^{g \bar q}_\alpha (x) & = \;
P^{gq} (x) \log (\alpha (1-x)/x) + T_R \times 2x(1-x) \\
%%%
\overline{K}^{qq}_\alpha &=
C_F (1 - x) + P^{qq}_{\text{reg}} (x) \log \frac{\alpha(1-x)}{x}
\notag{}\\ &\quad
+ C_F \delta (1 - x) \log^2 \alpha
+ C_F \left( \frac{2}{1-x} \log \frac{1-x}{x} \right)_+ \notag{}\\
&\quad
- \left( \gamma_q + K_q(\alpha) - \frac56 \pi^2 C_F \right) \cdot
\delta(1-x) \; C_F \Bigl[ + \frac{2}{1-x} \log \left(
\frac{\alpha (2-x)}{1+\alpha-x} \right)
- \theta(1 - \alpha - x) \cdot \left( \frac{2}{1-x} \log
\frac{2-x}{1-x} \right) \Bigr] \\
%%%
\overline{K}^{gg}_\alpha &=\;
P^{gg}_{\text{reg}} (x) \log \frac{\alpha(1-x)}{x}
+ C_A \delta (1 - x) \log^2 \alpha \notag{}\\
&\quad
+ C_A \left( \frac{2}{1-x} \log \frac{1-x}{x} \right)_+
- \left( \gamma_g + K_g(\alpha) - \frac56 \pi^2 C_A \right) \cdot
\delta(1-x) \; C_A \Bigl[ + \frac{2}{1-x} \log \left(
\frac{\alpha (2-x)}{1+\alpha-x} \right)
- \theta(1 - \alpha - x) \cdot \left( \frac{2}{1-x} \log
\frac{2-x}{1-x} \right) \Bigr]
\end{align}
<<SM physics: public>>=
public :: kbarqg_al
<<SM physics: procedures>>=
function kbarqg_al (x,alpha,eps) result (kbarqgx)
real(kind=default), intent(in) :: x, alpha, eps
real(kind=default) :: kbarqgx
kbarqgx = pqg (x) * log(alpha*(one-x)/x) + CF * x
end function kbarqg_al
@ %def kbarqg_al
@
<<SM physics: public>>=
public :: kbargq_al
<<SM physics: procedures>>=
function kbargq_al (x,alpha,eps) result (kbargqx)
real(kind=default), intent(in) :: x, alpha, eps
real(kind=default) :: kbargqx
kbargqx = pgq (x) * log(alpha*(one-x)/x) + two * TR * x * (one-x)
end function kbargq_al
@ %def kbargq_al
@
<<SM physics: public>>=
public :: kbarqq_al
<<SM physics: procedures>>=
function kbarqq_al (x,alpha,eps) result (kbarqqx)
real(kind=default), intent(in) :: x, alpha, eps
real(kind=default) :: kbarqqx
kbarqqx = CF * (one - x) + pqq_reg(x) * log(alpha*(one-x)/x) &
+ CF * log_plus_distr(x,eps) &
- (gamma_q + k_q_al(alpha) - CF * &
five/6.0_default * pi**2 - CF * (log(alpha))**2) * &
delta(x,eps) + &
CF * two/(one -x)*log(alpha*(two-x)/(one+alpha-x))
if (x < (one-alpha)) then
kbarqqx = kbarqqx - CF * two/(one-x) * log((two-x)/(one-x))
end if
end function kbarqq_al
@ %def kbarqq_al
<<SM physics: public>>=
public :: kbargg_al
<<SM physics: procedures>>=
function kbargg_al (x,alpha,eps,nf) result (kbarggx)
real(kind=default), intent(in) :: x, alpha, eps, nf
real(kind=default) :: kbarggx
kbarggx = pgg_reg(x) * log(alpha*(one-x)/x) &
+ CA * log_plus_distr(x,eps) &
- (gamma_g(nf) + k_g_al(alpha,nf) - CA * &
five/6.0_default * pi**2 - CA * (log(alpha))**2) * &
delta(x,eps) + &
CA * two/(one -x)*log(alpha*(two-x)/(one+alpha-x))
if (x < (one-alpha)) then
kbarggx = kbarggx - CA * two/(one-x) * log((two-x)/(one-x))
end if
end function kbargg_al
@ %def kbargg_al
@ The $\tilde{K}$ flavor kernels in the presence of a phase-space slicing
parameter, are:
\begin{equation}
\tilde{K}^{ab} (x,\alpha) = P^{qq, \text{reg}} (x)
\log\frac{1-x}{\alpha} + ..........
\end{equation}
<<SM physics: public>>=
public :: ktildeqq_al
<<SM physics: procedures>>=
function ktildeqq_al (x,alpha,eps) result (ktildeqqx)
real(kind=default), intent(in) :: x, eps, alpha
real(kind=default) :: ktildeqqx
ktildeqqx = pqq_reg(x) * log((one-x)/alpha) + CF*( &
- log2_plus_distr_al(x,alpha,eps) - Pi**2/three * delta(x,eps) &
+ (one+x**2)/(one-x) * log(min(one,(alpha/(one-x)))) &
+ two/(one-x) * log((one+alpha-x)/alpha))
if (x > (one-alpha)) then
ktildeqqx = ktildeqqx - CF*two/(one-x)*log(two-x)
end if
end function ktildeqq_al
@ %def ktildeqq_al
@ This is a logarithmic $+$-distribution, $\left(
\frac{\log((1-x)/x)}{1-x} \right)_+$. For the sampling, we need the
integral over this function over the incomplete sampling interval
$[0,1-\epsilon]$, which is $\log^2(x) + 2 Li_2(x) -
\frac{\pi^2}{3}$. As this function is negative definite for $\epsilon
> 0.1816$, we take a hard upper limit for that sampling parameter,
irrespective of the fact what the user chooses.
<<SM physics: public>>=
public :: log_plus_distr
<<SM physics: procedures>>=
function log_plus_distr (x,eps) result (lpd)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: lpd, eps2
eps2 = min (eps, 0.1816_default)
if (x > (1.0_default - eps2)) then
lpd = ((log(eps2))**2 + two*Li2(eps2) - pi**2/three)/eps2
else
lpd = two*log((one-x)/x)/(one-x)
end if
end function log_plus_distr
@ %def log_plus_distr
@ Logarithmic $+$-distribution, $2 \left( \frac{\log(1/(1-x))}{1-x} \right)_+$.
<<SM physics: public>>=
public :: log2_plus_distr
<<SM physics: procedures>>=
function log2_plus_distr (x,eps) result (lpd)
real(kind=default), intent(in) :: x, eps
real(kind=default) :: lpd
if (x > (1.0_default - eps)) then
lpd = - (log(eps))**2/eps
else
lpd = two*log(one/(one-x))/(one-x)
end if
end function log2_plus_distr
@ %def log2_plus_distr
@ Logarithmic $+$-distribution with phase-space slicing parameter, $2
\left( \frac{\log(1/(1-x))}{1-x} \right)_{1-\alpha}$.
<<SM physics: public>>=
public :: log2_plus_distr_al
<<SM physics: procedures>>=
function log2_plus_distr_al (x,alpha,eps) result (lpd_al)
real(kind=default), intent(in) :: x, eps, alpha
real(kind=default) :: lpd_al
if ((one - alpha) >= (one - eps)) then
lpd_al = zero
call msg_fatal ('alpha and epsilon chosen wrongly')
elseif (x < (one - alpha)) then
lpd_al = 0
elseif (x > (1.0_default - eps)) then
lpd_al = - ((log(eps))**2 - (log(alpha))**2)/eps
else
lpd_al = two*log(one/(one-x))/(one-x)
end if
end function log2_plus_distr_al
@ %def log2_plus_distr_al
@
\subsection{Splitting Functions}
@ Analogue to the regularized distributions of the last subsection, we
give here the unregularized splitting functions, relevant for the parton
shower algorithm. We can use this unregularized version since there will
be a cut-off $\epsilon$ that ensures that $\{z,1-z\}>\epsilon(t)$. This
cut-off seperates resolvable from unresolvable emissions.
[[p_xxx]] are the kernels that are summed over helicity:
<<SM physics: public>>=
public :: p_qqg
public :: p_gqq
public :: p_ggg
@ $q\to q g$
<<SM physics: procedures>>=
elemental function p_qqg (z) result (P)
real(default), intent(in) :: z
real(default) :: P
P = CF * (one + z**2) / (one - z)
end function p_qqg
@ $g\to q \bar{q}$
<<SM physics: procedures>>=
elemental function p_gqq (z) result (P)
real(default), intent(in) :: z
real(default) :: P
P = TR * (z**2 + (one - z)**2)
end function p_gqq
@ $g\to g g$
<<SM physics: procedures>>=
elemental function p_ggg (z) result (P)
real(default), intent(in) :: z
real(default) :: P
P = NC * ((one - z) / z + z / (one - z) + z * (one - z))
end function p_ggg
@ %def p_qqg p_gqq p_ggg
@ Analytically integrated splitting kernels:
<<SM physics: public>>=
public :: integral_over_p_qqg
public :: integral_over_p_gqq
public :: integral_over_p_ggg
<<SM physics: procedures>>=
pure function integral_over_p_qqg (zmin, zmax) result (integral)
real(default), intent(in) :: zmin, zmax
real(default) :: integral
integral = (two / three) * (- zmax**2 + zmin**2 - &
two * (zmax - zmin) + four * log((one - zmin) / (one - zmax)))
end function integral_over_p_qqg
pure function integral_over_p_gqq (zmin, zmax) result (integral)
real(default), intent(in) :: zmin, zmax
real(default) :: integral
integral = 0.5_default * ((two / three) * &
(zmax**3 - zmin**3) - (zmax**2 - zmin**2) + (zmax - zmin))
end function integral_over_p_gqq
pure function integral_over_p_ggg (zmin, zmax) result (integral)
real(default), intent(in) :: zmin, zmax
real(default) :: integral
integral = three * ((log(zmax) - two * zmax - &
log(one - zmax) + zmax**2 / two - zmax**3 / three) - &
(log(zmin) - zmin - zmin - log(one - zmin) + zmin**2 &
/ two - zmin**3 / three) )
end function integral_over_p_ggg
@ %def integral_over_p_gqq integral_over_p_ggg integral_over_p_qqg
@ We can also use (massless) helicity dependent splitting functions:
<<SM physics: public>>=
public :: p_qqg_pol
@ $q_a\to q_b g_c$, the helicity of the quark is not changed by gluon
emission and the gluon is preferably polarized in the branching plane
($l_c=1$):
<<SM physics: procedures>>=
elemental function p_qqg_pol (z, l_a, l_b, l_c) result (P)
real(default), intent(in) :: z
integer, intent(in) :: l_a, l_b, l_c
real(default) :: P
if (l_a /= l_b) then
P = zero
return
end if
if (l_c == -1) then
P = one - z
else
P = (one + z)**2 / (one - z)
end if
P = P * CF
end function p_qqg_pol
@
\subsection{Top width}
In order to produce sensible results, the widths have to be recomputed
for each parameter and order.
We start with the LO-expression for the top width given by the decay
$t\,\to\,W^+,b$, cf. [[doi:10.1016/0550-3213(91)90530-B]]:\\
The analytic formula given there is
\begin{equation*}
\Gamma = \frac{G_F m_t^2}{16\sqrt{2}\pi}
\left[\mathcal{F}_0(\varepsilon, \xi^{-1/2}) -
\frac{2\alpha_s}{3\pi} \mathcal{F}_1 (\varepsilon, \xi^{-1/2})\right],
\end{equation*}
with
\begin{align*}
\mathcal{F}_0 &= \frac{\sqrt{\lambda}}{2} f_0, \\
f_0 &= 4\left[(1-\varepsilon^2)^2 + w^2(1+\varepsilon^2) - 2w^4\right], \\
\lambda = 1 + w^4 + \varepsilon^4 - 2(w^2 + \varepsilon^2 + w^2\varepsilon^2).
\end{align*}
Defining
\begin{equation*}
u_q = \frac{1 + \varepsilon^2 - w^2 - \lambda^{1/2}}{1 +
- \varepsilon^2 - w^2 + \lambda^{1/2}}
+ \varepsilon^2 - w^2 + \lambda^{1/2}}
\end{equation*}
and
\begin{equation*}
u_w = \frac{1 - \varepsilon^2 + w^2 - \lambda^{1/2}}{1 -
- \varepsilon^2 + w^2 + \lambda^{1/2}}
+ \varepsilon^2 + w^2 + \lambda^{1/2}}
\end{equation*}
the factor $\mathcal{F}_1$ can be expressed as
\begin{align*}
\mathcal{F}_1 = \frac{1}{2}f_0(1+\varepsilon^2-w^2)
& \left[\pi^2 + 2Li_2(u_w) - 2Li_2(1-u_w) - 4Li_2(u_q) \right. \\
& -4Li_2(u_q u_w) + \log\left(\frac{1-u_q}{w^2}\right)\log(1-u_q)
- \log^2(1-u_q u_w) \\
& \left.+\frac{1}{4}\log^2\left(\frac{w^2}{u_w}\right) - \log(u_w)
\log\left[\frac{(1-u_q u_w)^2}{1-u_q}\right]
-2\log(u_q)\log\left[(1-u_q)(1-u_q u_w)\right]\right] \\
& -\sqrt{\lambda}f_0(2\log(w) + 3\log(\varepsilon) - 2\log{\lambda}) \\
& +4(1-\varepsilon^2)\left[(1-\varepsilon^2)^2 +
w^2(1+\varepsilon^2) - 4w^4\right]\log(u_w) \\
& \left[(3 - \varepsilon^2 + 11\varepsilon^4 - \varepsilon^6)
+ w^2(6 - 12\varepsilon^2 +2\varepsilon^4) - w^4(21 +
5\varepsilon^2) + 12w^6\right] \log(u_q) \\
& 6\sqrt{\lambda} (1-\varepsilon^2) (1 + \varepsilon^2 - w^2)
\log(\varepsilon)
+ \sqrt{\lambda}\left[-5 + 22\varepsilon^2 - 5\varepsilon^4 -
9w^2(1+\varepsilon^2) + 6w^4\right].
\end{align*}
@
<<SM physics: public>>=
public :: top_width_sm_lo
<<SM physics: procedures>>=
elemental function top_width_sm_lo (alpha, sinthw, vtb, mtop, mw, mb) &
result (gamma)
real(default) :: gamma
real(default), intent(in) :: alpha, sinthw, vtb, mtop, mw, mb
real(default) :: kappa
kappa = sqrt ((mtop**2 - (mw + mb)**2) * (mtop**2 - (mw - mb)**2))
gamma = alpha / four * mtop / (two * sinthw**2) * &
vtb**2 * kappa / mtop**2 * &
((mtop**2 + mb**2) / (two * mtop**2) + &
(mtop**2 - mb**2)**2 / (two * mtop**2 * mw**2) - &
mw**2 / mtop**2)
end function top_width_sm_lo
@ %def top_width_sm_lo
@
<<SM physics: public>>=
public :: g_mu_from_alpha
<<SM physics: procedures>>=
elemental function g_mu_from_alpha (alpha, mw, sinthw) result (g_mu)
real(default) :: g_mu
real(default), intent(in) :: alpha, mw, sinthw
g_mu = pi * alpha / sqrt(two) / mw**2 / sinthw**2
end function g_mu_from_alpha
@ %def g_mu_from_alpha
@
<<SM physics: public>>=
public :: alpha_from_g_mu
<<SM physics: procedures>>=
elemental function alpha_from_g_mu (g_mu, mw, sinthw) result (alpha)
real(default) :: alpha
real(default), intent(in) :: g_mu, mw, sinthw
alpha = g_mu * sqrt(two) / pi * mw**2 * sinthw**2
end function alpha_from_g_mu
@ %def alpha_from_g_mu
@ Cf. (3.3)-(3.7) in [[1207.5018]].
<<SM physics: public>>=
public :: top_width_sm_qcd_nlo_massless_b
<<SM physics: procedures>>=
elemental function top_width_sm_qcd_nlo_massless_b &
(alpha, sinthw, vtb, mtop, mw, alphas) result (gamma)
real(default) :: gamma
real(default), intent(in) :: alpha, sinthw, vtb, mtop, mw, alphas
real(default) :: prefac, g_mu, w2
g_mu = g_mu_from_alpha (alpha, mw, sinthw)
prefac = g_mu * mtop**3 * vtb**2 / (16 * sqrt(two) * pi)
w2 = mw**2 / mtop**2
gamma = prefac * (f0 (w2) - (two * alphas) / (3 * Pi) * f1 (w2))
end function top_width_sm_qcd_nlo_massless_b
@ %def top_width_sm_qcd_nlo_massless_b
@
<<SM physics: public>>=
public :: f0
<<SM physics: procedures>>=
elemental function f0 (w2) result (f)
real(default) :: f
real(default), intent(in) :: w2
f = two * (one - w2)**2 * (1 + 2 * w2)
end function f0
@ %def f0
@
<<SM physics: public>>=
public :: f1
<<SM physics: procedures>>=
elemental function f1 (w2) result (f)
real(default) :: f
real(default), intent(in) :: w2
f = f0 (w2) * (pi**2 + two * Li2 (w2) - two * Li2 (one - w2)) &
+ four * w2 * (one - w2 - two * w2**2) * log (w2) &
+ two * (one - w2)**2 * (five + four * w2) * log (one - w2) &
- (one - w2) * (five + 9 * w2 - 6 * w2**2)
end function f1
@ %def f1
@ Basically, the same as above but with $m_b$ dependence,
cf. Jezabek / Kuehn 1989.
<<SM physics: public>>=
public :: top_width_sm_qcd_nlo_jk
<<SM physics: procedures>>=
elemental function top_width_sm_qcd_nlo_jk &
(alpha, sinthw, vtb, mtop, mw, mb, alphas) result (gamma)
real(default) :: gamma
real(default), intent(in) :: alpha, sinthw, vtb, mtop, mw, mb, alphas
real(default) :: prefac, g_mu, eps2, i_xi
g_mu = g_mu_from_alpha (alpha, mw, sinthw)
prefac = g_mu * mtop**3 * vtb**2 / (16 * sqrt(two) * pi)
eps2 = (mb / mtop)**2
i_xi = (mw / mtop)**2
gamma = prefac * (ff0 (eps2, i_xi) - &
(two * alphas) / (3 * Pi) * ff1 (eps2, i_xi))
end function top_width_sm_qcd_nlo_jk
@ %def top_width_sm_qcd_nlo_jk
@ Same as above, $m_b > 0$, with the slightly different implementation
(2.6) of arXiv:1204.1513v1 by Campbell and Ellis.
<<SM physics: public>>=
public :: top_width_sm_qcd_nlo_ce
<<SM physics: procedures>>=
elemental function top_width_sm_qcd_nlo_ce &
(alpha, sinthw, vtb, mtop, mw, mb, alpha_s) result (gamma)
real(default) :: gamma
real(default), intent(in) :: alpha, sinthw, vtb, mtop, mw, mb, alpha_s
real(default) :: pm, pp, p0, p3
real(default) :: yw, yp
real(default) :: W0, Wp, Wm, w2
real(default) :: beta2
real(default) :: f
real(default) :: g_mu, gamma0
beta2 = (mb / mtop)**2
w2 = (mw / mtop)**2
p0 = (one - w2 + beta2) / two
p3 = sqrt (lambda (one, w2, beta2)) / two
pp = p0 + p3
pm = p0 - p3
W0 = (one + w2 - beta2) / two
Wp = W0 + p3
Wm = W0 - p3
yp = log (pp / pm) / two
yw = log (Wp / Wm) / two
f = (one - beta2)**2 + w2 * (one + beta2) - two * w2**2
g_mu = g_mu_from_alpha (alpha, mw, sinthw)
gamma0 = g_mu * mtop**3 * vtb**2 / (8 * pi * sqrt(two))
gamma = gamma0 * alpha_s / twopi * CF * &
(8 * f * p0 * (Li2(one - pm) - Li2(one - pp) - two * Li2(one - pm / pp) &
+ yp * log((four * p3**2) / (pp**2 * Wp)) + yw * log (pp)) &
+ four * (one - beta2) * ((one - beta2)**2 + w2 * (one + beta2) - four * w2**2) * yw &
+ (3 - beta2 + 11 * beta2**2 - beta2**3 + w2 * (6 - 12 * beta2 + two * beta2**2) &
- w2**2 * (21 + 5 * beta2) + 12 * w2**3) * yp &
+ 8 * f * p3 * log (sqrt(w2) / (four * p3**2)) &
+ 6 * (one - four * beta2 + 3 * beta2**2 + w2 * (3 + beta2) - four * w2**2) * p3 * log(sqrt(beta2)) &
+ (5 - 22 * beta2 + 5 * beta2**2 + 9 * w2 * (one + beta2) - 6 * w2**2) * p3)
end function top_width_sm_qcd_nlo_ce
@ %def top_width_sm_qcd_nlo_ce
@
<<SM physics: public>>=
public :: ff0
<<SM physics: procedures>>=
elemental function ff0 (eps2, w2) result (f)
real(default) :: f
real(default), intent(in) :: eps2, w2
f = one / two * sqrt(ff_lambda (eps2, w2)) * ff_f0 (eps2, w2)
end function ff0
@ %def ff0
@
<<SM physics: public>>=
public :: ff_f0
<<SM physics: procedures>>=
elemental function ff_f0 (eps2, w2) result (f)
real(default) :: f
real(default), intent(in) :: eps2, w2
f = four * ((1 - eps2)**2 + w2 * (1 + eps2) - 2 * w2**2)
end function ff_f0
@ %def ff_f0
@
<<SM physics: public>>=
public :: ff_lambda
<<SM physics: procedures>>=
elemental function ff_lambda (eps2, w2) result (l)
real(default) :: l
real(default), intent(in) :: eps2, w2
l = one + w2**2 + eps2**2 - two * (w2 + eps2 + w2 * eps2)
end function ff_lambda
@ %def ff_lambda
@
<<SM physics: public>>=
public :: ff1
<<SM physics: procedures>>=
elemental function ff1 (eps2, w2) result (f)
real(default) :: f
real(default), intent(in) :: eps2, w2
real(default) :: uq, uw, sq_lam, fff
sq_lam = sqrt (ff_lambda (eps2, w2))
fff = ff_f0 (eps2, w2)
uw = (one - eps2 + w2 - sq_lam) / &
(one - eps2 + w2 + sq_lam)
uq = (one + eps2 - w2 - sq_lam) / &
(one + eps2 - w2 + sq_lam)
f = one / two * fff * (one + eps2 - w2) * &
(pi**2 + two * Li2 (uw) - two * Li2 (one - uw) - four * Li2 (uq) &
- four * Li2 (uq * uw) + log ((one - uq) / w2) * log (one - uq) &
- log (one - uq * uw)**2 + one / four * log (w2 / uw)**2 &
- log (uw) * log ((one - uq * uw)**2 / (one - uq)) &
- two * log (uq) * log ((one - uq) * (one - uq * uw))) &
- sq_lam * fff * (two * log (sqrt (w2)) &
+ three * log (sqrt (eps2)) - two * log (sq_lam**2)) &
+ four * (one - eps2) * ((one - eps2)**2 + w2 * (one + eps2) &
- four * w2**2) * log (uw) &
+ (three - eps2 + 11 * eps2**2 - eps2**3 + w2 * &
(6 - 12 * eps2 + 2 * eps2**2) - w2**2 * (21 + five * eps2) &
+ 12 * w2**3) * log (uq) &
+ 6 * sq_lam * (one - eps2) * &
(one + eps2 - w2) * log (sqrt (eps2)) &
+ sq_lam * (- five + 22 * eps2 - five * eps2**2 - 9 * w2 * &
(one + eps2) + 6 * w2**2)
end function ff1
@ %def ff1
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sm_physics_ut.f90]]>>=
<<File header>>
module sm_physics_ut
use unit_tests
use sm_physics_uti
<<Standard module head>>
<<SM physics: public test>>
contains
<<SM physics: test driver>>
end module sm_physics_ut
@ %def sm_physics_ut
@
<<[[sm_physics_uti.f90]]>>=
<<File header>>
module sm_physics_uti
<<Use kinds>>
use numeric_utils
use format_defs, only: FMT_15
use constants
use sm_physics
<<Standard module head>>
<<SM physics: test declarations>>
contains
<<SM physics: tests>>
end module sm_physics_uti
@ %def sm_physics_ut
@ API: driver for the unit tests below.
<<SM physics: public test>>=
public :: sm_physics_test
<<SM physics: test driver>>=
subroutine sm_physics_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SM physics: execute tests>>
end subroutine sm_physics_test
@ %def sm_physics_test
@
\subsubsection{Splitting functions}
<<SM physics: execute tests>>=
call test (sm_physics_1, "sm_physics_1", &
"Splitting functions", &
u, results)
<<SM physics: test declarations>>=
public :: sm_physics_1
<<SM physics: tests>>=
subroutine sm_physics_1 (u)
integer, intent(in) :: u
real(default) :: z = 0.75_default
write (u, "(A)") "* Test output: sm_physics_1"
write (u, "(A)") "* Purpose: check analytic properties"
write (u, "(A)")
write (u, "(A)") "* Splitting functions:"
write (u, "(A)")
call assert (u, vanishes (p_qqg_pol (z, +1, -1, +1)), "+-+")
call assert (u, vanishes (p_qqg_pol (z, +1, -1, -1)), "+--")
call assert (u, vanishes (p_qqg_pol (z, -1, +1, +1)), "-++")
call assert (u, vanishes (p_qqg_pol (z, -1, +1, -1)), "-+-")
!call assert (u, nearly_equal ( &
!p_qqg_pol (z, +1, +1, -1) + p_qqg_pol (z, +1, +1, +1), &
!p_qqg (z)), "pol sum")
write (u, "(A)")
write (u, "(A)") "* Test output end: sm_physics_1"
end subroutine sm_physics_1
@ %def sm_physics_1
@
\subsubsection{Top width}
<<SM physics: execute tests>>=
call test(sm_physics_2, "sm_physics_2", &
"Top width", u, results)
<<SM physics: test declarations>>=
public :: sm_physics_2
<<SM physics: tests>>=
subroutine sm_physics_2 (u)
integer, intent(in) :: u
real(default) :: mtop, mw, mz, mb, g_mu, sinthw, alpha, vtb, gamma0
real(default) :: w2, alphas, alphas_mz, gamma1
write (u, "(A)") "* Test output: sm_physics_2"
write (u, "(A)") "* Purpose: Check different top width computations"
write (u, "(A)")
write (u, "(A)") "* Values from [[1207.5018]] (massless b)"
mtop = 172.0
mw = 80.399
mz = 91.1876
mb = zero
mb = 0.00001
g_mu = 1.16637E-5
sinthw = sqrt(one - mw**2 / mz**2)
alpha = alpha_from_g_mu (g_mu, mw, sinthw)
vtb = one
w2 = mw**2 / mtop**2
write (u, "(A)") "* Check Li2 implementation"
call assert_equal (u, Li2(w2), 0.2317566263959552_default, &
"Li2(w2)", rel_smallness=1.0E-6_default)
call assert_equal (u, Li2(one - w2), 1.038200378935867_default, &
"Li2(one - w2)", rel_smallness=1.0E-6_default)
write (u, "(A)") "* Check LO Width"
gamma0 = top_width_sm_lo (alpha, sinthw, vtb, mtop, mw, mb)
call assert_equal (u, gamma0, 1.4655_default, &
"top_width_sm_lo", rel_smallness=1.0E-5_default)
alphas = zero
gamma0 = top_width_sm_qcd_nlo_massless_b &
(alpha, sinthw, vtb, mtop, mw, alphas)
call assert_equal (u, gamma0, 1.4655_default, &
"top_width_sm_qcd_nlo_massless_b", rel_smallness=1.0E-5_default)
gamma0 = top_width_sm_qcd_nlo_jk &
(alpha, sinthw, vtb, mtop, mw, mb, alphas)
call assert_equal (u, gamma0, 1.4655_default, &
"top_width_sm_qcd_nlo", rel_smallness=1.0E-5_default)
write (u, "(A)") "* Check NLO Width"
alphas_mz = 0.1202 ! MSTW2008 NLO fit
alphas = running_as (mtop, alphas_mz, mz, 1, 5.0_default)
gamma1 = top_width_sm_qcd_nlo_massless_b &
(alpha, sinthw, vtb, mtop, mw, alphas)
call assert_equal (u, gamma1, 1.3376_default, rel_smallness=1.0E-4_default)
gamma1 = top_width_sm_qcd_nlo_jk &
(alpha, sinthw, vtb, mtop, mw, mb, alphas)
! It would be nice to get one more significant digit but the
! expression is numerically rather unstable for mb -> 0
call assert_equal (u, gamma1, 1.3376_default, rel_smallness=1.0E-3_default)
write (u, "(A)") "* Values from threshold validation (massive b)"
alpha = one / 125.924
! ee = 0.315901
! cw = 0.881903
! v = 240.024
mtop = 172.0 ! This is the value for M1S !!!
mb = 4.2
sinthw = 0.47143
mz = 91.188
mw = 80.419
call assert_equal (u, sqrt(one - mw**2 / mz**2), sinthw, &
"sinthw", rel_smallness=1.0E-6_default)
write (u, "(A)") "* Check LO Width"
gamma0 = top_width_sm_lo (alpha, sinthw, vtb, mtop, mw, mb)
call assert_equal (u, gamma0, 1.5386446_default, &
"gamma0", rel_smallness=1.0E-7_default)
alphas = zero
gamma0 = top_width_sm_qcd_nlo_jk &
(alpha, sinthw, vtb, mtop, mw, mb, alphas)
call assert_equal (u, gamma0, 1.5386446_default, &
"gamma0", rel_smallness=1.0E-7_default)
write (u, "(A)") "* Check NLO Width"
alphas_mz = 0.118 !(Z pole, NLL running to mu_h)
alphas = running_as (mtop, alphas_mz, mz, 1, 5.0_default)
write (u, "(A," // FMT_15 // ")") "* alphas = ", alphas
gamma1 = top_width_sm_qcd_nlo_jk &
(alpha, sinthw, vtb, mtop, mw, mb, alphas)
write (u, "(A," // FMT_15 // ")") "* Gamma1 = ", gamma1
mb = zero
gamma1 = top_width_sm_qcd_nlo_massless_b &
(alpha, sinthw, vtb, mtop, mw, alphas)
alphas = running_as (mtop, alphas_mz, mz, 1, 5.0_default)
write (u, "(A," // FMT_15 // ")") "* Gamma1(mb=0) = ", gamma1
write (u, "(A)")
write (u, "(A)") "* Test output end: sm_physics_2"
end subroutine sm_physics_2
@ %def sm_physics_2
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{QCD Coupling}
We provide various distinct implementations of the QCD coupling. In
this module, we define an abstract data type and three
implementations: fixed, running with $\alpha_s(M_Z)$ as input, and
running with $\Lambda_{\text{QCD}}$ as input. We use the functions
defined above in the module [[sm_physics]] but provide a common
interface. Later modules may define additional implementations.
<<[[sm_qcd.f90]]>>=
<<File header>>
module sm_qcd
<<Use kinds>>
use io_units
use format_defs, only: FMT_12
use numeric_utils
use diagnostics
use md5
use physics_defs
use sm_physics
<<Standard module head>>
<<SM qcd: public>>
<<SM qcd: types>>
<<SM qcd: interfaces>>
contains
<<SM qcd: procedures>>
end module sm_qcd
@ %def sm_qcd
@
\subsection{Coupling: Abstract Data Type}
This is the abstract version of the QCD coupling implementation.
<<SM qcd: public>>=
public :: alpha_qcd_t
<<SM qcd: types>>=
type, abstract :: alpha_qcd_t
contains
<<SM qcd: alpha qcd: TBP>>
end type alpha_qcd_t
@ %def alpha_qcd_t
@ There must be an output routine.
<<SM qcd: alpha qcd: TBP>>=
procedure (alpha_qcd_write), deferred :: write
<<SM qcd: interfaces>>=
abstract interface
subroutine alpha_qcd_write (object, unit)
import
class(alpha_qcd_t), intent(in) :: object
integer, intent(in), optional :: unit
end subroutine alpha_qcd_write
end interface
@ %def alpha_qcd_write
@ This method computes the running coupling, given a certain scale. All
parameters (reference value, order of the approximation, etc.) must be
set before calling this.
<<SM qcd: alpha qcd: TBP>>=
procedure (alpha_qcd_get), deferred :: get
<<SM qcd: interfaces>>=
abstract interface
function alpha_qcd_get (alpha_qcd, scale) result (alpha)
import
class(alpha_qcd_t), intent(in) :: alpha_qcd
real(default), intent(in) :: scale
real(default) :: alpha
end function alpha_qcd_get
end interface
@ %def alpha_qcd_get
@
\subsection{Fixed Coupling}
In this version, the $\alpha_s$ value is fixed, the [[scale]] argument
of the [[get]] method is ignored. There is only one parameter, the
value. By default, this is the value at $M_Z$.
<<SM qcd: public>>=
public :: alpha_qcd_fixed_t
<<SM qcd: types>>=
type, extends (alpha_qcd_t) :: alpha_qcd_fixed_t
real(default) :: val = ALPHA_QCD_MZ_REF
contains
<<SM qcd: alpha qcd fixed: TBP>>
end type alpha_qcd_fixed_t
@ %def alpha_qcd_fixed_t
@ Output.
<<SM qcd: alpha qcd fixed: TBP>>=
procedure :: write => alpha_qcd_fixed_write
<<SM qcd: procedures>>=
subroutine alpha_qcd_fixed_write (object, unit)
class(alpha_qcd_fixed_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(3x,A)") "QCD parameters (fixed coupling):"
write (u, "(5x,A," // FMT_12 // ")") "alpha = ", object%val
end subroutine alpha_qcd_fixed_write
@ %def alpha_qcd_fixed_write
@ Calculation: the scale is ignored in this case.
<<SM qcd: alpha qcd fixed: TBP>>=
procedure :: get => alpha_qcd_fixed_get
<<SM qcd: procedures>>=
function alpha_qcd_fixed_get (alpha_qcd, scale) result (alpha)
class(alpha_qcd_fixed_t), intent(in) :: alpha_qcd
real(default), intent(in) :: scale
real(default) :: alpha
alpha = alpha_qcd%val
end function alpha_qcd_fixed_get
@ %def alpha_qcd_fixed_get
@
\subsection{Running Coupling}
In this version, the $\alpha_s$ value runs relative to the value at a
given reference scale. There are two parameters: the value of this
scale (default: $M_Z$), the value of $\alpha_s$ at this scale, and the
number of effective flavors. Furthermore, we have the order of the
approximation.
<<SM qcd: public>>=
public :: alpha_qcd_from_scale_t
<<SM qcd: types>>=
type, extends (alpha_qcd_t) :: alpha_qcd_from_scale_t
real(default) :: mu_ref = MZ_REF
real(default) :: ref = ALPHA_QCD_MZ_REF
integer :: order = 0
integer :: nf = 5
contains
<<SM qcd: alpha qcd from scale: TBP>>
end type alpha_qcd_from_scale_t
@ %def alpha_qcd_from_scale_t
@ Output.
<<SM qcd: alpha qcd from scale: TBP>>=
procedure :: write => alpha_qcd_from_scale_write
<<SM qcd: procedures>>=
subroutine alpha_qcd_from_scale_write (object, unit)
class(alpha_qcd_from_scale_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(3x,A)") "QCD parameters (running coupling):"
write (u, "(5x,A," // FMT_12 // ")") "Scale mu = ", object%mu_ref
write (u, "(5x,A," // FMT_12 // ")") "alpha(mu) = ", object%ref
write (u, "(5x,A,I0)") "LL order = ", object%order
write (u, "(5x,A,I0)") "N(flv) = ", object%nf
end subroutine alpha_qcd_from_scale_write
@ %def alpha_qcd_from_scale_write
@ Calculation: here, we call the function for running $\alpha_s$ that
was defined in [[sm_physics]] above. The function does not take into
account thresholds, so the number of flavors should be the correct one
for the chosen scale. Normally, this should be the $Z$ boson mass.
<<SM qcd: alpha qcd from scale: TBP>>=
procedure :: get => alpha_qcd_from_scale_get
<<SM qcd: procedures>>=
function alpha_qcd_from_scale_get (alpha_qcd, scale) result (alpha)
class(alpha_qcd_from_scale_t), intent(in) :: alpha_qcd
real(default), intent(in) :: scale
real(default) :: alpha
alpha = running_as (scale, &
alpha_qcd%ref, alpha_qcd%mu_ref, alpha_qcd%order, &
real (alpha_qcd%nf, kind=default))
end function alpha_qcd_from_scale_get
@ %def alpha_qcd_from_scale_get
@
\subsection{Running Coupling, determined by $\Lambda_{\text{QCD}}$}
In this version, the input are the value $\Lambda_{\text{QCD}}$ and
the order of the approximation.
<<SM qcd: public>>=
public :: alpha_qcd_from_lambda_t
<<SM qcd: types>>=
type, extends (alpha_qcd_t) :: alpha_qcd_from_lambda_t
real(default) :: lambda = LAMBDA_QCD_REF
integer :: order = 0
integer :: nf = 5
contains
<<SM qcd: alpha qcd from lambda: TBP>>
end type alpha_qcd_from_lambda_t
@ %def alpha_qcd_from_lambda_t
@ Output.
<<SM qcd: alpha qcd from lambda: TBP>>=
procedure :: write => alpha_qcd_from_lambda_write
<<SM qcd: procedures>>=
subroutine alpha_qcd_from_lambda_write (object, unit)
class(alpha_qcd_from_lambda_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(3x,A)") "QCD parameters (Lambda_QCD as input):"
write (u, "(5x,A," // FMT_12 // ")") "Lambda_QCD = ", object%lambda
write (u, "(5x,A,I0)") "LL order = ", object%order
write (u, "(5x,A,I0)") "N(flv) = ", object%nf
end subroutine alpha_qcd_from_lambda_write
@ %def alpha_qcd_from_lambda_write
@ Calculation: here, we call the second function for running $\alpha_s$ that
was defined in [[sm_physics]] above. The $\Lambda$ value should be
the one that is appropriate for the chosen number of effective
flavors. Again, thresholds are not incorporated.
<<SM qcd: alpha qcd from lambda: TBP>>=
procedure :: get => alpha_qcd_from_lambda_get
<<SM qcd: procedures>>=
function alpha_qcd_from_lambda_get (alpha_qcd, scale) result (alpha)
class(alpha_qcd_from_lambda_t), intent(in) :: alpha_qcd
real(default), intent(in) :: scale
real(default) :: alpha
alpha = running_as_lam (real (alpha_qcd%nf, kind=default), scale, &
alpha_qcd%lambda, alpha_qcd%order)
end function alpha_qcd_from_lambda_get
@ %def alpha_qcd_from_lambda_get
@
\subsection{Wrapper type}
We could get along with a polymorphic QCD type, but a monomorphic wrapper type
with a polymorphic component is easier to handle and probably safer
(w.r.t.\ compiler bugs). However, we keep the object transparent, so we can
set the type-specific parameters directly (by a [[dispatch]] routine).
<<SM qcd: public>>=
public :: qcd_t
<<SM qcd: types>>=
type :: qcd_t
class(alpha_qcd_t), allocatable :: alpha
character(32) :: md5sum = ""
integer :: n_f = -1
contains
<<SM qcd: qcd: TBP>>
end type qcd_t
@ %def qcd_t
@ Output. We first print the polymorphic [[alpha]] which contains a headline,
then any extra components.
<<SM qcd: qcd: TBP>>=
procedure :: write => qcd_write
<<SM qcd: procedures>>=
subroutine qcd_write (qcd, unit, show_md5sum)
class(qcd_t), intent(in) :: qcd
integer, intent(in), optional :: unit
logical, intent(in), optional :: show_md5sum
logical :: show_md5
integer :: u
u = given_output_unit (unit); if (u < 0) return
show_md5 = .true.; if (present (show_md5sum)) show_md5 = show_md5sum
if (allocated (qcd%alpha)) then
call qcd%alpha%write (u)
else
write (u, "(3x,A)") "QCD parameters (coupling undefined)"
end if
if (show_md5 .and. qcd%md5sum /= "") &
write (u, "(5x,A,A,A)") "md5sum = '", qcd%md5sum, "'"
end subroutine qcd_write
@ %def qcd_write
@ Compute an MD5 sum for the [[alpha_s]] setup. This is
done by writing them to a temporary file, using a standard format.
<<SM qcd: qcd: TBP>>=
procedure :: compute_alphas_md5sum => qcd_compute_alphas_md5sum
<<SM qcd: procedures>>=
subroutine qcd_compute_alphas_md5sum (qcd)
class(qcd_t), intent(inout) :: qcd
integer :: unit
if (allocated (qcd%alpha)) then
unit = free_unit ()
open (unit, status="scratch", action="readwrite")
call qcd%alpha%write (unit)
rewind (unit)
qcd%md5sum = md5sum (unit)
close (unit)
end if
end subroutine qcd_compute_alphas_md5sum
@ %def qcd_compute_alphas_md5sum
@
@ Retrieve the MD5 sum of the qcd setup.
<<SM qcd: qcd: TBP>>=
procedure :: get_md5sum => qcd_get_md5sum
<<SM qcd: procedures>>=
function qcd_get_md5sum (qcd) result (md5sum)
character(32) :: md5sum
class(qcd_t), intent(inout) :: qcd
md5sum = qcd%md5sum
end function qcd_get_md5sum
@ %def qcd_get_md5sum
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sm_qcd_ut.f90]]>>=
<<File header>>
module sm_qcd_ut
use unit_tests
use sm_qcd_uti
<<Standard module head>>
<<SM qcd: public test>>
contains
<<SM qcd: test driver>>
end module sm_qcd_ut
@ %def sm_qcd_ut
@
<<[[sm_qcd_uti.f90]]>>=
<<File header>>
module sm_qcd_uti
<<Use kinds>>
use physics_defs, only: MZ_REF
use sm_qcd
<<Standard module head>>
<<SM qcd: test declarations>>
contains
<<SM qcd: tests>>
end module sm_qcd_uti
@ %def sm_qcd_ut
@ API: driver for the unit tests below.
<<SM qcd: public test>>=
public :: sm_qcd_test
<<SM qcd: test driver>>=
subroutine sm_qcd_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SM qcd: execute tests>>
end subroutine sm_qcd_test
@ %def sm_qcd_test
@
\subsubsection{QCD Coupling}
We check two different implementations of the abstract QCD coupling.
<<SM qcd: execute tests>>=
call test (sm_qcd_1, "sm_qcd_1", &
"running alpha_s", &
u, results)
<<SM qcd: test declarations>>=
public :: sm_qcd_1
<<SM qcd: tests>>=
subroutine sm_qcd_1 (u)
integer, intent(in) :: u
type(qcd_t) :: qcd
write (u, "(A)") "* Test output: sm_qcd_1"
write (u, "(A)") "* Purpose: compute running alpha_s"
write (u, "(A)")
write (u, "(A)") "* Fixed:"
write (u, "(A)")
allocate (alpha_qcd_fixed_t :: qcd%alpha)
call qcd%compute_alphas_md5sum ()
call qcd%write (u)
write (u, *)
write (u, "(1x,A,F10.7)") "alpha_s (mz) =", &
qcd%alpha%get (MZ_REF)
write (u, "(1x,A,F10.7)") "alpha_s (1 TeV) =", &
qcd%alpha%get (1000._default)
write (u, *)
deallocate (qcd%alpha)
write (u, "(A)") "* Running from MZ (LO):"
write (u, "(A)")
allocate (alpha_qcd_from_scale_t :: qcd%alpha)
call qcd%compute_alphas_md5sum ()
call qcd%write (u)
write (u, *)
write (u, "(1x,A,F10.7)") "alpha_s (mz) =", &
qcd%alpha%get (MZ_REF)
write (u, "(1x,A,F10.7)") "alpha_s (1 TeV) =", &
qcd%alpha%get (1000._default)
write (u, *)
write (u, "(A)") "* Running from MZ (NLO):"
write (u, "(A)")
select type (alpha => qcd%alpha)
type is (alpha_qcd_from_scale_t)
alpha%order = 1
end select
call qcd%compute_alphas_md5sum ()
call qcd%write (u)
write (u, *)
write (u, "(1x,A,F10.7)") "alpha_s (mz) =", &
qcd%alpha%get (MZ_REF)
write (u, "(1x,A,F10.7)") "alpha_s (1 TeV) =", &
qcd%alpha%get (1000._default)
write (u, *)
write (u, "(A)") "* Running from MZ (NNLO):"
write (u, "(A)")
select type (alpha => qcd%alpha)
type is (alpha_qcd_from_scale_t)
alpha%order = 2
end select
call qcd%compute_alphas_md5sum ()
call qcd%write (u)
write (u, *)
write (u, "(1x,A,F10.7)") "alpha_s (mz) =", &
qcd%alpha%get (MZ_REF)
write (u, "(1x,A,F10.7)") "alpha_s (1 TeV) =", &
qcd%alpha%get (1000._default)
write (u, *)
deallocate (qcd%alpha)
write (u, "(A)") "* Running from Lambda_QCD (LO):"
write (u, "(A)")
allocate (alpha_qcd_from_lambda_t :: qcd%alpha)
call qcd%compute_alphas_md5sum ()
call qcd%write (u)
write (u, *)
write (u, "(1x,A,F10.7)") "alpha_s (mz) =", &
qcd%alpha%get (MZ_REF)
write (u, "(1x,A,F10.7)") "alpha_s (1 TeV) =", &
qcd%alpha%get (1000._default)
write (u, *)
write (u, "(A)") "* Running from Lambda_QCD (NLO):"
write (u, "(A)")
select type (alpha => qcd%alpha)
type is (alpha_qcd_from_lambda_t)
alpha%order = 1
end select
call qcd%compute_alphas_md5sum ()
call qcd%write (u)
write (u, *)
write (u, "(1x,A,F10.7)") "alpha_s (mz) =", &
qcd%alpha%get (MZ_REF)
write (u, "(1x,A,F10.7)") "alpha_s (1 TeV) =", &
qcd%alpha%get (1000._default)
write (u, *)
write (u, "(A)") "* Running from Lambda_QCD (NNLO):"
write (u, "(A)")
select type (alpha => qcd%alpha)
type is (alpha_qcd_from_lambda_t)
alpha%order = 2
end select
call qcd%compute_alphas_md5sum ()
call qcd%write (u)
write (u, *)
write (u, "(1x,A,F10.7)") "alpha_s (mz) =", &
qcd%alpha%get (MZ_REF)
write (u, "(1x,A,F10.7)") "alpha_s (1 TeV) =", &
qcd%alpha%get (1000._default)
write (u, "(A)")
write (u, "(A)") "* Test output end: sm_qcd_1"
end subroutine sm_qcd_1
@ %def sm_qcd_1
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Shower algorithms}
<<[[shower_algorithms.f90]]>>=
<<File header>>
module shower_algorithms
<<Use kinds>>
use diagnostics
use constants
<<Standard module head>>
<<shower algorithms: public>>
<<shower algorithms: interfaces>>
contains
<<shower algorithms: procedures>>
<<shower algorithms: tests>>
end module shower_algorithms
@ %def shower_algorithms
@ We want to generate emission variables [[x]]$\in\mathds{R}^d$
proportional to
\begin{align}
&\quad f(x)\; \Delta(f, h(x)) \quad\text{with}\\
\Delta(f, H) &= \exp\left\{-\int\text{d}^d x'f(x') \Theta(h(x') -
H)\right\}
\end{align}
The [[true_function]] $f$ is however too complicated and we are only
able to generate [[x]] according to the [[overestimator]] $F$. This
algorithm is described in Appendix B of 0709.2092 and is proven e.g.~in
1211.7204 and hep-ph/0606275. Intuitively speaking, we overestimate the
emission probability and can therefore set [[scale_max = scale]] if the
emission is rejected.
<<shower algorithms: procedures>>=
subroutine generate_vetoed (x, overestimator, true_function, &
sudakov, inverse_sudakov, scale_min)
real(default), dimension(:), intent(out) :: x
!class(rng_t), intent(inout) :: rng
procedure(XXX_function), pointer, intent(in) :: overestimator, true_function
procedure(sudakov_p), pointer, intent(in) :: sudakov, inverse_sudakov
real(default), intent(in) :: scale_min
real(default) :: random, scale_max, scale
scale_max = inverse_sudakov (one)
do while (scale_max > scale_min)
!call rng%generate (random)
scale = inverse_sudakov (random * sudakov (scale_max))
call generate_on_hypersphere (x, overestimator, scale)
!call rng%generate (random)
if (random < true_function (x) / overestimator (x)) then
return !!! accept x
end if
scale_max = scale
end do
end subroutine generate_vetoed
@ %def generate_vetoed
@
<<shower algorithms: procedures>>=
subroutine generate_on_hypersphere (x, overestimator, scale)
real(default), dimension(:), intent(out) :: x
procedure(XXX_function), pointer, intent(in) :: overestimator
real(default), intent(in) :: scale
call msg_bug ("generate_on_hypersphere: not implemented")
end subroutine generate_on_hypersphere
@ %def generate_on_hypersphere
@
<<shower algorithms: interfaces>>=
interface
pure function XXX_function (x)
import
real(default) :: XXX_function
real(default), dimension(:), intent(in) :: x
end function XXX_function
end interface
interface
pure function sudakov_p (x)
import
real(default) :: sudakov_p
real(default), intent(in) :: x
end function sudakov_p
end interface
@
\subsection{Unit tests}
(Currently unused.)
<<XXX shower algorithms: public>>=
public :: shower_algorithms_test
<<XXX shower algorithms: tests>>=
subroutine shower_algorithms_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<shower algorithms: execute tests>>
end subroutine shower_algorithms_test
@ %def shower_algorithms_test
@
\subsubsection{Splitting functions}
<<XXX shower algorithms: execute tests>>=
call test (shower_algorithms_1, "shower_algorithms_1", &
"veto technique", &
u, results)
<<XXX shower algorithms: tests>>=
subroutine shower_algorithms_1 (u)
integer, intent(in) :: u
write (u, "(A)") "* Test output: shower_algorithms_1"
write (u, "(A)") "* Purpose: check veto technique"
write (u, "(A)")
write (u, "(A)") "* Splitting functions:"
write (u, "(A)")
!call assert (u, vanishes (p_qqg_pol (z, +1, -1, +1)))
!call assert (u, nearly_equal ( &
!p_qqg_pol (z, +1, +1, -1) + p_qqg_pol (z, +1, +1, +1),
!p_qqg (z))
write (u, "(A)")
write (u, "(A)") "* Test output end: shower_algorithms_1"
end subroutine shower_algorithms_1
@ %def shower_algorithms_1
Index: trunk/src/beams/beams.nw
===================================================================
--- trunk/src/beams/beams.nw (revision 8293)
+++ trunk/src/beams/beams.nw (revision 8294)
@@ -1,25220 +1,25260 @@
%% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: beams and beam structure
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Beams}
\includemodulegraph{beams}
These modules implement beam configuration and beam structure, the
latter in abstract terms.
\begin{description}
\item[beam\_structures]
The [[beam_structure_t]] type is a messenger type that communicates
the user settings to the \whizard\ core.
\item[beams]
Beam configuration.
\item[sf\_aux]
Tools for handling structure functions and splitting
\item[sf\_mappings]
Mapping functions, useful for structure function implementation
\item[sf\_base]
The abstract structure-function interaction and structure-function
chain types.
\end{description}
These are the implementation modules, the concrete counterparts of
[[sf_base]]:
\begin{description}
\item[sf\_isr]
ISR structure function (photon radiation inclusive and resummed in
collinear and IR regions).
\item[sf\_epa]
Effective Photon Approximation.
\item[sf\_ewa]
Effective $W$ (and $Z$) approximation.
\item[sf\_escan]
Energy spectrum that emulates a uniform energy scan.
\item[sf\_gaussian]
Gaussian beam spread
\item[sf\_beam\_events]
Beam-event generator that reads its input from an external file.
\item[sf\_circe1]
CIRCE1 beam spectra for electrons and photons.
\item[sf\_circe2]
CIRCE2 beam spectra for electrons and photons.
\item[hoppet\_interface]
Support for $b$-quark matching, addon to PDF modules.
\item[sf\_pdf\_builtin]
Direct support for selected hadron PDFs.
\item[sf\_lhapdf]
LHAPDF library support.
\end{description}
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Beam structure}
This module stores the beam structure definition as it is declared in
the SINDARIN script. The structure definition is not analyzed, just
recorded for later use.
We do not capture any numerical parameters, just names of particles and
structure functions.
<<[[beam_structures.f90]]>>=
<<File header>>
module beam_structures
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_19
use diagnostics
use lorentz
use polarizations
<<Standard module head>>
<<Beam structures: public>>
<<Beam structures: types>>
<<Beam structures: interfaces>>
contains
<<Beam structures: procedures>>
end module beam_structures
@ %def beam_structures
@
\subsection{Beam structure elements}
An entry in a beam-structure record consists of a string
that denotes a type of structure function.
<<Beam structures: types>>=
type :: beam_structure_entry_t
logical :: is_valid = .false.
type(string_t) :: name
contains
<<Beam structures: beam structure entry: TBP>>
end type beam_structure_entry_t
@ %def beam_structure_entry_t
@ Output.
<<Beam structures: beam structure entry: TBP>>=
procedure :: to_string => beam_structure_entry_to_string
<<Beam structures: procedures>>=
function beam_structure_entry_to_string (object) result (string)
class(beam_structure_entry_t), intent(in) :: object
type(string_t) :: string
if (object%is_valid) then
string = object%name
else
string = "none"
end if
end function beam_structure_entry_to_string
@ %def beam_structure_entry_to_string
@
A record in the beam-structure sequence denotes either a
structure-function entry, a pair of such entries, or a pair spectrum.
<<Beam structures: types>>=
type :: beam_structure_record_t
type(beam_structure_entry_t), dimension(:), allocatable :: entry
end type beam_structure_record_t
@ %def beam_structure_record_t
@
\subsection{Beam structure type}
The beam-structure object contains the beam particle(s) as simple strings.
The sequence of records indicates the structure functions by name. No
numerical parameters are stored.
<<Beam structures: public>>=
public :: beam_structure_t
<<Beam structures: types>>=
type :: beam_structure_t
private
integer :: n_beam = 0
type(string_t), dimension(:), allocatable :: prt
type(beam_structure_record_t), dimension(:), allocatable :: record
type(smatrix_t), dimension(:), allocatable :: smatrix
real(default), dimension(:), allocatable :: pol_f
real(default), dimension(:), allocatable :: p
real(default), dimension(:), allocatable :: theta
real(default), dimension(:), allocatable :: phi
contains
<<Beam structures: beam structure: TBP>>
end type beam_structure_t
@ %def beam_structure_t
@ The finalizer deletes all contents explicitly, so we can continue
with an empty beam record. (It is not needed for deallocation.) We
have distinct finalizers for the independent parts of the beam structure.
<<Beam structures: beam structure: TBP>>=
procedure :: final_sf => beam_structure_final_sf
<<Beam structures: procedures>>=
subroutine beam_structure_final_sf (object)
class(beam_structure_t), intent(inout) :: object
if (allocated (object%prt)) deallocate (object%prt)
if (allocated (object%record)) deallocate (object%record)
object%n_beam = 0
end subroutine beam_structure_final_sf
@ %def beam_structure_final_sf
@ Output. The actual information fits in a single line, therefore we can
provide a [[to_string]] method. The [[show]] method also lists the
current values of relevant global variables.
<<Beam structures: beam structure: TBP>>=
procedure :: write => beam_structure_write
procedure :: to_string => beam_structure_to_string
<<Beam structures: procedures>>=
subroutine beam_structure_write (object, unit)
class(beam_structure_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit)
write (u, "(1x,A,A)") "Beam structure: ", char (object%to_string ())
if (allocated (object%smatrix)) then
do i = 1, size (object%smatrix)
write (u, "(3x,A,I0,A)") "polarization (beam ", i, "):"
call object%smatrix(i)%write (u, indent=2)
end do
end if
if (allocated (object%pol_f)) then
write (u, "(3x,A,F10.7,:,',',F10.7)") "polarization degree =", &
object%pol_f
end if
if (allocated (object%p)) then
write (u, "(3x,A," // FMT_19 // ",:,','," // FMT_19 // &
")") "momentum =", object%p
end if
if (allocated (object%theta)) then
write (u, "(3x,A," // FMT_19 // ",:,','," // FMT_19 // &
")") "angle th =", object%theta
end if
if (allocated (object%phi)) then
write (u, "(3x,A," // FMT_19 // ",:,','," // FMT_19 // &
")") "angle ph =", object%phi
end if
end subroutine beam_structure_write
function beam_structure_to_string (object, sf_only) result (string)
class(beam_structure_t), intent(in) :: object
logical, intent(in), optional :: sf_only
type(string_t) :: string
integer :: i, j
logical :: with_beams
with_beams = .true.; if (present (sf_only)) with_beams = .not. sf_only
select case (object%n_beam)
case (1)
if (with_beams) then
string = object%prt(1)
else
string = ""
end if
case (2)
if (with_beams) then
string = object%prt(1) // ", " // object%prt(2)
else
string = ""
end if
if (allocated (object%record)) then
if (size (object%record) > 0) then
if (with_beams) string = string // " => "
do i = 1, size (object%record)
if (i > 1) string = string // " => "
do j = 1, size (object%record(i)%entry)
if (j > 1) string = string // ", "
string = string // object%record(i)%entry(j)%to_string ()
end do
end do
end if
end if
case default
string = "[any particles]"
end select
end function beam_structure_to_string
@ %def beam_structure_write beam_structure_to_string
@ Initializer: dimension the beam structure record. Each array
element denotes the number of entries for a record within the
beam-structure sequence. The number of entries is either one or two,
while the number of records is unlimited.
<<Beam structures: beam structure: TBP>>=
procedure :: init_sf => beam_structure_init_sf
<<Beam structures: procedures>>=
subroutine beam_structure_init_sf (beam_structure, prt, dim_array)
class(beam_structure_t), intent(inout) :: beam_structure
type(string_t), dimension(:), intent(in) :: prt
integer, dimension(:), intent(in), optional :: dim_array
integer :: i
call beam_structure%final_sf ()
beam_structure%n_beam = size (prt)
allocate (beam_structure%prt (size (prt)))
beam_structure%prt = prt
if (present (dim_array)) then
allocate (beam_structure%record (size (dim_array)))
do i = 1, size (dim_array)
allocate (beam_structure%record(i)%entry (dim_array(i)))
end do
else
allocate (beam_structure%record (0))
end if
end subroutine beam_structure_init_sf
@ %def beam_structure_init_sf
@ Set an entry, specified by record number and entry number.
<<Beam structures: beam structure: TBP>>=
procedure :: set_sf => beam_structure_set_sf
<<Beam structures: procedures>>=
subroutine beam_structure_set_sf (beam_structure, i, j, name)
class(beam_structure_t), intent(inout) :: beam_structure
integer, intent(in) :: i, j
type(string_t), intent(in) :: name
associate (entry => beam_structure%record(i)%entry(j))
entry%name = name
entry%is_valid = .true.
end associate
end subroutine beam_structure_set_sf
@ %def beam_structure_set_sf
@ Expand the beam-structure object. (i) For a pair spectrum, keep the
entry. (ii) For a single-particle structure function written as a
single entry, replace this by a record with two entries.
(ii) For a record with two nontrivial entries, separate this into two
records with one trivial entry each.
To achieve this, we need a function that tells us whether an entry is
a spectrum or a structure function. It returns 0 for a trivial entry,
1 for a single-particle structure function, and 2 for a two-particle
spectrum.
<<Beam structures: interfaces>>=
abstract interface
function strfun_mode_fun (name) result (n)
import
type(string_t), intent(in) :: name
integer :: n
end function strfun_mode_fun
end interface
@ %def is_spectrum_t
@ Algorithm: (1) Mark entries as invalid where necessary. (2) Count
the number of entries that we will need. (3) Expand and copy
entries to a new record array. (4) Replace the old array by the new one.
<<Beam structures: beam structure: TBP>>=
procedure :: expand => beam_structure_expand
<<Beam structures: procedures>>=
subroutine beam_structure_expand (beam_structure, strfun_mode)
class(beam_structure_t), intent(inout) :: beam_structure
procedure(strfun_mode_fun) :: strfun_mode
type(beam_structure_record_t), dimension(:), allocatable :: new
integer :: n_record, i, j
if (.not. allocated (beam_structure%record)) return
do i = 1, size (beam_structure%record)
associate (entry => beam_structure%record(i)%entry)
do j = 1, size (entry)
select case (strfun_mode (entry(j)%name))
case (0); entry(j)%is_valid = .false.
end select
end do
end associate
end do
n_record = 0
do i = 1, size (beam_structure%record)
associate (entry => beam_structure%record(i)%entry)
select case (size (entry))
case (1)
if (entry(1)%is_valid) then
select case (strfun_mode (entry(1)%name))
case (1); n_record = n_record + 2
case (2); n_record = n_record + 1
end select
end if
case (2)
do j = 1, 2
if (entry(j)%is_valid) then
select case (strfun_mode (entry(j)%name))
case (1); n_record = n_record + 1
case (2)
call beam_structure%write ()
call msg_fatal ("Pair spectrum used as &
&single-particle structure function")
end select
end if
end do
end select
end associate
end do
allocate (new (n_record))
n_record = 0
do i = 1, size (beam_structure%record)
associate (entry => beam_structure%record(i)%entry)
select case (size (entry))
case (1)
if (entry(1)%is_valid) then
select case (strfun_mode (entry(1)%name))
case (1)
n_record = n_record + 1
allocate (new(n_record)%entry (2))
new(n_record)%entry(1) = entry(1)
n_record = n_record + 1
allocate (new(n_record)%entry (2))
new(n_record)%entry(2) = entry(1)
case (2)
n_record = n_record + 1
allocate (new(n_record)%entry (1))
new(n_record)%entry(1) = entry(1)
end select
end if
case (2)
do j = 1, 2
if (entry(j)%is_valid) then
n_record = n_record + 1
allocate (new(n_record)%entry (2))
new(n_record)%entry(j) = entry(j)
end if
end do
end select
end associate
end do
call move_alloc (from = new, to = beam_structure%record)
end subroutine beam_structure_expand
@ %def beam_structure_expand
@
\subsection{Polarization}
To record polarization, we provide an allocatable array of [[smatrix]]
objects, sparse matrices. The polarization structure is independent of the
structure-function setup, they are combined only when an actual beam object is
constructed.
<<Beam structures: beam structure: TBP>>=
procedure :: final_pol => beam_structure_final_pol
procedure :: init_pol => beam_structure_init_pol
<<Beam structures: procedures>>=
subroutine beam_structure_final_pol (beam_structure)
class(beam_structure_t), intent(inout) :: beam_structure
if (allocated (beam_structure%smatrix)) deallocate (beam_structure%smatrix)
if (allocated (beam_structure%pol_f)) deallocate (beam_structure%pol_f)
end subroutine beam_structure_final_pol
subroutine beam_structure_init_pol (beam_structure, n)
class(beam_structure_t), intent(inout) :: beam_structure
integer, intent(in) :: n
if (allocated (beam_structure%smatrix)) deallocate (beam_structure%smatrix)
allocate (beam_structure%smatrix (n))
if (.not. allocated (beam_structure%pol_f)) &
allocate (beam_structure%pol_f (n), source = 1._default)
end subroutine beam_structure_init_pol
@ %def beam_structure_final_pol
@ %def beam_structure_init_pol
@ Check if polarized beams are used.
<<Beam structures: beam structure: TBP>>=
procedure :: has_polarized_beams => beam_structure_has_polarized_beams
<<Beam structures: procedures>>=
elemental function beam_structure_has_polarized_beams (beam_structure) result (pol)
logical :: pol
class(beam_structure_t), intent(in) :: beam_structure
if (allocated (beam_structure%pol_f)) then
pol = any (beam_structure%pol_f /= 0)
else
pol = .false.
end if
end function beam_structure_has_polarized_beams
@ %def beam_structure_has_polarized_beams
@ Directly copy the spin density matrices.
<<Beam structures: beam structure: TBP>>=
procedure :: set_smatrix => beam_structure_set_smatrix
<<Beam structures: procedures>>=
subroutine beam_structure_set_smatrix (beam_structure, i, smatrix)
class(beam_structure_t), intent(inout) :: beam_structure
integer, intent(in) :: i
type(smatrix_t), intent(in) :: smatrix
beam_structure%smatrix(i) = smatrix
end subroutine beam_structure_set_smatrix
@ %def beam_structure_set_smatrix
@ Initialize one of the spin density matrices manually.
<<Beam structures: beam structure: TBP>>=
procedure :: init_smatrix => beam_structure_init_smatrix
<<Beam structures: procedures>>=
subroutine beam_structure_init_smatrix (beam_structure, i, n_entry)
class(beam_structure_t), intent(inout) :: beam_structure
integer, intent(in) :: i
integer, intent(in) :: n_entry
call beam_structure%smatrix(i)%init (2, n_entry)
end subroutine beam_structure_init_smatrix
@ %def beam_structure_init_smatrix
@ Set a polarization entry.
<<Beam structures: beam structure: TBP>>=
procedure :: set_sentry => beam_structure_set_sentry
<<Beam structures: procedures>>=
subroutine beam_structure_set_sentry &
(beam_structure, i, i_entry, index, value)
class(beam_structure_t), intent(inout) :: beam_structure
integer, intent(in) :: i
integer, intent(in) :: i_entry
integer, dimension(:), intent(in) :: index
complex(default), intent(in) :: value
call beam_structure%smatrix(i)%set_entry (i_entry, index, value)
end subroutine beam_structure_set_sentry
@ %def beam_structure_set_sentry
@ Set the array of polarization fractions.
<<Beam structures: beam structure: TBP>>=
procedure :: set_pol_f => beam_structure_set_pol_f
<<Beam structures: procedures>>=
subroutine beam_structure_set_pol_f (beam_structure, f)
class(beam_structure_t), intent(inout) :: beam_structure
real(default), dimension(:), intent(in) :: f
if (allocated (beam_structure%pol_f)) deallocate (beam_structure%pol_f)
allocate (beam_structure%pol_f (size (f)), source = f)
end subroutine beam_structure_set_pol_f
@ %def beam_structure_set_pol_f
@
\subsection{Beam momenta}
By default, beam momenta are deduced from the [[sqrts]] value or from
the mass of the decaying particle, assuming a c.m.\ setup. Here we
set them explicitly.
<<Beam structures: beam structure: TBP>>=
procedure :: final_mom => beam_structure_final_mom
<<Beam structures: procedures>>=
subroutine beam_structure_final_mom (beam_structure)
class(beam_structure_t), intent(inout) :: beam_structure
if (allocated (beam_structure%p)) deallocate (beam_structure%p)
if (allocated (beam_structure%theta)) deallocate (beam_structure%theta)
if (allocated (beam_structure%phi)) deallocate (beam_structure%phi)
end subroutine beam_structure_final_mom
@ %def beam_structure_final_mom
<<Beam structures: beam structure: TBP>>=
procedure :: set_momentum => beam_structure_set_momentum
procedure :: set_theta => beam_structure_set_theta
procedure :: set_phi => beam_structure_set_phi
<<Beam structures: procedures>>=
subroutine beam_structure_set_momentum (beam_structure, p)
class(beam_structure_t), intent(inout) :: beam_structure
real(default), dimension(:), intent(in) :: p
if (allocated (beam_structure%p)) deallocate (beam_structure%p)
allocate (beam_structure%p (size (p)), source = p)
end subroutine beam_structure_set_momentum
subroutine beam_structure_set_theta (beam_structure, theta)
class(beam_structure_t), intent(inout) :: beam_structure
real(default), dimension(:), intent(in) :: theta
if (allocated (beam_structure%theta)) deallocate (beam_structure%theta)
allocate (beam_structure%theta (size (theta)), source = theta)
end subroutine beam_structure_set_theta
subroutine beam_structure_set_phi (beam_structure, phi)
class(beam_structure_t), intent(inout) :: beam_structure
real(default), dimension(:), intent(in) :: phi
if (allocated (beam_structure%phi)) deallocate (beam_structure%phi)
allocate (beam_structure%phi (size (phi)), source = phi)
end subroutine beam_structure_set_phi
@ %def beam_structure_set_momentum
@ %def beam_structure_set_theta
@ %def beam_structure_set_phi
@
\subsection{Get contents}
Look at the incoming particles. We may also have the case that beam
particles are not specified, but polarization.
<<Beam structures: beam structure: TBP>>=
procedure :: is_set => beam_structure_is_set
procedure :: get_n_beam => beam_structure_get_n_beam
procedure :: get_prt => beam_structure_get_prt
<<Beam structures: procedures>>=
function beam_structure_is_set (beam_structure) result (flag)
class(beam_structure_t), intent(in) :: beam_structure
logical :: flag
flag = beam_structure%n_beam > 0 .or. beam_structure%asymmetric ()
end function beam_structure_is_set
function beam_structure_get_n_beam (beam_structure) result (n)
class(beam_structure_t), intent(in) :: beam_structure
integer :: n
n = beam_structure%n_beam
end function beam_structure_get_n_beam
function beam_structure_get_prt (beam_structure) result (prt)
class(beam_structure_t), intent(in) :: beam_structure
type(string_t), dimension(:), allocatable :: prt
allocate (prt (size (beam_structure%prt)))
prt = beam_structure%prt
end function beam_structure_get_prt
@ %def beam_structure_is_set
@ %def beam_structure_get_n_beam
@ %def beam_structure_get_prt
@
Return the number of records.
<<Beam structures: beam structure: TBP>>=
procedure :: get_n_record => beam_structure_get_n_record
<<Beam structures: procedures>>=
function beam_structure_get_n_record (beam_structure) result (n)
class(beam_structure_t), intent(in) :: beam_structure
integer :: n
if (allocated (beam_structure%record)) then
n = size (beam_structure%record)
else
n = 0
end if
end function beam_structure_get_n_record
@ %def beam_structure_get_n_record
@ Return an array consisting of the beam indices affected by the valid
entries within a record. After expansion, there should be exactly one
valid entry per record.
<<Beam structures: beam structure: TBP>>=
procedure :: get_i_entry => beam_structure_get_i_entry
<<Beam structures: procedures>>=
function beam_structure_get_i_entry (beam_structure, i) result (i_entry)
class(beam_structure_t), intent(in) :: beam_structure
integer, intent(in) :: i
integer, dimension(:), allocatable :: i_entry
associate (record => beam_structure%record(i))
select case (size (record%entry))
case (1)
if (record%entry(1)%is_valid) then
allocate (i_entry (2), source = [1, 2])
else
allocate (i_entry (0))
end if
case (2)
if (all (record%entry%is_valid)) then
allocate (i_entry (2), source = [1, 2])
else if (record%entry(1)%is_valid) then
allocate (i_entry (1), source = [1])
else if (record%entry(2)%is_valid) then
allocate (i_entry (1), source = [2])
else
allocate (i_entry (0))
end if
end select
end associate
end function beam_structure_get_i_entry
@ %def beam_structure_get_i_entry
@ Return the name of the first valid entry within a record. After
expansion, there should be exactly one valid entry per record.
<<Beam structures: beam structure: TBP>>=
procedure :: get_name => beam_structure_get_name
<<Beam structures: procedures>>=
function beam_structure_get_name (beam_structure, i) result (name)
type(string_t) :: name
class(beam_structure_t), intent(in) :: beam_structure
integer, intent(in) :: i
associate (record => beam_structure%record(i))
if (record%entry(1)%is_valid) then
name = record%entry(1)%name
else if (size (record%entry) == 2) then
name = record%entry(2)%name
end if
end associate
end function beam_structure_get_name
@ %def beam_structure_get_name
@
<<Beam structures: beam structure: TBP>>=
procedure :: has_pdf => beam_structure_has_pdf
<<Beam structures: procedures>>=
function beam_structure_has_pdf (beam_structure) result (has_pdf)
logical :: has_pdf
class(beam_structure_t), intent(in) :: beam_structure
integer :: i
type(string_t) :: name
has_pdf = .false.
do i = 1, beam_structure%get_n_record ()
name = beam_structure%get_name (i)
has_pdf = has_pdf .or. name == var_str ("pdf_builtin") .or. name == var_str ("lhapdf")
end do
end function beam_structure_has_pdf
@ %def beam_structure_has_pdf
@ Return true if the beam structure contains a particular structure
function identifier (such as [[lhapdf]], [[isr]], etc.)
<<Beam structures: beam structure: TBP>>=
procedure :: contains => beam_structure_contains
<<Beam structures: procedures>>=
function beam_structure_contains (beam_structure, name) result (flag)
class(beam_structure_t), intent(in) :: beam_structure
character(*), intent(in) :: name
logical :: flag
integer :: i, j
flag = .false.
if (allocated (beam_structure%record)) then
do i = 1, size (beam_structure%record)
do j = 1, size (beam_structure%record(i)%entry)
flag = beam_structure%record(i)%entry(j)%name == name
if (flag) return
end do
end do
end if
end function beam_structure_contains
@ %def beam_structure_contains
@ Return polarization data.
<<Beam structures: beam structure: TBP>>=
procedure :: polarized => beam_structure_polarized
procedure :: get_smatrix => beam_structure_get_smatrix
procedure :: get_pol_f => beam_structure_get_pol_f
procedure :: asymmetric => beam_structure_asymmetric
<<Beam structures: procedures>>=
function beam_structure_polarized (beam_structure) result (flag)
class(beam_structure_t), intent(in) :: beam_structure
logical :: flag
flag = allocated (beam_structure%smatrix)
end function beam_structure_polarized
function beam_structure_get_smatrix (beam_structure) result (smatrix)
class(beam_structure_t), intent(in) :: beam_structure
type(smatrix_t), dimension(:), allocatable :: smatrix
allocate (smatrix (size (beam_structure%smatrix)), &
source = beam_structure%smatrix)
end function beam_structure_get_smatrix
function beam_structure_get_pol_f (beam_structure) result (pol_f)
class(beam_structure_t), intent(in) :: beam_structure
real(default), dimension(:), allocatable :: pol_f
allocate (pol_f (size (beam_structure%pol_f)), &
source = beam_structure%pol_f)
end function beam_structure_get_pol_f
function beam_structure_asymmetric (beam_structure) result (flag)
class(beam_structure_t), intent(in) :: beam_structure
logical :: flag
flag = allocated (beam_structure%p) &
.or. allocated (beam_structure%theta) &
.or. allocated (beam_structure%phi)
end function beam_structure_asymmetric
@ %def beam_structure_polarized
@ %def beam_structure_get_smatrix
@ %def beam_structure_get_pol_f
@ %def beam_structure_asymmetric
@ Return the beam momenta (the space part, i.e., three-momenta). This
is meaningful only if momenta and, optionally, angles have been set.
<<Beam structures: beam structure: TBP>>=
procedure :: get_momenta => beam_structure_get_momenta
<<Beam structures: procedures>>=
function beam_structure_get_momenta (beam_structure) result (p)
class(beam_structure_t), intent(in) :: beam_structure
type(vector3_t), dimension(:), allocatable :: p
real(default), dimension(:), allocatable :: theta, phi
integer :: n, i
if (allocated (beam_structure%p)) then
n = size (beam_structure%p)
if (allocated (beam_structure%theta)) then
if (size (beam_structure%theta) == n) then
allocate (theta (n), source = beam_structure%theta)
else
call msg_fatal ("Beam structure: mismatch in momentum vs. &
&angle theta specification")
end if
else
allocate (theta (n), source = 0._default)
end if
if (allocated (beam_structure%phi)) then
if (size (beam_structure%phi) == n) then
allocate (phi (n), source = beam_structure%phi)
else
call msg_fatal ("Beam structure: mismatch in momentum vs. &
&angle phi specification")
end if
else
allocate (phi (n), source = 0._default)
end if
allocate (p (n))
do i = 1, n
p(i) = beam_structure%p(i) * vector3_moving ([ &
sin (theta(i)) * cos (phi(i)), &
sin (theta(i)) * sin (phi(i)), &
cos (theta(i))])
end do
if (n == 2) p(2) = - p(2)
else
call msg_fatal ("Beam structure: angle theta/phi specified but &
&momentum/a p undefined")
end if
end function beam_structure_get_momenta
@ %def beam_structure_get_momenta
@ Check for a complete beam structure. The [[applies]] flag tells if
the beam structure should actually be used for a process with the
given [[n_in]] number of incoming particles.
It set if the beam structure matches the process as either decay or
scattering. It is unset if beam structure references a scattering
setup but the process is a decay. It is also unset if the beam
structure itself is empty.
If the beam structure cannot be used, terminate with fatal error.
<<Beam structures: beam structure: TBP>>=
procedure :: check_against_n_in => beam_structure_check_against_n_in
<<Beam structures: procedures>>=
subroutine beam_structure_check_against_n_in (beam_structure, n_in, applies)
class(beam_structure_t), intent(in) :: beam_structure
integer, intent(in) :: n_in
logical, intent(out) :: applies
if (beam_structure%is_set ()) then
if (n_in == beam_structure%get_n_beam ()) then
applies = .true.
else if (beam_structure%get_n_beam () == 0) then
call msg_fatal &
("Asymmetric beams: missing beam particle specification")
applies = .false.
else
call msg_fatal &
("Mismatch of process and beam setup (scattering/decay)")
applies = .false.
end if
else
applies = .false.
end if
end subroutine beam_structure_check_against_n_in
@ %def beam_structure_check_against_n_in
@
\subsection{Unit Tests}
Test module, followed by the corresponding implementation module.
<<[[beam_structures_ut.f90]]>>=
<<File header>>
module beam_structures_ut
use unit_tests
use beam_structures_uti
<<Standard module head>>
<<Beam structures: public test>>
contains
<<Beam structures: test driver>>
end module beam_structures_ut
@ %def beam_structures_ut
@
<<[[beam_structures_uti.f90]]>>=
<<File header>>
module beam_structures_uti
<<Use kinds>>
<<Use strings>>
use beam_structures
<<Standard module head>>
<<Beam structures: test declarations>>
contains
<<Beam structures: tests>>
<<Beam structures: test auxiliary>>
end module beam_structures_uti
@ %def beam_structures_ut
@ API: driver for the unit tests below.
<<Beam structures: public test>>=
public :: beam_structures_test
<<Beam structures: test driver>>=
subroutine beam_structures_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Beam structures: execute tests>>
end subroutine beam_structures_test
@ %def beam_structures_tests
@
\subsubsection{Empty structure}
<<Beam structures: execute tests>>=
call test (beam_structures_1, "beam_structures_1", &
"empty beam structure record", &
u, results)
<<Beam structures: test declarations>>=
public :: beam_structures_1
<<Beam structures: tests>>=
subroutine beam_structures_1 (u)
integer, intent(in) :: u
type(beam_structure_t) :: beam_structure
write (u, "(A)") "* Test output: beam_structures_1"
write (u, "(A)") "* Purpose: display empty beam structure record"
write (u, "(A)")
call beam_structure%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_structures_1"
end subroutine beam_structures_1
@ %def beam_structures_1
@
\subsubsection{Nontrivial configurations}
<<Beam structures: execute tests>>=
call test (beam_structures_2, "beam_structures_2", &
"beam structure records", &
u, results)
<<Beam structures: test declarations>>=
public :: beam_structures_2
<<Beam structures: tests>>=
subroutine beam_structures_2 (u)
integer, intent(in) :: u
type(beam_structure_t) :: beam_structure
integer, dimension(0) :: empty_array
type(string_t) :: s
write (u, "(A)") "* Test output: beam_structures_2"
write (u, "(A)") "* Purpose: setup beam structure records"
write (u, "(A)")
s = "s"
call beam_structure%init_sf ([s], empty_array)
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%init_sf ([s, s], [1])
call beam_structure%set_sf (1, 1, var_str ("a"))
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%init_sf ([s, s], [2])
call beam_structure%set_sf (1, 1, var_str ("a"))
call beam_structure%set_sf (1, 2, var_str ("b"))
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%init_sf ([s, s], [2, 1])
call beam_structure%set_sf (1, 1, var_str ("a"))
call beam_structure%set_sf (1, 2, var_str ("b"))
call beam_structure%set_sf (2, 1, var_str ("c"))
call beam_structure%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_structures_2"
end subroutine beam_structures_2
@ %def beam_structures_2
@
\subsubsection{Expansion}
Provide a function that tells, for the dummy structure function names
used here, whether they are considered a two-particle spectrum or a
single-particle structure function:
<<Beam structures: test auxiliary>>=
function test_strfun_mode (name) result (n)
type(string_t), intent(in) :: name
integer :: n
select case (char (name))
case ("a"); n = 2
case ("b"); n = 1
case default; n = 0
end select
end function test_strfun_mode
@ %def test_ist_pair_spectrum
@
<<Beam structures: execute tests>>=
call test (beam_structures_3, "beam_structures_3", &
"beam structure expansion", &
u, results)
<<Beam structures: test declarations>>=
public :: beam_structures_3
<<Beam structures: tests>>=
subroutine beam_structures_3 (u)
integer, intent(in) :: u
type(beam_structure_t) :: beam_structure
type(string_t) :: s
write (u, "(A)") "* Test output: beam_structures_3"
write (u, "(A)") "* Purpose: expand beam structure records"
write (u, "(A)")
s = "s"
write (u, "(A)") "* Pair spectrum (keep as-is)"
write (u, "(A)")
call beam_structure%init_sf ([s, s], [1])
call beam_structure%set_sf (1, 1, var_str ("a"))
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%expand (test_strfun_mode)
call beam_structure%write (u)
write (u, "(A)")
write (u, "(A)") "* Structure function pair (expand)"
write (u, "(A)")
call beam_structure%init_sf ([s, s], [2])
call beam_structure%set_sf (1, 1, var_str ("b"))
call beam_structure%set_sf (1, 2, var_str ("b"))
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%expand (test_strfun_mode)
call beam_structure%write (u)
write (u, "(A)")
write (u, "(A)") "* Structure function (separate and expand)"
write (u, "(A)")
call beam_structure%init_sf ([s, s], [1])
call beam_structure%set_sf (1, 1, var_str ("b"))
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%expand (test_strfun_mode)
call beam_structure%write (u)
write (u, "(A)")
write (u, "(A)") "* Combination"
write (u, "(A)")
call beam_structure%init_sf ([s, s], [1, 1])
call beam_structure%set_sf (1, 1, var_str ("a"))
call beam_structure%set_sf (2, 1, var_str ("b"))
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%expand (test_strfun_mode)
call beam_structure%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_structures_3"
end subroutine beam_structures_3
@ %def beam_structures_3
@
\subsubsection{Public methods}
Check the methods that can be called to get the beam-structure
contents.
<<Beam structures: execute tests>>=
call test (beam_structures_4, "beam_structures_4", &
"beam structure contents", &
u, results)
<<Beam structures: test declarations>>=
public :: beam_structures_4
<<Beam structures: tests>>=
subroutine beam_structures_4 (u)
integer, intent(in) :: u
type(beam_structure_t) :: beam_structure
type(string_t) :: s
type(string_t), dimension(2) :: prt
integer :: i
write (u, "(A)") "* Test output: beam_structures_4"
write (u, "(A)") "* Purpose: check the API"
write (u, "(A)")
s = "s"
write (u, "(A)") "* Structure-function combination"
write (u, "(A)")
call beam_structure%init_sf ([s, s], [1, 2, 2])
call beam_structure%set_sf (1, 1, var_str ("a"))
call beam_structure%set_sf (2, 1, var_str ("b"))
call beam_structure%set_sf (3, 2, var_str ("c"))
call beam_structure%write (u)
write (u, *)
write (u, "(1x,A,I0)") "n_beam = ", beam_structure%get_n_beam ()
prt = beam_structure%get_prt ()
write (u, "(1x,A,2(1x,A))") "prt =", char (prt(1)), char (prt(2))
write (u, *)
write (u, "(1x,A,I0)") "n_record = ", beam_structure%get_n_record ()
do i = 1, 3
write (u, "(A)")
write (u, "(1x,A,I0,A,A)") "name(", i, ") = ", &
char (beam_structure%get_name (i))
write (u, "(1x,A,I0,A,2(1x,I0))") "i_entry(", i, ") =", &
beam_structure%get_i_entry (i)
end do
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_structures_4"
end subroutine beam_structures_4
@ %def beam_structures_4
@
\subsubsection{Polarization}
The polarization properties are independent from the structure-function setup.
<<Beam structures: execute tests>>=
call test (beam_structures_5, "beam_structures_5", &
"polarization", &
u, results)
<<Beam structures: test declarations>>=
public :: beam_structures_5
<<Beam structures: tests>>=
subroutine beam_structures_5 (u)
integer, intent(in) :: u
type(beam_structure_t) :: beam_structure
integer, dimension(0) :: empty_array
type(string_t) :: s
write (u, "(A)") "* Test output: beam_structures_5"
write (u, "(A)") "* Purpose: setup polarization in beam structure records"
write (u, "(A)")
s = "s"
call beam_structure%init_sf ([s], empty_array)
call beam_structure%init_pol (1)
call beam_structure%init_smatrix (1, 1)
call beam_structure%set_sentry (1, 1, [0,0], (1._default, 0._default))
call beam_structure%set_pol_f ([0.5_default])
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%final_sf ()
call beam_structure%final_pol ()
call beam_structure%init_sf ([s, s], [1])
call beam_structure%set_sf (1, 1, var_str ("a"))
call beam_structure%init_pol (2)
call beam_structure%init_smatrix (1, 2)
call beam_structure%set_sentry (1, 1, [-1,1], (0.5_default,-0.5_default))
call beam_structure%set_sentry (1, 2, [ 1,1], (1._default, 0._default))
call beam_structure%init_smatrix (2, 0)
call beam_structure%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_structures_5"
end subroutine beam_structures_5
@ %def beam_structures_5
@
\subsubsection{Momenta}
The momenta are independent from the structure-function setup.
<<Beam structures: execute tests>>=
call test (beam_structures_6, "beam_structures_6", &
"momenta", &
u, results)
<<Beam structures: test declarations>>=
public :: beam_structures_6
<<Beam structures: tests>>=
subroutine beam_structures_6 (u)
integer, intent(in) :: u
type(beam_structure_t) :: beam_structure
integer, dimension(0) :: empty_array
type(string_t) :: s
write (u, "(A)") "* Test output: beam_structures_6"
write (u, "(A)") "* Purpose: setup momenta in beam structure records"
write (u, "(A)")
s = "s"
call beam_structure%init_sf ([s], empty_array)
call beam_structure%set_momentum ([500._default])
call beam_structure%write (u)
write (u, "(A)")
call beam_structure%final_sf ()
call beam_structure%final_mom ()
call beam_structure%init_sf ([s, s], [1])
call beam_structure%set_momentum ([500._default, 700._default])
call beam_structure%set_theta ([0._default, 0.1_default])
call beam_structure%set_phi ([0._default, 1.51_default])
call beam_structure%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_structures_6"
end subroutine beam_structures_6
@ %def beam_structures_6
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Beams for collisions and decays}
<<[[beams.f90]]>>=
<<File header>>
module beams
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_19
use numeric_utils
use diagnostics
use md5
use lorentz
use model_data
use flavors
use quantum_numbers
use state_matrices
use interactions
use polarizations
use beam_structures
<<Standard module head>>
<<Beams: public>>
<<Beams: types>>
<<Beams: interfaces>>
contains
<<Beams: procedures>>
end module beams
@ %def beams
@
\subsection{Beam data}
The beam data type contains beam data for one or two beams, depending
on whether we are dealing with beam collisions or particle decay. In
addition, it holds the c.m.\ energy [[sqrts]], the Lorentz
transformation [[L]] that transforms the c.m.\ system into the lab
system, and the pair of c.m.\ momenta.
<<Beams: public>>=
public :: beam_data_t
<<Beams: types>>=
type :: beam_data_t
logical :: initialized = .false.
integer :: n = 0
type(flavor_t), dimension(:), allocatable :: flv
real(default), dimension(:), allocatable :: mass
type(pmatrix_t), dimension(:), allocatable :: pmatrix
logical :: lab_is_cm_frame = .true.
type(vector4_t), dimension(:), allocatable :: p_cm
type(vector4_t), dimension(:), allocatable :: p
type(lorentz_transformation_t), allocatable :: L_cm_to_lab
real(default) :: sqrts = 0
character(32) :: md5sum = ""
contains
<<Beams: beam data: TBP>>
end type beam_data_t
@ %def beam_data_t
@ Generic initializer. This is called by the specific initializers
below. Initialize either for decay or for collision.
<<Beams: procedures>>=
subroutine beam_data_init (beam_data, n)
type(beam_data_t), intent(out) :: beam_data
integer, intent(in) :: n
beam_data%n = n
allocate (beam_data%flv (n))
allocate (beam_data%mass (n))
allocate (beam_data%pmatrix (n))
allocate (beam_data%p_cm (n))
allocate (beam_data%p (n))
beam_data%initialized = .true.
end subroutine beam_data_init
@ %def beam_data_init
@ Finalizer: needed for the polarization components of the beams.
<<Beams: beam data: TBP>>=
procedure :: final => beam_data_final
<<Beams: procedures>>=
subroutine beam_data_final (beam_data)
class(beam_data_t), intent(inout) :: beam_data
beam_data%initialized = .false.
end subroutine beam_data_final
@ %def beam_data_final
@ The verbose (default) version is for debugging. The short version
is for screen output in the UI.
<<Beams: beam data: TBP>>=
procedure :: write => beam_data_write
<<Beams: procedures>>=
subroutine beam_data_write (beam_data, unit, verbose, write_md5sum)
class(beam_data_t), intent(in) :: beam_data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose, write_md5sum
integer :: prt_name_len
logical :: verb, write_md5
integer :: u
u = given_output_unit (unit); if (u < 0) return
verb = .false.; if (present (verbose)) verb = verbose
write_md5 = verb; if (present (write_md5sum)) write_md5 = write_md5sum
if (.not. beam_data%initialized) then
write (u, "(1x,A)") "Beam data: [undefined]"
return
end if
prt_name_len = maxval (len (beam_data%flv%get_name ()))
select case (beam_data%n)
case (1)
write (u, "(1x,A)") "Beam data (decay):"
if (verb) then
call write_prt (1)
call beam_data%pmatrix(1)%write (u)
write (u, *) "R.f. momentum:"
call vector4_write (beam_data%p_cm(1), u)
write (u, *) "Lab momentum:"
call vector4_write (beam_data%p(1), u)
else
call write_prt (1)
end if
case (2)
write (u, "(1x,A)") "Beam data (collision):"
if (verb) then
call write_prt (1)
call beam_data%pmatrix(1)%write (u)
call write_prt (2)
call beam_data%pmatrix(2)%write (u)
call write_sqrts
write (u, *) "C.m. momenta:"
call vector4_write (beam_data%p_cm(1), u)
call vector4_write (beam_data%p_cm(2), u)
write (u, *) "Lab momenta:"
call vector4_write (beam_data%p(1), u)
call vector4_write (beam_data%p(2), u)
else
call write_prt (1)
call write_prt (2)
call write_sqrts
end if
end select
if (allocated (beam_data%L_cm_to_lab)) then
if (verb) then
call lorentz_transformation_write (beam_data%L_cm_to_lab, u)
else
write (u, "(1x,A)") "Beam structure: lab and c.m. frame differ"
end if
end if
if (write_md5) then
write (u, *) "MD5 sum: ", beam_data%md5sum
end if
contains
subroutine write_sqrts
character(80) :: sqrts_str
write (sqrts_str, "(" // FMT_19 // ")") beam_data%sqrts
write (u, "(3x,A)") "sqrts = " // trim (adjustl (sqrts_str)) // " GeV"
end subroutine write_sqrts
subroutine write_prt (i)
integer, intent(in) :: i
character(80) :: name_str, mass_str
write (name_str, "(A)") char (beam_data%flv(i)%get_name ())
write (mass_str, "(ES13.7)") beam_data%mass(i)
write (u, "(3x,A)", advance="no") &
name_str(:prt_name_len) // " (mass = " &
// trim (adjustl (mass_str)) // " GeV)"
if (beam_data%pmatrix(i)%is_polarized ()) then
write (u, "(2x,A)") "polarized"
else
write (u, *)
end if
end subroutine write_prt
end subroutine beam_data_write
@ %def beam_data_write
@ Return initialization status:
<<Beams: beam data: TBP>>=
procedure :: are_valid => beam_data_are_valid
<<Beams: procedures>>=
function beam_data_are_valid (beam_data) result (flag)
class(beam_data_t), intent(in) :: beam_data
logical :: flag
flag = beam_data%initialized
end function beam_data_are_valid
@ %def beam_data_are_valid
@ Check whether beam data agree with the current values of relevant
parameters.
<<Beams: beam data: TBP>>=
procedure :: check_scattering => beam_data_check_scattering
<<Beams: procedures>>=
subroutine beam_data_check_scattering (beam_data, sqrts)
class(beam_data_t), intent(in) :: beam_data
real(default), intent(in), optional :: sqrts
if (beam_data_are_valid (beam_data)) then
if (present (sqrts)) then
if (.not. nearly_equal (sqrts, beam_data%sqrts)) then
call msg_error ("Current setting of sqrts is inconsistent " &
// "with beam setup (ignored).")
end if
end if
else
call msg_bug ("Beam setup: invalid beam data")
end if
end subroutine beam_data_check_scattering
@ %def beam_data_check_scattering
@ Return the number of beams (1 for decays, 2 for collisions).
<<Beams: beam data: TBP>>=
procedure :: get_n_in => beam_data_get_n_in
<<Beams: procedures>>=
function beam_data_get_n_in (beam_data) result (n_in)
class(beam_data_t), intent(in) :: beam_data
integer :: n_in
n_in = beam_data%n
end function beam_data_get_n_in
@ %def beam_data_get_n_in
@ Return the beam flavor
<<Beams: beam data: TBP>>=
procedure :: get_flavor => beam_data_get_flavor
<<Beams: procedures>>=
function beam_data_get_flavor (beam_data) result (flv)
class(beam_data_t), intent(in) :: beam_data
type(flavor_t), dimension(:), allocatable :: flv
allocate (flv (beam_data%n))
flv = beam_data%flv
end function beam_data_get_flavor
@ %def beam_data_get_flavor
@ Return the beam energies
<<Beams: beam data: TBP>>=
procedure :: get_energy => beam_data_get_energy
<<Beams: procedures>>=
function beam_data_get_energy (beam_data) result (e)
class(beam_data_t), intent(in) :: beam_data
real(default), dimension(:), allocatable :: e
integer :: i
allocate (e (beam_data%n))
if (beam_data%initialized) then
do i = 1, beam_data%n
e(i) = energy (beam_data%p(i))
end do
else
e = 0
end if
end function beam_data_get_energy
@ %def beam_data_get_energy
@ Return the c.m.\ energy.
<<Beams: beam data: TBP>>=
procedure :: get_sqrts => beam_data_get_sqrts
<<Beams: procedures>>=
function beam_data_get_sqrts (beam_data) result (sqrts)
class(beam_data_t), intent(in) :: beam_data
real(default) :: sqrts
sqrts = beam_data%sqrts
end function beam_data_get_sqrts
@ %def beam_data_get_sqrts
@ Return true if the lab and c.m.\ frame are specified as identical.
<<Beams: beam data: TBP>>=
procedure :: cm_frame => beam_data_cm_frame
<<Beams: procedures>>=
function beam_data_cm_frame (beam_data) result (flag)
class(beam_data_t), intent(in) :: beam_data
logical :: flag
flag = beam_data%lab_is_cm_frame
end function beam_data_cm_frame
@ %def beam_data_cm_frame
@ Return the polarization in case it is just two degrees
<<Beams: beam data: TBP>>=
procedure :: get_polarization => beam_data_get_polarization
<<Beams: procedures>>=
function beam_data_get_polarization (beam_data) result (pol)
class(beam_data_t), intent(in) :: beam_data
real(default), dimension(2) :: pol
if (beam_data%n /= 2) &
call msg_fatal ("Beam data: can only treat scattering processes.")
pol = beam_data%pmatrix%get_simple_pol ()
end function beam_data_get_polarization
@ %def beam_data_get_polarization
@
<<Beams: beam data: TBP>>=
procedure :: get_helicity_state_matrix => beam_data_get_helicity_state_matrix
<<Beams: procedures>>=
function beam_data_get_helicity_state_matrix (beam_data) result (state_hel)
type(state_matrix_t) :: state_hel
class(beam_data_t), intent(in) :: beam_data
type(polarization_t), dimension(:), allocatable :: pol
integer :: i
allocate (pol (beam_data%n))
do i = 1, beam_data%n
call pol(i)%init_pmatrix (beam_data%pmatrix(i))
end do
call combine_polarization_states (pol, state_hel)
end function beam_data_get_helicity_state_matrix
@ %def beam_data_get_helicity_state_matrix
@
<<Beams: beam data: TBP>>=
procedure :: is_initialized => beam_data_is_initialized
<<Beams: procedures>>=
function beam_data_is_initialized (beam_data) result (initialized)
logical :: initialized
class(beam_data_t), intent(in) :: beam_data
initialized = any (beam_data%pmatrix%exists ())
end function beam_data_is_initialized
@ %def beam_data_is_initialized
@ Return a MD5 checksum for beam data. If no checksum is present
(because beams have not been initialized), compute the checksum of the
sqrts value.
<<Beams: beam data: TBP>>=
procedure :: get_md5sum => beam_data_get_md5sum
<<Beams: procedures>>=
function beam_data_get_md5sum (beam_data, sqrts) result (md5sum_beams)
class(beam_data_t), intent(in) :: beam_data
real(default), intent(in) :: sqrts
character(32) :: md5sum_beams
character(80) :: buffer
if (beam_data%md5sum /= "") then
md5sum_beams = beam_data%md5sum
else
write (buffer, *) sqrts
md5sum_beams = md5sum (buffer)
end if
end function beam_data_get_md5sum
@ %def beam_data_get_md5sum
@
\subsection{Initializers: beam structure}
Initialize the beam data object from a beam structure object, given energy and
model.
<<Beams: beam data: TBP>>=
procedure :: init_structure => beam_data_init_structure
<<Beams: procedures>>=
subroutine beam_data_init_structure &
(beam_data, structure, sqrts, model, decay_rest_frame)
class(beam_data_t), intent(out) :: beam_data
type(beam_structure_t), intent(in) :: structure
integer :: n_beam
real(default), intent(in) :: sqrts
class(model_data_t), intent(in), target :: model
logical, intent(in), optional :: decay_rest_frame
type(flavor_t), dimension(:), allocatable :: flv
n_beam = structure%get_n_beam ()
allocate (flv (n_beam))
call flv%init (structure%get_prt (), model)
if (structure%asymmetric ()) then
if (structure%polarized ()) then
call beam_data%init_momenta (structure%get_momenta (), flv, &
structure%get_smatrix (), structure%get_pol_f ())
else
call beam_data%init_momenta (structure%get_momenta (), flv)
end if
else
select case (n_beam)
case (1)
if (structure%polarized ()) then
call beam_data%init_decay (flv, &
structure%get_smatrix (), structure%get_pol_f (), &
rest_frame = decay_rest_frame)
else
call beam_data%init_decay (flv, &
rest_frame = decay_rest_frame)
end if
case (2)
if (structure%polarized ()) then
call beam_data%init_sqrts (sqrts, flv, &
structure%get_smatrix (), structure%get_pol_f ())
else
call beam_data%init_sqrts (sqrts, flv)
end if
case default
call msg_bug ("Beam data: invalid beam structure object")
end select
end if
end subroutine beam_data_init_structure
@ %def beam_data_init_structure
@
\subsection{Initializers: collisions}
This is the simplest one: just the two flavors, c.m.\ energy,
polarization. Color is inferred from flavor. Beam momenta and c.m.\
momenta coincide.
<<Beams: beam data: TBP>>=
procedure :: init_sqrts => beam_data_init_sqrts
<<Beams: procedures>>=
subroutine beam_data_init_sqrts (beam_data, sqrts, flv, smatrix, pol_f)
class(beam_data_t), intent(out) :: beam_data
real(default), intent(in) :: sqrts
type(flavor_t), dimension(:), intent(in) :: flv
type(smatrix_t), dimension(:), intent(in), optional :: smatrix
real(default), dimension(:), intent(in), optional :: pol_f
real(default), dimension(size(flv)) :: E, p
call beam_data_init (beam_data, size (flv))
beam_data%sqrts = sqrts
beam_data%lab_is_cm_frame = .true.
select case (beam_data%n)
case (1)
E = sqrts; p = 0
beam_data%p_cm = vector4_moving (E, p, 3)
beam_data%p = beam_data%p_cm
case (2)
beam_data%p_cm = colliding_momenta (sqrts, flv%get_mass ())
beam_data%p = colliding_momenta (sqrts, flv%get_mass ())
end select
call beam_data_finish_initialization (beam_data, flv, smatrix, pol_f)
end subroutine beam_data_init_sqrts
@ %def beam_data_init_sqrts
@ This version sets beam momenta directly, assuming that they are
asymmetric, i.e., lab frame and c.m.\ frame do not coincide.
Polarization info is deferred to a common initializer.
The Lorentz transformation that we compute here is not actually used
in the calculation; instead, it will be recomputed for each event in
the subroutine [[phs_set_incoming_momenta]]. We compute it here for
the nominal beam setup nevertheless, so we can print it and, in
particular, include it in the MD5 sum.
<<Beams: beam data: TBP>>=
procedure :: init_momenta => beam_data_init_momenta
<<Beams: procedures>>=
subroutine beam_data_init_momenta (beam_data, p3, flv, smatrix, pol_f)
class(beam_data_t), intent(out) :: beam_data
type(vector3_t), dimension(:), intent(in) :: p3
type(flavor_t), dimension(:), intent(in) :: flv
type(smatrix_t), dimension(:), intent(in), optional :: smatrix
real(default), dimension(:), intent(in), optional :: pol_f
type(vector4_t) :: p0
type(vector4_t), dimension(:), allocatable :: p, p_cm_rot
real(default), dimension(size(p3)) :: e
real(default), dimension(size(flv)) :: m
type(lorentz_transformation_t) :: L_boost, L_rot
call beam_data_init (beam_data, size (flv))
m = flv%get_mass ()
e = sqrt (p3 ** 2 + m ** 2)
allocate (p (beam_data%n))
p = vector4_moving (e, p3)
p0 = sum (p)
beam_data%p = p
beam_data%lab_is_cm_frame = .false.
beam_data%sqrts = p0 ** 1
L_boost = boost (p0, beam_data%sqrts)
allocate (p_cm_rot (beam_data%n))
p_cm_rot = inverse (L_boost) * p
allocate (beam_data%L_cm_to_lab)
select case (beam_data%n)
case (1)
beam_data%L_cm_to_lab = L_boost
beam_data%p_cm = vector4_at_rest (beam_data%sqrts)
case (2)
L_rot = rotation_to_2nd (3, space_part (p_cm_rot(1)))
beam_data%L_cm_to_lab = L_boost * L_rot
beam_data%p_cm = &
colliding_momenta (beam_data%sqrts, flv%get_mass ())
end select
call beam_data_finish_initialization (beam_data, flv, smatrix, pol_f)
end subroutine beam_data_init_momenta
@ %def beam_data_init_momenta
@
Final steps:
If requested, rotate the beams in the lab frame, and set
the beam-data components.
<<Beams: procedures>>=
subroutine beam_data_finish_initialization (beam_data, flv, smatrix, pol_f)
type(beam_data_t), intent(inout) :: beam_data
type(flavor_t), dimension(:), intent(in) :: flv
type(smatrix_t), dimension(:), intent(in), optional :: smatrix
real(default), dimension(:), intent(in), optional :: pol_f
integer :: i
do i = 1, beam_data%n
beam_data%flv(i) = flv(i)
beam_data%mass(i) = flv(i)%get_mass ()
if (present (smatrix)) then
if (size (smatrix) /= beam_data%n) &
call msg_fatal ("Beam data: &
&polarization density array has wrong dimension")
beam_data%pmatrix(i) = smatrix(i)
if (present (pol_f)) then
if (size (pol_f) /= size (smatrix)) &
call msg_fatal ("Beam data: &
&polarization fraction array has wrong dimension")
call beam_data%pmatrix(i)%normalize (flv(i), pol_f(i))
else
call beam_data%pmatrix(i)%normalize (flv(i), 1._default)
end if
else
call beam_data%pmatrix(i)%init (2, 0)
call beam_data%pmatrix(i)%normalize (flv(i), 0._default)
end if
end do
call beam_data%compute_md5sum ()
end subroutine beam_data_finish_initialization
@ %def beam_data_finish_initialization
@
The MD5 sum is stored within the beam-data record, so it can be
checked for integrity in subsequent runs.
<<Beams: beam data: TBP>>=
procedure :: compute_md5sum => beam_data_compute_md5sum
<<Beams: procedures>>=
subroutine beam_data_compute_md5sum (beam_data)
class(beam_data_t), intent(inout) :: beam_data
integer :: unit
unit = free_unit ()
open (unit = unit, status = "scratch", action = "readwrite")
call beam_data%write (unit, write_md5sum = .false., &
verbose = .true.)
rewind (unit)
beam_data%md5sum = md5sum (unit)
close (unit)
end subroutine beam_data_compute_md5sum
@ %def beam_data_compute_md5sum
@
\subsection{Initializers: decays}
This is the simplest one: decay in rest frame. We need just flavor
and polarization. Color is inferred from flavor. Beam momentum and
c.m.\ momentum coincide.
<<Beams: beam data: TBP>>=
procedure :: init_decay => beam_data_init_decay
<<Beams: procedures>>=
subroutine beam_data_init_decay (beam_data, flv, smatrix, pol_f, rest_frame)
class(beam_data_t), intent(out) :: beam_data
type(flavor_t), dimension(1), intent(in) :: flv
type(smatrix_t), dimension(1), intent(in), optional :: smatrix
real(default), dimension(:), intent(in), optional :: pol_f
logical, intent(in), optional :: rest_frame
real(default), dimension(1) :: m
m = flv%get_mass ()
if (present (smatrix)) then
call beam_data%init_sqrts (m(1), flv, smatrix, pol_f)
else
call beam_data%init_sqrts (m(1), flv, smatrix, pol_f)
end if
if (present (rest_frame)) beam_data%lab_is_cm_frame = rest_frame
end subroutine beam_data_init_decay
@ %def beam_data_init_decay
@
\subsection{The beams type}
Beam objects are interaction objects that contain the actual beam
data including polarization and density matrix. For collisions, the
beam object actually contains two beams.
<<Beams: public>>=
public :: beam_t
<<Beams: types>>=
type :: beam_t
private
type(interaction_t) :: int
end type beam_t
@ %def beam_t
@ The constructor contains code that converts beam data into the
(entangled) particle-pair quantum state. First, we set the number of
particles and polarization mask. (The polarization mask is handed
over to all later interactions, so if helicity is diagonal or absent, this fact
is used when constructing the hard-interaction events.) Then, we
construct the entangled state that combines helicity, flavor and color
of the two particles (where flavor and color are unique, while several
helicity states are possible). Then, we transfer this state together
with the associated values from the spin density matrix into the
[[interaction_t]] object.
Calling the [[add_state]] method of the interaction object, we keep
the entries of the helicity density matrix without adding them up.
This ensures that for unpolarized states, we do not normalize but end
up with an $1/N$ entry, where $N$ is the initial-state multiplicity.
<<Beams: public>>=
public :: beam_init
<<Beams: procedures>>=
subroutine beam_init (beam, beam_data)
type(beam_t), intent(out) :: beam
type(beam_data_t), intent(in), target :: beam_data
logical, dimension(beam_data%n) :: polarized, diagonal
type(quantum_numbers_mask_t), dimension(beam_data%n) :: mask, mask_d
type(state_matrix_t), target :: state_hel, state_fc, state_tmp
type(state_iterator_t) :: it_hel, it_tmp
type(quantum_numbers_t), dimension(:), allocatable :: qn
complex(default) :: value
real(default), parameter :: tolerance = 100 * epsilon (1._default)
polarized = beam_data%pmatrix%is_polarized ()
diagonal = beam_data%pmatrix%is_diagonal ()
mask = quantum_numbers_mask (.false., .false., &
mask_h = .not. polarized, &
mask_hd = diagonal)
mask_d = quantum_numbers_mask (.false., .false., .false., &
mask_hd = polarized .and. diagonal)
call beam%int%basic_init &
(0, 0, beam_data%n, mask = mask, store_values = .true.)
state_hel = beam_data%get_helicity_state_matrix ()
allocate (qn (beam_data%n))
call qn%init (beam_data%flv, color_from_flavor (beam_data%flv, 1))
call state_fc%init ()
call state_fc%add_state (qn)
call merge_state_matrices (state_hel, state_fc, state_tmp)
call it_hel%init (state_hel)
call it_tmp%init (state_tmp)
do while (it_hel%is_valid ())
qn = it_tmp%get_quantum_numbers ()
value = it_hel%get_matrix_element ()
if (any (qn%are_redundant (mask_d))) then
! skip off-diagonal elements for diagonal polarization
else if (abs (value) <= tolerance) then
! skip zero entries
else
call beam%int%add_state (qn, value = value)
end if
call it_hel%advance ()
call it_tmp%advance ()
end do
call beam%int%freeze ()
call beam%int%set_momenta (beam_data%p, outgoing = .true.)
call state_hel%final ()
call state_fc%final ()
call state_tmp%final ()
end subroutine beam_init
@ %def beam_init
@ Finalizer:
<<Beams: public>>=
public :: beam_final
<<Beams: procedures>>=
subroutine beam_final (beam)
type(beam_t), intent(inout) :: beam
call beam%int%final ()
end subroutine beam_final
@ %def beam_final
@ I/O:
<<Beams: public>>=
public :: beam_write
<<Beams: procedures>>=
subroutine beam_write (beam, unit, verbose, show_momentum_sum, show_mass, col_verbose)
type(beam_t), intent(in) :: beam
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose, show_momentum_sum, show_mass
logical, intent(in), optional :: col_verbose
integer :: u
u = given_output_unit (unit); if (u < 0) return
select case (beam%int%get_n_out ())
case (1); write (u, *) "Decaying particle:"
case (2); write (u, *) "Colliding beams:"
end select
call beam%int%basic_write &
(unit, verbose = verbose, show_momentum_sum = &
show_momentum_sum, show_mass = show_mass, &
col_verbose = col_verbose)
end subroutine beam_write
@ %def beam_write
@ Defined assignment: deep copy
<<Beams: public>>=
public :: assignment(=)
<<Beams: interfaces>>=
interface assignment(=)
module procedure beam_assign
end interface
<<Beams: procedures>>=
subroutine beam_assign (beam_out, beam_in)
type(beam_t), intent(out) :: beam_out
type(beam_t), intent(in) :: beam_in
beam_out%int = beam_in%int
end subroutine beam_assign
@ %def beam_assign
@
\subsection{Inherited procedures}
<<Beams: public>>=
public :: interaction_set_source_link
<<Beams: interfaces>>=
interface interaction_set_source_link
module procedure interaction_set_source_link_beam
end interface
<<Beams: procedures>>=
subroutine interaction_set_source_link_beam (int, i, beam1, i1)
type(interaction_t), intent(inout) :: int
type(beam_t), intent(in), target :: beam1
integer, intent(in) :: i, i1
call int%set_source_link (i, beam1%int, i1)
end subroutine interaction_set_source_link_beam
@ %def interaction_set_source_link_beam
@
\subsection{Accessing contents}
Return the interaction component -- as a pointer, to avoid any copying.
<<Beams: public>>=
public :: beam_get_int_ptr
<<Beams: procedures>>=
function beam_get_int_ptr (beam) result (int)
type(interaction_t), pointer :: int
type(beam_t), intent(in), target :: beam
int => beam%int
end function beam_get_int_ptr
@ %def beam_get_int_ptr
@ Set beam momenta directly. (Used for cascade decays.)
<<Beams: public>>=
public :: beam_set_momenta
<<Beams: procedures>>=
subroutine beam_set_momenta (beam, p)
type(beam_t), intent(inout) :: beam
type(vector4_t), dimension(:), intent(in) :: p
call beam%int%set_momenta (p)
end subroutine beam_set_momenta
@ %def beam_set_momenta
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[beams_ut.f90]]>>=
<<File header>>
module beams_ut
use unit_tests
use beams_uti
<<Standard module head>>
<<Beams: public test>>
contains
<<Beams: test driver>>
end module beams_ut
@ %def beams_ut
@
<<[[beams_uti.f90]]>>=
<<File header>>
module beams_uti
<<Use kinds>>
use lorentz
use flavors
use interactions, only: reset_interaction_counter
use polarizations, only: smatrix_t
use model_data
use beam_structures
use beams
<<Standard module head>>
<<Beams: test declarations>>
contains
<<Beams: tests>>
end module beams_uti
@ %def beams_ut
@ API: driver for the unit tests below.
<<Beams: public test>>=
public :: beams_test
<<Beams: test driver>>=
subroutine beams_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Beams: execute tests>>
end subroutine beams_test
@ %def beams_test
@ Test the basic beam setup.
<<Beams: execute tests>>=
call test (beam_1, "beam_1", &
"check basic beam setup", &
u, results)
<<Beams: test declarations>>=
public :: beam_1
<<Beams: tests>>=
subroutine beam_1 (u)
integer, intent(in) :: u
type(beam_data_t), target :: beam_data
type(beam_t) :: beam
real(default) :: sqrts
type(flavor_t), dimension(2) :: flv
type(smatrix_t), dimension(2) :: smatrix
real(default), dimension(2) :: pol_f
type(model_data_t), target :: model
write (u, "(A)") "* Test output: beam_1"
write (u, "(A)") "* Purpose: test basic beam setup"
write (u, "(A)")
write (u, "(A)") "* Reading model file"
write (u, "(A)")
call reset_interaction_counter ()
call model%init_sm_test ()
write (u, "(A)") "* Unpolarized scattering, massless fermions"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([1,-1], model)
call beam_data%init_sqrts (sqrts, flv)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Unpolarized scattering, massless bosons"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([22,22], model)
call beam_data%init_sqrts (sqrts, flv)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Unpolarized scattering, massive bosons"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([24,-24], model)
call beam_data%init_sqrts (sqrts, flv)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Polarized scattering, massless fermions"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([1,-1], model)
call smatrix(1)%init (2, 1)
call smatrix(1)%set_entry (1, [1,1], (1._default, 0._default))
pol_f(1) = 0.5_default
call smatrix(2)%init (2, 3)
call smatrix(2)%set_entry (1, [1,1], (1._default, 0._default))
call smatrix(2)%set_entry (2, [-1,-1], (1._default, 0._default))
call smatrix(2)%set_entry (3, [-1,1], (1._default, 0._default))
pol_f(2) = 1._default
call beam_data%init_sqrts (sqrts, flv, smatrix, pol_f)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Semi-polarized scattering, massless bosons"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([22,22], model)
call smatrix(1)%init (2, 0)
pol_f(1) = 0._default
call smatrix(2)%init (2, 1)
call smatrix(2)%set_entry (1, [1,1], (1._default, 0._default))
pol_f(2) = 1._default
call beam_data%init_sqrts (sqrts, flv, smatrix, pol_f)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Semi-polarized scattering, massive bosons"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([24,-24], model)
call smatrix(1)%init (2, 0)
pol_f(1) = 0._default
call smatrix(2)%init (2, 1)
call smatrix(2)%set_entry (1, [0,0], (1._default, 0._default))
pol_f(2) = 1._default
call beam_data%init_sqrts (sqrts, flv, smatrix, pol_f)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Unpolarized decay, massive boson"
write (u, "(A)")
call reset_interaction_counter ()
call flv(1)%init (23, model)
call beam_data%init_decay (flv(1:1))
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
write (u, "(A)")
write (u, "(A)") "* Polarized decay, massive boson"
write (u, "(A)")
call reset_interaction_counter ()
call flv(1)%init (23, model)
call smatrix(1)%init (2, 1)
call smatrix(1)%set_entry (1, [0,0], (1._default, 0._default))
pol_f(1) = 0.4_default
call beam_data%init_decay (flv(1:1), smatrix(1:1), pol_f(1:1))
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call beam_final (beam)
call beam_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_1"
end subroutine beam_1
@ %def beam_1
@ Test advanced beam setup.
<<Beams: execute tests>>=
call test (beam_2, "beam_2", &
"beam initialization", &
u, results)
<<Beams: test declarations>>=
public :: beam_2
<<Beams: tests>>=
subroutine beam_2 (u)
integer, intent(in) :: u
type(beam_data_t), target :: beam_data
type(beam_t) :: beam
real(default) :: sqrts
type(flavor_t), dimension(2) :: flv
integer, dimension(0) :: no_records
type(beam_structure_t) :: beam_structure
type(model_data_t), target :: model
write (u, "(A)") "* Test output: beam_2"
write (u, "(A)") "* Purpose: transfer beam polarization using &
&beam structure"
write (u, "(A)")
write (u, "(A)") "* Reading model file"
write (u, "(A)")
call model%init_sm_test ()
write (u, "(A)") "* Unpolarized scattering, massless fermions"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([1,-1], model)
call beam_structure%init_sf (flv%get_name (), no_records)
call beam_structure%final_pol ()
call beam_structure%write (u)
write (u, *)
call beam_data%init_structure (beam_structure, sqrts, model)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Unpolarized scattering, massless bosons"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([22,22], model)
call beam_structure%init_sf (flv%get_name (), no_records)
call beam_structure%final_pol ()
call beam_structure%write (u)
write (u, *)
call beam_data%init_structure (beam_structure, sqrts, model)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Unpolarized scattering, massive bosons"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([24,-24], model)
call beam_structure%init_sf (flv%get_name (), no_records)
call beam_structure%final_pol ()
call beam_structure%write (u)
write (u, *)
call beam_data%init_structure (beam_structure, sqrts, model)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Polarized scattering, massless fermions"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([1,-1], model)
call beam_structure%init_sf (flv%get_name (), no_records)
call beam_structure%init_pol (2)
call beam_structure%init_smatrix (1, 1)
call beam_structure%set_sentry (1, 1, [1,1], (1._default, 0._default))
call beam_structure%init_smatrix (2, 3)
call beam_structure%set_sentry (2, 1, [1,1], (1._default, 0._default))
call beam_structure%set_sentry (2, 2, [-1,-1], (1._default, 0._default))
call beam_structure%set_sentry (2, 3, [-1,1], (1._default, 0._default))
call beam_structure%set_pol_f ([0.5_default, 1._default])
call beam_structure%write (u)
write (u, *)
call beam_data%init_structure (beam_structure, sqrts, model)
call beam_data%write (u)
write (u, *)
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
call beam_structure%final_pol ()
call beam_structure%final_sf ()
write (u, "(A)")
write (u, "(A)") "* Semi-polarized scattering, massless bosons"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([22,22], model)
call beam_structure%init_sf (flv%get_name (), no_records)
call beam_structure%init_pol (2)
call beam_structure%init_smatrix (1, 0)
call beam_structure%init_smatrix (2, 1)
call beam_structure%set_sentry (2, 1, [1,1], (1._default, 0._default))
call beam_structure%set_pol_f ([0._default, 1._default])
call beam_structure%write (u)
write (u, *)
call beam_data%init_structure (beam_structure, sqrts, model)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Semi-polarized scattering, massive bosons"
write (u, "(A)")
call reset_interaction_counter ()
sqrts = 500
call flv%init ([24,-24], model)
call beam_structure%init_sf (flv%get_name (), no_records)
call beam_structure%init_pol (2)
call beam_structure%init_smatrix (1, 0)
call beam_structure%init_smatrix (2, 1)
call beam_structure%set_sentry (2, 1, [0,0], (1._default, 0._default))
call beam_structure%write (u)
write (u, "(A)")
call beam_data%init_structure (beam_structure, sqrts, model)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
write (u, "(A)")
write (u, "(A)") "* Unpolarized decay, massive boson"
write (u, "(A)")
call reset_interaction_counter ()
call flv(1)%init (23, model)
call beam_structure%init_sf ([flv(1)%get_name ()], no_records)
call beam_structure%final_pol ()
call beam_structure%write (u)
write (u, "(A)")
call beam_data%init_structure (beam_structure, sqrts, model)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
write (u, "(A)")
write (u, "(A)") "* Polarized decay, massive boson"
write (u, "(A)")
call reset_interaction_counter ()
call flv(1)%init (23, model)
call beam_structure%init_sf ([flv(1)%get_name ()], no_records)
call beam_structure%init_pol (1)
call beam_structure%init_smatrix (1, 1)
call beam_structure%set_sentry (1, 1, [0,0], (1._default, 0._default))
call beam_structure%set_pol_f ([0.4_default])
call beam_structure%write (u)
write (u, *)
call beam_data%init_structure (beam_structure, sqrts, model)
call beam_data%write (u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call beam_final (beam)
call beam_data%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_2"
end subroutine beam_2
@ %def beam_2
@ Test advanced beam setup, completely arbitrary momenta.
<<Beams: execute tests>>=
call test (beam_3, "beam_3", &
"generic beam momenta", &
u, results)
<<Beams: test declarations>>=
public :: beam_3
<<Beams: tests>>=
subroutine beam_3 (u)
integer, intent(in) :: u
type(beam_data_t), target :: beam_data
type(beam_t) :: beam
type(flavor_t), dimension(2) :: flv
integer, dimension(0) :: no_records
type(model_data_t), target :: model
type(beam_structure_t) :: beam_structure
type(vector3_t), dimension(2) :: p3
type(vector4_t), dimension(2) :: p
write (u, "(A)") "* Test output: beam_3"
write (u, "(A)") "* Purpose: set up beams with generic momenta"
write (u, "(A)")
write (u, "(A)") "* Reading model file"
write (u, "(A)")
call reset_interaction_counter ()
call model%init_sm_test ()
write (u, "(A)") "* 1: Scattering process"
write (u, "(A)")
call flv%init ([2212,2212], model)
p3(1) = vector3_moving ([5._default, 0._default, 10._default])
p3(2) = -vector3_moving ([1._default, 1._default, -10._default])
call beam_structure%init_sf (flv%get_name (), no_records)
call beam_structure%set_momentum (p3 ** 1)
call beam_structure%set_theta (polar_angle (p3))
call beam_structure%set_phi (azimuthal_angle (p3))
call beam_structure%write (u)
write (u, *)
call beam_data%init_structure (beam_structure, 0._default, model)
call pacify (beam_data%l_cm_to_lab, 1e-20_default)
call beam_data%compute_md5sum ()
call beam_data%write (u, verbose = .true.)
write (u, *)
write (u, "(1x,A)") "Beam momenta reconstructed from LT:"
p = beam_data%L_cm_to_lab * beam_data%p_cm
call pacify (p, 1e-12_default)
call vector4_write (p(1), u)
call vector4_write (p(2), u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
call beam_final (beam)
call beam_data%final ()
call beam_structure%final_sf ()
call beam_structure%final_mom ()
write (u, "(A)")
write (u, "(A)") "* 2: Decay"
write (u, "(A)")
call flv(1)%init (23, model)
p3(1) = vector3_moving ([10._default, 5._default, 50._default])
call beam_structure%init_sf ([flv(1)%get_name ()], no_records)
call beam_structure%set_momentum ([p3(1) ** 1])
call beam_structure%set_theta ([polar_angle (p3(1))])
call beam_structure%set_phi ([azimuthal_angle (p3(1))])
call beam_structure%write (u)
write (u, *)
call beam_data%init_structure (beam_structure, 0._default, model)
call beam_data%write (u, verbose = .true.)
write (u, "(A)")
write (u, "(1x,A)") "Beam momentum reconstructed from LT:"
p(1) = beam_data%L_cm_to_lab * beam_data%p_cm(1)
call pacify (p(1), 1e-12_default)
call vector4_write (p(1), u)
write (u, "(A)")
call beam_init (beam, beam_data)
call beam_write (beam, u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call beam_final (beam)
call beam_data%final ()
call beam_structure%final_sf ()
call beam_structure%final_mom ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: beam_3"
end subroutine beam_3
@ %def beam_3
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Tools}
This module contains auxiliary procedures that can be accessed by the
structure function code.
<<[[sf_aux.f90]]>>=
<<File header>>
module sf_aux
<<Use kinds>>
use io_units
use constants, only: twopi
use numeric_utils
use lorentz
<<Standard module head>>
<<SF aux: public>>
<<SF aux: parameters>>
<<SF aux: types>>
contains
<<SF aux: procedures>>
end module sf_aux
@ %def sf_aux
@
\subsection{Momentum splitting}
Let us consider first an incoming parton with momentum $k$ and
invariant mass squared $s=k^2$ that splits into two partons with
momenta $q,p$ and invariant masses $t=q^2$ and $u=p^2$. (This is an
abuse of the Mandelstam notation. $t$ is actually the momentum
transfer, assuming that $p$ is radiated and $q$ initiates the hard
process.) The energy is split among the partons such that if $E=k^0$,
we have $q^0 = xE$ and $p^0=\bar x E$, where $\bar x\equiv 1-x$.
We define the angle $\theta$ as the polar angle of $p$ w.r.t.\ the
momentum axis of the incoming momentum $k$. Ignoring azimuthal angle,
we can write the four-momenta in the basis $(E,p_T,p_L)$ as
\begin{equation}
k =
\begin{pmatrix}
E \\ 0 \\ p
\end{pmatrix},
\qquad
p =
\begin{pmatrix}
\bar x E \\ \bar x\bar p\sin\theta \\ \bar x\bar p\cos\theta
\end{pmatrix},
\qquad
q =
\begin{pmatrix}
x E \\ -\bar x\bar p\sin\theta \\ p - \bar x\bar p\cos\theta
\end{pmatrix},
\end{equation}
where the first two mass-shell conditions are
\begin{equation}
p^2 = E^2 - s,
\qquad
\bar p^2 = E^2 - \frac{u}{\bar x^2}.
\end{equation}
The second condition implies that, for positive $u$, $\bar x^2 >
u/E^2$, or equivalently
\begin{equation}
x < 1 - \sqrt{u} / E.
\end{equation}
We are interested in the third mass-shell conditions: $s$ and $u$ are
fixed, so we need $t$ as a function of $\cos\theta$:
\begin{equation}
t = -2\bar x \left(E^2 - p\bar p\cos\theta\right) + s + u.
\end{equation}
Solving for $\cos\theta$, we get
\begin{equation}
\cos\theta = \frac{2\bar x E^2 + t - s - u}{2\bar x p\bar p}.
\end{equation}
We can compute $\sin\theta$ numerically as
$\sin^2\theta=1-\cos^2\theta$, but it is important to reexpress this
in view of numerical stability. To this end, we first determine the
bounds for $t$. The cosine must be between $-1$ and $1$, so the
bounds are
\begin{align}
t_0 &= -2\bar x\left(E^2 + p\bar p\right) + s + u,
\\
t_1 &= -2\bar x\left(E^2 - p\bar p\right) + s + u.
\end{align}
Computing $\sin^2\theta$ from $\cos\theta$ above, we observe that the
numerator is a quadratic polynomial in $t$ which has the zeros $t_0$
and $t_1$, while the common denominator is given by $(2\bar x p\bar
p)^2$. Hence, we can write
\begin{equation}
\sin^2\theta = -\frac{(t - t_0)(t - t_1)}{(2\bar x p\bar p)^2}
\qquad\text{and}\qquad
\cos\theta = \frac{(t-t_0) + (t-t_1)}{4\bar x p\bar p},
\end{equation}
which is free of large cancellations near $t=t_0$ or $t=t_1$.
If all is massless, i.e., $s=u=0$, this simplifies to
\begin{align}
t_0 &= -4\bar x E^2,
&
t_1 &= 0,
\\
\sin^2\theta &= -\frac{t}{\bar x E^2}
\left(1 + \frac{t}{4\bar x E^2}\right),
&
\cos\theta &= 1 + \frac{t}{2\bar x E^2}.
\end{align}
Here is the implementation. First, we define a container for the
kinematical integration limits and some further data.
Note: contents are public only for easy access in unit test.
<<SF aux: public>>=
public :: splitting_data_t
<<SF aux: types>>=
type :: splitting_data_t
! private
logical :: collinear = .false.
real(default) :: x0 = 0
real(default) :: x1
real(default) :: t0
real(default) :: t1
real(default) :: phi0 = 0
real(default) :: phi1 = twopi
real(default) :: E, p, s, u, m2
real(default) :: x, xb, pb
real(default) :: t = 0
real(default) :: phi = 0
contains
<<SF aux: splitting data: TBP>>
end type splitting_data_t
@ %def splitting_data_t
@ I/O for debugging:
<<SF aux: splitting data: TBP>>=
procedure :: write => splitting_data_write
<<SF aux: procedures>>=
subroutine splitting_data_write (d, unit)
class(splitting_data_t), intent(in) :: d
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(A)") "Splitting data:"
write (u, "(2x,A,L1)") "collinear = ", d%collinear
1 format (2x,A,1x,ES15.8)
write (u, 1) "x0 =", d%x0
write (u, 1) "x =", d%x
write (u, 1) "xb =", d%xb
write (u, 1) "x1 =", d%x1
write (u, 1) "t0 =", d%t0
write (u, 1) "t =", d%t
write (u, 1) "t1 =", d%t1
write (u, 1) "phi0 =", d%phi0
write (u, 1) "phi =", d%phi
write (u, 1) "phi1 =", d%phi1
write (u, 1) "E =", d%E
write (u, 1) "p =", d%p
write (u, 1) "pb =", d%pb
write (u, 1) "s =", d%s
write (u, 1) "u =", d%u
write (u, 1) "m2 =", d%m2
end subroutine splitting_data_write
@ %def splitting_data_write
@
\subsection{Constant data}
This is the initializer for the data. The input consists of the
incoming momentum, its invariant mass squared, and the invariant mass
squared of the radiated particle. $m2$ is the \emph{physical} mass
squared of the outgoing particle. The $t$ bounds depend on the chosen $x$
value and cannot be determined yet.
<<SF aux: splitting data: TBP>>=
procedure :: init => splitting_data_init
<<SF aux: procedures>>=
subroutine splitting_data_init (d, k, mk2, mr2, mo2, collinear)
class(splitting_data_t), intent(out) :: d
type(vector4_t), intent(in) :: k
real(default), intent(in) :: mk2, mr2, mo2
logical, intent(in), optional :: collinear
if (present (collinear)) d%collinear = collinear
d%E = energy (k)
d%x1 = 1 - sqrt (max (mr2, 0._default)) / d%E
d%p = sqrt (d%E**2 - mk2)
d%s = mk2
d%u = mr2
d%m2 = mo2
end subroutine splitting_data_init
@ %def splitting_data_init
@ Retrieve the $x$ bounds, if needed for $x$ sampling. Generating an
$x$ value is done by the caller, since this is the part that depends
on the nature of the structure function.
<<SF aux: splitting data: TBP>>=
procedure :: get_x_bounds => splitting_get_x_bounds
<<SF aux: procedures>>=
function splitting_get_x_bounds (d) result (x)
class(splitting_data_t), intent(in) :: d
real(default), dimension(2) :: x
x = [ d%x0, d%x1 ]
end function splitting_get_x_bounds
@ %def splitting_get_x_bounds
@ Now set the momentum fraction and compute $t_0$ and $t_1$.
[The calculation of $t_1$ is subject to numerical problems. The exact
formula is ($s=m_i^2$, $u=m_r^2$)
\begin{equation}
t_1 = -2\bar x E^2 + m_i^2 + m_r^2
+ 2\bar x \sqrt{E^2-m_i^2}\,\sqrt{E^2 - m_r^2/\bar x^2}.
\end{equation}
The structure-function paradigm is useful only if $E\gg m_i,m_r$. In
a Taylor expansion for large $E$, the leading term cancels. The
expansion of the square roots (to subleading order) yields
\begin{equation}
t_1 = xm_i^2 - \frac{x}{\bar x}m_r^2.
\end{equation}
There are two cases of interest: $m_i=m_o$ and $m_r=0$,
\begin{equation}
t_1 = xm_o^2
\end{equation}
and $m_i=m_r$ and $m_o=0$,
\begin{equation}
t_1 = -\frac{x^2}{\bar x}m_i^2.
\end{equation}
In both cases, $t_1\leq m_o^2$.]
That said, it turns out that taking the $t_1$ evaluation at face value
leads to less problems than the approximation. We express the angles
in terms of $t-t_0$ and $t-t_1$. Numerical noise in $t_1$ can then be
tolerated.
<<SF aux: splitting data: TBP>>=
procedure :: set_t_bounds => splitting_set_t_bounds
<<SF aux: procedures>>=
elemental subroutine splitting_set_t_bounds (d, x, xb)
class(splitting_data_t), intent(inout) :: d
real(default), intent(in), optional :: x, xb
real(default) :: tp, tm
if (present (x)) d%x = x
if (present (xb)) d%xb = xb
if (vanishes (d%u)) then
d%pb = d%E
else
if (.not. vanishes (d%xb)) then
d%pb = sqrt (max (d%E**2 - d%u / d%xb**2, 0._default))
else
d%pb = 0
end if
end if
tp = -2 * d%xb * d%E**2 + d%s + d%u
tm = -2 * d%xb * d%p * d%pb
d%t0 = tp + tm
d%t1 = tp - tm
d%t = d%t1
end subroutine splitting_set_t_bounds
@ %def splitting_set_t_bounds
@
\subsection{Sampling recoil}
Compute a value for the momentum transfer $t$, using a random number
$r$. We assume a logarithmic distribution for $t-m^2$, corresponding
to the propagator $1/(t-m^2)$ with the physical mass $m$ for the
outgoing particle. Optionally, we can narrow the kinematical bounds.
If all three masses in the splitting vanish, the upper limit for $t$
is zero. In that case, the $t$ value is set to zero and the splitting
will be collinear.
<<SF aux: splitting data: TBP>>=
procedure :: sample_t => splitting_sample_t
<<SF aux: procedures>>=
subroutine splitting_sample_t (d, r, t0, t1)
class(splitting_data_t), intent(inout) :: d
real(default), intent(in) :: r
real(default), intent(in), optional :: t0, t1
real(default) :: tt0, tt1, tt0m, tt1m
if (d%collinear) then
d%t = d%t1
else
tt0 = d%t0; if (present (t0)) tt0 = max (t0, tt0)
tt1 = d%t1; if (present (t1)) tt1 = min (t1, tt1)
tt0m = tt0 - d%m2
tt1m = tt1 - d%m2
if (tt0m < 0 .and. tt1m < 0 .and. abs(tt0m) > &
epsilon(tt0m) .and. abs(tt1m) > epsilon(tt0m)) then
d%t = d%m2 + tt0m * exp (r * log (tt1m / tt0m))
else
d%t = tt1
end if
end if
end subroutine splitting_sample_t
@ %def splitting_sample_t
@ The inverse operation: Given $t$, we recover the value of $r$ that
would have produced this value.
<<SF aux: splitting data: TBP>>=
procedure :: inverse_t => splitting_inverse_t
<<SF aux: procedures>>=
subroutine splitting_inverse_t (d, r, t0, t1)
class(splitting_data_t), intent(in) :: d
real(default), intent(out) :: r
real(default), intent(in), optional :: t0, t1
real(default) :: tt0, tt1, tt0m, tt1m
if (d%collinear) then
r = 0
else
tt0 = d%t0; if (present (t0)) tt0 = max (t0, tt0)
tt1 = d%t1; if (present (t1)) tt1 = min (t1, tt1)
tt0m = tt0 - d%m2
tt1m = tt1 - d%m2
if (tt0m < 0 .and. tt1m < 0) then
r = log ((d%t - d%m2) / tt0m) / log (tt1m / tt0m)
else
r = 0
end if
end if
end subroutine splitting_inverse_t
@ %def splitting_inverse_t
@ This is trivial, but provided for convenience:
<<SF aux: splitting data: TBP>>=
procedure :: sample_phi => splitting_sample_phi
<<SF aux: procedures>>=
subroutine splitting_sample_phi (d, r)
class(splitting_data_t), intent(inout) :: d
real(default), intent(in) :: r
if (d%collinear) then
d%phi = 0
else
d%phi = (1-r) * d%phi0 + r * d%phi1
end if
end subroutine splitting_sample_phi
@ %def splitting_sample_phi
@ Inverse:
<<SF aux: splitting data: TBP>>=
procedure :: inverse_phi => splitting_inverse_phi
<<SF aux: procedures>>=
subroutine splitting_inverse_phi (d, r)
class(splitting_data_t), intent(in) :: d
real(default), intent(out) :: r
if (d%collinear) then
r = 0
else
r = (d%phi - d%phi0) / (d%phi1 - d%phi0)
end if
end subroutine splitting_inverse_phi
@ %def splitting_inverse_phi
@
\subsection{Splitting}
In this function, we actually perform the splitting. The incoming momentum
$k$ is split into (if no recoil) $q_1=(1-x)k$ and $q_2=xk$.
Apart from the splitting data, we need the incoming momentum $k$, the momentum
transfer $t$, and the azimuthal angle $\phi$. The momentum fraction $x$ is
already known here.
Alternatively, we can split without recoil. The azimuthal angle is
irrelevant, and the momentum transfer is always equal to the upper
limit $t_1$, so the polar angle is zero. Obviously, if there are
nonzero masses it is not possible to keep both energy-momentum
conservation and at the same time all particles on shell. We choose
for dropping the on-shell condition here.
<<SF aux: splitting data: TBP>>=
procedure :: split_momentum => splitting_split_momentum
<<SF aux: procedures>>=
function splitting_split_momentum (d, k) result (q)
class(splitting_data_t), intent(in) :: d
type(vector4_t), dimension(2) :: q
type(vector4_t), intent(in) :: k
real(default) :: st2, ct2, st, ct, cp, sp
type(lorentz_transformation_t) :: rot
real(default) :: tt0, tt1, den
type(vector3_t) :: kk, q1, q2
if (d%collinear) then
if (vanishes (d%s) .and. vanishes(d%u)) then
q(1) = d%xb * k
q(2) = d%x * k
else
kk = space_part (k)
q1 = d%xb * (d%pb / d%p) * kk
q2 = kk - q1
q(1) = vector4_moving (d%xb * d%E, q1)
q(2) = vector4_moving (d%x * d%E, q2)
end if
else
den = 2 * d%xb * d%p * d%pb
tt0 = max (d%t - d%t0, 0._default)
tt1 = min (d%t - d%t1, 0._default)
if (den**2 <= epsilon(den)) then
st2 = 0
else
st2 = - (tt0 * tt1) / den ** 2
end if
if (st2 > 1) then
st2 = 1
end if
ct2 = 1 - st2
st = sqrt (max (st2, 0._default))
ct = sqrt (max (ct2, 0._default))
if ((d%t - d%t0 + d%t - d%t1) < 0) then
ct = - ct
end if
sp = sin (d%phi)
cp = cos (d%phi)
rot = rotation_to_2nd (3, space_part (k))
q1 = vector3_moving (d%xb * d%pb * [st * cp, st * sp, ct])
q2 = vector3_moving (d%p, 3) - q1
q(1) = rot * vector4_moving (d%xb * d%E, q1)
q(2) = rot * vector4_moving (d%x * d%E, q2)
end if
end function splitting_split_momentum
@ %def splitting_split_momentum
@
Momenta generated by splitting will in general be off-shell. They are
on-shell only if they are collinear and massless. This subroutine
puts them on shell by brute force, violating either momentum or energy
conservation. The direction of three-momentum is always retained.
If the energy is below mass shell, we return a zero momentum.
<<SF aux: parameters>>=
integer, parameter, public :: KEEP_ENERGY = 0, KEEP_MOMENTUM = 1
@ %def KEEP_ENERGY KEEP_MOMENTUM
<<SF aux: public>>=
public :: on_shell
<<SF aux: procedures>>=
elemental subroutine on_shell (p, m2, keep)
type(vector4_t), intent(inout) :: p
real(default), intent(in) :: m2
integer, intent(in) :: keep
real(default) :: E, E2, pn
select case (keep)
case (KEEP_ENERGY)
E = energy (p)
E2 = E ** 2
if (E2 >= m2) then
pn = sqrt (E2 - m2)
p = vector4_moving (E, pn * direction (space_part (p)))
else
p = vector4_null
end if
case (KEEP_MOMENTUM)
E = sqrt (space_part (p) ** 2 + m2)
p = vector4_moving (E, space_part (p))
end select
end subroutine on_shell
@ %def on_shell
@
\subsection{Recovering the splitting}
This is the inverse problem. We have on-shell momenta and want to
deduce the splitting parameters $x$, $t$, and $\phi$.
Update 2018-08-22: As a true inverse to [[splitting_split_momentum]], we now use
not just a single momentum [[q2]] as before, but the momentum pair [[q1]], [[q2]]
for recovering $x$ and $\bar x$ separately. If $x$ happens to be
close to $1$, we would completely lose the tiny $\bar x$ value,
otherwise, and thus get a meaningless result.
<<SF aux: splitting data: TBP>>=
procedure :: recover => splitting_recover
<<SF aux: procedures>>=
subroutine splitting_recover (d, k, q, keep)
class(splitting_data_t), intent(inout) :: d
type(vector4_t), intent(in) :: k
type(vector4_t), dimension(2), intent(in) :: q
integer, intent(in) :: keep
type(lorentz_transformation_t) :: rot
type(vector4_t) :: k0
type(vector4_t), dimension(2) :: q0
real(default) :: p1, p2, p3, pt2, pp2, pl
real(default) :: aux, den, norm
real(default) :: st2, ct2, ct
rot = inverse (rotation_to_2nd (3, space_part (k)))
q0 = rot * q
p1 = vector4_get_component (q0(2), 1)
p2 = vector4_get_component (q0(2), 2)
p3 = vector4_get_component (q0(2), 3)
pt2 = p1 ** 2 + p2 ** 2
pp2 = p1 ** 2 + p2 ** 2 + p3 ** 2
pl = abs (p3)
k0 = vector4_moving (d%E, d%p, 3)
select case (keep)
case (KEEP_ENERGY)
d%x = energy (q0(2)) / d%E
d%xb = energy (q0(1)) / d%E
call d%set_t_bounds ()
if (.not. d%collinear) then
aux = (d%xb * d%pb) ** 2 * pp2 - d%p ** 2 * pt2
den = d%p ** 2 - (d%xb * d%pb) ** 2
if (aux >= 0 .and. den > 0) then
norm = (d%p * pl + sqrt (aux)) / den
else
norm = 1
end if
end if
case (KEEP_MOMENTUM)
d%xb = sqrt (space_part (q0(1)) ** 2 + d%u) / d%E
d%x = 1 - d%xb
call d%set_t_bounds ()
norm = 1
end select
if (d%collinear) then
d%t = d%t1
d%phi = 0
else
if ((d%xb * d%pb * norm)**2 < epsilon(d%xb)) then
st2 = 1
else
st2 = pt2 / (d%xb * d%pb * norm ) ** 2
end if
if (st2 > 1) then
st2 = 1
end if
ct2 = 1 - st2
ct = sqrt (max (ct2, 0._default))
if (.not. vanishes (1 + ct)) then
d%t = d%t1 - 2 * d%xb * d%p * d%pb * st2 / (1 + ct)
else
d%t = d%t0
end if
if (.not. vanishes (p1) .or. .not. vanishes (p2)) then
d%phi = atan2 (-p2, -p1)
else
d%phi = 0
end if
end if
end subroutine splitting_recover
@ %def splitting_recover
@
\subsection{Extract data}
<<SF aux: splitting data: TBP>>=
procedure :: get_x => splitting_get_x
procedure :: get_xb => splitting_get_xb
<<SF aux: procedures>>=
function splitting_get_x (sd) result (x)
class(splitting_data_t), intent(in) :: sd
real(default) :: x
x = sd%x
end function splitting_get_x
function splitting_get_xb (sd) result (xb)
class(splitting_data_t), intent(in) :: sd
real(default) :: xb
xb = sd%xb
end function splitting_get_xb
@ %def splitting_get_x
@ %def splitting_get_xb
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_aux_ut.f90]]>>=
<<File header>>
module sf_aux_ut
use unit_tests
use sf_aux_uti
<<Standard module head>>
<<SF aux: public test>>
contains
<<SF aux: test driver>>
end module sf_aux_ut
@ %def sf_aux_ut
@
<<[[sf_aux_uti.f90]]>>=
<<File header>>
module sf_aux_uti
<<Use kinds>>
use lorentz
use sf_aux
<<Standard module head>>
<<SF aux: test declarations>>
contains
<<SF aux: tests>>
end module sf_aux_uti
@ %def sf_aux_ut
@ API: driver for the unit tests below.
<<SF aux: public test>>=
public :: sf_aux_test
<<SF aux: test driver>>=
subroutine sf_aux_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF aux: execute tests>>
end subroutine sf_aux_test
@ %def sf_aux_test
@
\subsubsection{Momentum splitting: massless radiation}
Compute momentum splitting for generic kinematics. It turns out that
for $x=0.5$, where $t-m^2$ is the geometric mean between its upper and
lower bounds (this can be directly seen from the logarithmic
distribution in the function [[sample_t]] for $r \equiv x = 1 - x =
0.5$), we arrive at an exact number $t=-0.15$ for the given
input values.
<<SF aux: execute tests>>=
call test (sf_aux_1, "sf_aux_1", &
"massless radiation", &
u, results)
<<SF aux: test declarations>>=
public :: sf_aux_1
<<SF aux: tests>>=
subroutine sf_aux_1 (u)
integer, intent(in) :: u
type(splitting_data_t) :: sd
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q, q0
real(default) :: E, mk, mp, mq
real(default) :: x, r1, r2, r1o, r2o
real(default) :: k2, q0_2, q1_2, q2_2
write (u, "(A)") "* Test output: sf_aux_1"
write (u, "(A)") "* Purpose: compute momentum splitting"
write (u, "(A)") " (massless radiated particle)"
write (u, "(A)")
E = 1
mk = 0.3_default
mp = 0
mq = mk
k = vector4_moving (E, sqrt (E**2 - mk**2), 3)
k2 = k ** 2; call pacify (k2, 1e-10_default)
x = 0.6_default
r1 = 0.5_default
r2 = 0.125_default
write (u, "(A)") "* (1) Non-collinear setup"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%sample_t (r1)
call sd%sample_phi (r2)
call sd%write (u)
q = sd%split_momentum (k)
q1_2 = q(1) ** 2; call pacify (q1_2, 1e-10_default)
q2_2 = q(2) ** 2; call pacify (q2_2, 1e-10_default)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: s"
write (u, "(2(1x,F11.8))") sd%s, k2
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") sd%t, q2_2
write (u, "(A)") "Compare: u"
write (u, "(2(1x,F11.8))") sd%u, q1_2
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") sd%x, energy (q(2)) / energy (k)
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") sd%xb, energy (q(1)) / energy (k)
write (u, "(A)")
write (u, "(A)") "Extract: x, 1-x"
write (u, "(2(1x,F11.8))") sd%get_x (), sd%get_xb ()
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep energy)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_ENERGY)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q0_2 = q0(2) ** 2; call pacify (q0_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q0_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%recover (k, q0, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
call sd%inverse_t (r1o)
write (u, "(A)") "Compare: r1"
write (u, "(2(1x,F11.8))") r1, r1o
call sd%inverse_phi (r2o)
write (u, "(A)") "Compare: r2"
write (u, "(2(1x,F11.8))") r2, r2o
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep momentum)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_MOMENTUM)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q0_2 = q0(2) ** 2; call pacify (q0_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q0_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%recover (k, q0, KEEP_MOMENTUM)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
call sd%inverse_t (r1o)
write (u, "(A)") "Compare: r1"
write (u, "(2(1x,F11.8))") r1, r1o
call sd%inverse_phi (r2o)
write (u, "(A)") "Compare: r2"
write (u, "(2(1x,F11.8))") r2, r2o
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* (2) Collinear setup"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, 1 - x)
call sd%write (u)
q = sd%split_momentum (k)
q1_2 = q(1) ** 2; call pacify (q1_2, 1e-10_default)
q2_2 = q(2) ** 2; call pacify (q2_2, 1e-10_default)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: s"
write (u, "(2(1x,F11.8))") sd%s, k2
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") sd%t, q2_2
write (u, "(A)") "Compare: u"
write (u, "(2(1x,F11.8))") sd%u, q1_2
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") sd%x, energy (q(2)) / energy (k)
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") sd%xb, energy (q(1)) / energy (k)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep energy)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_ENERGY)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q0_2 = q0(2) ** 2; call pacify (q0_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q0_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%recover (k, q0, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep momentum)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_MOMENTUM)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q0_2 = q0(2) ** 2; call pacify (q0_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q0_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%recover (k, q0, KEEP_MOMENTUM)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_aux_1"
end subroutine sf_aux_1
@ %def sf_aux_1
@
\subsubsection{Momentum splitting: massless parton}
Compute momentum splitting for generic kinematics. It turns out that
for $x=0.5$, where $t-m^2$ is the geometric mean between its upper and
lower bounds, we arrive at an exact number $t=-0.36$ for the given
input values.
<<SF aux: execute tests>>=
call test (sf_aux_2, "sf_aux_2", &
"massless parton", &
u, results)
<<SF aux: test declarations>>=
public :: sf_aux_2
<<SF aux: tests>>=
subroutine sf_aux_2 (u)
integer, intent(in) :: u
type(splitting_data_t) :: sd
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q, q0
real(default) :: E, mk, mp, mq
real(default) :: x, r1, r2, r1o, r2o
real(default) :: k2, q02_2, q1_2, q2_2
write (u, "(A)") "* Test output: sf_aux_2"
write (u, "(A)") "* Purpose: compute momentum splitting"
write (u, "(A)") " (massless outgoing particle)"
write (u, "(A)")
E = 1
mk = 0.3_default
mp = mk
mq = 0
k = vector4_moving (E, sqrt (E**2 - mk**2), 3)
k2 = k ** 2; call pacify (k2, 1e-10_default)
x = 0.6_default
r1 = 0.5_default
r2 = 0.125_default
write (u, "(A)") "* (1) Non-collinear setup"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%sample_t (r1)
call sd%sample_phi (r2)
call sd%write (u)
q = sd%split_momentum (k)
q1_2 = q(1) ** 2; call pacify (q1_2, 1e-10_default)
q2_2 = q(2) ** 2; call pacify (q2_2, 1e-10_default)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: s"
write (u, "(2(1x,F11.8))") sd%s, k2
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") sd%t, q2_2
write (u, "(A)") "Compare: u"
write (u, "(2(1x,F11.8))") sd%u, q1_2
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") sd%x, energy (q(2)) / energy (k)
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") sd%xb, energy (q(1)) / energy (k)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep energy)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_ENERGY)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q02_2 = q0(2) ** 2; call pacify (q02_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q02_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%recover (k, q0, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
call sd%inverse_t (r1o)
write (u, "(A)") "Compare: r1"
write (u, "(2(1x,F11.8))") r1, r1o
call sd%inverse_phi (r2o)
write (u, "(A)") "Compare: r2"
write (u, "(2(1x,F11.8))") r2, r2o
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep momentum)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_MOMENTUM)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q02_2 = q0(2) ** 2; call pacify (q02_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q02_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%recover (k, q0, KEEP_MOMENTUM)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
call sd%inverse_t (r1o)
write (u, "(A)") "Compare: r1"
write (u, "(2(1x,F11.8))") r1, r1o
call sd%inverse_phi (r2o)
write (u, "(A)") "Compare: r2"
write (u, "(2(1x,F11.8))") r2, r2o
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* (2) Collinear setup"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, 1 - x)
call sd%write (u)
q = sd%split_momentum (k)
q1_2 = q(1) ** 2; call pacify (q1_2, 1e-10_default)
q2_2 = q(2) ** 2; call pacify (q2_2, 1e-10_default)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: s"
write (u, "(2(1x,F11.8))") sd%s, k2
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") sd%t, q2_2
write (u, "(A)") "Compare: u"
write (u, "(2(1x,F11.8))") sd%u, q1_2
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") sd%x, energy (q(2)) / energy (k)
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") sd%xb, energy (q(1)) / energy (k)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep energy)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_ENERGY)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q02_2 = q0(2) ** 2; call pacify (q02_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q02_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%recover (k, q0, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep momentum)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_MOMENTUM)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q02_2 = q0(2) ** 2; call pacify (q02_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q02_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%recover (k, q0, KEEP_MOMENTUM)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_aux_2"
end subroutine sf_aux_2
@ %def sf_aux_2
@
\subsubsection{Momentum splitting: all massless}
Compute momentum splitting for massless kinematics. In the non-collinear
case, we need a lower cutoff for $|t|$, otherwise a logarithmic distribution
is not possible.
<<SF aux: execute tests>>=
call test (sf_aux_3, "sf_aux_3", &
"massless parton", &
u, results)
<<SF aux: test declarations>>=
public :: sf_aux_3
<<SF aux: tests>>=
subroutine sf_aux_3 (u)
integer, intent(in) :: u
type(splitting_data_t) :: sd
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q, q0
real(default) :: E, mk, mp, mq, qmin, qmax
real(default) :: x, r1, r2, r1o, r2o
real(default) :: k2, q02_2, q1_2, q2_2
write (u, "(A)") "* Test output: sf_aux_3"
write (u, "(A)") "* Purpose: compute momentum splitting"
write (u, "(A)") " (all massless, q cuts)"
write (u, "(A)")
E = 1
mk = 0
mp = 0
mq = 0
qmin = 1e-2_default
qmax = 1e0_default
k = vector4_moving (E, sqrt (E**2 - mk**2), 3)
k2 = k ** 2; call pacify (k2, 1e-10_default)
x = 0.6_default
r1 = 0.5_default
r2 = 0.125_default
write (u, "(A)") "* (1) Non-collinear setup"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%sample_t (r1, t1 = - qmin ** 2, t0 = - qmax **2)
call sd%sample_phi (r2)
call sd%write (u)
q = sd%split_momentum (k)
q1_2 = q(1) ** 2; call pacify (q1_2, 1e-10_default)
q2_2 = q(2) ** 2; call pacify (q2_2, 1e-10_default)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: s"
write (u, "(2(1x,F11.8))") sd%s, k2
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") sd%t, q2_2
write (u, "(A)") "Compare: u"
write (u, "(2(1x,F11.8))") sd%u, q1_2
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") sd%x, energy (q(2)) / energy (k)
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") sd%xb, energy (q(1)) / energy (k)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep energy)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_ENERGY)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q02_2 = q0(2) ** 2; call pacify (q02_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q02_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%recover (k, q0, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
call sd%inverse_t (r1o, t1 = - qmin ** 2, t0 = - qmax **2)
write (u, "(A)") "Compare: r1"
write (u, "(2(1x,F11.8))") r1, r1o
call sd%inverse_phi (r2o)
write (u, "(A)") "Compare: r2"
write (u, "(2(1x,F11.8))") r2, r2o
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep momentum)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_MOMENTUM)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q02_2 = q0(2) ** 2; call pacify (q02_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q02_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%recover (k, q0, KEEP_MOMENTUM)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
call sd%inverse_t (r1o, t1 = - qmin ** 2, t0 = - qmax **2)
write (u, "(A)") "Compare: r1"
write (u, "(2(1x,F11.8))") r1, r1o
call sd%inverse_phi (r2o)
write (u, "(A)") "Compare: r2"
write (u, "(2(1x,F11.8))") r2, r2o
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* (2) Collinear setup"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, 1 - x)
call sd%write (u)
q = sd%split_momentum (k)
q1_2 = q(1) ** 2; call pacify (q1_2, 1e-10_default)
q2_2 = q(2) ** 2; call pacify (q2_2, 1e-10_default)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: s"
write (u, "(2(1x,F11.8))") sd%s, k2
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") sd%t, q2_2
write (u, "(A)") "Compare: u"
write (u, "(2(1x,F11.8))") sd%u, q1_2
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") sd%x, energy (q(2)) / energy (k)
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") sd%xb, energy (q(1)) / energy (k)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep energy)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_ENERGY)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q02_2 = q0(2) ** 2; call pacify (q02_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q02_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%recover (k, q0, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Project on-shell (keep momentum)"
q0 = q
call on_shell (q0, [mp**2, mq**2], KEEP_MOMENTUM)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q0), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q0(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q0(2), u)
write (u, "(A)")
write (u, "(A)") "Compare: mo^2"
q02_2 = q0(2) ** 2; call pacify (q02_2, 1e-10_default)
write (u, "(2(1x,F11.8))") sd%m2, q02_2
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momentum"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2)
call sd%set_t_bounds (x, 1 - x)
call sd%recover (k, q0, KEEP_MOMENTUM)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: t"
write (u, "(2(1x,F11.8))") q2_2, sd%t
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_aux_3"
end subroutine sf_aux_3
@ %def sf_aux_3
@
\subsubsection{Endpoint stability}
Compute momentum splitting for collinear kinematics close to both
endpoints. In particular, check both directions $x\to$ momenta and
momenta $\to x$.
For purely massless collinear splitting, the [[KEEP_XXX]] flag is
irrelevant. We choose [[KEEP_ENERGY]] here.
<<SF aux: execute tests>>=
call test (sf_aux_4, "sf_aux_4", &
"endpoint numerics", &
u, results)
<<SF aux: test declarations>>=
public :: sf_aux_4
<<SF aux: tests>>=
subroutine sf_aux_4 (u)
integer, intent(in) :: u
type(splitting_data_t) :: sd
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E, mk, mp, mq, qmin, qmax
real(default) :: x, xb
write (u, "(A)") "* Test output: sf_aux_4"
write (u, "(A)") "* Purpose: compute massless collinear splitting near endpoint"
E = 1
mk = 0
mp = 0
mq = 0
qmin = 1e-2_default
qmax = 1e0_default
k = vector4_moving (E, sqrt (E**2 - mk**2), 3)
x = 0.1_default
xb = 1 - x
write (u, "(A)")
write (u, "(A)") "* (1) Collinear setup, moderate kinematics"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, xb)
call sd%write (u)
q = sd%split_momentum (k)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momenta"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, xb)
call sd%recover (k, q, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") xb, sd%xb
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* (2) Close to x=0"
write (u, "(A)")
x = 1e-9_default
xb = 1 - x
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, xb)
call sd%write (u)
q = sd%split_momentum (k)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momenta"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, xb)
call sd%recover (k, q, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") xb, sd%xb
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* (3) Close to x=1"
write (u, "(A)")
xb = 1e-9_default
x = 1 - xb
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, xb)
call sd%write (u)
q = sd%split_momentum (k)
write (u, "(A)")
write (u, "(A)") "Incoming momentum k ="
call vector4_write (k, u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum sum p + q ="
call vector4_write (sum (q), u)
write (u, "(A)")
write (u, "(A)") "Radiated momentum p ="
call vector4_write (q(1), u)
write (u, "(A)")
write (u, "(A)") "Outgoing momentum q ="
call vector4_write (q(2), u)
write (u, "(A)")
write (u, "(A)") "* Recover parameters from outgoing momenta"
write (u, "(A)")
call sd%init (k, mk**2, mp**2, mq**2, collinear = .true.)
call sd%set_t_bounds (x, xb)
call sd%recover (k, q, KEEP_ENERGY)
write (u, "(A)") "Compare: x"
write (u, "(2(1x,F11.8))") x, sd%x
write (u, "(A)") "Compare: 1-x"
write (u, "(2(1x,F11.8))") xb, sd%xb
write (u, "(A)")
call sd%write (u)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_aux_4"
end subroutine sf_aux_4
@ %def sf_aux_4
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Mappings for structure functions}
In this module, we provide a wrapper for useful mappings of the unit
(hyper-)square that we can apply to a set of structure functions.
In some cases it is useful, or even mandatory, to map the MC input
parameters nontrivially onto a set of structure functions for the two
beams. In all cases considered here, instead of $x_1,x_2,\ldots$ as
parameters for the beams, we generate one parameter that is equal, or
related to, the product $x_1x_2\cdots$ (so it directly corresponds to
$\sqrt{s}$). The other parameters describe the distribution of energy
(loss) between beams and radiations.
<<[[sf_mappings.f90]]>>=
<<File header>>
module sf_mappings
<<Use kinds>>
use kinds, only: double
use io_units
use constants, only: pi, zero, one
use numeric_utils
use diagnostics
<<Standard module head>>
<<SF mappings: public>>
<<SF mappings: parameters>>
<<SF mappings: types>>
<<SF mappings: interfaces>>
contains
<<SF mappings: procedures>>
end module sf_mappings
@ %def sf_mappings
@
\subsection{Base type}
First, we define an abstract base type for the mapping. In all cases
we need to store the indices of the parameters on which the mapping
applies. Additional parameters can be stored in the extensions of
this type.
<<SF mappings: public>>=
public :: sf_mapping_t
<<SF mappings: types>>=
type, abstract :: sf_mapping_t
integer, dimension(:), allocatable :: i
contains
<<SF mappings: sf mapping: TBP>>
end type sf_mapping_t
@ %def sf_mapping_t
@ The output routine is deferred:
<<SF mappings: sf mapping: TBP>>=
procedure (sf_mapping_write), deferred :: write
<<SF mappings: interfaces>>=
abstract interface
subroutine sf_mapping_write (object, unit)
import
class(sf_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
end subroutine sf_mapping_write
end interface
@ %def sf_mapping_write
@ Initializer for the base type. The array of parameter indices is
allocated but initialized to zero.
<<SF mappings: sf mapping: TBP>>=
procedure :: base_init => sf_mapping_base_init
<<SF mappings: procedures>>=
subroutine sf_mapping_base_init (mapping, n_par)
class(sf_mapping_t), intent(out) :: mapping
integer, intent(in) :: n_par
allocate (mapping%i (n_par))
mapping%i = 0
end subroutine sf_mapping_base_init
@ %def sf_mapping_base_init
@ Set an index value.
<<SF mappings: sf mapping: TBP>>=
procedure :: set_index => sf_mapping_set_index
<<SF mappings: procedures>>=
subroutine sf_mapping_set_index (mapping, j, i)
class(sf_mapping_t), intent(inout) :: mapping
integer, intent(in) :: j, i
mapping%i(j) = i
end subroutine sf_mapping_set_index
@ %def sf_mapping_set_index
@ Retrieve an index value.
<<SF mappings: sf mapping: TBP>>=
procedure :: get_index => sf_mapping_get_index
<<SF mappings: procedures>>=
function sf_mapping_get_index (mapping, j) result (i)
class(sf_mapping_t), intent(inout) :: mapping
integer, intent(in) :: j
integer :: i
i = mapping%i(j)
end function sf_mapping_get_index
@ %def sf_mapping_get_index
@ Return the dimensionality, i.e., the number of parameters.
<<SF mappings: sf mapping: TBP>>=
procedure :: get_n_dim => sf_mapping_get_n_dim
<<SF mappings: procedures>>=
function sf_mapping_get_n_dim (mapping) result (n)
class(sf_mapping_t), intent(in) :: mapping
integer :: n
n = size (mapping%i)
end function sf_mapping_get_n_dim
@ %def sf_mapping_get_n_dim
@ Computation: the values [[p]] are the input parameters, the values
[[r]] are the output parameters. The values [[rb]] are defined as
$\bar r = 1 - r$, but provided explicitly. They allow us to avoid
numerical problems near $r=1$.
The extra parameter [[x_free]]
indicates that the total energy has already been renormalized by this
factor. We have to take such a factor into account in a resonance or
on-shell mapping.
The Jacobian is [[f]]. We modify only
the two parameters indicated by the indices [[i]].
<<SF mappings: sf mapping: TBP>>=
procedure (sf_mapping_compute), deferred :: compute
<<SF mappings: interfaces>>=
abstract interface
subroutine sf_mapping_compute (mapping, r, rb, f, p, pb, x_free)
import
class(sf_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
end subroutine sf_mapping_compute
end interface
@ %def sf_mapping_compute
@ The inverse mapping. Use [[r]] and/or [[rb]] to reconstruct [[p]]
and also compute [[f]].
<<SF mappings: sf mapping: TBP>>=
procedure (sf_mapping_inverse), deferred :: inverse
<<SF mappings: interfaces>>=
abstract interface
subroutine sf_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
import
class(sf_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
end subroutine sf_mapping_inverse
end interface
@ %def sf_mapping_inverse
@
\subsection{Methods for self-tests}
This is a shorthand for: inject parameters, compute the mapping,
display results, compute the inverse, display again. We provide an
output format for the parameters and, optionally, a different output
format for the Jacobians.
<<SF mappings: sf mapping: TBP>>=
procedure :: check => sf_mapping_check
<<SF mappings: procedures>>=
subroutine sf_mapping_check (mapping, u, p_in, pb_in, fmt_p, fmt_f)
class(sf_mapping_t), intent(inout) :: mapping
integer, intent(in) :: u
real(default), dimension(:), intent(in) :: p_in, pb_in
character(*), intent(in) :: fmt_p
character(*), intent(in), optional :: fmt_f
real(default), dimension(size(p_in)) :: p, pb, r, rb
real(default) :: f, tolerance
tolerance = 1.5E-17
p = p_in
pb= pb_in
call mapping%compute (r, rb, f, p, pb)
call pacify (p, tolerance)
call pacify (pb, tolerance)
call pacify (r, tolerance)
call pacify (rb, tolerance)
write (u, "(3x,A,9(1x," // fmt_p // "))") "p =", p
write (u, "(3x,A,9(1x," // fmt_p // "))") "pb=", pb
write (u, "(3x,A,9(1x," // fmt_p // "))") "r =", r
write (u, "(3x,A,9(1x," // fmt_p // "))") "rb=", rb
if (present (fmt_f)) then
write (u, "(3x,A,9(1x," // fmt_f // "))") "f =", f
else
write (u, "(3x,A,9(1x," // fmt_p // "))") "f =", f
end if
write (u, *)
call mapping%inverse (r, rb, f, p, pb)
call pacify (p, tolerance)
call pacify (pb, tolerance)
call pacify (r, tolerance)
call pacify (rb, tolerance)
write (u, "(3x,A,9(1x," // fmt_p // "))") "p =", p
write (u, "(3x,A,9(1x," // fmt_p // "))") "pb=", pb
write (u, "(3x,A,9(1x," // fmt_p // "))") "r =", r
write (u, "(3x,A,9(1x," // fmt_p // "))") "rb=", rb
if (present (fmt_f)) then
write (u, "(3x,A,9(1x," // fmt_f // "))") "f =", f
else
write (u, "(3x,A,9(1x," // fmt_p // "))") "f =", f
end if
write (u, *)
write (u, "(3x,A,9(1x," // fmt_p // "))") "*r=", product (r)
end subroutine sf_mapping_check
@ %def sf_mapping_check
@ This is a consistency check for the self-tests: the integral over the unit
square should be unity. We estimate this by a simple binning and adding up
the values; this should be sufficient for a self-test.
The argument is the requested number of sampling points. We take the square
root for binning in both dimensions, so the precise number might be
different.
<<SF mappings: sf mapping: TBP>>=
procedure :: integral => sf_mapping_integral
<<SF mappings: procedures>>=
function sf_mapping_integral (mapping, n_calls) result (integral)
class(sf_mapping_t), intent(inout) :: mapping
integer, intent(in) :: n_calls
real(default) :: integral
integer :: n_dim, n_bin, k
real(default), dimension(:), allocatable :: p, pb, r, rb
integer, dimension(:), allocatable :: ii
real(default) :: dx, f, s
n_dim = mapping%get_n_dim ()
allocate (p (n_dim))
allocate (pb(n_dim))
allocate (r (n_dim))
allocate (rb(n_dim))
allocate (ii(n_dim))
n_bin = nint (real (n_calls, default) ** (1._default / n_dim))
dx = 1._default / n_bin
s = 0
ii = 1
SAMPLE: do
do k = 1, n_dim
p(k) = ii(k) * dx - dx/2
pb(k) = (n_bin - ii(k)) * dx + dx/2
end do
call mapping%compute (r, rb, f, p, pb)
s = s + f
INCR: do k = 1, n_dim
ii(k) = ii(k) + 1
if (ii(k) <= n_bin) then
exit INCR
else if (k < n_dim) then
ii(k) = 1
else
exit SAMPLE
end if
end do INCR
end do SAMPLE
integral = s / real (n_bin, default) ** n_dim
end function sf_mapping_integral
@ %def sf_mapping_integral
@
\subsection{Implementation: standard mapping}
This maps the unit square ($r_1,r_2$) such that $p_1$ is the product $r_1r_2$,
while $p_2$ is related to the ratio.
<<SF mappings: public>>=
public :: sf_s_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_s_mapping_t
logical :: power_set = .false.
real(default) :: power = 1
contains
<<SF mappings: sf standard mapping: TBP>>
end type sf_s_mapping_t
@ %def sf_s_mapping_t
@ Output.
<<SF mappings: sf standard mapping: TBP>>=
procedure :: write => sf_s_mapping_write
<<SF mappings: procedures>>=
subroutine sf_s_mapping_write (object, unit)
class(sf_s_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
write (u, "(A,F7.5,A)") ": standard (", object%power, ")"
end subroutine sf_s_mapping_write
@ %def sf_s_mapping_write
@ Initialize: index pair and power parameter.
<<SF mappings: sf standard mapping: TBP>>=
procedure :: init => sf_s_mapping_init
<<SF mappings: procedures>>=
subroutine sf_s_mapping_init (mapping, power)
class(sf_s_mapping_t), intent(out) :: mapping
real(default), intent(in), optional :: power
call mapping%base_init (2)
if (present (power)) then
mapping%power_set = .true.
mapping%power = power
end if
end subroutine sf_s_mapping_init
@ %def sf_s_mapping_init
@ Apply mapping.
<<SF mappings: sf standard mapping: TBP>>=
procedure :: compute => sf_s_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_s_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_s_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: r2
integer :: j
if (mapping%power_set) then
call map_unit_square (r2, f, p(mapping%i), mapping%power)
else
call map_unit_square (r2, f, p(mapping%i))
end if
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2(j)
rb(mapping%i(j)) = 1 - r2(j)
end do
end subroutine sf_s_mapping_compute
@ %def sf_s_mapping_compute
@ Apply inverse.
<<SF mappings: sf standard mapping: TBP>>=
procedure :: inverse => sf_s_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_s_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_s_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: p2
integer :: j
if (mapping%power_set) then
call map_unit_square_inverse (r(mapping%i), f, p2, mapping%power)
else
call map_unit_square_inverse (r(mapping%i), f, p2)
end if
p = r
pb= rb
do j = 1, 2
p (mapping%i(j)) = p2(j)
pb(mapping%i(j)) = 1 - p2(j)
end do
end subroutine sf_s_mapping_inverse
@ %def sf_s_mapping_inverse
@
\subsection{Implementation: resonance pair mapping}
This maps the unit square ($r_1,r_2$) such that $p_1$ is the product $r_1r_2$,
while $p_2$ is related to the ratio, then it maps $p_1$ to itself
according to a Breit-Wigner shape, i.e., a flat prior distribution in $p_1$
results in a Breit-Wigner distribution. Mass and width of the BW are
rescaled by the energy, thus dimensionless fractions.
<<SF mappings: public>>=
public :: sf_res_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_res_mapping_t
real(default) :: m = 0
real(default) :: w = 0
contains
<<SF mappings: sf resonance mapping: TBP>>
end type sf_res_mapping_t
@ %def sf_res_mapping_t
@ Output.
<<SF mappings: sf resonance mapping: TBP>>=
procedure :: write => sf_res_mapping_write
<<SF mappings: procedures>>=
subroutine sf_res_mapping_write (object, unit)
class(sf_res_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
write (u, "(A,F7.5,', ',F7.5,A)") ": resonance (", object%m, object%w, ")"
end subroutine sf_res_mapping_write
@ %def sf_res_mapping_write
@ Initialize: index pair and dimensionless mass and width parameters.
<<SF mappings: sf resonance mapping: TBP>>=
procedure :: init => sf_res_mapping_init
<<SF mappings: procedures>>=
subroutine sf_res_mapping_init (mapping, m, w)
class(sf_res_mapping_t), intent(out) :: mapping
real(default), intent(in) :: m, w
call mapping%base_init (2)
mapping%m = m
mapping%w = w
end subroutine sf_res_mapping_init
@ %def sf_res_mapping_init
@ Apply mapping.
<<SF mappings: sf resonance mapping: TBP>>=
procedure :: compute => sf_res_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_res_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_res_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: r2, p2
real(default) :: fbw, f2, p1m
integer :: j
p2 = p(mapping%i)
call map_breit_wigner &
(p1m, fbw, p2(1), mapping%m, mapping%w, x_free)
call map_unit_square (r2, f2, [p1m, p2(2)])
f = fbw * f2
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2(j)
rb(mapping%i(j)) = 1 - r2(j)
end do
end subroutine sf_res_mapping_compute
@ %def sf_res_mapping_compute
@ Apply inverse.
<<SF mappings: sf resonance mapping: TBP>>=
procedure :: inverse => sf_res_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_res_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_res_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: p2
real(default) :: fbw, f2, p1m
call map_unit_square_inverse (r(mapping%i), f2, p2)
call map_breit_wigner_inverse &
(p2(1), fbw, p1m, mapping%m, mapping%w, x_free)
p = r
pb= rb
p (mapping%i(1)) = p1m
pb(mapping%i(1)) = 1 - p1m
p (mapping%i(2)) = p2(2)
pb(mapping%i(2)) = 1 - p2(2)
f = fbw * f2
end subroutine sf_res_mapping_inverse
@ %def sf_res_mapping_inverse
@
\subsection{Implementation: resonance single mapping}
While simpler, this is needed for structure-function setups only in
exceptional cases.
This maps the unit interval ($r_1$) to itself
according to a Breit-Wigner shape, i.e., a flat prior distribution in $r_1$
results in a Breit-Wigner distribution. Mass and width of the BW are
rescaled by the energy, thus dimensionless fractions.
<<SF mappings: public>>=
public :: sf_res_mapping_single_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_res_mapping_single_t
real(default) :: m = 0
real(default) :: w = 0
contains
<<SF mappings: sf resonance single mapping: TBP>>
end type sf_res_mapping_single_t
@ %def sf_res_mapping_single_t
@ Output.
<<SF mappings: sf resonance single mapping: TBP>>=
procedure :: write => sf_res_mapping_single_write
<<SF mappings: procedures>>=
subroutine sf_res_mapping_single_write (object, unit)
class(sf_res_mapping_single_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,')')", advance="no") object%i
end if
write (u, "(A,F7.5,', ',F7.5,A)") ": resonance (", object%m, object%w, ")"
end subroutine sf_res_mapping_single_write
@ %def sf_res_mapping_single_write
@ Initialize: single index (!) and dimensionless mass and width parameters.
<<SF mappings: sf resonance single mapping: TBP>>=
procedure :: init => sf_res_mapping_single_init
<<SF mappings: procedures>>=
subroutine sf_res_mapping_single_init (mapping, m, w)
class(sf_res_mapping_single_t), intent(out) :: mapping
real(default), intent(in) :: m, w
call mapping%base_init (1)
mapping%m = m
mapping%w = w
end subroutine sf_res_mapping_single_init
@ %def sf_res_mapping_single_init
@ Apply mapping.
<<SF mappings: sf resonance single mapping: TBP>>=
procedure :: compute => sf_res_mapping_single_compute
<<SF mappings: procedures>>=
subroutine sf_res_mapping_single_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_res_mapping_single_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(1) :: r2, p2
real(default) :: fbw
integer :: j
p2 = p(mapping%i)
call map_breit_wigner &
(r2(1), fbw, p2(1), mapping%m, mapping%w, x_free)
f = fbw
r = p
rb= pb
r (mapping%i(1)) = r2(1)
rb(mapping%i(1)) = 1 - r2(1)
end subroutine sf_res_mapping_single_compute
@ %def sf_res_mapping_single_compute
@ Apply inverse.
<<SF mappings: sf resonance single mapping: TBP>>=
procedure :: inverse => sf_res_mapping_single_inverse
<<SF mappings: procedures>>=
subroutine sf_res_mapping_single_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_res_mapping_single_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(1) :: p2
real(default) :: fbw
call map_breit_wigner_inverse &
(r(mapping%i(1)), fbw, p2(1), mapping%m, mapping%w, x_free)
p = r
pb= rb
p (mapping%i(1)) = p2(1)
pb(mapping%i(1)) = 1 - p2(1)
f = fbw
end subroutine sf_res_mapping_single_inverse
@ %def sf_res_mapping_single_inverse
@
\subsection{Implementation: on-shell mapping}
This is a degenerate version of the unit-square mapping where the
product $r_1r_2$ is constant. This product is given by the rescaled
squared mass. We introduce an artificial first parameter $p_1$ to
keep the counting, but nothing depends on it. The second parameter is
the same $p_2$ as for the standard unit-square mapping for $\alpha=1$,
it parameterizes the ratio of $r_1$ and $r_2$.
<<SF mappings: public>>=
public :: sf_os_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_os_mapping_t
real(default) :: m = 0
real(default) :: lm2 = 0
contains
<<SF mappings: sf on-shell mapping: TBP>>
end type sf_os_mapping_t
@ %def sf_os_mapping_t
@ Output.
<<SF mappings: sf on-shell mapping: TBP>>=
procedure :: write => sf_os_mapping_write
<<SF mappings: procedures>>=
subroutine sf_os_mapping_write (object, unit)
class(sf_os_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
write (u, "(A,F7.5,A)") ": on-shell (", object%m, ")"
end subroutine sf_os_mapping_write
@ %def sf_os_mapping_write
@ Initialize: index pair and dimensionless mass parameter.
<<SF mappings: sf on-shell mapping: TBP>>=
procedure :: init => sf_os_mapping_init
<<SF mappings: procedures>>=
subroutine sf_os_mapping_init (mapping, m)
class(sf_os_mapping_t), intent(out) :: mapping
real(default), intent(in) :: m
call mapping%base_init (2)
mapping%m = m
mapping%lm2 = abs (2 * log (mapping%m))
end subroutine sf_os_mapping_init
@ %def sf_os_mapping_init
@ Apply mapping. The [[x_free]] parameter rescales the total energy,
which must be accounted for in the enclosed mapping.
<<SF mappings: sf on-shell mapping: TBP>>=
procedure :: compute => sf_os_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_os_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_os_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: r2, p2
integer :: j
p2 = p(mapping%i)
call map_on_shell (r2, f, p2, mapping%lm2, x_free)
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2(j)
rb(mapping%i(j)) = 1 - r2(j)
end do
end subroutine sf_os_mapping_compute
@ %def sf_os_mapping_compute
@ Apply inverse. The irrelevant parameter $p_1$ is always set zero.
<<SF mappings: sf on-shell mapping: TBP>>=
procedure :: inverse => sf_os_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_os_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_os_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: p2, r2
r2 = r(mapping%i)
call map_on_shell_inverse (r2, f, p2, mapping%lm2, x_free)
p = r
pb= rb
p (mapping%i(1)) = p2(1)
pb(mapping%i(1)) = 1 - p2(1)
p (mapping%i(2)) = p2(2)
pb(mapping%i(2)) = 1 - p2(2)
end subroutine sf_os_mapping_inverse
@ %def sf_os_mapping_inverse
@
\subsection{Implementation: on-shell single mapping}
This is a degenerate version of the unit-interval mapping where the
result $r$ is constant. The value is given by the rescaled squared
mass. The input parameter $p_1$ is actually ignored, nothing depends
on it.
<<SF mappings: public>>=
public :: sf_os_mapping_single_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_os_mapping_single_t
real(default) :: m = 0
real(default) :: lm2 = 0
contains
<<SF mappings: sf on-shell mapping single: TBP>>
end type sf_os_mapping_single_t
@ %def sf_os_mapping_single_t
@ Output.
<<SF mappings: sf on-shell mapping single: TBP>>=
procedure :: write => sf_os_mapping_single_write
<<SF mappings: procedures>>=
subroutine sf_os_mapping_single_write (object, unit)
class(sf_os_mapping_single_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,')')", advance="no") object%i
end if
write (u, "(A,F7.5,A)") ": on-shell (", object%m, ")"
end subroutine sf_os_mapping_single_write
@ %def sf_os_mapping_single_write
@ Initialize: index pair and dimensionless mass parameter.
<<SF mappings: sf on-shell mapping single: TBP>>=
procedure :: init => sf_os_mapping_single_init
<<SF mappings: procedures>>=
subroutine sf_os_mapping_single_init (mapping, m)
class(sf_os_mapping_single_t), intent(out) :: mapping
real(default), intent(in) :: m
call mapping%base_init (1)
mapping%m = m
mapping%lm2 = abs (2 * log (mapping%m))
end subroutine sf_os_mapping_single_init
@ %def sf_os_mapping_single_init
@ Apply mapping. The [[x_free]] parameter rescales the total energy,
which must be accounted for in the enclosed mapping.
<<SF mappings: sf on-shell mapping single: TBP>>=
procedure :: compute => sf_os_mapping_single_compute
<<SF mappings: procedures>>=
subroutine sf_os_mapping_single_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_os_mapping_single_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(1) :: r2, p2
integer :: j
p2 = p(mapping%i)
call map_on_shell_single (r2, f, p2, mapping%lm2, x_free)
r = p
rb= pb
r (mapping%i(1)) = r2(1)
rb(mapping%i(1)) = 1 - r2(1)
end subroutine sf_os_mapping_single_compute
@ %def sf_os_mapping_single_compute
@ Apply inverse. The irrelevant parameter $p_1$ is always set zero.
<<SF mappings: sf on-shell mapping single: TBP>>=
procedure :: inverse => sf_os_mapping_single_inverse
<<SF mappings: procedures>>=
subroutine sf_os_mapping_single_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_os_mapping_single_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(1) :: p2, r2
r2 = r(mapping%i)
call map_on_shell_single_inverse (r2, f, p2, mapping%lm2, x_free)
p = r
pb= rb
p (mapping%i(1)) = p2(1)
pb(mapping%i(1)) = 1 - p2(1)
end subroutine sf_os_mapping_single_inverse
@ %def sf_os_mapping_single_inverse
@
\subsection{Implementation: endpoint mapping}
This maps the unit square ($r_1,r_2$) such that $p_1$ is the product $r_1r_2$,
while $p_2$ is related to the ratio. Furthermore, we enhance the
region at $r_1=1$ and $r_2=1$, which translates into $p_1=1$ and
$p_2=0,1$. The enhancement is such that any power-like singularity is
caught. This is useful for beamstrahlung spectra.
In addition, we allow for a delta-function singularity in $r_1$ and/or
$r_2$. The singularity is smeared to an interval of width
$\epsilon$. If nonzero, we distinguish the kinematical momentum
fractions $r_i$ from effective values $x_i$, which should go into the
structure-function evaluation. A bin of width $\epsilon$ in $r$ is
mapped to $x=1$ exactly, while the interval $(0,1-\epsilon)$ is mapped
to $(0,1)$ in $x$. The Jacobian reflects this distinction, and the
logical [[in_peak]] allows for an unambiguous distinction.
The delta-peak fraction is used only for the integration self-test.
<<SF mappings: public>>=
public :: sf_ep_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_ep_mapping_t
real(default) :: a = 1
contains
<<SF mappings: sf endpoint mapping: TBP>>
end type sf_ep_mapping_t
@ %def sf_ep_mapping_t
@ Output.
<<SF mappings: sf endpoint mapping: TBP>>=
procedure :: write => sf_ep_mapping_write
<<SF mappings: procedures>>=
subroutine sf_ep_mapping_write (object, unit)
class(sf_ep_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
write (u, "(A,ES12.5,A)") ": endpoint (a =", object%a, ")"
end subroutine sf_ep_mapping_write
@ %def sf_ep_mapping_write
@ Initialize: no extra parameters.
<<SF mappings: sf endpoint mapping: TBP>>=
procedure :: init => sf_ep_mapping_init
<<SF mappings: procedures>>=
subroutine sf_ep_mapping_init (mapping, a)
class(sf_ep_mapping_t), intent(out) :: mapping
real(default), intent(in), optional :: a
call mapping%base_init (2)
if (present (a)) mapping%a = a
end subroutine sf_ep_mapping_init
@ %def sf_ep_mapping_init
@ Apply mapping.
<<SF mappings: sf endpoint mapping: TBP>>=
procedure :: compute => sf_ep_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_ep_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_ep_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: px, r2
real(default) :: f1, f2
integer :: j
call map_endpoint_1 (px(1), f1, p(mapping%i(1)), mapping%a)
call map_endpoint_01 (px(2), f2, p(mapping%i(2)), mapping%a)
call map_unit_square (r2, f, px)
f = f * f1 * f2
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2(j)
rb(mapping%i(j)) = 1 - r2(j)
end do
end subroutine sf_ep_mapping_compute
@ %def sf_ep_mapping_compute
@ Apply inverse.
<<SF mappings: sf endpoint mapping: TBP>>=
procedure :: inverse => sf_ep_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_ep_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_ep_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: r2, px, p2
real(default) :: f1, f2
integer :: j
do j = 1, 2
r2(j) = r(mapping%i(j))
end do
call map_unit_square_inverse (r2, f, px)
call map_endpoint_inverse_1 (px(1), f1, p2(1), mapping%a)
call map_endpoint_inverse_01 (px(2), f2, p2(2), mapping%a)
f = f * f1 * f2
p = r
pb= rb
do j = 1, 2
p (mapping%i(j)) = p2(j)
pb(mapping%i(j)) = 1 - p2(j)
end do
end subroutine sf_ep_mapping_inverse
@ %def sf_ep_mapping_inverse
@
\subsection{Implementation: endpoint mapping with resonance}
Like the endpoint mapping for $p_2$, but replace the endpoint mapping
by a Breit-Wigner mapping for $p_1$. This covers resonance production
in the presence of beamstrahlung.
If the flag [[resonance]] is unset, we skip the resonance mapping, so
the parameter $p_1$ remains equal to $r_1r_2$, as in the standard
s-channel mapping.
<<SF mappings: public>>=
public :: sf_epr_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_epr_mapping_t
real(default) :: a = 1
real(default) :: m = 0
real(default) :: w = 0
logical :: resonance = .true.
contains
<<SF mappings: sf endpoint/res mapping: TBP>>
end type sf_epr_mapping_t
@ %def sf_epr_mapping_t
@ Output.
<<SF mappings: sf endpoint/res mapping: TBP>>=
procedure :: write => sf_epr_mapping_write
<<SF mappings: procedures>>=
subroutine sf_epr_mapping_write (object, unit)
class(sf_epr_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
if (object%resonance) then
write (u, "(A,F7.5,A,F7.5,', ',F7.5,A)") ": ep/res (a = ", object%a, &
" | ", object%m, object%w, ")"
else
write (u, "(A,F7.5,A)") ": ep/nores (a = ", object%a, ")"
end if
end subroutine sf_epr_mapping_write
@ %def sf_epr_mapping_write
@ Initialize: if mass and width are not given, we initialize a
non-resonant version of the mapping.
<<SF mappings: sf endpoint/res mapping: TBP>>=
procedure :: init => sf_epr_mapping_init
<<SF mappings: procedures>>=
subroutine sf_epr_mapping_init (mapping, a, m, w)
class(sf_epr_mapping_t), intent(out) :: mapping
real(default), intent(in) :: a
real(default), intent(in), optional :: m, w
call mapping%base_init (2)
mapping%a = a
if (present (m) .and. present (w)) then
mapping%m = m
mapping%w = w
else
mapping%resonance = .false.
end if
end subroutine sf_epr_mapping_init
@ %def sf_epr_mapping_init
@ Apply mapping.
<<SF mappings: sf endpoint/res mapping: TBP>>=
procedure :: compute => sf_epr_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_epr_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_epr_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: px, r2
real(default) :: f1, f2
integer :: j
if (mapping%resonance) then
call map_breit_wigner &
(px(1), f1, p(mapping%i(1)), mapping%m, mapping%w, x_free)
else
px(1) = p(mapping%i(1))
f1 = 1
end if
call map_endpoint_01 (px(2), f2, p(mapping%i(2)), mapping%a)
call map_unit_square (r2, f, px)
f = f * f1 * f2
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2(j)
rb(mapping%i(j)) = 1 - r2(j)
end do
end subroutine sf_epr_mapping_compute
@ %def sf_epr_mapping_compute
@ Apply inverse.
<<SF mappings: sf endpoint/res mapping: TBP>>=
procedure :: inverse => sf_epr_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_epr_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_epr_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: px, p2
real(default) :: f1, f2
integer :: j
call map_unit_square_inverse (r(mapping%i), f, px)
if (mapping%resonance) then
call map_breit_wigner_inverse &
(px(1), f1, p2(1), mapping%m, mapping%w, x_free)
else
p2(1) = px(1)
f1 = 1
end if
call map_endpoint_inverse_01 (px(2), f2, p2(2), mapping%a)
f = f * f1 * f2
p = r
pb= rb
do j = 1, 2
p (mapping%i(j)) = p2(j)
pb(mapping%i(j)) = 1 - p2(j)
end do
end subroutine sf_epr_mapping_inverse
@ %def sf_epr_mapping_inverse
@
\subsection{Implementation: endpoint mapping for on-shell particle}
Analogous to the resonance mapping, but the $p_1$ input is ignored
altogether. This covers on-shell particle production
in the presence of beamstrahlung.
<<SF mappings: public>>=
public :: sf_epo_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_epo_mapping_t
real(default) :: a = 1
real(default) :: m = 0
real(default) :: lm2 = 0
contains
<<SF mappings: sf endpoint/os mapping: TBP>>
end type sf_epo_mapping_t
@ %def sf_epo_mapping_t
@ Output.
<<SF mappings: sf endpoint/os mapping: TBP>>=
procedure :: write => sf_epo_mapping_write
<<SF mappings: procedures>>=
subroutine sf_epo_mapping_write (object, unit)
class(sf_epo_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
write (u, "(A,F7.5,A,F7.5,A)") ": ep/on-shell (a = ", object%a, &
" | ", object%m, ")"
end subroutine sf_epo_mapping_write
@ %def sf_epo_mapping_write
@ Initialize: no extra parameters.
<<SF mappings: sf endpoint/os mapping: TBP>>=
procedure :: init => sf_epo_mapping_init
<<SF mappings: procedures>>=
subroutine sf_epo_mapping_init (mapping, a, m)
class(sf_epo_mapping_t), intent(out) :: mapping
real(default), intent(in) :: a, m
call mapping%base_init (2)
mapping%a = a
mapping%m = m
mapping%lm2 = abs (2 * log (mapping%m))
end subroutine sf_epo_mapping_init
@ %def sf_epo_mapping_init
@ Apply mapping.
<<SF mappings: sf endpoint/os mapping: TBP>>=
procedure :: compute => sf_epo_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_epo_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_epo_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: px, r2
real(default) :: f2
integer :: j
px(1) = 0
call map_endpoint_01 (px(2), f2, p(mapping%i(2)), mapping%a)
call map_on_shell (r2, f, px, mapping%lm2)
f = f * f2
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2(j)
rb(mapping%i(j)) = 1 - r2(j)
end do
end subroutine sf_epo_mapping_compute
@ %def sf_epo_mapping_compute
@ Apply inverse.
<<SF mappings: sf endpoint/os mapping: TBP>>=
procedure :: inverse => sf_epo_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_epo_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_epo_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: px, p2
real(default) :: f2
integer :: j
call map_on_shell_inverse (r(mapping%i), f, px, mapping%lm2)
p2(1) = 0
call map_endpoint_inverse_01 (px(2), f2, p2(2), mapping%a)
f = f * f2
p = r
pb= rb
do j = 1, 2
p (mapping%i(j)) = p2(j)
pb(mapping%i(j)) = 1 - p2(j)
end do
end subroutine sf_epo_mapping_inverse
@ %def sf_epo_mapping_inverse
@
\subsection{Implementation: ISR endpoint mapping}
Similar to the endpoint mapping above: This maps the unit square
($r_1,r_2$) such that $p_1$ is the product $r_1r_2$, while $p_2$ is
related to the ratio. Furthermore, we enhance the region at $r_1=1$
and $r_2=1$, which translates into $p_1=1$ and $p_2=0,1$.
The enhancement is such that ISR singularity $(1-x)^{-1+\epsilon}$ is
flattened. This would be easy in one dimension, but becomes
nontrivial in two dimensions.
<<SF mappings: public>>=
public :: sf_ip_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_ip_mapping_t
real(default) :: eps = 0
contains
<<SF mappings: sf power mapping: TBP>>
end type sf_ip_mapping_t
@ %def sf_ip_mapping_t
@ Output.
<<SF mappings: sf power mapping: TBP>>=
procedure :: write => sf_ip_mapping_write
<<SF mappings: procedures>>=
subroutine sf_ip_mapping_write (object, unit)
class(sf_ip_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
write (u, "(A,ES12.5,A)") ": isr (eps =", object%eps, ")"
end subroutine sf_ip_mapping_write
@ %def sf_ip_mapping_write
@ Initialize: no extra parameters.
<<SF mappings: sf power mapping: TBP>>=
procedure :: init => sf_ip_mapping_init
<<SF mappings: procedures>>=
subroutine sf_ip_mapping_init (mapping, eps)
class(sf_ip_mapping_t), intent(out) :: mapping
real(default), intent(in), optional :: eps
call mapping%base_init (2)
if (present (eps)) mapping%eps = eps
if (mapping%eps <= 0) &
call msg_fatal ("ISR mapping: regulator epsilon must not be zero")
end subroutine sf_ip_mapping_init
@ %def sf_ip_mapping_init
@ Apply mapping.
<<SF mappings: sf power mapping: TBP>>=
procedure :: compute => sf_ip_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_ip_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_ip_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: px, pxb, r2, r2b
real(default) :: f1, f2, xb, y, yb
integer :: j
call map_power_1 (xb, f1, pb(mapping%i(1)), 2 * mapping%eps)
call map_power_01 (y, yb, f2, pb(mapping%i(2)), mapping%eps)
px(1) = 1 - xb
pxb(1) = xb
px(2) = y
pxb(2) = yb
call map_unit_square_prec (r2, r2b, f, px, pxb)
f = f * f1 * f2
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2 (j)
rb(mapping%i(j)) = r2b(j)
end do
end subroutine sf_ip_mapping_compute
@ %def sf_ip_mapping_compute
@ Apply inverse.
<<SF mappings: sf power mapping: TBP>>=
procedure :: inverse => sf_ip_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_ip_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_ip_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: r2, r2b, px, pxb, p2, p2b
real(default) :: f1, f2, xb, y, yb
integer :: j
do j = 1, 2
r2 (j) = r (mapping%i(j))
r2b(j) = rb(mapping%i(j))
end do
call map_unit_square_inverse_prec (r2, r2b, f, px, pxb)
xb = pxb(1)
if (px(1) > 0) then
y = px(2)
yb = pxb(2)
else
y = 0.5_default
yb = 0.5_default
end if
call map_power_inverse_1 (xb, f1, p2b(1), 2 * mapping%eps)
call map_power_inverse_01 (y, yb, f2, p2b(2), mapping%eps)
p2 = 1 - p2b
f = f * f1 * f2
p = r
pb= rb
do j = 1, 2
p (mapping%i(j)) = p2(j)
pb(mapping%i(j)) = p2b(j)
end do
end subroutine sf_ip_mapping_inverse
@ %def sf_ip_mapping_inverse
@
\subsection{Implementation: ISR endpoint mapping, resonant}
Similar to the endpoint mapping above: This maps the unit square
($r_1,r_2$) such that $p_1$ is the product $r_1r_2$, while $p_2$ is
related to the ratio. Furthermore, we enhance the region at $r_1=1$
and $r_2=1$, which translates into $p_1=1$ and $p_2=0,1$.
The enhancement is such that ISR singularity $(1-x)^{-1+\epsilon}$ is
flattened. This would be easy in one dimension, but becomes
nontrivial in two dimensions.
The resonance can be turned off by the flag [[resonance]].
<<SF mappings: public>>=
public :: sf_ipr_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_ipr_mapping_t
real(default) :: eps = 0
real(default) :: m = 0
real(default) :: w = 0
logical :: resonance = .true.
contains
<<SF mappings: sf power/res mapping: TBP>>
end type sf_ipr_mapping_t
@ %def sf_ipr_mapping_t
@ Output.
<<SF mappings: sf power/res mapping: TBP>>=
procedure :: write => sf_ipr_mapping_write
<<SF mappings: procedures>>=
subroutine sf_ipr_mapping_write (object, unit)
class(sf_ipr_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
if (object%resonance) then
write (u, "(A,F7.5,A,F7.5,', ',F7.5,A)") ": isr/res (eps = ", &
object%eps, " | ", object%m, object%w, ")"
else
write (u, "(A,F7.5,A)") ": isr/res (eps = ", object%eps, ")"
end if
end subroutine sf_ipr_mapping_write
@ %def sf_ipr_mapping_write
@ Initialize:
<<SF mappings: sf power/res mapping: TBP>>=
procedure :: init => sf_ipr_mapping_init
<<SF mappings: procedures>>=
subroutine sf_ipr_mapping_init (mapping, eps, m, w)
class(sf_ipr_mapping_t), intent(out) :: mapping
real(default), intent(in), optional :: eps, m, w
call mapping%base_init (2)
if (present (eps)) mapping%eps = eps
if (mapping%eps <= 0) &
call msg_fatal ("ISR mapping: regulator epsilon must not be zero")
if (present (m) .and. present (w)) then
mapping%m = m
mapping%w = w
else
mapping%resonance = .false.
end if
end subroutine sf_ipr_mapping_init
@ %def sf_ipr_mapping_init
@ Apply mapping.
<<SF mappings: sf power/res mapping: TBP>>=
procedure :: compute => sf_ipr_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_ipr_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_ipr_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: px, pxb, r2, r2b
real(default) :: f1, f2, y, yb
integer :: j
if (mapping%resonance) then
call map_breit_wigner &
(px(1), f1, p(mapping%i(1)), mapping%m, mapping%w, x_free)
else
px(1) = p(mapping%i(1))
f1 = 1
end if
call map_power_01 (y, yb, f2, pb(mapping%i(2)), mapping%eps)
pxb(1) = 1 - px(1)
px(2) = y
pxb(2) = yb
call map_unit_square_prec (r2, r2b, f, px, pxb)
f = f * f1 * f2
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2 (j)
rb(mapping%i(j)) = r2b(j)
end do
end subroutine sf_ipr_mapping_compute
@ %def sf_ipr_mapping_compute
@ Apply inverse.
<<SF mappings: sf power/res mapping: TBP>>=
procedure :: inverse => sf_ipr_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_ipr_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_ipr_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: r2, r2b, px, pxb, p2, p2b
real(default) :: f1, f2, y, yb
integer :: j
do j = 1, 2
r2 (j) = r (mapping%i(j))
r2b(j) = rb(mapping%i(j))
end do
call map_unit_square_inverse_prec (r2, r2b, f, px, pxb)
if (px(1) > 0) then
y = px(2)
yb = pxb(2)
else
y = 0.5_default
yb = 0.5_default
end if
if (mapping%resonance) then
call map_breit_wigner_inverse &
(px(1), f1, p2(1), mapping%m, mapping%w, x_free)
else
p2(1) = px(1)
f1 = 1
end if
call map_power_inverse_01 (y, yb, f2, p2b(2), mapping%eps)
p2b(1) = 1 - p2(1)
p2 (2) = 1 - p2b(2)
f = f * f1 * f2
p = r
pb= rb
do j = 1, 2
p (mapping%i(j)) = p2(j)
pb(mapping%i(j)) = p2b(j)
end do
end subroutine sf_ipr_mapping_inverse
@ %def sf_ipr_mapping_inverse
@
\subsection{Implementation: ISR on-shell mapping}
Similar to the endpoint mapping above: This maps the unit square
($r_1,r_2$) such that $p_1$ is ignored while the product $r_1r_2$ is
constant. $p_2$ is related to the ratio. Furthermore, we enhance the
region at $r_1=1$ and $r_2=1$, which translates into $p_1=1$ and
$p_2=0,1$.
The enhancement is such that ISR singularity $(1-x)^{-1+\epsilon}$ is
flattened. This would be easy in one dimension, but becomes
nontrivial in two dimensions.
<<SF mappings: public>>=
public :: sf_ipo_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_ipo_mapping_t
real(default) :: eps = 0
real(default) :: m = 0
contains
<<SF mappings: sf power/os mapping: TBP>>
end type sf_ipo_mapping_t
@ %def sf_ipo_mapping_t
@ Output.
<<SF mappings: sf power/os mapping: TBP>>=
procedure :: write => sf_ipo_mapping_write
<<SF mappings: procedures>>=
subroutine sf_ipo_mapping_write (object, unit)
class(sf_ipo_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,',',I0,')')", advance="no") object%i
end if
write (u, "(A,F7.5,A,F7.5,A)") ": isr/os (eps = ", object%eps, &
" | ", object%m, ")"
end subroutine sf_ipo_mapping_write
@ %def sf_ipo_mapping_write
@ Initialize: no extra parameters.
<<SF mappings: sf power/os mapping: TBP>>=
procedure :: init => sf_ipo_mapping_init
<<SF mappings: procedures>>=
subroutine sf_ipo_mapping_init (mapping, eps, m)
class(sf_ipo_mapping_t), intent(out) :: mapping
real(default), intent(in), optional :: eps, m
call mapping%base_init (2)
if (present (eps)) mapping%eps = eps
if (mapping%eps <= 0) &
call msg_fatal ("ISR mapping: regulator epsilon must not be zero")
mapping%m = m
end subroutine sf_ipo_mapping_init
@ %def sf_ipo_mapping_init
@ Apply mapping.
<<SF mappings: sf power/os mapping: TBP>>=
procedure :: compute => sf_ipo_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_ipo_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_ipo_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: px, pxb, r2, r2b
real(default) :: f1, f2, y, yb
integer :: j
call map_power_01 (y, yb, f2, pb(mapping%i(2)), mapping%eps)
px(1) = mapping%m ** 2
if (present (x_free)) px(1) = px(1) / x_free
pxb(1) = 1 - px(1)
px(2) = y
pxb(2) = yb
call map_unit_square_prec (r2, r2b, f1, px, pxb)
f = f1 * f2
r = p
rb= pb
do j = 1, 2
r (mapping%i(j)) = r2 (j)
rb(mapping%i(j)) = r2b(j)
end do
end subroutine sf_ipo_mapping_compute
@ %def sf_ipo_mapping_compute
@ Apply inverse.
<<SF mappings: sf power/os mapping: TBP>>=
procedure :: inverse => sf_ipo_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_ipo_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_ipo_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: r2, r2b, px, pxb, p2, p2b
real(default) :: f1, f2, y, yb
integer :: j
do j = 1, 2
r2 (j) = r (mapping%i(j))
r2b(j) = rb(mapping%i(j))
end do
call map_unit_square_inverse_prec (r2, r2b, f1, px, pxb)
y = px(2)
yb = pxb(2)
call map_power_inverse_01 (y, yb, f2, p2b(2), mapping%eps)
p2(1) = 0
p2b(1)= 1
p2(2) = 1 - p2b(2)
f = f1 * f2
p = r
pb= rb
do j = 1, 2
p (mapping%i(j)) = p2(j)
pb(mapping%i(j)) = p2b(j)
end do
end subroutine sf_ipo_mapping_inverse
@ %def sf_ipo_mapping_inverse
@
\subsection{Implementation: Endpoint + ISR power mapping}
This is a combination of endpoint (i.e., beamstrahlung) and ISR power
mapping. The first two parameters apply to the beamstrahlung
spectrum, the last two to the ISR function for the first and second
beam, respectively.
<<SF mappings: public>>=
public :: sf_ei_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_ei_mapping_t
type(sf_ep_mapping_t) :: ep
type(sf_ip_mapping_t) :: ip
contains
<<SF mappings: sf ep-ip mapping: TBP>>
end type sf_ei_mapping_t
@ %def sf_ei_mapping_t
@ Output.
<<SF mappings: sf ep-ip mapping: TBP>>=
procedure :: write => sf_ei_mapping_write
<<SF mappings: procedures>>=
subroutine sf_ei_mapping_write (object, unit)
class(sf_ei_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,3(',',I0),')')", advance="no") object%i
end if
write (u, "(A,ES12.5,A,ES12.5,A)") ": ep/isr (a =", object%ep%a, &
", eps =", object%ip%eps, ")"
end subroutine sf_ei_mapping_write
@ %def sf_ei_mapping_write
@ Initialize: no extra parameters.
<<SF mappings: sf ep-ip mapping: TBP>>=
procedure :: init => sf_ei_mapping_init
<<SF mappings: procedures>>=
subroutine sf_ei_mapping_init (mapping, a, eps)
class(sf_ei_mapping_t), intent(out) :: mapping
real(default), intent(in), optional :: a, eps
call mapping%base_init (4)
call mapping%ep%init (a)
call mapping%ip%init (eps)
end subroutine sf_ei_mapping_init
@ %def sf_ei_mapping_init
@ Set an index value. We should communicate the appropriate indices to the
enclosed sub-mappings, therefore override the method.
<<SF mappings: sf ep-ip mapping: TBP>>=
procedure :: set_index => sf_ei_mapping_set_index
<<SF mappings: procedures>>=
subroutine sf_ei_mapping_set_index (mapping, j, i)
class(sf_ei_mapping_t), intent(inout) :: mapping
integer, intent(in) :: j, i
mapping%i(j) = i
select case (j)
case (1:2); call mapping%ep%set_index (j, i)
case (3:4); call mapping%ip%set_index (j-2, i)
end select
end subroutine sf_ei_mapping_set_index
@ %def sf_mapping_set_index
@ Apply mapping. Now, the beamstrahlung and ISR mappings are
independent of each other. The parameter subsets that are actually
used should not overlap. The Jacobians are multiplied.
<<SF mappings: sf ep-ip mapping: TBP>>=
procedure :: compute => sf_ei_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_ei_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_ei_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(size(p)) :: q, qb
real(default) :: f1, f2
call mapping%ep%compute (q, qb, f1, p, pb, x_free)
call mapping%ip%compute (r, rb, f2, q, qb, x_free)
f = f1 * f2
end subroutine sf_ei_mapping_compute
@ %def sf_ei_mapping_compute
@ Apply inverse.
<<SF mappings: sf ep-ip mapping: TBP>>=
procedure :: inverse => sf_ei_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_ei_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_ei_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(size(p)) :: q, qb
real(default) :: f1, f2
call mapping%ip%inverse (r, rb, f2, q, qb, x_free)
call mapping%ep%inverse (q, qb, f1, p, pb, x_free)
f = f1 * f2
end subroutine sf_ei_mapping_inverse
@ %def sf_ei_mapping_inverse
@
\subsection{Implementation: Endpoint + ISR + resonance}
This is a combination of endpoint (i.e., beamstrahlung) and ISR power
mapping, adapted for an s-channel resonance. The first two internal
parameters apply to the beamstrahlung spectrum, the last two to the
ISR function for the first and second beam, respectively. The first
and third parameters are the result of an overall resonance mapping,
so on the outside, the first parameter is the total momentum fraction,
the third one describes the distribution between beamstrahlung and ISR.
<<SF mappings: public>>=
public :: sf_eir_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_eir_mapping_t
type(sf_res_mapping_t) :: res
type(sf_epr_mapping_t) :: ep
type(sf_ipr_mapping_t) :: ip
contains
<<SF mappings: sf ep-ip-res mapping: TBP>>
end type sf_eir_mapping_t
@ %def sf_eir_mapping_t
@ Output.
<<SF mappings: sf ep-ip-res mapping: TBP>>=
procedure :: write => sf_eir_mapping_write
<<SF mappings: procedures>>=
subroutine sf_eir_mapping_write (object, unit)
class(sf_eir_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,3(',',I0),')')", advance="no") object%i
end if
write (u, "(A,F7.5,A,F7.5,A,F7.5,', ',F7.5,A)") &
": ep/isr/res (a =", object%ep%a, &
", eps =", object%ip%eps, " | ", object%res%m, object%res%w, ")"
end subroutine sf_eir_mapping_write
@ %def sf_eir_mapping_write
@ Initialize: no extra parameters.
<<SF mappings: sf ep-ip-res mapping: TBP>>=
procedure :: init => sf_eir_mapping_init
<<SF mappings: procedures>>=
subroutine sf_eir_mapping_init (mapping, a, eps, m, w)
class(sf_eir_mapping_t), intent(out) :: mapping
real(default), intent(in) :: a, eps, m, w
call mapping%base_init (4)
call mapping%res%init (m, w)
call mapping%ep%init (a)
call mapping%ip%init (eps)
end subroutine sf_eir_mapping_init
@ %def sf_eir_mapping_init
@ Set an index value. We should communicate the appropriate indices to the
enclosed sub-mappings, therefore override the method.
<<SF mappings: sf ep-ip-res mapping: TBP>>=
procedure :: set_index => sf_eir_mapping_set_index
<<SF mappings: procedures>>=
subroutine sf_eir_mapping_set_index (mapping, j, i)
class(sf_eir_mapping_t), intent(inout) :: mapping
integer, intent(in) :: j, i
mapping%i(j) = i
select case (j)
case (1); call mapping%res%set_index (1, i)
case (3); call mapping%res%set_index (2, i)
end select
select case (j)
case (1:2); call mapping%ep%set_index (j, i)
case (3:4); call mapping%ip%set_index (j-2, i)
end select
end subroutine sf_eir_mapping_set_index
@ %def sf_mapping_set_index
@ Apply mapping. Now, the beamstrahlung and ISR mappings are
independent of each other. The parameter subsets that are actually
used should not overlap. The Jacobians are multiplied.
<<SF mappings: sf ep-ip-res mapping: TBP>>=
procedure :: compute => sf_eir_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_eir_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_eir_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(size(p)) :: px, pxb, q, qb
real(default) :: f0, f1, f2
call mapping%res%compute (px, pxb, f0, p, pb, x_free)
call mapping%ep%compute (q, qb, f1, px, pxb, x_free)
call mapping%ip%compute (r, rb, f2, q, qb, x_free)
f = f0 * f1 * f2
end subroutine sf_eir_mapping_compute
@ %def sf_eir_mapping_compute
@ Apply inverse.
<<SF mappings: sf ep-ip-res mapping: TBP>>=
procedure :: inverse => sf_eir_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_eir_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_eir_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(size(p)) :: px, pxb, q, qb
real(default) :: f0, f1, f2
call mapping%ip%inverse (r, rb, f2, q, qb, x_free)
call mapping%ep%inverse (q, qb, f1, px, pxb, x_free)
call mapping%res%inverse (px, pxb, f0, p, pb, x_free)
f = f0 * f1 * f2
end subroutine sf_eir_mapping_inverse
@ %def sf_eir_mapping_inverse
@
\subsection{Implementation: Endpoint + ISR power mapping, on-shell}
This is a combination of endpoint (i.e., beamstrahlung) and ISR power
mapping. The first two parameters apply to the beamstrahlung
spectrum, the last two to the ISR function for the first and second
beam, respectively. On top of that, we map the first and third parameter
such that the product is constant. From the outside, the first
parameter is irrelevant while the third parameter describes the
distribution of energy (loss) among beamstrahlung and ISR.
<<SF mappings: public>>=
public :: sf_eio_mapping_t
<<SF mappings: types>>=
type, extends (sf_mapping_t) :: sf_eio_mapping_t
type(sf_os_mapping_t) :: os
type(sf_epr_mapping_t) :: ep
type(sf_ipr_mapping_t) :: ip
contains
<<SF mappings: sf ep-ip-os mapping: TBP>>
end type sf_eio_mapping_t
@ %def sf_eio_mapping_t
@ Output.
<<SF mappings: sf ep-ip-os mapping: TBP>>=
procedure :: write => sf_eio_mapping_write
<<SF mappings: procedures>>=
subroutine sf_eio_mapping_write (object, unit)
class(sf_eio_mapping_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "map"
if (any (object%i /= 0)) then
write (u, "('(',I0,3(',',I0),')')", advance="no") object%i
end if
write (u, "(A,F7.5,A,F7.5,A,F7.5,A)") ": ep/isr/os (a =", object%ep%a, &
", eps =", object%ip%eps, " | ", object%os%m, ")"
end subroutine sf_eio_mapping_write
@ %def sf_eio_mapping_write
@ Initialize: no extra parameters.
<<SF mappings: sf ep-ip-os mapping: TBP>>=
procedure :: init => sf_eio_mapping_init
<<SF mappings: procedures>>=
subroutine sf_eio_mapping_init (mapping, a, eps, m)
class(sf_eio_mapping_t), intent(out) :: mapping
real(default), intent(in), optional :: a, eps, m
call mapping%base_init (4)
call mapping%os%init (m)
call mapping%ep%init (a)
call mapping%ip%init (eps)
end subroutine sf_eio_mapping_init
@ %def sf_eio_mapping_init
@ Set an index value. We should communicate the appropriate indices to the
enclosed sub-mappings, therefore override the method.
<<SF mappings: sf ep-ip-os mapping: TBP>>=
procedure :: set_index => sf_eio_mapping_set_index
<<SF mappings: procedures>>=
subroutine sf_eio_mapping_set_index (mapping, j, i)
class(sf_eio_mapping_t), intent(inout) :: mapping
integer, intent(in) :: j, i
mapping%i(j) = i
select case (j)
case (1); call mapping%os%set_index (1, i)
case (3); call mapping%os%set_index (2, i)
end select
select case (j)
case (1:2); call mapping%ep%set_index (j, i)
case (3:4); call mapping%ip%set_index (j-2, i)
end select
end subroutine sf_eio_mapping_set_index
@ %def sf_mapping_set_index
@ Apply mapping. Now, the beamstrahlung and ISR mappings are
independent of each other. The parameter subsets that are actually
used should not overlap. The Jacobians are multiplied.
<<SF mappings: sf ep-ip-os mapping: TBP>>=
procedure :: compute => sf_eio_mapping_compute
<<SF mappings: procedures>>=
subroutine sf_eio_mapping_compute (mapping, r, rb, f, p, pb, x_free)
class(sf_eio_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(size(p)) :: px, pxb, q, qb
real(default) :: f0, f1, f2
call mapping%os%compute (px, pxb, f0, p, pb, x_free)
call mapping%ep%compute (q, qb, f1, px, pxb, x_free)
call mapping%ip%compute (r, rb, f2, q, qb, x_free)
f = f0 * f1 * f2
end subroutine sf_eio_mapping_compute
@ %def sf_eio_mapping_compute
@ Apply inverse.
<<SF mappings: sf ep-ip-os mapping: TBP>>=
procedure :: inverse => sf_eio_mapping_inverse
<<SF mappings: procedures>>=
subroutine sf_eio_mapping_inverse (mapping, r, rb, f, p, pb, x_free)
class(sf_eio_mapping_t), intent(inout) :: mapping
real(default), dimension(:), intent(in) :: r, rb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: p, pb
real(default), intent(inout), optional :: x_free
real(default), dimension(size(p)) :: px, pxb, q, qb
real(default) :: f0, f1, f2
call mapping%ip%inverse (r, rb, f2, q, qb, x_free)
call mapping%ep%inverse (q, qb, f1, px, pxb, x_free)
call mapping%os%inverse (px, pxb, f0, p, pb, x_free)
f = f0 * f1 * f2
end subroutine sf_eio_mapping_inverse
@ %def sf_eio_mapping_inverse
@
\subsection{Basic formulas}
\subsubsection{Standard mapping of the unit square}
This mapping of the unit square is appropriate in particular for
structure functions which are concentrated at the lower end. Instead
of a rectangular grid, one set of grid lines corresponds to constant
parton c.m. energy. The other set is chosen such that the jacobian is
only mildly singular ($\ln x$ which is zero at $x=1$), corresponding
to an initial concentration of sampling points at the maximum energy.
If [[power]] is greater than one (the default), points are also
concentrated at the lower end.
The formula is ([[power]]=$\alpha$):
\begin{align}
r_1 &= (p_1 ^ {p_2})^\alpha \\
r_2 &= (p_1 ^ {1 - p_2})^\alpha\\
f &= \alpha^2 p_1 ^ {\alpha - 1} |\log p_1|
\end{align}
and for the default case $\alpha=1$:
\begin{align}
r_1 &= p_1 ^ {p_2} \\
r_2 &= p_1 ^ {1 - p_2} \\
f &= |\log p_1|
\end{align}
<<SF mappings: procedures>>=
subroutine map_unit_square (r, factor, p, power)
real(default), dimension(2), intent(out) :: r
real(default), intent(out) :: factor
real(default), dimension(2), intent(in) :: p
real(default), intent(in), optional :: power
real(default) :: xx, yy
factor = 1
xx = p(1)
yy = p(2)
if (present(power)) then
if (p(1) > 0 .and. power > 1) then
xx = p(1)**power
factor = factor * power * xx / p(1)
end if
end if
if (.not. vanishes (xx)) then
r(1) = xx ** yy
r(2) = xx / r(1)
factor = factor * abs (log (xx))
else
r = 0
end if
end subroutine map_unit_square
@ %def map_unit_square
@ This is the inverse mapping.
<<SF mappings: procedures>>=
subroutine map_unit_square_inverse (r, factor, p, power)
real(kind=default), dimension(2), intent(in) :: r
real(kind=default), intent(out) :: factor
real(kind=default), dimension(2), intent(out) :: p
real(kind=default), intent(in), optional :: power
real(kind=default) :: lg, xx, yy
factor = 1
xx = r(1) * r(2)
if (.not. vanishes (xx)) then
lg = log (xx)
if (.not. vanishes (lg)) then
yy = log (r(1)) / lg
else
yy = 0
end if
p(2) = yy
factor = factor * abs (lg)
if (present(power)) then
p(1) = xx**(1._default/power)
factor = factor * power * xx / p(1)
else
p(1) = xx
end if
else
p = 0
end if
end subroutine map_unit_square_inverse
@ %def map_unit_square_inverse
@
\subsubsection{Precise mapping of the unit square}
A more precise version (with unit power parameter). This version
should be numerically stable near $x=1$ and $y=0,1$. The formulas are again
\begin{equation}
r_1 = p_1^{p_2}, \qquad
r_2 = p_1^{\bar p_2}, \qquad
f = - \log p_1
\end{equation}
but we compute both $r_i$ and $\bar r_i$ simultaneously and make
direct use of both $p_i$ and $\bar p_i$ as appropriate.
<<SF mappings: procedures>>=
subroutine map_unit_square_prec (r, rb, factor, p, pb)
real(default), dimension(2), intent(out) :: r
real(default), dimension(2), intent(out) :: rb
real(default), intent(out) :: factor
real(default), dimension(2), intent(in) :: p
real(default), dimension(2), intent(in) :: pb
if (p(1) > 0.5_default) then
call compute_prec_xy_1 (r(1), rb(1), p(1), pb(1), p (2))
call compute_prec_xy_1 (r(2), rb(2), p(1), pb(1), pb(2))
factor = - log_prec (p(1), pb(1))
else if (.not. vanishes (p(1))) then
call compute_prec_xy_0 (r(1), rb(1), p(1), pb(1), p (2))
call compute_prec_xy_0 (r(2), rb(2), p(1), pb(1), pb(2))
factor = - log_prec (p(1), pb(1))
else
r = 0
rb = 1
factor = 0
end if
end subroutine map_unit_square_prec
@ %def map_unit_square_prec
@ This is the inverse mapping.
<<SF mappings: procedures>>=
subroutine map_unit_square_inverse_prec (r, rb, factor, p, pb)
real(default), dimension(2), intent(in) :: r
real(default), dimension(2), intent(in) :: rb
real(default), intent(out) :: factor
real(default), dimension(2), intent(out) :: p
real(default), dimension(2), intent(out) :: pb
call inverse_prec_x (r, rb, p(1), pb(1))
if (all (r > 0)) then
if (rb(1) < rb(2)) then
call inverse_prec_y (r, rb, p(2), pb(2))
else
call inverse_prec_y ([r(2),r(1)], [rb(2),rb(1)], pb(2), p(2))
end if
factor = - log_prec (p(1), pb(1))
else
p(1) = 0
pb(1) = 1
p(2) = 0.5_default
pb(2) = 0.5_default
factor = 0
end if
end subroutine map_unit_square_inverse_prec
@ %def map_unit_square_prec_inverse
@ This is an auxiliary function: evaluate the expression $\bar z = 1 -
x^y$ in a numerically stable way. Instabilities occur for $y=0$ and
$x=1$. The idea is to replace the bracket by the first terms of its
Taylor expansion around $x=1$ (read $\bar x\equiv 1 -x$)
\begin{equation}
1 - x^y = y\bar x\left(1 + \frac12(1-y)\bar x +
\frac16(2-y)(1-y)\bar x^2\right)
\end{equation}
whenever this is the better approximation. Actually, the relative
numerical error of the exact formula is about $\eta/(y\bar x)$ where
$\eta$ is given by [[epsilon(KIND)]] in Fortran. The relative error
of the approximation is better than the last included term divided by
$(y\bar x)$.
The first subroutine computes $z$ and $\bar z$ near $x=1$ where $\log
x$ should be expanded, the second one near $x=0$ where $\log x$ can be
kept.
<<SF mappings: procedures>>=
subroutine compute_prec_xy_1 (z, zb, x, xb, y)
real(default), intent(out) :: z, zb
real(default), intent(in) :: x, xb, y
real(default) :: a1, a2, a3
a1 = y * xb
a2 = a1 * (1 - y) * xb / 2
a3 = a2 * (2 - y) * xb / 3
if (abs (a3) < epsilon (a3)) then
zb = a1 + a2 + a3
z = 1 - zb
else
z = x ** y
zb = 1 - z
end if
end subroutine compute_prec_xy_1
subroutine compute_prec_xy_0 (z, zb, x, xb, y)
real(default), intent(out) :: z, zb
real(default), intent(in) :: x, xb, y
real(default) :: a1, a2, a3, lx
lx = -log (x)
a1 = y * lx
a2 = a1 * y * lx / 2
a3 = a2 * y * lx / 3
if (abs (a3) < epsilon (a3)) then
zb = a1 + a2 + a3
z = 1 - zb
else
z = x ** y
zb = 1 - z
end if
end subroutine compute_prec_xy_0
@ %def compute_prec_xy_1
@ %def compute_prec_xy_0
@ For the inverse calculation, we evaluate $x=r_1r_2$ in a stable way.
Since it is just a polynomial, the expansion near $x=1$ is
analytically exact, and we don't need to choose based on precision.
<<SF mappings: procedures>>=
subroutine inverse_prec_x (r, rb, x, xb)
real(default), dimension(2), intent(in) :: r, rb
real(default), intent(out) :: x, xb
real(default) :: a0, a1
a0 = rb(1) + rb(2)
a1 = rb(1) * rb(2)
if (a0 > 0.5_default) then
xb = a0 - a1
x = 1 - xb
else
x = r(1) * r(2)
xb = 1 - x
end if
end subroutine inverse_prec_x
@ %def inverse_prec_x
@ The inverse calculation for the relative momentum fraction
\begin{equation}
y = \frac{1}{1 + \frac{\log{r_2}}{\log{r_1}}}
\end{equation}
is slightly more complicated. We should take the precise form of the
logarithm, so we are safe near $r_i=1$. A series expansion is
required if $r_1\ll r_2$, since then $y$ becomes small. (We assume
$r_1<r_2$ here; for the opposite case, the arguments can be
exchanged.)
<<SF mappings: procedures>>=
subroutine inverse_prec_y (r, rb, y, yb)
real(default), dimension(2), intent(in) :: r, rb
real(default), intent(out) :: y, yb
real(default) :: log1, log2, a1, a2, a3
log1 = log_prec (r(1), rb(1))
log2 = log_prec (r(2), rb(2))
if (abs (log2**3) < epsilon (one)) then
if (abs(log1) < epsilon (one)) then
y = zero
else
y = one / (one + log2 / log1)
end if
if (abs(log2) < epsilon (one)) then
yb = zero
else
yb = one / (one + log1 / log2)
end if
return
end if
a1 = - rb(1) / log2
a2 = - rb(1) ** 2 * (one / log2**2 + one / (2 * log2))
a3 = - rb(1) ** 3 * (one / log2**3 + one / log2**2 + one/(3 * log2))
if (abs (a3) < epsilon (a3)) then
y = a1 + a2 + a3
yb = one - y
else
y = one / (one + log2 / log1)
yb = one / (one + log1 / log2)
end if
end subroutine inverse_prec_y
@ %def inverse_prec_y
@
\subsubsection{Mapping for on-shell s-channel}
The limiting case, if the product $r_1r_2$ is fixed for on-shell
production. The parameter $p_1$ is ignored. In the inverse mapping,
it is returned zero.
The parameter [[x_free]], if present, rescales the total energy. If
it is less than one, the rescaled mass parameter $m^2$ should be increased
accordingly.
Public for access in unit test.
<<SF mappings: public>>=
public :: map_on_shell
public :: map_on_shell_inverse
<<SF mappings: procedures>>=
subroutine map_on_shell (r, factor, p, lm2, x_free)
real(default), dimension(2), intent(out) :: r
real(default), intent(out) :: factor
real(default), dimension(2), intent(in) :: p
real(default), intent(in) :: lm2
real(default), intent(in), optional :: x_free
real(default) :: lx
lx = lm2; if (present (x_free)) lx = lx + log (x_free)
r(1) = exp (- p(2) * lx)
r(2) = exp (- (1 - p(2)) * lx)
factor = lx
end subroutine map_on_shell
subroutine map_on_shell_inverse (r, factor, p, lm2, x_free)
real(default), dimension(2), intent(in) :: r
real(default), intent(out) :: factor
real(default), dimension(2), intent(out) :: p
real(default), intent(in) :: lm2
real(default), intent(in), optional :: x_free
real(default) :: lx
lx = lm2; if (present (x_free)) lx = lx + log (x_free)
p(1) = 0
p(2) = abs (log (r(1))) / lx
factor = lx
end subroutine map_on_shell_inverse
@ %def map_on_shell
@ %def map_on_shell_inverse
@
\subsubsection{Mapping for on-shell s-channel, single parameter}
This is a pseudo-mapping which applies if there is actually just one
parameter [[p]]. The output parameter [[r]] is fixed for on-shell
production. The lone parameter $p_1$ is ignored. In the inverse mapping,
it is returned zero.
The parameter [[x_free]], if present, rescales the total energy. If
it is less than one, the rescaled mass parameter $m^2$ should be increased
accordingly.
Public for access in unit test.
<<SF mappings: public>>=
public :: map_on_shell_single
public :: map_on_shell_single_inverse
<<SF mappings: procedures>>=
subroutine map_on_shell_single (r, factor, p, lm2, x_free)
real(default), dimension(1), intent(out) :: r
real(default), intent(out) :: factor
real(default), dimension(1), intent(in) :: p
real(default), intent(in) :: lm2
real(default), intent(in), optional :: x_free
real(default) :: lx
lx = lm2; if (present (x_free)) lx = lx + log (x_free)
r(1) = exp (- lx)
factor = 1
end subroutine map_on_shell_single
subroutine map_on_shell_single_inverse (r, factor, p, lm2, x_free)
real(default), dimension(1), intent(in) :: r
real(default), intent(out) :: factor
real(default), dimension(1), intent(out) :: p
real(default), intent(in) :: lm2
real(default), intent(in), optional :: x_free
real(default) :: lx
lx = lm2; if (present (x_free)) lx = lx + log (x_free)
p(1) = 0
factor = 1
end subroutine map_on_shell_single_inverse
@ %def map_on_shell_single
@ %def map_on_shell_single_inverse
@
\subsubsection{Mapping for a Breit-Wigner resonance}
This is the standard Breit-Wigner mapping. We apply it to a single
variable, independently of or in addition to a unit-square mapping. We
assume here that the limits for the variable are 0 and 1, and that the
mass $m$ and width $w$ are rescaled appropriately, so they are
dimensionless and usually between 0 and 1.
If [[x_free]] is set, it rescales the total energy and thus mass and
width, since these are defined with respect to the total energy.
<<SF mappings: procedures>>=
subroutine map_breit_wigner (r, factor, p, m, w, x_free)
real(default), intent(out) :: r
real(default), intent(out) :: factor
real(default), intent(in) :: p
real(default), intent(in) :: m
real(default), intent(in) :: w
real(default), intent(in), optional :: x_free
real(default) :: m2, mw, a1, a2, a3, z, tmp
m2 = m ** 2
mw = m * w
if (present (x_free)) then
m2 = m2 / x_free
mw = mw / x_free
end if
a1 = atan (- m2 / mw)
a2 = atan ((1 - m2) / mw)
a3 = (a2 - a1) * mw
z = (1-p) * a1 + p * a2
if (-pi/2 < z .and. z < pi/2) then
tmp = tan (z)
r = max (m2 + mw * tmp, 0._default)
factor = a3 * (1 + tmp ** 2)
else
r = 0
factor = 0
end if
end subroutine map_breit_wigner
subroutine map_breit_wigner_inverse (r, factor, p, m, w, x_free)
real(default), intent(in) :: r
real(default), intent(out) :: factor
real(default), intent(out) :: p
real(default), intent(in) :: m
real(default), intent(in) :: w
real(default) :: m2, mw, a1, a2, a3, tmp
real(default), intent(in), optional :: x_free
m2 = m ** 2
mw = m * w
if (present (x_free)) then
m2 = m2 / x_free
mw = mw / x_free
end if
a1 = atan (- m2 / mw)
a2 = atan ((1 - m2) / mw)
a3 = (a2 - a1) * mw
tmp = (r - m2) / mw
p = (atan (tmp) - a1) / (a2 - a1)
factor = a3 * (1 + tmp ** 2)
end subroutine map_breit_wigner_inverse
@ %def map_breit_wigner
@ %def map_breit_wigner_inverse
@
\subsubsection{Mapping with endpoint enhancement}
This is a mapping which is close to the unit mapping, except that at
the endpoint(s), the output values are exponentially enhanced.
\begin{equation}
y = \tanh (a \tan (\frac{\pi}{2}x))
\end{equation}
We have two variants: one covers endpoints at $0$ and $1$
symmetrically, while the other one (which essentially maps one-half of
the range), covers only the endpoint at $1$.
<<SF mappings: procedures>>=
subroutine map_endpoint_1 (x3, factor, x1, a)
real(default), intent(out) :: x3, factor
real(default), intent(in) :: x1
real(default), intent(in) :: a
real(default) :: x2
if (abs (x1) < 1) then
x2 = tan (x1 * pi / 2)
x3 = tanh (a * x2)
factor = a * pi/2 * (1 + x2 ** 2) * (1 - x3 ** 2)
else
x3 = x1
factor = 0
end if
end subroutine map_endpoint_1
subroutine map_endpoint_inverse_1 (x3, factor, x1, a)
real(default), intent(in) :: x3
real(default), intent(out) :: x1, factor
real(default), intent(in) :: a
real(default) :: x2
if (abs (x3) < 1) then
x2 = atanh (x3) / a
x1 = 2 / pi * atan (x2)
factor = a * pi/2 * (1 + x2 ** 2) * (1 - x3 ** 2)
else
x1 = x3
factor = 0
end if
end subroutine map_endpoint_inverse_1
subroutine map_endpoint_01 (x4, factor, x0, a)
real(default), intent(out) :: x4, factor
real(default), intent(in) :: x0
real(default), intent(in) :: a
real(default) :: x1, x3
x1 = 2 * x0 - 1
call map_endpoint_1 (x3, factor, x1, a)
x4 = (x3 + 1) / 2
end subroutine map_endpoint_01
subroutine map_endpoint_inverse_01 (x4, factor, x0, a)
real(default), intent(in) :: x4
real(default), intent(out) :: x0, factor
real(default), intent(in) :: a
real(default) :: x1, x3
x3 = 2 * x4 - 1
call map_endpoint_inverse_1 (x3, factor, x1, a)
x0 = (x1 + 1) / 2
end subroutine map_endpoint_inverse_01
@ %def map_endpoint_1
@ %def map_endpoint_inverse_1
@ %def map_endpoint_01
@ %def map_endpoint_inverse_01
@
\subsubsection{Mapping with endpoint enhancement (ISR)}
This is another endpoint mapping. It is designed to flatten the ISR
singularity which is of power type at $x=1$, i.e., if
\begin{equation}
\sigma = \int_0^1 dx\,f(x)\,G(x)
= \int_0^1 dx\,\epsilon(1-x)^{-1+\epsilon} G(x),
\end{equation}
we replace this by
\begin{equation}
r = x^\epsilon \quad\Longrightarrow\quad
\sigma = \int_0^1 dr\,G(1- (1-r)^{1/\epsilon}).
\end{equation}
We expect that $\epsilon$ is small.
The actual mapping is $r\to x$ (so $x$ emerges closer to $1$). The
Jacobian that we return is thus $1/f(x)$. We compute the mapping in
terms of $\bar x\equiv 1 - x$, so we can achieve the required precision.
Because some compilers show quite wild numeric fluctuations, we
internally convert numeric types to explicit [[double]] precision.
<<SF mappings: public>>=
public :: map_power_1
public :: map_power_inverse_1
<<SF mappings: procedures>>=
subroutine map_power_1 (xb, factor, rb, eps)
real(default), intent(out) :: xb, factor
real(default), intent(in) :: rb
real(double) :: rb_db, factor_db, eps_db, xb_db
real(default), intent(in) :: eps
rb_db = real (rb, kind=double)
eps_db = real (eps, kind=double)
xb_db = rb_db ** (1 / eps_db)
if (rb_db > 0) then
factor_db = xb_db / rb_db / eps_db
factor = real (factor_db, kind=default)
else
factor = 0
end if
xb = real (xb_db, kind=default)
end subroutine map_power_1
subroutine map_power_inverse_1 (xb, factor, rb, eps)
real(default), intent(in) :: xb
real(default), intent(out) :: rb, factor
real(double) :: xb_db, factor_db, eps_db, rb_db
real(default), intent(in) :: eps
xb_db = real (xb, kind=double)
eps_db = real (eps, kind=double)
rb_db = xb_db ** eps_db
if (xb_db > 0) then
factor_db = xb_db / rb_db / eps_db
factor = real (factor_db, kind=default)
else
factor = 0
end if
rb = real (rb_db, kind=default)
end subroutine map_power_inverse_1
@ %def map_power_1
@ %def map_power_inverse_1
@ Here we apply a power mapping to both endpoints. We divide the
interval in two equal halves and apply the power mapping for the
nearest endpoint, either $0$ or $1$.
<<SF mappings: procedures>>=
subroutine map_power_01 (y, yb, factor, r, eps)
real(default), intent(out) :: y, yb, factor
real(default), intent(in) :: r
real(default), intent(in) :: eps
real(default) :: u, ub, zp, zm
u = 2 * r - 1
if (u > 0) then
ub = 2 * (1 - r)
call map_power_1 (zm, factor, ub, eps)
zp = 2 - zm
else if (u < 0) then
ub = 2 * r
call map_power_1 (zp, factor, ub, eps)
zm = 2 - zp
else
factor = 1 / eps
zp = 1
zm = 1
end if
y = zp / 2
yb = zm / 2
end subroutine map_power_01
subroutine map_power_inverse_01 (y, yb, factor, r, eps)
real(default), intent(in) :: y, yb
real(default), intent(out) :: r, factor
real(default), intent(in) :: eps
real(default) :: ub, zp, zm
zp = 2 * y
zm = 2 * yb
if (zm < zp) then
call map_power_inverse_1 (zm, factor, ub, eps)
r = 1 - ub / 2
else if (zp < zm) then
call map_power_inverse_1 (zp, factor, ub, eps)
r = ub / 2
else
factor = 1 / eps
ub = 1
r = ub / 2
end if
end subroutine map_power_inverse_01
@ %def map_power_01
@ %def map_power_inverse_01
@
\subsubsection{Structure-function channels}
A structure-function chain parameterization (channel) may contain a
mapping that applies to multiple structure functions. This is
described by an extension of the [[sf_mapping_t]] type. In addition,
it may contain mappings that apply to (other) individual structure
functions. The details of these mappings are implementation-specific.
The [[sf_channel_t]] type combines this information. It contains an
array of map codes, one for each structure-function entry. The code
values are:
\begin{description}
\item[none] MC input parameters $r$ directly become energy fractions $x$
\item[single] default mapping for a single structure-function entry
\item[multi/s] map $r\to x$ such that one MC input parameter is $\hat s/s$
\item[multi/resonance] as before, adapted to s-channel resonance
\item[multi/on-shell] as before, adapted to an on-shell particle in
the s channel
\item[multi/endpoint] like multi/s, but enhance the region near $r_i=1$
\item[multi/endpoint/res] endpoint mapping with resonance
\item[multi/endpoint/os] endpoint mapping for on-shell
\item[multi/power/os] like multi/endpoint, regulating a power singularity
\end{description}
<<SF mappings: parameters>>=
integer, parameter :: SFMAP_NONE = 0
integer, parameter :: SFMAP_SINGLE = 1
integer, parameter :: SFMAP_MULTI_S = 2
integer, parameter :: SFMAP_MULTI_RES = 3
integer, parameter :: SFMAP_MULTI_ONS = 4
integer, parameter :: SFMAP_MULTI_EP = 5
integer, parameter :: SFMAP_MULTI_EPR = 6
integer, parameter :: SFMAP_MULTI_EPO = 7
integer, parameter :: SFMAP_MULTI_IP = 8
integer, parameter :: SFMAP_MULTI_IPR = 9
integer, parameter :: SFMAP_MULTI_IPO = 10
integer, parameter :: SFMAP_MULTI_EI = 11
integer, parameter :: SFMAP_MULTI_SRS = 13
integer, parameter :: SFMAP_MULTI_SON = 14
@ %def SFMAP_NONE SFMAP_SINGLE
@ %def SFMAP_MULTI_S SFMAP_MULTI_RES SFMAP_MULTI_ONS
@ %def SFMAP_MULTI_EP SFMAP_MULTI_EPR SFMAP_MULTI_EPO
@ %def SFMAP_MULTI_IP SFMAP_MULTI_IPR SFMAP_MULTI_IPO
@ %def SFMAP_MULTI_EI
@ %def SFMAP_MULTI_SRS SFMAP_MULTI_SON
@ Then, it contains an allocatable entry for the multi mapping. This
entry holds the MC-parameter indices on which the mapping applies
(there may be more than one MC parameter per structure-function entry)
and any parameters associated with the mapping.
There can be only one multi-mapping per channel.
<<SF mappings: public>>=
public :: sf_channel_t
<<SF mappings: types>>=
type :: sf_channel_t
integer, dimension(:), allocatable :: map_code
class(sf_mapping_t), allocatable :: multi_mapping
contains
<<SF mappings: sf channel: TBP>>
end type sf_channel_t
@ %def sf_channel_t
@ The output format prints a single character for each
structure-function entry and, if applicable, an account of the mapping
parameters.
<<SF mappings: sf channel: TBP>>=
procedure :: write => sf_channel_write
<<SF mappings: procedures>>=
subroutine sf_channel_write (object, unit)
class(sf_channel_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit)
if (allocated (object%map_code)) then
do i = 1, size (object%map_code)
select case (object%map_code (i))
case (SFMAP_NONE)
write (u, "(1x,A)", advance="no") "-"
case (SFMAP_SINGLE)
write (u, "(1x,A)", advance="no") "+"
case (SFMAP_MULTI_S)
write (u, "(1x,A)", advance="no") "s"
case (SFMAP_MULTI_RES, SFMAP_MULTI_SRS)
write (u, "(1x,A)", advance="no") "r"
case (SFMAP_MULTI_ONS, SFMAP_MULTI_SON)
write (u, "(1x,A)", advance="no") "o"
case (SFMAP_MULTI_EP)
write (u, "(1x,A)", advance="no") "e"
case (SFMAP_MULTI_EPR)
write (u, "(1x,A)", advance="no") "p"
case (SFMAP_MULTI_EPO)
write (u, "(1x,A)", advance="no") "q"
case (SFMAP_MULTI_IP)
write (u, "(1x,A)", advance="no") "i"
case (SFMAP_MULTI_IPR)
write (u, "(1x,A)", advance="no") "i"
case (SFMAP_MULTI_IPO)
write (u, "(1x,A)", advance="no") "i"
case (SFMAP_MULTI_EI)
write (u, "(1x,A)", advance="no") "i"
case default
write (u, "(1x,A)", advance="no") "?"
end select
end do
else
write (u, "(1x,A)", advance="no") "-"
end if
if (allocated (object%multi_mapping)) then
write (u, "(1x,'/')", advance="no")
call object%multi_mapping%write (u)
else
write (u, *)
end if
end subroutine sf_channel_write
@ %def sf_channel_write
@ Initializer for a single [[sf_channel]] object.
<<SF mappings: sf channel: TBP>>=
procedure :: init => sf_channel_init
<<SF mappings: procedures>>=
subroutine sf_channel_init (channel, n_strfun)
class(sf_channel_t), intent(out) :: channel
integer, intent(in) :: n_strfun
allocate (channel%map_code (n_strfun))
channel%map_code = SFMAP_NONE
end subroutine sf_channel_init
@ %def sf_channel_init
@ Assignment. This merely copies intrinsic assignment, but apparently
the latter is bugged in gfortran 4.6.3, causing memory corruption.
<<SF mappings: sf channel: TBP>>=
generic :: assignment (=) => sf_channel_assign
procedure :: sf_channel_assign
<<SF mappings: procedures>>=
subroutine sf_channel_assign (copy, original)
class(sf_channel_t), intent(out) :: copy
type(sf_channel_t), intent(in) :: original
allocate (copy%map_code (size (original%map_code)))
copy%map_code = original%map_code
if (allocated (original%multi_mapping)) then
allocate (copy%multi_mapping, source = original%multi_mapping)
end if
end subroutine sf_channel_assign
@ %def sf_channel_assign
@ This initializer allocates an array of channels with common number of
structure-function entries, therefore it is not a type-bound procedure.
<<SF mappings: public>>=
public :: allocate_sf_channels
<<SF mappings: procedures>>=
subroutine allocate_sf_channels (channel, n_channel, n_strfun)
type(sf_channel_t), dimension(:), intent(out), allocatable :: channel
integer, intent(in) :: n_channel
integer, intent(in) :: n_strfun
integer :: c
allocate (channel (n_channel))
do c = 1, n_channel
call channel(c)%init (n_strfun)
end do
end subroutine allocate_sf_channels
@ %def allocate_sf_channels
@ This marks a given subset of indices as single-mapping.
<<SF mappings: sf channel: TBP>>=
procedure :: activate_mapping => sf_channel_activate_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_activate_mapping (channel, i_sf)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
channel%map_code(i_sf) = SFMAP_SINGLE
end subroutine sf_channel_activate_mapping
@ %def sf_channel_activate_mapping
@ This sets an s-channel multichannel mapping. The parameter indices
are not yet set.
<<SF mappings: sf channel: TBP>>=
procedure :: set_s_mapping => sf_channel_set_s_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_s_mapping (channel, i_sf, power)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in), optional :: power
channel%map_code(i_sf) = SFMAP_MULTI_S
allocate (sf_s_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_s_mapping_t)
call mapping%init (power)
end select
end subroutine sf_channel_set_s_mapping
@ %def sf_channel_set_s_mapping
@ This sets an s-channel resonance multichannel mapping.
<<SF mappings: sf channel: TBP>>=
procedure :: set_res_mapping => sf_channel_set_res_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_res_mapping (channel, i_sf, m, w, single)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in) :: m, w
logical, intent(in) :: single
if (single) then
channel%map_code(i_sf) = SFMAP_MULTI_SRS
allocate (sf_res_mapping_single_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_res_mapping_single_t)
call mapping%init (m, w)
end select
else
channel%map_code(i_sf) = SFMAP_MULTI_RES
allocate (sf_res_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_res_mapping_t)
call mapping%init (m, w)
end select
end if
end subroutine sf_channel_set_res_mapping
@ %def sf_channel_set_res_mapping
@ This sets an s-channel on-shell multichannel mapping. The length of the
[[i_sf]] array must be 2. (The first parameter actually becomes an
irrelevant dummy.)
<<SF mappings: sf channel: TBP>>=
procedure :: set_os_mapping => sf_channel_set_os_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_os_mapping (channel, i_sf, m, single)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in) :: m
logical, intent(in) :: single
if (single) then
channel%map_code(i_sf) = SFMAP_MULTI_SON
allocate (sf_os_mapping_single_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_os_mapping_single_t)
call mapping%init (m)
end select
else
channel%map_code(i_sf) = SFMAP_MULTI_ONS
allocate (sf_os_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_os_mapping_t)
call mapping%init (m)
end select
end if
end subroutine sf_channel_set_os_mapping
@ %def sf_channel_set_os_mapping
@ This sets an s-channel endpoint mapping. The parameter $a$ is the
slope parameter (default 1); increasing it moves the endpoint region
(at $x=1$ to lower values in the input parameter.
region even more.
<<SF mappings: sf channel: TBP>>=
procedure :: set_ep_mapping => sf_channel_set_ep_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_ep_mapping (channel, i_sf, a)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in), optional :: a
channel%map_code(i_sf) = SFMAP_MULTI_EP
allocate (sf_ep_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_ep_mapping_t)
call mapping%init (a = a)
end select
end subroutine sf_channel_set_ep_mapping
@ %def sf_channel_set_ep_mapping
@ This sets a resonant endpoint mapping.
<<SF mappings: sf channel: TBP>>=
procedure :: set_epr_mapping => sf_channel_set_epr_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_epr_mapping (channel, i_sf, a, m, w)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in) :: a, m, w
channel%map_code(i_sf) = SFMAP_MULTI_EPR
allocate (sf_epr_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_epr_mapping_t)
call mapping%init (a, m, w)
end select
end subroutine sf_channel_set_epr_mapping
@ %def sf_channel_set_epr_mapping
@ This sets an on-shell endpoint mapping.
<<SF mappings: sf channel: TBP>>=
procedure :: set_epo_mapping => sf_channel_set_epo_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_epo_mapping (channel, i_sf, a, m)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in) :: a, m
channel%map_code(i_sf) = SFMAP_MULTI_EPO
allocate (sf_epo_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_epo_mapping_t)
call mapping%init (a, m)
end select
end subroutine sf_channel_set_epo_mapping
@ %def sf_channel_set_epo_mapping
@ This sets an s-channel power mapping, regulating a singularity of
type $(1-x)^{-1+\epsilon}$. The parameter $\epsilon$ depends on the
structure function.
<<SF mappings: sf channel: TBP>>=
procedure :: set_ip_mapping => sf_channel_set_ip_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_ip_mapping (channel, i_sf, eps)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in), optional :: eps
channel%map_code(i_sf) = SFMAP_MULTI_IP
allocate (sf_ip_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_ip_mapping_t)
call mapping%init (eps)
end select
end subroutine sf_channel_set_ip_mapping
@ %def sf_channel_set_ip_mapping
@ This sets an s-channel resonant power mapping, regulating a
singularity of type $(1-x)^{-1+\epsilon}$ in the presence of an
s-channel resonance. The parameter $\epsilon$ depends on the
structure function.
<<SF mappings: sf channel: TBP>>=
procedure :: set_ipr_mapping => sf_channel_set_ipr_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_ipr_mapping (channel, i_sf, eps, m, w)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in), optional :: eps, m, w
channel%map_code(i_sf) = SFMAP_MULTI_IPR
allocate (sf_ipr_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_ipr_mapping_t)
call mapping%init (eps, m, w)
end select
end subroutine sf_channel_set_ipr_mapping
@ %def sf_channel_set_ipr_mapping
@ This sets an on-shell power mapping, regulating a
singularity of type $(1-x)^{-1+\epsilon}$ for the production of a
single on-shell particle.. The parameter $\epsilon$ depends on the
structure function.
<<SF mappings: sf channel: TBP>>=
procedure :: set_ipo_mapping => sf_channel_set_ipo_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_ipo_mapping (channel, i_sf, eps, m)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in), optional :: eps, m
channel%map_code(i_sf) = SFMAP_MULTI_IPO
allocate (sf_ipo_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_ipo_mapping_t)
call mapping%init (eps, m)
end select
end subroutine sf_channel_set_ipo_mapping
@ %def sf_channel_set_ipo_mapping
@ This sets a combined endpoint/ISR mapping.
<<SF mappings: sf channel: TBP>>=
procedure :: set_ei_mapping => sf_channel_set_ei_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_ei_mapping (channel, i_sf, a, eps)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in), optional :: a, eps
channel%map_code(i_sf) = SFMAP_MULTI_EI
allocate (sf_ei_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_ei_mapping_t)
call mapping%init (a, eps)
end select
end subroutine sf_channel_set_ei_mapping
@ %def sf_channel_set_ei_mapping
@ This sets a combined endpoint/ISR mapping with resonance.
<<SF mappings: sf channel: TBP>>=
procedure :: set_eir_mapping => sf_channel_set_eir_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_eir_mapping (channel, i_sf, a, eps, m, w)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in), optional :: a, eps, m, w
channel%map_code(i_sf) = SFMAP_MULTI_EI
allocate (sf_eir_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_eir_mapping_t)
call mapping%init (a, eps, m, w)
end select
end subroutine sf_channel_set_eir_mapping
@ %def sf_channel_set_eir_mapping
@ This sets a combined endpoint/ISR mapping, on-shell.
<<SF mappings: sf channel: TBP>>=
procedure :: set_eio_mapping => sf_channel_set_eio_mapping
<<SF mappings: procedures>>=
subroutine sf_channel_set_eio_mapping (channel, i_sf, a, eps, m)
class(sf_channel_t), intent(inout) :: channel
integer, dimension(:), intent(in) :: i_sf
real(default), intent(in), optional :: a, eps, m
channel%map_code(i_sf) = SFMAP_MULTI_EI
allocate (sf_eio_mapping_t :: channel%multi_mapping)
select type (mapping => channel%multi_mapping)
type is (sf_eio_mapping_t)
call mapping%init (a, eps, m)
end select
end subroutine sf_channel_set_eio_mapping
@ %def sf_channel_set_eio_mapping
@ Return true if the mapping code at position [[i_sf]] is [[SFMAP_SINGLE]].
<<SF mappings: sf channel: TBP>>=
procedure :: is_single_mapping => sf_channel_is_single_mapping
<<SF mappings: procedures>>=
function sf_channel_is_single_mapping (channel, i_sf) result (flag)
class(sf_channel_t), intent(in) :: channel
integer, intent(in) :: i_sf
logical :: flag
flag = channel%map_code(i_sf) == SFMAP_SINGLE
end function sf_channel_is_single_mapping
@ %def sf_channel_is_single_mapping
@ Return true if the mapping code at position [[i_sf]] is any of the
[[SFMAP_MULTI]] mappings.
<<SF mappings: sf channel: TBP>>=
procedure :: is_multi_mapping => sf_channel_is_multi_mapping
<<SF mappings: procedures>>=
function sf_channel_is_multi_mapping (channel, i_sf) result (flag)
class(sf_channel_t), intent(in) :: channel
integer, intent(in) :: i_sf
logical :: flag
select case (channel%map_code(i_sf))
case (SFMAP_NONE, SFMAP_SINGLE)
flag = .false.
case default
flag = .true.
end select
end function sf_channel_is_multi_mapping
@ %def sf_channel_is_multi_mapping
@ Return the number of parameters that the multi-mapping requires. The
mapping object must be allocated.
<<SF mappings: sf channel: TBP>>=
procedure :: get_multi_mapping_n_par => sf_channel_get_multi_mapping_n_par
<<SF mappings: procedures>>=
function sf_channel_get_multi_mapping_n_par (channel) result (n_par)
class(sf_channel_t), intent(in) :: channel
integer :: n_par
if (allocated (channel%multi_mapping)) then
n_par = channel%multi_mapping%get_n_dim ()
else
n_par = 0
end if
end function sf_channel_get_multi_mapping_n_par
@ %def sf_channel_is_multi_mapping
@ Return true if there is any nontrivial mapping in any of the channels.
Note: we provide an explicit public function. gfortran 4.6.3 has
problems with the alternative implementation as a type-bound
procedure for an array base object.
<<SF mappings: public>>=
public :: any_sf_channel_has_mapping
<<SF mappings: procedures>>=
function any_sf_channel_has_mapping (channel) result (flag)
type(sf_channel_t), dimension(:), intent(in) :: channel
logical :: flag
integer :: c
flag = .false.
do c = 1, size (channel)
flag = flag .or. any (channel(c)%map_code /= SFMAP_NONE)
end do
end function any_sf_channel_has_mapping
@ %def any_sf_channel_has_mapping
@ Set a parameter index for an active multi mapping. We assume that
the index array is allocated properly.
<<SF mappings: sf channel: TBP>>=
procedure :: set_par_index => sf_channel_set_par_index
<<SF mappings: procedures>>=
subroutine sf_channel_set_par_index (channel, j, i_par)
class(sf_channel_t), intent(inout) :: channel
integer, intent(in) :: j
integer, intent(in) :: i_par
associate (mapping => channel%multi_mapping)
if (j >= 1 .and. j <= mapping%get_n_dim ()) then
if (mapping%get_index (j) == 0) then
call channel%multi_mapping%set_index (j, i_par)
else
call msg_bug ("Structure-function setup: mapping index set twice")
end if
else
call msg_bug ("Structure-function setup: mapping index out of range")
end if
end associate
end subroutine sf_channel_set_par_index
@ %def sf_channel_set_par_index
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_mappings_ut.f90]]>>=
<<File header>>
module sf_mappings_ut
use unit_tests
use sf_mappings_uti
<<Standard module head>>
<<SF mappings: public test>>
contains
<<SF mappings: test driver>>
end module sf_mappings_ut
@ %def sf_mappings_ut
@
<<[[sf_mappings_uti.f90]]>>=
<<File header>>
module sf_mappings_uti
<<Use kinds>>
use format_defs, only: FMT_11, FMT_12, FMT_13, FMT_14, FMT_15, FMT_16
use sf_mappings
<<Standard module head>>
<<SF mappings: test declarations>>
contains
<<SF mappings: tests>>
end module sf_mappings_uti
@ %def sf_mappings_ut
@ API: driver for the unit tests below.
<<SF mappings: public test>>=
public :: sf_mappings_test
<<SF mappings: test driver>>=
subroutine sf_mappings_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF mappings: execute tests>>
end subroutine sf_mappings_test
@ %def sf_mappings_test
@
\subsubsection{Check standard mapping}
Probe the standard mapping of the unit square for different parameter
values. Also calculates integrals. For a finite number of bins, they differ
slightly from $1$, but the result is well-defined because we are not using
random points.
<<SF mappings: execute tests>>=
call test (sf_mappings_1, "sf_mappings_1", &
"standard pair mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_1
<<SF mappings: tests>>=
subroutine sf_mappings_1 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p
write (u, "(A)") "* Test output: sf_mappings_1"
write (u, "(A)") "* Purpose: probe standard mapping"
write (u, "(A)")
allocate (sf_s_mapping_t :: mapping)
select type (mapping)
type is (sf_s_mapping_t)
call mapping%init ()
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0):"
p = [0._default, 0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.5):"
p = [0.1_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.1):"
p = [0.1_default, 0.1_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
allocate (sf_s_mapping_t :: mapping)
select type (mapping)
type is (sf_s_mapping_t)
call mapping%init (power=2._default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
write (u, *)
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0):"
p = [0._default, 0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.5):"
p = [0.1_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.1):"
p = [0.1_default, 0.1_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_1"
end subroutine sf_mappings_1
@ %def sf_mappings_1
@
\subsubsection{Channel entries}
Construct channel entries and print them.
<<SF mappings: execute tests>>=
call test (sf_mappings_2, "sf_mappings_2", &
"structure-function mapping channels", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_2
<<SF mappings: tests>>=
subroutine sf_mappings_2 (u)
integer, intent(in) :: u
type(sf_channel_t), dimension(:), allocatable :: channel
integer :: c
write (u, "(A)") "* Test output: sf_mappings_2"
write (u, "(A)") "* Purpose: construct and display &
&mapping-channel objects"
write (u, "(A)")
call allocate_sf_channels (channel, n_channel = 8, n_strfun = 2)
call channel(2)%activate_mapping ([1])
call channel(3)%set_s_mapping ([1,2])
call channel(4)%set_s_mapping ([1,2], power=2._default)
call channel(5)%set_res_mapping ([1,2], m = 0.5_default, w = 0.1_default, single = .false.)
call channel(6)%set_os_mapping ([1,2], m = 0.5_default, single = .false.)
call channel(7)%set_res_mapping ([1], m = 0.5_default, w = 0.1_default, single = .true.)
call channel(8)%set_os_mapping ([1], m = 0.5_default, single = .true.)
call channel(3)%set_par_index (1, 1)
call channel(3)%set_par_index (2, 4)
call channel(4)%set_par_index (1, 1)
call channel(4)%set_par_index (2, 4)
call channel(5)%set_par_index (1, 1)
call channel(5)%set_par_index (2, 3)
call channel(6)%set_par_index (1, 1)
call channel(6)%set_par_index (2, 2)
call channel(7)%set_par_index (1, 1)
call channel(8)%set_par_index (1, 1)
do c = 1, size (channel)
write (u, "(I0,':')", advance="no") c
call channel(c)%write (u)
end do
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_2"
end subroutine sf_mappings_2
@ %def sf_mappings_2
@
\subsubsection{Check resonance mapping}
Probe the resonance mapping of the unit square for different parameter
values. Also calculates integrals. For a finite number of bins, they differ
slightly from $1$, but the result is well-defined because we are not using
random points.
The resonance mass is at $1/2$ the energy, the width is $1/10$.
<<SF mappings: execute tests>>=
call test (sf_mappings_3, "sf_mappings_3", &
"resonant pair mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_3
<<SF mappings: tests>>=
subroutine sf_mappings_3 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p
write (u, "(A)") "* Test output: sf_mappings_3"
write (u, "(A)") "* Purpose: probe resonance pair mapping"
write (u, "(A)")
allocate (sf_res_mapping_t :: mapping)
select type (mapping)
type is (sf_res_mapping_t)
call mapping%init (0.5_default, 0.1_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0):"
p = [0._default, 0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.5):"
p = [0.1_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.1):"
p = [0.1_default, 0.1_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_3"
end subroutine sf_mappings_3
@ %def sf_mappings_3
@
\subsubsection{Check on-shell mapping}
Probe the on-shell mapping of the unit square for different parameter
values. Also calculates integrals. In this case, the Jacobian is
constant and given by $|\log m^2|$, so this is also the value of the
integral. The factor results from the variable change in the $\delta$
function $\delta (m^2 - x_1x_2)$ which multiplies the cross section
for the case at hand.
For the test, the (rescaled) resonance mass is set at $1/2$ the
energy.
<<SF mappings: execute tests>>=
call test (sf_mappings_4, "sf_mappings_4", &
"on-shell pair mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_4
<<SF mappings: tests>>=
subroutine sf_mappings_4 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p
write (u, "(A)") "* Test output: sf_mappings_4"
write (u, "(A)") "* Purpose: probe on-shell pair mapping"
write (u, "(A)")
allocate (sf_os_mapping_t :: mapping)
select type (mapping)
type is (sf_os_mapping_t)
call mapping%init (0.5_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0):"
p = [0._default, 0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0,0.1):"
p = [0._default, 0.1_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0,1.0):"
p = [0._default, 1.0_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_4"
end subroutine sf_mappings_4
@ %def sf_mappings_4
@
\subsubsection{Check endpoint mapping}
Probe the endpoint mapping of the unit square for different parameter
values. Also calculates integrals. For a finite number of bins, they differ
slightly from $1$, but the result is well-defined because we are not using
random points.
<<SF mappings: execute tests>>=
call test (sf_mappings_5, "sf_mappings_5", &
"endpoint pair mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_5
<<SF mappings: tests>>=
subroutine sf_mappings_5 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p
write (u, "(A)") "* Test output: sf_mappings_5"
write (u, "(A)") "* Purpose: probe endpoint pair mapping"
write (u, "(A)")
allocate (sf_ep_mapping_t :: mapping)
select type (mapping)
type is (sf_ep_mapping_t)
call mapping%init ()
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0):"
p = [0._default, 0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.5):"
p = [0.1_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.7,0.2):"
p = [0.7_default, 0.2_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_5"
end subroutine sf_mappings_5
@ %def sf_mappings_5
@
\subsubsection{Check endpoint resonant mapping}
Probe the endpoint mapping with resonance. Also calculates integrals.
<<SF mappings: execute tests>>=
call test (sf_mappings_6, "sf_mappings_6", &
"endpoint resonant mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_6
<<SF mappings: tests>>=
subroutine sf_mappings_6 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p
write (u, "(A)") "* Test output: sf_mappings_6"
write (u, "(A)") "* Purpose: probe endpoint resonant mapping"
write (u, "(A)")
allocate (sf_epr_mapping_t :: mapping)
select type (mapping)
type is (sf_epr_mapping_t)
call mapping%init (a = 1._default, m = 0.5_default, w = 0.1_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0):"
p = [0._default, 0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.5):"
p = [0.1_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.7,0.2):"
p = [0.7_default, 0.2_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Same mapping without resonance:"
write (u, "(A)")
allocate (sf_epr_mapping_t :: mapping)
select type (mapping)
type is (sf_epr_mapping_t)
call mapping%init (a = 1._default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0):"
p = [0._default, 0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.5):"
p = [0.1_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.7,0.2):"
p = [0.7_default, 0.2_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_6"
end subroutine sf_mappings_6
@ %def sf_mappings_6
@
\subsubsection{Check endpoint on-shell mapping}
Probe the endpoint mapping with an on-shell particle. Also calculates
integrals.
<<SF mappings: execute tests>>=
call test (sf_mappings_7, "sf_mappings_7", &
"endpoint on-shell mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_7
<<SF mappings: tests>>=
subroutine sf_mappings_7 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p
write (u, "(A)") "* Test output: sf_mappings_7"
write (u, "(A)") "* Purpose: probe endpoint on-shell mapping"
write (u, "(A)")
allocate (sf_epo_mapping_t :: mapping)
select type (mapping)
type is (sf_epo_mapping_t)
call mapping%init (a = 1._default, m = 0.5_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0):"
p = [0._default, 0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1,0.5):"
p = [0.1_default, 0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.7,0.2):"
p = [0.7_default, 0.2_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_7"
end subroutine sf_mappings_7
@ %def sf_mappings_7
@
\subsubsection{Check power mapping}
Probe the power mapping of the unit square for different parameter
values. Also calculates integrals. For a finite number of bins, they differ
slightly from $1$, but the result is well-defined because we are not using
random points.
<<SF mappings: execute tests>>=
call test (sf_mappings_8, "sf_mappings_8", &
"power pair mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_8
<<SF mappings: tests>>=
subroutine sf_mappings_8 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p, pb
write (u, "(A)") "* Test output: sf_mappings_8"
write (u, "(A)") "* Purpose: probe power pair mapping"
write (u, "(A)")
allocate (sf_ip_mapping_t :: mapping)
select type (mapping)
type is (sf_ip_mapping_t)
call mapping%init (eps = 0.1_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0.5):"
p = [0._default, 0.5_default]
pb= [1._default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
pb= [0.5_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.9,0.5):"
p = [0.9_default, 0.5_default]
pb= [0.1_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7,0.2):"
p = [0.7_default, 0.2_default]
pb= [0.3_default, 0.8_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7,0.8):"
p = [0.7_default, 0.8_default]
pb= [0.3_default, 0.2_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.99,0.02):"
p = [0.99_default, 0.02_default]
pb= [0.01_default, 0.98_default]
call mapping%check (u, p, pb, FMT_14, FMT_12)
write (u, *)
write (u, "(A)") "Probe at (0.99,0.98):"
p = [0.99_default, 0.98_default]
pb= [0.01_default, 0.02_default]
call mapping%check (u, p, pb, FMT_14, FMT_12)
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_8"
end subroutine sf_mappings_8
@ %def sf_mappings_8
@
\subsubsection{Check resonant power mapping}
Probe the power mapping of the unit square, adapted for an s-channel
resonance, for different parameter values. Also calculates integrals.
For a finite number of bins, they differ slightly from $1$, but the
result is well-defined because we are not using random points.
<<SF mappings: execute tests>>=
call test (sf_mappings_9, "sf_mappings_9", &
"power resonance mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_9
<<SF mappings: tests>>=
subroutine sf_mappings_9 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p, pb
write (u, "(A)") "* Test output: sf_mappings_9"
write (u, "(A)") "* Purpose: probe power resonant pair mapping"
write (u, "(A)")
allocate (sf_ipr_mapping_t :: mapping)
select type (mapping)
type is (sf_ipr_mapping_t)
call mapping%init (eps = 0.1_default, m = 0.5_default, w = 0.1_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0.5):"
p = [0._default, 0.5_default]
pb= [1._default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
pb= [0.5_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.9,0.5):"
p = [0.9_default, 0.5_default]
pb= [0.1_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7,0.2):"
p = [0.7_default, 0.2_default]
pb= [0.3_default, 0.8_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7,0.8):"
p = [0.7_default, 0.8_default]
pb= [0.3_default, 0.2_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.9999,0.02):"
p = [0.9999_default, 0.02_default]
pb= [0.0001_default, 0.98_default]
call mapping%check (u, p, pb, FMT_11, FMT_12)
write (u, *)
write (u, "(A)") "Probe at (0.9999,0.98):"
p = [0.9999_default, 0.98_default]
pb= [0.0001_default, 0.02_default]
call mapping%check (u, p, pb, FMT_11, FMT_12)
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Same mapping without resonance:"
write (u, "(A)")
allocate (sf_ipr_mapping_t :: mapping)
select type (mapping)
type is (sf_ipr_mapping_t)
call mapping%init (eps = 0.1_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0.5):"
p = [0._default, 0.5_default]
pb= [1._default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.5,0.5):"
p = [0.5_default, 0.5_default]
pb= [0.5_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.9,0.5):"
p = [0.9_default, 0.5_default]
pb= [0.1_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7,0.2):"
p = [0.7_default, 0.2_default]
pb= [0.3_default, 0.8_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7,0.8):"
p = [0.7_default, 0.8_default]
pb= [0.3_default, 0.2_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_9"
end subroutine sf_mappings_9
@ %def sf_mappings_9
@
\subsubsection{Check on-shell power mapping}
Probe the power mapping of the unit square, adapted for
single-particle production, for different parameter values. Also
calculates integrals. For a finite number of bins, they differ
slightly from $1$, but the result is well-defined because we are not
using random points.
<<SF mappings: execute tests>>=
call test (sf_mappings_10, "sf_mappings_10", &
"power on-shell mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_10
<<SF mappings: tests>>=
subroutine sf_mappings_10 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(2) :: p, pb
write (u, "(A)") "* Test output: sf_mappings_10"
write (u, "(A)") "* Purpose: probe power on-shell mapping"
write (u, "(A)")
allocate (sf_ipo_mapping_t :: mapping)
select type (mapping)
type is (sf_ipo_mapping_t)
call mapping%init (eps = 0.1_default, m = 0.5_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0,0.5):"
p = [0._default, 0.5_default]
pb= [1._default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0,0.02):"
p = [0._default, 0.02_default]
pb= [1._default, 0.98_default]
call mapping%check (u, p, pb, FMT_15, FMT_12)
write (u, *)
write (u, "(A)") "Probe at (0,0.98):"
p = [0._default, 0.98_default]
pb= [1._default, 0.02_default]
call mapping%check (u, p, pb, FMT_15, FMT_12)
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_10"
end subroutine sf_mappings_10
@ %def sf_mappings_10
@
\subsubsection{Check combined endpoint-power mapping}
Probe the mapping for the beamstrahlung/ISR combination.
<<SF mappings: execute tests>>=
call test (sf_mappings_11, "sf_mappings_11", &
"endpoint/power combined mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_11
<<SF mappings: tests>>=
subroutine sf_mappings_11 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(4) :: p, pb
write (u, "(A)") "* Test output: sf_mappings_11"
write (u, "(A)") "* Purpose: probe power pair mapping"
write (u, "(A)")
allocate (sf_ei_mapping_t :: mapping)
select type (mapping)
type is (sf_ei_mapping_t)
call mapping%init (eps = 0.1_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
call mapping%set_index (3, 3)
call mapping%set_index (4, 4)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0.5, 0.5, 0.5, 0.5):"
p = [0.5_default, 0.5_default, 0.5_default, 0.5_default]
pb= [0.5_default, 0.5_default, 0.5_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7, 0.2, 0.4, 0.8):"
p = [0.7_default, 0.2_default, 0.4_default, 0.8_default]
pb= [0.3_default, 0.8_default, 0.6_default, 0.2_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.9, 0.06, 0.95, 0.1):"
p = [0.9_default, 0.06_default, 0.95_default, 0.1_default]
pb= [0.1_default, 0.94_default, 0.05_default, 0.9_default]
call mapping%check (u, p, pb, FMT_13, FMT_12)
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_11"
end subroutine sf_mappings_11
@ %def sf_mappings_11
@
\subsubsection{Check resonant endpoint-power mapping}
Probe the mapping for the beamstrahlung/ISR combination.
<<SF mappings: execute tests>>=
call test (sf_mappings_12, "sf_mappings_12", &
"endpoint/power resonant combined mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_12
<<SF mappings: tests>>=
subroutine sf_mappings_12 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(4) :: p, pb
write (u, "(A)") "* Test output: sf_mappings_12"
write (u, "(A)") "* Purpose: probe resonant combined mapping"
write (u, "(A)")
allocate (sf_eir_mapping_t :: mapping)
select type (mapping)
type is (sf_eir_mapping_t)
call mapping%init (a = 1._default, &
eps = 0.1_default, m = 0.5_default, w = 0.1_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
call mapping%set_index (3, 3)
call mapping%set_index (4, 4)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0.5, 0.5, 0.5, 0.5):"
p = [0.5_default, 0.5_default, 0.5_default, 0.5_default]
pb= [0.5_default, 0.5_default, 0.5_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7, 0.2, 0.4, 0.8):"
p = [0.7_default, 0.2_default, 0.4_default, 0.8_default]
pb= [0.3_default, 0.8_default, 0.6_default, 0.2_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.9, 0.06, 0.95, 0.1):"
p = [0.9_default, 0.06_default, 0.95_default, 0.1_default]
pb= [0.1_default, 0.94_default, 0.05_default, 0.9_default]
call mapping%check (u, p, pb, FMT_15, FMT_12)
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_12"
end subroutine sf_mappings_12
@ %def sf_mappings_12
@
\subsubsection{Check on-shell endpoint-power mapping}
Probe the mapping for the beamstrahlung/ISR combination.
<<SF mappings: execute tests>>=
call test (sf_mappings_13, "sf_mappings_13", &
"endpoint/power on-shell combined mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_13
<<SF mappings: tests>>=
subroutine sf_mappings_13 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(4) :: p, pb
write (u, "(A)") "* Test output: sf_mappings_13"
write (u, "(A)") "* Purpose: probe on-shell combined mapping"
write (u, "(A)")
allocate (sf_eio_mapping_t :: mapping)
select type (mapping)
type is (sf_eio_mapping_t)
call mapping%init (a = 1._default, eps = 0.1_default, m = 0.5_default)
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
call mapping%set_index (3, 3)
call mapping%set_index (4, 4)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0.5, 0.5, 0.5, 0.5):"
p = [0.5_default, 0.5_default, 0.5_default, 0.5_default]
pb= [0.5_default, 0.5_default, 0.5_default, 0.5_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.7, 0.2, 0.4, 0.8):"
p = [0.7_default, 0.2_default, 0.4_default, 0.8_default]
pb= [0.3_default, 0.8_default, 0.6_default, 0.2_default]
call mapping%check (u, p, pb, FMT_16)
write (u, *)
write (u, "(A)") "Probe at (0.9, 0.06, 0.95, 0.1):"
p = [0.9_default, 0.06_default, 0.95_default, 0.1_default]
pb= [0.1_default, 0.94_default, 0.05_default, 0.9_default]
call mapping%check (u, p, pb, FMT_14, FMT_12)
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_13"
end subroutine sf_mappings_13
@ %def sf_mappings_13
@
\subsubsection{Check rescaling}
Check the rescaling factor in on-shell basic mapping.
<<SF mappings: execute tests>>=
call test (sf_mappings_14, "sf_mappings_14", &
"rescaled on-shell mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_14
<<SF mappings: tests>>=
subroutine sf_mappings_14 (u)
integer, intent(in) :: u
real(default), dimension(2) :: p2, r2
real(default), dimension(1) :: p1, r1
real(default) :: f, x_free, m2
write (u, "(A)") "* Test output: sf_mappings_14"
write (u, "(A)") "* Purpose: probe rescaling in os mapping"
write (u, "(A)")
x_free = 0.9_default
m2 = 0.5_default
write (u, "(A)") "* Two parameters"
write (u, "(A)")
p2 = [0.1_default, 0.2_default]
call map_on_shell (r2, f, p2, -log (m2), x_free)
write (u, "(A,9(1x," // FMT_14 // "))") "p =", p2
write (u, "(A,9(1x," // FMT_14 // "))") "r =", r2
write (u, "(A,9(1x," // FMT_14 // "))") "f =", f
write (u, "(A,9(1x," // FMT_14 // "))") "*r=", x_free * product (r2)
write (u, *)
call map_on_shell_inverse (r2, f, p2, -log (m2), x_free)
write (u, "(A,9(1x," // FMT_14 // "))") "p =", p2
write (u, "(A,9(1x," // FMT_14 // "))") "r =", r2
write (u, "(A,9(1x," // FMT_14 // "))") "f =", f
write (u, "(A,9(1x," // FMT_14 // "))") "*r=", x_free * product (r2)
write (u, "(A)")
write (u, "(A)") "* One parameter"
write (u, "(A)")
p1 = [0.1_default]
call map_on_shell_single (r1, f, p1, -log (m2), x_free)
write (u, "(A,9(1x," // FMT_14 // "))") "p =", p1
write (u, "(A,9(1x," // FMT_14 // "))") "r =", r1
write (u, "(A,9(1x," // FMT_14 // "))") "f =", f
write (u, "(A,9(1x," // FMT_14 // "))") "*r=", x_free * product (r1)
write (u, *)
call map_on_shell_single_inverse (r1, f, p1, -log (m2), x_free)
write (u, "(A,9(1x," // FMT_14 // "))") "p =", p1
write (u, "(A,9(1x," // FMT_14 // "))") "r =", r1
write (u, "(A,9(1x," // FMT_14 // "))") "f =", f
write (u, "(A,9(1x," // FMT_14 // "))") "*r=", x_free * product (r1)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_14"
end subroutine sf_mappings_14
@ %def sf_mappings_14
@
\subsubsection{Check single parameter resonance mapping}
Probe the resonance mapping of the unit interval for different parameter
values. Also calculates integrals.
The resonance mass is at $1/2$ the energy, the width is $1/10$.
<<SF mappings: execute tests>>=
call test (sf_mappings_15, "sf_mappings_15", &
"resonant single mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_15
<<SF mappings: tests>>=
subroutine sf_mappings_15 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(1) :: p
write (u, "(A)") "* Test output: sf_mappings_15"
write (u, "(A)") "* Purpose: probe resonance single mapping"
write (u, "(A)")
allocate (sf_res_mapping_single_t :: mapping)
select type (mapping)
type is (sf_res_mapping_single_t)
call mapping%init (0.5_default, 0.1_default)
call mapping%set_index (1, 1)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0):"
p = [0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5):"
p = [0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.1):"
p = [0.1_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_15"
end subroutine sf_mappings_15
@ %def sf_mappings_15
@
\subsubsection{Check single parameter on-shell mapping}
Probe the on-shell (pseudo) mapping of the unit interval for different parameter
values. Also calculates integrals.
The resonance mass is at $1/2$ the energy.
<<SF mappings: execute tests>>=
call test (sf_mappings_16, "sf_mappings_16", &
"on-shell single mapping", &
u, results)
<<SF mappings: test declarations>>=
public :: sf_mappings_16
<<SF mappings: tests>>=
subroutine sf_mappings_16 (u)
integer, intent(in) :: u
class(sf_mapping_t), allocatable :: mapping
real(default), dimension(1) :: p
write (u, "(A)") "* Test output: sf_mappings_16"
write (u, "(A)") "* Purpose: probe on-shell single mapping"
write (u, "(A)")
allocate (sf_os_mapping_single_t :: mapping)
select type (mapping)
type is (sf_os_mapping_single_t)
call mapping%init (0.5_default)
call mapping%set_index (1, 1)
end select
call mapping%write (u)
write (u, *)
write (u, "(A)") "Probe at (0):"
p = [0._default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Probe at (0.5):"
p = [0.5_default]
call mapping%check (u, p, 1-p, "F7.5")
write (u, *)
write (u, "(A)") "Compute integral:"
write (u, "(3x,A,1x,F7.5)") "I =", mapping%integral (100000)
deallocate (mapping)
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_mappings_16"
end subroutine sf_mappings_16
@ %def sf_mappings_16
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Structure function base}
<<[[sf_base.f90]]>>=
<<File header>>
module sf_base
<<Use kinds>>
<<Use strings>>
use io_units
use format_utils, only: write_separator
use format_defs, only: FMT_17, FMT_19
- use physics_defs, only: n_beam_structure_int
use diagnostics
use lorentz
use quantum_numbers
use interactions
use evaluators
use pdg_arrays
use beams
use sf_aux
use sf_mappings
+ use constants, only: one, two
+ use physics_defs, only: n_beams_rescaled
<<Standard module head>>
<<SF base: public>>
<<SF base: parameters>>
<<SF base: types>>
<<SF base: interfaces>>
contains
<<SF base: procedures>>
end module sf_base
@ %def sf_base
@
\subsection{Abstract rescale data-type}
-NLO calculations involve treatment of initial state parton radiation.
-The radiation of a parton rescale the energy fraction which enters the hard process.
-We allow for different rescale settings by extending the abstract
+NLO calculations require the treatment of initial state parton radiation.
+The radiation of a parton rescales the energy fraction which enters the hard process.
+We allow for different rescale settings by extending the abstract.
[[sf_rescale_t]] data type.
<<SF base: public>>=
public :: sf_rescale_t
<<SF base: types>>=
type, abstract :: sf_rescale_t
- integer :: i_restricted_beam = -1
integer :: i_beam = 0
- logical :: gluon = .false.
contains
<<SF base: rescaling function: TBP>>
end type sf_rescale_t
@ %def sf_rescale_t
@
<<SF base: rescaling function: TBP>>=
procedure (sf_rescale_apply), deferred :: apply
<<SF base: interfaces>>=
abstract interface
subroutine sf_rescale_apply (func, x)
import
class(sf_rescale_t), intent(in) :: func
real(default), intent(inout) :: x
end subroutine sf_rescale_apply
end interface
@ %def rescale_apply
@
<<SF base: rescaling function: TBP>>=
procedure :: set_i_beam => sf_rescale_set_i_beam
<<SF base: procedures>>=
subroutine sf_rescale_set_i_beam (func, i_beam)
class(sf_rescale_t), intent(inout) :: func
integer, intent(in) :: i_beam
func%i_beam = i_beam
end subroutine sf_rescale_set_i_beam
@ %def rescale_set_i_beam
-@ Restrict rescaling to beam with index [[i_beam]].
-<<SF base: rescaling function: TBP>>=
- procedure :: restrict_to_beam => sf_rescale_restrict_to_beam
+@
+<<SF base: public>>=
+ public :: sf_rescale_collinear_t
+<<SF base: types>>=
+ type, extends (sf_rescale_t) :: sf_rescale_collinear_t
+ real(default) :: xi_tilde
+ contains
+ <<SF base: rescale collinear: TBP>>
+ end type sf_rescale_collinear_t
+
+@ %def sf_rescale_collinear_t
+@ For the subtraction terms we need to rescale the Born $x$ of both beams in the
+collinear limit. This leaves one beam unaffected and rescales the other according to
+\begin{equation}
+ x = \frac{\overline{x}}{1-\xi}
+\end{equation}
+which is the collinear limit of [[sf_rescale_real_apply]].
+<<SF base: rescale collinear: TBP>>=
+ procedure :: apply => sf_rescale_collinear_apply
<<SF base: procedures>>=
- subroutine sf_rescale_restrict_to_beam (func, i_beam)
- class(sf_rescale_t), intent(inout) :: func
- integer, intent(in) :: i_beam
- if (func%i_restricted_beam > 0) &
- call msg_bug ("[sf_rescale_restrict_to_beam] restricted beam already set.")
- func%i_restricted_beam = i_beam
- end subroutine sf_rescale_restrict_to_beam
+ subroutine sf_rescale_collinear_apply (func, x)
+ class(sf_rescale_collinear_t), intent(in) :: func
+ real(default), intent(inout) :: x
+ real(default) :: xi
+ if (debug2_active (D_BEAMS)) then
+ print *, 'Rescaling function - Collinear: '
+ print *, 'Input, unscaled x: ', x
+ print *, 'xi_tilde: ', func%xi_tilde
+ end if
+ xi = func%xi_tilde * (one - x)
+ x = x / (one - xi)
+ if (debug2_active (D_BEAMS)) print *, 'rescaled x: ', x
+ end subroutine sf_rescale_collinear_apply
-@ %def sf_rescale_set_rescaled_beam
-@ Test on restricted beam momentum rescaling or no restriction.
-<<SF base: rescaling function: TBP>>=
- procedure :: is_restricted => sf_rescale_is_restricted
+@ %def sf_rescale_collinear_apply
+@
+<<SF base: rescale collinear: TBP>>=
+ procedure :: set => sf_rescale_collinear_set
<<SF base: procedures>>=
- logical function sf_rescale_is_restricted (func, i_beam) result (yorn)
- class(sf_rescale_t), intent(in) :: func
- integer, intent(in) :: i_beam
- yorn = (func%i_restricted_beam > 0)
- yorn = yorn .and. (func%i_restricted_beam /= i_beam)
- end function sf_rescale_is_restricted
-
-@ %def sf_rescale_is_restricted
-@ In case, gluon splits into quark/anti-quark, the DGLAP formulas become
-degenerate over flavours. We add subtraction with gluonic pdfs only which are
-convoluted with all quark/anti-quark flavours - hence PDF singlet.
-<<SF base: rescaling function: TBP>>=
- procedure :: set_gluons => sf_rescale_set_gluons
- procedure :: has_gluons => sf_rescale_has_gluons
+ subroutine sf_rescale_collinear_set (func, xi_tilde)
+ class(sf_rescale_collinear_t), intent(inout) :: func
+ real(default), intent(in) :: xi_tilde
+ func%xi_tilde = xi_tilde
+ end subroutine sf_rescale_collinear_set
+
+@ %def sf_rescale_collinear_set
+@
+<<SF base: public>>=
+ public :: sf_rescale_real_t
+<<SF base: types>>=
+ type, extends (sf_rescale_t) :: sf_rescale_real_t
+ real(default) :: xi, y
+ contains
+ <<SF base: rescale real: TBP>>
+ end type sf_rescale_real_t
+
+@ %def sf_rescale_real_t
+@ In case of IS Splittings, the beam $x$ changes from Born to real and thus needs to be rescaled according to
+\begin{equation}
+ x_\oplus = \frac{\overline{x}_\oplus}{\sqrt{1-\xi}} \sqrt{\frac{2-\xi(1-y)}{2-\xi(1+y)}}
+ , \qquad
+ x_\ominus = \frac{\overline{x}_\ominus}{\sqrt{1-\xi}} \sqrt{\frac{2-\xi(1+y)}{2-\xi(1-y)}}
+\end{equation}
+Refs:
+\begin{itemize}
+ \item[\textbullet] [0709.2092] Eq. (5.7).
+ \item[\textbullet] [0907.4076] Eq. (2.21).
+ \item Christian Weiss' PhD Thesis (DESY-THESIS-2017-025), Eq. (A.2.3).
+\end{itemize}
+<<SF base: rescale real: TBP>>=
+ procedure :: apply => sf_rescale_real_apply
<<SF base: procedures>>=
- subroutine sf_rescale_set_gluons (func, yorn)
- class(sf_rescale_t), intent(inout) :: func
- logical, intent(in) :: yorn
- func%gluon = yorn
- end subroutine sf_rescale_set_gluons
-
- logical function sf_rescale_has_gluons (func) result (yorn)
- class(sf_rescale_t), intent(in) :: func
- yorn = func%gluon
- end function sf_rescale_has_gluons
+ subroutine sf_rescale_real_apply (func, x)
+ class(sf_rescale_real_t), intent(in) :: func
+ real(default), intent(inout) :: x
+ real(default) :: onepy, onemy
+ if (debug2_active (D_BEAMS)) then
+ print *, 'Rescaling function - Real: '
+ print *, 'Input, unscaled: ', x
+ print *, 'Beam index: ', func%i_beam
+ print *, 'xi: ', func%xi, 'y: ', func%y
+ end if
+ x = x / sqrt (one - func%xi)
+ onepy = one + func%y; onemy = one - func%y
+ if (func%i_beam == 1) then
+ x = x * sqrt ((two - func%xi * onemy) / (two - func%xi * onepy))
+ else if (func%i_beam == 2) then
+ x = x * sqrt ((two - func%xi * onepy) / (two - func%xi * onemy))
+ else
+ call msg_fatal ("sf_rescale_real_apply - invalid beam index")
+ end if
+ if (debug2_active (D_BEAMS)) print *, 'rescaled x: ', x
+ end subroutine sf_rescale_real_apply
+
+@ %def sf_rescale_real_apply
+@
+<<SF base: rescale real: TBP>>=
+ procedure :: set => sf_rescale_real_set
+<<SF base: procedures>>=
+ subroutine sf_rescale_real_set (func, xi, y)
+ class(sf_rescale_real_t), intent(inout) :: func
+ real(default), intent(in) :: xi, y
+ func%xi = xi; func%y = y
+ end subroutine sf_rescale_real_set
+
+@ %def sf_rescale_real_set
+<<SF base: public>>=
+ public :: sf_rescale_dglap_t
+<<SF base: types>>=
+ type, extends(sf_rescale_t) :: sf_rescale_dglap_t
+ real(default), dimension(:), allocatable :: z
+ contains
+ <<SF base: rescale dglap: TBP>>
+ end type sf_rescale_dglap_t
-@ %def sf_rescale_set_gluons rescale_has_gluons
+@ %def sf_rescale_dglap_t
+@
+<<SF base: rescale dglap: TBP>>=
+ procedure :: apply => sf_rescale_dglap_apply
+<<SF base: procedures>>=
+ subroutine sf_rescale_dglap_apply (func, x)
+ class(sf_rescale_dglap_t), intent(in) :: func
+ real(default), intent(inout) :: x
+ if (debug2_active (D_BEAMS)) then
+ print *, "Rescaling function - DGLAP:"
+ print *, "Input: ", x
+ print *, "Beam index: ", func%i_beam
+ print *, "z: ", func%z
+ end if
+ x = x / func%z(func%i_beam)
+ if (debug2_active (D_BEAMS)) print *, "scaled x: ", x
+ end subroutine sf_rescale_dglap_apply
+
+@ %def sf_rescale_dglap_apply
+@
+<<SF base: rescale dglap: TBP>>=
+ procedure :: set => sf_rescale_dglap_set
+<<SF base: procedures>>=
+ subroutine sf_rescale_dglap_set (func, z)
+ class(sf_rescale_dglap_t), intent(inout) :: func
+ real(default), dimension(:), intent(in) :: z
+ ! allocate-on-assginment
+ func%z = z
+ end subroutine sf_rescale_dglap_set
+
+@ %def sf_rescale_dglap_set
@
\subsection{Abstract structure-function data type}
This type should hold all configuration data for a specific type of
structure function. The base object is empty; the implementations
will fill it.
<<SF base: public>>=
public :: sf_data_t
<<SF base: types>>=
type, abstract :: sf_data_t
contains
<<SF base: sf data: TBP>>
end type sf_data_t
@ %def sf_data_t
@ Output.
<<SF base: sf data: TBP>>=
procedure (sf_data_write), deferred :: write
<<SF base: interfaces>>=
abstract interface
subroutine sf_data_write (data, unit, verbose)
import
class(sf_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
end subroutine sf_data_write
end interface
@ %def sf_data_write
@ Return true if this structure function is in generator mode. In
that case, all parameters are free, otherwise bound. (We do not
support mixed cases.) Default is: no generator.
<<SF base: sf data: TBP>>=
procedure :: is_generator => sf_data_is_generator
<<SF base: procedures>>=
function sf_data_is_generator (data) result (flag)
class(sf_data_t), intent(in) :: data
logical :: flag
flag = .false.
end function sf_data_is_generator
@ %def sf_data_is_generator
@ Return the number of input parameters that determine the
structure function.
<<SF base: sf data: TBP>>=
procedure (sf_data_get_int), deferred :: get_n_par
<<SF base: interfaces>>=
abstract interface
function sf_data_get_int (data) result (n)
import
class(sf_data_t), intent(in) :: data
integer :: n
end function sf_data_get_int
end interface
@ %def sf_data_get_int
@ Return the outgoing particle PDG codes for the current setup. The codes can
be an array of particles, for each beam.
<<SF base: sf data: TBP>>=
procedure (sf_data_get_pdg_out), deferred :: get_pdg_out
<<SF base: interfaces>>=
abstract interface
subroutine sf_data_get_pdg_out (data, pdg_out)
import
class(sf_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
end subroutine sf_data_get_pdg_out
end interface
@ %def sf_data_get_pdg_out
@ Allocate a matching structure function interaction object and
properly initialize it.
<<SF base: sf data: TBP>>=
procedure (sf_data_allocate_sf_int), deferred :: allocate_sf_int
<<SF base: interfaces>>=
abstract interface
subroutine sf_data_allocate_sf_int (data, sf_int)
import
class(sf_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
end subroutine sf_data_allocate_sf_int
end interface
@ %def sf_data_allocate_sf_int
@ Return the PDF set index, if applicable. We implement a default
method which returns zero. The PDF (builtin and LHA) implementations
will override this.
<<SF base: sf data: TBP>>=
procedure :: get_pdf_set => sf_data_get_pdf_set
<<SF base: procedures>>=
elemental function sf_data_get_pdf_set (data) result (pdf_set)
class(sf_data_t), intent(in) :: data
integer :: pdf_set
pdf_set = 0
end function sf_data_get_pdf_set
@ %def sf_data_get_pdf_set
@ Return the spectrum file, if applicable. We implement a default
method which returns zero. CIRCE1, CIRCE2 and the beam spectrum will
override this.
<<SF base: sf data: TBP>>=
procedure :: get_beam_file => sf_data_get_beam_file
<<SF base: procedures>>=
function sf_data_get_beam_file (data) result (file)
class(sf_data_t), intent(in) :: data
type(string_t) :: file
file = ""
end function sf_data_get_beam_file
@ %def sf_data_get_beam_file
@
\subsection{Structure-function chain configuration}
This is the data type that the [[process]] module uses for setting
up its structure-function chain. For each structure function described
by the beam data, there is an entry. The [[i]] array indicates the
beam(s) to which this structure function applies, and the [[data]]
object contains the actual configuration data.
<<SF base: public>>=
public :: sf_config_t
<<SF base: types>>=
type :: sf_config_t
integer, dimension(:), allocatable :: i
class(sf_data_t), allocatable :: data
contains
<<SF base: sf config: TBP>>
end type sf_config_t
@ %def sf_config_t
@ Output:
<<SF base: sf config: TBP>>=
procedure :: write => sf_config_write
<<SF base: procedures>>=
subroutine sf_config_write (object, unit)
class(sf_config_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
if (allocated (object%i)) then
write (u, "(1x,A,2(1x,I0))") "Structure-function configuration: &
&beam(s)", object%i
if (allocated (object%data)) call object%data%write (u)
else
write (u, "(1x,A)") "Structure-function configuration: [undefined]"
end if
end subroutine sf_config_write
@ %def sf_config_write
@ Initialize.
<<SF base: sf config: TBP>>=
procedure :: init => sf_config_init
<<SF base: procedures>>=
subroutine sf_config_init (sf_config, i_beam, sf_data)
class(sf_config_t), intent(out) :: sf_config
integer, dimension(:), intent(in) :: i_beam
class(sf_data_t), intent(in) :: sf_data
allocate (sf_config%i (size (i_beam)), source = i_beam)
allocate (sf_config%data, source = sf_data)
end subroutine sf_config_init
@ %def sf_config_init
@ Return the PDF set, if any.
<<SF base: sf config: TBP>>=
procedure :: get_pdf_set => sf_config_get_pdf_set
<<SF base: procedures>>=
elemental function sf_config_get_pdf_set (sf_config) result (pdf_set)
class(sf_config_t), intent(in) :: sf_config
integer :: pdf_set
pdf_set = sf_config%data%get_pdf_set ()
end function sf_config_get_pdf_set
@ %def sf_config_get_pdf_set
@ Return the beam spectrum file, if any.
<<SF base: sf config: TBP>>=
procedure :: get_beam_file => sf_config_get_beam_file
<<SF base: procedures>>=
function sf_config_get_beam_file (sf_config) result (file)
class(sf_config_t), intent(in) :: sf_config
type(string_t) :: file
file = sf_config%data%get_beam_file ()
end function sf_config_get_beam_file
@ %def sf_config_get_beam_file
@
\subsection{Structure-function instance}
The [[sf_int_t]] data type contains an [[interaction_t]] object (it is
an extension of this type) and a pointer to the [[sf_data_t]]
configuration data. This interaction, or copies of it, is used to
implement structure-function kinematics and dynamics in the context of
process evaluation.
The status code [[status]] tells whether the interaction is undefined,
has defined kinematics (but matrix elements invalid), or is completely
defined. There is also a status code for failure. The implementation
is responsible for updating the status.
The entries [[mi2]], [[mr2]], and [[mo2]] hold the squared
invariant masses of the incoming, radiated, and outgoing particle,
respectively. They are supposed to be set upon initialization, but
could also be varied event by event.
If the radiated or outgoing mass is nonzero, we may need to apply an
on-shell projection. The projection mode is stored as
[[on_shell_mode]].
The array [[beam_index]] is the list of beams on which this structure
function applies ($1$, $2$, or both). The arrays [[incoming]],
[[radiated]], and [[outgoing]] contain the indices of the respective
particle sets within the interaction, for convenient lookup. The
array [[par_index]] indicates the MC input parameters that this entry
will use up in the structure-function chain. The first parameter (or
the first two, for a spectrum) in this array determines the momentum
fraction and is thus subject to global mappings.
In the abstract base type, we do not implement the data pointer. This
allows us to restrict its type in the implementations.
<<SF base: public>>=
public :: sf_int_t
<<SF base: types>>=
type, abstract, extends (interaction_t) :: sf_int_t
integer :: status = SF_UNDEFINED
real(default), dimension(:), allocatable :: mi2
real(default), dimension(:), allocatable :: mr2
real(default), dimension(:), allocatable :: mo2
integer :: on_shell_mode = KEEP_ENERGY
logical :: qmin_defined = .false.
logical :: qmax_defined = .false.
real(default), dimension(:), allocatable :: qmin
real(default), dimension(:), allocatable :: qmax
integer, dimension(:), allocatable :: beam_index
integer, dimension(:), allocatable :: incoming
integer, dimension(:), allocatable :: radiated
integer, dimension(:), allocatable :: outgoing
integer, dimension(:), allocatable :: par_index
integer, dimension(:), allocatable :: par_primary
contains
<<SF base: sf int: TBP>>
end type sf_int_t
@ %def sf_int_t
@ Status codes. The codes that refer to links, masks, and
connections, apply to structure-function chains only.
The status codes are public.
<<SF base: parameters>>=
integer, parameter, public :: SF_UNDEFINED = 0
integer, parameter, public :: SF_INITIAL = 1
integer, parameter, public :: SF_DONE_LINKS = 2
integer, parameter, public :: SF_FAILED_MASK = 3
integer, parameter, public :: SF_DONE_MASK = 4
integer, parameter, public :: SF_FAILED_CONNECTIONS = 5
integer, parameter, public :: SF_DONE_CONNECTIONS = 6
integer, parameter, public :: SF_SEED_KINEMATICS = 10
integer, parameter, public :: SF_FAILED_KINEMATICS = 11
integer, parameter, public :: SF_DONE_KINEMATICS = 12
integer, parameter, public :: SF_FAILED_EVALUATION = 13
integer, parameter, public :: SF_EVALUATED = 20
@ %def SF_UNDEFINED SF_INITIAL
@ %def SF_DONE_LINKS SF_DONE_MASK SF_DONE_CONNECTIONS
@ %def SF_DONE_KINEMATICS SF_EVALUATED
@ %def SF_FAILED_MASK SF_FAILED_CONNECTIONS
@ %def SF_FAILED_KINEMATICS SF_FAILED_EVALUATION
@ Write a string version of the status code:
<<SF base: procedures>>=
subroutine write_sf_status (status, u)
integer, intent(in) :: status
integer, intent(in) :: u
select case (status)
case (SF_UNDEFINED)
write (u, "(1x,'[',A,']')") "undefined"
case (SF_INITIAL)
write (u, "(1x,'[',A,']')") "initialized"
case (SF_DONE_LINKS)
write (u, "(1x,'[',A,']')") "links set"
case (SF_FAILED_MASK)
write (u, "(1x,'[',A,']')") "mask mismatch"
case (SF_DONE_MASK)
write (u, "(1x,'[',A,']')") "mask set"
case (SF_FAILED_CONNECTIONS)
write (u, "(1x,'[',A,']')") "connections failed"
case (SF_DONE_CONNECTIONS)
write (u, "(1x,'[',A,']')") "connections set"
case (SF_SEED_KINEMATICS)
write (u, "(1x,'[',A,']')") "incoming momenta set"
case (SF_FAILED_KINEMATICS)
write (u, "(1x,'[',A,']')") "kinematics failed"
case (SF_DONE_KINEMATICS)
write (u, "(1x,'[',A,']')") "kinematics set"
case (SF_FAILED_EVALUATION)
write (u, "(1x,'[',A,']')") "evaluation failed"
case (SF_EVALUATED)
write (u, "(1x,'[',A,']')") "evaluated"
end select
end subroutine write_sf_status
@ %def write_sf_status
@ This is the basic output routine. Display status and interaction.
<<SF base: sf int: TBP>>=
procedure :: base_write => sf_int_base_write
<<SF base: procedures>>=
subroutine sf_int_base_write (object, unit, testflag)
class(sf_int_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "SF instance:"
call write_sf_status (object%status, u)
if (allocated (object%beam_index)) &
write (u, "(3x,A,2(1x,I0))") "beam =", object%beam_index
if (allocated (object%incoming)) &
write (u, "(3x,A,2(1x,I0))") "incoming =", object%incoming
if (allocated (object%radiated)) &
write (u, "(3x,A,2(1x,I0))") "radiated =", object%radiated
if (allocated (object%outgoing)) &
write (u, "(3x,A,2(1x,I0))") "outgoing =", object%outgoing
if (allocated (object%par_index)) &
write (u, "(3x,A,2(1x,I0))") "parameter =", object%par_index
if (object%qmin_defined) &
write (u, "(3x,A,1x," // FMT_19 // ")") "q_min =", object%qmin
if (object%qmax_defined) &
write (u, "(3x,A,1x," // FMT_19 // ")") "q_max =", object%qmax
call object%interaction_t%basic_write (u, testflag = testflag)
end subroutine sf_int_base_write
@ %def sf_int_base_write
@ The type string identifies the structure function class, and possibly more
details about the structure function.
<<SF base: sf int: TBP>>=
procedure (sf_int_type_string), deferred :: type_string
<<SF base: interfaces>>=
abstract interface
function sf_int_type_string (object) result (string)
import
class(sf_int_t), intent(in) :: object
type(string_t) :: string
end function sf_int_type_string
end interface
@ %def sf_int_type_string
@ Output of the concrete object. We should not forget to call the
output routine for the base type.
<<SF base: sf int: TBP>>=
procedure (sf_int_write), deferred :: write
<<SF base: interfaces>>=
abstract interface
subroutine sf_int_write (object, unit, testflag)
import
class(sf_int_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
end subroutine sf_int_write
end interface
@ %def sf_int_write
@ Basic initialization: set the invariant masses for the particles and
initialize the interaction. The caller should then add states to the
interaction and freeze it.
The dimension of the mask should be equal to the sum of the dimensions
of the mass-squared arrays, which determine incoming, radiated, and
outgoing particles, respectively.
Optionally, we can define minimum and maximum values for the momentum
transfer to the outgoing particle(s). If all masses are zero, this is
actually required for non-collinear splitting.
<<SF base: sf int: TBP>>=
procedure :: base_init => sf_int_base_init
<<SF base: procedures>>=
subroutine sf_int_base_init &
(sf_int, mask, mi2, mr2, mo2, qmin, qmax, hel_lock)
class(sf_int_t), intent(out) :: sf_int
type (quantum_numbers_mask_t), dimension(:), intent(in) :: mask
real(default), dimension(:), intent(in) :: mi2, mr2, mo2
real(default), dimension(:), intent(in), optional :: qmin, qmax
integer, dimension(:), intent(in), optional :: hel_lock
allocate (sf_int%mi2 (size (mi2)))
sf_int%mi2 = mi2
allocate (sf_int%mr2 (size (mr2)))
sf_int%mr2 = mr2
allocate (sf_int%mo2 (size (mo2)))
sf_int%mo2 = mo2
if (present (qmin)) then
sf_int%qmin_defined = .true.
allocate (sf_int%qmin (size (qmin)))
sf_int%qmin = qmin
end if
if (present (qmax)) then
sf_int%qmax_defined = .true.
allocate (sf_int%qmax (size (qmax)))
sf_int%qmax = qmax
end if
call sf_int%interaction_t%basic_init &
(size (mi2), 0, size (mr2) + size (mo2), &
mask = mask, hel_lock = hel_lock, set_relations = .true.)
end subroutine sf_int_base_init
@ %def sf_int_base_init
@ Set the indices of the incoming, radiated, and outgoing particles,
respectively.
<<SF base: sf int: TBP>>=
procedure :: set_incoming => sf_int_set_incoming
procedure :: set_radiated => sf_int_set_radiated
procedure :: set_outgoing => sf_int_set_outgoing
<<SF base: procedures>>=
subroutine sf_int_set_incoming (sf_int, incoming)
class(sf_int_t), intent(inout) :: sf_int
integer, dimension(:), intent(in) :: incoming
allocate (sf_int%incoming (size (incoming)))
sf_int%incoming = incoming
end subroutine sf_int_set_incoming
subroutine sf_int_set_radiated (sf_int, radiated)
class(sf_int_t), intent(inout) :: sf_int
integer, dimension(:), intent(in) :: radiated
allocate (sf_int%radiated (size (radiated)))
sf_int%radiated = radiated
end subroutine sf_int_set_radiated
subroutine sf_int_set_outgoing (sf_int, outgoing)
class(sf_int_t), intent(inout) :: sf_int
integer, dimension(:), intent(in) :: outgoing
allocate (sf_int%outgoing (size (outgoing)))
sf_int%outgoing = outgoing
end subroutine sf_int_set_outgoing
@ %def sf_int_set_incoming
@ %def sf_int_set_radiated
@ %def sf_int_set_outgoing
@ Initialization. This proceeds via an abstract data object, which
for the actual implementation should have the matching concrete type.
Since all implementations have the same signature, we can prepare a
deferred procedure. The data object will become the target of a
corresponding pointer within the [[sf_int_t]] implementation.
This should call the previous procedure.
<<SF base: sf int: TBP>>=
procedure (sf_int_init), deferred :: init
<<SF base: interfaces>>=
abstract interface
subroutine sf_int_init (sf_int, data)
import
class(sf_int_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
end subroutine sf_int_init
end interface
@ %def sf_int_init
@ Complete initialization. This routine contains initializations that can
only be performed after the interaction object got its final shape, i.e.,
redundant helicities have been eliminated by matching with beams and process.
The default implementation does nothing.
The [[target]] attribute is formally required since some overriding
implementations use a temporary pointer (iterator) to the state-matrix
component. It doesn't appear to make a real difference, though.
<<SF base: sf int: TBP>>=
procedure :: setup_constants => sf_int_setup_constants
<<SF base: procedures>>=
subroutine sf_int_setup_constants (sf_int)
class(sf_int_t), intent(inout), target :: sf_int
end subroutine sf_int_setup_constants
@ %def sf_int_setup_constants
@ Set beam indices, i.e., the beam(s) on which
this structure function applies.
<<SF base: sf int: TBP>>=
procedure :: set_beam_index => sf_int_set_beam_index
<<SF base: procedures>>=
subroutine sf_int_set_beam_index (sf_int, beam_index)
class(sf_int_t), intent(inout) :: sf_int
integer, dimension(:), intent(in) :: beam_index
allocate (sf_int%beam_index (size (beam_index)))
sf_int%beam_index = beam_index
end subroutine sf_int_set_beam_index
@ %def sf_int_set_beam_index
@ Set parameter indices, indicating which MC input parameters are to
be used for evaluating this structure function.
<<SF base: sf int: TBP>>=
procedure :: set_par_index => sf_int_set_par_index
<<SF base: procedures>>=
subroutine sf_int_set_par_index (sf_int, par_index)
class(sf_int_t), intent(inout) :: sf_int
integer, dimension(:), intent(in) :: par_index
allocate (sf_int%par_index (size (par_index)))
sf_int%par_index = par_index
end subroutine sf_int_set_par_index
@ %def sf_int_set_par_index
@ Initialize the structure-function kinematics, setting incoming
momenta. We assume that array shapes match.
Three versions. The first version relies on the momenta being linked
to another interaction. The second version sets the momenta
explicitly. In the third version, we first compute momenta for the
specified energies and store those.
<<SF base: sf int: TBP>>=
generic :: seed_kinematics => sf_int_receive_momenta
generic :: seed_kinematics => sf_int_seed_momenta
generic :: seed_kinematics => sf_int_seed_energies
procedure :: sf_int_receive_momenta
procedure :: sf_int_seed_momenta
procedure :: sf_int_seed_energies
<<SF base: procedures>>=
subroutine sf_int_receive_momenta (sf_int)
class(sf_int_t), intent(inout) :: sf_int
if (sf_int%status >= SF_INITIAL) then
call sf_int%receive_momenta ()
sf_int%status = SF_SEED_KINEMATICS
end if
end subroutine sf_int_receive_momenta
subroutine sf_int_seed_momenta (sf_int, k)
class(sf_int_t), intent(inout) :: sf_int
type(vector4_t), dimension(:), intent(in) :: k
if (sf_int%status >= SF_INITIAL) then
call sf_int%set_momenta (k, outgoing=.false.)
sf_int%status = SF_SEED_KINEMATICS
end if
end subroutine sf_int_seed_momenta
subroutine sf_int_seed_energies (sf_int, E)
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: E
type(vector4_t), dimension(:), allocatable :: k
integer :: j
if (sf_int%status >= SF_INITIAL) then
allocate (k (size (E)))
if (all (E**2 >= sf_int%mi2)) then
do j = 1, size (E)
k(j) = vector4_moving (E(j), &
(3-2*j) * sqrt (E(j)**2 - sf_int%mi2(j)), 3)
end do
call sf_int%seed_kinematics (k)
end if
end if
end subroutine sf_int_seed_energies
@ %def sf_int_seed_momenta
@ %def sf_int_seed_energies
@ Tell if in generator mode. By default, this is false. To be
overridden where appropriate; we may refer to the [[is_generator]]
method of the [[data]] component in the concrete type.
<<SF base: sf int: TBP>>=
procedure :: is_generator => sf_int_is_generator
<<SF base: procedures>>=
function sf_int_is_generator (sf_int) result (flag)
class(sf_int_t), intent(in) :: sf_int
logical :: flag
flag = .false.
end function sf_int_is_generator
@ %def sf_int_is_generator
@ Generate free parameters [[r]]. Parameters are free if they do not
correspond to integration parameters (i.e., are bound), but are
generated by the structure function object itself. By default, all
parameters are bound, and the output values of this procedure will be
discarded. With free parameters, we have to override this procedure.
The value [[x_free]] is the renormalization factor of the total energy
that corresponds to the free parameters. If there are no free
parameters, the procedure will not change its value, which starts as
unity. Otherwise, the fraction is typically decreased, but may also
be increased in some cases.
<<SF base: sf int: TBP>>=
procedure :: generate_free => sf_int_generate_free
<<SF base: procedures>>=
subroutine sf_int_generate_free (sf_int, r, rb, x_free)
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(inout) :: x_free
r = 0
rb= 1
end subroutine sf_int_generate_free
@ %def sf_int_generate_free
@ Complete the structure-function kinematics, derived from an input
parameter (array) $r$ between 0 and 1. The interaction momenta are
calculated, and we return $x$ (the momentum fraction), and $f$ (the
Jacobian factor for the map $r\to x$), if [[map]] is set.
If the [[map]] flag is unset, $r$ and $x$ values will coincide, and $f$ will
become unity. If it is set, the structure-function implementation chooses a
convenient mapping from $r$ to $x$ with Jacobian $f$.
In the [[inverse_kinematics]] variant, we exchange the intent of [[x]]
and [[r]]. The momenta are calculated only if the optional flag
[[set_momenta]] is present and set. Internal parameters of [[sf_int]]
are calculated only if the optional flag [[set_x]] is present and set.
Update 2018-08-22: Throughout this algorithm, we now carry
[[xb]]=$1-x$ together with [[x]] values, as we did for [[r]] before.
This allows us to handle unstable endpoint numerics wherever
necessary. The only place where the changes actually did matter was
for inverse kinematics in the ISR setup, with a very soft photon, but
it might be most sensible to apply the extension with [[xb]] everywhere.
<<SF base: sf int: TBP>>=
procedure (sf_int_complete_kinematics), deferred :: complete_kinematics
procedure (sf_int_inverse_kinematics), deferred :: inverse_kinematics
<<SF base: interfaces>>=
abstract interface
subroutine sf_int_complete_kinematics (sf_int, x, xb, f, r, rb, map)
import
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
end subroutine sf_int_complete_kinematics
end interface
abstract interface
subroutine sf_int_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
import
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
end subroutine sf_int_inverse_kinematics
end interface
@ %def sf_int_complete_kinematics
@ %def sf_int_inverse_kinematics
@ Single splitting: compute momenta, given $x$ input parameters. We
assume that the incoming momentum is set. The status code is set to
[[SF_FAILED_KINEMATICS]] if
the $x$ array does not correspond to a valid momentum configuration.
Otherwise, it is updated to [[SF_DONE_KINEMATICS]].
We force the outgoing particle on-shell. The on-shell projection is
determined by the [[on_shell_mode]]. The radiated particle should already be
on shell.
<<SF base: sf int: TBP>>=
procedure :: split_momentum => sf_int_split_momentum
<<SF base: procedures>>=
subroutine sf_int_split_momentum (sf_int, x, xb)
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
type(splitting_data_t) :: sd
real(default) :: E1, E2
logical :: fail
if (sf_int%status >= SF_SEED_KINEMATICS) then
k = sf_int%get_momentum (1)
call sd%init (k, &
sf_int%mi2(1), sf_int%mr2(1), sf_int%mo2(1), &
collinear = size (x) == 1)
call sd%set_t_bounds (x(1), xb(1))
select case (size (x))
case (1)
case (3)
if (sf_int%qmax_defined) then
if (sf_int%qmin_defined) then
call sd%sample_t (x(2), &
t0 = - sf_int%qmax(1) ** 2, t1 = - sf_int%qmin(1) ** 2)
else
call sd%sample_t (x(2), &
t0 = - sf_int%qmax(1) ** 2)
end if
else
if (sf_int%qmin_defined) then
call sd%sample_t (x(2), t1 = - sf_int%qmin(1) ** 2)
else
call sd%sample_t (x(2))
end if
end if
call sd%sample_phi (x(3))
case default
call msg_bug ("Structure function: impossible number of parameters")
end select
q = sd%split_momentum (k)
call on_shell (q, [sf_int%mr2, sf_int%mo2], &
sf_int%on_shell_mode)
call sf_int%set_momenta (q, outgoing=.true.)
E1 = energy (q(1))
E2 = energy (q(2))
fail = E1 < 0 .or. E2 < 0 &
.or. E1 ** 2 < sf_int%mr2(1) &
.or. E2 ** 2 < sf_int%mo2(1)
if (fail) then
sf_int%status = SF_FAILED_KINEMATICS
else
sf_int%status = SF_DONE_KINEMATICS
end if
end if
end subroutine sf_int_split_momentum
@ %def sf_test_split_momentum
@ Pair splitting: two incoming momenta, two radiated, two outgoing.
This is simple because we insist on all momenta being collinear.
<<SF base: sf int: TBP>>=
procedure :: split_momenta => sf_int_split_momenta
<<SF base: procedures>>=
subroutine sf_int_split_momenta (sf_int, x, xb)
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
type(vector4_t), dimension(2) :: k
type(vector4_t), dimension(4) :: q
real(default), dimension(4) :: E
logical :: fail
if (sf_int%status >= SF_SEED_KINEMATICS) then
select case (size (x))
case (2)
case default
call msg_bug ("Pair structure function: recoil requested &
&but not implemented yet")
end select
k(1) = sf_int%get_momentum (1)
k(2) = sf_int%get_momentum (2)
q(1:2) = xb * k
q(3:4) = x * k
select case (size (sf_int%mr2))
case (2)
call on_shell (q, &
[sf_int%mr2(1), sf_int%mr2(2), &
sf_int%mo2(1), sf_int%mo2(2)], &
sf_int%on_shell_mode)
call sf_int%set_momenta (q, outgoing=.true.)
E = energy (q)
fail = any (E < 0) &
.or. any (E(1:2) ** 2 < sf_int%mr2) &
.or. any (E(3:4) ** 2 < sf_int%mo2)
case default; call msg_bug ("split momenta: incorrect use")
end select
if (fail) then
sf_int%status = SF_FAILED_KINEMATICS
else
sf_int%status = SF_DONE_KINEMATICS
end if
end if
end subroutine sf_int_split_momenta
@ %def sf_int_split_momenta
@ Pair spectrum: the reduced version of the previous splitting,
without radiated momenta.
<<SF base: sf int: TBP>>=
procedure :: reduce_momenta => sf_int_reduce_momenta
<<SF base: procedures>>=
subroutine sf_int_reduce_momenta (sf_int, x)
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
type(vector4_t), dimension(2) :: k
type(vector4_t), dimension(2) :: q
real(default), dimension(2) :: E
logical :: fail
if (sf_int%status >= SF_SEED_KINEMATICS) then
select case (size (x))
case (2)
case default
call msg_bug ("Pair spectrum: recoil requested &
&but not implemented yet")
end select
k(1) = sf_int%get_momentum (1)
k(2) = sf_int%get_momentum (2)
q = x * k
call on_shell (q, &
[sf_int%mo2(1), sf_int%mo2(2)], &
sf_int%on_shell_mode)
call sf_int%set_momenta (q, outgoing=.true.)
E = energy (q)
fail = any (E < 0) &
.or. any (E ** 2 < sf_int%mo2)
if (fail) then
sf_int%status = SF_FAILED_KINEMATICS
else
sf_int%status = SF_DONE_KINEMATICS
end if
end if
end subroutine sf_int_reduce_momenta
@ %def sf_int_reduce_momenta
@ The inverse procedure: we compute the [[x]] array from the momentum
configuration. In an overriding TBP, we may also set internal data
that depend on this, for convenience.
NOTE: Here and above, the single-particle case is treated in detail,
allowing for non-collinearity and non-vanishing masses and nontrivial
momentum-transfer bounds. For the pair case, we currently implement
only collinear splitting and assume massless particles. This should
be improved.
Update 2017-08-22: recover also [[xb]], using the updated [[recover]]
method of the splitting-data object. Th
<<SF base: sf int: TBP>>=
procedure :: recover_x => sf_int_recover_x
procedure :: base_recover_x => sf_int_recover_x
<<SF base: procedures>>=
subroutine sf_int_recover_x (sf_int, x, xb, x_free)
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(inout), optional :: x_free
type(vector4_t), dimension(:), allocatable :: k
type(vector4_t), dimension(:), allocatable :: q
type(splitting_data_t) :: sd
if (sf_int%status >= SF_SEED_KINEMATICS) then
allocate (k (sf_int%interaction_t%get_n_in ()))
allocate (q (sf_int%interaction_t%get_n_out ()))
k = sf_int%get_momenta (outgoing=.false.)
q = sf_int%get_momenta (outgoing=.true.)
select case (size (k))
case (1)
call sd%init (k(1), &
sf_int%mi2(1), sf_int%mr2(1), sf_int%mo2(1), &
collinear = size (x) == 1)
call sd%recover (k(1), q, sf_int%on_shell_mode)
x(1) = sd%get_x ()
xb(1) = sd%get_xb ()
select case (size (x))
case (1)
case (3)
if (sf_int%qmax_defined) then
if (sf_int%qmin_defined) then
call sd%inverse_t (x(2), &
t0 = - sf_int%qmax(1) ** 2, t1 = - sf_int%qmin(1) ** 2)
else
call sd%inverse_t (x(2), &
t0 = - sf_int%qmax(1) ** 2)
end if
else
if (sf_int%qmin_defined) then
call sd%inverse_t (x(2), t1 = - sf_int%qmin(1) ** 2)
else
call sd%inverse_t (x(2))
end if
end if
call sd%inverse_phi (x(3))
xb(2:3) = 1 - x(2:3)
case default
call msg_bug ("Structure function: impossible number &
&of parameters")
end select
case (2)
select case (size (x))
case (2)
case default
call msg_bug ("Pair structure function: recoil requested &
&but not implemented yet")
end select
select case (sf_int%on_shell_mode)
case (KEEP_ENERGY)
select case (size (q))
case (4)
x = energy (q(3:4)) / energy (k)
xb= energy (q(1:2)) / energy (k)
case (2)
x = energy (q) / energy (k)
xb= 1 - x
end select
case (KEEP_MOMENTUM)
select case (size (q))
case (4)
x = longitudinal_part (q(3:4)) / longitudinal_part (k)
xb= longitudinal_part (q(1:2)) / longitudinal_part (k)
case (2)
x = longitudinal_part (q) / longitudinal_part (k)
xb= 1 - x
end select
end select
end select
end if
end subroutine sf_int_recover_x
@ %def sf_int_recover_x
@ Apply the structure function, i.e., evaluate the interaction. For
the calculation, we may use the stored momenta, any further
information stored inside the [[sf_int]] implementation during
kinematics setup, and the given energy scale. It may happen that for
the given kinematics the value is not defined. This should be
indicated by the status code.
<<SF base: sf int: TBP>>=
procedure (sf_int_apply), deferred :: apply
<<SF base: interfaces>>=
abstract interface
- subroutine sf_int_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine sf_int_apply (sf_int, scale, rescale, i_sub)
import
class(sf_int_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
end subroutine sf_int_apply
end interface
@ %def sf_int_apply
@
\subsection{Accessing the structure function}
Return metadata. Once [[interaction_t]] is rewritten in OO, some of this will
be inherited.
-The number of outgoing is equal to the number of incoming particles. The
-radiated particles are the difference.
+The number of outgoing particles is equal to the number of incoming particles.
+The radiated particles are the difference.
<<SF base: sf int: TBP>>=
procedure :: get_n_in => sf_int_get_n_in
procedure :: get_n_rad => sf_int_get_n_rad
procedure :: get_n_out => sf_int_get_n_out
<<SF base: procedures>>=
pure function sf_int_get_n_in (object) result (n_in)
class(sf_int_t), intent(in) :: object
integer :: n_in
n_in = object%interaction_t%get_n_in ()
end function sf_int_get_n_in
pure function sf_int_get_n_rad (object) result (n_rad)
class(sf_int_t), intent(in) :: object
integer :: n_rad
n_rad = object%interaction_t%get_n_out () &
- object%interaction_t%get_n_in ()
end function sf_int_get_n_rad
pure function sf_int_get_n_out (object) result (n_out)
class(sf_int_t), intent(in) :: object
integer :: n_out
n_out = object%interaction_t%get_n_in ()
end function sf_int_get_n_out
@ %def sf_int_get_n_in
@ %def sf_int_get_n_rad
@ %def sf_int_get_n_out
@ Number of matrix element entries in the interaction:
<<SF base: sf int: TBP>>=
procedure :: get_n_states => sf_int_get_n_states
<<SF base: procedures>>=
function sf_int_get_n_states (sf_int) result (n_states)
class(sf_int_t), intent(in) :: sf_int
integer :: n_states
n_states = sf_int%get_n_matrix_elements ()
end function sf_int_get_n_states
@ %def sf_int_get_n_states
@ Return a specific state as a quantum-number array.
<<SF base: sf int: TBP>>=
procedure :: get_state => sf_int_get_state
<<SF base: procedures>>=
function sf_int_get_state (sf_int, i) result (qn)
class(sf_int_t), intent(in) :: sf_int
type(quantum_numbers_t), dimension(:), allocatable :: qn
integer, intent(in) :: i
allocate (qn (sf_int%get_n_tot ()))
qn = sf_int%get_quantum_numbers (i)
end function sf_int_get_state
@ %def sf_int_get_state
@ Return the matrix-element values for all states. We can assume that
the matrix elements are real, so we take the real part.
<<SF base: sf int: TBP>>=
procedure :: get_values => sf_int_get_values
<<SF base: procedures>>=
subroutine sf_int_get_values (sf_int, value)
class(sf_int_t), intent(in) :: sf_int
real(default), dimension(:), intent(out) :: value
integer :: i
if (sf_int%status >= SF_EVALUATED) then
do i = 1, size (value)
value(i) = real (sf_int%get_matrix_element (i))
end do
else
value = 0
end if
end subroutine sf_int_get_values
@ %def sf_int_get_values
@
\subsection{Direct calculations}
Compute a structure function value (array) directly, given an array of $x$
values and a scale. If the energy is also given, we initialize the
kinematics for that energy, otherwise take it from a previous run.
We assume that the [[E]] array has dimension [[n_in]], and the [[x]]
array has [[n_par]].
Note: the output x values ([[xx]] and [[xxb]]) are unused in this use case.
<<SF base: sf int: TBP>>=
procedure :: compute_values => sf_int_compute_values
<<SF base: procedures>>=
subroutine sf_int_compute_values (sf_int, value, x, xb, scale, E)
class(sf_int_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: value
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(in) :: scale
real(default), dimension(:), intent(in), optional :: E
real(default), dimension(size (x)) :: xx, xxb
real(default) :: f
if (present (E)) call sf_int%seed_kinematics (E)
if (sf_int%status >= SF_SEED_KINEMATICS) then
call sf_int%complete_kinematics (xx, xxb, f, x, xb, map=.false.)
call sf_int%apply (scale)
call sf_int%get_values (value)
value = value * f
else
value = 0
end if
end subroutine sf_int_compute_values
@ %def sf_int_compute_values
@ Compute just a single value for one of the states, i.e., throw the
others away.
<<SF base: sf int: TBP>>=
procedure :: compute_value => sf_int_compute_value
<<SF base: procedures>>=
subroutine sf_int_compute_value &
(sf_int, i_state, value, x, xb, scale, E)
class(sf_int_t), intent(inout) :: sf_int
integer, intent(in) :: i_state
real(default), intent(out) :: value
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(in) :: scale
real(default), dimension(:), intent(in), optional :: E
real(default), dimension(:), allocatable :: value_array
if (sf_int%status >= SF_INITIAL) then
allocate (value_array (sf_int%get_n_states ()))
call sf_int%compute_values (value_array, x, xb, scale, E)
value = value_array(i_state)
else
value = 0
end if
end subroutine sf_int_compute_value
@ %def sf_int_compute_value
@
\subsection{Structure-function instance}
This is a wrapper for [[sf_int_t]] objects, such that we can
build an array with different structure-function types. The
structure-function contains an array (a sequence) of [[sf_int_t]]
objects.
The object, it holds the evaluator that connects the preceding part of the
structure-function chain to the current interaction.
It also stores the input and output parameter values for the
contained structure function. The [[r]] array has a second dimension,
corresponding to the mapping channels in a multi-channel
configuration. There is a Jacobian entry [[f]] for each channel. The
corresponding logical array [[mapping]] tells whether we apply the
mapping appropriate for the current structure function in this channel.
The [[x]] parameter values (energy fractions) are common to all
channels.
<<SF base: types>>=
type :: sf_instance_t
class(sf_int_t), allocatable :: int
type(evaluator_t) :: eval
real(default), dimension(:,:), allocatable :: r
real(default), dimension(:,:), allocatable :: rb
real(default), dimension(:), allocatable :: f
logical, dimension(:), allocatable :: m
real(default), dimension(:), allocatable :: x
real(default), dimension(:), allocatable :: xb
end type sf_instance_t
@ %def sf_instance_t
@
\subsection{Structure-function chain}
A chain is an array of structure functions [[sf]], initiated by a beam setup.
We do not use this directly for evaluation, but create instances with
copies of the contained interactions.
[[n_par]] is the total number of parameters that is necessary for
completely determining the structure-function chain. [[n_bound]] is
the number of MC input parameters that are requested from the
integrator. The difference of [[n_par]] and [[n_bound]] is the number
of free parameters, which are generated by a structure-function
object in generator mode.
<<SF base: public>>=
public :: sf_chain_t
<<SF base: types>>=
type, extends (beam_t) :: sf_chain_t
type(beam_data_t), pointer :: beam_data => null ()
integer :: n_in = 0
integer :: n_strfun = 0
integer :: n_par = 0
integer :: n_bound = 0
type(sf_instance_t), dimension(:), allocatable :: sf
logical :: trace_enable = .false.
integer :: trace_unit = 0
contains
<<SF base: sf chain: TBP>>
end type sf_chain_t
@ %def sf_chain_t
@ Finalizer.
<<SF base: sf chain: TBP>>=
procedure :: final => sf_chain_final
<<SF base: procedures>>=
subroutine sf_chain_final (object)
class(sf_chain_t), intent(inout) :: object
integer :: i
call object%final_tracing ()
if (allocated (object%sf)) then
do i = 1, size (object%sf, 1)
associate (sf => object%sf(i))
if (allocated (sf%int)) then
call sf%int%final ()
end if
end associate
end do
end if
call beam_final (object%beam_t)
end subroutine sf_chain_final
@ %def sf_chain_final
@ Output.
<<SF base: sf chain: TBP>>=
procedure :: write => sf_chain_write
<<SF base: procedures>>=
subroutine sf_chain_write (object, unit)
class(sf_chain_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit)
write (u, "(1x,A)") "Incoming particles / structure-function chain:"
if (associated (object%beam_data)) then
write (u, "(3x,A,I0)") "n_in = ", object%n_in
write (u, "(3x,A,I0)") "n_strfun = ", object%n_strfun
write (u, "(3x,A,I0)") "n_par = ", object%n_par
if (object%n_par /= object%n_bound) then
write (u, "(3x,A,I0)") "n_bound = ", object%n_bound
end if
call object%beam_data%write (u)
call write_separator (u)
call beam_write (object%beam_t, u)
if (allocated (object%sf)) then
do i = 1, object%n_strfun
associate (sf => object%sf(i))
call write_separator (u)
if (allocated (sf%int)) then
call sf%int%write (u)
else
write (u, "(1x,A)") "SF instance: [undefined]"
end if
end associate
end do
end if
else
write (u, "(3x,A)") "[undefined]"
end if
end subroutine sf_chain_write
@ %def sf_chain_write
@ Initialize: setup beams. The [[beam_data]] target must remain valid
for the lifetime of the chain, since we just establish a pointer. The
structure-function configuration array is used to initialize the
individual structure-function entries. The target attribute is needed
because the [[sf_int]] entries establish pointers to the configuration data.
<<SF base: sf chain: TBP>>=
procedure :: init => sf_chain_init
<<SF base: procedures>>=
subroutine sf_chain_init (sf_chain, beam_data, sf_config)
class(sf_chain_t), intent(out) :: sf_chain
type(beam_data_t), intent(in), target :: beam_data
type(sf_config_t), dimension(:), intent(in), optional, target :: sf_config
integer :: i
sf_chain%beam_data => beam_data
sf_chain%n_in = beam_data%get_n_in ()
call beam_init (sf_chain%beam_t, beam_data)
if (present (sf_config)) then
sf_chain%n_strfun = size (sf_config)
allocate (sf_chain%sf (sf_chain%n_strfun))
do i = 1, sf_chain%n_strfun
call sf_chain%set_strfun (i, sf_config(i)%i, sf_config(i)%data)
end do
end if
end subroutine sf_chain_init
@ %def sf_chain_init
@ Receive the beam momenta from a source to which the beam interaction
is linked.
<<SF base: sf chain: TBP>>=
procedure :: receive_beam_momenta => sf_chain_receive_beam_momenta
<<SF base: procedures>>=
subroutine sf_chain_receive_beam_momenta (sf_chain)
class(sf_chain_t), intent(inout), target :: sf_chain
type(interaction_t), pointer :: beam_int
beam_int => sf_chain%get_beam_int_ptr ()
call beam_int%receive_momenta ()
end subroutine sf_chain_receive_beam_momenta
@ %def sf_chain_receive_beam_momenta
@ Explicitly set the beam momenta.
<<SF base: sf chain: TBP>>=
procedure :: set_beam_momenta => sf_chain_set_beam_momenta
<<SF base: procedures>>=
subroutine sf_chain_set_beam_momenta (sf_chain, p)
class(sf_chain_t), intent(inout) :: sf_chain
type(vector4_t), dimension(:), intent(in) :: p
call beam_set_momenta (sf_chain%beam_t, p)
end subroutine sf_chain_set_beam_momenta
@ %def sf_chain_set_beam_momenta
@ Set a structure-function entry. We use the [[data]] input to
allocate the [[int]] structure-function instance with appropriate
type, then initialize the entry. The entry establishes a pointer to
[[data]].
The index [[i]] is the structure-function index in the chain.
<<SF base: sf chain: TBP>>=
procedure :: set_strfun => sf_chain_set_strfun
<<SF base: procedures>>=
subroutine sf_chain_set_strfun (sf_chain, i, beam_index, data)
class(sf_chain_t), intent(inout) :: sf_chain
integer, intent(in) :: i
integer, dimension(:), intent(in) :: beam_index
class(sf_data_t), intent(in), target :: data
integer :: n_par, j
n_par = data%get_n_par ()
call data%allocate_sf_int (sf_chain%sf(i)%int)
associate (sf_int => sf_chain%sf(i)%int)
call sf_int%init (data)
call sf_int%set_beam_index (beam_index)
call sf_int%set_par_index &
([(j, j = sf_chain%n_par + 1, sf_chain%n_par + n_par)])
sf_chain%n_par = sf_chain%n_par + n_par
if (.not. data%is_generator ()) then
sf_chain%n_bound = sf_chain%n_bound + n_par
end if
end associate
end subroutine sf_chain_set_strfun
@ %def sf_chain_set_strfun
@ Return the number of structure-function parameters.
<<SF base: sf chain: TBP>>=
procedure :: get_n_par => sf_chain_get_n_par
procedure :: get_n_bound => sf_chain_get_n_bound
<<SF base: procedures>>=
function sf_chain_get_n_par (sf_chain) result (n)
class(sf_chain_t), intent(in) :: sf_chain
integer :: n
n = sf_chain%n_par
end function sf_chain_get_n_par
function sf_chain_get_n_bound (sf_chain) result (n)
class(sf_chain_t), intent(in) :: sf_chain
integer :: n
n = sf_chain%n_bound
end function sf_chain_get_n_bound
@ %def sf_chain_get_n_par
@ %def sf_chain_get_n_bound
@ Return a pointer to the beam interaction.
<<SF base: sf chain: TBP>>=
procedure :: get_beam_int_ptr => sf_chain_get_beam_int_ptr
<<SF base: procedures>>=
function sf_chain_get_beam_int_ptr (sf_chain) result (int)
type(interaction_t), pointer :: int
class(sf_chain_t), intent(in), target :: sf_chain
int => beam_get_int_ptr (sf_chain%beam_t)
end function sf_chain_get_beam_int_ptr
@ %def sf_chain_get_beam_int_ptr
@ Enable the trace feature: record structure function data (input
parameters, $x$ values, evaluation result) to an external file.
<<SF base: sf chain: TBP>>=
procedure :: setup_tracing => sf_chain_setup_tracing
procedure :: final_tracing => sf_chain_final_tracing
<<SF base: procedures>>=
subroutine sf_chain_setup_tracing (sf_chain, file)
class(sf_chain_t), intent(inout) :: sf_chain
type(string_t), intent(in) :: file
if (sf_chain%n_strfun > 0) then
sf_chain%trace_enable = .true.
sf_chain%trace_unit = free_unit ()
open (sf_chain%trace_unit, file = char (file), action = "write", &
status = "replace")
call sf_chain%write_trace_header ()
else
call msg_error ("Beam structure: no structure functions, tracing &
&disabled")
end if
end subroutine sf_chain_setup_tracing
subroutine sf_chain_final_tracing (sf_chain)
class(sf_chain_t), intent(inout) :: sf_chain
if (sf_chain%trace_enable) then
close (sf_chain%trace_unit)
sf_chain%trace_enable = .false.
end if
end subroutine sf_chain_final_tracing
@ %def sf_chain_setup_tracing
@ %def sf_chain_final_tracing
@ Write the header for the tracing file.
<<SF base: sf chain: TBP>>=
procedure :: write_trace_header => sf_chain_write_trace_header
<<SF base: procedures>>=
subroutine sf_chain_write_trace_header (sf_chain)
class(sf_chain_t), intent(in) :: sf_chain
integer :: u
if (sf_chain%trace_enable) then
u = sf_chain%trace_unit
write (u, "('# ',A)") "WHIZARD output: &
&structure-function sampling data"
write (u, "('# ',A,1x,I0)") "Number of sf records:", sf_chain%n_strfun
write (u, "('# ',A,1x,I0)") "Number of parameters:", sf_chain%n_par
write (u, "('# ',A)") "Columns: channel, p(n_par), x(n_par), f, Jac * f"
end if
end subroutine sf_chain_write_trace_header
@ %def sf_chain_write_trace_header
@ Write a record which collects the structure function data for the
current data point. For the selected channel, we print first the
input integration parameters, then the $x$ values, then the
structure-function value summed over all quantum numbers, then the
structure function value times the mapping Jacobian.
<<SF base: sf chain: TBP>>=
procedure :: trace => sf_chain_trace
<<SF base: procedures>>=
subroutine sf_chain_trace (sf_chain, c_sel, p, x, f, sf_sum)
class(sf_chain_t), intent(in) :: sf_chain
integer, intent(in) :: c_sel
real(default), dimension(:,:), intent(in) :: p
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: f
real(default), intent(in) :: sf_sum
real(default) :: sf_sum_pac, f_sf_sum_pac
integer :: u, i
if (sf_chain%trace_enable) then
u = sf_chain%trace_unit
write (u, "(1x,I0)", advance="no") c_sel
write (u, "(2x)", advance="no")
do i = 1, sf_chain%n_par
write (u, "(1x," // FMT_17 // ")", advance="no") p(i,c_sel)
end do
write (u, "(2x)", advance="no")
do i = 1, sf_chain%n_par
write (u, "(1x," // FMT_17 // ")", advance="no") x(i)
end do
write (u, "(2x)", advance="no")
sf_sum_pac = sf_sum
f_sf_sum_pac = f(c_sel) * sf_sum
call pacify (sf_sum_pac, 1.E-28_default)
call pacify (f_sf_sum_pac, 1.E-28_default)
write (u, "(2(1x," // FMT_17 // "))") sf_sum_pac, f_sf_sum_pac
end if
end subroutine sf_chain_trace
@ %def sf_chain_trace
@
\subsection{Chain instances}
A structure-function chain instance contains copies of the
interactions in the configuration chain, suitably linked to each other
and connected by evaluators.
After initialization, [[out_sf]] should point, for each beam, to the
last structure function that affects this beam. [[out_sf_i]] should
indicate the index of the corresponding outgoing particle within that
structure-function interaction.
Analogously, [[out_eval]] is the last evaluator in the
structure-function chain, which contains the complete set of outgoing
particles. [[out_eval_i]] should indicate the index of the outgoing
particles, within that evaluator, which will initiate the collision.
When calculating actual kinematics, we fill the [[p]], [[r]], and
[[x]] arrays and the [[f]] factor. The [[p]] array denotes the MC
input parameters as they come from the random-number generator. The
[[r]] array results from applying global mappings. The [[x]] array
results from applying structure-function local mappings. The $x$
values can be interpreted directly as momentum fractions (or angle
fractions, where recoil is involved). The [[f]] factor is the
Jacobian that results from applying all mappings.
Update 2017-08-22: carry and output all complements ([[pb]], [[rb]],
[[xb]]). Previously, [[xb]] was not included in the record, and the
output did not contain either. It does become more verbose, however.
The [[mapping]] entry may store a global mapping that is applied to a
combination of $x$ values and structure functions, as opposed to mappings that
affect only a single structure function. It is applied before the latter
mappings, in the transformation from the [[p]] array to the [[r]] array. For
parameters affected by this mapping, we should ensure that they are not
involved in a local mapping.
<<SF base: public>>=
public :: sf_chain_instance_t
<<SF base: types>>=
type, extends (beam_t) :: sf_chain_instance_t
type(sf_chain_t), pointer :: config => null ()
integer :: status = SF_UNDEFINED
type(sf_instance_t), dimension(:), allocatable :: sf
integer, dimension(:), allocatable :: out_sf
integer, dimension(:), allocatable :: out_sf_i
integer :: out_eval = 0
integer, dimension(:), allocatable :: out_eval_i
integer :: selected_channel = 0
real(default), dimension(:,:), allocatable :: p, pb
real(default), dimension(:,:), allocatable :: r, rb
real(default), dimension(:), allocatable :: f
real(default), dimension(:), allocatable :: x, xb
logical, dimension(:), allocatable :: bound
real(default) :: x_free = 1
type(sf_channel_t), dimension(:), allocatable :: channel
contains
<<SF base: sf chain instance: TBP>>
end type sf_chain_instance_t
@ %def sf_chain_instance_t
@ Finalizer.
<<SF base: sf chain instance: TBP>>=
procedure :: final => sf_chain_instance_final
<<SF base: procedures>>=
subroutine sf_chain_instance_final (object)
class(sf_chain_instance_t), intent(inout) :: object
integer :: i
if (allocated (object%sf)) then
do i = 1, size (object%sf, 1)
associate (sf => object%sf(i))
if (allocated (sf%int)) then
call sf%eval%final ()
call sf%int%final ()
end if
end associate
end do
end if
call beam_final (object%beam_t)
end subroutine sf_chain_instance_final
@ %def sf_chain_instance_final
@ Output.
Note: nagfor 5.3.1 appears to be slightly confused with the allocation
status. We check both for allocation and nonzero size.
<<SF base: sf chain instance: TBP>>=
procedure :: write => sf_chain_instance_write
<<SF base: procedures>>=
subroutine sf_chain_instance_write (object, unit, col_verbose)
class(sf_chain_instance_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: col_verbose
integer :: u, i, c
u = given_output_unit (unit)
write (u, "(1x,A)", advance="no") "Structure-function chain instance:"
call write_sf_status (object%status, u)
if (allocated (object%out_sf)) then
write (u, "(3x,A)", advance="no") "outgoing (interactions) ="
do i = 1, size (object%out_sf)
write (u, "(1x,I0,':',I0)", advance="no") &
object%out_sf(i), object%out_sf_i(i)
end do
write (u, *)
end if
if (object%out_eval /= 0) then
write (u, "(3x,A)", advance="no") "outgoing (evaluators) ="
do i = 1, size (object%out_sf)
write (u, "(1x,I0,':',I0)", advance="no") &
object%out_eval, object%out_eval_i(i)
end do
write (u, *)
end if
if (allocated (object%sf)) then
if (size (object%sf) /= 0) then
write (u, "(1x,A)") "Structure-function parameters:"
do c = 1, size (object%f)
write (u, "(1x,A,I0,A)", advance="no") "Channel #", c, ":"
if (c == object%selected_channel) then
write (u, "(1x,A)") "[selected]"
else
write (u, *)
end if
write (u, "(3x,A,9(1x,F9.7))") "p =", object%p(:,c)
write (u, "(3x,A,9(1x,F9.7))") "pb=", object%pb(:,c)
write (u, "(3x,A,9(1x,F9.7))") "r =", object%r(:,c)
write (u, "(3x,A,9(1x,F9.7))") "rb=", object%rb(:,c)
write (u, "(3x,A,9(1x,ES13.7))") "f =", object%f(c)
write (u, "(3x,A)", advance="no") "m ="
call object%channel(c)%write (u)
end do
write (u, "(3x,A,9(1x,F9.7))") "x =", object%x
write (u, "(3x,A,9(1x,F9.7))") "xb=", object%xb
if (.not. all (object%bound)) then
write (u, "(3x,A,9(1x,L1))") "bound =", object%bound
end if
end if
end if
call write_separator (u)
call beam_write (object%beam_t, u, col_verbose = col_verbose)
if (allocated (object%sf)) then
do i = 1, size (object%sf)
associate (sf => object%sf(i))
call write_separator (u)
if (allocated (sf%int)) then
if (allocated (sf%r)) then
write (u, "(1x,A)") "Structure-function parameters:"
do c = 1, size (sf%f)
write (u, "(1x,A,I0,A)", advance="no") "Channel #", c, ":"
if (c == object%selected_channel) then
write (u, "(1x,A)") "[selected]"
else
write (u, *)
end if
write (u, "(3x,A,9(1x,F9.7))") "r =", sf%r(:,c)
write (u, "(3x,A,9(1x,F9.7))") "rb=", sf%rb(:,c)
write (u, "(3x,A,9(1x,ES13.7))") "f =", sf%f(c)
write (u, "(3x,A,9(1x,L1,7x))") "m =", sf%m(c)
end do
write (u, "(3x,A,9(1x,F9.7))") "x =", sf%x
write (u, "(3x,A,9(1x,F9.7))") "xb=", sf%xb
end if
call sf%int%write(u)
if (.not. sf%eval%is_empty ()) then
call sf%eval%write (u, col_verbose = col_verbose)
end if
end if
end associate
end do
end if
end subroutine sf_chain_instance_write
@ %def sf_chain_instance_write
@ Initialize. This creates a copy of the interactions in the
configuration chain, assumed to be properly initialized. In the copy,
we allocate the [[p]] etc.\ arrays.
The brute-force assignment of the [[sf]] component would be
straightforward, but at least gfortran 4.6.3 would like a more
fine-grained copy. In any case, the copy is deep
as far as allocatables are concerned, but for the contained
[[interaction_t]] objects the copy is shallow, as long as we do not
bind defined assignment to the type. Therefore, we have to re-assign
the [[interaction_t]] components explicitly, this time calling the
proper defined assignment. Furthermore, we allocate the parameter
arrays for each structure function.
<<SF base: sf chain instance: TBP>>=
procedure :: init => sf_chain_instance_init
<<SF base: procedures>>=
subroutine sf_chain_instance_init (chain, config, n_channel)
class(sf_chain_instance_t), intent(out), target :: chain
type(sf_chain_t), intent(in), target :: config
integer, intent(in) :: n_channel
integer :: i, j
integer :: n_par_tot, n_par, n_strfun
chain%config => config
n_strfun = config%n_strfun
chain%beam_t = config%beam_t
allocate (chain%out_sf (config%n_in), chain%out_sf_i (config%n_in))
allocate (chain%out_eval_i (config%n_in))
chain%out_sf = 0
chain%out_sf_i = [(i, i = 1, config%n_in)]
chain%out_eval_i = chain%out_sf_i
n_par_tot = 0
if (n_strfun /= 0) then
allocate (chain%sf (n_strfun))
do i = 1, n_strfun
associate (sf => chain%sf(i))
allocate (sf%int, source=config%sf(i)%int)
sf%int%interaction_t = config%sf(i)%int%interaction_t
n_par = size (sf%int%par_index)
allocate (sf%r (n_par, n_channel)); sf%r = 0
allocate (sf%rb(n_par, n_channel)); sf%rb= 0
allocate (sf%f (n_channel)); sf%f = 0
allocate (sf%m (n_channel)); sf%m = .false.
allocate (sf%x (n_par)); sf%x = 0
allocate (sf%xb(n_par)); sf%xb= 0
n_par_tot = n_par_tot + n_par
end associate
end do
allocate (chain%p (n_par_tot, n_channel)); chain%p = 0
allocate (chain%pb(n_par_tot, n_channel)); chain%pb= 0
allocate (chain%r (n_par_tot, n_channel)); chain%r = 0
allocate (chain%rb(n_par_tot, n_channel)); chain%rb= 0
allocate (chain%f (n_channel)); chain%f = 0
allocate (chain%x (n_par_tot)); chain%x = 0
allocate (chain%xb(n_par_tot)); chain%xb= 0
call allocate_sf_channels &
(chain%channel, n_channel=n_channel, n_strfun=n_strfun)
end if
allocate (chain%bound (n_par_tot), source = .true.)
do i = 1, n_strfun
associate (sf => chain%sf(i))
if (sf%int%is_generator ()) then
do j = 1, size (sf%int%par_index)
chain%bound(sf%int%par_index(j)) = .false.
end do
end if
end associate
end do
chain%status = SF_INITIAL
end subroutine sf_chain_instance_init
@ %def sf_chain_instance_init
@ Manually select a channel.
<<SF base: sf chain instance: TBP>>=
procedure :: select_channel => sf_chain_instance_select_channel
<<SF base: procedures>>=
subroutine sf_chain_instance_select_channel (chain, channel)
class(sf_chain_instance_t), intent(inout) :: chain
integer, intent(in), optional :: channel
if (present (channel)) then
chain%selected_channel = channel
else
chain%selected_channel = 0
end if
end subroutine sf_chain_instance_select_channel
@ %def sf_chain_instance_select_channel
@ Copy a channel-mapping object to the structure-function
chain instance. We assume that assignment is sufficient, i.e., any
non-static components of the [[channel]] object are allocatable und
thus recursively copied.
After the copy, we extract the single-entry mappings and activate them
for the individual structure functions. If there is a multi-entry
mapping, we obtain the corresponding MC parameter indices and set them
in the copy of the channel object.
<<SF base: sf chain instance: TBP>>=
procedure :: set_channel => sf_chain_instance_set_channel
<<SF base: procedures>>=
subroutine sf_chain_instance_set_channel (chain, c, channel)
class(sf_chain_instance_t), intent(inout) :: chain
integer, intent(in) :: c
type(sf_channel_t), intent(in) :: channel
integer :: i, j, k
if (chain%status >= SF_INITIAL) then
chain%channel(c) = channel
j = 0
do i = 1, chain%config%n_strfun
associate (sf => chain%sf(i))
sf%m(c) = channel%is_single_mapping (i)
if (channel%is_multi_mapping (i)) then
do k = 1, size (sf%int%beam_index)
j = j + 1
call chain%channel(c)%set_par_index &
(j, sf%int%par_index(k))
end do
end if
end associate
end do
if (j /= chain%channel(c)%get_multi_mapping_n_par ()) then
print *, "index last filled = ", j
print *, "number of parameters = ", &
chain%channel(c)%get_multi_mapping_n_par ()
call msg_bug ("Structure-function setup: mapping index mismatch")
end if
chain%status = SF_INITIAL
end if
end subroutine sf_chain_instance_set_channel
@ %def sf_chain_instance_set_channel
@ Link the interactions in the chain. First, link the beam instance
to its template in the configuration chain, which should have the
appropriate momenta fixed.
Then, we follow the chain via the
arrays [[out_sf]] and [[out_sf_i]]. The arrays are (up to)
two-dimensional, the entries correspond to the beam particle(s).
For each beam, the entry [[out_sf]] points to the last interaction
that affected this beam, and [[out_sf_i]] is the
out-particle index within that interaction. For the initial beam,
[[out_sf]] is zero by definition.
For each entry in the chain, we scan the affected beams (one or two).
We look for [[out_sf]] and link the out-particle there to the
corresponding in-particle in the current interaction. Then, we update
the entry in [[out_sf]] and [[out_sf_i]] to point to the current
interaction.
<<SF base: sf chain instance: TBP>>=
procedure :: link_interactions => sf_chain_instance_link_interactions
<<SF base: procedures>>=
subroutine sf_chain_instance_link_interactions (chain)
class(sf_chain_instance_t), intent(inout), target :: chain
type(interaction_t), pointer :: int
integer :: i, j, b
if (chain%status >= SF_INITIAL) then
do b = 1, chain%config%n_in
int => beam_get_int_ptr (chain%beam_t)
call interaction_set_source_link (int, b, &
chain%config%beam_t, b)
end do
if (allocated (chain%sf)) then
do i = 1, size (chain%sf)
associate (sf_int => chain%sf(i)%int)
do j = 1, size (sf_int%beam_index)
b = sf_int%beam_index(j)
call link (sf_int%interaction_t, b, sf_int%incoming(j))
chain%out_sf(b) = i
chain%out_sf_i(b) = sf_int%outgoing(j)
end do
end associate
end do
end if
chain%status = SF_DONE_LINKS
end if
contains
subroutine link (int, b, in_index)
type(interaction_t), intent(inout) :: int
integer, intent(in) :: b, in_index
integer :: i
i = chain%out_sf(b)
select case (i)
case (0)
call interaction_set_source_link (int, in_index, &
chain%beam_t, chain%out_sf_i(b))
case default
call int%set_source_link (in_index, &
chain%sf(i)%int, chain%out_sf_i(b))
end select
end subroutine link
end subroutine sf_chain_instance_link_interactions
@ %def sf_chain_instance_link_interactions
@ Exchange the quantum-number masks between the interactions in the
chain, so we can combine redundant entries and detect any obvious mismatch.
We proceed first in the forward direction and then backwards again.
After this is finished, we finalize initialization by calling the
[[setup_constants]] method, which prepares constant data that depend on the
matrix element structure.
<<SF base: sf chain instance: TBP>>=
procedure :: exchange_mask => sf_chain_exchange_mask
<<SF base: procedures>>=
subroutine sf_chain_exchange_mask (chain)
class(sf_chain_instance_t), intent(inout), target :: chain
type(interaction_t), pointer :: int
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask
integer :: i
if (chain%status >= SF_DONE_LINKS) then
if (allocated (chain%sf)) then
int => beam_get_int_ptr (chain%beam_t)
allocate (mask (int%get_n_out ()))
mask = int%get_mask ()
if (size (chain%sf) /= 0) then
do i = 1, size (chain%sf) - 1
call interaction_exchange_mask (chain%sf(i)%int%interaction_t)
end do
do i = size (chain%sf), 1, -1
call interaction_exchange_mask (chain%sf(i)%int%interaction_t)
end do
if (any (mask .neqv. int%get_mask ())) then
chain%status = SF_FAILED_MASK
return
end if
do i = 1, size (chain%sf)
call chain%sf(i)%int%setup_constants ()
end do
end if
end if
chain%status = SF_DONE_MASK
end if
end subroutine sf_chain_exchange_mask
@ %def sf_chain_exchange_mask
@ Initialize the evaluators that connect the interactions in the
chain.
<<SF base: sf chain instance: TBP>>=
procedure :: init_evaluators => sf_chain_instance_init_evaluators
<<SF base: procedures>>=
subroutine sf_chain_instance_init_evaluators (chain, extended_sf)
class(sf_chain_instance_t), intent(inout), target :: chain
logical, intent(in), optional :: extended_sf
type(interaction_t), pointer :: int
type(quantum_numbers_mask_t) :: mask
integer :: i
logical :: yorn
yorn = .false.; if (present (extended_sf)) yorn = extended_sf
if (chain%status >= SF_DONE_MASK) then
if (allocated (chain%sf)) then
if (size (chain%sf) /= 0) then
mask = quantum_numbers_mask (.false., .false., .true.)
int => beam_get_int_ptr (chain%beam_t)
do i = 1, size (chain%sf)
associate (sf => chain%sf(i))
if (yorn) then
if (int%get_n_sub () == 0) then
- call int%declare_subtraction (n_beam_structure_int)
+ call int%declare_subtraction (n_beams_rescaled)
end if
if (sf%int%interaction_t%get_n_sub () == 0) then
- call sf%int%interaction_t%declare_subtraction &
- (n_beam_structure_int)
+ call sf%int%interaction_t%declare_subtraction (n_beams_rescaled)
end if
end if
call sf%eval%init_product (int, sf%int%interaction_t, mask,&
- & ignore_sub = .true.)
+ & ignore_sub_for_qn = .true.)
if (sf%eval%is_empty ()) then
chain%status = SF_FAILED_CONNECTIONS
return
end if
int => sf%eval%interaction_t
end associate
end do
call find_outgoing_particles ()
end if
else if (chain%out_eval == 0) then
int => beam_get_int_ptr (chain%beam_t)
call int%tag_hard_process ()
end if
chain%status = SF_DONE_CONNECTIONS
end if
contains
<<SF base: init evaluators: find outgoing particles>>
end subroutine sf_chain_instance_init_evaluators
@ %def sf_chain_instance_init_evaluators
@ For debug purposes
<<SF base: sf chain instance: TBP>>=
procedure :: write_interaction => sf_chain_instance_write_interaction
<<SF base: procedures>>=
subroutine sf_chain_instance_write_interaction (chain, i_sf, i_int, unit)
class(sf_chain_instance_t), intent(in) :: chain
integer, intent(in) :: i_sf, i_int
integer, intent(in) :: unit
class(interaction_t), pointer :: int_in1 => null ()
class(interaction_t), pointer :: int_in2 => null ()
integer :: u
u = given_output_unit (unit); if (u < 0) return
if (chain%status >= SF_DONE_MASK) then
if (allocated (chain%sf)) then
int_in1 => evaluator_get_int_in_ptr (chain%sf(i_sf)%eval, 1)
int_in2 => evaluator_get_int_in_ptr (chain%sf(i_sf)%eval, 2)
if (int_in1%get_tag () == i_int) then
call int_in1%basic_write (u)
else if (int_in2%get_tag () == i_int) then
call int_in2%basic_write (u)
else
write (u, "(A,1x,I0,1x,A,1x,I0)") 'No tag of sf', i_sf, 'matches' , i_int
end if
else
write (u, "(A)") 'No sf_chain allocated!'
end if
else
write (u, "(A)") 'sf_chain not ready!'
end if
end subroutine sf_chain_instance_write_interaction
@ %def sf_chain_instance_write_interaction
@ This is an internal subroutine of the previous one: After evaluators
are set, trace the outgoing particles to the last evaluator. We only
need the first channel, all channels are equivalent for this purpose.
For each beam, the outgoing particle is located by [[out_sf]] (the
structure-function object where it originates) and [[out_sf_i]] (the
index within that object). This particle is referenced by the
corresponding evaluator, which in turn is referenced by the next
evaluator, until we are at the end of the chain. We can trace back
references by [[interaction_find_link]]. Knowing that [[out_eval]] is
the index of the last evaluator, we thus determine [[out_eval_i]], the
index of the outgoing particle within that evaluator.
<<SF base: init evaluators: find outgoing particles>>=
subroutine find_outgoing_particles ()
type(interaction_t), pointer :: int, int_next
integer :: i, j, out_sf, out_i
chain%out_eval = size (chain%sf)
do j = 1, size (chain%out_eval_i)
out_sf = chain%out_sf(j)
out_i = chain%out_sf_i(j)
if (out_sf == 0) then
int => beam_get_int_ptr (chain%beam_t)
out_sf = 1
else
int => chain%sf(out_sf)%int%interaction_t
end if
do i = out_sf, chain%out_eval
int_next => chain%sf(i)%eval%interaction_t
out_i = interaction_find_link (int_next, int, out_i)
int => int_next
end do
chain%out_eval_i(j) = out_i
end do
call int%tag_hard_process (chain%out_eval_i)
end subroutine find_outgoing_particles
@ %def find_outgoing_particles
@ Compute the kinematics in the chain instance. We can assume that
the seed momenta are set in the configuration beams. Scanning the
chain, we first transfer the incoming momenta. Then, the use up the MC input
parameter array [[p]] to compute the radiated and outgoing momenta.
In the multi-channel case, [[c_sel]] is the channel which we use for
computing the kinematics and the [[x]] values. In the other channels,
we invert the kinematics in order to recover the corresponding rows in
the [[r]] array, and the Jacobian [[f]].
We first apply any global mapping to transform the input [[p]] into
the array [[r]]. This is then given to the structure functions which
compute the final array [[x]] and Jacobian factors [[f]], which we
multiply to obtain the overall Jacobian.
<<SF base: sf chain instance: TBP>>=
procedure :: compute_kinematics => sf_chain_instance_compute_kinematics
<<SF base: procedures>>=
subroutine sf_chain_instance_compute_kinematics (chain, c_sel, p_in)
class(sf_chain_instance_t), intent(inout), target :: chain
integer, intent(in) :: c_sel
real(default), dimension(:), intent(in) :: p_in
type(interaction_t), pointer :: int
real(default) :: f_mapping
logical, dimension(size (chain%bound)) :: bound
integer :: i, j, c
if (chain%status >= SF_DONE_CONNECTIONS) then
call chain%select_channel (c_sel)
int => beam_get_int_ptr (chain%beam_t)
call int%receive_momenta ()
if (allocated (chain%sf)) then
if (size (chain%sf) /= 0) then
forall (i = 1:size (chain%sf)) chain%sf(i)%int%status = SF_INITIAL
!!! Bug in nagfor 5.3.1(907), fixed in 5.3.1(982)
! chain%p (:,c_sel) = unpack (p_in, chain%bound, 0._default)
!!! Workaround:
bound = chain%bound
chain%p (:,c_sel) = unpack (p_in, bound, 0._default)
chain%pb(:,c_sel) = 1 - chain%p(:,c_sel)
chain%f = 1
chain%x_free = 1
do i = 1, size (chain%sf)
associate (sf => chain%sf(i))
call sf%int%generate_free (sf%r(:,c_sel), sf%rb(:,c_sel), &
chain%x_free)
do j = 1, size (sf%x)
if (.not. chain%bound(sf%int%par_index(j))) then
chain%p (sf%int%par_index(j),c_sel) = sf%r (j,c_sel)
chain%pb(sf%int%par_index(j),c_sel) = sf%rb(j,c_sel)
end if
end do
end associate
end do
if (allocated (chain%channel(c_sel)%multi_mapping)) then
call chain%channel(c_sel)%multi_mapping%compute &
(chain%r(:,c_sel), chain%rb(:,c_sel), &
f_mapping, &
chain%p(:,c_sel), chain%pb(:,c_sel), &
chain%x_free)
chain%f(c_sel) = f_mapping
else
chain%r (:,c_sel) = chain%p (:,c_sel)
chain%rb(:,c_sel) = chain%pb(:,c_sel)
chain%f(c_sel) = 1
end if
do i = 1, size (chain%sf)
associate (sf => chain%sf(i))
call sf%int%seed_kinematics ()
do j = 1, size (sf%x)
sf%r (j,c_sel) = chain%r (sf%int%par_index(j),c_sel)
sf%rb(j,c_sel) = chain%rb(sf%int%par_index(j),c_sel)
end do
call sf%int%complete_kinematics &
(sf%x, sf%xb, sf%f(c_sel), sf%r(:,c_sel), sf%rb(:,c_sel), &
sf%m(c_sel))
do j = 1, size (sf%x)
chain%x(sf%int%par_index(j)) = sf%x(j)
chain%xb(sf%int%par_index(j)) = sf%xb(j)
end do
if (sf%int%status <= SF_FAILED_KINEMATICS) then
chain%status = SF_FAILED_KINEMATICS
return
end if
do c = 1, size (sf%f)
if (c /= c_sel) then
call sf%int%inverse_kinematics &
(sf%x, sf%xb, sf%f(c), sf%r(:,c), sf%rb(:,c), sf%m(c))
do j = 1, size (sf%x)
chain%r (sf%int%par_index(j),c) = sf%r (j,c)
chain%rb(sf%int%par_index(j),c) = sf%rb(j,c)
end do
end if
chain%f(c) = chain%f(c) * sf%f(c)
end do
if (.not. sf%eval%is_empty ()) then
call sf%eval%receive_momenta ()
end if
end associate
end do
do c = 1, size (chain%f)
if (c /= c_sel) then
if (allocated (chain%channel(c)%multi_mapping)) then
call chain%channel(c)%multi_mapping%inverse &
(chain%r(:,c), chain%rb(:,c), &
f_mapping, &
chain%p(:,c), chain%pb(:,c), &
chain%x_free)
chain%f(c) = chain%f(c) * f_mapping
else
chain%p (:,c) = chain%r (:,c)
chain%pb(:,c) = chain%rb(:,c)
end if
end if
end do
end if
end if
chain%status = SF_DONE_KINEMATICS
end if
end subroutine sf_chain_instance_compute_kinematics
@ %def sf_chain_instance_compute_kinematics
@ This is a variant of the previous procedure. We know the $x$ parameters and
reconstruct the momenta and the MC input parameters [[p]]. We do not need to
select a channel.
Note: this is probably redundant, since the method we actually want
starts from the momenta, recovers all $x$ parameters, and then
inverts mappings. See below [[recover_kinematics]].
<<SF base: sf chain instance: TBP>>=
procedure :: inverse_kinematics => sf_chain_instance_inverse_kinematics
<<SF base: procedures>>=
subroutine sf_chain_instance_inverse_kinematics (chain, x, xb)
class(sf_chain_instance_t), intent(inout), target :: chain
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
type(interaction_t), pointer :: int
real(default) :: f_mapping
integer :: i, j, c
if (chain%status >= SF_DONE_CONNECTIONS) then
call chain%select_channel ()
int => beam_get_int_ptr (chain%beam_t)
call int%receive_momenta ()
if (allocated (chain%sf)) then
chain%f = 1
if (size (chain%sf) /= 0) then
forall (i = 1:size (chain%sf)) chain%sf(i)%int%status = SF_INITIAL
chain%x = x
chain%xb= xb
do i = 1, size (chain%sf)
associate (sf => chain%sf(i))
call sf%int%seed_kinematics ()
do j = 1, size (sf%x)
sf%x(j) = chain%x(sf%int%par_index(j))
sf%xb(j) = chain%xb(sf%int%par_index(j))
end do
do c = 1, size (sf%f)
call sf%int%inverse_kinematics &
(sf%x, sf%xb, sf%f(c), sf%r(:,c), sf%rb(:,c), sf%m(c), &
set_momenta = c==1)
chain%f(c) = chain%f(c) * sf%f(c)
do j = 1, size (sf%x)
chain%r (sf%int%par_index(j),c) = sf%r (j,c)
chain%rb(sf%int%par_index(j),c) = sf%rb(j,c)
end do
end do
if (.not. sf%eval%is_empty ()) then
call sf%eval%receive_momenta ()
end if
end associate
end do
do c = 1, size (chain%f)
if (allocated (chain%channel(c)%multi_mapping)) then
call chain%channel(c)%multi_mapping%inverse &
(chain%r(:,c), chain%rb(:,c), &
f_mapping, &
chain%p(:,c), chain%pb(:,c), &
chain%x_free)
chain%f(c) = chain%f(c) * f_mapping
else
chain%p (:,c) = chain%r (:,c)
chain%pb(:,c) = chain%rb(:,c)
end if
end do
end if
end if
chain%status = SF_DONE_KINEMATICS
end if
end subroutine sf_chain_instance_inverse_kinematics
@ %def sf_chain_instance_inverse_kinematics
@ Recover the kinematics: assuming that the last evaluator has
been filled with a valid set of momenta, we travel the momentum links
backwards and fill the preceding evaluators and, as a side effect,
interactions. We stop at the beam interaction.
After all momenta are set, apply the [[inverse_kinematics]] procedure
above, suitably modified, to recover the $x$ and $p$ parameters and
the Jacobian factors.
The [[c_sel]] (channel) argument is just used to mark a selected
channel for the records, otherwise the recovery procedure is
independent of this.
<<SF base: sf chain instance: TBP>>=
procedure :: recover_kinematics => sf_chain_instance_recover_kinematics
<<SF base: procedures>>=
subroutine sf_chain_instance_recover_kinematics (chain, c_sel)
class(sf_chain_instance_t), intent(inout), target :: chain
integer, intent(in) :: c_sel
real(default) :: f_mapping
integer :: i, j, c
if (chain%status >= SF_DONE_CONNECTIONS) then
call chain%select_channel (c_sel)
if (allocated (chain%sf)) then
do i = size (chain%sf), 1, -1
associate (sf => chain%sf(i))
if (.not. sf%eval%is_empty ()) then
call interaction_send_momenta (sf%eval%interaction_t)
end if
end associate
end do
chain%f = 1
if (size (chain%sf) /= 0) then
forall (i = 1:size (chain%sf)) chain%sf(i)%int%status = SF_INITIAL
chain%x_free = 1
do i = 1, size (chain%sf)
associate (sf => chain%sf(i))
call sf%int%seed_kinematics ()
call sf%int%recover_x (sf%x, sf%xb, chain%x_free)
do j = 1, size (sf%x)
chain%x(sf%int%par_index(j)) = sf%x(j)
chain%xb(sf%int%par_index(j)) = sf%xb(j)
end do
do c = 1, size (sf%f)
call sf%int%inverse_kinematics &
(sf%x, sf%xb, sf%f(c), sf%r(:,c), sf%rb(:,c), sf%m(c), &
set_momenta = .false.)
chain%f(c) = chain%f(c) * sf%f(c)
do j = 1, size (sf%x)
chain%r (sf%int%par_index(j),c) = sf%r (j,c)
chain%rb(sf%int%par_index(j),c) = sf%rb(j,c)
end do
end do
end associate
end do
do c = 1, size (chain%f)
if (allocated (chain%channel(c)%multi_mapping)) then
call chain%channel(c)%multi_mapping%inverse &
(chain%r(:,c), chain%rb(:,c), &
f_mapping, &
chain%p(:,c), chain%pb(:,c), &
chain%x_free)
chain%f(c) = chain%f(c) * f_mapping
else
chain%p (:,c) = chain%r (:,c)
chain%pb(:,c) = chain%rb(:,c)
end if
end do
end if
end if
chain%status = SF_DONE_KINEMATICS
end if
end subroutine sf_chain_instance_recover_kinematics
@ %def sf_chain_instance_recover_kinematics
@ Return the initial beam momenta to their source, thus completing
kinematics recovery. Obviously, this works as a side effect.
<<SF base: sf chain instance: TBP>>=
procedure :: return_beam_momenta => sf_chain_instance_return_beam_momenta
<<SF base: procedures>>=
subroutine sf_chain_instance_return_beam_momenta (chain)
class(sf_chain_instance_t), intent(in), target :: chain
type(interaction_t), pointer :: int
if (chain%status >= SF_DONE_KINEMATICS) then
int => beam_get_int_ptr (chain%beam_t)
call interaction_send_momenta (int)
end if
end subroutine sf_chain_instance_return_beam_momenta
@ %def sf_chain_instance_return_beam_momenta
@ Evaluate all interactions in the chain and the product evaluators.
We provide a [[scale]] argument that is given to all structure
functions in the chain.
Hadronic NLO calculations involve rescaled fractions of the original beam
-momentum and PDF singlets (sums over flavors). In particular, we have to handle the following cases:
+momentum. In particular, we have to handle the following cases:
\begin{itemize}
- \item normal evaluation (where [[n_sub = 0]]) for Born and Virtual processes,
- \item rescaled momentum fraction for matching [[i_beam == i_sub]], [[n_sub > 0]] and
- [[sf_rescale]] present, the other beam is kept at born kinematics,
- \item filling the subtraction terms with values from the current evaluation
- [[fill_sub = .true.]], used for the non-rescaled beam,
- \item restricted rescaling to only one beam with [[sf_rescale%is_restricted]].
+ \item normal evaluation (where [[i_sub = 0]]) for all terms except the
+ real non-subtracted,
+ \item rescaled momentum fraction for both beams in the case of the
+ real non-subtracted term ([[i_sub = 0]]),
+ \item and rescaled momentum fraction for one of both beams in the case of the
+ subtraction and DGLAP component ([[i_sub = 1,2]]).
\end{itemize}
For the collinear final or intial state counter terms, we apply a rescaling to
one beam, and keep the other beam as is. We redo it then vice versa having now two subtractions.
-We add two more subtraction where we apply the rescaled gluonic PDF to
-\textit{all} flavors for the PDF singlet calculations.
-For the real rescalation, we have only one rescaled beams, therefore, we have only one
-subtraction.
<<SF base: sf chain instance: TBP>>=
procedure :: evaluate => sf_chain_instance_evaluate
<<SF base: procedures>>=
subroutine sf_chain_instance_evaluate (chain, scale, sf_rescale)
class(sf_chain_instance_t), intent(inout), target :: chain
real(default), intent(in) :: scale
class(sf_rescale_t), intent(inout), optional :: sf_rescale
type(interaction_t), pointer :: out_int
real(default) :: sf_sum
integer :: i_beam, i_sub, n_sub
+ logical :: rescale
+ n_sub = 0
+ rescale = .false.; if (present (sf_rescale)) rescale = .true.
+ if (rescale) then
+ n_sub = chain%get_n_sub ()
+ end if
if (chain%status >= SF_DONE_KINEMATICS) then
if (allocated (chain%sf)) then
if (size (chain%sf) /= 0) then
do i_beam = 1, size (chain%sf)
associate (sf => chain%sf(i_beam))
- n_sub = 0 ! default: no looping over rescaled beams
- if (present (sf_rescale)) then
- n_sub = sf%int%get_n_sub ()
- call sf_rescale%set_i_beam (i_beam)
- end if
- SUB: do i_sub = 0, n_sub
- select case (i_sub)
- case (0)
- if (n_sub == 0) then
- call sf%int%apply (scale, sf_rescale)
- else
- call sf%int%apply (scale, fill_sub = .true.)
- end if
- case (1:2)
- if (present (sf_rescale)) then
- if (sf_rescale%is_restricted (i_beam)) cycle SUB
- end if
- if (i_sub == i_beam) then
- call sf%int%apply(scale, sf_rescale, i_sub)
- end if
- case (3:4)
- ! dummy : handled more appropriately on a lower level (sf%int%apply ())
- case default
- call msg_bug ("sf_chain_instance_evaluate: more than 2&
- & subtraction indices are curently not handled.")
- end select
- if (sf%int%status <= SF_FAILED_EVALUATION) then
- chain%status = SF_FAILED_EVALUATION
- return
- end if
- end do SUB
- if (.not. sf%eval%is_empty ()) call sf%eval%evaluate ()
+ if (rescale) then
+ call sf_rescale%set_i_beam (i_beam)
+ do i_sub = 0, n_sub
+ select case (i_sub)
+ case (0)
+ if (n_sub == 0) then
+ call sf%int%apply (scale, sf_rescale, i_sub = i_sub)
+ else
+ call sf%int%apply (scale, i_sub = i_sub)
+ end if
+ case default
+ if (i_beam == i_sub) then
+ call sf%int%apply (scale, sf_rescale, i_sub = i_sub)
+ else
+ call sf%int%apply (scale, i_sub = i_sub)
+ end if
+ end select
+ end do
+ else
+ call sf%int%apply (scale, i_sub = n_sub)
+ end if
+ if (sf%int%status <= SF_FAILED_EVALUATION) then
+ chain%status = SF_FAILED_EVALUATION
+ return
+ end if
+ if (.not. sf%eval%is_empty ()) call sf%eval%evaluate ()
end associate
end do
out_int => chain%get_out_int_ptr ()
sf_sum = real (out_int%sum ())
call chain%config%trace &
(chain%selected_channel, chain%p, chain%x, chain%f, sf_sum)
end if
end if
chain%status = SF_EVALUATED
end if
end subroutine sf_chain_instance_evaluate
@ %def sf_chain_instance_evaluate
@
\subsection{Access to the chain instance}
Transfer the outgoing momenta to the array [[p]]. We assume that
array sizes match.
<<SF base: sf chain instance: TBP>>=
procedure :: get_out_momenta => sf_chain_instance_get_out_momenta
<<SF base: procedures>>=
subroutine sf_chain_instance_get_out_momenta (chain, p)
class(sf_chain_instance_t), intent(in), target :: chain
type(vector4_t), dimension(:), intent(out) :: p
type(interaction_t), pointer :: int
integer :: i, j
if (chain%status >= SF_DONE_KINEMATICS) then
do j = 1, size (chain%out_sf)
i = chain%out_sf(j)
select case (i)
case (0)
int => beam_get_int_ptr (chain%beam_t)
case default
int => chain%sf(i)%int%interaction_t
end select
p(j) = int%get_momentum (chain%out_sf_i(j))
end do
end if
end subroutine sf_chain_instance_get_out_momenta
@ %def sf_chain_instance_get_out_momenta
@ Return a pointer to the last evaluator in the chain (to the interaction).
<<SF base: sf chain instance: TBP>>=
procedure :: get_out_int_ptr => sf_chain_instance_get_out_int_ptr
<<SF base: procedures>>=
function sf_chain_instance_get_out_int_ptr (chain) result (int)
class(sf_chain_instance_t), intent(in), target :: chain
type(interaction_t), pointer :: int
if (chain%out_eval == 0) then
int => beam_get_int_ptr (chain%beam_t)
else
int => chain%sf(chain%out_eval)%eval%interaction_t
end if
end function sf_chain_instance_get_out_int_ptr
@ %def sf_chain_instance_get_out_int_ptr
@ Return the index of the [[j]]-th outgoing particle, within the last
evaluator.
<<SF base: sf chain instance: TBP>>=
procedure :: get_out_i => sf_chain_instance_get_out_i
<<SF base: procedures>>=
function sf_chain_instance_get_out_i (chain, j) result (i)
class(sf_chain_instance_t), intent(in) :: chain
integer, intent(in) :: j
integer :: i
i = chain%out_eval_i(j)
end function sf_chain_instance_get_out_i
@ %def sf_chain_instance_get_out_i
@ Return the mask for the outgoing particle(s), within the last evaluator.
<<SF base: sf chain instance: TBP>>=
procedure :: get_out_mask => sf_chain_instance_get_out_mask
<<SF base: procedures>>=
function sf_chain_instance_get_out_mask (chain) result (mask)
class(sf_chain_instance_t), intent(in), target :: chain
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask
type(interaction_t), pointer :: int
allocate (mask (chain%config%n_in))
int => chain%get_out_int_ptr ()
mask = int%get_mask (chain%out_eval_i)
end function sf_chain_instance_get_out_mask
@ %def sf_chain_instance_get_out_mask
@ Return the array of MC input parameters that corresponds to channel [[c]].
This is the [[p]] array, the parameters before all mappings.
The [[p]] array may be deallocated. This should correspond to a
zero-size [[r]] argument, so nothing to do then.
<<SF base: sf chain instance: TBP>>=
procedure :: get_mcpar => sf_chain_instance_get_mcpar
<<SF base: procedures>>=
subroutine sf_chain_instance_get_mcpar (chain, c, r)
class(sf_chain_instance_t), intent(in) :: chain
integer, intent(in) :: c
real(default), dimension(:), intent(out) :: r
if (allocated (chain%p)) r = pack (chain%p(:,c), chain%bound)
end subroutine sf_chain_instance_get_mcpar
@ %def sf_chain_instance_get_mcpar
@ Return the Jacobian factor that corresponds to channel [[c]].
<<SF base: sf chain instance: TBP>>=
procedure :: get_f => sf_chain_instance_get_f
<<SF base: procedures>>=
function sf_chain_instance_get_f (chain, c) result (f)
class(sf_chain_instance_t), intent(in) :: chain
integer, intent(in) :: c
real(default) :: f
if (allocated (chain%f)) then
f = chain%f(c)
else
f = 1
end if
end function sf_chain_instance_get_f
@ %def sf_chain_instance_get_f
@ Return the evaluation status.
<<SF base: sf chain instance: TBP>>=
procedure :: get_status => sf_chain_instance_get_status
<<SF base: procedures>>=
function sf_chain_instance_get_status (chain) result (status)
class(sf_chain_instance_t), intent(in) :: chain
integer :: status
status = chain%status
end function sf_chain_instance_get_status
@ %def sf_chain_instance_get_status
@
<<SF base: sf chain instance: TBP>>=
procedure :: get_matrix_elements => sf_chain_instance_get_matrix_elements
<<SF base: procedures>>=
subroutine sf_chain_instance_get_matrix_elements (chain, i, ff)
class(sf_chain_instance_t), intent(in) :: chain
integer, intent(in) :: i
real(default), intent(out), dimension(:), allocatable :: ff
associate (sf => chain%sf(i))
ff = real (sf%int%get_matrix_element ())
end associate
end subroutine sf_chain_instance_get_matrix_elements
@ %def sf_chain_instance_get_matrix_elements
@
<<SF base: sf chain instance: TBP>>=
procedure :: get_beam_int_ptr => sf_chain_instance_get_beam_int_ptr
<<SF base: procedures>>=
function sf_chain_instance_get_beam_int_ptr (chain) result (int)
type(interaction_t), pointer :: int
class(sf_chain_instance_t), intent(in), target :: chain
int => beam_get_int_ptr (chain%beam_t)
end function sf_chain_instance_get_beam_int_ptr
@ %def sf_chain_instance_get_beam_ptr
@
+<<SF base: sf chain instance: TBP>>=
+ procedure :: get_n_sub => sf_chain_instance_get_n_sub
+<<SF base: procedures>>=
+ integer function sf_chain_instance_get_n_sub (chain) result (n_sub)
+ type(interaction_t), pointer :: int
+ class(sf_chain_instance_t), intent(in), target :: chain
+ int => beam_get_int_ptr (chain%beam_t)
+ n_sub = int%get_n_sub ()
+ end function sf_chain_instance_get_n_sub
+
+@ %def sf_chain_instance_get_n_sub
+@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_base_ut.f90]]>>=
<<File header>>
module sf_base_ut
use unit_tests
use sf_base_uti
<<Standard module head>>
<<SF base: public test auxiliary>>
<<SF base: public test>>
contains
<<SF base: test driver>>
end module sf_base_ut
@ %def sf_base_ut
@
<<[[sf_base_uti.f90]]>>=
<<File header>>
module sf_base_uti
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_19
use format_utils, only: write_separator
use diagnostics
use lorentz
use pdg_arrays
use flavors
use colors
use helicities
use quantum_numbers
use state_matrices, only: FM_IGNORE_HELICITY
use interactions
use particles
use model_data
use beams
use sf_aux
use sf_mappings
use sf_base
<<Standard module head>>
<<SF base: test declarations>>
<<SF base: public test auxiliary>>
<<SF base: test types>>
contains
<<SF base: tests>>
<<SF base: test auxiliary>>
end module sf_base_uti
@ %def sf_base_ut
@ API: driver for the unit tests below.
<<SF base: public test>>=
public :: sf_base_test
<<SF base: test driver>>=
subroutine sf_base_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF base: execute tests>>
end subroutine sf_base_test
@ %def sf_base_test
@
\subsection{Test implementation: structure function}
This is a template for the actual structure-function implementation
which will be defined in separate modules.
\subsubsection{Configuration data}
The test structure function uses the [[Test]] model. It describes a
scalar within an arbitrary initial particle, which is given in the
initialization. The radiated particle is also a scalar, the same one,
but we set its mass artificially to zero.
<<SF base: public test auxiliary>>=
public :: sf_test_data_t
<<SF base: test types>>=
type, extends (sf_data_t) :: sf_test_data_t
class(model_data_t), pointer :: model => null ()
integer :: mode = 0
type(flavor_t) :: flv_in
type(flavor_t) :: flv_out
type(flavor_t) :: flv_rad
real(default) :: m = 0
logical :: collinear = .true.
real(default), dimension(:), allocatable :: qbounds
contains
<<SF base: sf test data: TBP>>
end type sf_test_data_t
@ %def sf_test_data_t
@ Output.
<<SF base: sf test data: TBP>>=
procedure :: write => sf_test_data_write
<<SF base: test auxiliary>>=
subroutine sf_test_data_write (data, unit, verbose)
class(sf_test_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") "SF test data:"
write (u, "(3x,A,A)") "model = ", char (data%model%get_name ())
write (u, "(3x,A)", advance="no") "incoming = "
call data%flv_in%write (u); write (u, *)
write (u, "(3x,A)", advance="no") "outgoing = "
call data%flv_out%write (u); write (u, *)
write (u, "(3x,A)", advance="no") "radiated = "
call data%flv_rad%write (u); write (u, *)
write (u, "(3x,A," // FMT_19 // ")") "mass = ", data%m
write (u, "(3x,A,L1)") "collinear = ", data%collinear
if (.not. data%collinear .and. allocated (data%qbounds)) then
write (u, "(3x,A," // FMT_19 // ")") "qmin = ", data%qbounds(1)
write (u, "(3x,A," // FMT_19 // ")") "qmax = ", data%qbounds(2)
end if
end subroutine sf_test_data_write
@ %def sf_test_data_write
@ Initialization.
<<SF base: sf test data: TBP>>=
procedure :: init => sf_test_data_init
<<SF base: test auxiliary>>=
subroutine sf_test_data_init (data, model, pdg_in, collinear, qbounds, mode)
class(sf_test_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), intent(in) :: pdg_in
logical, intent(in), optional :: collinear
real(default), dimension(2), intent(in), optional :: qbounds
integer, intent(in), optional :: mode
data%model => model
if (present (mode)) data%mode = mode
if (pdg_array_get (pdg_in, 1) /= 25) then
call msg_fatal ("Test spectrum function: input flavor must be 's'")
end if
call data%flv_in%init (25, model)
data%m = data%flv_in%get_mass ()
if (present (collinear)) data%collinear = collinear
call data%flv_out%init (25, model)
call data%flv_rad%init (25, model)
if (present (qbounds)) then
allocate (data%qbounds (2))
data%qbounds = qbounds
end if
end subroutine sf_test_data_init
@ %def sf_test_data_init
@ Return the number of parameters: 1 if only consider collinear
splitting, 3 otherwise.
<<SF base: sf test data: TBP>>=
procedure :: get_n_par => sf_test_data_get_n_par
<<SF base: test auxiliary>>=
function sf_test_data_get_n_par (data) result (n)
class(sf_test_data_t), intent(in) :: data
integer :: n
if (data%collinear) then
n = 1
else
n = 3
end if
end function sf_test_data_get_n_par
@ %def sf_test_data_get_n_par
@ Return the outgoing particle PDG code: 25
<<SF base: sf test data: TBP>>=
procedure :: get_pdg_out => sf_test_data_get_pdg_out
<<SF base: test auxiliary>>=
subroutine sf_test_data_get_pdg_out (data, pdg_out)
class(sf_test_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
pdg_out(1) = 25
end subroutine sf_test_data_get_pdg_out
@ %def sf_test_data_get_pdg_out
@ Allocate the matching interaction.
<<SF base: sf test data: TBP>>=
procedure :: allocate_sf_int => sf_test_data_allocate_sf_int
<<SF base: test auxiliary>>=
subroutine sf_test_data_allocate_sf_int (data, sf_int)
class(sf_test_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
if (allocated (sf_int)) deallocate (sf_int)
allocate (sf_test_t :: sf_int)
end subroutine sf_test_data_allocate_sf_int
@ %def sf_test_data_allocate_sf_int
@
\subsubsection{Interaction}
<<SF base: test types>>=
type, extends (sf_int_t) :: sf_test_t
type(sf_test_data_t), pointer :: data => null ()
real(default) :: x = 0
contains
<<SF base: sf test int: TBP>>
end type sf_test_t
@ %def sf_test_t
@ Type string: constant
<<SF base: sf test int: TBP>>=
procedure :: type_string => sf_test_type_string
<<SF base: test auxiliary>>=
function sf_test_type_string (object) result (string)
class(sf_test_t), intent(in) :: object
type(string_t) :: string
string = "Test"
end function sf_test_type_string
@ %def sf_test_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF base: sf test int: TBP>>=
procedure :: write => sf_test_write
<<SF base: test auxiliary>>=
subroutine sf_test_write (object, unit, testflag)
class(sf_test_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "SF test data: [undefined]"
end if
end subroutine sf_test_write
@ %def sf_test_write
@ Initialize. We know that [[data]] will be of concrete type
[[sf_test_data_t]], but we have to cast this explicitly.
For this implementation, we set the incoming and outgoing masses equal
to the physical particle mass, but keep the radiated mass zero.
Optionally, we can provide minimum and maximum values for the momentum
transfer.
<<SF base: sf test int: TBP>>=
procedure :: init => sf_test_init
<<SF base: test auxiliary>>=
subroutine sf_test_init (sf_int, data)
class(sf_test_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(3) :: mask
type(helicity_t) :: hel0
type(color_t) :: col0
type(quantum_numbers_t), dimension(3) :: qn
mask = quantum_numbers_mask (.false., .false., .false.)
select type (data)
type is (sf_test_data_t)
if (allocated (data%qbounds)) then
call sf_int%base_init (mask, &
[data%m**2], [0._default], [data%m**2], &
[data%qbounds(1)], [data%qbounds(2)])
else
call sf_int%base_init (mask, &
[data%m**2], [0._default], [data%m**2])
end if
sf_int%data => data
call hel0%init (0)
call col0%init ()
call qn(1)%init (data%flv_in, col0, hel0)
call qn(2)%init (data%flv_rad, col0, hel0)
call qn(3)%init (data%flv_out, col0, hel0)
call sf_int%add_state (qn)
call sf_int%freeze ()
call sf_int%set_incoming ([1])
call sf_int%set_radiated ([2])
call sf_int%set_outgoing ([3])
end select
sf_int%status = SF_INITIAL
end subroutine sf_test_init
@ %def sf_test_init
@ Set kinematics. If [[map]] is unset, the $r$ and $x$ values
coincide, and the Jacobian $f(r)$ is trivial.
If [[map]] is set, we are asked to provide an efficient mapping.
For the test case, we set $x=r^2$ and consequently $f(r)=2r$.
<<SF base: sf test int: TBP>>=
procedure :: complete_kinematics => sf_test_complete_kinematics
<<SF base: test auxiliary>>=
subroutine sf_test_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(sf_test_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
if (map) then
x(1) = r(1)**2
f = 2 * r(1)
else
x(1) = r(1)
f = 1
end if
xb(1) = 1 - x(1)
if (size (x) == 3) then
x(2:3) = r(2:3)
xb(2:3) = rb(2:3)
end if
call sf_int%split_momentum (x, xb)
sf_int%x = x(1)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end subroutine sf_test_complete_kinematics
@ %def sf_test_complete_kinematics
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
<<SF base: sf test int: TBP>>=
procedure :: inverse_kinematics => sf_test_inverse_kinematics
<<SF base: test auxiliary>>=
subroutine sf_test_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(sf_test_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
r(1) = sqrt (x(1))
f = 2 * r(1)
else
r(1) = x(1)
f = 1
end if
if (size (x) == 3) r(2:3) = x(2:3)
rb = 1 - r
sf_int%x = x(1)
if (set_mom) then
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end if
end subroutine sf_test_inverse_kinematics
@ %def sf_test_inverse_kinematics
@ Apply the structure function. The matrix element becomes unity and
the application always succeeds.
If the [[mode]] indicator is one, the matrix element is equal to the
parameter~$x$.
<<SF base: sf test int: TBP>>=
procedure :: apply => sf_test_apply
<<SF base: test auxiliary>>=
- subroutine sf_test_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine sf_test_apply (sf_int, scale, rescale, i_sub)
class(sf_test_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
select case (sf_int%data%mode)
case (0)
call sf_int%set_matrix_element &
(cmplx (1._default, kind=default))
case (1)
call sf_int%set_matrix_element &
(cmplx (sf_int%x, kind=default))
end select
sf_int%status = SF_EVALUATED
end subroutine sf_test_apply
@ %def sf_test_apply
@
\subsection{Test implementation: pair spectrum}
Another template, this time for a incoming particle pair, splitting
into two radiated and two outgoing particles.
\subsubsection{Configuration data}
For simplicity, the spectrum contains two mirror images of the
previous structure-function configuration: the incoming and all
outgoing particles are test scalars.
We have two versions, one with radiated particles, one without.
<<SF base: test types>>=
type, extends (sf_data_t) :: sf_test_spectrum_data_t
class(model_data_t), pointer :: model => null ()
type(flavor_t) :: flv_in
type(flavor_t) :: flv_out
type(flavor_t) :: flv_rad
logical :: with_radiation = .true.
real(default) :: m = 0
contains
<<SF base: sf test spectrum data: TBP>>
end type sf_test_spectrum_data_t
@ %def sf_test_spectrum_data_t
@ Output.
<<SF base: sf test spectrum data: TBP>>=
procedure :: write => sf_test_spectrum_data_write
<<SF base: test auxiliary>>=
subroutine sf_test_spectrum_data_write (data, unit, verbose)
class(sf_test_spectrum_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") "SF test spectrum data:"
write (u, "(3x,A,A)") "model = ", char (data%model%get_name ())
write (u, "(3x,A)", advance="no") "incoming = "
call data%flv_in%write (u); write (u, *)
write (u, "(3x,A)", advance="no") "outgoing = "
call data%flv_out%write (u); write (u, *)
write (u, "(3x,A)", advance="no") "radiated = "
call data%flv_rad%write (u); write (u, *)
write (u, "(3x,A," // FMT_19 // ")") "mass = ", data%m
end subroutine sf_test_spectrum_data_write
@ %def sf_test_spectrum_data_write
@ Initialization.
<<SF base: sf test spectrum data: TBP>>=
procedure :: init => sf_test_spectrum_data_init
<<SF base: test auxiliary>>=
subroutine sf_test_spectrum_data_init (data, model, pdg_in, with_radiation)
class(sf_test_spectrum_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), intent(in) :: pdg_in
logical, intent(in) :: with_radiation
data%model => model
data%with_radiation = with_radiation
if (pdg_array_get (pdg_in, 1) /= 25) then
call msg_fatal ("Test structure function: input flavor must be 's'")
end if
call data%flv_in%init (25, model)
data%m = data%flv_in%get_mass ()
call data%flv_out%init (25, model)
if (with_radiation) then
call data%flv_rad%init (25, model)
end if
end subroutine sf_test_spectrum_data_init
@ %def sf_test_spectrum_data_init
@ Return the number of parameters: 2, since we have only collinear
splitting here.
<<SF base: sf test spectrum data: TBP>>=
procedure :: get_n_par => sf_test_spectrum_data_get_n_par
<<SF base: test auxiliary>>=
function sf_test_spectrum_data_get_n_par (data) result (n)
class(sf_test_spectrum_data_t), intent(in) :: data
integer :: n
n = 2
end function sf_test_spectrum_data_get_n_par
@ %def sf_test_spectrum_data_get_n_par
@ Return the outgoing particle PDG codes: 25
<<SF base: sf test spectrum data: TBP>>=
procedure :: get_pdg_out => sf_test_spectrum_data_get_pdg_out
<<SF base: test auxiliary>>=
subroutine sf_test_spectrum_data_get_pdg_out (data, pdg_out)
class(sf_test_spectrum_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
pdg_out(1) = 25
pdg_out(2) = 25
end subroutine sf_test_spectrum_data_get_pdg_out
@ %def sf_test_spectrum_data_get_pdg_out
@ Allocate the matching interaction.
<<SF base: sf test spectrum data: TBP>>=
procedure :: allocate_sf_int => &
sf_test_spectrum_data_allocate_sf_int
<<SF base: test auxiliary>>=
subroutine sf_test_spectrum_data_allocate_sf_int (data, sf_int)
class(sf_test_spectrum_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (sf_test_spectrum_t :: sf_int)
end subroutine sf_test_spectrum_data_allocate_sf_int
@ %def sf_test_spectrum_data_allocate_sf_int
@
\subsubsection{Interaction}
<<SF base: test types>>=
type, extends (sf_int_t) :: sf_test_spectrum_t
type(sf_test_spectrum_data_t), pointer :: data => null ()
contains
<<SF base: sf test spectrum: TBP>>
end type sf_test_spectrum_t
@ %def sf_test_spectrum_t
<<SF base: sf test spectrum: TBP>>=
procedure :: type_string => sf_test_spectrum_type_string
<<SF base: test auxiliary>>=
function sf_test_spectrum_type_string (object) result (string)
class(sf_test_spectrum_t), intent(in) :: object
type(string_t) :: string
string = "Test Spectrum"
end function sf_test_spectrum_type_string
@ %def sf_test_spectrum_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF base: sf test spectrum: TBP>>=
procedure :: write => sf_test_spectrum_write
<<SF base: test auxiliary>>=
subroutine sf_test_spectrum_write (object, unit, testflag)
class(sf_test_spectrum_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "SF test spectrum data: [undefined]"
end if
end subroutine sf_test_spectrum_write
@ %def sf_test_spectrum_write
@ Initialize. We know that [[data]] will be of concrete type
[[sf_test_spectrum_data_t]], but we have to cast this explicitly.
For this implementation, we set the incoming and outgoing masses equal
to the physical particle mass, but keep the radiated mass zero.
Optionally, we can provide minimum and maximum values for the momentum
transfer.
<<SF base: sf test spectrum: TBP>>=
procedure :: init => sf_test_spectrum_init
<<SF base: test auxiliary>>=
subroutine sf_test_spectrum_init (sf_int, data)
class(sf_test_spectrum_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(6) :: mask
type(helicity_t) :: hel0
type(color_t) :: col0
type(quantum_numbers_t), dimension(6) :: qn
mask = quantum_numbers_mask (.false., .false., .false.)
select type (data)
type is (sf_test_spectrum_data_t)
if (data%with_radiation) then
call sf_int%base_init (mask(1:6), &
[data%m**2, data%m**2], &
[0._default, 0._default], &
[data%m**2, data%m**2])
sf_int%data => data
call hel0%init (0)
call col0%init ()
call qn(1)%init (data%flv_in, col0, hel0)
call qn(2)%init (data%flv_in, col0, hel0)
call qn(3)%init (data%flv_rad, col0, hel0)
call qn(4)%init (data%flv_rad, col0, hel0)
call qn(5)%init (data%flv_out, col0, hel0)
call qn(6)%init (data%flv_out, col0, hel0)
call sf_int%add_state (qn(1:6))
call sf_int%set_incoming ([1,2])
call sf_int%set_radiated ([3,4])
call sf_int%set_outgoing ([5,6])
else
call sf_int%base_init (mask(1:4), &
[data%m**2, data%m**2], &
[real(default) :: ], &
[data%m**2, data%m**2])
sf_int%data => data
call hel0%init (0)
call col0%init ()
call qn(1)%init (data%flv_in, col0, hel0)
call qn(2)%init (data%flv_in, col0, hel0)
call qn(3)%init (data%flv_out, col0, hel0)
call qn(4)%init (data%flv_out, col0, hel0)
call sf_int%add_state (qn(1:4))
call sf_int%set_incoming ([1,2])
call sf_int%set_outgoing ([3,4])
end if
call sf_int%freeze ()
end select
sf_int%status = SF_INITIAL
end subroutine sf_test_spectrum_init
@ %def sf_test_spectrum_init
@ Set kinematics. If [[map]] is unset, the $r$ and $x$ values
coincide, and the Jacobian $f(r)$ is trivial.
If [[map]] is set, we are asked to provide an efficient mapping.
For the test case, we set $x=r^2$ (as above) for both $x$ parameters
and consequently $f(r)=4r_1r_2$.
<<SF base: sf test spectrum: TBP>>=
procedure :: complete_kinematics => sf_test_spectrum_complete_kinematics
<<SF base: test auxiliary>>=
subroutine sf_test_spectrum_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(sf_test_spectrum_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
real(default), dimension(2) :: xb1
if (map) then
x = r**2
f = 4 * r(1) * r(2)
else
x = r
f = 1
end if
xb = 1 - x
if (sf_int%data%with_radiation) then
call sf_int%split_momenta (x, xb)
else
call sf_int%reduce_momenta (x)
end if
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end subroutine sf_test_spectrum_complete_kinematics
@ %def sf_test_spectrum_complete_kinematics
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
<<SF base: sf test spectrum: TBP>>=
procedure :: inverse_kinematics => sf_test_spectrum_inverse_kinematics
<<SF base: test auxiliary>>=
subroutine sf_test_spectrum_inverse_kinematics &
(sf_int, x, xb, f, r, rb, map, set_momenta)
class(sf_test_spectrum_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
real(default), dimension(2) :: xb1
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
r = sqrt (x)
f = 4 * r(1) * r(2)
else
r = x
f = 1
end if
rb = 1 - r
if (set_mom) then
if (sf_int%data%with_radiation) then
call sf_int%split_momenta (x, xb)
else
call sf_int%reduce_momenta (x)
end if
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end if
end subroutine sf_test_spectrum_inverse_kinematics
@ %def sf_test_spectrum_inverse_kinematics
@ Apply the structure function. The matrix element becomes unity and
the application always succeeds.
<<SF base: sf test spectrum: TBP>>=
procedure :: apply => sf_test_spectrum_apply
<<SF base: test auxiliary>>=
- subroutine sf_test_spectrum_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine sf_test_spectrum_apply (sf_int, scale, rescale, i_sub)
class(sf_test_spectrum_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
call sf_int%set_matrix_element &
(cmplx (1._default, kind=default))
sf_int%status = SF_EVALUATED
end subroutine sf_test_spectrum_apply
@ %def sf_test_spectrum_apply
@
\subsection{Test implementation: generator spectrum}
A generator for two beams, no radiation (for simplicity).
\subsubsection{Configuration data}
For simplicity, the spectrum contains two mirror images of the
previous structure-function configuration: the incoming and all
outgoing particles are test scalars.
We have two versions, one with radiated particles, one without.
<<SF base: test types>>=
type, extends (sf_data_t) :: sf_test_generator_data_t
class(model_data_t), pointer :: model => null ()
type(flavor_t) :: flv_in
type(flavor_t) :: flv_out
type(flavor_t) :: flv_rad
real(default) :: m = 0
contains
<<SF base: sf test generator data: TBP>>
end type sf_test_generator_data_t
@ %def sf_test_generator_data_t
@ Output.
<<SF base: sf test generator data: TBP>>=
procedure :: write => sf_test_generator_data_write
<<SF base: test auxiliary>>=
subroutine sf_test_generator_data_write (data, unit, verbose)
class(sf_test_generator_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A)") "SF test generator data:"
write (u, "(3x,A,A)") "model = ", char (data%model%get_name ())
write (u, "(3x,A)", advance="no") "incoming = "
call data%flv_in%write (u); write (u, *)
write (u, "(3x,A)", advance="no") "outgoing = "
call data%flv_out%write (u); write (u, *)
write (u, "(3x,A," // FMT_19 // ")") "mass = ", data%m
end subroutine sf_test_generator_data_write
@ %def sf_test_generator_data_write
@ Initialization.
<<SF base: sf test generator data: TBP>>=
procedure :: init => sf_test_generator_data_init
<<SF base: test auxiliary>>=
subroutine sf_test_generator_data_init (data, model, pdg_in)
class(sf_test_generator_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), intent(in) :: pdg_in
data%model => model
if (pdg_array_get (pdg_in, 1) /= 25) then
call msg_fatal ("Test generator: input flavor must be 's'")
end if
call data%flv_in%init (25, model)
data%m = data%flv_in%get_mass ()
call data%flv_out%init (25, model)
end subroutine sf_test_generator_data_init
@ %def sf_test_generator_data_init
@ This structure function is a generator.
<<SF base: sf test generator data: TBP>>=
procedure :: is_generator => sf_test_generator_data_is_generator
<<SF base: test auxiliary>>=
function sf_test_generator_data_is_generator (data) result (flag)
class(sf_test_generator_data_t), intent(in) :: data
logical :: flag
flag = .true.
end function sf_test_generator_data_is_generator
@ %def sf_test_generator_data_is_generator
@ Return the number of parameters: 2, since we have only collinear
splitting here.
<<SF base: sf test generator data: TBP>>=
procedure :: get_n_par => sf_test_generator_data_get_n_par
<<SF base: test auxiliary>>=
function sf_test_generator_data_get_n_par (data) result (n)
class(sf_test_generator_data_t), intent(in) :: data
integer :: n
n = 2
end function sf_test_generator_data_get_n_par
@ %def sf_test_generator_data_get_n_par
@ Return the outgoing particle PDG codes: 25
<<SF base: sf test generator data: TBP>>=
procedure :: get_pdg_out => sf_test_generator_data_get_pdg_out
<<SF base: test auxiliary>>=
subroutine sf_test_generator_data_get_pdg_out (data, pdg_out)
class(sf_test_generator_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
pdg_out(1) = 25
pdg_out(2) = 25
end subroutine sf_test_generator_data_get_pdg_out
@ %def sf_test_generator_data_get_pdg_out
@ Allocate the matching interaction.
<<SF base: sf test generator data: TBP>>=
procedure :: allocate_sf_int => &
sf_test_generator_data_allocate_sf_int
<<SF base: test auxiliary>>=
subroutine sf_test_generator_data_allocate_sf_int (data, sf_int)
class(sf_test_generator_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (sf_test_generator_t :: sf_int)
end subroutine sf_test_generator_data_allocate_sf_int
@ %def sf_test_generator_data_allocate_sf_int
@
\subsubsection{Interaction}
<<SF base: test types>>=
type, extends (sf_int_t) :: sf_test_generator_t
type(sf_test_generator_data_t), pointer :: data => null ()
contains
<<SF base: sf test generator: TBP>>
end type sf_test_generator_t
@ %def sf_test_generator_t
<<SF base: sf test generator: TBP>>=
procedure :: type_string => sf_test_generator_type_string
<<SF base: test auxiliary>>=
function sf_test_generator_type_string (object) result (string)
class(sf_test_generator_t), intent(in) :: object
type(string_t) :: string
string = "Test Generator"
end function sf_test_generator_type_string
@ %def sf_test_generator_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF base: sf test generator: TBP>>=
procedure :: write => sf_test_generator_write
<<SF base: test auxiliary>>=
subroutine sf_test_generator_write (object, unit, testflag)
class(sf_test_generator_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "SF test generator data: [undefined]"
end if
end subroutine sf_test_generator_write
@ %def sf_test_generator_write
@ Initialize. We know that [[data]] will be of concrete type
[[sf_test_generator_data_t]], but we have to cast this explicitly.
For this implementation, we set the incoming and outgoing masses equal
to the physical particle mass. No radiation.
<<SF base: sf test generator: TBP>>=
procedure :: init => sf_test_generator_init
<<SF base: test auxiliary>>=
subroutine sf_test_generator_init (sf_int, data)
class(sf_test_generator_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(4) :: mask
type(helicity_t) :: hel0
type(color_t) :: col0
type(quantum_numbers_t), dimension(4) :: qn
mask = quantum_numbers_mask (.false., .false., .false.)
select type (data)
type is (sf_test_generator_data_t)
call sf_int%base_init (mask(1:4), &
[data%m**2, data%m**2], &
[real(default) :: ], &
[data%m**2, data%m**2])
sf_int%data => data
call hel0%init (0)
call col0%init ()
call qn(1)%init (data%flv_in, col0, hel0)
call qn(2)%init (data%flv_in, col0, hel0)
call qn(3)%init (data%flv_out, col0, hel0)
call qn(4)%init (data%flv_out, col0, hel0)
call sf_int%add_state (qn(1:4))
call sf_int%set_incoming ([1,2])
call sf_int%set_outgoing ([3,4])
call sf_int%freeze ()
end select
sf_int%status = SF_INITIAL
end subroutine sf_test_generator_init
@ %def sf_test_generator_init
@ This structure function is a generator.
<<SF base: sf test generator: TBP>>=
procedure :: is_generator => sf_test_generator_is_generator
<<SF base: test auxiliary>>=
function sf_test_generator_is_generator (sf_int) result (flag)
class(sf_test_generator_t), intent(in) :: sf_int
logical :: flag
flag = sf_int%data%is_generator ()
end function sf_test_generator_is_generator
@ %def sf_test_generator_is_generator
@ Generate free parameters. This mock generator always produces the
nubmers 0.8 and 0.5.
<<SF base: sf test generator: TBP>>=
procedure :: generate_free => sf_test_generator_generate_free
<<SF base: test auxiliary>>=
subroutine sf_test_generator_generate_free (sf_int, r, rb, x_free)
class(sf_test_generator_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(inout) :: x_free
r = [0.8, 0.5]
rb= 1 - r
x_free = x_free * product (r)
end subroutine sf_test_generator_generate_free
@ %def sf_test_generator_generate_free
@ Recover momentum fractions. Since the x values are free, we also set the [[x_free]] parameter.
<<SF base: sf test generator: TBP>>=
procedure :: recover_x => sf_test_generator_recover_x
<<SF base: test auxiliary>>=
subroutine sf_test_generator_recover_x (sf_int, x, xb, x_free)
class(sf_test_generator_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(inout), optional :: x_free
call sf_int%base_recover_x (x, xb)
if (present (x_free)) x_free = x_free * product (x)
end subroutine sf_test_generator_recover_x
@ %def sf_test_generator_recover_x
@ Set kinematics. Since this is a generator, just transfer input to output.
<<SF base: sf test generator: TBP>>=
procedure :: complete_kinematics => sf_test_generator_complete_kinematics
<<SF base: test auxiliary>>=
subroutine sf_test_generator_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(sf_test_generator_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
x = r
xb= rb
f = 1
call sf_int%reduce_momenta (x)
end subroutine sf_test_generator_complete_kinematics
@ %def sf_test_generator_complete_kinematics
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
<<SF base: sf test generator: TBP>>=
procedure :: inverse_kinematics => sf_test_generator_inverse_kinematics
<<SF base: test auxiliary>>=
subroutine sf_test_generator_inverse_kinematics &
(sf_int, x, xb, f, r, rb, map, set_momenta)
class(sf_test_generator_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
r = x
rb= xb
f = 1
if (set_mom) call sf_int%reduce_momenta (x)
end subroutine sf_test_generator_inverse_kinematics
@ %def sf_test_generator_inverse_kinematics
@ Apply the structure function. The matrix element becomes unity and
the application always succeeds.
<<SF base: sf test generator: TBP>>=
procedure :: apply => sf_test_generator_apply
<<SF base: test auxiliary>>=
- subroutine sf_test_generator_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine sf_test_generator_apply (sf_int, scale, rescale, i_sub)
class(sf_test_generator_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
call sf_int%set_matrix_element &
(cmplx (1._default, kind=default))
sf_int%status = SF_EVALUATED
end subroutine sf_test_generator_apply
@ %def sf_test_generator_apply
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF base: execute tests>>=
call test (sf_base_1, "sf_base_1", &
"structure function configuration", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_1
<<SF base: tests>>=
subroutine sf_base_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(pdg_array_t), dimension(1) :: pdg_out
integer, dimension(:), allocatable :: pdg1
class(sf_data_t), allocatable :: data
write (u, "(A)") "* Test output: sf_base_1"
write (u, "(A)") "* Purpose: initialize and display &
&test structure function data"
write (u, "(A)")
call model%init_test ()
pdg_in = 25
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (model, pdg_in)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle code:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
write (u, "(2x,99(1x,I0))") pdg1
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_1"
end subroutine sf_base_1
@ %def sf_base_1
@
\subsubsection{Test and probe structure function}
Construct and display a structure function object based on the test
structure function.
<<SF base: execute tests>>=
call test (sf_base_2, "sf_base_2", &
"structure function instance", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_2
<<SF base: tests>>=
subroutine sf_base_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_base_2"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
pdg_in = 25
call flv%init (25, model)
call reset_interaction_counter ()
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (model, pdg_in)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=1"
write (u, "(A)")
r = 1
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Set kinematics with mapping for r=0.8"
write (u, "(A)")
r = 0.8_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A)")
write (u, "(A)") "* Compute inverse kinematics for x=0.64 and evaluate"
write (u, "(A)")
x = 0.64_default
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.true.)
call sf_int%apply (scale=0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_2"
end subroutine sf_base_2
@ %def sf_base_2
@
\subsubsection{Collinear kinematics}
Scan over the possibilities for mass assignment and on-shell
projections, collinear case.
<<SF base: execute tests>>=
call test (sf_base_3, "sf_base_3", &
"alternatives for collinear kinematics", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_3
<<SF base: tests>>=
subroutine sf_base_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(flavor_t) :: flv
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_base_3"
write (u, "(A)") "* Purpose: check various kinematical setups"
write (u, "(A)") "* for collinear structure-function splitting."
write (u, "(A)") " (two masses equal, one zero)"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
pdg_in = 25
call flv%init (25, model)
call reset_interaction_counter ()
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (model, pdg_in)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%write (u)
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=500"
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set radiated mass to zero"
sf_int%mr2 = 0
sf_int%mo2 = sf_int%mi2
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5, keeping energy"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5, keeping momentum"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
sf_int%on_shell_mode = KEEP_MOMENTUM
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set outgoing mass to zero"
sf_int%mr2 = sf_int%mi2
sf_int%mo2 = 0
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5, keeping energy"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5, keeping momentum"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
sf_int%on_shell_mode = KEEP_MOMENTUM
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set incoming mass to zero"
k = vector4_moving (E, E, 3)
call sf_int%seed_kinematics ([k])
sf_int%mr2 = sf_int%mi2
sf_int%mo2 = sf_int%mi2
sf_int%mi2 = 0
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5, keeping energy"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5, keeping momentum"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
sf_int%on_shell_mode = KEEP_MOMENTUM
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set all masses to zero"
sf_int%mr2 = 0
sf_int%mo2 = 0
sf_int%mi2 = 0
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5, keeping energy"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5, keeping momentum"
write (u, "(A)")
r = 0.5_default
rb = 1 - r
sf_int%on_shell_mode = KEEP_MOMENTUM
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_3"
end subroutine sf_base_3
@ %def sf_base_3
@
\subsubsection{Non-collinear kinematics}
Scan over the possibilities for mass assignment and on-shell
projections, non-collinear case.
<<SF base: execute tests>>=
call test (sf_base_4, "sf_base_4", &
"alternatives for non-collinear kinematics", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_4
<<SF base: tests>>=
subroutine sf_base_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(flavor_t) :: flv
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_base_4"
write (u, "(A)") "* Purpose: check various kinematical setups"
write (u, "(A)") "* for free structure-function splitting."
write (u, "(A)") " (two masses equal, one zero)"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
pdg_in = 25
call flv%init (25, model)
call reset_interaction_counter ()
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (model, pdg_in, collinear=.false.)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%write (u)
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=500"
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set radiated mass to zero"
sf_int%mr2 = 0
sf_int%mo2 = sf_int%mi2
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.125, keeping energy"
write (u, "(A)")
r = [0.5_default, 0.5_default, 0.125_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.125, keeping momentum"
write (u, "(A)")
r = [0.5_default, 0.5_default, 0.125_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_MOMENTUM
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set outgoing mass to zero"
sf_int%mr2 = sf_int%mi2
sf_int%mo2 = 0
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.125, keeping energy"
write (u, "(A)")
r = [0.5_default, 0.5_default, 0.125_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.125, keeping momentum"
write (u, "(A)")
r = [0.5_default, 0.5_default, 0.125_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_MOMENTUM
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set incoming mass to zero"
k = vector4_moving (E, E, 3)
call sf_int%seed_kinematics ([k])
sf_int%mr2 = sf_int%mi2
sf_int%mo2 = sf_int%mi2
sf_int%mi2 = 0
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.125, keeping energy"
write (u, "(A)")
r = [0.5_default, 0.5_default, 0.125_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.125, keeping momentum"
write (u, "(A)")
r = [0.5_default, 0.5_default, 0.125_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_MOMENTUM
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set all masses to zero"
sf_int%mr2 = 0
sf_int%mo2 = 0
sf_int%mi2 = 0
write (u, "(A)")
write (u, "(A)") "* Re-Initialize structure-function object with Q bounds"
call reset_interaction_counter ()
select type (data)
type is (sf_test_data_t)
call data%init (model, pdg_in, collinear=.false., &
qbounds = [1._default, 100._default])
end select
call sf_int%init (data)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.125, keeping energy"
write (u, "(A)")
r = [0.5_default, 0.5_default, 0.125_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.125, keeping momentum"
write (u, "(A)")
r = [0.5_default, 0.5_default, 0.125_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_MOMENTUM
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Recover x and r"
write (u, "(A)")
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_4"
end subroutine sf_base_4
@ %def sf_base_4
@
\subsubsection{Pair spectrum}
Construct and display a structure function object for a pair spectrum
(a structure function involving two particles simultaneously).
<<SF base: execute tests>>=
call test (sf_base_5, "sf_base_5", &
"pair spectrum with radiation", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_5
<<SF base: tests>>=
subroutine sf_base_5 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(pdg_array_t), dimension(2) :: pdg_out
integer, dimension(:), allocatable :: pdg1, pdg2
type(flavor_t) :: flv
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t), dimension(2) :: k
type(vector4_t), dimension(4) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_base_5"
write (u, "(A)") "* Purpose: initialize and fill &
&a pair spectrum object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
allocate (sf_test_spectrum_data_t :: data)
select type (data)
type is (sf_test_spectrum_data_t)
call data%init (model, pdg_in, with_radiation=.true.)
end select
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
pdg2 = pdg_out(2)
write (u, "(2x,99(1x,I0))") pdg1, pdg2
write (u, "(A)")
write (u, "(A)") "* Initialize spectrum object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momenta with sqrts=1000"
E = 500
k(1) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
k(2) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call sf_int%seed_kinematics (k)
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.4,0.8"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = [0.4_default, 0.8_default]
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Set kinematics with mapping for r=0.6,0.8"
write (u, "(A)")
r = [0.6_default, 0.8_default]
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call reset_interaction_counter ()
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%seed_kinematics (k)
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A)")
write (u, "(A)") "* Compute inverse kinematics for x=0.36,0.64 &
&and evaluate"
write (u, "(A)")
x = [0.36_default, 0.64_default]
xb = 1 - x
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.true.)
call sf_int%apply (scale=0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_5"
end subroutine sf_base_5
@ %def sf_base_5
@
\subsubsection{Pair spectrum without radiation}
Construct and display a structure function object for a pair spectrum
(a structure function involving two particles simultaneously).
<<SF base: execute tests>>=
call test (sf_base_6, "sf_base_6", &
"pair spectrum without radiation", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_6
<<SF base: tests>>=
subroutine sf_base_6 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(flavor_t) :: flv
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t), dimension(2) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_base_6"
write (u, "(A)") "* Purpose: initialize and fill &
&a pair spectrum object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
allocate (sf_test_spectrum_data_t :: data)
select type (data)
type is (sf_test_spectrum_data_t)
call data%init (model, pdg_in, with_radiation=.false.)
end select
write (u, "(A)") "* Initialize spectrum object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
write (u, "(A)") "* Initialize incoming momenta with sqrts=1000"
E = 500
k(1) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
k(2) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call sf_int%seed_kinematics (k)
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.4,0.8"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = [0.4_default, 0.8_default]
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call reset_interaction_counter ()
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%seed_kinematics (k)
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A)")
write (u, "(A)") "* Compute inverse kinematics for x=0.4,0.8 &
&and evaluate"
write (u, "(A)")
x = [0.4_default, 0.8_default]
xb = 1 - x
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%apply (scale=0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_6"
end subroutine sf_base_6
@ %def sf_base_6
@
\subsubsection{Direct access to structure function}
Probe a structure function directly.
<<SF base: execute tests>>=
call test (sf_base_7, "sf_base_7", &
"direct access", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_7
<<SF base: tests>>=
subroutine sf_base_7 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(flavor_t) :: flv
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
real(default), dimension(:), allocatable :: value
write (u, "(A)") "* Test output: sf_base_7"
write (u, "(A)") "* Purpose: check direct access method"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (model, pdg_in)
end select
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
write (u, "(A)") "* Probe structure function: states"
write (u, "(A)")
write (u, "(A,I0)") "n_states = ", sf_int%get_n_states ()
write (u, "(A,I0)") "n_in = ", sf_int%get_n_in ()
write (u, "(A,I0)") "n_rad = ", sf_int%get_n_rad ()
write (u, "(A,I0)") "n_out = ", sf_int%get_n_out ()
write (u, "(A)")
write (u, "(A)", advance="no") "state(1) = "
call quantum_numbers_write (sf_int%get_state (1), u)
write (u, *)
allocate (value (sf_int%get_n_states ()))
call sf_int%compute_values (value, &
E=[500._default], x=[0.5_default], xb=[0.5_default], scale=0._default)
write (u, "(A)")
write (u, "(A)", advance="no") "value (E=500, x=0.5) ="
write (u, "(9(1x," // FMT_19 // "))") value
call sf_int%compute_values (value, &
x=[0.1_default], xb=[0.9_default], scale=0._default)
write (u, "(A)")
write (u, "(A)", advance="no") "value (E=500, x=0.1) ="
write (u, "(9(1x," // FMT_19 // "))") value
write (u, "(A)")
write (u, "(A)") "* Initialize spectrum object"
write (u, "(A)")
deallocate (value)
call sf_int%final ()
deallocate (sf_int)
deallocate (data)
allocate (sf_test_spectrum_data_t :: data)
select type (data)
type is (sf_test_spectrum_data_t)
call data%init (model, pdg_in, with_radiation=.false.)
end select
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
write (u, "(A)") "* Probe spectrum: states"
write (u, "(A)")
write (u, "(A,I0)") "n_states = ", sf_int%get_n_states ()
write (u, "(A,I0)") "n_in = ", sf_int%get_n_in ()
write (u, "(A,I0)") "n_rad = ", sf_int%get_n_rad ()
write (u, "(A,I0)") "n_out = ", sf_int%get_n_out ()
write (u, "(A)")
write (u, "(A)", advance="no") "state(1) = "
call quantum_numbers_write (sf_int%get_state (1), u)
write (u, *)
allocate (value (sf_int%get_n_states ()))
call sf_int%compute_value (1, value(1), &
E = [500._default, 500._default], &
x = [0.5_default, 0.6_default], &
xb= [0.5_default, 0.4_default], &
scale = 0._default)
write (u, "(A)")
write (u, "(A)", advance="no") "value (E=500,500, x=0.5,0.6) ="
write (u, "(9(1x," // FMT_19 // "))") value
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_7"
end subroutine sf_base_7
@ %def sf_base_7
@
\subsubsection{Structure function chain configuration}
<<SF base: execute tests>>=
call test (sf_base_8, "sf_base_8", &
"structure function chain configuration", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_8
<<SF base: tests>>=
subroutine sf_base_8 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
type(beam_data_t), target :: beam_data
class(sf_data_t), allocatable, target :: data_strfun
class(sf_data_t), allocatable, target :: data_spectrum
type(sf_config_t), dimension(:), allocatable :: sf_config
type(sf_chain_t) :: sf_chain
write (u, "(A)") "* Test output: sf_base_8"
write (u, "(A)") "* Purpose: set up a structure-function chain"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
call beam_data%init_sqrts (1000._default, [flv, flv])
allocate (sf_test_data_t :: data_strfun)
select type (data_strfun)
type is (sf_test_data_t)
call data_strfun%init (model, pdg_in)
end select
allocate (sf_test_spectrum_data_t :: data_spectrum)
select type (data_spectrum)
type is (sf_test_spectrum_data_t)
call data_spectrum%init (model, pdg_in, with_radiation=.true.)
end select
write (u, "(A)") "* Set up chain with beams only"
write (u, "(A)")
call sf_chain%init (beam_data)
call write_separator (u, 2)
call sf_chain%write (u)
call write_separator (u, 2)
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Set up chain with structure function"
write (u, "(A)")
allocate (sf_config (1))
call sf_config(1)%init ([1], data_strfun)
call sf_chain%init (beam_data, sf_config)
call write_separator (u, 2)
call sf_chain%write (u)
call write_separator (u, 2)
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Set up chain with spectrum and structure function"
write (u, "(A)")
deallocate (sf_config)
allocate (sf_config (2))
call sf_config(1)%init ([1,2], data_spectrum)
call sf_config(2)%init ([2], data_strfun)
call sf_chain%init (beam_data, sf_config)
call write_separator (u, 2)
call sf_chain%write (u)
call write_separator (u, 2)
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_8"
end subroutine sf_base_8
@ %def sf_base_8
@
\subsubsection{Structure function instance configuration}
We create a structure-function chain instance which implements a
configured structure-function chain. We link the momentum entries in
the interactions and compute kinematics.
We do not actually connect the interactions and create evaluators. We
skip this step and manually advance the status of the chain instead.
<<SF base: execute tests>>=
call test (sf_base_9, "sf_base_9", &
"structure function chain instance", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_9
<<SF base: tests>>=
subroutine sf_base_9 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
type(beam_data_t), target :: beam_data
class(sf_data_t), allocatable, target :: data_strfun
class(sf_data_t), allocatable, target :: data_spectrum
type(sf_config_t), dimension(:), allocatable, target :: sf_config
type(sf_chain_t), target :: sf_chain
type(sf_chain_instance_t), target :: sf_chain_instance
type(sf_channel_t), dimension(2) :: sf_channel
type(vector4_t), dimension(2) :: p
integer :: j
write (u, "(A)") "* Test output: sf_base_9"
write (u, "(A)") "* Purpose: set up a structure-function chain &
&and create an instance"
write (u, "(A)") "* compute kinematics"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
call beam_data%init_sqrts (1000._default, [flv, flv])
allocate (sf_test_data_t :: data_strfun)
select type (data_strfun)
type is (sf_test_data_t)
call data_strfun%init (model, pdg_in)
end select
allocate (sf_test_spectrum_data_t :: data_spectrum)
select type (data_spectrum)
type is (sf_test_spectrum_data_t)
call data_spectrum%init (model, pdg_in, with_radiation=.true.)
end select
write (u, "(A)") "* Set up chain with beams only"
write (u, "(A)")
call sf_chain%init (beam_data)
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_chain_instance%link_interactions ()
sf_chain_instance%status = SF_DONE_CONNECTIONS
call sf_chain_instance%compute_kinematics (1, [real(default) ::])
call write_separator (u, 2)
call sf_chain%write (u)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
call sf_chain_instance%get_out_momenta (p)
write (u, "(A)")
write (u, "(A)") "* Outgoing momenta:"
do j = 1, 2
write (u, "(A)")
call vector4_write (p(j), u)
end do
call sf_chain_instance%final ()
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Set up chain with structure function"
write (u, "(A)")
allocate (sf_config (1))
call sf_config(1)%init ([1], data_strfun)
call sf_chain%init (beam_data, sf_config)
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_channel(1)%init (1)
call sf_channel(1)%activate_mapping ([1])
call sf_chain_instance%set_channel (1, sf_channel(1))
call sf_chain_instance%link_interactions ()
sf_chain_instance%status = SF_DONE_CONNECTIONS
call sf_chain_instance%compute_kinematics (1, [0.8_default])
call write_separator (u, 2)
call sf_chain%write (u)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
call sf_chain_instance%get_out_momenta (p)
write (u, "(A)")
write (u, "(A)") "* Outgoing momenta:"
do j = 1, 2
write (u, "(A)")
call vector4_write (p(j), u)
end do
call sf_chain_instance%final ()
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Set up chain with spectrum and structure function"
write (u, "(A)")
deallocate (sf_config)
allocate (sf_config (2))
call sf_config(1)%init ([1,2], data_spectrum)
call sf_config(2)%init ([2], data_strfun)
call sf_chain%init (beam_data, sf_config)
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_channel(2)%init (2)
call sf_channel(2)%activate_mapping ([2])
call sf_chain_instance%set_channel (1, sf_channel(2))
call sf_chain_instance%link_interactions ()
sf_chain_instance%status = SF_DONE_CONNECTIONS
call sf_chain_instance%compute_kinematics &
(1, [0.5_default, 0.6_default, 0.8_default])
call write_separator (u, 2)
call sf_chain%write (u)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
call sf_chain_instance%get_out_momenta (p)
write (u, "(A)")
write (u, "(A)") "* Outgoing momenta:"
do j = 1, 2
write (u, "(A)")
call vector4_write (p(j), u)
end do
call sf_chain_instance%final ()
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_9"
end subroutine sf_base_9
@ %def sf_base_9
@
\subsubsection{Structure function chain mappings}
Set up a structure function chain instance with a pair of
single-particle structure functions. We test different global
mappings for this setup.
Again, we skip evaluators.
<<SF base: execute tests>>=
call test (sf_base_10, "sf_base_10", &
"structure function chain mapping", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_10
<<SF base: tests>>=
subroutine sf_base_10 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
type(beam_data_t), target :: beam_data
class(sf_data_t), allocatable, target :: data_strfun
type(sf_config_t), dimension(:), allocatable, target :: sf_config
type(sf_chain_t), target :: sf_chain
type(sf_chain_instance_t), target :: sf_chain_instance
type(sf_channel_t), dimension(2) :: sf_channel
real(default), dimension(2) :: x_saved
write (u, "(A)") "* Test output: sf_base_10"
write (u, "(A)") "* Purpose: set up a structure-function chain"
write (u, "(A)") "* and check mappings"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
call beam_data%init_sqrts (1000._default, [flv, flv])
allocate (sf_test_data_t :: data_strfun)
select type (data_strfun)
type is (sf_test_data_t)
call data_strfun%init (model, pdg_in)
end select
write (u, "(A)") "* Set up chain with structure function pair &
&and standard mapping"
write (u, "(A)")
allocate (sf_config (2))
call sf_config(1)%init ([1], data_strfun)
call sf_config(2)%init ([2], data_strfun)
call sf_chain%init (beam_data, sf_config)
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_channel(1)%init (2)
call sf_channel(1)%set_s_mapping ([1,2])
call sf_chain_instance%set_channel (1, sf_channel(1))
call sf_chain_instance%link_interactions ()
sf_chain_instance%status = SF_DONE_CONNECTIONS
call sf_chain_instance%compute_kinematics (1, [0.8_default, 0.6_default])
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
write (u, "(A)")
write (u, "(A)") "* Invert the kinematics calculation"
write (u, "(A)")
x_saved = sf_chain_instance%x
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_channel(2)%init (2)
call sf_channel(2)%set_s_mapping ([1, 2])
call sf_chain_instance%set_channel (1, sf_channel(2))
call sf_chain_instance%link_interactions ()
sf_chain_instance%status = SF_DONE_CONNECTIONS
call sf_chain_instance%inverse_kinematics (x_saved, 1 - x_saved)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
call sf_chain_instance%final ()
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_10"
end subroutine sf_base_10
@ %def sf_base_10
@
\subsubsection{Structure function chain evaluation}
Here, we test the complete workflow for structure-function chains.
First, we create the template chain, then initialize an instance. We
set up links, mask, and evaluators. Finally, we set kinematics and
evaluate the matrix elements and their products.
<<SF base: execute tests>>=
call test (sf_base_11, "sf_base_11", &
"structure function chain evaluation", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_11
<<SF base: tests>>=
subroutine sf_base_11 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
type(beam_data_t), target :: beam_data
class(sf_data_t), allocatable, target :: data_strfun
class(sf_data_t), allocatable, target :: data_spectrum
type(sf_config_t), dimension(:), allocatable, target :: sf_config
type(sf_chain_t), target :: sf_chain
type(sf_chain_instance_t), target :: sf_chain_instance
type(sf_channel_t), dimension(2) :: sf_channel
type(particle_set_t) :: pset
type(interaction_t), pointer :: int
logical :: ok
write (u, "(A)") "* Test output: sf_base_11"
write (u, "(A)") "* Purpose: set up a structure-function chain"
write (u, "(A)") "* create an instance and evaluate"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
call beam_data%init_sqrts (1000._default, [flv, flv])
allocate (sf_test_data_t :: data_strfun)
select type (data_strfun)
type is (sf_test_data_t)
call data_strfun%init (model, pdg_in)
end select
allocate (sf_test_spectrum_data_t :: data_spectrum)
select type (data_spectrum)
type is (sf_test_spectrum_data_t)
call data_spectrum%init (model, pdg_in, with_radiation=.true.)
end select
write (u, "(A)") "* Set up chain with beams only"
write (u, "(A)")
call sf_chain%init (beam_data)
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_chain_instance%link_interactions ()
call sf_chain_instance%exchange_mask ()
call sf_chain_instance%init_evaluators ()
call sf_chain_instance%compute_kinematics (1, [real(default) ::])
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
int => sf_chain_instance%get_out_int_ptr ()
call pset%init (ok, int, int, FM_IGNORE_HELICITY, &
[0._default, 0._default], .false., .true.)
call sf_chain_instance%final ()
write (u, "(A)")
write (u, "(A)") "* Particle content:"
write (u, "(A)")
call write_separator (u)
call pset%write (u)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Recover chain:"
write (u, "(A)")
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_chain_instance%link_interactions ()
call sf_chain_instance%exchange_mask ()
call sf_chain_instance%init_evaluators ()
int => sf_chain_instance%get_out_int_ptr ()
call pset%fill_interaction (int, 2, check_match=.false.)
call sf_chain_instance%recover_kinematics (1)
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
call pset%final ()
call sf_chain_instance%final ()
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)")
write (u, "(A)")
write (u, "(A)") "* Set up chain with structure function"
write (u, "(A)")
allocate (sf_config (1))
call sf_config(1)%init ([1], data_strfun)
call sf_chain%init (beam_data, sf_config)
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_channel(1)%init (1)
call sf_channel(1)%activate_mapping ([1])
call sf_chain_instance%set_channel (1, sf_channel(1))
call sf_chain_instance%link_interactions ()
call sf_chain_instance%exchange_mask ()
call sf_chain_instance%init_evaluators ()
call sf_chain_instance%compute_kinematics (1, [0.8_default])
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
int => sf_chain_instance%get_out_int_ptr ()
call pset%init (ok, int, int, FM_IGNORE_HELICITY, &
[0._default, 0._default], .false., .true.)
call sf_chain_instance%final ()
write (u, "(A)")
write (u, "(A)") "* Particle content:"
write (u, "(A)")
call write_separator (u)
call pset%write (u)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Recover chain:"
write (u, "(A)")
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_channel(1)%init (1)
call sf_channel(1)%activate_mapping ([1])
call sf_chain_instance%set_channel (1, sf_channel(1))
call sf_chain_instance%link_interactions ()
call sf_chain_instance%exchange_mask ()
call sf_chain_instance%init_evaluators ()
int => sf_chain_instance%get_out_int_ptr ()
call pset%fill_interaction (int, 2, check_match=.false.)
call sf_chain_instance%recover_kinematics (1)
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
call pset%final ()
call sf_chain_instance%final ()
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)")
write (u, "(A)")
write (u, "(A)") "* Set up chain with spectrum and structure function"
write (u, "(A)")
deallocate (sf_config)
allocate (sf_config (2))
call sf_config(1)%init ([1,2], data_spectrum)
call sf_config(2)%init ([2], data_strfun)
call sf_chain%init (beam_data, sf_config)
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_channel(2)%init (2)
call sf_channel(2)%activate_mapping ([2])
call sf_chain_instance%set_channel (1, sf_channel(2))
call sf_chain_instance%link_interactions ()
call sf_chain_instance%exchange_mask ()
call sf_chain_instance%init_evaluators ()
call sf_chain_instance%compute_kinematics &
(1, [0.5_default, 0.6_default, 0.8_default])
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
int => sf_chain_instance%get_out_int_ptr ()
call pset%init (ok, int, int, FM_IGNORE_HELICITY, &
[0._default, 0._default], .false., .true.)
call sf_chain_instance%final ()
write (u, "(A)")
write (u, "(A)") "* Particle content:"
write (u, "(A)")
call write_separator (u)
call pset%write (u)
call write_separator (u)
write (u, "(A)")
write (u, "(A)") "* Recover chain:"
write (u, "(A)")
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_channel(2)%init (2)
call sf_channel(2)%activate_mapping ([2])
call sf_chain_instance%set_channel (1, sf_channel(2))
call sf_chain_instance%link_interactions ()
call sf_chain_instance%exchange_mask ()
call sf_chain_instance%init_evaluators ()
int => sf_chain_instance%get_out_int_ptr ()
call pset%fill_interaction (int, 2, check_match=.false.)
call sf_chain_instance%recover_kinematics (1)
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
call pset%final ()
call sf_chain_instance%final ()
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_11"
end subroutine sf_base_11
@ %def sf_base_11
@
\subsubsection{Multichannel case}
We set up a structure-function chain as before, but with three
different parameterizations. The first instance is without mappings,
the second one with single-particle mappings, and the third one with
two-particle mappings.
<<SF base: execute tests>>=
call test (sf_base_12, "sf_base_12", &
"multi-channel structure function chain", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_12
<<SF base: tests>>=
subroutine sf_base_12 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
type(beam_data_t), target :: beam_data
class(sf_data_t), allocatable, target :: data
type(sf_config_t), dimension(:), allocatable, target :: sf_config
type(sf_chain_t), target :: sf_chain
type(sf_chain_instance_t), target :: sf_chain_instance
real(default), dimension(2) :: x_saved
real(default), dimension(2,3) :: p_saved
type(sf_channel_t), dimension(:), allocatable :: sf_channel
write (u, "(A)") "* Test output: sf_base_12"
write (u, "(A)") "* Purpose: set up and evaluate a multi-channel &
&structure-function chain"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
call beam_data%init_sqrts (1000._default, [flv, flv])
allocate (sf_test_data_t :: data)
select type (data)
type is (sf_test_data_t)
call data%init (model, pdg_in)
end select
write (u, "(A)") "* Set up chain with structure function pair &
&and three different mappings"
write (u, "(A)")
allocate (sf_config (2))
call sf_config(1)%init ([1], data)
call sf_config(2)%init ([2], data)
call sf_chain%init (beam_data, sf_config)
call sf_chain_instance%init (sf_chain, n_channel = 3)
call allocate_sf_channels (sf_channel, n_channel = 3, n_strfun = 2)
! channel 1: no mapping
call sf_chain_instance%set_channel (1, sf_channel(1))
! channel 2: single-particle mappings
call sf_channel(2)%activate_mapping ([1,2])
! call sf_chain_instance%activate_mapping (2, [1,2])
call sf_chain_instance%set_channel (2, sf_channel(2))
! channel 3: two-particle mapping
call sf_channel(3)%set_s_mapping ([1,2])
! call sf_chain_instance%set_s_mapping (3, [1, 2])
call sf_chain_instance%set_channel (3, sf_channel(3))
call sf_chain_instance%link_interactions ()
call sf_chain_instance%exchange_mask ()
call sf_chain_instance%init_evaluators ()
write (u, "(A)") "* Compute kinematics in channel 1 and evaluate"
write (u, "(A)")
call sf_chain_instance%compute_kinematics (1, [0.8_default, 0.6_default])
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
write (u, "(A)")
write (u, "(A)") "* Invert the kinematics calculation"
write (u, "(A)")
x_saved = sf_chain_instance%x
call sf_chain_instance%inverse_kinematics (x_saved, 1 - x_saved)
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
write (u, "(A)")
write (u, "(A)") "* Compute kinematics in channel 2 and evaluate"
write (u, "(A)")
p_saved = sf_chain_instance%p
call sf_chain_instance%compute_kinematics (2, p_saved(:,2))
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
write (u, "(A)")
write (u, "(A)") "* Compute kinematics in channel 3 and evaluate"
write (u, "(A)")
call sf_chain_instance%compute_kinematics (3, p_saved(:,3))
call sf_chain_instance%evaluate (scale=0._default)
call write_separator (u, 2)
call sf_chain_instance%write (u)
call write_separator (u, 2)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_chain_instance%final ()
call sf_chain%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_12"
end subroutine sf_base_12
@ %def sf_base_12
@
\subsubsection{Generated spectrum}
Construct and evaluate a structure function object for a pair spectrum
which is evaluated as a beam-event generator.
<<SF base: execute tests>>=
call test (sf_base_13, "sf_base_13", &
"pair spectrum generator", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_13
<<SF base: tests>>=
subroutine sf_base_13 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t), dimension(2) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f, x_free
write (u, "(A)") "* Test output: sf_base_13"
write (u, "(A)") "* Purpose: initialize and fill &
&a pair generator object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
allocate (sf_test_generator_data_t :: data)
select type (data)
type is (sf_test_generator_data_t)
call data%init (model, pdg_in)
end select
write (u, "(A)") "* Initialize generator object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
write (u, "(A)") "* Generate free r values"
write (u, "(A)")
x_free = 1
call sf_int%generate_free (r, rb, x_free)
write (u, "(A)") "* Initialize incoming momenta with sqrts=1000"
E = 500
k(1) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
k(2) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call sf_int%seed_kinematics (k)
write (u, "(A)")
write (u, "(A)") "* Complete kinematics"
write (u, "(A)")
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A,9(1x,F10.7))") "xf=", x_free
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call reset_interaction_counter ()
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%seed_kinematics (k)
call sf_int%set_momenta (q, outgoing=.true.)
x_free = 1
call sf_int%recover_x (x, xb, x_free)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "xf=", x_free
write (u, "(A)")
write (u, "(A)") "* Compute inverse kinematics &
&and evaluate"
write (u, "(A)")
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%apply (scale=0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_13"
end subroutine sf_base_13
@ %def sf_base_13
@
\subsubsection{Structure function chain evaluation}
Here, we test the complete workflow for a structure-function chain
with generator. First, we create the template chain, then initialize
an instance. We set up links, mask, and evaluators. Finally, we set
kinematics and evaluate the matrix elements and their products.
<<SF base: execute tests>>=
call test (sf_base_14, "sf_base_14", &
"structure function generator evaluation", &
u, results)
<<SF base: test declarations>>=
public :: sf_base_14
<<SF base: tests>>=
subroutine sf_base_14 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
type(beam_data_t), target :: beam_data
class(sf_data_t), allocatable, target :: data_strfun
class(sf_data_t), allocatable, target :: data_generator
type(sf_config_t), dimension(:), allocatable, target :: sf_config
real(default), dimension(:), allocatable :: p_in
type(sf_chain_t), target :: sf_chain
type(sf_chain_instance_t), target :: sf_chain_instance
write (u, "(A)") "* Test output: sf_base_14"
write (u, "(A)") "* Purpose: set up a structure-function chain"
write (u, "(A)") "* create an instance and evaluate"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_test ()
call flv%init (25, model)
pdg_in = 25
call reset_interaction_counter ()
call beam_data%init_sqrts (1000._default, [flv, flv])
allocate (sf_test_data_t :: data_strfun)
select type (data_strfun)
type is (sf_test_data_t)
call data_strfun%init (model, pdg_in)
end select
allocate (sf_test_generator_data_t :: data_generator)
select type (data_generator)
type is (sf_test_generator_data_t)
call data_generator%init (model, pdg_in)
end select
write (u, "(A)") "* Set up chain with generator and structure function"
write (u, "(A)")
allocate (sf_config (2))
call sf_config(1)%init ([1,2], data_generator)
call sf_config(2)%init ([2], data_strfun)
call sf_chain%init (beam_data, sf_config)
call sf_chain_instance%init (sf_chain, n_channel = 1)
call sf_chain_instance%link_interactions ()
call sf_chain_instance%exchange_mask ()
call sf_chain_instance%init_evaluators ()
write (u, "(A)") "* Inject integration parameter"
write (u, "(A)")
allocate (p_in (sf_chain%get_n_bound ()), source = 0.9_default)
write (u, "(A,9(1x,F10.7))") "p_in =", p_in
write (u, "(A)")
write (u, "(A)") "* Evaluate"
write (u, "(A)")
call sf_chain_instance%compute_kinematics (1, p_in)
call sf_chain_instance%evaluate (scale=0._default)
call sf_chain_instance%write (u)
write (u, "(A)")
write (u, "(A)") "* Extract integration parameter"
write (u, "(A)")
call sf_chain_instance%get_mcpar (1, p_in)
write (u, "(A,9(1x,F10.7))") "p_in =", p_in
call sf_chain_instance%final ()
call sf_chain%final ()
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_base_14"
end subroutine sf_base_14
@ %def sf_base_14
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Photon radiation: ISR}
<<[[sf_isr.f90]]>>=
<<File header>>
module sf_isr
<<Use kinds>>
<<Use strings>>
use io_units
use constants, only: pi
use format_defs, only: FMT_15, FMT_19
use numeric_utils
use diagnostics
use physics_defs, only: PHOTON
use lorentz
use sm_physics, only: Li2
use pdg_arrays
use model_data
use flavors
use colors
use quantum_numbers
use polarizations
use sf_aux
use sf_mappings
use sf_base
use electron_pdfs
<<Standard module head>>
<<SF isr: public>>
<<SF isr: parameters>>
<<SF isr: types>>
contains
<<SF isr: procedures>>
end module sf_isr
@ %def sf_isr
@
\subsection{Physics}
The ISR structure function is in the most crude approximation (LLA
without $\alpha$ corrections, i.e. $\epsilon^0$)
\begin{equation}
f_0(x) = \epsilon (1-x)^{-1+\epsilon} \qquad\text{with}\qquad
\epsilon = \frac{\alpha}{\pi}q_e^2\ln\frac{s}{m^2},
\end{equation}
where $m$ is the mass of the incoming (and outgoing) particle, which
is initially assumed on-shell.
In $f_0(x)$, there is an integrable singularity at $x=1$ which does
not spoil the integration, but would lead to an unbounded $f_{\rm
max}$. Therefore, we map this singularity like
\begin{equation}\label{ISR-mapping}
x = 1 - (1-x')^{1/\epsilon}
\end{equation}
such that
\begin{equation}
\int dx\,f_0(x) = \int dx'
\end{equation}
For the detailed form of the QED ISR structure function
cf. Chap.~\ref{chap:qed_pdf}.
\subsection{Implementation}
In the concrete implementation, the zeroth order mapping
(\ref{ISR-mapping}) is implemented, and the Jacobian is equal to
$f_i(x)/f_0(x)$. This can be written as
\begin{align}
\frac{f_0(x)}{f_0(x)} &= 1 \\
\frac{f_1(x)}{f_0(x)} &= 1 + \frac34\epsilon - \frac{1-x^2}{2(1-x')} \\
\begin{split}\label{ISR-f2}
\frac{f_2(x)}{f_0(x)} &= 1 + \frac34\epsilon
+ \frac{27 - 8\pi^2}{96}\epsilon^2
- \frac{1-x^2}{2(1-x')} \\
&\quad - \frac{(1+3x^2)\ln x
+ (1-x)\left(4(1+x)\ln(1-x) + 5 + x\right)}{8(1-x')}\epsilon
\end{split}
\end{align}
%'
For $x=1$ (i.e., numerically indistinguishable from $1$), this reduces to
\begin{align}
\frac{f_0(x)}{f_0(x)} &= 1 \\
\frac{f_1(x)}{f_0(x)} &= 1 + \frac34\epsilon \\
\frac{f_2(x)}{f_0(x)} &= 1 + \frac34\epsilon
+ \frac{27 - 8\pi^2}{96}\epsilon^2
\end{align}
The last line in (\ref{ISR-f2}) is zero for
\begin{equation}
x_{\rm min} = 0.00714053329734592839549879772019
\end{equation}
(Mathematica result), independent of $\epsilon$. For $x$ values less
than this we ignore this correction because of the logarithmic
singularity which should in principle be resummed.
\subsection{The ISR data block}
<<SF isr: public>>=
public :: isr_data_t
<<SF isr: types>>=
type, extends (sf_data_t) :: isr_data_t
private
class(model_data_t), pointer :: model => null ()
type(flavor_t), dimension(:), allocatable :: flv_in
type(qed_pdf_t) :: pdf
real(default) :: alpha = 0
real(default) :: q_max = 0
real(default) :: real_mass = 0
real(default) :: mass = 0
real(default) :: eps = 0
real(default) :: log = 0
logical :: recoil = .false.
logical :: keep_energy = .true.
integer :: order = 3
integer :: error = NONE
contains
<<SF isr: isr data: TBP>>
end type isr_data_t
@ %def isr_data_t
@ Error codes
<<SF isr: parameters>>=
integer, parameter :: NONE = 0
integer, parameter :: ZERO_MASS = 1
integer, parameter :: Q_MAX_TOO_SMALL = 2
integer, parameter :: EPS_TOO_LARGE = 3
integer, parameter :: INVALID_ORDER = 4
integer, parameter :: CHARGE_MIX = 5
integer, parameter :: CHARGE_ZERO = 6
integer, parameter :: MASS_MIX = 7
@ Generate flavor-dependent ISR data:
<<SF isr: isr data: TBP>>=
procedure :: init => isr_data_init
<<SF isr: procedures>>=
subroutine isr_data_init (data, model, pdg_in, alpha, q_max, &
mass, order, recoil, keep_energy)
class(isr_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), intent(in) :: pdg_in
real(default), intent(in) :: alpha
real(default), intent(in) :: q_max
real(default), intent(in), optional :: mass
integer, intent(in), optional :: order
logical, intent(in), optional :: recoil
logical, intent(in), optional :: keep_energy
integer :: i, n_flv
real(default) :: charge
data%model => model
n_flv = pdg_array_get_length (pdg_in)
allocate (data%flv_in (n_flv))
do i = 1, n_flv
call data%flv_in(i)%init (pdg_array_get (pdg_in, i), model)
end do
data%alpha = alpha
data%q_max = q_max
if (present (order)) then
call data%set_order (order)
end if
if (present (recoil)) then
data%recoil = recoil
end if
if (present (keep_energy)) then
data%keep_energy = keep_energy
end if
data%real_mass = data%flv_in(1)%get_mass ()
if (present (mass)) then
if (mass > 0) then
data%mass = mass
else
data%mass = data%real_mass
if (any (data%flv_in%get_mass () /= data%mass)) then
data%error = MASS_MIX; return
end if
end if
else
data%mass = data%real_mass
if (any (data%flv_in%get_mass () /= data%mass)) then
data%error = MASS_MIX; return
end if
end if
if (vanishes (data%mass)) then
data%error = ZERO_MASS; return
else if (data%mass >= data%q_max) then
data%error = Q_MAX_TOO_SMALL; return
end if
data%log = log (1 + (data%q_max / data%mass)**2)
charge = data%flv_in(1)%get_charge ()
if (any (abs (data%flv_in%get_charge ()) /= abs (charge))) then
data%error = CHARGE_MIX; return
else if (charge == 0) then
data%error = CHARGE_ZERO; return
end if
data%eps = data%alpha / pi * charge ** 2 &
* (2 * log (data%q_max / data%mass) - 1)
if (data%eps > 1) then
data%error = EPS_TOO_LARGE; return
end if
call data%pdf%init &
(data%mass, data%alpha, charge, data%q_max, data%order)
end subroutine isr_data_init
@ %def isr_data_init
@ Explicitly set ISR order
<<SF isr: isr data: TBP>>=
procedure :: set_order => isr_data_set_order
<<SF isr: procedures>>=
elemental subroutine isr_data_set_order (data, order)
class(isr_data_t), intent(inout) :: data
integer, intent(in) :: order
if (order < 0 .or. order > 3) then
data%error = INVALID_ORDER
else
data%order = order
end if
end subroutine isr_data_set_order
@ %def isr_data_set_order
@ Handle error conditions. Should always be done after
initialization, unless we are sure everything is ok.
<<SF isr: isr data: TBP>>=
procedure :: check => isr_data_check
<<SF isr: procedures>>=
subroutine isr_data_check (data)
class(isr_data_t), intent(in) :: data
select case (data%error)
case (ZERO_MASS)
call msg_fatal ("ISR: Particle mass is zero")
case (Q_MAX_TOO_SMALL)
call msg_fatal ("ISR: Particle mass exceeds Qmax")
case (EPS_TOO_LARGE)
call msg_fatal ("ISR: Expansion parameter too large, " // &
"perturbative expansion breaks down")
case (INVALID_ORDER)
call msg_error ("ISR: LLA order invalid (valid values are 0,1,2,3)")
case (MASS_MIX)
call msg_fatal ("ISR: Incoming particle masses must be uniform")
case (CHARGE_MIX)
call msg_fatal ("ISR: Incoming particle charges must be uniform")
case (CHARGE_ZERO)
call msg_fatal ("ISR: Incoming particle must be charged")
end select
end subroutine isr_data_check
@ %def isr_data_check
@ Output
<<SF isr: isr data: TBP>>=
procedure :: write => isr_data_write
<<SF isr: procedures>>=
subroutine isr_data_write (data, unit, verbose)
class(isr_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "ISR data:"
if (allocated (data%flv_in)) then
write (u, "(3x,A)", advance="no") " flavor = "
do i = 1, size (data%flv_in)
if (i > 1) write (u, "(',',1x)", advance="no")
call data%flv_in(i)%write (u)
end do
write (u, *)
write (u, "(3x,A," // FMT_19 // ")") " alpha = ", data%alpha
write (u, "(3x,A," // FMT_19 // ")") " q_max = ", data%q_max
write (u, "(3x,A," // FMT_19 // ")") " mass = ", data%mass
write (u, "(3x,A," // FMT_19 // ")") " eps = ", data%eps
write (u, "(3x,A," // FMT_19 // ")") " log = ", data%log
write (u, "(3x,A,I2)") " order = ", data%order
write (u, "(3x,A,L2)") " recoil = ", data%recoil
write (u, "(3x,A,L2)") " keep en. = ", data%keep_energy
else
write (u, "(3x,A)") "[undefined]"
end if
end subroutine isr_data_write
@ %def isr_data_write
@ For ISR, there is the option to generate transverse momentum is
generated. Hence, there can be up to three parameters, $x$, and two
angles.
<<SF isr: isr data: TBP>>=
procedure :: get_n_par => isr_data_get_n_par
<<SF isr: procedures>>=
function isr_data_get_n_par (data) result (n)
class(isr_data_t), intent(in) :: data
integer :: n
if (data%recoil) then
n = 3
else
n = 1
end if
end function isr_data_get_n_par
@ %def isr_data_get_n_par
@ Return the outgoing particles PDG codes. For ISR, these are
identical to the incoming particles.
<<SF isr: isr data: TBP>>=
procedure :: get_pdg_out => isr_data_get_pdg_out
<<SF isr: procedures>>=
subroutine isr_data_get_pdg_out (data, pdg_out)
class(isr_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
pdg_out(1) = data%flv_in%get_pdg ()
end subroutine isr_data_get_pdg_out
@ %def isr_data_get_pdg_out
@ Return the [[eps]] value. We need it for an appropriate mapping of
structure-function parameters.
<<SF isr: isr data: TBP>>=
procedure :: get_eps => isr_data_get_eps
<<SF isr: procedures>>=
function isr_data_get_eps (data) result (eps)
class(isr_data_t), intent(in) :: data
real(default) :: eps
eps = data%eps
end function isr_data_get_eps
@ %def isr_data_get_eps
@ Allocate the interaction record.
<<SF isr: isr data: TBP>>=
procedure :: allocate_sf_int => isr_data_allocate_sf_int
<<SF isr: procedures>>=
subroutine isr_data_allocate_sf_int (data, sf_int)
class(isr_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (isr_t :: sf_int)
end subroutine isr_data_allocate_sf_int
@ %def isr_data_allocate_sf_int
@
\subsection{The ISR object}
The [[isr_t]] data type is a $1\to 2$ interaction, i.e., we allow for
single-photon emission only (but use the multi-photon resummed
radiator function). The particles are ordered as (incoming, photon,
outgoing).
There is no need to handle several flavors (and data blocks) in
parallel, since ISR is always applied immediately after beam
collision. (ISR for partons is accounted for by the PDFs themselves.)
Polarization is carried through, i.e., we retain the polarization of
the incoming particle and treat the emitted photon as unpolarized.
Color is trivially carried through. This implies that particles 1 and
3 should be locked together. For ISR we don't need the q variable.
<<SF isr: public>>=
public :: isr_t
<<SF isr: types>>=
type, extends (sf_int_t) :: isr_t
private
type(isr_data_t), pointer :: data => null ()
real(default) :: x = 0
real(default) :: xb= 0
contains
<<SF isr: isr: TBP>>
end type isr_t
@ %def isr_t
@ Type string: has to be here, but there is no string variable on which ISR
depends. Hence, a dummy routine.
<<SF isr: isr: TBP>>=
procedure :: type_string => isr_type_string
<<SF isr: procedures>>=
function isr_type_string (object) result (string)
class(isr_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "ISR: e+ e- ISR spectrum"
else
string = "ISR: [undefined]"
end if
end function isr_type_string
@ %def isr_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF isr: isr: TBP>>=
procedure :: write => isr_write
<<SF isr: procedures>>=
subroutine isr_write (object, unit, testflag)
class(isr_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
if (object%status >= SF_DONE_KINEMATICS) then
write (u, "(1x,A)") "SF parameters:"
write (u, "(3x,A," // FMT_15 // ")") "x =", object%x
write (u, "(3x,A," // FMT_15 // ")") "xb=", object%xb
end if
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "ISR data: [undefined]"
end if
end subroutine isr_write
@ %def isr_write
@ Explicitly set ISR order (for unit test).
<<SF isr: isr: TBP>>=
procedure :: set_order => isr_set_order
<<SF isr: procedures>>=
subroutine isr_set_order (object, order)
class(isr_t), intent(inout) :: object
integer, intent(in) :: order
call object%data%set_order (order)
call object%data%pdf%set_order (order)
end subroutine isr_set_order
@ %def isr_set_order
@
\subsection{Kinematics}
Set kinematics. If [[map]] is unset, the $r$ and $x$ values
coincide, and the Jacobian $f(r)$ were trivial. The ISR structure
function allows for a straightforward mapping of the unit interval.
So, to leading order, the structure function value is unity, but the
$x$ value is transformed. Higher orders affect the function value.
The structure function implementation applies the above mapping to the
input (random) number [[r]] to generate the momentum fraction [[x]]
and the function value [[f]]. For numerical stability reasons, we
also output [[xb]], which is $\bar x=1-x$.
For the ISR structure function, the mapping Jacobian cancels the
structure function (to order zero). We apply the cancellation
explicitly, therefore both the Jacobian [[f]] and the zeroth-order value
(see the [[apply]] method) are unity if mapping is turned on. If
mapping is turned off, the Jacobian [[f]] includes the value of the
(zeroth-order) structure function, and strongly peaked.
<<SF isr: isr: TBP>>=
procedure :: complete_kinematics => isr_complete_kinematics
<<SF isr: procedures>>=
subroutine isr_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(isr_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
real(default) :: eps
eps = sf_int%data%eps
if (map) then
call map_power_1 (sf_int%xb, f, rb(1), eps)
else
sf_int%xb = rb(1)
if (rb(1) > 0) then
f = 1
else
f = 0
end if
end if
sf_int%x = 1 - sf_int%xb
x(1) = sf_int%x
xb(1) = sf_int%xb
if (size (x) == 3) then
x(2:3) = r(2:3)
xb(2:3) = rb(2:3)
end if
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS)
sf_int%x = 0
sf_int%xb= 0
f = 0
end select
end subroutine isr_complete_kinematics
@ %def isr_complete_kinematics
@ Overriding the default method: we compute the [[x]] array from the
momentum configuration. In the specific case of ISR, we also set the
internally stored $x$ and $\bar x$ values, so they can be used in the
following routine.
<<SF isr: isr: TBP>>=
procedure :: recover_x => sf_isr_recover_x
<<SF isr: procedures>>=
subroutine sf_isr_recover_x (sf_int, x, xb, x_free)
class(isr_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(inout), optional :: x_free
call sf_int%base_recover_x (x, xb, x_free)
sf_int%x = x(1)
sf_int%xb = xb(1)
end subroutine sf_isr_recover_x
@ %def sf_isr_recover_x
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
For extracting $x$, we rely on the stored $\bar x$ value, since the
$x$ value in the argument is likely imprecise. This means that either
[[complete_kinematics]] or [[recover_x]] must be called first, for the
current sampling point (but maybe another channel).
<<SF isr: isr: TBP>>=
procedure :: inverse_kinematics => isr_inverse_kinematics
<<SF isr: procedures>>=
subroutine isr_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(isr_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
real(default) :: eps
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
eps = sf_int%data%eps
if (map) then
call map_power_inverse_1 (xb(1), f, rb(1), eps)
else
rb(1) = xb(1)
if (rb(1) > 0) then
f = 1
else
f = 0
end if
end if
r(1) = 1 - rb(1)
if (size(r) == 3) then
r(2:3) = x(2:3)
rb(2:3)= xb(2:3)
end if
if (set_mom) then
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS)
r = 0
rb= 0
f = 0
end select
end if
end subroutine isr_inverse_kinematics
@ %def isr_inverse_kinematics
@
<<SF isr: isr: TBP>>=
procedure :: init => isr_init
<<SF isr: procedures>>=
subroutine isr_init (sf_int, data)
class(isr_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(3) :: mask
integer, dimension(3) :: hel_lock
type(polarization_t), target :: pol
type(quantum_numbers_t), dimension(1) :: qn_fc
type(flavor_t) :: flv_photon
type(color_t) :: col_photon
type(quantum_numbers_t) :: qn_hel, qn_photon, qn
type(polarization_iterator_t) :: it_hel
real(default) :: m2
integer :: i
mask = quantum_numbers_mask (.false., .false., &
mask_h = [.false., .true., .false.])
hel_lock = [3, 0, 1]
select type (data)
type is (isr_data_t)
m2 = data%mass**2
call sf_int%base_init (mask, [m2], [0._default], [m2], &
hel_lock = hel_lock)
sf_int%data => data
call flv_photon%init (PHOTON, data%model)
call col_photon%init ()
call qn_photon%init (flv_photon, col_photon)
call qn_photon%tag_radiated ()
do i = 1, size (data%flv_in)
call pol%init_generic (data%flv_in(i))
call qn_fc(1)%init (&
flv = data%flv_in(i), &
col = color_from_flavor (data%flv_in(i), 1))
call it_hel%init (pol)
do while (it_hel%is_valid ())
qn_hel = it_hel%get_quantum_numbers ()
qn = qn_hel .merge. qn_fc(1)
call sf_int%add_state ([qn, qn_photon, qn])
call it_hel%advance ()
end do
! call pol%final () !!! Obsolete
end do
call sf_int%freeze ()
if (data%keep_energy) then
sf_int%on_shell_mode = KEEP_ENERGY
else
sf_int%on_shell_mode = KEEP_MOMENTUM
end if
call sf_int%set_incoming ([1])
call sf_int%set_radiated ([2])
call sf_int%set_outgoing ([3])
sf_int%status = SF_INITIAL
end select
end subroutine isr_init
@ %def isr_init
@
\subsection{ISR application}
For ISR, we could in principle compute kinematics and function value
in a single step. In order to be able to reweight matrix elements
including structure functions we split kinematics and structure
function calculation. The structure function works on a single beam,
assuming that the input momentum has been set.
For the structure-function evaluation, we rely on the fact that the
power mapping, which we apply in the kinematics method (if the [[map]]
flag is set), has a Jacobian which is just the inverse lowest-order
structure function. With mapping active, the two should cancel
exactly.
After splitting momenta, we set the outgoing momenta on-shell. We
choose to conserve momentum, so energy conservation may be violated.
<<SF isr: isr: TBP>>=
procedure :: apply => isr_apply
<<SF isr: procedures>>=
- subroutine isr_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine isr_apply (sf_int, scale, rescale, i_sub)
class(isr_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default) :: f, finv, x, xb, eps, rb
real(default) :: log_x, log_xb, x_2
associate (data => sf_int%data)
eps = sf_int%data%eps
x = sf_int%x
xb = sf_int%xb
call map_power_inverse_1 (xb, finv, rb, eps)
if (finv > 0) then
f = 1 / finv
else
f = 0
end if
call data%pdf%evolve_qed_pdf (x, xb, rb, f)
end associate
call sf_int%set_matrix_element (cmplx (f, kind=default))
sf_int%status = SF_EVALUATED
end subroutine isr_apply
@ %def isr_apply
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_isr_ut.f90]]>>=
<<File header>>
module sf_isr_ut
use unit_tests
use sf_isr_uti
<<Standard module head>>
<<SF isr: public test>>
contains
<<SF isr: test driver>>
end module sf_isr_ut
@ %def sf_isr_ut
@
<<[[sf_isr_uti.f90]]>>=
<<File header>>
module sf_isr_uti
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_12
use physics_defs, only: ELECTRON
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use interactions, only: interaction_pacify_momenta
use model_data
use sf_aux, only: KEEP_ENERGY
use sf_mappings
use sf_base
use sf_isr
<<Standard module head>>
<<SF isr: test declarations>>
contains
<<SF isr: tests>>
end module sf_isr_uti
@ %def sf_isr_ut
@ API: driver for the unit tests below.
<<SF isr: public test>>=
public :: sf_isr_test
<<SF isr: test driver>>=
subroutine sf_isr_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF isr: execute tests>>
end subroutine sf_isr_test
@ %def sf_isr_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF isr: execute tests>>=
call test (sf_isr_1, "sf_isr_1", &
"structure function configuration", &
u, results)
<<SF isr: test declarations>>=
public :: sf_isr_1
<<SF isr: tests>>=
subroutine sf_isr_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(pdg_array_t), dimension(1) :: pdg_out
integer, dimension(:), allocatable :: pdg1
class(sf_data_t), allocatable :: data
write (u, "(A)") "* Test output: sf_isr_1"
write (u, "(A)") "* Purpose: initialize and display &
&test structure function data"
write (u, "(A)")
write (u, "(A)") "* Create empty data object"
write (u, "(A)")
call model%init_qed_test ()
pdg_in = ELECTRON
allocate (isr_data_t :: data)
call data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize"
write (u, "(A)")
select type (data)
type is (isr_data_t)
call data%init (model, pdg_in, 1./137._default, 10._default, &
0.000511_default, order = 3, recoil = .false.)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
write (u, "(2x,99(1x,I0))") pdg1
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_isr_1"
end subroutine sf_isr_1
@ %def sf_isr_1
@
\subsubsection{Structure function without mapping}
Direct ISR evaluation. This is the use case for a double-beam
structure function. The parameter pair is mapped in the calling program.
<<SF isr: execute tests>>=
call test (sf_isr_2, "sf_isr_2", &
"no ISR mapping", &
u, results)
<<SF isr: test declarations>>=
public :: sf_isr_2
<<SF isr: tests>>=
subroutine sf_isr_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(flavor_t) :: flv
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f, f_isr
write (u, "(A)") "* Test output: sf_isr_2"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
pdg_in = ELECTRON
call flv%init (ELECTRON, model)
call reset_interaction_counter ()
allocate (isr_data_t :: data)
select type (data)
type is (isr_data_t)
call data%init (model, pdg_in, 1./137._default, 500._default, &
0.000511_default, order = 3, recoil = .false.)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.9, no ISR mapping, &
&collinear"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.9_default
rb = 1 - r
write (u, "(A,9(1x," // FMT_12 // "))") "r =", r
write (u, "(A,9(1x," // FMT_12 // "))") "rb=", rb
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A)")
write (u, "(A,9(1x," // FMT_12 // "))") "x =", x
write (u, "(A,9(1x," // FMT_12 // "))") "xb=", xb
write (u, "(A,9(1x," // FMT_12 // "))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Invert kinematics"
write (u, "(A)")
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x," // FMT_12 // "))") "r =", r
write (u, "(A,9(1x," // FMT_12 // "))") "rb=", rb
write (u, "(A,9(1x," // FMT_12 // "))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate ISR structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Structure-function value, default order"
write (u, "(A)")
f_isr = sf_int%get_matrix_element (1)
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr =", f_isr
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr * f_map =", f_isr * f
write (u, "(A)")
write (u, "(A)") "* Re-evaluate structure function, leading order"
write (u, "(A)")
select type (sf_int)
type is (isr_t)
call sf_int%set_order (0)
end select
call sf_int%apply (scale = 100._default)
f_isr = sf_int%get_matrix_element (1)
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr =", f_isr
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr * f_map =", f_isr * f
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_isr_2"
end subroutine sf_isr_2
@ %def sf_isr_2
@
\subsubsection{Structure function with mapping}
Apply the optimal ISR mapping. This is the use case for a single-beam
structure function.
<<SF isr: execute tests>>=
call test (sf_isr_3, "sf_isr_3", &
"ISR mapping", &
u, results)
<<SF isr: test declarations>>=
public :: sf_isr_3
<<SF isr: tests>>=
subroutine sf_isr_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f, f_isr
write (u, "(A)") "* Test output: sf_isr_3"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
call flv%init (ELECTRON, model)
pdg_in = ELECTRON
call reset_interaction_counter ()
allocate (isr_data_t :: data)
select type (data)
type is (isr_data_t)
call data%init (model, pdg_in, 1./137._default, 500._default, &
0.000511_default, order = 3, recoil = .false.)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.7, with ISR mapping, &
&collinear"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.7_default
rb = 1 - r
write (u, "(A,9(1x," // FMT_12 // "))") "r =", r
write (u, "(A,9(1x," // FMT_12 // "))") "rb=", rb
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
write (u, "(A)")
write (u, "(A,9(1x," // FMT_12 // "))") "x =", x
write (u, "(A,9(1x," // FMT_12 // "))") "xb=", xb
write (u, "(A,9(1x," // FMT_12 // "))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Invert kinematics"
write (u, "(A)")
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.true.)
write (u, "(A,9(1x," // FMT_12 // "))") "r =", r
write (u, "(A,9(1x," // FMT_12 // "))") "rb=", rb
write (u, "(A,9(1x," // FMT_12 // "))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate ISR structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Structure-function value, default order"
write (u, "(A)")
f_isr = sf_int%get_matrix_element (1)
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr =", f_isr
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr * f_map =", f_isr * f
write (u, "(A)")
write (u, "(A)") "* Re-evaluate structure function, leading order"
write (u, "(A)")
select type (sf_int)
type is (isr_t)
call sf_int%set_order (0)
end select
call sf_int%apply (scale = 100._default)
f_isr = sf_int%get_matrix_element (1)
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr =", f_isr
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr * f_map =", f_isr * f
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_isr_3"
end subroutine sf_isr_3
@ %def sf_isr_3
@
\subsubsection{Non-collinear ISR splitting}
Construct and display a structure function object based on the ISR
structure function. We blank out numerical fluctuations for 32bit.
<<SF isr: execute tests>>=
call test (sf_isr_4, "sf_isr_4", &
"ISR non-collinear", &
u, results)
<<SF isr: test declarations>>=
public :: sf_isr_4
<<SF isr: tests>>=
subroutine sf_isr_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f, f_isr
character(len=80) :: buffer
integer :: u_scratch, iostat
write (u, "(A)") "* Test output: sf_isr_4"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
call flv%init (ELECTRON, model)
pdg_in = ELECTRON
call reset_interaction_counter ()
write (u, "(A)")
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
allocate (isr_data_t :: data)
select type (data)
type is (isr_data_t)
call data%init (model, pdg_in, 1./137._default, 500._default, &
0.000511_default, order = 3, recoil = .true.)
end select
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5/0.5/0.25, with ISR mapping, "
write (u, "(A)") " non-coll., keeping energy"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = [0.5_default, 0.5_default, 0.25_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
call interaction_pacify_momenta (sf_int%interaction_t, 1e-10_default)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x and r from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.true.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A)")
write (u, "(A)") "* Evaluate ISR structure function"
write (u, "(A)")
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
call interaction_pacify_momenta (sf_int%interaction_t, 1e-10_default)
call sf_int%apply (scale = 10._default)
u_scratch = free_unit ()
open (u_scratch, status="scratch", action = "readwrite")
call sf_int%write (u_scratch, testflag = .true.)
rewind (u_scratch)
do
read (u_scratch, "(A)", iostat=iostat) buffer
if (iostat /= 0) exit
if (buffer(1:25) == " P = 0.000000E+00 9.57") then
buffer = replace (buffer, 26, "XXXX")
end if
if (buffer(1:25) == " P = 0.000000E+00 -9.57") then
buffer = replace (buffer, 26, "XXXX")
end if
write (u, "(A)") buffer
end do
close (u_scratch)
write (u, "(A)")
write (u, "(A)") "* Structure-function value"
write (u, "(A)")
f_isr = sf_int%get_matrix_element (1)
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr =", f_isr
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr * f_map =", f_isr * f
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_isr_4"
end subroutine sf_isr_4
@ %def sf_isr_4
@
\subsubsection{Structure function pair with mapping}
Apply the ISR mapping for a ISR pair.
structure function.
<<SF isr: execute tests>>=
call test (sf_isr_5, "sf_isr_5", &
"ISR pair mapping", &
u, results)
<<SF isr: test declarations>>=
public :: sf_isr_5
<<SF isr: tests>>=
subroutine sf_isr_5 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_mapping_t), allocatable :: mapping
class(sf_int_t), dimension(:), allocatable :: sf_int
type(vector4_t), dimension(2) :: k
real(default) :: E, f_map
real(default), dimension(:), allocatable :: p, pb, r, rb, x, xb
real(default), dimension(2) :: f, f_isr
integer :: i
write (u, "(A)") "* Test output: sf_isr_5"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
call flv%init (ELECTRON, model)
pdg_in = ELECTRON
call reset_interaction_counter ()
allocate (isr_data_t :: data)
select type (data)
type is (isr_data_t)
call data%init (model, pdg_in, 1./137._default, 500._default, &
0.000511_default, order = 3, recoil = .false.)
end select
allocate (sf_ip_mapping_t :: mapping)
select type (mapping)
type is (sf_ip_mapping_t)
select type (data)
type is (isr_data_t)
call mapping%init (eps = data%get_eps ())
end select
call mapping%set_index (1, 1)
call mapping%set_index (2, 2)
end select
call mapping%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
allocate (isr_t :: sf_int (2))
do i = 1, 2
call sf_int(i)%init (data)
call sf_int(i)%set_beam_index ([i])
end do
write (u, "(A)") "* Initialize incoming momenta with E=500"
write (u, "(A)")
E = 500
k(1) = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
k(2) = vector4_moving (E, - sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
do i = 1, 2
call vector4_write (k(i), u)
call sf_int(i)%seed_kinematics (k(i:i))
end do
write (u, "(A)")
write (u, "(A)") "* Set kinematics for p=[0.7,0.4], collinear"
write (u, "(A)")
allocate (p (2 * data%get_n_par ()))
allocate (pb(size (p)))
allocate (r (size (p)))
allocate (rb(size (p)))
allocate (x (size (p)))
allocate (xb(size (p)))
p = [0.7_default, 0.4_default]
pb= 1 - p
call mapping%compute (r, rb, f_map, p, pb)
write (u, "(A,9(1x," // FMT_12 // "))") "p =", p
write (u, "(A,9(1x," // FMT_12 // "))") "pb=", pb
write (u, "(A,9(1x," // FMT_12 // "))") "r =", r
write (u, "(A,9(1x," // FMT_12 // "))") "rb=", rb
write (u, "(A,9(1x," // FMT_12 // "))") "fm=", f_map
do i = 1, 2
call sf_int(i)%complete_kinematics (x(i:i), xb(i:i), f(i), r(i:i), rb(i:i), &
map=.false.)
end do
write (u, "(A)")
write (u, "(A,9(1x," // FMT_12 // "))") "x =", x
write (u, "(A,9(1x," // FMT_12 // "))") "xb=", xb
write (u, "(A,9(1x," // FMT_12 // "))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Invert kinematics"
write (u, "(A)")
do i = 1, 2
call sf_int(i)%inverse_kinematics (x(i:i), xb(i:i), f(i), r(i:i), rb(i:i), &
map=.false.)
end do
call mapping%inverse (r, rb, f_map, p, pb)
write (u, "(A,9(1x," // FMT_12 // "))") "p =", p
write (u, "(A,9(1x," // FMT_12 // "))") "pb=", pb
write (u, "(A,9(1x," // FMT_12 // "))") "r =", r
write (u, "(A,9(1x," // FMT_12 // "))") "rb=", rb
write (u, "(A,9(1x," // FMT_12 // "))") "fm=", f_map
write (u, "(A)")
write (u, "(A)") "* Evaluate ISR structure function"
call sf_int(1)%apply (scale = 100._default)
call sf_int(2)%apply (scale = 100._default)
write (u, "(A)")
write (u, "(A)") "* Structure function #1"
write (u, "(A)")
call sf_int(1)%write (u, testflag = .true.)
write (u, "(A)")
write (u, "(A)") "* Structure function #2"
write (u, "(A)")
call sf_int(2)%write (u, testflag = .true.)
write (u, "(A)")
write (u, "(A)") "* Structure-function value, default order"
write (u, "(A)")
do i = 1, 2
f_isr(i) = sf_int(i)%get_matrix_element (1)
end do
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr =", &
product (f_isr)
write (u, "(A,9(1x," // FMT_12 // "))") "f_isr * f_map =", &
product (f_isr * f) * f_map
write (u, "(A)")
write (u, "(A)") "* Cleanup"
do i = 1, 2
call sf_int(i)%final ()
end do
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_isr_5"
end subroutine sf_isr_5
@ %def sf_isr_5
@
\clearpage
%------------------------------------------------------------------------
\section{EPA}
<<[[sf_epa.f90]]>>=
<<File header>>
module sf_epa
<<Use kinds>>
<<Use strings>>
use io_units
use constants, only: pi
use format_defs, only: FMT_17, FMT_19
use numeric_utils
use diagnostics
use physics_defs, only: PHOTON
use lorentz
use pdg_arrays
use model_data
use flavors
use colors
use quantum_numbers
use state_matrices
use polarizations
use interactions
use sf_aux
use sf_base
<<Standard module head>>
<<SF epa: public>>
<<SF epa: parameters>>
<<SF epa: types>>
contains
<<SF epa: procedures>>
end module sf_epa
@ %def sf_epa
@
\subsection{Physics}
The EPA structure function for a photon inside an (elementary)
particle $p$ with energy $E$, mass $m$ and charge $q_p$ (e.g.,
electron) is given by ($\bar x \equiv 1-x$)
%% %\cite{Budnev:1974de}
%% \bibitem{Budnev:1974de}
%% V.~M.~Budnev, I.~F.~Ginzburg, G.~V.~Meledin and V.~G.~Serbo,
%% %``The Two photon particle production mechanism. Physical problems.
%% %Applications. Equivalent photon approximation,''
%% Phys.\ Rept.\ {\bf 15} (1974) 181.
%% %%CITATION = PRPLC,15,181;%%
\begin{multline}
\label{EPA}
f(x) =
\frac{\alpha}{\pi}\,q_p^2\,
\frac{1}{x}\,
\biggl[\left(\bar x + \frac{x^2}{2}\right)
\ln\frac{Q^2_{\rm max}}{Q^2_{\rm min}}
\\
- \left(1 - \frac{x}{2}\right)^2
\ln\frac{x^2+\frac{Q^2_{\rm max}}{E^2}}
{x^2+\frac{Q^2_{\rm min}}{E^2}}
- x^2\frac{m^2}{Q^2_{\rm min}}
\left(1 - \frac{Q^2_{\rm min}}{Q^2_{\rm max}}\right)
\biggr].
\end{multline}
If no explicit $Q$ bounds are provided, the kinematical bounds are
\begin{align}
-Q^2_{\rm max} &= t_0 = -2\bar x(E^2+p\bar p) + 2m^2 \approx -4\bar x E^2,
\\
-Q^2_{\rm min} &= t_1 = -2\bar x(E^2-p\bar p) + 2m^2
\approx
-\frac{x^2}{\bar x}m^2.
\end{align}
The second and third terms in (\ref{EPA}) are negative definite (and
subleading). Noting that $\bar x + x^2/2$ is bounded between
$1/2$ and $1$, we derive that $f(x)$ is always smaller than
\begin{equation}
\bar f(x) = \frac{\alpha}{\pi}\,q_p^2\,\frac{L - 2\ln x}{x}
\qquad\text{where}\qquad
L = \ln\frac{\min(4E_{\rm max}^2,Q^2_{\rm max})}{\max(m^2,Q_{\rm min}^2)},
\end{equation}
where we allow for explicit $Q$ bounds that narrow the kinematical range.
Therefore, we generate this distribution:
\begin{equation}\label{EPA-subst}
\int_{x_0}^{x_1} dx\,\bar f(x) = C(x_0,x_1)\int_0^1 dx'
\end{equation}
We set
\begin{equation}\label{EPA-x(x')}
\ln x = \frac12\left\{ L - \sqrt{L^2 - 4\left[ x'\ln x_1(L-\ln x_1)
+ \bar x'\ln x_0(L-\ln x_0) \right]} \right\}
\end{equation}
such that $x(0)=x_0$ and $x(1)=x_1$ and
\begin{equation}
\frac{dx}{dx'} = \left(\frac{\alpha}{\pi} q_p^2 \right)^{-1}
x\frac{C(x_0,x_1)}{L - 2\ln x}
\end{equation}
with
\begin{equation}
C(x_0,x_1) = \frac{\alpha}{\pi} q_p^2\,\left[\ln x_1(L-\ln x_1) - \ln
x_0(L-\ln x_0)\right]
\end{equation}
such that (\ref{EPA-subst}) is satisfied. Finally, we have
\begin{equation}
\int_{x_0}^{x_1} dx\,f(x) = C(x_0,x_1)\int_0^1 dx'\,
\frac{f(x(x'))}{\bar f(x(x'))}
\end{equation}
where $x'$ is calculated from $x$ via (\ref{EPA-x(x')}).
The structure of the mapping is most obvious from:
\begin{equation}
x'(x) = \frac{\log x ( L - \log x) - \log x_0 (L - \log x_0)}
{\log x_1 ( L - \log x_1) - \log x_0 (L - \log x_0)} \; .
\end{equation}
\subsection{The EPA data block}
The EPA parameters are: $\alpha$, $E_{\rm max}$, $m$, $Q_{\rm min}$, and
$x_{\rm min}$. Instead of $m$ we can use the incoming particle PDG
code as input; from this we can deduce the mass and charge.
Internally we store in addition $C_{0/1} = \frac{\alpha}{\pi}q_e^2\ln
x_{0/1} (L - \ln x_{0/1})$, the c.m. energy squared and the incoming
particle mass.
<<SF epa: public>>=
public :: epa_data_t
<<SF epa: types>>=
type, extends(sf_data_t) :: epa_data_t
private
class(model_data_t), pointer :: model => null ()
type(flavor_t), dimension(:), allocatable :: flv_in
real(default) :: alpha
real(default) :: x_min
real(default) :: x_max
real(default) :: q_min
real(default) :: q_max
real(default) :: E_max
real(default) :: mass
real(default) :: log
real(default) :: a
real(default) :: c0
real(default) :: c1
real(default) :: dc
integer :: error = NONE
logical :: recoil = .false.
logical :: keep_energy = .true.
contains
<<SF epa: epa data: TBP>>
end type epa_data_t
@ %def epa_data_t
@ Error codes
<<SF epa: parameters>>=
integer, parameter :: NONE = 0
integer, parameter :: ZERO_QMIN = 1
integer, parameter :: Q_MAX_TOO_SMALL = 2
integer, parameter :: ZERO_XMIN = 3
integer, parameter :: MASS_MIX = 4
integer, parameter :: NO_EPA = 5
<<SF epa: epa data: TBP>>=
procedure :: init => epa_data_init
<<SF epa: procedures>>=
subroutine epa_data_init (data, model, pdg_in, alpha, &
x_min, q_min, E_max, mass, recoil, keep_energy)
class(epa_data_t), intent(inout) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), intent(in) :: pdg_in
real(default), intent(in) :: alpha, x_min, q_min, E_max
real(default), intent(in), optional :: mass
logical, intent(in), optional :: recoil
logical, intent(in), optional :: keep_energy
integer :: n_flv, i
data%model => model
n_flv = pdg_array_get_length (pdg_in)
allocate (data%flv_in (n_flv))
do i = 1, n_flv
call data%flv_in(i)%init (pdg_array_get (pdg_in, i), model)
end do
data%alpha = alpha
data%E_max = E_max
data%x_min = x_min
data%x_max = 1
if (vanishes (data%x_min)) then
data%error = ZERO_XMIN; return
end if
data%q_min = q_min
data%q_max = 2 * data%E_max
select case (char (data%model%get_name ()))
case ("QCD","Test")
data%error = NO_EPA; return
end select
if (present (recoil)) then
data%recoil = recoil
end if
if (present (keep_energy)) then
data%keep_energy = keep_energy
end if
if (present (mass)) then
data%mass = mass
else
data%mass = data%flv_in(1)%get_mass ()
if (any (data%flv_in%get_mass () /= data%mass)) then
data%error = MASS_MIX; return
end if
end if
if (max (data%mass, data%q_min) == 0) then
data%error = ZERO_QMIN; return
else if (max (data%mass, data%q_min) >= data%E_max) then
data%error = Q_MAX_TOO_SMALL; return
end if
data%log = log (4 * (data%E_max / max (data%mass, data%q_min)) ** 2 )
data%a = data%alpha / pi
data%c0 = log (data%x_min) * (data%log - log (data%x_min))
data%c1 = log (data%x_max) * (data%log - log (data%x_max))
data%dc = data%c1 - data%c0
end subroutine epa_data_init
@ %def epa_data_init
@ Handle error conditions. Should always be done after
initialization, unless we are sure everything is ok.
<<SF epa: epa data: TBP>>=
procedure :: check => epa_data_check
<<SF epa: procedures>>=
subroutine epa_data_check (data)
class(epa_data_t), intent(in) :: data
select case (data%error)
case (NO_EPA)
call msg_fatal ("EPA structure function not available for model " &
// char (data%model%get_name ()) // ".")
case (ZERO_QMIN)
call msg_fatal ("EPA: Particle mass is zero")
case (Q_MAX_TOO_SMALL)
call msg_fatal ("EPA: Particle mass exceeds Qmax")
case (ZERO_XMIN)
call msg_fatal ("EPA: x_min must be larger than zero")
case (MASS_MIX)
call msg_fatal ("EPA: incoming particle masses must be uniform")
end select
end subroutine epa_data_check
@ %def epa_data_check
@ Output
<<SF epa: epa data: TBP>>=
procedure :: write => epa_data_write
<<SF epa: procedures>>=
subroutine epa_data_write (data, unit, verbose)
class(epa_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "EPA data:"
if (allocated (data%flv_in)) then
write (u, "(3x,A)", advance="no") " flavor = "
do i = 1, size (data%flv_in)
if (i > 1) write (u, "(',',1x)", advance="no")
call data%flv_in(i)%write (u)
end do
write (u, *)
write (u, "(3x,A," // FMT_19 // ")") " alpha = ", data%alpha
write (u, "(3x,A," // FMT_19 // ")") " x_min = ", data%x_min
write (u, "(3x,A," // FMT_19 // ")") " x_max = ", data%x_max
write (u, "(3x,A," // FMT_19 // ")") " q_min = ", data%q_min
write (u, "(3x,A," // FMT_19 // ")") " q_max = ", data%q_max
write (u, "(3x,A," // FMT_19 // ")") " E_max = ", data%e_max
write (u, "(3x,A," // FMT_19 // ")") " mass = ", data%mass
write (u, "(3x,A," // FMT_19 // ")") " a = ", data%a
write (u, "(3x,A," // FMT_19 // ")") " c0 = ", data%c0
write (u, "(3x,A," // FMT_19 // ")") " c1 = ", data%c1
write (u, "(3x,A," // FMT_19 // ")") " log = ", data%log
write (u, "(3x,A,L2)") " recoil = ", data%recoil
write (u, "(3x,A,L2)") " keep en. = ", data%keep_energy
else
write (u, "(3x,A)") "[undefined]"
end if
end subroutine epa_data_write
@ %def epa_data_write
@ The number of kinematic parameters.
<<SF epa: epa data: TBP>>=
procedure :: get_n_par => epa_data_get_n_par
<<SF epa: procedures>>=
function epa_data_get_n_par (data) result (n)
class(epa_data_t), intent(in) :: data
integer :: n
if (data%recoil) then
n = 3
else
n = 1
end if
end function epa_data_get_n_par
@ %def epa_data_get_n_par
@ Return the outgoing particles PDG codes. The outgoing particle is always
the photon while the radiated particle is identical to the incoming one.
<<SF epa: epa data: TBP>>=
procedure :: get_pdg_out => epa_data_get_pdg_out
<<SF epa: procedures>>=
subroutine epa_data_get_pdg_out (data, pdg_out)
class(epa_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
pdg_out(1) = PHOTON
end subroutine epa_data_get_pdg_out
@ %def epa_data_get_pdg_out
@ Allocate the interaction record.
<<SF epa: epa data: TBP>>=
procedure :: allocate_sf_int => epa_data_allocate_sf_int
<<SF epa: procedures>>=
subroutine epa_data_allocate_sf_int (data, sf_int)
class(epa_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (epa_t :: sf_int)
end subroutine epa_data_allocate_sf_int
@ %def epa_data_allocate_sf_int
@
\subsection{The EPA object}
The [[epa_t]] data type is a $1\to 2$ interaction. We should be able
to handle several flavors in parallel, since EPA is not necessarily
applied immediately after beam collision: Photons may be radiated
from quarks. In that case, the partons are massless and $q_{\rm min}$
applies instead, so we do not need to generate several kinematical
configurations in parallel.
The squared charge values multiply the matrix elements, depending on the
flavour. We scan the interaction after building it, so we have the correct
assignments.
The particles are ordered as (incoming, radiated, photon), where the
photon initiates the hard interaction.
We generate an unpolarized photon and transfer initial polarization to
the radiated parton. Color is transferred in the same way.
<<SF epa: types>>=
type, extends (sf_int_t) :: epa_t
type(epa_data_t), pointer :: data => null ()
real(default) :: x = 0
real(default) :: xb = 0
real(default) :: E = 0
real(default), dimension(:), allocatable :: charge2
contains
<<SF epa: epa: TBP>>
end type epa_t
@ %def epa_t
@ Type string: has to be here, but there is no string variable on which EPA
depends. Hence, a dummy routine.
<<SF epa: epa: TBP>>=
procedure :: type_string => epa_type_string
<<SF epa: procedures>>=
function epa_type_string (object) result (string)
class(epa_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "EPA: equivalent photon approx."
else
string = "EPA: [undefined]"
end if
end function epa_type_string
@ %def epa_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF epa: epa: TBP>>=
procedure :: write => epa_write
<<SF epa: procedures>>=
subroutine epa_write (object, unit, testflag)
class(epa_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
if (object%status >= SF_DONE_KINEMATICS) then
write (u, "(1x,A)") "SF parameters:"
write (u, "(3x,A," // FMT_17 // ")") "x =", object%x
if (object%status >= SF_FAILED_EVALUATION) then
write (u, "(3x,A," // FMT_17 // ")") "E =", object%E
end if
end if
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "EPA data: [undefined]"
end if
end subroutine epa_write
@ %def epa_write
@ Prepare the interaction object. We have to construct transition matrix
elements for all flavor and helicity combinations.
<<SF epa: epa: TBP>>=
procedure :: init => epa_init
<<SF epa: procedures>>=
subroutine epa_init (sf_int, data)
class(epa_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(3) :: mask
integer, dimension(3) :: hel_lock
type(polarization_t), target :: pol
type(quantum_numbers_t), dimension(1) :: qn_fc
type(flavor_t) :: flv_photon
type(color_t) :: col_photon
type(quantum_numbers_t) :: qn_hel, qn_photon, qn, qn_rad
type(polarization_iterator_t) :: it_hel
integer :: i
mask = quantum_numbers_mask (.false., .false., &
mask_h = [.false., .false., .true.])
hel_lock = [2, 1, 0]
select type (data)
type is (epa_data_t)
call sf_int%base_init (mask, [data%mass**2], &
[data%mass**2], [0._default], hel_lock = hel_lock)
sf_int%data => data
call flv_photon%init (PHOTON, data%model)
call col_photon%init ()
call qn_photon%init (flv_photon, col_photon)
do i = 1, size (data%flv_in)
call pol%init_generic (data%flv_in(i))
call qn_fc(1)%init ( &
flv = data%flv_in(i), &
col = color_from_flavor (data%flv_in(i), 1))
call it_hel%init (pol)
do while (it_hel%is_valid ())
qn_hel = it_hel%get_quantum_numbers ()
qn = qn_hel .merge. qn_fc(1)
qn_rad = qn
call qn_rad%tag_radiated ()
call sf_int%add_state ([qn, qn_rad, qn_photon])
call it_hel%advance ()
end do
! call pol%final ()
end do
call sf_int%freeze ()
if (data%keep_energy) then
sf_int%on_shell_mode = KEEP_ENERGY
else
sf_int%on_shell_mode = KEEP_MOMENTUM
end if
call sf_int%set_incoming ([1])
call sf_int%set_radiated ([2])
call sf_int%set_outgoing ([3])
end select
end subroutine epa_init
@ %def epa_init
@ Prepare the charge array. This is separate from the previous routine since
the state matrix may be helicity-contracted.
<<SF epa: epa: TBP>>=
procedure :: setup_constants => epa_setup_constants
<<SF epa: procedures>>=
subroutine epa_setup_constants (sf_int)
class(epa_t), intent(inout), target :: sf_int
type(state_iterator_t) :: it
type(flavor_t) :: flv
integer :: i, n_me
n_me = sf_int%get_n_matrix_elements ()
allocate (sf_int%charge2 (n_me))
call it%init (sf_int%interaction_t%get_state_matrix_ptr ())
do while (it%is_valid ())
i = it%get_me_index ()
flv = it%get_flavor (1)
sf_int%charge2(i) = flv%get_charge () ** 2
call it%advance ()
end do
sf_int%status = SF_INITIAL
end subroutine epa_setup_constants
@ %def epa_setup_constants
@
\subsection{Kinematics}
Set kinematics. If [[map]] is unset, the $r$ and $x$ values
coincide, and the Jacobian $f(r)$ is trivial.
The EPA structure function allows for a straightforward mapping of the
unit interval. The $x$ value is transformed, and the mapped structure
function becomes unity at its upper boundary.
The structure function implementation applies the above mapping to the
input (random) number [[r]] to generate the momentum fraction [[x]]
and the function value [[f]]. For numerical stability reasons, we
also output [[xb]], which is $\bar x=1-x$.
<<SF epa: epa: TBP>>=
procedure :: complete_kinematics => epa_complete_kinematics
<<SF epa: procedures>>=
subroutine epa_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(epa_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
real(default) :: delta, sqrt_delta, lx
if (map) then
associate (data => sf_int%data)
delta = data%log ** 2 - 4 * (r(1) * data%c1 + rb(1) * data%c0)
if (delta > 0) then
sqrt_delta = sqrt (delta)
lx = (data%log - sqrt_delta) / 2
else
sf_int%status = SF_FAILED_KINEMATICS
f = 0
return
end if
x(1) = exp (lx)
f = x(1) * data%dc / sqrt_delta
end associate
else
x(1) = r(1)
if (sf_int%data%x_min < x(1) .and. x(1) < sf_int%data%x_max) then
f = 1
else
sf_int%status = SF_FAILED_KINEMATICS
f = 0
return
end if
end if
xb(1) = 1 - x(1)
if (size(x) == 3) then
x(2:3) = r(2:3)
xb(2:3) = rb(2:3)
end if
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_DONE_KINEMATICS)
sf_int%x = x(1)
sf_int%xb= xb(1)
sf_int%E = energy (sf_int%get_momentum (1))
case (SF_FAILED_KINEMATICS)
sf_int%x = 0
sf_int%xb= 0
f = 0
end select
end subroutine epa_complete_kinematics
@ %def epa_complete_kinematics
@ Overriding the default method: we compute the [[x]] array from the
momentum configuration. In the specific case of EPA, we also set the
internally stored $x$ and $\bar x$ values, so they can be used in the
following routine.
Note: the extraction of $\bar x$ is not numerically safe, but it cannot
be as long as the base [[recover_x]] is not.
<<SF epa: epa: TBP>>=
procedure :: recover_x => sf_epa_recover_x
<<SF epa: procedures>>=
subroutine sf_epa_recover_x (sf_int, x, xb, x_free)
class(epa_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(inout), optional :: x_free
call sf_int%base_recover_x (x, xb, x_free)
sf_int%x = x(1)
sf_int%xb = xb(1)
end subroutine sf_epa_recover_x
@ %def sf_epa_recover_x
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
<<SF epa: epa: TBP>>=
procedure :: inverse_kinematics => epa_inverse_kinematics
<<SF epa: procedures>>=
subroutine epa_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(epa_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
real(default) :: lx, delta, sqrt_delta, c
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
associate (data => sf_int%data)
lx = log (x(1))
sqrt_delta = data%log - 2 * lx
delta = sqrt_delta ** 2
c = (data%log ** 2 - delta) / 4
r (1) = (c - data%c0) / data%dc
rb(1) = (data%c1 - c) / data%dc
f = x(1) * data%dc / sqrt_delta
end associate
else
r (1) = x(1)
rb(1) = xb(1)
if (sf_int%data%x_min < x(1) .and. x(1) < sf_int%data%x_max) then
f = 1
else
f = 0
end if
end if
if (size(r) == 3) then
r (2:3) = x(2:3)
rb(2:3) = xb(2:3)
end if
if (set_mom) then
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end if
sf_int%E = energy (sf_int%get_momentum (1))
end subroutine epa_inverse_kinematics
@ %def epa_inverse_kinematics
@
\subsection{EPA application}
For EPA, we can in principle compute kinematics and function value in
a single step. In order to be able to reweight events, kinematics and
structure function application are separated. This function works on a
single beam, assuming that the input momentum has been set. We need
three random numbers as input: one for $x$, and two for the polar and
azimuthal angles. Alternatively, for the no-recoil case, we can skip
$p_T$ generation; in this case, we only need one.
For obtaining splitting kinematics, we rely on the assumption that all
in-particles are mass-degenerate (or there is only one), so the
generated $x$ values are identical.
<<SF epa: epa: TBP>>=
procedure :: apply => epa_apply
<<SF epa: procedures>>=
- subroutine epa_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine epa_apply (sf_int, scale, rescale, i_sub)
class(epa_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default) :: x, xb, qminsq, qmaxsq, f, E
associate (data => sf_int%data)
x = sf_int%x
xb= sf_int%xb
E = sf_int%E
qminsq = max (x ** 2 / xb * data%mass ** 2, data%q_min ** 2)
qmaxsq = min (4 * xb * E ** 2, data%q_max ** 2)
if (qminsq < qmaxsq) then
f = data%a / x &
* ((xb + x ** 2 / 2) * log (qmaxsq / qminsq) &
- (1 - x / 2) ** 2 &
* log ((x**2 + qmaxsq / E ** 2) / (x**2 + qminsq / E ** 2)) &
- x ** 2 * data%mass ** 2 / qminsq * (1 - qminsq / qmaxsq))
else
f = 0
end if
call sf_int%set_matrix_element &
(cmplx (f, kind=default) * sf_int%charge2)
end associate
sf_int%status = SF_EVALUATED
end subroutine epa_apply
@ %def epa_apply
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_epa_ut.f90]]>>=
<<File header>>
module sf_epa_ut
use unit_tests
use sf_epa_uti
<<Standard module head>>
<<SF epa: public test>>
contains
<<SF epa: test driver>>
end module sf_epa_ut
@ %def sf_epa_ut
@
<<[[sf_epa_uti.f90]]>>=
<<File header>>
module sf_epa_uti
<<Use kinds>>
use physics_defs, only: ELECTRON
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use interactions, only: interaction_pacify_momenta
use model_data
use sf_aux
use sf_base
use sf_epa
<<Standard module head>>
<<SF epa: test declarations>>
contains
<<SF epa: tests>>
end module sf_epa_uti
@ %def sf_epa_ut
@ API: driver for the unit tests below.
<<SF epa: public test>>=
public :: sf_epa_test
<<SF epa: test driver>>=
subroutine sf_epa_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF epa: execute tests>>
end subroutine sf_epa_test
@ %def sf_epa_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF epa: execute tests>>=
call test (sf_epa_1, "sf_epa_1", &
"structure function configuration", &
u, results)
<<SF epa: test declarations>>=
public :: sf_epa_1
<<SF epa: tests>>=
subroutine sf_epa_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(pdg_array_t), dimension(1) :: pdg_out
integer, dimension(:), allocatable :: pdg1
class(sf_data_t), allocatable :: data
write (u, "(A)") "* Test output: sf_epa_1"
write (u, "(A)") "* Purpose: initialize and display &
&test structure function data"
write (u, "(A)")
write (u, "(A)") "* Create empty data object"
write (u, "(A)")
call model%init_qed_test ()
pdg_in = ELECTRON
allocate (epa_data_t :: data)
call data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize"
write (u, "(A)")
select type (data)
type is (epa_data_t)
call data%init (model, pdg_in, 1./137._default, 0.01_default, &
10._default, 50._default, 0.000511_default, recoil = .false.)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
write (u, "(2x,99(1x,I0))") pdg1
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_epa_1"
end subroutine sf_epa_1
@ %def sf_epa_1
@
\subsubsection{Test and probe structure function}
Construct and display a structure function object based on the EPA
structure function.
<<SF epa: execute tests>>=
call test (sf_epa_2, "sf_epa_2", &
"structure function instance", &
u, results)
<<SF epa: test declarations>>=
public :: sf_epa_2
<<SF epa: tests>>=
subroutine sf_epa_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_epa_2"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
call flv%init (ELECTRON, model)
pdg_in = ELECTRON
call reset_interaction_counter ()
allocate (epa_data_t :: data)
select type (data)
type is (epa_data_t)
call data%init (model, pdg_in, 1./137._default, 0.01_default, &
10._default, 50._default, 0.000511_default, recoil = .false.)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.4, no EPA mapping, collinear"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.4_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false., &
set_momenta=.true.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate EPA structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_epa_2"
end subroutine sf_epa_2
@ %def sf_epa_2
@
\subsubsection{Standard mapping}
Construct and display a structure function object based on the EPA
structure function, applying the standard single-particle mapping.
<<SF epa: execute tests>>=
call test (sf_epa_3, "sf_epa_3", &
"apply mapping", &
u, results)
<<SF epa: test declarations>>=
public :: sf_epa_3
<<SF epa: tests>>=
subroutine sf_epa_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_epa_3"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
call flv%init (ELECTRON, model)
pdg_in = ELECTRON
call reset_interaction_counter ()
allocate (epa_data_t :: data)
select type (data)
type is (epa_data_t)
call data%init (model, pdg_in, 1./137._default, 0.01_default, &
10._default, 50._default, 0.000511_default, recoil = .false.)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.4, with EPA mapping, collinear"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.4_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.true., &
set_momenta=.true.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate EPA structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_epa_3"
end subroutine sf_epa_3
@ %def sf_epa_3
@
\subsubsection{Non-collinear case}
Construct and display a structure function object based on the EPA
structure function.
<<SF epa: execute tests>>=
call test (sf_epa_4, "sf_epa_4", &
"non-collinear", &
u, results)
<<SF epa: test declarations>>=
public :: sf_epa_4
<<SF epa: tests>>=
subroutine sf_epa_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E, m
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_epa_4"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
call flv%init (ELECTRON, model)
pdg_in = ELECTRON
call reset_interaction_counter ()
allocate (epa_data_t :: data)
select type (data)
type is (epa_data_t)
call data%init (model, pdg_in, 1./137._default, 0.01_default, &
10._default, 50._default, 5.0_default, recoil = .true.)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
write (u, "(A)") "* Initialize incoming momentum with E=500, me = 5 GeV"
write (u, "(A)")
E = 500
m = 5
k = vector4_moving (E, sqrt (E**2 - m**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.5/0.5/0.25, with EPA mapping, "
write (u, "(A)") " non-coll., keeping energy, me = 5 GeV"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = [0.5_default, 0.5_default, 0.25_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
call interaction_pacify_momenta (sf_int%interaction_t, 1e-10_default)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x and r from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.true., &
set_momenta=.true.)
call interaction_pacify_momenta (sf_int%interaction_t, 1e-10_default)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate EPA structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u, testflag = .true.)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_epa_4"
end subroutine sf_epa_4
@ %def sf_epa_4
@
\subsubsection{Structure function for multiple flavors}
Construct and display a structure function object based on the EPA
structure function. The incoming state has multiple particles with
non-uniform charge.
<<SF epa: execute tests>>=
call test (sf_epa_5, "sf_epa_5", &
"multiple flavors", &
u, results)
<<SF epa: test declarations>>=
public :: sf_epa_5
<<SF epa: tests>>=
subroutine sf_epa_5 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_epa_5"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_sm_test ()
call flv%init (1, model)
pdg_in = [1, 2, -1, -2]
call reset_interaction_counter ()
allocate (epa_data_t :: data)
select type (data)
type is (epa_data_t)
call data%init (model, pdg_in, 1./137._default, 0.01_default, &
10._default, 50._default, 0.000511_default, recoil = .false.)
call data%check ()
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.4, no EPA mapping, collinear"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.4_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate EPA structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_epa_5"
end subroutine sf_epa_5
@ %def sf_epa_5
@
\clearpage
%------------------------------------------------------------------------
\section{EWA}
<<[[sf_ewa.f90]]>>=
<<File header>>
module sf_ewa
<<Use kinds>>
<<Use strings>>
use io_units
use constants, only: pi
use format_defs, only: FMT_17, FMT_19
use numeric_utils
use diagnostics
use physics_defs, only: W_BOSON, Z_BOSON
use lorentz
use pdg_arrays
use model_data
use flavors
use colors
use quantum_numbers
use state_matrices
use polarizations
use interactions
use sf_aux
use sf_base
<<Standard module head>>
<<SF ewa: public>>
<<SF ewa: parameters>>
<<SF ewa: types>>
contains
<<SF ewa: procedures>>
end module sf_ewa
@ %def sf_ewa
@
\subsection{Physics}
The EWA structure function for a $Z$ or $W$ inside a fermion (lepton
or quark) depends on the vector-boson polarization. We distinguish
transversal ($\pm$) and longitudinal ($0$) polarization.
\begin{align}
F_{+}(x) &= \frac{1}{16\pi^2}\,\frac{(v-a)^2 + (v+a)^2\bar x^2}{x}
\left[
\ln\left(\frac{p_{\perp,\textrm{max}}^2 + \bar x M^2}{\bar x M^2}\right)
-
\frac{p_{\perp,\textrm{max}}^2}{p_{\perp,\textrm{max}}^2 + \bar x M^2}
\right]
\\
F_{-}(x) &= \frac{1}{16\pi^2}\,\frac{(v+a)^2 + (v-a)^2\bar x^2}{x}
\left[
\ln\left(\frac{p_{\perp,\textrm{max}}^2 + \bar x M^2}{\bar x M^2}\right)
-
\frac{p_{\perp,\textrm{max}}^2}{p_{\perp,\textrm{max}}^2 + \bar x M^2}
\right]
\\
F_0(x) &= \frac{v^2+a^2}{8\pi^2}\,\frac{2\bar x}{x}\,
\frac{p_{\perp,\textrm{max}}^2}{p_{\perp,\textrm{max}}^2 + \bar x M^2}
\end{align}
where $p_{\perp,\textrm{max}}$ is the cutoff in transversal momentum, $M$ is
the vector-boson mass, $v$ and $a$ are the vector and axial-vector
couplings, and $\bar x\equiv 1-x$. Note that the longitudinal
structure function is finite for large cutoff, while the transversal
structure function is logarithmically divergent.
The maximal transverse momentum is given by the kinematical limit, it is
\begin{equation}
p_{\perp,\textrm{max}} = \bar x \sqrt{s}/2.
\end{equation}
The vector and axial couplings for a fermion branching into a $W$ are
\begin{align}
v_W &= \frac{g}{2\sqrt 2},
& a_W &= \frac{g}{2\sqrt 2}.
\end{align}
For $Z$ emission, this is replaced by
\begin{align}
v_Z &= \frac{g}{2\cos\theta_w}\left(t_3 - 2q\sin^2\theta_w\right),
& a_Z &= \frac{g}{2\cos\theta_w}t_3,
\end{align}
where $t_3=\pm\frac12$ is the fermion isospin, and $q$ its charge.
For an initial antifermion, the signs of the axial couplings are
inverted. Note that a common sign change of $v$ and $a$ is
irrelevant.
%% Differentiating with respect to the cutoff, we get structure functions
%% \begin{align}
%% f_{W,\pm}(x,p_T) &= \frac{g^2}{16\pi^2}\,
%% \frac{1+\bar x^2}{x}
%% \frac{p_\perp}{p_\perp^2 + \bar x M^2}
%% \\
%% f_{W,0}(x,p_T) &= \frac{g^2}{16\pi^2}\,
%% \frac{2\bar x}{x}\,
%% \frac{p_\perp \bar xM^2}{(p_\perp^2 + \bar x M^2)^2}
%% \\
%% F_{Z,\pm}(x,p_T) &= \frac{g^2}{16\pi^2\cos\theta_w^2}
%% \left[(t_3^f-2q^2\sin\theta_w^2)^2 + (t_3^f)^2\right]\,
%% \frac{1+\bar x^2}{x}
%% \frac{p_\perp}{p_\perp^2 + \bar x M^2}
%% \\
%% F_{Z,0}(x,p_T) &= \frac{g^2}{16\pi^2\cos\theta_w^2}\,
%% \left[(t_3^f-2q^2\sin\theta_w^2)^2 + (t_3^f)^2\right]\,
%% \frac{2\bar x}{x}\,
%% \frac{p_\perp \bar xM^2}{(p_\perp^2 + \bar x M^2)^2}
%% \end{align}
%% Here, $t_3^f$ is the $SU(2)_L$ quantum number of the fermion
%% $(\pm\frac12)$, and $q^f$ is the fermion charge in units of the
%% positron charge.
The EWA depends on the parameters $g$, $\sin^2\theta_w$, $M_W$, and
$M_Z$. These can all be taken from the SM input, and the prefactors
are calculated from those and the incoming particle type.
Since these structure functions have a $1/x$ singularity (which is not
really relevant in practice, however, since the vector boson mass is
finite), we map this singularity allowing for nontrivial $x$ bounds:
\begin{equation}
x = \exp(\bar r\ln x_0 + r\ln x_1)
\end{equation}
such that
\begin{equation}
\int_{x_0}^{x_1}\frac{dx}{x} = (\ln x_1 - \ln x_0)\int_0^1 dr.
\end{equation}
As a user parameter, we have the cutoff $p_{\perp,\textrm{max}}$.
The divergence $1/x$ also requires a $x_0$ cutoff; and for
completeness we introduce a corresponding $x_1$. Physically, the
minimal sensible value of $x$ is $M^2/s$, although the approximation
loses its value already at higher $x$ values.
\subsection{The EWA data block}
The EWA parameters are: $p_{T,\rm max}$, $c_V$, $c_A$, and
$m$. Instead of $m$ we can use the incoming particle PDG code as
input; from this we can deduce the mass and charges. In the
initialization phase it is not yet determined whether a $W$ or a $Z$
is radiated, hence we set the vector and axial-vector couplings equal
to the common prefactors $g/2 = e/2/\sin\theta_W$.
In principle, for EWA it would make sense to allow the user to also
set the upper bound for $x$, $x_{\rm max}$, but we fix it to one here.
<<SF ewa: public>>=
public :: ewa_data_t
<<SF ewa: types>>=
type, extends(sf_data_t) :: ewa_data_t
private
class(model_data_t), pointer :: model => null ()
type(flavor_t), dimension(:), allocatable :: flv_in
type(flavor_t), dimension(:), allocatable :: flv_out
real(default) :: pt_max
real(default) :: sqrts
real(default) :: x_min
real(default) :: x_max
real(default) :: mass
real(default) :: m_out
real(default) :: q_min
real(default) :: cv
real(default) :: ca
real(default) :: costhw
real(default) :: sinthw
real(default) :: mW
real(default) :: mZ
real(default) :: coeff
logical :: mass_set = .false.
logical :: recoil = .false.
logical :: keep_energy = .false.
integer :: id = 0
integer :: error = NONE
contains
<<SF ewa: ewa data: TBP>>
end type ewa_data_t
@ %def ewa_data_t
@ Error codes
<<SF ewa: parameters>>=
integer, parameter :: NONE = 0
integer, parameter :: ZERO_QMIN = 1
integer, parameter :: Q_MAX_TOO_SMALL = 2
integer, parameter :: ZERO_XMIN = 3
integer, parameter :: MASS_MIX = 4
integer, parameter :: ZERO_SW = 5
integer, parameter :: ISOSPIN_MIX = 6
integer, parameter :: WRONG_PRT = 7
integer, parameter :: MASS_MIX_OUT = 8
integer, parameter :: NO_EWA = 9
<<SF ewa: ewa data: TBP>>=
procedure :: init => ewa_data_init
<<SF ewa: procedures>>=
subroutine ewa_data_init (data, model, pdg_in, x_min, pt_max, &
sqrts, recoil, keep_energy, mass)
class(ewa_data_t), intent(inout) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), intent(in) :: pdg_in
real(default), intent(in) :: x_min, pt_max, sqrts
logical, intent(in) :: recoil, keep_energy
real(default), intent(in), optional :: mass
real(default) :: g, ee
integer :: n_flv, i
data%model => model
if (.not. any (pdg_in .match. &
[1,2,3,4,5,6,11,13,15,-1,-2,-3,-4,-5,-6,-11,-13,-15])) then
data%error = WRONG_PRT; return
end if
n_flv = pdg_array_get_length (pdg_in)
allocate (data%flv_in (n_flv))
allocate (data%flv_out(n_flv))
do i = 1, n_flv
call data%flv_in(i)%init (pdg_array_get (pdg_in, i), model)
end do
data%pt_max = pt_max
data%sqrts = sqrts
data%x_min = x_min
data%x_max = 1
if (vanishes (data%x_min)) then
data%error = ZERO_XMIN; return
end if
select case (char (data%model%get_name ()))
case ("QCD","QED","Test")
data%error = NO_EWA; return
end select
ee = data%model%get_real (var_str ("ee"))
data%sinthw = data%model%get_real (var_str ("sw"))
data%costhw = data%model%get_real (var_str ("cw"))
data%mZ = data%model%get_real (var_str ("mZ"))
data%mW = data%model%get_real (var_str ("mW"))
if (data%sinthw /= 0) then
g = ee / data%sinthw
else
data%error = ZERO_SW; return
end if
data%cv = g / 2._default
data%ca = g / 2._default
data%coeff = 1._default / (8._default * PI**2)
data%recoil = recoil
data%keep_energy = keep_energy
if (present (mass)) then
data%mass = mass
data%m_out = mass
data%mass_set = .true.
else
data%mass = data%flv_in(1)%get_mass ()
if (any (data%flv_in%get_mass () /= data%mass)) then
data%error = MASS_MIX; return
end if
end if
end subroutine ewa_data_init
@ %def ewa_data_init
@ Set the vector boson ID for distinguishing $W$ and $Z$ bosons.
<<SF ewa: ewa data: TBP>>=
procedure :: set_id => ewa_set_id
<<SF ewa: procedures>>=
subroutine ewa_set_id (data, id)
class(ewa_data_t), intent(inout) :: data
integer, intent(in) :: id
integer :: i, isospin, pdg
if (.not. allocated (data%flv_in)) &
call msg_bug ("EWA: incoming particles not set")
data%id = id
select case (data%id)
case (23)
data%m_out = data%mass
data%flv_out = data%flv_in
case (24)
do i = 1, size (data%flv_in)
pdg = data%flv_in(i)%get_pdg ()
isospin = data%flv_in(i)%get_isospin_type ()
if (isospin > 0) then
!!! up-type quark or neutrinos
if (data%flv_in(i)%is_antiparticle ()) then
call data%flv_out(i)%init (pdg + 1, data%model)
else
call data%flv_out(i)%init (pdg - 1, data%model)
end if
else
!!! down-type quark or lepton
if (data%flv_in(i)%is_antiparticle ()) then
call data%flv_out(i)%init (pdg - 1, data%model)
else
call data%flv_out(i)%init (pdg + 1, data%model)
end if
end if
end do
if (.not. data%mass_set) then
data%m_out = data%flv_out(1)%get_mass ()
if (any (data%flv_out%get_mass () /= data%m_out)) then
data%error = MASS_MIX_OUT; return
end if
end if
end select
end subroutine ewa_set_id
@ %def ewa_set_id
@ Handle error conditions. Should always be done after
initialization, unless we are sure everything is ok.
<<SF ewa: ewa data: TBP>>=
procedure :: check => ewa_data_check
<<SF ewa: procedures>>=
subroutine ewa_data_check (data)
class(ewa_data_t), intent(in) :: data
select case (data%error)
case (WRONG_PRT)
call msg_fatal ("EWA structure function only accessible for " &
// "SM quarks and leptons.")
case (NO_EWA)
call msg_fatal ("EWA structure function not available for model " &
// char (data%model%get_name ()))
case (ZERO_SW)
call msg_fatal ("EWA: Vanishing value of sin(theta_w)")
case (ZERO_QMIN)
call msg_fatal ("EWA: Particle mass is zero")
case (Q_MAX_TOO_SMALL)
call msg_fatal ("EWA: Particle mass exceeds Qmax")
case (ZERO_XMIN)
call msg_fatal ("EWA: x_min must be larger than zero")
case (MASS_MIX)
call msg_fatal ("EWA: incoming particle masses must be uniform")
case (MASS_MIX_OUT)
call msg_fatal ("EWA: outgoing particle masses must be uniform")
case (ISOSPIN_MIX)
call msg_fatal ("EWA: incoming particle isospins must be uniform")
end select
end subroutine ewa_data_check
@ %def ewa_data_check
@ Output
<<SF ewa: ewa data: TBP>>=
procedure :: write => ewa_data_write
<<SF ewa: procedures>>=
subroutine ewa_data_write (data, unit, verbose)
class(ewa_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "EWA data:"
if (allocated (data%flv_in) .and. allocated (data%flv_out)) then
write (u, "(3x,A)", advance="no") " flavor(in) = "
do i = 1, size (data%flv_in)
if (i > 1) write (u, "(',',1x)", advance="no")
call data%flv_in(i)%write (u)
end do
write (u, *)
write (u, "(3x,A)", advance="no") " flavor(out) = "
do i = 1, size (data%flv_out)
if (i > 1) write (u, "(',',1x)", advance="no")
call data%flv_out(i)%write (u)
end do
write (u, *)
write (u, "(3x,A," // FMT_19 // ")") " x_min = ", data%x_min
write (u, "(3x,A," // FMT_19 // ")") " x_max = ", data%x_max
write (u, "(3x,A," // FMT_19 // ")") " pt_max = ", data%pt_max
write (u, "(3x,A," // FMT_19 // ")") " sqrts = ", data%sqrts
write (u, "(3x,A," // FMT_19 // ")") " mass = ", data%mass
write (u, "(3x,A," // FMT_19 // ")") " cv = ", data%cv
write (u, "(3x,A," // FMT_19 // ")") " ca = ", data%ca
write (u, "(3x,A," // FMT_19 // ")") " coeff = ", data%coeff
write (u, "(3x,A," // FMT_19 // ")") " costhw = ", data%costhw
write (u, "(3x,A," // FMT_19 // ")") " sinthw = ", data%sinthw
write (u, "(3x,A," // FMT_19 // ")") " mZ = ", data%mZ
write (u, "(3x,A," // FMT_19 // ")") " mW = ", data%mW
write (u, "(3x,A,L2)") " recoil = ", data%recoil
write (u, "(3x,A,L2)") " keep en. = ", data%keep_energy
write (u, "(3x,A,I2)") " PDG (VB) = ", data%id
else
write (u, "(3x,A)") "[undefined]"
end if
end subroutine ewa_data_write
@ %def ewa_data_write
@ The number of parameters is one for collinear splitting, in case the
[[recoil]] option is set, we take the recoil into account.
<<SF ewa: ewa data: TBP>>=
procedure :: get_n_par => ewa_data_get_n_par
<<SF ewa: procedures>>=
function ewa_data_get_n_par (data) result (n)
class(ewa_data_t), intent(in) :: data
integer :: n
if (data%recoil) then
n = 3
else
n = 1
end if
end function ewa_data_get_n_par
@ %def ewa_data_get_n_par
@ Return the outgoing particles PDG codes. This depends, whether this
is a charged-current or neutral-current interaction.
<<SF ewa: ewa data: TBP>>=
procedure :: get_pdg_out => ewa_data_get_pdg_out
<<SF ewa: procedures>>=
subroutine ewa_data_get_pdg_out (data, pdg_out)
class(ewa_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
integer, dimension(:), allocatable :: pdg1
integer :: i, n_flv
if (allocated (data%flv_out)) then
n_flv = size (data%flv_out)
else
n_flv = 0
end if
allocate (pdg1 (n_flv))
do i = 1, n_flv
pdg1(i) = data%flv_out(i)%get_pdg ()
end do
pdg_out(1) = pdg1
end subroutine ewa_data_get_pdg_out
@ %def ewa_data_get_pdg_out
@ Allocate the interaction record.
<<SF ewa: ewa data: TBP>>=
procedure :: allocate_sf_int => ewa_data_allocate_sf_int
<<SF ewa: procedures>>=
subroutine ewa_data_allocate_sf_int (data, sf_int)
class(ewa_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (ewa_t :: sf_int)
end subroutine ewa_data_allocate_sf_int
@ %def ewa_data_allocate_sf_int
@
\subsection{The EWA object}
The [[ewa_t]] data type is a $1\to 2$ interaction. We should be able
to handle several flavors in parallel, since EWA is not necessarily
applied immediately after beam collision: $W/Z$ bosons may be radiated
from quarks. In that case, the partons are massless and $q_{\rm min}$
applies instead, so we do not need to generate several kinematical
configurations in parallel.
The particles are ordered as (incoming, radiated, W/Z), where the
W/Z initiates the hard interaction.
In the case of EPA, we generated an unpolarized photon and transferred
initial polarization to the radiated parton. Color is transferred in
the same way. I do not know whether the same can/should be done for
EWA, as the structure functions depend on the W/Z polarization. If we
are having $Z$ bosons, both up- and down-type fermions can
participate. Otherwise, with a $W^+$ an up-type fermion is transferred
to a down-type fermion, and the other way round.
<<SF ewa: types>>=
type, extends (sf_int_t) :: ewa_t
type(ewa_data_t), pointer :: data => null ()
real(default) :: x = 0
real(default) :: xb = 0
integer :: n_me = 0
real(default), dimension(:), allocatable :: cv
real(default), dimension(:), allocatable :: ca
contains
<<SF ewa: ewa: TBP>>
end type ewa_t
@ %def ewa_t
@ Type string: has to be here, but there is no string variable on which EWA
depends. Hence, a dummy routine.
<<SF ewa: ewa: TBP>>=
procedure :: type_string => ewa_type_string
<<SF ewa: procedures>>=
function ewa_type_string (object) result (string)
class(ewa_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "EWA: equivalent W/Z approx."
else
string = "EWA: [undefined]"
end if
end function ewa_type_string
@ %def ewa_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF ewa: ewa: TBP>>=
procedure :: write => ewa_write
<<SF ewa: procedures>>=
subroutine ewa_write (object, unit, testflag)
class(ewa_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
if (object%status >= SF_DONE_KINEMATICS) then
write (u, "(1x,A)") "SF parameters:"
write (u, "(3x,A," // FMT_17 // ")") "x =", object%x
write (u, "(3x,A," // FMT_17 // ")") "xb=", object%xb
end if
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "EWA data: [undefined]"
end if
end subroutine ewa_write
@ %def ewa_write
@ The current implementation requires uniform isospin for all incoming
particles, therefore we need to probe only the first one.
<<SF ewa: ewa: TBP>>=
procedure :: init => ewa_init
<<SF ewa: procedures>>=
subroutine ewa_init (sf_int, data)
class(ewa_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(3) :: mask
integer, dimension(3) :: hel_lock
type(polarization_t), target :: pol
type(quantum_numbers_t), dimension(1) :: qn_fc, qn_fc_fin
type(flavor_t) :: flv_z, flv_wp, flv_wm
type(color_t) :: col0
type(quantum_numbers_t) :: qn_hel, qn_z, qn_wp, qn_wm, qn, qn_rad, qn_w
type(polarization_iterator_t) :: it_hel
integer :: i, isospin
select type (data)
type is (ewa_data_t)
mask = quantum_numbers_mask (.false., .false., &
mask_h = [.false., .false., .true.])
hel_lock = [2, 1, 0]
call col0%init ()
select case (data%id)
case (23)
!!! Z boson, flavor is not changing
call sf_int%base_init (mask, [data%mass**2], [data%mass**2], &
[data%mZ**2], hel_lock = hel_lock)
sf_int%data => data
call flv_z%init (Z_BOSON, data%model)
call qn_z%init (flv_z, col0)
do i = 1, size (data%flv_in)
call pol%init_generic (data%flv_in(i))
call qn_fc(1)%init ( &
flv = data%flv_in(i), &
col = color_from_flavor (data%flv_in(i), 1))
call it_hel%init (pol)
do while (it_hel%is_valid ())
qn_hel = it_hel%get_quantum_numbers ()
qn = qn_hel .merge. qn_fc(1)
qn_rad = qn
call qn_rad%tag_radiated ()
call sf_int%add_state ([qn, qn_rad, qn_z])
call it_hel%advance ()
end do
! call pol%final ()
end do
case (24)
call sf_int%base_init (mask, [data%mass**2], [data%m_out**2], &
[data%mW**2], hel_lock = hel_lock)
sf_int%data => data
call flv_wp%init (W_BOSON, data%model)
call flv_wm%init (- W_BOSON, data%model)
call qn_wp%init (flv_wp, col0)
call qn_wm%init (flv_wm, col0)
do i = 1, size (data%flv_in)
isospin = data%flv_in(i)%get_isospin_type ()
if (isospin > 0) then
!!! up-type quark or neutrinos
if (data%flv_in(i)%is_antiparticle ()) then
qn_w = qn_wm
else
qn_w = qn_wp
end if
else
!!! down-type quark or lepton
if (data%flv_in(i)%is_antiparticle ()) then
qn_w = qn_wp
else
qn_w = qn_wm
end if
end if
call pol%init_generic (data%flv_in(i))
call qn_fc(1)%init ( &
flv = data%flv_in(i), &
col = color_from_flavor (data%flv_in(i), 1))
call qn_fc_fin(1)%init ( &
flv = data%flv_out(i), &
col = color_from_flavor (data%flv_out(i), 1))
call it_hel%init (pol)
do while (it_hel%is_valid ())
qn_hel = it_hel%get_quantum_numbers ()
qn = qn_hel .merge. qn_fc(1)
qn_rad = qn_hel .merge. qn_fc_fin(1)
call qn_rad%tag_radiated ()
call sf_int%add_state ([qn, qn_rad, qn_w])
call it_hel%advance ()
end do
! call pol%final ()
end do
case default
call msg_fatal ("EWA initialization failed: wrong particle type.")
end select
call sf_int%freeze ()
if (data%keep_energy) then
sf_int%on_shell_mode = KEEP_ENERGY
else
sf_int%on_shell_mode = KEEP_MOMENTUM
end if
call sf_int%set_incoming ([1])
call sf_int%set_radiated ([2])
call sf_int%set_outgoing ([3])
end select
end subroutine ewa_init
@ %def ewa_init
@ Prepare the coupling arrays. This is separate from the previous routine since
the state matrix may be helicity-contracted.
<<SF ewa: ewa: TBP>>=
procedure :: setup_constants => ewa_setup_constants
<<SF ewa: procedures>>=
subroutine ewa_setup_constants (sf_int)
class(ewa_t), intent(inout), target :: sf_int
type(state_iterator_t) :: it
type(flavor_t) :: flv
real(default) :: q, t3
integer :: i
sf_int%n_me = sf_int%get_n_matrix_elements ()
allocate (sf_int%cv (sf_int%n_me))
allocate (sf_int%ca (sf_int%n_me))
associate (data => sf_int%data)
select case (data%id)
case (23)
call it%init (sf_int%interaction_t%get_state_matrix_ptr ())
do while (it%is_valid ())
i = it%get_me_index ()
flv = it%get_flavor (1)
q = flv%get_charge ()
t3 = flv%get_isospin ()
if (flv%is_antiparticle ()) then
sf_int%cv(i) = - data%cv &
* (t3 - 2._default * q * data%sinthw**2) / data%costhw
sf_int%ca(i) = data%ca * t3 / data%costhw
else
sf_int%cv(i) = data%cv &
* (t3 - 2._default * q * data%sinthw**2) / data%costhw
sf_int%ca(i) = data%ca * t3 / data%costhw
end if
call it%advance ()
end do
case (24)
call it%init (sf_int%interaction_t%get_state_matrix_ptr ())
do while (it%is_valid ())
i = it%get_me_index ()
flv = it%get_flavor (1)
if (flv%is_antiparticle ()) then
sf_int%cv(i) = data%cv / sqrt(2._default)
sf_int%ca(i) = - data%ca / sqrt(2._default)
else
sf_int%cv(i) = data%cv / sqrt(2._default)
sf_int%ca(i) = data%ca / sqrt(2._default)
end if
call it%advance ()
end do
end select
end associate
sf_int%status = SF_INITIAL
end subroutine ewa_setup_constants
@ %def ewa_setup_constants
@
\subsection{Kinematics}
Set kinematics. The EWA structure function allows for a
straightforward mapping of the unit interval. So, to leading order,
the structure function value is unity, but the $x$ value is
transformed. Higher orders affect the function value.
If [[map]] is unset, the $r$ and $x$ values coincide, and the Jacobian
$f(r)$ is trivial.
If [[map]] is set, the exponential mapping for the $1/x$ singularity
discussed above is applied.
<<SF ewa: ewa: TBP>>=
procedure :: complete_kinematics => ewa_complete_kinematics
<<SF ewa: procedures>>=
subroutine ewa_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(ewa_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
real(default) :: e_1
real(default) :: x0, x1, lx0, lx1, lx
e_1 = energy (sf_int%get_momentum (1))
if (sf_int%data%recoil) then
select case (sf_int%data%id)
case (23)
x0 = max (sf_int%data%x_min, sf_int%data%mz / e_1)
case (24)
x0 = max (sf_int%data%x_min, sf_int%data%mw / e_1)
end select
else
x0 = sf_int%data%x_min
end if
x1 = sf_int%data%x_max
if ( x0 >= x1) then
f = 0
sf_int%status = SF_FAILED_KINEMATICS
return
end if
if (map) then
lx0 = log (x0)
lx1 = log (x1)
lx = lx1 * r(1) + lx0 * rb(1)
x(1) = exp(lx)
f = x(1) * (lx1 - lx0)
else
x(1) = r(1)
if (x0 < x(1) .and. x(1) < x1) then
f = 1
else
sf_int%status = SF_FAILED_KINEMATICS
f = 0
return
end if
end if
xb(1) = 1 - x(1)
if (size(x) == 3) then
x(2:3) = r(2:3)
xb(2:3) = rb(2:3)
end if
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_DONE_KINEMATICS)
sf_int%x = x(1)
sf_int%xb = xb(1)
case (SF_FAILED_KINEMATICS)
sf_int%x = 0
sf_int%xb = 0
f = 0
end select
end subroutine ewa_complete_kinematics
@ %def ewa_complete_kinematics
@ Overriding the default method: we compute the [[x]] array from the
momentum configuration. In the specific case of EWA, we also set the
internally stored $x$ and $\bar x$ values, so they can be used in the
following routine.
<<SF ewa: ewa: TBP>>=
procedure :: recover_x => sf_ewa_recover_x
<<SF ewa: procedures>>=
subroutine sf_ewa_recover_x (sf_int, x, xb, x_free)
class(ewa_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(inout), optional :: x_free
call sf_int%base_recover_x (x, xb, x_free)
sf_int%x = x(1)
sf_int%xb = xb(1)
end subroutine sf_ewa_recover_x
@ %def sf_ewa_recover_x
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
<<SF ewa: ewa: TBP>>=
procedure :: inverse_kinematics => ewa_inverse_kinematics
<<SF ewa: procedures>>=
subroutine ewa_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(ewa_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
real(default) :: x0, x1, lx0, lx1, lx, e_1
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
e_1 = energy (sf_int%get_momentum (1))
if (sf_int%data%recoil) then
select case (sf_int%data%id)
case (23)
x0 = max (sf_int%data%x_min, sf_int%data%mz / e_1)
case (24)
x0 = max (sf_int%data%x_min, sf_int%data%mw / e_1)
end select
else
x0 = sf_int%data%x_min
end if
x1 = sf_int%data%x_max
if (map) then
lx0 = log (x0)
lx1 = log (x1)
lx = log (x(1))
r(1) = (lx - lx0) / (lx1 - lx0)
rb(1) = (lx1 - lx) / (lx1 - lx0)
f = x(1) * (lx1 - lx0)
else
r (1) = x(1)
rb(1) = 1 - x(1)
if (x0 < x(1) .and. x(1) < x1) then
f = 1
else
f = 0
end if
end if
if (size(r) == 3) then
r (2:3) = x(2:3)
rb(2:3) = xb(2:3)
end if
if (set_mom) then
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end if
end subroutine ewa_inverse_kinematics
@ %def ewa_inverse_kinematics
@
\subsection{EWA application}
For EWA, we can compute kinematics and function value in a single
step. This function works on a single beam, assuming that the input
momentum has been set. We need four random numbers as input: one for
$x$, one for $Q^2$, and two for the polar and azimuthal angles.
Alternatively, we can skip $p_T$ generation; in this case, we only
need one.
For obtaining splitting kinematics, we rely on the assumption that all
in-particles are mass-degenerate (or there is only one), so the
generated $x$ values are identical.
<<SF ewa: ewa: TBP>>=
procedure :: apply => ewa_apply
<<SF ewa: procedures>>=
- subroutine ewa_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine ewa_apply (sf_int, scale, rescale, i_sub)
class(ewa_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default) :: x, xb, pt2, c1, c2
real(default) :: cv, ca
real(default) :: f, fm, fp, fL
integer :: i
associate (data => sf_int%data)
x = sf_int%x
xb = sf_int%xb
pt2 = min ((data%pt_max)**2, (xb * data%sqrts / 2)**2)
select case (data%id)
case (23)
!!! Z boson structure function
c1 = log (1 + pt2 / (xb * (data%mZ)**2))
c2 = 1 / (1 + (xb * (data%mZ)**2) / pt2)
case (24)
!!! W boson structure function
c1 = log (1 + pt2 / (xb * (data%mW)**2))
c2 = 1 / (1 + (xb * (data%mW)**2) / pt2)
end select
do i = 1, sf_int%n_me
cv = sf_int%cv(i)
ca = sf_int%ca(i)
fm = data%coeff * &
((cv + ca)**2 + ((cv - ca) * xb)**2) * (c1 - c2) / (2 * x)
fp = data%coeff * &
((cv - ca)**2 + ((cv + ca) * xb)**2) * (c1 - c2) / (2 * x)
fL = data%coeff * &
(cv**2 + ca**2) * (2 * xb / x) * c2
f = fp + fm + fL
if (.not. vanishes (f)) then
fp = fp / f
fm = fm / f
fL = fL / f
end if
call sf_int%set_matrix_element (i, cmplx (f, kind=default))
end do
end associate
sf_int%status = SF_EVALUATED
end subroutine ewa_apply
@ %def ewa_apply
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_ewa_ut.f90]]>>=
<<File header>>
module sf_ewa_ut
use unit_tests
use sf_ewa_uti
<<Standard module head>>
<<SF ewa: public test>>
contains
<<SF ewa: test driver>>
end module sf_ewa_ut
@ %def sf_ewa_ut
@
<<[[sf_ewa_uti.f90]]>>=
<<File header>>
module sf_ewa_uti
<<Use kinds>>
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use interactions, only: interaction_pacify_momenta
use model_data
use sf_aux
use sf_base
use sf_ewa
<<Standard module head>>
<<SF ewa: test declarations>>
contains
<<SF ewa: tests>>
end module sf_ewa_uti
@ %def sf_ewa_ut
@ API: driver for the unit tests below.
<<SF ewa: public test>>=
public :: sf_ewa_test
<<SF ewa: test driver>>=
subroutine sf_ewa_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF ewa: execute tests>>
end subroutine sf_ewa_test
@ %def sf_ewa_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF ewa: execute tests>>=
call test (sf_ewa_1, "sf_ewa_1", &
"structure function configuration", &
u, results)
<<SF ewa: test declarations>>=
public :: sf_ewa_1
<<SF ewa: tests>>=
subroutine sf_ewa_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(pdg_array_t), dimension(1) :: pdg_out
integer, dimension(:), allocatable :: pdg1
class(sf_data_t), allocatable :: data
write (u, "(A)") "* Test output: sf_ewa_1"
write (u, "(A)") "* Purpose: initialize and display &
&test structure function data"
write (u, "(A)")
write (u, "(A)") "* Create empty data object"
write (u, "(A)")
call model%init_sm_test ()
pdg_in = 2
allocate (ewa_data_t :: data)
call data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize for Z boson"
write (u, "(A)")
select type (data)
type is (ewa_data_t)
call data%init (model, pdg_in, 0.01_default, &
500._default, 5000._default, .false., .false.)
call data%set_id (23)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
write (u, "(2x,99(1x,I0))") pdg1
write (u, "(A)")
write (u, "(A)") "* Initialize for W boson"
write (u, "(A)")
deallocate (data)
allocate (ewa_data_t :: data)
select type (data)
type is (ewa_data_t)
call data%init (model, pdg_in, 0.01_default, &
500._default, 5000._default, .false., .false.)
call data%set_id (24)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
write (u, "(2x,99(1x,I0))") pdg1
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_ewa_1"
end subroutine sf_ewa_1
@ %def sf_ewa_1
@
\subsubsection{Test and probe structure function}
Construct and display a structure function object based on the EWA
structure function.
<<SF ewa: execute tests>>=
call test (sf_ewa_2, "sf_ewa_2", &
"structure function instance", &
u, results)
<<SF ewa: test declarations>>=
public :: sf_ewa_2
<<SF ewa: tests>>=
subroutine sf_ewa_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_ewa_2"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_sm_test ()
call flv%init (2, model)
pdg_in = 2
call reset_interaction_counter ()
allocate (ewa_data_t :: data)
select type (data)
type is (ewa_data_t)
call data%init (model, pdg_in, 0.01_default, &
500._default, 3000._default, .false., .true.)
call data%set_id (24)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=1500"
write (u, "(A)")
E = 1500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.4, no EWA mapping, collinear"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.4_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false., &
set_momenta=.true.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate EWA structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_ewa_2"
end subroutine sf_ewa_2
@ %def sf_ewa_2
@
\subsubsection{Standard mapping}
Construct and display a structure function object based on the EWA
structure function, applying the standard single-particle mapping.
<<SF ewa: execute tests>>=
call test (sf_ewa_3, "sf_ewa_3", &
"apply mapping", &
u, results)
<<SF ewa: test declarations>>=
public :: sf_ewa_3
<<SF ewa: tests>>=
subroutine sf_ewa_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_ewa_3"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_sm_test ()
call flv%init (2, model)
pdg_in = 2
call reset_interaction_counter ()
allocate (ewa_data_t :: data)
select type (data)
type is (ewa_data_t)
call data%init (model, pdg_in, 0.01_default, &
500._default, 3000._default, .false., .true.)
call data%set_id (24)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=1500"
write (u, "(A)")
E = 1500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.4, with EWA mapping, collinear"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.4_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.true., &
set_momenta=.true.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate EWA structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_ewa_3"
end subroutine sf_ewa_3
@ %def sf_ewa_3
@
\subsubsection{Non-collinear case}
Construct and display a structure function object based on the EPA
structure function.
<<SF ewa: execute tests>>=
call test (sf_ewa_4, "sf_ewa_4", &
"non-collinear", &
u, results)
<<SF ewa: test declarations>>=
public :: sf_ewa_4
<<SF ewa: tests>>=
subroutine sf_ewa_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_ewa_4"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call modeL%init_sm_test ()
call flv%init (2, model)
pdg_in = 2
call reset_interaction_counter ()
allocate (ewa_data_t :: data)
select type (data)
type is (ewa_data_t)
call data%init (model, pdg_in, 0.01_default, &
500._default, 3000.0_default, .true., .true.)
call data%set_id (24)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
write (u, "(A)") "* Initialize incoming momentum with E=1500"
write (u, "(A)")
E = 1500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.5/0.5/0.25, with EWA mapping, "
write (u, "(A)") " non-coll., keeping energy"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = [0.5_default, 0.5_default, 0.25_default]
rb = 1 - r
sf_int%on_shell_mode = KEEP_ENERGY
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.true.)
call interaction_pacify_momenta (sf_int%interaction_t, 1e-10_default)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x and r from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.true., &
set_momenta=.true.)
call interaction_pacify_momenta (sf_int%interaction_t, 1e-10_default)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate EWA structure function"
write (u, "(A)")
call sf_int%apply (scale = 1500._default)
call sf_int%write (u, testflag = .true.)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_ewa_4"
end subroutine sf_ewa_4
@ %def sf_ewa_4
@
\subsubsection{Structure function for multiple flavors}
Construct and display a structure function object based on the EWA
structure function. The incoming state has multiple particles with
non-uniform quantum numbers.
<<SF ewa: execute tests>>=
call test (sf_ewa_5, "sf_ewa_5", &
"structure function instance", &
u, results)
<<SF ewa: test declarations>>=
public :: sf_ewa_5
<<SF ewa: tests>>=
subroutine sf_ewa_5 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_ewa_5"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_sm_test ()
call flv%init (2, model)
pdg_in = [1, 2, -1, -2]
call reset_interaction_counter ()
allocate (ewa_data_t :: data)
select type (data)
type is (ewa_data_t)
call data%init (model, pdg_in, 0.01_default, &
500._default, 3000._default, .false., .true.)
call data%set_id (24)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%setup_constants ()
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=1500"
write (u, "(A)")
E = 1500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call pacify (k, 1e-10_default)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for r=0.4, no EWA mapping, collinear"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.4_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Evaluate EWA structure function"
write (u, "(A)")
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_ewa_5"
end subroutine sf_ewa_5
@ %def sf_ewa_5
@
\clearpage
%------------------------------------------------------------------------
\section{Energy-scan spectrum}
This spectrum is actually a trick that allows us to plot the c.m.\ energy
dependence of a cross section without scanning the input energy. We
start with the observation that a spectrum $f(x)$, applied to one of
the incoming beams only, results in a cross section
\begin{equation}
\sigma = \int dx\,f(x)\,\hat\sigma(xs).
\end{equation}
We want to compute the distribution of $E=\sqrt{\hat s}=\sqrt{xs}$, i.e.,
\begin{equation}
\frac{d\sigma}{dE} = \frac{2\sqrt{x}}{\sqrt{s}}\,\frac{d\sigma}{dx}
= \frac{2\sqrt{x}}{\sqrt{s}}\,f(x)\,\hat\sigma(xs),
\end{equation}
so if we set
\begin{equation}
f(x) = \frac{\sqrt{s}}{2\sqrt{x}},
\end{equation}
we get the distribution
\begin{equation}
\frac{d\sigma}{dE} = \hat\sigma(\hat s=E^2).
\end{equation}
We implement this as a spectrum with a single parameter $x$. The
parameters for the individual beams are computed as $x_i=\sqrt{x}$, so
they are equal and the kinematics is always symmetric.
<<[[sf_escan.f90]]>>=
<<File header>>
module sf_escan
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_12
use numeric_utils
use diagnostics
use lorentz
use pdg_arrays
use model_data
use flavors
use quantum_numbers
use state_matrices
use polarizations
use sf_base
<<Standard module head>>
<<SF escan: public>>
<<SF escan: types>>
contains
<<SF escan: procedures>>
end module sf_escan
@ %def sf_escan
@
\subsection{Data type}
The [[norm]] is unity if the total cross section should be normalized
to one, and $\sqrt{s}$ if it should be normalized to the total
energy. In the latter case, the differential distribution
$d\sigma/d\sqrt{\hat s}$ coincides with the partonic cross section
$\hat\sigma$ as a function of $\sqrt{\hat s}$.
<<SF escan: public>>=
public :: escan_data_t
<<SF escan: types>>=
type, extends(sf_data_t) :: escan_data_t
private
type(flavor_t), dimension(:,:), allocatable :: flv_in
integer, dimension(2) :: n_flv = 0
real(default) :: norm = 1
contains
<<SF escan: escan data: TBP>>
end type escan_data_t
@ %def escan_data_t
<<SF escan: escan data: TBP>>=
procedure :: init => escan_data_init
<<SF escan: procedures>>=
subroutine escan_data_init (data, model, pdg_in, norm)
class(escan_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), dimension(2), intent(in) :: pdg_in
real(default), intent(in), optional :: norm
real(default), dimension(2) :: m2
integer :: i, j
data%n_flv = pdg_array_get_length (pdg_in)
allocate (data%flv_in (maxval (data%n_flv), 2))
do i = 1, 2
do j = 1, data%n_flv(i)
call data%flv_in(j, i)%init (pdg_array_get (pdg_in(i), j), model)
end do
end do
m2 = data%flv_in(1,:)%get_mass ()
do i = 1, 2
if (.not. any (nearly_equal (data%flv_in(1:data%n_flv(i),i)%get_mass (), m2(i)))) then
call msg_fatal ("Energy scan: incoming particle mass must be uniform")
end if
end do
if (present (norm)) data%norm = norm
end subroutine escan_data_init
@ %def escan_data_init
@ Output
<<SF escan: escan data: TBP>>=
procedure :: write => escan_data_write
<<SF escan: procedures>>=
subroutine escan_data_write (data, unit, verbose)
class(escan_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u, i, j
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "Energy-scan data:"
write (u, "(3x,A)", advance="no") "prt_in = "
do i = 1, 2
if (i > 1) write (u, "(',',1x)", advance="no")
do j = 1, data%n_flv(i)
if (j > 1) write (u, "(':')", advance="no")
write (u, "(A)", advance="no") char (data%flv_in(j,i)%get_name ())
end do
end do
write (u, *)
write (u, "(3x,A," // FMT_12 // ")") "norm =", data%norm
end subroutine escan_data_write
@ %def escan_data_write
@ Kinematics is completely collinear, hence there is only one
parameter for a pair spectrum.
<<SF escan: escan data: TBP>>=
procedure :: get_n_par => escan_data_get_n_par
<<SF escan: procedures>>=
function escan_data_get_n_par (data) result (n)
class(escan_data_t), intent(in) :: data
integer :: n
n = 1
end function escan_data_get_n_par
@ %def escan_data_get_n_par
@ Return the outgoing particles PDG codes. This is always the same as
the incoming particle, where we use two indices for the two beams.
<<SF escan: escan data: TBP>>=
procedure :: get_pdg_out => escan_data_get_pdg_out
<<SF escan: procedures>>=
subroutine escan_data_get_pdg_out (data, pdg_out)
class(escan_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
integer :: i, n
n = 2
do i = 1, n
pdg_out(i) = data%flv_in(1:data%n_flv(i),i)%get_pdg ()
end do
end subroutine escan_data_get_pdg_out
@ %def escan_data_get_pdg_out
@ Allocate the interaction record.
<<SF escan: escan data: TBP>>=
procedure :: allocate_sf_int => escan_data_allocate_sf_int
<<SF escan: procedures>>=
subroutine escan_data_allocate_sf_int (data, sf_int)
class(escan_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (escan_t :: sf_int)
end subroutine escan_data_allocate_sf_int
@ %def escan_data_allocate_sf_int
@
\subsection{The Energy-scan object}
This is a spectrum, not a radiation. We create an interaction with
two incoming and two outgoing particles, flavor, color, and helicity
being carried through. $x$ nevertheless is only one-dimensional, as we
are always using only one beam parameter.
<<SF escan: types>>=
type, extends (sf_int_t) :: escan_t
type(escan_data_t), pointer :: data => null ()
contains
<<SF escan: escan: TBP>>
end type escan_t
@ %def escan_t
@ Type string: for the energy scan this is just a dummy function.
<<SF escan: escan: TBP>>=
procedure :: type_string => escan_type_string
<<SF escan: procedures>>=
function escan_type_string (object) result (string)
class(escan_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "Escan: energy scan"
else
string = "Escan: [undefined]"
end if
end function escan_type_string
@ %def escan_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF escan: escan: TBP>>=
procedure :: write => escan_write
<<SF escan: procedures>>=
subroutine escan_write (object, unit, testflag)
class(escan_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "Energy scan data: [undefined]"
end if
end subroutine escan_write
@ %def escan_write
@
<<SF escan: escan: TBP>>=
procedure :: init => escan_init
<<SF escan: procedures>>=
subroutine escan_init (sf_int, data)
class(escan_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(4) :: mask
integer, dimension(4) :: hel_lock
real(default), dimension(2) :: m2
real(default), dimension(0) :: mr2
type(quantum_numbers_t), dimension(4) :: qn_fc, qn_hel, qn
type(polarization_t), target :: pol1, pol2
type(polarization_iterator_t) :: it_hel1, it_hel2
integer :: j1, j2
select type (data)
type is (escan_data_t)
hel_lock = [3, 4, 1, 2]
m2 = data%flv_in(1,:)%get_mass ()
call sf_int%base_init (mask, m2, mr2, m2, hel_lock = hel_lock)
sf_int%data => data
do j1 = 1, data%n_flv(1)
call qn_fc(1)%init ( &
flv = data%flv_in(j1,1), &
col = color_from_flavor (data%flv_in(j1,1)))
call qn_fc(3)%init ( &
flv = data%flv_in(j1,1), &
col = color_from_flavor (data%flv_in(j1,1)))
call pol1%init_generic (data%flv_in(j1,1))
do j2 = 1, data%n_flv(2)
call qn_fc(2)%init ( &
flv = data%flv_in(j2,2), &
col = color_from_flavor (data%flv_in(j2,2)))
call qn_fc(4)%init ( &
flv = data%flv_in(j2,2), &
col = color_from_flavor (data%flv_in(j2,2)))
call pol2%init_generic (data%flv_in(j2,2))
call it_hel1%init (pol1)
do while (it_hel1%is_valid ())
qn_hel(1) = it_hel1%get_quantum_numbers ()
qn_hel(3) = it_hel1%get_quantum_numbers ()
call it_hel2%init (pol2)
do while (it_hel2%is_valid ())
qn_hel(2) = it_hel2%get_quantum_numbers ()
qn_hel(4) = it_hel2%get_quantum_numbers ()
qn = qn_hel .merge. qn_fc
call sf_int%add_state (qn)
call it_hel2%advance ()
end do
call it_hel1%advance ()
end do
! call pol2%final ()
end do
! call pol1%final ()
end do
call sf_int%set_incoming ([1,2])
call sf_int%set_outgoing ([3,4])
call sf_int%freeze ()
sf_int%status = SF_INITIAL
end select
end subroutine escan_init
@ %def escan_init
@
\subsection{Kinematics}
Set kinematics. We have a single parameter, but reduce both beams.
The [[map]] flag is ignored.
<<SF escan: escan: TBP>>=
procedure :: complete_kinematics => escan_complete_kinematics
<<SF escan: procedures>>=
subroutine escan_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(escan_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default) :: sqrt_x
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
x = r
xb= rb
sqrt_x = sqrt (x(1))
if (sqrt_x > 0) then
f = 1 / (2 * sqrt_x)
else
f = 0
sf_int%status = SF_FAILED_KINEMATICS
return
end if
call sf_int%reduce_momenta ([sqrt_x, sqrt_x])
end subroutine escan_complete_kinematics
@ %def escan_complete_kinematics
@ Recover $x$. The base procedure should return two momentum
fractions for the two beams, while we have only one parameter. This
is the product of the extracted momentum fractions.
<<SF escan: escan: TBP>>=
procedure :: recover_x => escan_recover_x
<<SF escan: procedures>>=
subroutine escan_recover_x (sf_int, x, xb, x_free)
class(escan_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(inout), optional :: x_free
real(default), dimension(2) :: xi, xib
call sf_int%base_recover_x (xi, xib, x_free)
x = product (xi)
xb= 1 - x
end subroutine escan_recover_x
@ %def escan_recover_x
@ Compute inverse kinematics.
<<SF escan: escan: TBP>>=
procedure :: inverse_kinematics => escan_inverse_kinematics
<<SF escan: procedures>>=
subroutine escan_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(escan_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
real(default) :: sqrt_x
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
sqrt_x = sqrt (x(1))
if (sqrt_x > 0) then
f = 1 / (2 * sqrt_x)
else
f = 0
sf_int%status = SF_FAILED_KINEMATICS
return
end if
r = x
rb = xb
if (set_mom) then
call sf_int%reduce_momenta ([sqrt_x, sqrt_x])
end if
end subroutine escan_inverse_kinematics
@ %def escan_inverse_kinematics
@
\subsection{Energy scan application}
Here, we insert the predefined norm.
<<SF escan: escan: TBP>>=
procedure :: apply => escan_apply
<<SF escan: procedures>>=
- subroutine escan_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine escan_apply (sf_int, scale, rescale, i_sub)
class(escan_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default) :: f
associate (data => sf_int%data)
f = data%norm
end associate
call sf_int%set_matrix_element (cmplx (f, kind=default))
sf_int%status = SF_EVALUATED
end subroutine escan_apply
@ %def escan_apply
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_escan_ut.f90]]>>=
<<File header>>
module sf_escan_ut
use unit_tests
use sf_escan_uti
<<Standard module head>>
<<SF escan: public test>>
contains
<<SF escan: test driver>>
end module sf_escan_ut
@ %def sf_escan_ut
@
<<[[sf_escan_uti.f90]]>>=
<<File header>>
module sf_escan_uti
<<Use kinds>>
use physics_defs, only: ELECTRON
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use model_data
use sf_aux
use sf_base
use sf_escan
<<Standard module head>>
<<SF escan: test declarations>>
contains
<<SF escan: tests>>
end module sf_escan_uti
@ %def sf_escan_ut
@ API: driver for the unit tests below.
<<SF escan: public test>>=
public :: sf_escan_test
<<SF escan: test driver>>=
subroutine sf_escan_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF escan: execute tests>>
end subroutine sf_escan_test
@ %def sf_escan_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF escan: execute tests>>=
call test (sf_escan_1, "sf_escan_1", &
"structure function configuration", &
u, results)
<<SF escan: test declarations>>=
public :: sf_escan_1
<<SF escan: tests>>=
subroutine sf_escan_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t), dimension(2) :: pdg_in
type(pdg_array_t), dimension(2) :: pdg_out
integer, dimension(:), allocatable :: pdg1, pdg2
class(sf_data_t), allocatable :: data
write (u, "(A)") "* Test output: sf_escan_1"
write (u, "(A)") "* Purpose: initialize and display &
&energy-scan structure function data"
write (u, "(A)")
call model%init_qed_test ()
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
allocate (escan_data_t :: data)
select type (data)
type is (escan_data_t)
call data%init (model, pdg_in, norm = 2._default)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
pdg2 = pdg_out(2)
write (u, "(2x,99(1x,I0))") pdg1, pdg2
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_escan_1"
end subroutine sf_escan_1
@ %def sf_escan_1
g@
\subsubsection{Probe the structure-function object}
Active the beam event reader, generate an event.
<<SF escan: execute tests>>=
call test (sf_escan_2, "sf_escan_2", &
"generate event", &
u, results)
<<SF escan: test declarations>>=
public :: sf_escan_2
<<SF escan: tests>>=
subroutine sf_escan_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t), dimension(2) :: flv
type(pdg_array_t), dimension(2) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k1, k2
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: x_free, f
write (u, "(A)") "* Test output: sf_escan_2"
write (u, "(A)") "* Purpose: initialize and display &
&beam-events structure function data"
write (u, "(A)")
call model%init_qed_test ()
call flv(1)%init (ELECTRON, model)
call flv(2)%init (-ELECTRON, model)
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
call reset_interaction_counter ()
allocate (escan_data_t :: data)
select type (data)
type is (escan_data_t)
call data%init (model, pdg_in)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1,2])
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 250
k1 = vector4_moving (E, sqrt (E**2 - flv(1)%get_mass ()**2), 3)
k2 = vector4_moving (E,-sqrt (E**2 - flv(2)%get_mass ()**2), 3)
call vector4_write (k1, u)
call vector4_write (k2, u)
call sf_int%seed_kinematics ([k1, k2])
write (u, "(A)")
write (u, "(A)") "* Set dummy parameters and generate x"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.8
rb = 1 - r
x_free = 1
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A,9(1x,F10.7))") "xf=", x_free
write (u, "(A)")
write (u, "(A)") "* Inverse kinematics"
write (u, "(A)")
call sf_int%recover_x (x, xb, x_free)
call sf_int%inverse_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A,9(1x,F10.7))") "xf=", x_free
write (u, "(A)")
write (u, "(A)") "* Evaluate"
write (u, "(A)")
call sf_int%apply (scale = 0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_escan_2"
end subroutine sf_escan_2
@ %def sf_escan_2
@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Gaussian beam spread}
Instead of an analytic beam description, beam data may be provided in
form of an event file. In its most simple form, the event file
contains pairs of $x$ values, relative to nominal beam energies. More
advanced formats may include polarization, etc. The current
implementation carries beam polarization through, if specified.
The code is very similar to the energy scan described above.
However, we must include a file-handle manager for the beam-event
files. Two different processes may access a given beam-event file at
the same time (i.e., serially but alternating). Accessing an open
file from two different units is non-standard and not supported by all
compilers. Therefore, we keep a global registry of open files,
associated units, and reference counts. The [[gaussian_t]] objects
act as proxies to this registry.
<<[[sf_gaussian.f90]]>>=
<<File header>>
module sf_gaussian
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_12
use file_registries
use diagnostics
use lorentz
use rng_base
use pdg_arrays
use model_data
use flavors
use quantum_numbers
use state_matrices
use polarizations
use sf_base
<<Standard module head>>
<<SF gaussian: public>>
<<SF gaussian: types>>
contains
<<SF gaussian: procedures>>
end module sf_gaussian
@ %def sf_gaussian
@
\subsection{The beam-data file registry}
We manage data files via the [[file_registries]] module. To this end,
we keep the registry as a private module variable here.
<<CCC SF gaussian: variables>>=
type(file_registry_t), save :: beam_file_registry
@ %def beam_file_registry
@
\subsection{Data type}
We store the spread for each beam, as a relative number related to the beam
energy. For the actual generation, we include an (abstract) random-number
generator factory.
<<SF gaussian: public>>=
public :: gaussian_data_t
<<SF gaussian: types>>=
type, extends(sf_data_t) :: gaussian_data_t
private
type(flavor_t), dimension(2) :: flv_in
real(default), dimension(2) :: spread
class(rng_factory_t), allocatable :: rng_factory
contains
<<SF gaussian: gaussian data: TBP>>
end type gaussian_data_t
@ %def gaussian_data_t
<<SF gaussian: gaussian data: TBP>>=
procedure :: init => gaussian_data_init
<<SF gaussian: procedures>>=
subroutine gaussian_data_init (data, model, pdg_in, spread, rng_factory)
class(gaussian_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), dimension(2), intent(in) :: pdg_in
real(default), dimension(2), intent(in) :: spread
class(rng_factory_t), intent(inout), allocatable :: rng_factory
if (any (spread < 0)) then
call msg_fatal ("Gaussian beam spread: must not be negative")
end if
call data%flv_in(1)%init (pdg_array_get (pdg_in(1), 1), model)
call data%flv_in(2)%init (pdg_array_get (pdg_in(2), 1), model)
data%spread = spread
call move_alloc (from = rng_factory, to = data%rng_factory)
end subroutine gaussian_data_init
@ %def gaussian_data_init
@ Return true since this spectrum is always in generator mode.
<<SF gaussian: gaussian data: TBP>>=
procedure :: is_generator => gaussian_data_is_generator
<<SF gaussian: procedures>>=
function gaussian_data_is_generator (data) result (flag)
class(gaussian_data_t), intent(in) :: data
logical :: flag
flag = .true.
end function gaussian_data_is_generator
@ %def gaussian_data_is_generator
@ The number of parameters is two. They are free parameters.
<<SF gaussian: gaussian data: TBP>>=
procedure :: get_n_par => gaussian_data_get_n_par
<<SF gaussian: procedures>>=
function gaussian_data_get_n_par (data) result (n)
class(gaussian_data_t), intent(in) :: data
integer :: n
n = 2
end function gaussian_data_get_n_par
@ %def gaussian_data_get_n_par
<<SF gaussian: gaussian data: TBP>>=
procedure :: get_pdg_out => gaussian_data_get_pdg_out
<<SF gaussian: procedures>>=
subroutine gaussian_data_get_pdg_out (data, pdg_out)
class(gaussian_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
integer :: i, n
n = 2
do i = 1, n
pdg_out(i) = data%flv_in(i)%get_pdg ()
end do
end subroutine gaussian_data_get_pdg_out
@ %def gaussian_data_get_pdg_out
@ Allocate the interaction record.
<<SF gaussian: gaussian data: TBP>>=
procedure :: allocate_sf_int => gaussian_data_allocate_sf_int
<<SF gaussian: procedures>>=
subroutine gaussian_data_allocate_sf_int (data, sf_int)
class(gaussian_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (gaussian_t :: sf_int)
end subroutine gaussian_data_allocate_sf_int
@ %def gaussian_data_allocate_sf_int
@ Output
<<SF gaussian: gaussian data: TBP>>=
procedure :: write => gaussian_data_write
<<SF gaussian: procedures>>=
subroutine gaussian_data_write (data, unit, verbose)
class(gaussian_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "Gaussian beam spread data:"
write (u, "(3x,A,A,A,A)") "prt_in = ", &
char (data%flv_in(1)%get_name ()), &
", ", char (data%flv_in(2)%get_name ())
write (u, "(3x,A,2(1x," // FMT_12 // "))") "spread =", data%spread
call data%rng_factory%write (u)
end subroutine gaussian_data_write
@ %def gaussian_data_write
@
\subsection{The gaussian object}
Flavor and polarization carried through, no radiated particles. The generator
needs a random-number generator, obviously.
<<SF gaussian: public>>=
public :: gaussian_t
<<SF gaussian: types>>=
type, extends (sf_int_t) :: gaussian_t
type(gaussian_data_t), pointer :: data => null ()
class(rng_t), allocatable :: rng
contains
<<SF gaussian: gaussian: TBP>>
end type gaussian_t
@ %def gaussian_t
@ Type string: show gaussian file.
<<SF gaussian: gaussian: TBP>>=
procedure :: type_string => gaussian_type_string
<<SF gaussian: procedures>>=
function gaussian_type_string (object) result (string)
class(gaussian_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "Gaussian: gaussian beam-energy spread"
else
string = "Gaussian: [undefined]"
end if
end function gaussian_type_string
@ %def gaussian_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF gaussian: gaussian: TBP>>=
procedure :: write => gaussian_write
<<SF gaussian: procedures>>=
subroutine gaussian_write (object, unit, testflag)
class(gaussian_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
call object%rng%write (u)
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "gaussian data: [undefined]"
end if
end subroutine gaussian_write
@ %def gaussian_write
@
<<SF gaussian: gaussian: TBP>>=
procedure :: init => gaussian_init
<<SF gaussian: procedures>>=
subroutine gaussian_init (sf_int, data)
class(gaussian_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
real(default), dimension(2) :: m2
real(default), dimension(0) :: mr2
type(quantum_numbers_mask_t), dimension(4) :: mask
integer, dimension(4) :: hel_lock
type(quantum_numbers_t), dimension(4) :: qn_fc, qn_hel, qn
type(polarization_t), target :: pol1, pol2
type(polarization_iterator_t) :: it_hel1, it_hel2
integer :: i
select type (data)
type is (gaussian_data_t)
m2 = data%flv_in%get_mass () ** 2
hel_lock = [3, 4, 1, 2]
mask = quantum_numbers_mask (.false., .false., .false.)
call sf_int%base_init (mask, m2, mr2, m2, hel_lock = hel_lock)
sf_int%data => data
do i = 1, 2
call qn_fc(i)%init ( &
flv = data%flv_in(i), &
col = color_from_flavor (data%flv_in(i)))
call qn_fc(i+2)%init ( &
flv = data%flv_in(i), &
col = color_from_flavor (data%flv_in(i)))
end do
call pol1%init_generic (data%flv_in(1))
call it_hel1%init (pol1)
do while (it_hel1%is_valid ())
qn_hel(1) = it_hel1%get_quantum_numbers ()
qn_hel(3) = it_hel1%get_quantum_numbers ()
call pol2%init_generic (data%flv_in(2))
call it_hel2%init (pol2)
do while (it_hel2%is_valid ())
qn_hel(2) = it_hel2%get_quantum_numbers ()
qn_hel(4) = it_hel2%get_quantum_numbers ()
qn = qn_hel .merge. qn_fc
call sf_int%add_state (qn)
call it_hel2%advance ()
end do
! call pol2%final ()
call it_hel1%advance ()
end do
! call pol1%final ()
call sf_int%freeze ()
call sf_int%set_incoming ([1,2])
call sf_int%set_outgoing ([3,4])
sf_int%status = SF_INITIAL
end select
call sf_int%data%rng_factory%make (sf_int%rng)
end subroutine gaussian_init
@ %def gaussian_init
@ This spectrum type needs a finalizer, which closes the data file.
<<SF gaussian: gaussian: TBP>>=
procedure :: final => sf_gaussian_final
<<SF gaussian: procedures>>=
subroutine sf_gaussian_final (object)
class(gaussian_t), intent(inout) :: object
call object%interaction_t%final ()
end subroutine sf_gaussian_final
@ %def sf_gaussian_final
@
\subsection{Kinematics}
Refer to the [[data]] component.
<<SF gaussian: gaussian: TBP>>=
procedure :: is_generator => gaussian_is_generator
<<SF gaussian: procedures>>=
function gaussian_is_generator (sf_int) result (flag)
class(gaussian_t), intent(in) :: sf_int
logical :: flag
flag = sf_int%data%is_generator ()
end function gaussian_is_generator
@ %def gaussian_is_generator
@ Generate free parameters. The $x$ value should be distributed with mean $1$
and $\sigma$ given by the spread. We reject negative $x$ values. (This
cut slightly biases the distribution, but for reasonable (small)
spreads negative $r$ should not occur.
<<SF gaussian: gaussian: TBP>>=
procedure :: generate_free => gaussian_generate_free
<<SF gaussian: procedures>>=
subroutine gaussian_generate_free (sf_int, r, rb, x_free)
class(gaussian_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(inout) :: x_free
real(default), dimension(size(r)) :: z
associate (data => sf_int%data)
do
call sf_int%rng%generate_gaussian (z)
rb = z * data%spread
r = 1 - rb
x_free = x_free * product (r)
if (all (r > 0)) exit
end do
end associate
end subroutine gaussian_generate_free
@ %def gaussian_generate_free
@ Set kinematics. Trivial transfer since this is a pure generator.
The [[map]] flag doesn't apply.
<<SF gaussian: gaussian: TBP>>=
procedure :: complete_kinematics => gaussian_complete_kinematics
<<SF gaussian: procedures>>=
subroutine gaussian_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(gaussian_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
if (map) then
call msg_fatal ("gaussian: map flag not supported")
else
x = r
xb= rb
f = 1
end if
call sf_int%reduce_momenta (x)
end subroutine gaussian_complete_kinematics
@ %def gaussian_complete_kinematics
@ Compute inverse kinematics. Trivial in this case.
<<SF gaussian: gaussian: TBP>>=
procedure :: inverse_kinematics => gaussian_inverse_kinematics
<<SF gaussian: procedures>>=
subroutine gaussian_inverse_kinematics &
(sf_int, x, xb, f, r, rb, map, set_momenta)
class(gaussian_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
call msg_fatal ("gaussian: map flag not supported")
else
r = x
rb= xb
f = 1
end if
if (set_mom) then
call sf_int%reduce_momenta (x)
end if
end subroutine gaussian_inverse_kinematics
@ %def gaussian_inverse_kinematics
@
\subsection{gaussian application}
Trivial, just set the unit weight.
<<SF gaussian: gaussian: TBP>>=
procedure :: apply => gaussian_apply
<<SF gaussian: procedures>>=
- subroutine gaussian_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine gaussian_apply (sf_int, scale, rescale, i_sub)
class(gaussian_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default) :: f
f = 1
call sf_int%set_matrix_element (cmplx (f, kind=default))
sf_int%status = SF_EVALUATED
end subroutine gaussian_apply
@ %def gaussian_apply
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_gaussian_ut.f90]]>>=
<<File header>>
module sf_gaussian_ut
use unit_tests
use sf_gaussian_uti
<<Standard module head>>
<<SF gaussian: public test>>
contains
<<SF gaussian: test driver>>
end module sf_gaussian_ut
@ %def sf_gaussian_ut
@
<<[[sf_gaussian_uti.f90]]>>=
<<File header>>
module sf_gaussian_uti
<<Use kinds>>
use physics_defs, only: ELECTRON
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use model_data
use rng_base
use sf_aux
use sf_base
use sf_gaussian
use rng_base_ut, only: rng_test_factory_t
<<Standard module head>>
<<SF gaussian: test declarations>>
contains
<<SF gaussian: tests>>
end module sf_gaussian_uti
@ %def sf_gaussian_ut
@ API: driver for the unit tests below.
<<SF gaussian: public test>>=
public :: sf_gaussian_test
<<SF gaussian: test driver>>=
subroutine sf_gaussian_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF gaussian: execute tests>>
end subroutine sf_gaussian_test
@ %def sf_gaussian_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF gaussian: execute tests>>=
call test (sf_gaussian_1, "sf_gaussian_1", &
"structure function configuration", &
u, results)
<<SF gaussian: test declarations>>=
public :: sf_gaussian_1
<<SF gaussian: tests>>=
subroutine sf_gaussian_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t), dimension(2) :: pdg_in
type(pdg_array_t), dimension(2) :: pdg_out
integer, dimension(:), allocatable :: pdg1, pdg2
class(sf_data_t), allocatable :: data
class(rng_factory_t), allocatable :: rng_factory
write (u, "(A)") "* Test output: sf_gaussian_1"
write (u, "(A)") "* Purpose: initialize and display &
&gaussian-spread structure function data"
write (u, "(A)")
call model%init_qed_test ()
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
allocate (gaussian_data_t :: data)
allocate (rng_test_factory_t :: rng_factory)
select type (data)
type is (gaussian_data_t)
call data%init (model, pdg_in, [1e-2_default, 2e-2_default], rng_factory)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
pdg2 = pdg_out(2)
write (u, "(2x,99(1x,I0))") pdg1, pdg2
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_gaussian_1"
end subroutine sf_gaussian_1
@ %def sf_gaussian_1
@
\subsubsection{Probe the structure-function object}
Active the beam event reader, generate an event.
<<SF gaussian: execute tests>>=
call test (sf_gaussian_2, "sf_gaussian_2", &
"generate event", &
u, results)
<<SF gaussian: test declarations>>=
public :: sf_gaussian_2
<<SF gaussian: tests>>=
subroutine sf_gaussian_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t), dimension(2) :: flv
type(pdg_array_t), dimension(2) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(rng_factory_t), allocatable :: rng_factory
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k1, k2
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: x_free, f
integer :: i
write (u, "(A)") "* Test output: sf_gaussian_2"
write (u, "(A)") "* Purpose: initialize and display &
&gaussian-spread structure function data"
write (u, "(A)")
call model%init_qed_test ()
call flv(1)%init (ELECTRON, model)
call flv(2)%init (-ELECTRON, model)
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
call reset_interaction_counter ()
allocate (gaussian_data_t :: data)
allocate (rng_test_factory_t :: rng_factory)
select type (data)
type is (gaussian_data_t)
call data%init (model, pdg_in, [1e-2_default, 2e-2_default], rng_factory)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1,2])
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 250
k1 = vector4_moving (E, sqrt (E**2 - flv(1)%get_mass ()**2), 3)
k2 = vector4_moving (E,-sqrt (E**2 - flv(2)%get_mass ()**2), 3)
call vector4_write (k1, u)
call vector4_write (k2, u)
call sf_int%seed_kinematics ([k1, k2])
write (u, "(A)")
write (u, "(A)") "* Set dummy parameters and generate x."
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0
rb = 0
x_free = 1
call sf_int%generate_free (r, rb, x_free)
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call pacify (rb, 1.e-8_default)
call pacify (xb, 1.e-8_default)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A,9(1x,F10.7))") "xf=", x_free
write (u, "(A)")
write (u, "(A)") "* Evaluate"
write (u, "(A)")
call sf_int%apply (scale = 0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Generate more events"
write (u, "(A)")
select type (sf_int)
type is (gaussian_t)
do i = 1, 3
call sf_int%generate_free (r, rb, x_free)
write (u, "(A,9(1x,F10.7))") "r =", r
end do
end select
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_gaussian_2"
end subroutine sf_gaussian_2
@ %def sf_gaussian_2
@
\clearpage
@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Using beam event data}
Instead of an analytic beam description, beam data may be provided in
form of an event file. In its most simple form, the event file
contains pairs of $x$ values, relative to nominal beam energies. More
advanced formats may include polarization, etc. The current
implementation carries beam polarization through, if specified.
The code is very similar to the energy scan described above.
However, we must include a file-handle manager for the beam-event
files. Two different processes may access a given beam-event file at
the same time (i.e., serially but alternating). Accessing an open
file from two different units is non-standard and not supported by all
compilers. Therefore, we keep a global registry of open files,
associated units, and reference counts. The [[beam_events_t]] objects
act as proxies to this registry.
<<[[sf_beam_events.f90]]>>=
<<File header>>
module sf_beam_events
<<Use kinds>>
<<Use strings>>
use io_units
use file_registries
use diagnostics
use lorentz
use pdg_arrays
use model_data
use flavors
use quantum_numbers
use state_matrices
use polarizations
use sf_base
<<Standard module head>>
<<SF beam events: public>>
<<SF beam events: types>>
<<SF beam events: variables>>
contains
<<SF beam events: procedures>>
end module sf_beam_events
@ %def sf_beam_events
@
\subsection{The beam-data file registry}
We manage data files via the [[file_registries]] module. To this end,
we keep the registry as a private module variable here.
This is public only for the unit tests.
<<SF beam events: public>>=
public :: beam_file_registry
<<SF beam events: variables>>=
type(file_registry_t), save :: beam_file_registry
@ %def beam_file_registry
@
\subsection{Data type}
<<SF beam events: public>>=
public :: beam_events_data_t
<<SF beam events: types>>=
type, extends(sf_data_t) :: beam_events_data_t
private
type(flavor_t), dimension(2) :: flv_in
type(string_t) :: dir
type(string_t) :: file
type(string_t) :: fqn
integer :: unit = 0
logical :: warn_eof = .true.
contains
<<SF beam events: beam events data: TBP>>
end type beam_events_data_t
@ %def beam_events_data_t
<<SF beam events: beam events data: TBP>>=
procedure :: init => beam_events_data_init
<<SF beam events: procedures>>=
subroutine beam_events_data_init (data, model, pdg_in, dir, file, warn_eof)
class(beam_events_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), dimension(2), intent(in) :: pdg_in
type(string_t), intent(in) :: dir
type(string_t), intent(in) :: file
logical, intent(in), optional :: warn_eof
if (any (pdg_array_get_length (pdg_in) /= 1)) then
call msg_fatal ("Beam events: incoming beam particles must be unique")
end if
call data%flv_in(1)%init (pdg_array_get (pdg_in(1), 1), model)
call data%flv_in(2)%init (pdg_array_get (pdg_in(2), 1), model)
data%dir = dir
data%file = file
if (present (warn_eof)) data%warn_eof = warn_eof
end subroutine beam_events_data_init
@ %def beam_events_data_init
@ Return true since this spectrum is always in generator mode.
<<SF beam events: beam events data: TBP>>=
procedure :: is_generator => beam_events_data_is_generator
<<SF beam events: procedures>>=
function beam_events_data_is_generator (data) result (flag)
class(beam_events_data_t), intent(in) :: data
logical :: flag
flag = .true.
end function beam_events_data_is_generator
@ %def beam_events_data_is_generator
@ The number of parameters is two. They are free parameters.
<<SF beam events: beam events data: TBP>>=
procedure :: get_n_par => beam_events_data_get_n_par
<<SF beam events: procedures>>=
function beam_events_data_get_n_par (data) result (n)
class(beam_events_data_t), intent(in) :: data
integer :: n
n = 2
end function beam_events_data_get_n_par
@ %def beam_events_data_get_n_par
<<SF beam events: beam events data: TBP>>=
procedure :: get_pdg_out => beam_events_data_get_pdg_out
<<SF beam events: procedures>>=
subroutine beam_events_data_get_pdg_out (data, pdg_out)
class(beam_events_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
integer :: i, n
n = 2
do i = 1, n
pdg_out(i) = data%flv_in(i)%get_pdg ()
end do
end subroutine beam_events_data_get_pdg_out
@ %def beam_events_data_get_pdg_out
@ Allocate the interaction record.
<<SF beam events: beam events data: TBP>>=
procedure :: allocate_sf_int => beam_events_data_allocate_sf_int
<<SF beam events: procedures>>=
subroutine beam_events_data_allocate_sf_int (data, sf_int)
class(beam_events_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (beam_events_t :: sf_int)
end subroutine beam_events_data_allocate_sf_int
@ %def beam_events_data_allocate_sf_int
@ Output
<<SF beam events: beam events data: TBP>>=
procedure :: write => beam_events_data_write
<<SF beam events: procedures>>=
subroutine beam_events_data_write (data, unit, verbose)
class(beam_events_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "Beam-event file data:"
write (u, "(3x,A,A,A,A)") "prt_in = ", &
char (data%flv_in(1)%get_name ()), &
", ", char (data%flv_in(2)%get_name ())
write (u, "(3x,A,A,A)") "file = '", char (data%file), "'"
write (u, "(3x,A,I0)") "unit = ", data%unit
write (u, "(3x,A,L1)") "warn = ", data%warn_eof
end subroutine beam_events_data_write
@ %def beam_events_data_write
@ The data file needs to be opened and closed explicitly. The
open/close message is communicated to the file handle registry, which
does the actual work.
We determine first whether to look in the local directory or in the
given system directory.
<<SF beam events: beam events data: TBP>>=
procedure :: open => beam_events_data_open
procedure :: close => beam_events_data_close
<<SF beam events: procedures>>=
subroutine beam_events_data_open (data)
class(beam_events_data_t), intent(inout) :: data
logical :: exist
if (data%unit == 0) then
data%fqn = data%file
if (data%fqn == "") &
call msg_fatal ("Beam events: $beam_events_file is not set")
inquire (file = char (data%fqn), exist = exist)
if (.not. exist) then
data%fqn = data%dir // "/" // data%file
inquire (file = char (data%fqn), exist = exist)
if (.not. exist) then
data%fqn = ""
call msg_fatal ("Beam events: file '" &
// char (data%file) // "' not found")
return
end if
end if
call msg_message ("Beam events: reading from file '" &
// char (data%file) // "'")
call beam_file_registry%open (data%fqn, data%unit)
else
call msg_bug ("Beam events: file '" &
// char (data%file) // "' is already open")
end if
end subroutine beam_events_data_open
subroutine beam_events_data_close (data)
class(beam_events_data_t), intent(inout) :: data
if (data%unit /= 0) then
call beam_file_registry%close (data%fqn)
call msg_message ("Beam events: closed file '" &
// char (data%file) // "'")
data%unit = 0
end if
end subroutine beam_events_data_close
@ %def beam_events_data_close
@ Return the beam event file.
<<SF beam events: beam events data: TBP>>=
procedure :: get_beam_file => beam_events_data_get_beam_file
<<SF beam events: procedures>>=
function beam_events_data_get_beam_file (data) result (file)
class(beam_events_data_t), intent(in) :: data
type(string_t) :: file
file = "Beam events: " // data%file
end function beam_events_data_get_beam_file
@ %def beam_events_data_get_beam_file
@
\subsection{The beam events object}
Flavor and polarization carried through, no radiated particles.
<<SF beam events: public>>=
public :: beam_events_t
<<SF beam events: types>>=
type, extends (sf_int_t) :: beam_events_t
type(beam_events_data_t), pointer :: data => null ()
integer :: count = 0
contains
<<SF beam events: beam events: TBP>>
end type beam_events_t
@ %def beam_events_t
@ Type string: show beam events file.
<<SF beam events: beam events: TBP>>=
procedure :: type_string => beam_events_type_string
<<SF beam events: procedures>>=
function beam_events_type_string (object) result (string)
class(beam_events_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "Beam events: " // object%data%file
else
string = "Beam events: [undefined]"
end if
end function beam_events_type_string
@ %def beam_events_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF beam events: beam events: TBP>>=
procedure :: write => beam_events_write
<<SF beam events: procedures>>=
subroutine beam_events_write (object, unit, testflag)
class(beam_events_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "Beam events data: [undefined]"
end if
end subroutine beam_events_write
@ %def beam_events_write
@
<<SF beam events: beam events: TBP>>=
procedure :: init => beam_events_init
<<SF beam events: procedures>>=
subroutine beam_events_init (sf_int, data)
class(beam_events_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
real(default), dimension(2) :: m2
real(default), dimension(0) :: mr2
type(quantum_numbers_mask_t), dimension(4) :: mask
integer, dimension(4) :: hel_lock
type(quantum_numbers_t), dimension(4) :: qn_fc, qn_hel, qn
type(polarization_t), target :: pol1, pol2
type(polarization_iterator_t) :: it_hel1, it_hel2
integer :: i
select type (data)
type is (beam_events_data_t)
m2 = data%flv_in%get_mass () ** 2
hel_lock = [3, 4, 1, 2]
mask = quantum_numbers_mask (.false., .false., .false.)
call sf_int%base_init (mask, m2, mr2, m2, hel_lock = hel_lock)
sf_int%data => data
do i = 1, 2
call qn_fc(i)%init ( &
flv = data%flv_in(i), &
col = color_from_flavor (data%flv_in(i)))
call qn_fc(i+2)%init ( &
flv = data%flv_in(i), &
col = color_from_flavor (data%flv_in(i)))
end do
call pol1%init_generic (data%flv_in(1))
call it_hel1%init (pol1)
do while (it_hel1%is_valid ())
qn_hel(1) = it_hel1%get_quantum_numbers ()
qn_hel(3) = it_hel1%get_quantum_numbers ()
call pol2%init_generic (data%flv_in(2))
call it_hel2%init (pol2)
do while (it_hel2%is_valid ())
qn_hel(2) = it_hel2%get_quantum_numbers ()
qn_hel(4) = it_hel2%get_quantum_numbers ()
qn = qn_hel .merge. qn_fc
call sf_int%add_state (qn)
call it_hel2%advance ()
end do
! call pol2%final ()
call it_hel1%advance ()
end do
! call pol1%final ()
call sf_int%freeze ()
call sf_int%set_incoming ([1,2])
call sf_int%set_outgoing ([3,4])
call sf_int%data%open ()
sf_int%status = SF_INITIAL
end select
end subroutine beam_events_init
@ %def beam_events_init
@ This spectrum type needs a finalizer, which closes the data file.
<<SF beam events: beam events: TBP>>=
procedure :: final => sf_beam_events_final
<<SF beam events: procedures>>=
subroutine sf_beam_events_final (object)
class(beam_events_t), intent(inout) :: object
call object%data%close ()
call object%interaction_t%final ()
end subroutine sf_beam_events_final
@ %def sf_beam_events_final
@
\subsection{Kinematics}
Refer to the [[data]] component.
<<SF beam events: beam events: TBP>>=
procedure :: is_generator => beam_events_is_generator
<<SF beam events: procedures>>=
function beam_events_is_generator (sf_int) result (flag)
class(beam_events_t), intent(in) :: sf_int
logical :: flag
flag = sf_int%data%is_generator ()
end function beam_events_is_generator
@ %def beam_events_is_generator
@ Generate free parameters. We read them from file.
<<SF beam events: beam events: TBP>>=
procedure :: generate_free => beam_events_generate_free
<<SF beam events: procedures>>=
recursive subroutine beam_events_generate_free (sf_int, r, rb, x_free)
class(beam_events_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(inout) :: x_free
integer :: iostat
associate (data => sf_int%data)
if (data%unit /= 0) then
read (data%unit, fmt=*, iostat=iostat) r
if (iostat > 0) then
write (msg_buffer, "(A,I0,A)") &
"Beam events: I/O error after reading ", sf_int%count, &
" events"
call msg_fatal ()
else if (iostat < 0) then
if (sf_int%count == 0) then
call msg_fatal ("Beam events: file is empty")
else if (sf_int%data%warn_eof) then
write (msg_buffer, "(A,I0,A)") &
"Beam events: End of file after reading ", sf_int%count, &
" events, rewinding"
call msg_warning ()
end if
rewind (data%unit)
sf_int%count = 0
call sf_int%generate_free (r, rb, x_free)
else
sf_int%count = sf_int%count + 1
rb = 1 - r
x_free = x_free * product (r)
end if
else
call msg_bug ("Beam events: file is not open for reading")
end if
end associate
end subroutine beam_events_generate_free
@ %def beam_events_generate_free
@ Set kinematics. Trivial transfer since this is a pure generator.
The [[map]] flag doesn't apply.
<<SF beam events: beam events: TBP>>=
procedure :: complete_kinematics => beam_events_complete_kinematics
<<SF beam events: procedures>>=
subroutine beam_events_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(beam_events_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
if (map) then
call msg_fatal ("Beam events: map flag not supported")
else
x = r
xb= rb
f = 1
end if
call sf_int%reduce_momenta (x)
end subroutine beam_events_complete_kinematics
@ %def beam_events_complete_kinematics
@ Compute inverse kinematics. Trivial in this case.
<<SF beam events: beam events: TBP>>=
procedure :: inverse_kinematics => beam_events_inverse_kinematics
<<SF beam events: procedures>>=
subroutine beam_events_inverse_kinematics &
(sf_int, x, xb, f, r, rb, map, set_momenta)
class(beam_events_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
call msg_fatal ("Beam events: map flag not supported")
else
r = x
rb= xb
f = 1
end if
if (set_mom) then
call sf_int%reduce_momenta (x)
end if
end subroutine beam_events_inverse_kinematics
@ %def beam_events_inverse_kinematics
@
\subsection{Beam events application}
Trivial, just set the unit weight.
<<SF beam events: beam events: TBP>>=
procedure :: apply => beam_events_apply
<<SF beam events: procedures>>=
- subroutine beam_events_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine beam_events_apply (sf_int, scale, rescale, i_sub)
class(beam_events_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default) :: f
f = 1
call sf_int%set_matrix_element (cmplx (f, kind=default))
sf_int%status = SF_EVALUATED
end subroutine beam_events_apply
@ %def beam_events_apply
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_beam_events_ut.f90]]>>=
<<File header>>
module sf_beam_events_ut
use unit_tests
use sf_beam_events_uti
<<Standard module head>>
<<SF beam events: public test>>
contains
<<SF beam events: test driver>>
end module sf_beam_events_ut
@ %def sf_beam_events_ut
@
<<[[sf_beam_events_uti.f90]]>>=
<<File header>>
module sf_beam_events_uti
<<Use kinds>>
<<Use strings>>
use io_units
use physics_defs, only: ELECTRON
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use model_data
use sf_aux
use sf_base
use sf_beam_events
<<Standard module head>>
<<SF beam events: test declarations>>
contains
<<SF beam events: tests>>
end module sf_beam_events_uti
@ %def sf_beam_events_ut
@ API: driver for the unit tests below.
<<SF beam events: public test>>=
public :: sf_beam_events_test
<<SF beam events: test driver>>=
subroutine sf_beam_events_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF beam events: execute tests>>
end subroutine sf_beam_events_test
@ %def sf_beam_events_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF beam events: execute tests>>=
call test (sf_beam_events_1, "sf_beam_events_1", &
"structure function configuration", &
u, results)
<<SF beam events: test declarations>>=
public :: sf_beam_events_1
<<SF beam events: tests>>=
subroutine sf_beam_events_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t), dimension(2) :: pdg_in
type(pdg_array_t), dimension(2) :: pdg_out
integer, dimension(:), allocatable :: pdg1, pdg2
class(sf_data_t), allocatable :: data
write (u, "(A)") "* Test output: sf_beam_events_1"
write (u, "(A)") "* Purpose: initialize and display &
&beam-events structure function data"
write (u, "(A)")
call model%init_qed_test ()
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
allocate (beam_events_data_t :: data)
select type (data)
type is (beam_events_data_t)
call data%init (model, pdg_in, var_str (""), var_str ("beam_events.dat"))
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
pdg2 = pdg_out(2)
write (u, "(2x,99(1x,I0))") pdg1, pdg2
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_beam_events_1"
end subroutine sf_beam_events_1
@ %def sf_beam_events_1
@
\subsubsection{Probe the structure-function object}
Active the beam event reader, generate an event.
<<SF beam events: execute tests>>=
call test (sf_beam_events_2, "sf_beam_events_2", &
"generate event", &
u, results)
<<SF beam events: test declarations>>=
public :: sf_beam_events_2
<<SF beam events: tests>>=
subroutine sf_beam_events_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t), dimension(2) :: flv
type(pdg_array_t), dimension(2) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k1, k2
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: x_free, f
integer :: i
write (u, "(A)") "* Test output: sf_beam_events_2"
write (u, "(A)") "* Purpose: initialize and display &
&beam-events structure function data"
write (u, "(A)")
call model%init_qed_test ()
call flv(1)%init (ELECTRON, model)
call flv(2)%init (-ELECTRON, model)
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
call reset_interaction_counter ()
allocate (beam_events_data_t :: data)
select type (data)
type is (beam_events_data_t)
call data%init (model, pdg_in, &
var_str (""), var_str ("test_beam_events.dat"))
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1,2])
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 250
k1 = vector4_moving (E, sqrt (E**2 - flv(1)%get_mass ()**2), 3)
k2 = vector4_moving (E,-sqrt (E**2 - flv(2)%get_mass ()**2), 3)
call vector4_write (k1, u)
call vector4_write (k2, u)
call sf_int%seed_kinematics ([k1, k2])
write (u, "(A)")
write (u, "(A)") "* Set dummy parameters and generate x."
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0
rb = 0
x_free = 1
call sf_int%generate_free (r, rb, x_free)
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,9(1x,F10.7))") "rb=", rb
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A,9(1x,F10.7))") "xf=", x_free
select type (sf_int)
type is (beam_events_t)
write (u, "(A,1x,I0)") "count =", sf_int%count
end select
write (u, "(A)")
write (u, "(A)") "* Evaluate"
write (u, "(A)")
call sf_int%apply (scale = 0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Generate more events, rewind"
write (u, "(A)")
select type (sf_int)
type is (beam_events_t)
do i = 1, 3
call sf_int%generate_free (r, rb, x_free)
write (u, "(A,9(1x,F10.7))") "r =", r
write (u, "(A,1x,I0)") "count =", sf_int%count
end do
end select
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_beam_events_2"
end subroutine sf_beam_events_2
@ %def sf_beam_events_2
@
\subsubsection{Check the file handle registry}
Open and close some files, checking the registry contents.
<<SF beam events: execute tests>>=
call test (sf_beam_events_3, "sf_beam_events_3", &
"check registry", &
u, results)
<<SF beam events: test declarations>>=
public :: sf_beam_events_3
<<SF beam events: tests>>=
subroutine sf_beam_events_3 (u)
integer, intent(in) :: u
integer :: u1
write (u, "(A)") "* Test output: sf_beam_events_2"
write (u, "(A)") "* Purpose: check file handle registry"
write (u, "(A)")
write (u, "(A)") "* Create some empty files"
write (u, "(A)")
u1 = free_unit ()
open (u1, file = "sf_beam_events_f1.tmp", action="write", status="new")
close (u1)
open (u1, file = "sf_beam_events_f2.tmp", action="write", status="new")
close (u1)
open (u1, file = "sf_beam_events_f3.tmp", action="write", status="new")
close (u1)
write (u, "(A)") "* Empty registry"
write (u, "(A)")
call beam_file_registry%write (u)
write (u, "(A)")
write (u, "(A)") "* Insert three entries"
write (u, "(A)")
call beam_file_registry%open (var_str ("sf_beam_events_f3.tmp"))
call beam_file_registry%open (var_str ("sf_beam_events_f2.tmp"))
call beam_file_registry%open (var_str ("sf_beam_events_f1.tmp"))
call beam_file_registry%write (u)
write (u, "(A)")
write (u, "(A)") "* Open a second channel"
write (u, "(A)")
call beam_file_registry%open (var_str ("sf_beam_events_f2.tmp"))
call beam_file_registry%write (u)
write (u, "(A)")
write (u, "(A)") "* Close second entry twice"
write (u, "(A)")
call beam_file_registry%close (var_str ("sf_beam_events_f2.tmp"))
call beam_file_registry%close (var_str ("sf_beam_events_f2.tmp"))
call beam_file_registry%write (u)
write (u, "(A)")
write (u, "(A)") "* Close last entry"
write (u, "(A)")
call beam_file_registry%close (var_str ("sf_beam_events_f3.tmp"))
call beam_file_registry%write (u)
write (u, "(A)")
write (u, "(A)") "* Close remaining entry"
write (u, "(A)")
call beam_file_registry%close (var_str ("sf_beam_events_f1.tmp"))
call beam_file_registry%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
open (u1, file = "sf_beam_events_f1.tmp", action="write")
close (u1, status = "delete")
open (u1, file = "sf_beam_events_f2.tmp", action="write")
close (u1, status = "delete")
open (u1, file = "sf_beam_events_f3.tmp", action="write")
close (u1, status = "delete")
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_beam_events_3"
end subroutine sf_beam_events_3
@ %def sf_beam_events_3
@
\clearpage
%------------------------------------------------------------------------
\section{Lepton collider beamstrahlung: CIRCE1}
<<[[sf_circe1.f90]]>>=
<<File header>>
module sf_circe1
<<Use kinds>>
use kinds, only: double
<<Use strings>>
use io_units
use format_defs, only: FMT_17, FMT_19
use diagnostics
use physics_defs, only: ELECTRON, PHOTON
use lorentz
use rng_base
use pdg_arrays
use model_data
use flavors
use colors
use quantum_numbers
use state_matrices
use polarizations
use sf_mappings
use sf_base
use circe1, circe1_rng_t => rng_type !NODEP!
<<Standard module head>>
<<SF circe1: public>>
<<SF circe1: types>>
contains
<<SF circe1: procedures>>
end module sf_circe1
@ %def sf_circe1
@
\subsection{Physics}
Beamstrahlung is applied before ISR. The [[CIRCE1]] implementation has
a single structure function for both beams (which makes sense since it
has to be switched on or off for both beams simultaneously).
Nevertheless it is factorized:
The functional form in the [[CIRCE1]] parameterization is defined for
electrons or photons
\begin{equation}
f(x) = \alpha\,x^\beta\,(1-x)^\gamma
\end{equation}
for $x<1-\epsilon$ (resp.\ $x>\epsilon$ in the photon case). In the
remaining interval, the standard form is zero, with a delta
singularity at $x=1$ (resp.\ $x=0$). Equivalently, the delta part may be
distributed uniformly among this interval. This latter form is
implemented in the [[kirke]] version of the [[CIRCE1]] subroutines, and
is used here.
The parameter [[circe1\_eps]] sets the peak mapping of the [[CIRCE1]]
structure function. Its default value is $10^{-5}$.
The other parameters are the parameterization version and revision
number, the accelerator type, and the $\sqrt{s}$ value used by
[[CIRCE1]]. The chattiness can also be set.
Since the energy is distributed in a narrow region around unity (for
electrons) or zero (for photons), it is advantageous to map the
interval first. The mapping is controlled by the parameter
[[circe1\_epsilon]] which is taken from the [[CIRCE1]]
internal data structure.
The $\sqrt{s}$ value, if not explicitly set, is taken from the
process data. Note that interpolating $\sqrt{s}$ is not recommended;
one should rather choose one of the distinct values known to [[CIRCE1]].
\subsection{The CIRCE1 data block}
The CIRCE1 parameters are: The incoming flavors, the flags whether the photon
or the lepton is the parton in the hard interaction, the flags for the
generation mode (generator/mapping/no mapping), the mapping parameter
$\epsilon$, $\sqrt{s}$ and several steering parameters: [[ver]],
[[rev]], [[acc]], [[chat]].
In generator mode, the $x$ values are actually discarded and a random number
generator is used instead.
<<SF circe1: public>>=
public :: circe1_data_t
<<SF circe1: types>>=
type, extends (sf_data_t) :: circe1_data_t
private
class(model_data_t), pointer :: model => null ()
type(flavor_t), dimension(2) :: flv_in
integer, dimension(2) :: pdg_in
real(default), dimension(2) :: m_in = 0
logical, dimension(2) :: photon = .false.
logical :: generate = .false.
class(rng_factory_t), allocatable :: rng_factory
real(default) :: sqrts = 0
real(default) :: eps = 0
integer :: ver = 0
integer :: rev = 0
character(6) :: acc = "?"
integer :: chat = 0
logical :: with_radiation = .false.
contains
<<SF circe1: circe1 data: TBP>>
end type circe1_data_t
@ %def circe1_data_t
@
<<SF circe1: circe1 data: TBP>>=
procedure :: init => circe1_data_init
<<SF circe1: procedures>>=
subroutine circe1_data_init &
(data, model, pdg_in, sqrts, eps, out_photon, &
ver, rev, acc, chat, with_radiation)
class(circe1_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), dimension(2), intent(in) :: pdg_in
real(default), intent(in) :: sqrts
real(default), intent(in) :: eps
logical, dimension(2), intent(in) :: out_photon
character(*), intent(in) :: acc
integer, intent(in) :: ver, rev, chat
logical, intent(in) :: with_radiation
data%model => model
if (any (pdg_array_get_length (pdg_in) /= 1)) then
call msg_fatal ("CIRCE1: incoming beam particles must be unique")
end if
call data%flv_in(1)%init (pdg_array_get (pdg_in(1), 1), model)
call data%flv_in(2)%init (pdg_array_get (pdg_in(2), 1), model)
data%pdg_in = data%flv_in%get_pdg ()
data%m_in = data%flv_in%get_mass ()
data%sqrts = sqrts
data%eps = eps
data%photon = out_photon
data%ver = ver
data%rev = rev
data%acc = acc
data%chat = chat
data%with_radiation = with_radiation
call data%check ()
call circex (0.d0, 0.d0, dble (data%sqrts), &
data%acc, data%ver, data%rev, data%chat)
end subroutine circe1_data_init
@ %def circe1_data_init
@ Activate the generator mode. We import a RNG factory into the data
type, which can then spawn RNG generator objects.
<<SF circe1: circe1 data: TBP>>=
procedure :: set_generator_mode => circe1_data_set_generator_mode
<<SF circe1: procedures>>=
subroutine circe1_data_set_generator_mode (data, rng_factory)
class(circe1_data_t), intent(inout) :: data
class(rng_factory_t), intent(inout), allocatable :: rng_factory
data%generate = .true.
call move_alloc (from = rng_factory, to = data%rng_factory)
end subroutine circe1_data_set_generator_mode
@ %def circe1_data_set_generator_mode
@ Handle error conditions.
<<SF circe1: circe1 data: TBP>>=
procedure :: check => circe1_data_check
<<SF circe1: procedures>>=
subroutine circe1_data_check (data)
class(circe1_data_t), intent(in) :: data
type(flavor_t) :: flv_electron, flv_photon
call flv_electron%init (ELECTRON, data%model)
call flv_photon%init (PHOTON, data%model)
if (.not. flv_electron%is_defined () &
.or. .not. flv_photon%is_defined ()) then
call msg_fatal ("CIRCE1: model must contain photon and electron")
end if
if (any (abs (data%pdg_in) /= ELECTRON) &
.or. (data%pdg_in(1) /= - data%pdg_in(2))) then
call msg_fatal ("CIRCE1: applicable only for e+e- or e-e+ collisions")
end if
if (data%eps <= 0) then
call msg_error ("CIRCE1: circe1_eps = 0: integration will &
&miss x=1 peak")
end if
end subroutine circe1_data_check
@ %def circe1_data_check
@ Output
<<SF circe1: circe1 data: TBP>>=
procedure :: write => circe1_data_write
<<SF circe1: procedures>>=
subroutine circe1_data_write (data, unit, verbose)
class(circe1_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "CIRCE1 data:"
write (u, "(3x,A,2(1x,A))") "prt_in =", &
char (data%flv_in(1)%get_name ()), &
char (data%flv_in(2)%get_name ())
write (u, "(3x,A,2(1x,L1))") "photon =", data%photon
write (u, "(3x,A,L1)") "generate = ", data%generate
write (u, "(3x,A,2(1x," // FMT_19 // "))") "m_in =", data%m_in
write (u, "(3x,A," // FMT_19 // ")") "sqrts = ", data%sqrts
write (u, "(3x,A," // FMT_19 // ")") "eps = ", data%eps
write (u, "(3x,A,I0)") "ver = ", data%ver
write (u, "(3x,A,I0)") "rev = ", data%rev
write (u, "(3x,A,A)") "acc = ", data%acc
write (u, "(3x,A,I0)") "chat = ", data%chat
write (u, "(3x,A,L1)") "with rad.= ", data%with_radiation
if (data%generate) call data%rng_factory%write (u)
end subroutine circe1_data_write
@ %def circe1_data_write
@ Return true if this structure function is in generator mode. In
that case, all parameters are free, otherwise bound. (We do not
support mixed cases.) Default is: no generator.
<<SF circe1: circe1 data: TBP>>=
procedure :: is_generator => circe1_data_is_generator
<<SF circe1: procedures>>=
function circe1_data_is_generator (data) result (flag)
class(circe1_data_t), intent(in) :: data
logical :: flag
flag = data%generate
end function circe1_data_is_generator
@ %def circe1_data_is_generator
@ The number of parameters is two, collinear splitting for the two beams.
<<SF circe1: circe1 data: TBP>>=
procedure :: get_n_par => circe1_data_get_n_par
<<SF circe1: procedures>>=
function circe1_data_get_n_par (data) result (n)
class(circe1_data_t), intent(in) :: data
integer :: n
n = 2
end function circe1_data_get_n_par
@ %def circe1_data_get_n_par
@ Return the outgoing particles PDG codes. This is either the incoming
particle (if a photon is radiated), or the photon if that is the particle
of the hard interaction. The latter is determined via the [[photon]]
flag. There are two entries for the two beams.
<<SF circe1: circe1 data: TBP>>=
procedure :: get_pdg_out => circe1_data_get_pdg_out
<<SF circe1: procedures>>=
subroutine circe1_data_get_pdg_out (data, pdg_out)
class(circe1_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
integer :: i, n
n = 2
do i = 1, n
if (data%photon(i)) then
pdg_out(i) = PHOTON
else
pdg_out(i) = data%pdg_in(i)
end if
end do
end subroutine circe1_data_get_pdg_out
@ %def circe1_data_get_pdg_out
@ This variant is not inherited, it returns integers.
<<SF circe1: circe1 data: TBP>>=
procedure :: get_pdg_int => circe1_data_get_pdg_int
<<SF circe1: procedures>>=
function circe1_data_get_pdg_int (data) result (pdg)
class(circe1_data_t), intent(in) :: data
integer, dimension(2) :: pdg
integer :: i
do i = 1, 2
if (data%photon(i)) then
pdg(i) = PHOTON
else
pdg(i) = data%pdg_in(i)
end if
end do
end function circe1_data_get_pdg_int
@ %def circe1_data_get_pdg_int
@ Allocate the interaction record.
<<SF circe1: circe1 data: TBP>>=
procedure :: allocate_sf_int => circe1_data_allocate_sf_int
<<SF circe1: procedures>>=
subroutine circe1_data_allocate_sf_int (data, sf_int)
class(circe1_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (circe1_t :: sf_int)
end subroutine circe1_data_allocate_sf_int
@ %def circe1_data_allocate_sf_int
@ Return the accelerator type.
<<SF circe1: circe1 data: TBP>>=
procedure :: get_beam_file => circe1_data_get_beam_file
<<SF circe1: procedures>>=
function circe1_data_get_beam_file (data) result (file)
class(circe1_data_t), intent(in) :: data
type(string_t) :: file
file = "CIRCE1: " // data%acc
end function circe1_data_get_beam_file
@ %def circe1_data_get_beam_file
@
\subsection{Random Number Generator for CIRCE}
The CIRCE implementation now supports a generic random-number
generator object that allows for a local state as a component. To
support this, we must extend the abstract type provided by CIRCE and
delegate the generator call to the (also abstract) RNG used by WHIZARD.
<<SF circe1: types>>=
type, extends (circe1_rng_t) :: rng_obj_t
class(rng_t), allocatable :: rng
contains
procedure :: generate => rng_obj_generate
end type rng_obj_t
@ %def rng_obj_t
<<SF circe1: procedures>>=
subroutine rng_obj_generate (rng_obj, u)
class(rng_obj_t), intent(inout) :: rng_obj
real(double), intent(out) :: u
real(default) :: x
call rng_obj%rng%generate (x)
u = x
end subroutine rng_obj_generate
@ %def rng_obj_generate
@
\subsection{The CIRCE1 object}
This is a $2\to 4$ interaction, where, depending on the parameters, any two of
the four outgoing particles are connected to the hard interactions, the others
are radiated. Knowing that all particles are colorless, we do not have to
deal with color.
The flavors are sorted such that the first two particles are the incoming
leptons, the next two are the radiated particles, and the last two are the
partons initiating the hard interaction.
CIRCE1 does not support polarized beams explicitly. For simplicity, we
nevertheless carry beam polarization through to the outgoing electrons and
make the photons unpolarized.
In the case that no radiated particle is kept (which actually is the
default), polarization is always transferred to the electrons, too. If
there is a recoil photon in the event, the radiated particles are 3
and 4, respectively, and 5 and 6 are the outgoing ones (triggering the
hard scattering process), while in the case of no radiation, the
outgoing particles are 3 and 4, respectively. In the case of the
electron being the radiated particle, helicity is not kept.
<<SF circe1: public>>=
public :: circe1_t
<<SF circe1: types>>=
type, extends (sf_int_t) :: circe1_t
type(circe1_data_t), pointer :: data => null ()
real(default), dimension(2) :: x = 0
real(default), dimension(2) :: xb= 0
real(default) :: f = 0
logical, dimension(2) :: continuum = .true.
logical, dimension(2) :: peak = .true.
type(rng_obj_t) :: rng_obj
contains
<<SF circe1: circe1: TBP>>
end type circe1_t
@ %def circe1_t
@ Type string: has to be here, but there is no string variable on which CIRCE1
depends. Hence, a dummy routine.
<<SF circe1: circe1: TBP>>=
procedure :: type_string => circe1_type_string
<<SF circe1: procedures>>=
function circe1_type_string (object) result (string)
class(circe1_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "CIRCE1: beamstrahlung"
else
string = "CIRCE1: [undefined]"
end if
end function circe1_type_string
@ %def circe1_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF circe1: circe1: TBP>>=
procedure :: write => circe1_write
<<SF circe1: procedures>>=
subroutine circe1_write (object, unit, testflag)
class(circe1_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
if (object%data%generate) call object%rng_obj%rng%write (u)
if (object%status >= SF_DONE_KINEMATICS) then
write (u, "(3x,A,2(1x," // FMT_17 // "))") "x =", object%x
write (u, "(3x,A,2(1x," // FMT_17 // "))") "xb=", object%xb
if (object%status >= SF_FAILED_EVALUATION) then
write (u, "(3x,A,1x," // FMT_17 // ")") "f =", object%f
end if
end if
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "CIRCE1 data: [undefined]"
end if
end subroutine circe1_write
@ %def circe1_write
@
<<SF circe1: circe1: TBP>>=
procedure :: init => circe1_init
<<SF circe1: procedures>>=
subroutine circe1_init (sf_int, data)
class(circe1_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
logical, dimension(6) :: mask_h
type(quantum_numbers_mask_t), dimension(6) :: mask
integer, dimension(6) :: hel_lock
type(polarization_t), target :: pol1, pol2
type(quantum_numbers_t), dimension(1) :: qn_fc1, qn_fc2
type(flavor_t) :: flv_photon
type(color_t) :: col0
real(default), dimension(2) :: mi2, mr2, mo2
type(quantum_numbers_t) :: qn_hel1, qn_hel2, qn_photon, qn1, qn2
type(quantum_numbers_t), dimension(6) :: qn
type(polarization_iterator_t) :: it_hel1, it_hel2
hel_lock = 0
mask_h = .false.
select type (data)
type is (circe1_data_t)
mi2 = data%m_in**2
if (data%with_radiation) then
if (data%photon(1)) then
hel_lock(1) = 3; hel_lock(3) = 1; mask_h(5) = .true.
mr2(1) = mi2(1)
mo2(1) = 0._default
else
hel_lock(1) = 5; hel_lock(5) = 1; mask_h(3) = .true.
mr2(1) = 0._default
mo2(1) = mi2(1)
end if
if (data%photon(2)) then
hel_lock(2) = 4; hel_lock(4) = 2; mask_h(6) = .true.
mr2(2) = mi2(2)
mo2(2) = 0._default
else
hel_lock(2) = 6; hel_lock(6) = 2; mask_h(4) = .true.
mr2(2) = 0._default
mo2(2) = mi2(2)
end if
mask = quantum_numbers_mask (.false., .false., mask_h)
call sf_int%base_init (mask, mi2, mr2, mo2, &
hel_lock = hel_lock)
sf_int%data => data
call flv_photon%init (PHOTON, data%model)
call col0%init ()
call qn_photon%init (flv_photon, col0)
call pol1%init_generic (data%flv_in(1))
call qn_fc1(1)%init (flv = data%flv_in(1), col = col0)
call pol2%init_generic (data%flv_in(2))
call qn_fc2(1)%init (flv = data%flv_in(2), col = col0)
call it_hel1%init (pol1)
do while (it_hel1%is_valid ())
qn_hel1 = it_hel1%get_quantum_numbers ()
qn1 = qn_hel1 .merge. qn_fc1(1)
qn(1) = qn1
if (data%photon(1)) then
qn(3) = qn1; qn(5) = qn_photon
else
qn(3) = qn_photon; qn(5) = qn1
end if
call it_hel2%init (pol2)
do while (it_hel2%is_valid ())
qn_hel2 = it_hel2%get_quantum_numbers ()
qn2 = qn_hel2 .merge. qn_fc2(1)
qn(2) = qn2
if (data%photon(2)) then
qn(4) = qn2; qn(6) = qn_photon
else
qn(4) = qn_photon; qn(6) = qn2
end if
call qn(3:4)%tag_radiated ()
call sf_int%add_state (qn)
call it_hel2%advance ()
end do
call it_hel1%advance ()
end do
! call pol1%final ()
! call pol2%final ()
call sf_int%freeze ()
call sf_int%set_incoming ([1,2])
call sf_int%set_radiated ([3,4])
call sf_int%set_outgoing ([5,6])
else
if (data%photon(1)) then
mask_h(3) = .true.
mo2(1) = 0._default
else
hel_lock(1) = 3; hel_lock(3) = 1
mo2(1) = mi2(1)
end if
if (data%photon(2)) then
mask_h(4) = .true.
mo2(2) = 0._default
else
hel_lock(2) = 4; hel_lock(4) = 2
mo2(2) = mi2(2)
end if
mask = quantum_numbers_mask (.false., .false., mask_h)
call sf_int%base_init (mask(1:4), mi2, [real(default) :: ], mo2, &
hel_lock = hel_lock(1:4))
sf_int%data => data
call flv_photon%init (PHOTON, data%model)
call col0%init ()
call qn_photon%init (flv_photon, col0)
call pol1%init_generic (data%flv_in(1))
call qn_fc1(1)%init (flv = data%flv_in(1), col = col0)
call pol2%init_generic (data%flv_in(2))
call qn_fc2(1)%init (flv = data%flv_in(2), col = col0)
call it_hel1%init (pol1)
do while (it_hel1%is_valid ())
qn_hel1 = it_hel1%get_quantum_numbers ()
qn1 = qn_hel1 .merge. qn_fc1(1)
qn(1) = qn1
if (data%photon(1)) then
qn(3) = qn_photon
else
qn(3) = qn1
end if
call it_hel2%init (pol2)
do while (it_hel2%is_valid ())
qn_hel2 = it_hel2%get_quantum_numbers ()
qn2 = qn_hel2 .merge. qn_fc2(1)
qn(2) = qn2
if (data%photon(2)) then
qn(4) = qn_photon
else
qn(4) = qn2
end if
call sf_int%add_state (qn(1:4))
call it_hel2%advance ()
end do
call it_hel1%advance ()
end do
! call pol1%final ()
! call pol2%final ()
call sf_int%freeze ()
call sf_int%set_incoming ([1,2])
call sf_int%set_outgoing ([3,4])
end if
sf_int%status = SF_INITIAL
end select
if (sf_int%data%generate) then
call sf_int%data%rng_factory%make (sf_int%rng_obj%rng)
end if
end subroutine circe1_init
@ %def circe1_init
@
\subsection{Kinematics}
Refer to the [[data]] component.
<<SF circe1: circe1: TBP>>=
procedure :: is_generator => circe1_is_generator
<<SF circe1: procedures>>=
function circe1_is_generator (sf_int) result (flag)
class(circe1_t), intent(in) :: sf_int
logical :: flag
flag = sf_int%data%is_generator ()
end function circe1_is_generator
@ %def circe1_is_generator
@ Generate free parameters, if generator mode is on. Otherwise, the
parameters will be discarded.
<<SF circe1: circe1: TBP>>=
procedure :: generate_free => circe1_generate_free
<<SF circe1: procedures>>=
subroutine circe1_generate_free (sf_int, r, rb, x_free)
class(circe1_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(inout) :: x_free
if (sf_int%data%generate) then
call circe_generate (r, sf_int%data%get_pdg_int (), sf_int%rng_obj)
rb = 1 - r
x_free = x_free * product (r)
else
r = 0
rb= 1
end if
end subroutine circe1_generate_free
@ %def circe1_generate_free
@ Generator mode: depending on the particle codes, call one of the
available [[girce]] generators. Illegal particle code combinations
should have been caught during data initialization.
<<SF circe1: procedures>>=
subroutine circe_generate (x, pdg, rng_obj)
real(default), dimension(2), intent(out) :: x
integer, dimension(2), intent(in) :: pdg
class(rng_obj_t), intent(inout) :: rng_obj
real(double) :: xc1, xc2
select case (abs (pdg(1)))
case (ELECTRON)
select case (abs (pdg(2)))
case (ELECTRON)
call gircee (xc1, xc2, rng_obj = rng_obj)
case (PHOTON)
call girceg (xc1, xc2, rng_obj = rng_obj)
end select
case (PHOTON)
select case (abs (pdg(2)))
case (ELECTRON)
call girceg (xc2, xc1, rng_obj = rng_obj)
case (PHOTON)
call gircgg (xc1, xc2, rng_obj = rng_obj)
end select
end select
x = [xc1, xc2]
end subroutine circe_generate
@ %def circe_generate
@ Set kinematics. The $r$ values (either from integration or from the
generator call above) are copied to $x$ unchanged, and $f$ is unity.
We store the $x$ values, so we can use them for the evaluation later.
<<SF circe1: circe1: TBP>>=
procedure :: complete_kinematics => circe1_complete_kinematics
<<SF circe1: procedures>>=
subroutine circe1_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(circe1_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
x = r
xb = rb
sf_int%x = x
sf_int%xb= xb
f = 1
if (sf_int%data%with_radiation) then
call sf_int%split_momenta (x, xb)
else
call sf_int%reduce_momenta (x)
end if
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end subroutine circe1_complete_kinematics
@ %def circe1_complete_kinematics
@ Compute inverse kinematics. In generator mode, the $r$ values are
meaningless, but we copy them anyway.
<<SF circe1: circe1: TBP>>=
procedure :: inverse_kinematics => circe1_inverse_kinematics
<<SF circe1: procedures>>=
subroutine circe1_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(circe1_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
r = x
rb = xb
sf_int%x = x
sf_int%xb= xb
f = 1
if (set_mom) then
call sf_int%split_momenta (x, xb)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end if
end subroutine circe1_inverse_kinematics
@ %def circe1_inverse_kinematics
@
\subsection{CIRCE1 application}
CIRCE is applied for the two beams at once. We can safely assume that no
structure functions are applied before this, so the incoming particles are
on-shell electrons/positrons.
The scale is ignored.
<<SF circe1: circe1: TBP>>=
procedure :: apply => circe1_apply
<<SF circe1: procedures>>=
- subroutine circe1_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine circe1_apply (sf_int, scale, rescale, i_sub)
class(circe1_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default), dimension(2) :: xb
real(double), dimension(2) :: xc
real(double), parameter :: one = 1
associate (data => sf_int%data)
xc = sf_int%x
xb = sf_int%xb
if (data%generate) then
sf_int%f = 1
else
sf_int%f = 0
if (all (sf_int%continuum)) then
sf_int%f = circe (xc(1), xc(2), data%pdg_in(1), data%pdg_in(2))
end if
if (sf_int%continuum(2) .and. sf_int%peak(1)) then
sf_int%f = sf_int%f &
+ circe (one, xc(2), data%pdg_in(1), data%pdg_in(2)) &
* peak (xb(1), data%eps)
end if
if (sf_int%continuum(1) .and. sf_int%peak(2)) then
sf_int%f = sf_int%f &
+ circe (xc(1), one, data%pdg_in(1), data%pdg_in(2)) &
* peak (xb(2), data%eps)
end if
if (all (sf_int%peak)) then
sf_int%f = sf_int%f &
+ circe (one, one, data%pdg_in(1), data%pdg_in(2)) &
* peak (xb(1), data%eps) * peak (xb(2), data%eps)
end if
end if
end associate
call sf_int%set_matrix_element (cmplx (sf_int%f, kind=default))
sf_int%status = SF_EVALUATED
end subroutine circe1_apply
@ %def circe1_apply
@ This is a smeared delta peak at zero, as an endpoint singularity.
We choose an exponentially decreasing function, starting at zero, with
integral (from $0$ to $1$) $1-e^{-1/\epsilon}$. For small $\epsilon$,
this reduces to one.
<<SF circe1: procedures>>=
function peak (x, eps) result (f)
real(default), intent(in) :: x, eps
real(default) :: f
f = exp (-x / eps) / eps
end function peak
@ %def peak
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_circe1_ut.f90]]>>=
<<File header>>
module sf_circe1_ut
use unit_tests
use sf_circe1_uti
<<Standard module head>>
<<SF circe1: public test>>
contains
<<SF circe1: test driver>>
end module sf_circe1_ut
@ %def sf_circe1_ut
@
<<[[sf_circe1_uti.f90]]>>=
<<File header>>
module sf_circe1_uti
<<Use kinds>>
use physics_defs, only: ELECTRON
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use model_data
use rng_base
use sf_aux
use sf_base
use sf_circe1
use rng_base_ut, only: rng_test_factory_t
<<Standard module head>>
<<SF circe1: test declarations>>
contains
<<SF circe1: tests>>
end module sf_circe1_uti
@ %def sf_circe1_ut
@ API: driver for the unit tests below.
<<SF circe1: public test>>=
public :: sf_circe1_test
<<SF circe1: test driver>>=
subroutine sf_circe1_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF circe1: execute tests>>
end subroutine sf_circe1_test
@ %def sf_circe1_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF circe1: execute tests>>=
call test (sf_circe1_1, "sf_circe1_1", &
"structure function configuration", &
u, results)
<<SF circe1: test declarations>>=
public :: sf_circe1_1
<<SF circe1: tests>>=
subroutine sf_circe1_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t), dimension(2) :: pdg_in
type(pdg_array_t), dimension(2) :: pdg_out
integer, dimension(:), allocatable :: pdg1, pdg2
class(sf_data_t), allocatable :: data
write (u, "(A)") "* Test output: sf_circe1_1"
write (u, "(A)") "* Purpose: initialize and display &
&CIRCE structure function data"
write (u, "(A)")
write (u, "(A)") "* Create empty data object"
write (u, "(A)")
call model%init_qed_test ()
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
allocate (circe1_data_t :: data)
call data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize"
write (u, "(A)")
select type (data)
type is (circe1_data_t)
call data%init (model, pdg_in, &
sqrts = 500._default, &
eps = 1e-6_default, &
out_photon = [.false., .false.], &
ver = 0, &
rev = 0, &
acc = "SBAND", &
chat = 0, &
with_radiation = .true.)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
pdg2 = pdg_out(2)
write (u, "(2x,99(1x,I0))") pdg1, pdg2
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_circe1_1"
end subroutine sf_circe1_1
@ %def sf_circe1_1
@
\subsubsection{Test and probe structure function}
Construct and display a structure function object based on the PDF builtin
structure function.
<<SF circe1: execute tests>>=
call test (sf_circe1_2, "sf_circe1_2", &
"structure function instance", &
u, results)
<<SF circe1: test declarations>>=
public :: sf_circe1_2
<<SF circe1: tests>>=
subroutine sf_circe1_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t), dimension(2) :: flv
type(pdg_array_t), dimension(2) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k1, k2
type(vector4_t), dimension(4) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_circe1_2"
write (u, "(A)") "* Purpose: initialize and fill &
&circe1 structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
call flv(1)%init (ELECTRON, model)
call flv(2)%init (-ELECTRON, model)
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
call reset_interaction_counter ()
allocate (circe1_data_t :: data)
select type (data)
type is (circe1_data_t)
call data%init (model, pdg_in, &
sqrts = 500._default, &
eps = 1e-6_default, &
out_photon = [.false., .false.], &
ver = 0, &
rev = 0, &
acc = "SBAND", &
chat = 0, &
with_radiation = .true.)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1,2])
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 250
k1 = vector4_moving (E, sqrt (E**2 - flv(1)%get_mass ()**2), 3)
k2 = vector4_moving (E,-sqrt (E**2 - flv(2)%get_mass ()**2), 3)
call vector4_write (k1, u)
call vector4_write (k2, u)
call sf_int%seed_kinematics ([k1, k2])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.95,0.85."
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = [0.9_default, 0.8_default]
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1, 2])
call sf_int%seed_kinematics ([k1, k2])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A)")
write (u, "(A)") "* Evaluate"
write (u, "(A)")
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%apply (scale = 0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_circe1_2"
end subroutine sf_circe1_2
@ %def sf_circe1_2
@
\subsubsection{Generator mode}
Construct and evaluate a structure function object in generator mode.
<<SF circe1: execute tests>>=
call test (sf_circe1_3, "sf_circe1_3", &
"generator mode", &
u, results)
<<SF circe1: test declarations>>=
public :: sf_circe1_3
<<SF circe1: tests>>=
subroutine sf_circe1_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t), dimension(2) :: flv
type(pdg_array_t), dimension(2) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(rng_factory_t), allocatable :: rng_factory
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k1, k2
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f, x_free
write (u, "(A)") "* Test output: sf_circe1_3"
write (u, "(A)") "* Purpose: initialize and fill &
&circe1 structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_qed_test ()
call flv(1)%init (ELECTRON, model)
call flv(2)%init (-ELECTRON, model)
pdg_in(1) = ELECTRON
pdg_in(2) = -ELECTRON
call reset_interaction_counter ()
allocate (circe1_data_t :: data)
allocate (rng_test_factory_t :: rng_factory)
select type (data)
type is (circe1_data_t)
call data%init (model, pdg_in, &
sqrts = 500._default, &
eps = 1e-6_default, &
out_photon = [.false., .false.], &
ver = 0, &
rev = 0, &
acc = "SBAND", &
chat = 0, &
with_radiation = .true.)
call data%set_generator_mode (rng_factory)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1,2])
select type (sf_int)
type is (circe1_t)
call sf_int%rng_obj%rng%init (3)
end select
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 250
k1 = vector4_moving (E, sqrt (E**2 - flv(1)%get_mass ()**2), 3)
k2 = vector4_moving (E,-sqrt (E**2 - flv(2)%get_mass ()**2), 3)
call vector4_write (k1, u)
call vector4_write (k2, u)
call sf_int%seed_kinematics ([k1, k2])
write (u, "(A)")
write (u, "(A)") "* Generate x"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0
rb = 0
x_free = 1
call sf_int%generate_free (r, rb, x_free)
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A,9(1x,F10.7))") "xf=", x_free
write (u, "(A)")
write (u, "(A)") "* Evaluate"
write (u, "(A)")
call sf_int%apply (scale = 0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_circe1_3"
end subroutine sf_circe1_3
@ %def sf_circe1_3
@
\clearpage
%------------------------------------------------------------------------
\section{Lepton Collider Beamstrahlung and Photon collider: CIRCE2}
<<[[sf_circe2.f90]]>>=
<<File header>>
module sf_circe2
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_19
use numeric_utils
use diagnostics
use os_interface
use physics_defs, only: PHOTON, ELECTRON
use lorentz
use rng_base
use selectors
use pdg_arrays
use model_data
use flavors
use colors
use helicities
use quantum_numbers
use state_matrices
use polarizations
use sf_base
use circe2, circe2_rng_t => rng_type !NODEP!
<<Standard module head>>
<<SF circe2: public>>
<<SF circe2: types>>
contains
<<SF circe2: procedures>>
end module sf_circe2
@ %def sf_circe2
@
\subsection{Physics}
[[CIRCE2]] describes photon spectra
Beamstrahlung is applied before ISR. The [[CIRCE2]] implementation has
a single structure function for both beams (which makes sense since it
has to be switched on or off for both beams simultaneously).
\subsection{The CIRCE2 data block}
The CIRCE2 parameters are: file and collider specification, incoming
(= outgoing) particles. The luminosity is returned by [[circe2_luminosity]].
<<SF circe2: public>>=
public :: circe2_data_t
<<SF circe2: types>>=
type, extends (sf_data_t) :: circe2_data_t
private
class(model_data_t), pointer :: model => null ()
type(flavor_t), dimension(2) :: flv_in
integer, dimension(2) :: pdg_in
real(default) :: sqrts = 0
logical :: polarized = .false.
logical :: beams_polarized = .false.
class(rng_factory_t), allocatable :: rng_factory
type(string_t) :: filename
type(string_t) :: file
type(string_t) :: design
real(default) :: lumi = 0
real(default), dimension(4) :: lumi_hel_frac = 0
integer, dimension(0:4) :: h1 = [0, -1, -1, 1, 1]
integer, dimension(0:4) :: h2 = [0, -1, 1,-1, 1]
integer :: error = 1
contains
<<SF circe2: circe2 data: TBP>>
end type circe2_data_t
@ %def circe2_data_t
<<SF circe2: types>>=
type(circe2_state) :: circe2_global_state
@
<<SF circe2: circe2 data: TBP>>=
procedure :: init => circe2_data_init
<<SF circe2: procedures>>=
subroutine circe2_data_init (data, os_data, model, pdg_in, &
sqrts, polarized, beam_pol, file, design)
class(circe2_data_t), intent(out) :: data
type(os_data_t), intent(in) :: os_data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), dimension(2), intent(in) :: pdg_in
real(default), intent(in) :: sqrts
logical, intent(in) :: polarized, beam_pol
type(string_t), intent(in) :: file, design
integer :: h
data%model => model
if (any (pdg_array_get_length (pdg_in) /= 1)) then
call msg_fatal ("CIRCE2: incoming beam particles must be unique")
end if
call data%flv_in(1)%init (pdg_array_get (pdg_in(1), 1), model)
call data%flv_in(2)%init (pdg_array_get (pdg_in(2), 1), model)
data%pdg_in = data%flv_in%get_pdg ()
data%sqrts = sqrts
data%polarized = polarized
data%beams_polarized = beam_pol
data%filename = file
data%design = design
call data%check_file (os_data)
call circe2_load (circe2_global_state, trim (char(data%file)), &
trim (char(data%design)), data%sqrts, data%error)
call data%check ()
data%lumi = circe2_luminosity (circe2_global_state, data%pdg_in, [0, 0])
if (vanishes (data%lumi)) then
call msg_fatal ("CIRCE2: luminosity vanishes for specified beams.")
end if
if (data%polarized) then
do h = 1, 4
data%lumi_hel_frac(h) = &
circe2_luminosity (circe2_global_state, data%pdg_in, &
[data%h1(h), data%h2(h)]) &
/ data%lumi
end do
end if
end subroutine circe2_data_init
@ %def circe2_data_init
@ Activate the generator mode. We import a RNG factory into the data
type, which can then spawn RNG generator objects.
<<SF circe2: circe2 data: TBP>>=
procedure :: set_generator_mode => circe2_data_set_generator_mode
<<SF circe2: procedures>>=
subroutine circe2_data_set_generator_mode (data, rng_factory)
class(circe2_data_t), intent(inout) :: data
class(rng_factory_t), intent(inout), allocatable :: rng_factory
call move_alloc (from = rng_factory, to = data%rng_factory)
end subroutine circe2_data_set_generator_mode
@ %def circe2_data_set_generator_mode
@ Check whether the requested data file is in the system directory or
in the current directory.
<<SF circe2: circe2 data: TBP>>=
procedure :: check_file => circe2_check_file
<<SF circe2: procedures>>=
subroutine circe2_check_file (data, os_data)
class(circe2_data_t), intent(inout) :: data
type(os_data_t), intent(in) :: os_data
logical :: exist
type(string_t) :: file
file = data%filename
if (file == "") &
call msg_fatal ("CIRCE2: $circe2_file is not set")
inquire (file = char (file), exist = exist)
if (exist) then
data%file = file
else
file = os_data%whizard_circe2path // "/" // data%filename
inquire (file = char (file), exist = exist)
if (exist) then
data%file = file
else
call msg_fatal ("CIRCE2: data file '" // char (data%filename) &
// "' not found")
end if
end if
end subroutine circe2_check_file
@ %def circe2_check_file
@ Handle error conditions.
<<SF circe2: circe2 data: TBP>>=
procedure :: check => circe2_data_check
<<SF circe2: procedures>>=
subroutine circe2_data_check (data)
class(circe2_data_t), intent(in) :: data
type(flavor_t) :: flv_photon, flv_electron
call flv_photon%init (PHOTON, data%model)
if (.not. flv_photon%is_defined ()) then
call msg_fatal ("CIRCE2: model must contain photon")
end if
call flv_electron%init (ELECTRON, data%model)
if (.not. flv_electron%is_defined ()) then
call msg_fatal ("CIRCE2: model must contain electron")
end if
if (any (abs (data%pdg_in) /= PHOTON .and. abs (data%pdg_in) /= ELECTRON)) &
then
call msg_fatal ("CIRCE2: applicable only for e+e- or photon collisions")
end if
select case (data%error)
case (-1)
call msg_fatal ("CIRCE2: data file not found.")
case (-2)
call msg_fatal ("CIRCE2: beam setup does not match data file.")
case (-3)
call msg_fatal ("CIRCE2: invalid format of data file.")
case (-4)
call msg_fatal ("CIRCE2: data file too large.")
end select
end subroutine circe2_data_check
@ %def circe2_data_check
@ Output
<<SF circe2: circe2 data: TBP>>=
procedure :: write => circe2_data_write
<<SF circe2: procedures>>=
subroutine circe2_data_write (data, unit, verbose)
class(circe2_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u, h
u = given_output_unit (unit)
write (u, "(1x,A)") "CIRCE2 data:"
write (u, "(3x,A,A)") "file = ", char(data%filename)
write (u, "(3x,A,A)") "design = ", char(data%design)
write (u, "(3x,A," // FMT_19 // ")") "sqrts = ", data%sqrts
write (u, "(3x,A,A,A,A)") "prt_in = ", &
char (data%flv_in(1)%get_name ()), &
", ", char (data%flv_in(2)%get_name ())
write (u, "(3x,A,L1)") "polarized = ", data%polarized
write (u, "(3x,A,L1)") "beams pol. = ", data%beams_polarized
write (u, "(3x,A," // FMT_19 // ")") "luminosity = ", data%lumi
if (data%polarized) then
do h = 1, 4
write (u, "(6x,'(',I2,1x,I2,')',1x,'=',1x)", advance="no") &
data%h1(h), data%h2(h)
write (u, "(6x, " // FMT_19 // ")") data%lumi_hel_frac(h)
end do
end if
call data%rng_factory%write (u)
end subroutine circe2_data_write
@ %def circe2_data_write
@ This is always in generator mode.
<<SF circe2: circe2 data: TBP>>=
procedure :: is_generator => circe2_data_is_generator
<<SF circe2: procedures>>=
function circe2_data_is_generator (data) result (flag)
class(circe2_data_t), intent(in) :: data
logical :: flag
flag = .true.
end function circe2_data_is_generator
@ %def circe2_data_is_generator
@ The number of parameters is two, collinear splitting for
the two beams.
<<SF circe2: circe2 data: TBP>>=
procedure :: get_n_par => circe2_data_get_n_par
<<SF circe2: procedures>>=
function circe2_data_get_n_par (data) result (n)
class(circe2_data_t), intent(in) :: data
integer :: n
n = 2
end function circe2_data_get_n_par
@ %def circe2_data_get_n_par
@ Return the outgoing particles PDG codes. They are equal to the
incoming ones.
<<SF circe2: circe2 data: TBP>>=
procedure :: get_pdg_out => circe2_data_get_pdg_out
<<SF circe2: procedures>>=
subroutine circe2_data_get_pdg_out (data, pdg_out)
class(circe2_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
integer :: i, n
n = 2
do i = 1, n
pdg_out(i) = data%pdg_in(i)
end do
end subroutine circe2_data_get_pdg_out
@ %def circe2_data_get_pdg_out
@ Allocate the interaction record.
<<SF circe2: circe2 data: TBP>>=
procedure :: allocate_sf_int => circe2_data_allocate_sf_int
<<SF circe2: procedures>>=
subroutine circe2_data_allocate_sf_int (data, sf_int)
class(circe2_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (circe2_t :: sf_int)
end subroutine circe2_data_allocate_sf_int
@ %def circe2_data_allocate_sf_int
@ Return the beam file.
<<SF circe2: circe2 data: TBP>>=
procedure :: get_beam_file => circe2_data_get_beam_file
<<SF circe2: procedures>>=
function circe2_data_get_beam_file (data) result (file)
class(circe2_data_t), intent(in) :: data
type(string_t) :: file
file = "CIRCE2: " // data%filename
end function circe2_data_get_beam_file
@ %def circe2_data_get_beam_file
@
\subsection{Random Number Generator for CIRCE}
The CIRCE implementation now supports a generic random-number
generator object that allows for a local state as a component. To
support this, we must extend the abstract type provided by CIRCE and
delegate the generator call to the (also abstract) RNG used by WHIZARD.
<<SF circe2: types>>=
type, extends (circe2_rng_t) :: rng_obj_t
class(rng_t), allocatable :: rng
contains
procedure :: generate => rng_obj_generate
end type rng_obj_t
@ %def rng_obj_t
<<SF circe2: procedures>>=
subroutine rng_obj_generate (rng_obj, u)
class(rng_obj_t), intent(inout) :: rng_obj
real(default), intent(out) :: u
real(default) :: x
call rng_obj%rng%generate (x)
u = x
end subroutine rng_obj_generate
@ %def rng_obj_generate
@
\subsection{The CIRCE2 object}
For CIRCE2 spectra it does not make sense to describe the state matrix
as a radiation interaction, even if photons originate from laser
backscattering. Instead, it is a $2\to 2$ interaction where the
incoming particles are identical to the outgoing ones.
The current implementation of CIRCE2 does support polarization and
classical correlations, but no entanglement, so the density matrix of
the outgoing particles is diagonal. The incoming particles are
unpolarized (user-defined polarization for beams is meaningless, since
polarization is described by the data file). The outgoing particles
are polarized or polarization-averaged, depending on user request.
When assigning matrix elements, we scan the previously initialized
state matrix. For each entry, we extract helicity and call the
structure function. In the unpolarized case, the helicity is
undefined and replaced by value zero. In the polarized case, there
are four entries. If the generator is used, only one entry is nonzero
in each call. Which one, is determined by comparing with a previously
(randomly, distributed by relative luminosity) selected pair of
helicities.
<<SF circe2: public>>=
public :: circe2_t
<<SF circe2: types>>=
type, extends (sf_int_t) :: circe2_t
type(circe2_data_t), pointer :: data => null ()
type(rng_obj_t) :: rng_obj
type(selector_t) :: selector
integer :: h_sel = 0
contains
<<SF circe2: circe2: TBP>>
end type circe2_t
@ %def circe2_t
@ Type string: show file and design of [[CIRCE2]] structure function.
<<SF circe2: circe2: TBP>>=
procedure :: type_string => circe2_type_string
<<SF circe2: procedures>>=
function circe2_type_string (object) result (string)
class(circe2_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "CIRCE2: " // object%data%design
else
string = "CIRCE2: [undefined]"
end if
end function circe2_type_string
@ %def circe2_type_string
@
@ Output. Call the interaction routine after displaying the configuration.
<<SF circe2: circe2: TBP>>=
procedure :: write => circe2_write
<<SF circe2: procedures>>=
subroutine circe2_write (object, unit, testflag)
class(circe2_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "CIRCE2 data: [undefined]"
end if
end subroutine circe2_write
@ %def circe2_write
@
<<SF circe2: circe2: TBP>>=
procedure :: init => circe2_init
<<SF circe2: procedures>>=
subroutine circe2_init (sf_int, data)
class(circe2_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
logical, dimension(4) :: mask_h
real(default), dimension(0) :: null_array
type(quantum_numbers_mask_t), dimension(4) :: mask
type(quantum_numbers_t), dimension(4) :: qn
type(helicity_t) :: hel
type(color_t) :: col0
integer :: h
select type (data)
type is (circe2_data_t)
if (data%polarized .and. data%beams_polarized) then
call msg_fatal ("CIRCE2: Beam polarization can't be set &
&for polarized data file")
else if (data%beams_polarized) then
call msg_warning ("CIRCE2: User-defined beam polarization set &
&for unpolarized CIRCE2 data file")
end if
mask_h(1:2) = .not. data%beams_polarized
mask_h(3:4) = .not. (data%polarized .or. data%beams_polarized)
mask = quantum_numbers_mask (.false., .false., mask_h)
call sf_int%base_init (mask, [0._default, 0._default], &
null_array, [0._default, 0._default])
sf_int%data => data
if (data%polarized) then
if (vanishes (sum (data%lumi_hel_frac)) .or. &
any (data%lumi_hel_frac < 0)) then
call msg_fatal ("CIRCE2: Helicity-dependent lumi " &
// "fractions all vanish or", &
[var_str ("are negative: Please inspect the " &
// "CIRCE2 file or "), &
var_str ("switch off the polarized" // &
" option for CIRCE2.")])
else
call sf_int%selector%init (data%lumi_hel_frac)
end if
end if
call col0%init ()
if (data%beams_polarized) then
do h = 1, 4
call hel%init (data%h1(h))
call qn(1)%init &
(flv = data%flv_in(1), col = col0, hel = hel)
call qn(3)%init &
(flv = data%flv_in(1), col = col0, hel = hel)
call hel%init (data%h2(h))
call qn(2)%init &
(flv = data%flv_in(2), col = col0, hel = hel)
call qn(4)%init &
(flv = data%flv_in(2), col = col0, hel = hel)
call sf_int%add_state (qn)
end do
else if (data%polarized) then
call qn(1)%init (flv = data%flv_in(1), col = col0)
call qn(2)%init (flv = data%flv_in(2), col = col0)
do h = 1, 4
call hel%init (data%h1(h))
call qn(3)%init &
(flv = data%flv_in(1), col = col0, hel = hel)
call hel%init (data%h2(h))
call qn(4)%init &
(flv = data%flv_in(2), col = col0, hel = hel)
call sf_int%add_state (qn)
end do
else
call qn(1)%init (flv = data%flv_in(1), col = col0)
call qn(2)%init (flv = data%flv_in(2), col = col0)
call qn(3)%init (flv = data%flv_in(1), col = col0)
call qn(4)%init (flv = data%flv_in(2), col = col0)
call sf_int%add_state (qn)
end if
call sf_int%freeze ()
call sf_int%set_incoming ([1,2])
call sf_int%set_outgoing ([3,4])
call sf_int%data%rng_factory%make (sf_int%rng_obj%rng)
sf_int%status = SF_INITIAL
end select
end subroutine circe2_init
@ %def circe2_init
@
\subsection{Kinematics}
Refer to the [[data]] component.
<<SF circe2: circe2: TBP>>=
procedure :: is_generator => circe2_is_generator
<<SF circe2: procedures>>=
function circe2_is_generator (sf_int) result (flag)
class(circe2_t), intent(in) :: sf_int
logical :: flag
flag = sf_int%data%is_generator ()
end function circe2_is_generator
@ %def circe2_is_generator
@ Generate free parameters. We first select a helicity, which we have
to store, then generate $x$ values for that helicity.
<<SF circe2: circe2: TBP>>=
procedure :: generate_free => circe2_generate_whizard_free
<<SF circe2: procedures>>=
subroutine circe2_generate_whizard_free (sf_int, r, rb, x_free)
class(circe2_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: r, rb
real(default), intent(inout) :: x_free
integer :: h_sel
if (sf_int%data%polarized) then
call sf_int%selector%generate (sf_int%rng_obj%rng, h_sel)
else
h_sel = 0
end if
sf_int%h_sel = h_sel
call circe2_generate_whizard (r, sf_int%data%pdg_in, &
[sf_int%data%h1(h_sel), sf_int%data%h2(h_sel)], &
sf_int%rng_obj)
rb = 1 - r
x_free = x_free * product (r)
end subroutine circe2_generate_whizard_free
@ %def circe2_generate_whizard_free
@ Generator mode: call the CIRCE2 generator for the given particles
and helicities. (For unpolarized generation, helicities are zero.)
<<SF circe2: procedures>>=
subroutine circe2_generate_whizard (x, pdg, hel, rng_obj)
real(default), dimension(2), intent(out) :: x
integer, dimension(2), intent(in) :: pdg
integer, dimension(2), intent(in) :: hel
class(rng_obj_t), intent(inout) :: rng_obj
call circe2_generate (circe2_global_state, rng_obj, x, pdg, hel)
end subroutine circe2_generate_whizard
@ %def circe2_generate_whizard
@ Set kinematics. Trivial here.
<<SF circe2: circe2: TBP>>=
procedure :: complete_kinematics => circe2_complete_kinematics
<<SF circe2: procedures>>=
subroutine circe2_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(circe2_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
if (map) then
call msg_fatal ("CIRCE2: map flag not supported")
else
x = r
xb= rb
f = 1
end if
call sf_int%reduce_momenta (x)
end subroutine circe2_complete_kinematics
@ %def circe2_complete_kinematics
@ Compute inverse kinematics.
<<SF circe2: circe2: TBP>>=
procedure :: inverse_kinematics => circe2_inverse_kinematics
<<SF circe2: procedures>>=
subroutine circe2_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(circe2_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
call msg_fatal ("CIRCE2: map flag not supported")
else
r = x
rb= xb
f = 1
end if
if (set_mom) then
call sf_int%reduce_momenta (x)
end if
end subroutine circe2_inverse_kinematics
@ %def circe2_inverse_kinematics
@
\subsection{CIRCE2 application}
This function works on both beams. In polarized mode, we set only the
selected helicity. In unpolarized mode,
the interaction has only one entry, and the factor is unity.
<<SF circe2: circe2: TBP>>=
procedure :: apply => circe2_apply
<<SF circe2: procedures>>=
- subroutine circe2_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine circe2_apply (sf_int, scale, rescale, i_sub)
class(circe2_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
complex(default) :: f
associate (data => sf_int%data)
f = 1
if (data%beams_polarized) then
call sf_int%set_matrix_element (f)
else if (data%polarized) then
call sf_int%set_matrix_element (sf_int%h_sel, f)
else
call sf_int%set_matrix_element (1, f)
end if
end associate
sf_int%status = SF_EVALUATED
end subroutine circe2_apply
@ %def circe2_apply
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_circe2_ut.f90]]>>=
<<File header>>
module sf_circe2_ut
use unit_tests
use sf_circe2_uti
<<Standard module head>>
<<SF circe2: public test>>
contains
<<SF circe2: test driver>>
end module sf_circe2_ut
@ %def sf_circe2_ut
@
<<[[sf_circe2_uti.f90]]>>=
<<File header>>
module sf_circe2_uti
<<Use kinds>>
<<Use strings>>
use os_interface
use physics_defs, only: PHOTON
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use model_data
use rng_base
use sf_aux
use sf_base
use sf_circe2
use rng_base_ut, only: rng_test_factory_t
<<Standard module head>>
<<SF circe2: test declarations>>
contains
<<SF circe2: tests>>
end module sf_circe2_uti
@ %def sf_circe2_ut
@ API: driver for the unit tests below.
<<SF circe2: public test>>=
public :: sf_circe2_test
<<SF circe2: test driver>>=
subroutine sf_circe2_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF circe2: execute tests>>
end subroutine sf_circe2_test
@ %def sf_circe2_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF circe2: execute tests>>=
call test (sf_circe2_1, "sf_circe2_1", &
"structure function configuration", &
u, results)
<<SF circe2: test declarations>>=
public :: sf_circe2_1
<<SF circe2: tests>>=
subroutine sf_circe2_1 (u)
integer, intent(in) :: u
type(os_data_t) :: os_data
type(model_data_t), target :: model
type(pdg_array_t), dimension(2) :: pdg_in
type(pdg_array_t), dimension(2) :: pdg_out
integer, dimension(:), allocatable :: pdg1, pdg2
class(sf_data_t), allocatable :: data
class(rng_factory_t), allocatable :: rng_factory
write (u, "(A)") "* Test output: sf_circe2_1"
write (u, "(A)") "* Purpose: initialize and display &
&CIRCE structure function data"
write (u, "(A)")
write (u, "(A)") "* Create empty data object"
write (u, "(A)")
call os_data%init ()
call model%init_qed_test ()
pdg_in(1) = PHOTON
pdg_in(2) = PHOTON
allocate (circe2_data_t :: data)
allocate (rng_test_factory_t :: rng_factory)
write (u, "(A)")
write (u, "(A)") "* Initialize (unpolarized)"
write (u, "(A)")
select type (data)
type is (circe2_data_t)
call data%init (os_data, model, pdg_in, &
sqrts = 500._default, &
polarized = .false., &
beam_pol = .false., &
file = var_str ("teslagg_500_polavg.circe"), &
design = var_str ("TESLA/GG"))
call data%set_generator_mode (rng_factory)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
pdg2 = pdg_out(2)
write (u, "(2x,99(1x,I0))") pdg1, pdg2
write (u, "(A)")
write (u, "(A)") "* Initialize (polarized)"
write (u, "(A)")
allocate (rng_test_factory_t :: rng_factory)
select type (data)
type is (circe2_data_t)
call data%init (os_data, model, pdg_in, &
sqrts = 500._default, &
polarized = .true., &
beam_pol = .false., &
file = var_str ("teslagg_500.circe"), &
design = var_str ("TESLA/GG"))
call data%set_generator_mode (rng_factory)
end select
call data%write (u)
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_circe2_1"
end subroutine sf_circe2_1
@ %def sf_circe2_1
@
\subsubsection{Generator mode, unpolarized}
Construct and evaluate a structure function object in generator mode.
<<SF circe2: execute tests>>=
call test (sf_circe2_2, "sf_circe2_2", &
"generator, unpolarized", &
u, results)
<<SF circe2: test declarations>>=
public :: sf_circe2_2
<<SF circe2: tests>>=
subroutine sf_circe2_2 (u)
integer, intent(in) :: u
type(os_data_t) :: os_data
type(model_data_t), target :: model
type(flavor_t), dimension(2) :: flv
type(pdg_array_t), dimension(2) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(rng_factory_t), allocatable :: rng_factory
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k1, k2
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f, x_free
write (u, "(A)") "* Test output: sf_circe2_2"
write (u, "(A)") "* Purpose: initialize and fill &
&circe2 structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call os_data%init ()
call model%init_qed_test ()
call flv(1)%init (PHOTON, model)
call flv(2)%init (PHOTON, model)
pdg_in(1) = PHOTON
pdg_in(2) = PHOTON
call reset_interaction_counter ()
allocate (circe2_data_t :: data)
allocate (rng_test_factory_t :: rng_factory)
select type (data)
type is (circe2_data_t)
call data%init (os_data, model, pdg_in, &
sqrts = 500._default, &
polarized = .false., &
beam_pol = .false., &
file = var_str ("teslagg_500_polavg.circe"), &
design = var_str ("TESLA/GG"))
call data%set_generator_mode (rng_factory)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1,2])
select type (sf_int)
type is (circe2_t)
call sf_int%rng_obj%rng%init (3)
end select
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 250
k1 = vector4_moving (E, sqrt (E**2 - flv(1)%get_mass ()**2), 3)
k2 = vector4_moving (E,-sqrt (E**2 - flv(2)%get_mass ()**2), 3)
call vector4_write (k1, u)
call vector4_write (k2, u)
call sf_int%seed_kinematics ([k1, k2])
write (u, "(A)")
write (u, "(A)") "* Generate x"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0
rb = 0
x_free = 1
call sf_int%generate_free (r, rb, x_free)
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A,9(1x,F10.7))") "xf=", x_free
write (u, "(A)")
write (u, "(A)") "* Evaluate"
write (u, "(A)")
call sf_int%apply (scale = 0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_circe2_2"
end subroutine sf_circe2_2
@ %def sf_circe2_2
@
\subsubsection{Generator mode, polarized}
Construct and evaluate a structure function object in generator mode.
<<SF circe2: execute tests>>=
call test (sf_circe2_3, "sf_circe2_3", &
"generator, polarized", &
u, results)
<<SF circe2: test declarations>>=
public :: sf_circe2_3
<<SF circe2: tests>>=
subroutine sf_circe2_3 (u)
integer, intent(in) :: u
type(os_data_t) :: os_data
type(model_data_t), target :: model
type(flavor_t), dimension(2) :: flv
type(pdg_array_t), dimension(2) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(rng_factory_t), allocatable :: rng_factory
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k1, k2
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f, x_free
write (u, "(A)") "* Test output: sf_circe2_3"
write (u, "(A)") "* Purpose: initialize and fill &
&circe2 structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call os_data%init ()
call model%init_qed_test ()
call flv(1)%init (PHOTON, model)
call flv(2)%init (PHOTON, model)
pdg_in(1) = PHOTON
pdg_in(2) = PHOTON
call reset_interaction_counter ()
allocate (circe2_data_t :: data)
allocate (rng_test_factory_t :: rng_factory)
select type (data)
type is (circe2_data_t)
call data%init (os_data, model, pdg_in, &
sqrts = 500._default, &
polarized = .true., &
beam_pol = .false., &
file = var_str ("teslagg_500.circe"), &
design = var_str ("TESLA/GG"))
call data%set_generator_mode (rng_factory)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1,2])
select type (sf_int)
type is (circe2_t)
call sf_int%rng_obj%rng%init (3)
end select
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 250
k1 = vector4_moving (E, sqrt (E**2 - flv(1)%get_mass ()**2), 3)
k2 = vector4_moving (E,-sqrt (E**2 - flv(2)%get_mass ()**2), 3)
call vector4_write (k1, u)
call vector4_write (k2, u)
call sf_int%seed_kinematics ([k1, k2])
write (u, "(A)")
write (u, "(A)") "* Generate x"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0
rb = 0
x_free = 1
call sf_int%generate_free (r, rb, x_free)
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A,9(1x,F10.7))") "xf=", x_free
write (u, "(A)")
write (u, "(A)") "* Evaluate"
write (u, "(A)")
call sf_int%apply (scale = 0._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_circe2_3"
end subroutine sf_circe2_3
@ %def sf_circe2_3
@
\clearpage
%------------------------------------------------------------------------
\section{HOPPET interface}
Interface to the HOPPET wrapper necessary to perform
the LO vs. NLO matching of processes containing an initial
b quark.
<<[[hoppet_interface.f90]]>>=
<<File header>>
module hoppet_interface
use lhapdf !NODEP!
<<Standard module head>>
public :: hoppet_init, hoppet_eval
contains
subroutine hoppet_init (pdf_builtin, pdf, pdf_id)
logical, intent(in) :: pdf_builtin
type(lhapdf_pdf_t), intent(inout), optional :: pdf
integer, intent(in), optional :: pdf_id
external InitForWhizard
call InitForWhizard (pdf_builtin, pdf, pdf_id)
end subroutine hoppet_init
subroutine hoppet_eval (x, q, f)
double precision, intent(in) :: x, q
double precision, intent(out) :: f(-6:6)
external EvalForWhizard
call EvalForWhizard (x, q, f)
end subroutine hoppet_eval
end module hoppet_interface
@ %def hoppet_interface
@
\clearpage
%------------------------------------------------------------------------
\section{Builtin PDF sets}
For convenience in order not to depend on the external package LHAPDF,
we ship some PDFs with WHIZARD.
@
\subsection{The module}
<<[[sf_pdf_builtin.f90]]>>=
<<File header>>
module sf_pdf_builtin
<<Use kinds>>
use kinds, only: double
<<Use strings>>
use io_units
use format_defs, only: FMT_17
use diagnostics
use os_interface
- use physics_defs, only: n_beam_gluon_offset
use physics_defs, only: PROTON, PHOTON, GLUON
use physics_defs, only: HADRON_REMNANT_SINGLET
use physics_defs, only: HADRON_REMNANT_TRIPLET
use physics_defs, only: HADRON_REMNANT_OCTET
use sm_qcd
use lorentz
use pdg_arrays
use model_data
use flavors
use colors
use quantum_numbers
use state_matrices
use polarizations
use sf_base
use pdf_builtin !NODEP!
use hoppet_interface
<<Standard module head>>
<<SF pdf builtin: public>>
<<SF pdf builtin: types>>
<<SF pdf builtin: parameters>>
contains
<<SF pdf builtin: procedures>>
end module sf_pdf_builtin
@ %def sf_pdf_builtin
@
\subsection{Codes for default PDF sets}
<<SF pdf builtin: parameters>>=
character(*), parameter :: PDF_BUILTIN_DEFAULT_PROTON = "CTEQ6L"
! character(*), parameter :: PDF_BUILTIN_DEFAULT_PION = "NONE"
! character(*), parameter :: PDF_BUILTIN_DEFAULT_PHOTON = "MRST2004QEDp"
@ %def PDF_BUILTIN_DEFAULT_SET
@
\subsection{The PDF builtin data block}
The data block holds the incoming flavor (which has to be proton,
pion, or photon), the corresponding pointer to the global access data
(1, 2, or 3), the flag [[invert]] which is set for an antiproton, the
bounds as returned by LHAPDF for the specified set, and a mask that
determines which partons will be actually in use.
<<SF pdf builtin: public>>=
public :: pdf_builtin_data_t
<<SF pdf builtin: types>>=
type, extends (sf_data_t) :: pdf_builtin_data_t
private
integer :: id = -1
type (string_t) :: name
class(model_data_t), pointer :: model => null ()
type(flavor_t) :: flv_in
logical :: invert
logical :: has_photon
logical :: photon
logical, dimension(-6:6) :: mask
logical :: mask_photon
logical :: hoppet_b_matching = .false.
contains
<<SF pdf builtin: pdf builtin data: TBP>>
end type pdf_builtin_data_t
@ %def pdf_builtin_data_t
@ Generate PDF data and initialize the requested set. Pion and photon PDFs
are disabled at the moment until we ship appropiate structure functions.
needed.
<<SF pdf builtin: pdf builtin data: TBP>>=
procedure :: init => pdf_builtin_data_init
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_data_init (data, &
model, pdg_in, name, path, hoppet_b_matching)
class(pdf_builtin_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), intent(in) :: pdg_in
type(string_t), intent(in) :: name
type(string_t), intent(in) :: path
logical, intent(in), optional :: hoppet_b_matching
data%model => model
if (pdg_array_get_length (pdg_in) /= 1) &
call msg_fatal ("PDF: incoming particle must be unique")
call data%flv_in%init (pdg_array_get (pdg_in, 1), model)
data%mask = .true.
data%mask_photon = .true.
select case (pdg_array_get (pdg_in, 1))
case (PROTON)
data%name = var_str (PDF_BUILTIN_DEFAULT_PROTON)
data%invert = .false.
data%photon = .false.
case (-PROTON)
data%name = var_str (PDF_BUILTIN_DEFAULT_PROTON)
data%invert = .true.
data%photon = .false.
! case (PIPLUS)
! data%name = var_str (PDF_BUILTIN_DEFAULT_PION)
! data%invert = .false.
! data%photon = .false.
! case (-PIPLUS)
! data%name = var_str (PDF_BUILTIN_DEFAULT_PION)
! data%invert = .true.
! data%photon = .false.
! case (PHOTON)
! data%name = var_str (PDF_BUILTIN_DEFAULT_PHOTON)
! data%invert = .false.
! data%photon = .true.
case default
call msg_fatal ("PDF: " &
// "incoming particle must either proton or antiproton.")
return
end select
data%name = name
data%id = pdf_get_id (data%name)
if (data%id < 0) call msg_fatal ("unknown PDF set " // char (data%name))
data%has_photon = pdf_provides_photon (data%id)
if (present (hoppet_b_matching)) data%hoppet_b_matching = hoppet_b_matching
call pdf_init (data%id, path)
if (data%hoppet_b_matching) call hoppet_init (.true., pdf_id = data%id)
end subroutine pdf_builtin_data_init
@ %def pdf_builtin_data_init
@ Enable/disable partons explicitly. If a mask entry is true,
applying the PDF will generate the corresponding flavor on output.
<<SF pdf builtin: pdf builtin data: TBP>>=
procedure :: set_mask => pdf_builtin_data_set_mask
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_data_set_mask (data, mask)
class(pdf_builtin_data_t), intent(inout) :: data
logical, dimension(-6:6), intent(in) :: mask
data%mask = mask
end subroutine pdf_builtin_data_set_mask
@ %def pdf_builtin_data_set_mask
@ Output.
<<SF pdf builtin: pdf builtin data: TBP>>=
procedure :: write => pdf_builtin_data_write
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_data_write (data, unit, verbose)
class(pdf_builtin_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "PDF builtin data:"
if (data%id < 0) then
write (u, "(3x,A)") "[undefined]"
return
end if
write (u, "(3x,A)", advance="no") "flavor = "
call data%flv_in%write (u); write (u, *)
write (u, "(3x,A,A)") "name = ", char (data%name)
write (u, "(3x,A,L1)") "invert = ", data%invert
write (u, "(3x,A,L1)") "has photon = ", data%has_photon
write (u, "(3x,A,6(1x,L1),1x,A,1x,L1,1x,A,6(1x,L1))") &
"mask =", &
data%mask(-6:-1), "*", data%mask(0), "*", data%mask(1:6)
write (u, "(3x,A,L1)") "photon mask = ", data%mask_photon
write (u, "(3x,A,L1)") "hoppet_b = ", data%hoppet_b_matching
end subroutine pdf_builtin_data_write
@ %def pdf_builtin_data_write
@ The number of parameters is one. We do not generate transverse momentum.
<<SF pdf builtin: pdf builtin data: TBP>>=
procedure :: get_n_par => pdf_builtin_data_get_n_par
<<SF pdf builtin: procedures>>=
function pdf_builtin_data_get_n_par (data) result (n)
class(pdf_builtin_data_t), intent(in) :: data
integer :: n
n = 1
end function pdf_builtin_data_get_n_par
@ %def pdf_builtin_data_get_n_par
@ Return the outgoing particle PDG codes. This is based on the mask.
<<SF pdf builtin: pdf builtin data: TBP>>=
procedure :: get_pdg_out => pdf_builtin_data_get_pdg_out
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_data_get_pdg_out (data, pdg_out)
class(pdf_builtin_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
integer, dimension(:), allocatable :: pdg1
integer :: n, np, i
n = count (data%mask)
np = 0; if (data%has_photon .and. data%mask_photon) np = 1
allocate (pdg1 (n + np))
pdg1(1:n) = pack ([(i, i = -6, 6)], data%mask)
if (np == 1) pdg1(n+np) = PHOTON
pdg_out(1) = pdg1
end subroutine pdf_builtin_data_get_pdg_out
@ %def pdf_builtin_data_get_pdg_out
@ Allocate the interaction record.
<<SF pdf builtin: pdf builtin data: TBP>>=
procedure :: allocate_sf_int => pdf_builtin_data_allocate_sf_int
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_data_allocate_sf_int (data, sf_int)
class(pdf_builtin_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (pdf_builtin_t :: sf_int)
end subroutine pdf_builtin_data_allocate_sf_int
@ %def pdf_builtin_data_allocate_sf_int
@ Return the numerical PDF set index.
<<SF pdf builtin: pdf builtin data: TBP>>=
procedure :: get_pdf_set => pdf_builtin_data_get_pdf_set
<<SF pdf builtin: procedures>>=
elemental function pdf_builtin_data_get_pdf_set (data) result (pdf_set)
class(pdf_builtin_data_t), intent(in) :: data
integer :: pdf_set
pdf_set = data%id
end function pdf_builtin_data_get_pdf_set
@ %def pdf_builtin_data_get_pdf_set
@
\subsection{The PDF object}
The PDF $1\to 2$ interaction which describes
the splitting of an (anti)proton into a parton and a beam remnant. We
stay in the strict forward-splitting limit, but allow some invariant
mass for the beam remnant such that the outgoing parton is exactly
massless. For a real event, we would replace this by a parton
cascade, where the outgoing partons have virtuality as dictated by
parton-shower kinematics, and transverse momentum is generated.
The PDF application is a $1\to 2$ splitting process, where the
particles are ordered as (hadron, remnant, parton).
Polarization is ignored completely. The beam particle is colorless,
while partons and beam remnant carry color. The remnant gets a
special flavor code.
<<SF pdf builtin: public>>=
public :: pdf_builtin_t
<<SF pdf builtin: types>>=
type, extends (sf_int_t) :: pdf_builtin_t
type(pdf_builtin_data_t), pointer :: data => null ()
real(default) :: x = 0
real(default) :: q = 0
contains
<<SF pdf builtin: pdf builtin: TBP>>
end type pdf_builtin_t
@ %def pdf_builtin_t
@ Type string: display the chosen PDF set.
<<SF pdf builtin: pdf builtin: TBP>>=
procedure :: type_string => pdf_builtin_type_string
<<SF pdf builtin: procedures>>=
function pdf_builtin_type_string (object) result (string)
class(pdf_builtin_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "PDF builtin: " // object%data%name
else
string = "PDF builtin: [undefined]"
end if
end function pdf_builtin_type_string
@ %def pdf_builtin_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF pdf builtin: pdf builtin: TBP>>=
procedure :: write => pdf_builtin_write
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_write (object, unit, testflag)
class(pdf_builtin_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
if (object%status >= SF_DONE_KINEMATICS) then
write (u, "(1x,A)") "SF parameters:"
write (u, "(3x,A," // FMT_17 // ")") "x =", object%x
if (object%status >= SF_FAILED_EVALUATION) then
write (u, "(3x,A," // FMT_17 // ")") "Q =", object%q
end if
end if
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "PDF builtin data: [undefined]"
end if
end subroutine pdf_builtin_write
@ %def pdf_builtin_write
@ Initialize. We know that [[data]] will be of concrete type
[[sf_test_data_t]], but we have to cast this explicitly.
For this implementation, we set the incoming and outgoing masses equal
to the physical particle mass, but keep the radiated mass zero.
Optionally, we can provide minimum and maximum values for the momentum
transfer.
<<SF pdf builtin: pdf builtin: TBP>>=
procedure :: init => pdf_builtin_init
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_init (sf_int, data)
class(pdf_builtin_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(3) :: mask
type(flavor_t) :: flv, flv_remnant
type(color_t) :: col0
type(quantum_numbers_t), dimension(3) :: qn
integer :: i
select type (data)
type is (pdf_builtin_data_t)
mask = quantum_numbers_mask (.false., .false., .true.)
call col0%init ()
call sf_int%base_init (mask, [0._default], [0._default], [0._default])
sf_int%data => data
do i = -6, 6
if (data%mask(i)) then
call qn(1)%init (data%flv_in, col = col0)
if (i == 0) then
call flv%init (GLUON, data%model)
call flv_remnant%init (HADRON_REMNANT_OCTET, data%model)
else
call flv%init (i, data%model)
call flv_remnant%init &
(sign (HADRON_REMNANT_TRIPLET, -i), data%model)
end if
call qn(2)%init ( &
flv = flv_remnant, col = color_from_flavor (flv_remnant, 1))
call qn(2)%tag_radiated ()
call qn(3)%init ( &
flv = flv, col = color_from_flavor (flv, 1, reverse=.true.))
call sf_int%add_state (qn)
end if
end do
if (data%has_photon .and. data%mask_photon) then
call flv%init (PHOTON, data%model)
call flv_remnant%init (HADRON_REMNANT_SINGLET, data%model)
call qn(2)%init (flv = flv_remnant, &
col = color_from_flavor (flv_remnant, 1))
call qn(2)%tag_radiated ()
call qn(3)%init (flv = flv, &
col = color_from_flavor (flv, 1, reverse = .true.))
call sf_int%add_state (qn)
end if
call sf_int%freeze ()
call sf_int%set_incoming ([1])
call sf_int%set_radiated ([2])
call sf_int%set_outgoing ([3])
sf_int%status = SF_INITIAL
end select
end subroutine pdf_builtin_init
@ %def pdf_builtin_init
@
\subsection{Kinematics}
Set kinematics. If [[map]] is unset, the $r$ and $x$ values
coincide, and the Jacobian $f(r)$ is trivial.
If [[map]] is set, we are asked to provide an efficient mapping.
For the test case, we set $x=r^2$ and consequently $f(r)=2r$.
<<SF pdf builtin: pdf builtin: TBP>>=
procedure :: complete_kinematics => pdf_builtin_complete_kinematics
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(pdf_builtin_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
if (map) then
call msg_fatal ("PDF builtin: map flag not supported")
else
x(1) = r(1)
xb(1)= rb(1)
f = 1
end if
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_DONE_KINEMATICS)
sf_int%x = x(1)
case (SF_FAILED_KINEMATICS)
sf_int%x = 0
f = 0
end select
end subroutine pdf_builtin_complete_kinematics
@ %def pdf_builtin_complete_kinematics
@ Overriding the default method: we compute the [[x]] value from the
momentum configuration. In this specific case, we also set the
internally stored $x$ value, so it can be used in the
following routine.
<<SF pdf builtin: pdf builtin: TBP>>=
procedure :: recover_x => pdf_builtin_recover_x
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_recover_x (sf_int, x, xb, x_free)
class(pdf_builtin_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(inout), optional :: x_free
call sf_int%base_recover_x (x, xb, x_free)
sf_int%x = x(1)
end subroutine pdf_builtin_recover_x
@ %def sf_pdf_builtin_recover_x
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
<<SF pdf builtin: pdf builtin: TBP>>=
procedure :: inverse_kinematics => pdf_builtin_inverse_kinematics
<<SF pdf builtin: procedures>>=
subroutine pdf_builtin_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(pdf_builtin_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
call msg_fatal ("PDF builtin: map flag not supported")
else
r(1) = x(1)
rb(1)= xb(1)
f = 1
end if
if (set_mom) then
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end if
end subroutine pdf_builtin_inverse_kinematics
@ %def pdf_builtin_inverse_kinematics
@
\subsection{Structure function}
Once the scale is also known, we can actually call the PDF and
set the values. Contrary to LHAPDF, the wrapper already takes care of
adjusting to the $x$ and $Q$ bounds. Account for the Jacobian.
-[[fill_sub]] allows us to the fill all matrix-elements with [[sub > 0]].
-Whereas [[rescale]] gives rescaling prescription for NLO convolution of the
+The class [[rescale]] gives rescaling prescription for NLO convolution of the
structure function in combination with [[i_sub]].
-[[fill_sub]] and [[rescale]] with [[i_sub]] are mutually exclusive.
<<SF pdf builtin: pdf builtin: TBP>>=
procedure :: apply => pdf_builtin_apply
<<SF pdf builtin: procedures>>=
- subroutine pdf_builtin_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine pdf_builtin_apply (sf_int, scale, rescale, i_sub)
class(pdf_builtin_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default), dimension(-6:6) :: ff
real(double), dimension(-6:6) :: ff_dbl
real(default) :: x, fph
real(double) :: xx, qq
complex(default), dimension(:), allocatable :: fc
integer :: i, j_sub, i_sub_opt
- logical :: fill_sub_opt
i_sub_opt = 0; if (present (i_sub)) i_sub_opt = i_sub
- fill_sub_opt = .false.; if (present (fill_sub)) fill_sub_opt = fill_sub
- if (present (rescale) .and. fill_sub_opt) then
- call msg_bug ("[pdf_builtin_apply] &
- & sf_rescale and fill_sub option are mutually exclusive.")
- end if
- if (i_sub_opt > 0 .and. fill_sub_opt) then
- call msg_bug ("[pdf_builtin_apply] &
- & i_sub and fill_sub options are mutually exclusive.")
- end if
associate (data => sf_int%data)
sf_int%q = scale
x = sf_int%x
if (present (rescale)) call rescale%apply (x)
if (debug2_active (D_BEAMS)) then
call msg_debug2 (D_BEAMS, "pdf_builtin_apply")
call msg_debug2 (D_BEAMS, "rescale: ", present(rescale))
call msg_debug2 (D_BEAMS, "i_sub: ", i_sub_opt)
- call msg_debug2 (D_BEAMS, "fill_sub: ", fill_sub_opt)
call msg_debug2 (D_BEAMS, "x: ", x)
end if
xx = x
qq = scale
if (data%invert) then
if (data%has_photon) then
call pdf_evolve (data%id, x, scale, ff(6:-6:-1), fph)
else
if (data%hoppet_b_matching) then
call hoppet_eval (xx, qq, ff_dbl(6:-6:-1))
ff = ff_dbl
else
call pdf_evolve (data%id, x, scale, ff(6:-6:-1))
end if
end if
else
if (data%has_photon) then
call pdf_evolve (data%id, x, scale, ff, fph)
else
if (data%hoppet_b_matching) then
call hoppet_eval (xx, qq, ff_dbl)
ff = ff_dbl
else
call pdf_evolve (data%id, x, scale, ff)
end if
end if
end if
if (data%has_photon) then
allocate (fc (count ([data%mask, data%mask_photon])))
fc = max (pack ([ff, fph], &
[data%mask, data%mask_photon]), 0._default)
else
allocate (fc (count (data%mask)))
fc = max (pack (ff, data%mask), 0._default)
end if
end associate
if (debug_active (D_BEAMS)) print *, 'Set pdfs: ', real (fc)
- if (present (rescale) .and. i_sub_opt > 0) then
- call sf_int%set_matrix_element (fc, [(i_sub_opt * size(fc) + i, i = 1, size(fc))])
- if (rescale%has_gluons ()) then
- j_sub = i_sub_opt + n_beam_gluon_offset
- call sf_int%set_matrix_element (&
- spread (fc(7), 1, size(fc)), [(j_sub * size(fc) + i, i = 1, size(fc))])
- end if
- else
- call sf_int%set_matrix_element (fc, [(i, i = 1, size(fc))])
- end if
- if(fill_sub_opt) then
- do j_sub = 1, sf_int%get_n_sub ()
- call sf_int%set_matrix_element (fc, [(j_sub * size(fc) + i, i = 1, size(fc))])
- end do
- end if
+ call sf_int%set_matrix_element (fc, [(i_sub_opt * size(fc) + i, i = 1, size(fc))])
sf_int%status = SF_EVALUATED
end subroutine pdf_builtin_apply
@ %def pdf_builtin_apply
@
\subsection{Strong Coupling}
Since the PDF codes provide a function for computing the running
$\alpha_s$ value, we make this available as an implementation of the
abstract [[alpha_qcd_t]] type, which is used for matrix element evaluation.
<<SF pdf builtin: public>>=
public :: alpha_qcd_pdf_builtin_t
<<SF pdf builtin: types>>=
type, extends (alpha_qcd_t) :: alpha_qcd_pdf_builtin_t
type(string_t) :: pdfset_name
integer :: pdfset_id = -1
contains
<<SF pdf builtin: alpha qcd: TBP>>
end type alpha_qcd_pdf_builtin_t
@ %def alpha_qcd_pdf_builtin_t
@ Output.
<<SF pdf builtin: alpha qcd: TBP>>=
procedure :: write => alpha_qcd_pdf_builtin_write
<<SF pdf builtin: procedures>>=
subroutine alpha_qcd_pdf_builtin_write (object, unit)
class(alpha_qcd_pdf_builtin_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(3x,A)") "QCD parameters (pdf_builtin):"
write (u, "(5x,A,A)") "PDF set = ", char (object%pdfset_name)
write (u, "(5x,A,I0)") "PDF ID = ", object%pdfset_id
end subroutine alpha_qcd_pdf_builtin_write
@ %def alpha_qcd_pdf_builtin_write
@ Calculation: the numeric ID selects the correct PDF set, which must
be properly initialized.
<<SF pdf builtin: alpha qcd: TBP>>=
procedure :: get => alpha_qcd_pdf_builtin_get
<<SF pdf builtin: procedures>>=
function alpha_qcd_pdf_builtin_get (alpha_qcd, scale) result (alpha)
class(alpha_qcd_pdf_builtin_t), intent(in) :: alpha_qcd
real(default), intent(in) :: scale
real(default) :: alpha
alpha = pdf_alphas (alpha_qcd%pdfset_id, scale)
end function alpha_qcd_pdf_builtin_get
@ %def alpha_qcd_pdf_builtin_get
@
Initialization. We need to access the global initialization status.
<<SF pdf builtin: alpha qcd: TBP>>=
procedure :: init => alpha_qcd_pdf_builtin_init
<<SF pdf builtin: procedures>>=
subroutine alpha_qcd_pdf_builtin_init (alpha_qcd, name, path)
class(alpha_qcd_pdf_builtin_t), intent(out) :: alpha_qcd
type(string_t), intent(in) :: name
type(string_t), intent(in) :: path
alpha_qcd%pdfset_name = name
alpha_qcd%pdfset_id = pdf_get_id (name)
if (alpha_qcd%pdfset_id < 0) &
call msg_fatal ("QCD parameter initialization: PDF set " &
// char (name) // " is unknown")
call pdf_init (alpha_qcd%pdfset_id, path)
end subroutine alpha_qcd_pdf_builtin_init
@ %def alpha_qcd_pdf_builtin_init
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_pdf_builtin_ut.f90]]>>=
<<File header>>
module sf_pdf_builtin_ut
use unit_tests
use sf_pdf_builtin_uti
<<Standard module head>>
<<SF pdf builtin: public test>>
contains
<<SF pdf builtin: test driver>>
end module sf_pdf_builtin_ut
@ %def sf_pdf_builtin_ut
@
<<[[sf_pdf_builtin_uti.f90]]>>=
<<File header>>
module sf_pdf_builtin_uti
<<Use kinds>>
<<Use strings>>
use os_interface
use physics_defs, only: PROTON
use sm_qcd
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use model_data
use sf_base
use sf_pdf_builtin
<<Standard module head>>
<<SF pdf builtin: test declarations>>
contains
<<SF pdf builtin: tests>>
end module sf_pdf_builtin_uti
@ %def sf_pdf_builtin_ut
@ API: driver for the unit tests below.
<<SF pdf builtin: public test>>=
public :: sf_pdf_builtin_test
<<SF pdf builtin: test driver>>=
subroutine sf_pdf_builtin_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF pdf builtin: execute tests>>
end subroutine sf_pdf_builtin_test
@ %def sf_pdf_builtin_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF pdf builtin: execute tests>>=
call test (sf_pdf_builtin_1, "sf_pdf_builtin_1", &
"structure function configuration", &
u, results)
<<SF pdf builtin: test declarations>>=
public :: sf_pdf_builtin_1
<<SF pdf builtin: tests>>=
subroutine sf_pdf_builtin_1 (u)
integer, intent(in) :: u
type(os_data_t) :: os_data
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(pdg_array_t), dimension(1) :: pdg_out
integer, dimension(:), allocatable :: pdg1
class(sf_data_t), allocatable :: data
type(string_t) :: name
write (u, "(A)") "* Test output: sf_pdf_builtin_1"
write (u, "(A)") "* Purpose: initialize and display &
&test structure function data"
write (u, "(A)")
write (u, "(A)") "* Create empty data object"
write (u, "(A)")
call os_data%init ()
call model%init_sm_test ()
pdg_in = PROTON
allocate (pdf_builtin_data_t :: data)
call data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize"
write (u, "(A)")
name = "CTEQ6L"
select type (data)
type is (pdf_builtin_data_t)
call data%init (model, pdg_in, name, &
os_data%pdf_builtin_datapath)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
write (u, "(2x,99(1x,I0))") pdg1
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_pdf_builtin_1"
end subroutine sf_pdf_builtin_1
@ %def sf_pdf_builtin_1
@
\subsubsection{Test and probe structure function}
Construct and display a structure function object based on the PDF builtin
structure function.
<<SF pdf builtin: execute tests>>=
call test (sf_pdf_builtin_2, "sf_pdf_builtin_2", &
"structure function instance", &
u, results)
<<SF pdf builtin: test declarations>>=
public :: sf_pdf_builtin_2
<<SF pdf builtin: tests>>=
subroutine sf_pdf_builtin_2 (u)
integer, intent(in) :: u
type(os_data_t) :: os_data
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(string_t) :: name
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_pdf_builtin_2"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call os_data%init ()
call model%init_sm_test ()
call flv%init (PROTON, model)
pdg_in = PROTON
call reset_interaction_counter ()
name = "CTEQ6L"
allocate (pdf_builtin_data_t :: data)
select type (data)
type is (pdf_builtin_data_t)
call data%init (model, pdg_in, name, &
os_data%pdf_builtin_datapath)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.5_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A)")
write (u, "(A)") "* Evaluate for Q = 100 GeV"
write (u, "(A)")
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%apply (scale = 100._default)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_pdf_builtin_2"
end subroutine sf_pdf_builtin_2
@ %def sf_pdf_builtin_2
@
\subsubsection{Strong Coupling}
Test $\alpha_s$ as an implementation of the [[alpha_qcd_t]] abstract
type.
<<SF pdf builtin: execute tests>>=
call test (sf_pdf_builtin_3, "sf_pdf_builtin_3", &
"running alpha_s", &
u, results)
<<SF pdf builtin: test declarations>>=
public :: sf_pdf_builtin_3
<<SF pdf builtin: tests>>=
subroutine sf_pdf_builtin_3 (u)
integer, intent(in) :: u
type(os_data_t) :: os_data
type(qcd_t) :: qcd
type(string_t) :: name
write (u, "(A)") "* Test output: sf_pdf_builtin_3"
write (u, "(A)") "* Purpose: initialize and evaluate alpha_s"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call os_data%init ()
name = "CTEQ6L"
write (u, "(A)") "* Initialize qcd object"
write (u, "(A)")
allocate (alpha_qcd_pdf_builtin_t :: qcd%alpha)
select type (alpha => qcd%alpha)
type is (alpha_qcd_pdf_builtin_t)
call alpha%init (name, os_data%pdf_builtin_datapath)
end select
call qcd%write (u)
write (u, "(A)")
write (u, "(A)") "* Evaluate for Q = 100"
write (u, "(A)")
write (u, "(1x,A,F8.5)") "alpha = ", qcd%alpha%get (100._default)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_pdf_builtin_3"
end subroutine sf_pdf_builtin_3
@ %def sf_pdf_builtin_3
@
\clearpage
%------------------------------------------------------------------------
\section{LHAPDF}
Parton distribution functions (PDFs) are available via an interface to
the LHAPDF standard library.
@
\subsection{The module}
<<[[sf_lhapdf.f90]]>>=
<<File header>>
module sf_lhapdf
<<Use kinds>>
<<Use strings>>
use format_defs, only: FMT_17, FMT_19
use io_units
use system_dependencies, only: LHAPDF_PDFSETS_PATH
use system_dependencies, only: LHAPDF5_AVAILABLE
use system_dependencies, only: LHAPDF6_AVAILABLE
use diagnostics
- use physics_defs, only: n_beam_gluon_offset
use physics_defs, only: PROTON, PHOTON, PIPLUS, GLUON
use physics_defs, only: HADRON_REMNANT_SINGLET
use physics_defs, only: HADRON_REMNANT_TRIPLET
use physics_defs, only: HADRON_REMNANT_OCTET
use lorentz
use sm_qcd
use pdg_arrays
use model_data
use flavors
use colors
use quantum_numbers
use state_matrices
use polarizations
use sf_base
use lhapdf !NODEP!
use hoppet_interface
<<Standard module head>>
<<SF lhapdf: public>>
<<SF lhapdf: types>>
<<SF lhapdf: parameters>>
<<SF lhapdf: variables>>
<<SF lhapdf: interfaces>>
contains
<<SF lhapdf: procedures>>
end module sf_lhapdf
@ %def sf_lhapdf
@
\subsection{Codes for default PDF sets}
The default PDF for protons set is chosen to be CTEQ6ll (LO fit with
LO $\alpha_s$).
<<SF lhapdf: parameters>>=
character(*), parameter :: LHAPDF5_DEFAULT_PROTON = "cteq6ll.LHpdf"
character(*), parameter :: LHAPDF5_DEFAULT_PION = "ABFKWPI.LHgrid"
character(*), parameter :: LHAPDF5_DEFAULT_PHOTON = "GSG960.LHgrid"
character(*), parameter :: LHAPDF6_DEFAULT_PROTON = "CT10"
@ %def LHAPDF5_DEFAULT_PROTON LHAPDF5_DEFAULT_PION
@ %def LHAPDF5_DEFAULT_PHOTON LHAPDF6_DEFAULT_PROTON
@
\subsection{LHAPDF library interface}
Here we specify explicit interfaces for all LHAPDF routines that we
use below.
<<SF lhapdf: interfaces>>=
interface
subroutine InitPDFsetM (set, file)
integer, intent(in) :: set
character(*), intent(in) :: file
end subroutine InitPDFsetM
end interface
@ %def InitPDFsetM
<<SF lhapdf: interfaces>>=
interface
subroutine InitPDFM (set, mem)
integer, intent(in) :: set, mem
end subroutine InitPDFM
end interface
@ %def InitPDFM
<<SF lhapdf: interfaces>>=
interface
subroutine numberPDFM (set, n_members)
integer, intent(in) :: set
integer, intent(out) :: n_members
end subroutine numberPDFM
end interface
@ %def numberPDFM
<<SF lhapdf: interfaces>>=
interface
subroutine evolvePDFM (set, x, q, ff)
integer, intent(in) :: set
double precision, intent(in) :: x, q
double precision, dimension(-6:6), intent(out) :: ff
end subroutine evolvePDFM
end interface
@ %def evolvePDFM
<<SF lhapdf: interfaces>>=
interface
subroutine evolvePDFphotonM (set, x, q, ff, fphot)
integer, intent(in) :: set
double precision, intent(in) :: x, q
double precision, dimension(-6:6), intent(out) :: ff
double precision, intent(out) :: fphot
end subroutine evolvePDFphotonM
end interface
@ %def evolvePDFphotonM
<<SF lhapdf: interfaces>>=
interface
subroutine evolvePDFpM (set, x, q, s, scheme, ff)
integer, intent(in) :: set
double precision, intent(in) :: x, q, s
integer, intent(in) :: scheme
double precision, dimension(-6:6), intent(out) :: ff
end subroutine evolvePDFpM
end interface
@ %def evolvePDFpM
<<SF lhapdf: interfaces>>=
interface
subroutine GetXminM (set, mem, xmin)
integer, intent(in) :: set, mem
double precision, intent(out) :: xmin
end subroutine GetXminM
end interface
@ %def GetXminM
<<SF lhapdf: interfaces>>=
interface
subroutine GetXmaxM (set, mem, xmax)
integer, intent(in) :: set, mem
double precision, intent(out) :: xmax
end subroutine GetXmaxM
end interface
@ %def GetXmaxM
<<SF lhapdf: interfaces>>=
interface
subroutine GetQ2minM (set, mem, q2min)
integer, intent(in) :: set, mem
double precision, intent(out) :: q2min
end subroutine GetQ2minM
end interface
@ %def GetQ2minM
<<SF lhapdf: interfaces>>=
interface
subroutine GetQ2maxM (set, mem, q2max)
integer, intent(in) :: set, mem
double precision, intent(out) :: q2max
end subroutine GetQ2maxM
end interface
@ %def GetQ2maxM
<<SF lhapdf: interfaces>>=
interface
function has_photon () result(flag)
logical :: flag
end function has_photon
end interface
@ %def has_photon
@
\subsection{The LHAPDF status}
This type holds the initialization status of the LHAPDF system. Entry
1 is for proton PDFs, entry 2 for pion PDFs, entry 3 for photon PDFs.
Since it is connected to the external LHAPDF library, this is a truly
global object. We implement it as a a private module variable. To
access it from elsewhere, the caller has to create and initialize an
object of type [[lhapdf_status_t]], which acts as a proxy.
<<SF lhapdf: types>>=
type :: lhapdf_global_status_t
private
logical, dimension(3) :: initialized = .false.
end type lhapdf_global_status_t
@ %def lhapdf_global_status_t
<<SF lhapdf: variables>>=
type(lhapdf_global_status_t), save :: lhapdf_global_status
@ %def lhapdf_global_status
<<SF lhapdf: procedures>>=
function lhapdf_global_status_is_initialized (set) result (flag)
logical :: flag
integer, intent(in), optional :: set
if (present (set)) then
select case (set)
case (1:3); flag = lhapdf_global_status%initialized(set)
case default; flag = .false.
end select
else
flag = any (lhapdf_global_status%initialized)
end if
end function lhapdf_global_status_is_initialized
@ %def lhapdf_global_status_is_initialized
<<SF lhapdf: procedures>>=
subroutine lhapdf_global_status_set_initialized (set)
integer, intent(in) :: set
lhapdf_global_status%initialized(set) = .true.
end subroutine lhapdf_global_status_set_initialized
@ %def lhapdf_global_status_set_initialized
@ This is the only public procedure, it tells the system to forget
about previous initialization, allowing for changing the chosen PDF
set. Note that such a feature works only if the global program flow
is serial, so no two distinct sets are accessed simultaneously. But
this applies to LHAPDF anyway.
<<SF lhapdf: public>>=
public :: lhapdf_global_reset
<<SF lhapdf: procedures>>=
subroutine lhapdf_global_reset ()
lhapdf_global_status%initialized = .false.
end subroutine lhapdf_global_reset
@ %def lhapdf_global_status_reset
@
\subsection{LHAPDF initialization}
Before using LHAPDF, we have to initialize it with a particular data
set and member. This applies not just if we use structure functions,
but also if we just use an $\alpha_s$ formula. The integer [[set]]
should be $1$ for proton, $2$ for pion, and $3$ for photon, but this
is just convention.
It appears as if LHAPDF does not allow for multiple data sets being
used concurrently (?), so multi-threaded usage with different sets
(e.g., a scan) is excluded. The current setup with a global flag that
indicates initialization is fine as long as Whizard itself is run in
serial mode at the Sindarin level. If we introduce multithreading in
any form from Sindarin, we have to rethink the implementation of the
LHAPDF interface. (The same considerations apply to builtin PDFs.)
If the particular set has already been initialized, do nothing. This
implies that whenever we want to change the setup for a particular
set, we have to reset the LHAPDF status.
[[lhapdf_initialize]] has an obvious name clash with [[lhapdf_init]],
the reason it works for [[pdf_builtin]] is that there things are
outsourced to a separate module (inc. [[lhapdf_status]] etc.).
<<SF lhapdf: public>>=
public :: lhapdf_initialize
<<SF lhapdf: procedures>>=
subroutine lhapdf_initialize (set, prefix, file, member, pdf, b_match)
integer, intent(in) :: set
type(string_t), intent(inout) :: prefix
type(string_t), intent(inout) :: file
type(lhapdf_pdf_t), intent(inout), optional :: pdf
integer, intent(inout) :: member
logical, intent(in), optional :: b_match
if (prefix == "") prefix = LHAPDF_PDFSETS_PATH
if (LHAPDF5_AVAILABLE) then
if (lhapdf_global_status_is_initialized (set)) return
if (file == "") then
select case (set)
case (1); file = LHAPDF5_DEFAULT_PROTON
case (2); file = LHAPDF5_DEFAULT_PION
case (3); file = LHAPDF5_DEFAULT_PHOTON
end select
end if
if (data_file_exists (prefix // "/" // file)) then
call InitPDFsetM (set, char (prefix // "/" // file))
else
call msg_fatal ("LHAPDF: Data file '" &
// char (file) // "' not found in '" // char (prefix) // "'.")
return
end if
if (.not. dataset_member_exists (set, member)) then
call msg_error (" LHAPDF: Chosen member does not exist for set '" &
// char (file) // "', using default.")
member = 0
end if
call InitPDFM (set, member)
else if (LHAPDF6_AVAILABLE) then
! TODO: (bcn 2015-07-07) we should have a closer look why this global
! check must not be executed
! if (lhapdf_global_status_is_initialized (set) .and. &
! pdf%is_associated ()) return
if (file == "") then
select case (set)
case (1); file = LHAPDF6_DEFAULT_PROTON
case (2);
call msg_fatal ("LHAPDF6: no pion PDFs supported")
case (3);
call msg_fatal ("LHAPDF6: no photon PDFs supported")
end select
end if
if (data_file_exists (prefix // "/" // file // "/" // file // ".info")) then
call pdf%init (char (file), member)
else
call msg_fatal ("LHAPDF: Data file '" &
// char (file) // "' not found in '" // char (prefix) // "'.")
return
end if
end if
if (present (b_match)) then
if (b_match) then
if (LHAPDF5_AVAILABLE) then
call hoppet_init (.false.)
else if (LHAPDF6_AVAILABLE) then
call hoppet_init (.false., pdf)
end if
end if
end if
call lhapdf_global_status_set_initialized (set)
contains
function data_file_exists (fq_name) result (exist)
type(string_t), intent(in) :: fq_name
logical :: exist
inquire (file = char(fq_name), exist = exist)
end function data_file_exists
function dataset_member_exists (set, member) result (exist)
integer, intent(in) :: set, member
logical :: exist
integer :: n_members
call numberPDFM (set, n_members)
exist = member >= 0 .and. member <= n_members
end function dataset_member_exists
end subroutine lhapdf_initialize
@ %def lhapdf_initialize
@
\subsection{Kinematics}
Set kinematics. If [[map]] is unset, the $r$ and $x$ values
coincide, and the Jacobian $f(r)$ is trivial.
If [[map]] is set, we are asked to provide an efficient mapping.
For the test case, we set $x=r^2$ and consequently $f(r)=2r$.
<<SF lhapdf: lhapdf: TBP>>=
procedure :: complete_kinematics => lhapdf_complete_kinematics
<<SF lhapdf: procedures>>=
subroutine lhapdf_complete_kinematics (sf_int, x, xb, f, r, rb, map)
class(lhapdf_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(in) :: r
real(default), dimension(:), intent(in) :: rb
logical, intent(in) :: map
if (map) then
call msg_fatal ("LHAPDF: map flag not supported")
else
x(1) = r(1)
xb(1)= rb(1)
f = 1
end if
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_DONE_KINEMATICS)
sf_int%x = x(1)
case (SF_FAILED_KINEMATICS)
sf_int%x = 0
f = 0
end select
end subroutine lhapdf_complete_kinematics
@ %def lhapdf_complete_kinematics
@ Overriding the default method: we compute the [[x]] value from the
momentum configuration. In this specific case, we also set the
internally stored $x$ value, so it can be used in the
following routine.
<<SF lhapdf: lhapdf: TBP>>=
procedure :: recover_x => lhapdf_recover_x
<<SF lhapdf: procedures>>=
subroutine lhapdf_recover_x (sf_int, x, xb, x_free)
class(lhapdf_t), intent(inout) :: sf_int
real(default), dimension(:), intent(out) :: x
real(default), dimension(:), intent(out) :: xb
real(default), intent(inout), optional :: x_free
call sf_int%base_recover_x (x, xb, x_free)
sf_int%x = x(1)
end subroutine lhapdf_recover_x
@ %def lhapdf_recover_x
@ Compute inverse kinematics. Here, we start with the $x$ array and
compute the ``input'' $r$ values and the Jacobian $f$. After this, we
can set momenta by the same formula as for normal kinematics.
<<SF lhapdf: lhapdf: TBP>>=
procedure :: inverse_kinematics => lhapdf_inverse_kinematics
<<SF lhapdf: procedures>>=
subroutine lhapdf_inverse_kinematics (sf_int, x, xb, f, r, rb, map, set_momenta)
class(lhapdf_t), intent(inout) :: sf_int
real(default), dimension(:), intent(in) :: x
real(default), dimension(:), intent(in) :: xb
real(default), intent(out) :: f
real(default), dimension(:), intent(out) :: r
real(default), dimension(:), intent(out) :: rb
logical, intent(in) :: map
logical, intent(in), optional :: set_momenta
logical :: set_mom
set_mom = .false.; if (present (set_momenta)) set_mom = set_momenta
if (map) then
call msg_fatal ("LHAPDF: map flag not supported")
else
r(1) = x(1)
rb(1)= xb(1)
f = 1
end if
if (set_mom) then
call sf_int%split_momentum (x, xb)
select case (sf_int%status)
case (SF_FAILED_KINEMATICS); f = 0
end select
end if
end subroutine lhapdf_inverse_kinematics
@ %def lhapdf_inverse_kinematics
@
\subsection{The LHAPDF data block}
The data block holds the incoming flavor (which has to be proton,
pion, or photon), the corresponding pointer to the global access data
(1, 2, or 3), the flag [[invert]] which is set for an antiproton, the
bounds as returned by LHAPDF for the specified set, and a mask that
determines which partons will be actually in use.
<<SF lhapdf: public>>=
public :: lhapdf_data_t
<<SF lhapdf: types>>=
type, extends (sf_data_t) :: lhapdf_data_t
private
type(string_t) :: prefix
type(string_t) :: file
type(lhapdf_pdf_t) :: pdf
integer :: member = 0
class(model_data_t), pointer :: model => null ()
type(flavor_t) :: flv_in
integer :: set = 0
logical :: invert = .false.
logical :: photon = .false.
logical :: has_photon = .false.
integer :: photon_scheme = 0
real(default) :: xmin = 0, xmax = 0
real(default) :: qmin = 0, qmax = 0
logical, dimension(-6:6) :: mask = .true.
logical :: mask_photon = .true.
logical :: hoppet_b_matching = .false.
contains
<<SF lhapdf: lhapdf data: TBP>>
end type lhapdf_data_t
@ %def lhapdf_data_t
@ Generate PDF data. This is provided as a function, but it has the
side-effect of initializing the requested PDF set. A finalizer is not
needed.
The library uses double precision, so since the default precision may be
extended or quadruple, we use auxiliary variables for type casting.
<<SF lhapdf: lhapdf data: TBP>>=
procedure :: init => lhapdf_data_init
<<SF lhapdf: procedures>>=
subroutine lhapdf_data_init &
(data, model, pdg_in, prefix, file, member, photon_scheme, &
hoppet_b_matching)
class(lhapdf_data_t), intent(out) :: data
class(model_data_t), intent(in), target :: model
type(pdg_array_t), intent(in) :: pdg_in
type(string_t), intent(in), optional :: prefix, file
integer, intent(in), optional :: member
integer, intent(in), optional :: photon_scheme
logical, intent(in), optional :: hoppet_b_matching
double precision :: xmin, xmax, q2min, q2max
external :: InitPDFsetM, InitPDFM, numberPDFM
external :: GetXminM, GetXmaxM, GetQ2minM, GetQ2maxM
if (.not. LHAPDF5_AVAILABLE .and. .not. LHAPDF6_AVAILABLE) then
call msg_fatal ("LHAPDF requested but library is not linked")
return
end if
data%model => model
if (pdg_array_get_length (pdg_in) /= 1) &
call msg_fatal ("PDF: incoming particle must be unique")
call data%flv_in%init (pdg_array_get (pdg_in, 1), model)
select case (pdg_array_get (pdg_in, 1))
case (PROTON)
data%set = 1
case (-PROTON)
data%set = 1
data%invert = .true.
case (PIPLUS)
data%set = 2
case (-PIPLUS)
data%set = 2
data%invert = .true.
case (PHOTON)
data%set = 3
data%photon = .true.
if (present (photon_scheme)) data%photon_scheme = photon_scheme
case default
call msg_fatal (" LHAPDF: " &
// "incoming particle must be (anti)proton, pion, or photon.")
return
end select
if (present (prefix)) then
data%prefix = prefix
else
data%prefix = ""
end if
if (present (file)) then
data%file = file
else
data%file = ""
end if
if (present (hoppet_b_matching)) data%hoppet_b_matching = hoppet_b_matching
if (LHAPDF5_AVAILABLE) then
- call lhapdf_initialize &
- (data%set, data%prefix, data%file, data%member, &
+ call lhapdf_initialize (data%set, &
+ data%prefix, data%file, data%member, &
b_match = data%hoppet_b_matching)
call GetXminM (data%set, data%member, xmin)
call GetXmaxM (data%set, data%member, xmax)
call GetQ2minM (data%set, data%member, q2min)
call GetQ2maxM (data%set, data%member, q2max)
data%xmin = xmin
data%xmax = xmax
data%qmin = sqrt (q2min)
data%qmax = sqrt (q2max)
data%has_photon = has_photon ()
else if (LHAPDF6_AVAILABLE) then
- call lhapdf_initialize &
- (data%set, data%prefix, data%file, data%member, &
+ call lhapdf_initialize (data%set, &
+ data%prefix, data%file, data%member, &
data%pdf, data%hoppet_b_matching)
data%xmin = data%pdf%getxmin ()
data%xmax = data%pdf%getxmax ()
data%qmin = sqrt(data%pdf%getq2min ())
data%qmax = sqrt(data%pdf%getq2max ())
data%has_photon = data%pdf%has_photon ()
end if
end subroutine lhapdf_data_init
@ %def lhapdf_data_init
@ Enable/disable partons explicitly. If a mask entry is true,
applying the PDF will generate the corresponding flavor on output.
<<LHAPDF: lhapdf data: TBP>>=
procedure :: set_mask => lhapdf_data_set_mask
<<LHAPDF: procedures>>=
subroutine lhapdf_data_set_mask (data, mask)
class(lhapdf_data_t), intent(inout) :: data
logical, dimension(-6:6), intent(in) :: mask
data%mask = mask
end subroutine lhapdf_data_set_mask
@ %def lhapdf_data_set_mask
@ Return the public part of the data set.
<<LHAPDF: public>>=
public :: lhapdf_data_get_public_info
<<LHAPDF: procedures>>=
subroutine lhapdf_data_get_public_info &
(data, lhapdf_dir, lhapdf_file, lhapdf_member)
type(lhapdf_data_t), intent(in) :: data
type(string_t), intent(out) :: lhapdf_dir, lhapdf_file
integer, intent(out) :: lhapdf_member
lhapdf_dir = data%prefix
lhapdf_file = data%file
lhapdf_member = data%member
end subroutine lhapdf_data_get_public_info
@ %def lhapdf_data_get_public_info
@ Return the number of the member of the data set.
<<LHAPDF: public>>=
public :: lhapdf_data_get_set
<<LHAPDF: procedures>>=
function lhapdf_data_get_set(data) result(set)
type(lhapdf_data_t), intent(in) :: data
integer :: set
set = data%set
end function lhapdf_data_get_set
@ %def lhapdf_data_get_set
@ Output
<<SF lhapdf: lhapdf data: TBP>>=
procedure :: write => lhapdf_data_write
<<SF lhapdf: procedures>>=
subroutine lhapdf_data_write (data, unit, verbose)
class(lhapdf_data_t), intent(in) :: data
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
logical :: verb
integer :: u
if (present (verbose)) then
verb = verbose
else
verb = .false.
end if
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A)") "LHAPDF data:"
if (data%set /= 0) then
write (u, "(3x,A)", advance="no") "flavor = "
call data%flv_in%write (u); write (u, *)
if (verb) then
write (u, "(3x,A,A)") " prefix = ", char (data%prefix)
else
write (u, "(3x,A,A)") " prefix = ", &
" <empty (non-verbose version)>"
end if
write (u, "(3x,A,A)") " file = ", char (data%file)
write (u, "(3x,A,I3)") " member = ", data%member
write (u, "(3x,A," // FMT_19 // ")") " x(min) = ", data%xmin
write (u, "(3x,A," // FMT_19 // ")") " x(max) = ", data%xmax
write (u, "(3x,A," // FMT_19 // ")") " Q(min) = ", data%qmin
write (u, "(3x,A," // FMT_19 // ")") " Q(max) = ", data%qmax
write (u, "(3x,A,L1)") " invert = ", data%invert
if (data%photon) write (u, "(3x,A,I3)") &
" IP2 (scheme) = ", data%photon_scheme
write (u, "(3x,A,6(1x,L1),1x,A,1x,L1,1x,A,6(1x,L1))") &
" mask = ", &
data%mask(-6:-1), "*", data%mask(0), "*", data%mask(1:6)
write (u, "(3x,A,L1)") " photon mask = ", data%mask_photon
if (data%set == 1) write (u, "(3x,A,L1)") &
" hoppet_b = ", data%hoppet_b_matching
else
write (u, "(3x,A)") "[undefined]"
end if
end subroutine lhapdf_data_write
@ %def lhapdf_data_write
@ The number of parameters is one. We do not generate transverse momentum.
<<SF lhapdf: lhapdf data: TBP>>=
procedure :: get_n_par => lhapdf_data_get_n_par
<<SF lhapdf: procedures>>=
function lhapdf_data_get_n_par (data) result (n)
class(lhapdf_data_t), intent(in) :: data
integer :: n
n = 1
end function lhapdf_data_get_n_par
@ %def lhapdf_data_get_n_par
@ Return the outgoing particle PDG codes. This is based on the mask.
<<SF lhapdf: lhapdf data: TBP>>=
procedure :: get_pdg_out => lhapdf_data_get_pdg_out
<<SF lhapdf: procedures>>=
subroutine lhapdf_data_get_pdg_out (data, pdg_out)
class(lhapdf_data_t), intent(in) :: data
type(pdg_array_t), dimension(:), intent(inout) :: pdg_out
integer, dimension(:), allocatable :: pdg1
integer :: n, np, i
n = count (data%mask)
np = 0; if (data%has_photon .and. data%mask_photon) np = 1
allocate (pdg1 (n + np))
pdg1(1:n) = pack ([(i, i = -6, 6)], data%mask)
if (np == 1) pdg1(n+np) = PHOTON
pdg_out(1) = pdg1
end subroutine lhapdf_data_get_pdg_out
@ %def lhapdf_data_get_pdg_out
@ Allocate the interaction record.
<<SF lhapdf: lhapdf data: TBP>>=
procedure :: allocate_sf_int => lhapdf_data_allocate_sf_int
<<SF lhapdf: procedures>>=
subroutine lhapdf_data_allocate_sf_int (data, sf_int)
class(lhapdf_data_t), intent(in) :: data
class(sf_int_t), intent(inout), allocatable :: sf_int
allocate (lhapdf_t :: sf_int)
end subroutine lhapdf_data_allocate_sf_int
@ %def lhapdf_data_allocate_sf_int
@ Return the numerical PDF set index.
<<SF lhapdf: lhapdf data: TBP>>=
procedure :: get_pdf_set => lhapdf_data_get_pdf_set
<<SF lhapdf: procedures>>=
elemental function lhapdf_data_get_pdf_set (data) result (pdf_set)
class(lhapdf_data_t), intent(in) :: data
integer :: pdf_set
pdf_set = data%set
end function lhapdf_data_get_pdf_set
@ %def lhapdf_data_get_pdf_set
@
\subsection{The LHAPDF object}
The [[lhapdf_t]] data type is a $1\to 2$ interaction which describes
the splitting of an (anti)proton into a parton and a beam remnant. We
stay in the strict forward-splitting limit, but allow some invariant
mass for the beam remnant such that the outgoing parton is exactly
massless. For a real event, we would replace this by a parton
cascade, where the outgoing partons have virtuality as dictated by
parton-shower kinematics, and transverse momentum is generated.
This is the LHAPDF object which holds input data together with the
interaction. We also store the $x$ momentum fraction and the scale,
since kinematics and function value are requested at different times.
The PDF application is a $1\to 2$ splitting process, where the
particles are ordered as (hadron, remnant, parton).
Polarization is ignored completely. The beam particle is colorless,
while partons and beam remnant carry color. The remnant gets a
special flavor code.
<<SF lhapdf: public>>=
public :: lhapdf_t
<<SF lhapdf: types>>=
type, extends (sf_int_t) :: lhapdf_t
type(lhapdf_data_t), pointer :: data => null ()
real(default) :: x = 0
real(default) :: q = 0
real(default) :: s = 0
contains
<<SF lhapdf: lhapdf: TBP>>
end type lhapdf_t
@ %def lhapdf_t
@ Type string: display the chosen PDF set.
<<SF lhapdf: lhapdf: TBP>>=
procedure :: type_string => lhapdf_type_string
<<SF lhapdf: procedures>>=
function lhapdf_type_string (object) result (string)
class(lhapdf_t), intent(in) :: object
type(string_t) :: string
if (associated (object%data)) then
string = "LHAPDF: " // object%data%file
else
string = "LHAPDF: [undefined]"
end if
end function lhapdf_type_string
@ %def lhapdf_type_string
@ Output. Call the interaction routine after displaying the configuration.
<<SF lhapdf: lhapdf: TBP>>=
procedure :: write => lhapdf_write
<<SF lhapdf: procedures>>=
subroutine lhapdf_write (object, unit, testflag)
class(lhapdf_t), intent(in) :: object
integer, intent(in), optional :: unit
logical, intent(in), optional :: testflag
integer :: u
u = given_output_unit (unit)
if (associated (object%data)) then
call object%data%write (u)
if (object%status >= SF_DONE_KINEMATICS) then
write (u, "(1x,A)") "SF parameters:"
write (u, "(3x,A," // FMT_17 // ")") "x =", object%x
if (object%status >= SF_FAILED_EVALUATION) then
write (u, "(3x,A," // FMT_17 // ")") "Q =", object%q
end if
end if
call object%base_write (u, testflag)
else
write (u, "(1x,A)") "LHAPDF data: [undefined]"
end if
end subroutine lhapdf_write
@ %def lhapdf_write
@ Initialize. We know that [[data]] will be of concrete type
[[sf_lhapdf_data_t]], but we have to cast this explicitly.
For this implementation, we set the incoming and outgoing masses equal
to the physical particle mass, but keep the radiated mass zero.
<<SF lhapdf: lhapdf: TBP>>=
procedure :: init => lhapdf_init
<<SF lhapdf: procedures>>=
subroutine lhapdf_init (sf_int, data)
class(lhapdf_t), intent(out) :: sf_int
class(sf_data_t), intent(in), target :: data
type(quantum_numbers_mask_t), dimension(3) :: mask
type(flavor_t) :: flv, flv_remnant
type(color_t) :: col0
type(quantum_numbers_t), dimension(3) :: qn
integer :: i
select type (data)
type is (lhapdf_data_t)
mask = quantum_numbers_mask (.false., .false., .true.)
call col0%init ()
call sf_int%base_init (mask, [0._default], [0._default], [0._default])
sf_int%data => data
do i = -6, 6
if (data%mask(i)) then
call qn(1)%init (data%flv_in, col = col0)
if (i == 0) then
call flv%init (GLUON, data%model)
call flv_remnant%init (HADRON_REMNANT_OCTET, data%model)
else
call flv%init (i, data%model)
call flv_remnant%init &
(sign (HADRON_REMNANT_TRIPLET, -i), data%model)
end if
call qn(2)%init ( &
flv = flv_remnant, col = color_from_flavor (flv_remnant, 1))
call qn(2)%tag_radiated ()
call qn(3)%init ( &
flv = flv, col = color_from_flavor (flv, 1, reverse=.true.))
call sf_int%add_state (qn)
end if
end do
if (data%has_photon .and. data%mask_photon) then
call flv%init (PHOTON, data%model)
call flv_remnant%init (HADRON_REMNANT_SINGLET, data%model)
call qn(2)%init (flv = flv_remnant, &
col = color_from_flavor (flv_remnant, 1))
call qn(2)%tag_radiated ()
call qn(3)%init (flv = flv, &
col = color_from_flavor (flv, 1, reverse=.true.))
call sf_int%add_state (qn)
end if
call sf_int%freeze ()
call sf_int%set_incoming ([1])
call sf_int%set_radiated ([2])
call sf_int%set_outgoing ([3])
sf_int%status = SF_INITIAL
end select
end subroutine lhapdf_init
@ %def lhapdf_init
@
\subsection{Structure function}
We have to cast the LHAPDF arguments to/from double precision (possibly
from/to extended/quadruple precision), if necessary. Furthermore,
some structure functions can yield negative results (sea quarks close
to $x=1$). We set these unphysical values to zero.
<<SF lhapdf: lhapdf: TBP>>=
procedure :: apply => lhapdf_apply
<<SF lhapdf: procedures>>=
- subroutine lhapdf_apply (sf_int, scale, rescale, i_sub, fill_sub)
+ subroutine lhapdf_apply (sf_int, scale, rescale, i_sub)
class(lhapdf_t), intent(inout) :: sf_int
real(default), intent(in) :: scale
class(sf_rescale_t), intent(in), optional :: rescale
integer, intent(in), optional :: i_sub
- logical, intent(in), optional :: fill_sub
real(default) :: x, s
double precision :: xx, qq, ss
double precision, dimension(-6:6) :: ff
double precision :: fphot
complex(default), dimension(:), allocatable :: fc
integer :: i, i_sub_opt, j_sub
- logical :: fill_sub_opt
external :: evolvePDFM, evolvePDFpM
i_sub_opt = 0; if (present (i_sub)) i_sub_opt = i_sub
- fill_sub_opt = .false.; if (present (fill_sub)) fill_sub_opt = fill_sub
- if (present (rescale) .and. fill_sub_opt) then
- call msg_bug ("[lhapdf_apply] &
- & sf_rescale and fill_sub option are mutually exclusive.")
- end if
- if (i_sub_opt > 0 .and. fill_sub_opt) then
- call msg_bug ("[lhapdf_apply] &
- & i_sub and fill_sub options are mutually exclusive.")
- end if
associate (data => sf_int%data)
sf_int%q = scale
x = sf_int%x
if (present (rescale)) call rescale%apply (x)
s = sf_int%s
xx = x
if (debug2_active (D_BEAMS)) then
call msg_debug2 (D_BEAMS, "lhapdf_apply")
call msg_debug2 (D_BEAMS, "rescale: ", present(rescale))
call msg_debug2 (D_BEAMS, "i_sub: ", i_sub_opt)
- call msg_debug2 (D_BEAMS, "fill_sub: ", fill_sub_opt)
call msg_debug2 (D_BEAMS, "x: ", x)
end if
qq = min (data%qmax, scale)
qq = max (data%qmin, qq)
- if (.not. data% photon) then
+ if (.not. data%photon) then
if (data%invert) then
if (data%has_photon) then
if (LHAPDF5_AVAILABLE) then
call evolvePDFphotonM &
- (data% set, xx, qq, ff(6:-6:-1), fphot)
+ (data%set, xx, qq, ff(6:-6:-1), fphot)
else if (LHAPDF6_AVAILABLE) then
call data%pdf%evolve_pdfphotonm &
(xx, qq, ff(6:-6:-1), fphot)
end if
else
if (data%hoppet_b_matching) then
call hoppet_eval (xx, qq, ff(6:-6:-1))
else
if (LHAPDF5_AVAILABLE) then
- call evolvePDFM (data% set, xx, qq, ff(6:-6:-1))
+ call evolvePDFM (data%set, xx, qq, ff(6:-6:-1))
else if (LHAPDF6_AVAILABLE) then
call data%pdf%evolve_pdfm (xx, qq, ff(6:-6:-1))
end if
end if
end if
else
if (data%has_photon) then
if (LHAPDF5_AVAILABLE) then
- call evolvePDFphotonM (data% set, xx, qq, ff, fphot)
+ call evolvePDFphotonM (data%set, xx, qq, ff, fphot)
else if (LHAPDF6_AVAILABLE) then
call data%pdf%evolve_pdfphotonm (xx, qq, ff, fphot)
end if
else
if (data%hoppet_b_matching) then
call hoppet_eval (xx, qq, ff)
else
if (LHAPDF5_AVAILABLE) then
- call evolvePDFM (data% set, xx, qq, ff)
+ call evolvePDFM (data%set, xx, qq, ff)
else if (LHAPDF6_AVAILABLE) then
call data%pdf%evolve_pdfm (xx, qq, ff)
end if
end if
end if
end if
else
ss = s
if (LHAPDF5_AVAILABLE) then
- call evolvePDFpM (data% set, xx, qq, &
- ss, data% photon_scheme, ff)
+ call evolvePDFpM (data%set, xx, qq, &
+ ss, data%photon_scheme, ff)
else if (LHAPDF6_AVAILABLE) then
call data%pdf%evolve_pdfpm (xx, qq, ss, &
data%photon_scheme, ff)
end if
end if
if (data%has_photon) then
allocate (fc (count ([data%mask, data%mask_photon])))
fc = max (pack ([ff, fphot] / x, &
- [data% mask, data%mask_photon]), 0._default)
+ [data%mask, data%mask_photon]), 0._default)
else
allocate (fc (count (data%mask)))
fc = max (pack (ff / x, data%mask), 0._default)
end if
end associate
if (debug_active (D_BEAMS)) print *, 'Set pdfs: ', real (fc)
- if (present (rescale) .and. i_sub_opt > 0) then
- call sf_int%set_matrix_element (fc, [(i_sub_opt * size(fc) + i, i = 1, size(fc))])
- if (rescale%has_gluons ()) then
- j_sub = i_sub_opt + n_beam_gluon_offset
- call sf_int%set_matrix_element (&
- spread (fc(7), 1, size(fc)), [(j_sub * size(fc) + i, i = 1, size(fc))])
- end if
- else
- call sf_int%set_matrix_element (fc, [(i, i = 1, size(fc))])
- end if
- if(fill_sub_opt) then
- do j_sub = 1, sf_int%get_n_sub ()
- call sf_int%set_matrix_element (fc, [(j_sub * size(fc) + i, i = 1, size(fc))])
- end do
- end if
+ call sf_int%set_matrix_element (fc, [(i_sub_opt * size(fc) + i, i = 1, size(fc))])
sf_int%status = SF_EVALUATED
end subroutine lhapdf_apply
@ %def apply_lhapdf
@
\subsection{Strong Coupling}
Since the PDF codes provide a function for computing the running
$\alpha_s$ value, we make this available as an implementation of the
abstract [[alpha_qcd_t]] type, which is used for matrix element evaluation.
<<SF lhapdf: public>>=
public :: alpha_qcd_lhapdf_t
<<SF lhapdf: types>>=
type, extends (alpha_qcd_t) :: alpha_qcd_lhapdf_t
type(string_t) :: pdfset_dir
type(string_t) :: pdfset_file
integer :: pdfset_member = -1
type(lhapdf_pdf_t) :: pdf
contains
<<SF lhapdf: alpha qcd: TBP>>
end type alpha_qcd_lhapdf_t
@ %def alpha_qcd_lhapdf_t
@ Output. As in earlier versions we leave the LHAPDF path out.
<<SF lhapdf: alpha qcd: TBP>>=
procedure :: write => alpha_qcd_lhapdf_write
<<SF lhapdf: procedures>>=
subroutine alpha_qcd_lhapdf_write (object, unit)
class(alpha_qcd_lhapdf_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(3x,A)") "QCD parameters (lhapdf):"
write (u, "(5x,A,A)") "PDF set = ", char (object%pdfset_file)
write (u, "(5x,A,I0)") "PDF member = ", object%pdfset_member
end subroutine alpha_qcd_lhapdf_write
@ %def alpha_qcd_lhapdf_write
@ Calculation: the numeric member ID selects the correct PDF set, which must
be properly initialized.
<<SF lhapdf: interfaces>>=
interface
double precision function alphasPDF (Q)
double precision, intent(in) :: Q
end function alphasPDF
end interface
@ %def alphasPDF
@
<<SF lhapdf: alpha qcd: TBP>>=
procedure :: get => alpha_qcd_lhapdf_get
<<SF lhapdf: procedures>>=
function alpha_qcd_lhapdf_get (alpha_qcd, scale) result (alpha)
class(alpha_qcd_lhapdf_t), intent(in) :: alpha_qcd
real(default), intent(in) :: scale
real(default) :: alpha
if (LHAPDF5_AVAILABLE) then
alpha = alphasPDF (dble (scale))
else if (LHAPDF6_AVAILABLE) then
alpha = alpha_qcd%pdf%alphas_pdf (dble (scale))
end if
end function alpha_qcd_lhapdf_get
@ %def alpha_qcd_lhapdf_get
@
Initialization. We need to access the (quasi-global) initialization status.
<<SF lhapdf: alpha qcd: TBP>>=
procedure :: init => alpha_qcd_lhapdf_init
<<SF lhapdf: procedures>>=
subroutine alpha_qcd_lhapdf_init (alpha_qcd, file, member, path)
class(alpha_qcd_lhapdf_t), intent(out) :: alpha_qcd
type(string_t), intent(inout) :: file
integer, intent(inout) :: member
type(string_t), intent(inout) :: path
alpha_qcd%pdfset_file = file
alpha_qcd%pdfset_member = member
if (alpha_qcd%pdfset_member < 0) &
call msg_fatal ("QCD parameter initialization: PDF set " &
// char (file) // " is unknown")
if (LHAPDF5_AVAILABLE) then
call lhapdf_initialize (1, path, file, member)
else if (LHAPDF6_AVAILABLE) then
call lhapdf_initialize &
(1, path, file, member, alpha_qcd%pdf)
end if
end subroutine alpha_qcd_lhapdf_init
@ %def alpha_qcd_lhapdf_init
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[sf_lhapdf_ut.f90]]>>=
<<File header>>
module sf_lhapdf_ut
use unit_tests
use system_dependencies, only: LHAPDF5_AVAILABLE
use system_dependencies, only: LHAPDF6_AVAILABLE
use sf_lhapdf_uti
<<Standard module head>>
<<SF lhapdf: public test>>
contains
<<SF lhapdf: test driver>>
end module sf_lhapdf_ut
@ %def sf_lhapdf_ut
@
<<[[sf_lhapdf_uti.f90]]>>=
<<File header>>
module sf_lhapdf_uti
<<Use kinds>>
<<Use strings>>
use system_dependencies, only: LHAPDF5_AVAILABLE
use system_dependencies, only: LHAPDF6_AVAILABLE
use os_interface
use physics_defs, only: PROTON
use sm_qcd
use lorentz
use pdg_arrays
use flavors
use interactions, only: reset_interaction_counter
use model_data
use sf_base
use sf_lhapdf
<<Standard module head>>
<<SF lhapdf: test declarations>>
contains
<<SF lhapdf: tests>>
end module sf_lhapdf_uti
@ %def sf_lhapdf_ut
@ API: driver for the unit tests below.
<<SF lhapdf: public test>>=
public :: sf_lhapdf_test
<<SF lhapdf: test driver>>=
subroutine sf_lhapdf_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<SF lhapdf: execute tests>>
end subroutine sf_lhapdf_test
@ %def sf_lhapdf_test
@
\subsubsection{Test structure function data}
Construct and display a test structure function data object.
<<SF lhapdf: execute tests>>=
if (LHAPDF5_AVAILABLE) then
call test (sf_lhapdf_1, "sf_lhapdf5_1", &
"structure function configuration", &
u, results)
else if (LHAPDF6_AVAILABLE) then
call test (sf_lhapdf_1, "sf_lhapdf6_1", &
"structure function configuration", &
u, results)
end if
<<SF lhapdf: test declarations>>=
public :: sf_lhapdf_1
<<SF lhapdf: tests>>=
subroutine sf_lhapdf_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(pdg_array_t) :: pdg_in
type(pdg_array_t), dimension(1) :: pdg_out
integer, dimension(:), allocatable :: pdg1
class(sf_data_t), allocatable :: data
write (u, "(A)") "* Test output: sf_lhapdf_1"
write (u, "(A)") "* Purpose: initialize and display &
&test structure function data"
write (u, "(A)")
write (u, "(A)") "* Create empty data object"
write (u, "(A)")
call model%init_sm_test ()
pdg_in = PROTON
allocate (lhapdf_data_t :: data)
call data%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize"
write (u, "(A)")
select type (data)
type is (lhapdf_data_t)
call data%init (model, pdg_in)
end select
call data%write (u)
write (u, "(A)")
write (u, "(1x,A)") "Outgoing particle codes:"
call data%get_pdg_out (pdg_out)
pdg1 = pdg_out(1)
write (u, "(2x,99(1x,I0))") pdg1
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_lhapdf_1"
end subroutine sf_lhapdf_1
@ %def sf_lhapdf_1
@
\subsubsection{Test and probe structure function}
Construct and display a structure function object based on the PDF builtin
structure function.
<<SF lhapdf: execute tests>>=
if (LHAPDF5_AVAILABLE) then
call test (sf_lhapdf_2, "sf_lhapdf5_2", &
"structure function instance", &
u, results)
else if (LHAPDF6_AVAILABLE) then
call test (sf_lhapdf_2, "sf_lhapdf6_2", &
"structure function instance", &
u, results)
end if
<<SF lhapdf: test declarations>>=
public :: sf_lhapdf_2
<<SF lhapdf: tests>>=
subroutine sf_lhapdf_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(flavor_t) :: flv
type(pdg_array_t) :: pdg_in
class(sf_data_t), allocatable, target :: data
class(sf_int_t), allocatable :: sf_int
type(vector4_t) :: k
type(vector4_t), dimension(2) :: q
real(default) :: E
real(default), dimension(:), allocatable :: r, rb, x, xb
real(default) :: f
write (u, "(A)") "* Test output: sf_lhapdf_2"
write (u, "(A)") "* Purpose: initialize and fill &
&test structure function object"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call model%init_sm_test ()
call flv%init (PROTON, model)
pdg_in = PROTON
call lhapdf_global_reset ()
call reset_interaction_counter ()
allocate (lhapdf_data_t :: data)
select type (data)
type is (lhapdf_data_t)
call data%init (model, pdg_in)
end select
write (u, "(A)") "* Initialize structure-function object"
write (u, "(A)")
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%write (u)
write (u, "(A)")
write (u, "(A)") "* Initialize incoming momentum with E=500"
write (u, "(A)")
E = 500
k = vector4_moving (E, sqrt (E**2 - flv%get_mass ()**2), 3)
call vector4_write (k, u)
call sf_int%seed_kinematics ([k])
write (u, "(A)")
write (u, "(A)") "* Set kinematics for x=0.5"
write (u, "(A)")
allocate (r (data%get_n_par ()))
allocate (rb(size (r)))
allocate (x (size (r)))
allocate (xb(size (r)))
r = 0.5_default
rb = 1 - r
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%write (u)
write (u, "(A)")
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A,9(1x,F10.7))") "f =", f
write (u, "(A)")
write (u, "(A)") "* Recover x from momenta"
write (u, "(A)")
q = sf_int%get_momenta (outgoing=.true.)
call sf_int%final ()
deallocate (sf_int)
call data%allocate_sf_int (sf_int)
call sf_int%init (data)
call sf_int%set_beam_index ([1])
call sf_int%seed_kinematics ([k])
call sf_int%set_momenta (q, outgoing=.true.)
call sf_int%recover_x (x, xb)
write (u, "(A,9(1x,F10.7))") "x =", x
write (u, "(A,9(1x,F10.7))") "xb=", xb
write (u, "(A)")
write (u, "(A)") "* Evaluate for Q = 100 GeV"
write (u, "(A)")
call sf_int%complete_kinematics (x, xb, f, r, rb, map=.false.)
call sf_int%apply (scale = 100._default)
call sf_int%write (u, testflag = .true.)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call sf_int%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_lhapdf_2"
end subroutine sf_lhapdf_2
@ %def sf_lhapdf_2
@
\subsubsection{Strong Coupling}
Test $\alpha_s$ as an implementation of the [[alpha_qcd_t]] abstract
type.
<<SF lhapdf: execute tests>>=
if (LHAPDF5_AVAILABLE) then
call test (sf_lhapdf_3, "sf_lhapdf5_3", &
"running alpha_s", &
u, results)
else if (LHAPDF6_AVAILABLE) then
call test (sf_lhapdf_3, "sf_lhapdf6_3", &
"running alpha_s", &
u, results)
end if
<<SF lhapdf: test declarations>>=
public :: sf_lhapdf_3
<<SF lhapdf: tests>>=
subroutine sf_lhapdf_3 (u)
integer, intent(in) :: u
type(qcd_t) :: qcd
type(string_t) :: name, path
integer :: member
write (u, "(A)") "* Test output: sf_lhapdf_3"
write (u, "(A)") "* Purpose: initialize and evaluate alpha_s"
write (u, "(A)")
write (u, "(A)") "* Initialize configuration data"
write (u, "(A)")
call lhapdf_global_reset ()
if (LHAPDF5_AVAILABLE) then
name = "cteq6ll.LHpdf"
member = 1
path = ""
else if (LHAPDF6_AVAILABLE) then
name = "CT10"
member = 1
path = ""
end if
write (u, "(A)") "* Initialize qcd object"
write (u, "(A)")
allocate (alpha_qcd_lhapdf_t :: qcd%alpha)
select type (alpha => qcd%alpha)
type is (alpha_qcd_lhapdf_t)
call alpha%init (name, member, path)
end select
call qcd%write (u)
write (u, "(A)")
write (u, "(A)") "* Evaluate for Q = 100"
write (u, "(A)")
write (u, "(1x,A,F8.5)") "alpha = ", qcd%alpha%get (100._default)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
write (u, "(A)")
write (u, "(A)") "* Test output end: sf_lhapdf_3"
end subroutine sf_lhapdf_3
@ %def sf_lhapdf_3
@
\section{Easy PDF Access}
For the shower, subtraction and matching, it is very useful to have
direct access to $f(x,Q)$ independently of the used library.
<<[[pdf.f90]]>>=
<<File header>>
module pdf
<<Use kinds with double>>
use io_units
use system_dependencies, only: LHAPDF5_AVAILABLE, LHAPDF6_AVAILABLE
use diagnostics
use beam_structures
use lhapdf !NODEP!
use pdf_builtin !NODEP!
<<Standard module head>>
<<PDF: public>>
<<PDF: parameters>>
<<PDF: types>>
contains
<<PDF: procedures>>
end module pdf
@ %def pdf
We support the following implementations:
<<PDF: parameters>>=
integer, parameter, public :: STRF_NONE = 0
integer, parameter, public :: STRF_LHAPDF6 = 1
integer, parameter, public :: STRF_LHAPDF5 = 2
integer, parameter, public :: STRF_PDF_BUILTIN = 3
@ %def STRF_NONE STRF_LHAPDF6 STRF_LHAPDF5 STRF_PDF_BUILTIN
@ A container to bundle all necessary PDF data. Could be moved to a more
central location.
<<PDF: public>>=
public :: pdf_data_t
<<PDF: types>>=
type :: pdf_data_t
type(lhapdf_pdf_t) :: pdf
real(default) :: xmin, xmax, qmin, qmax
integer :: type = STRF_NONE
integer :: set = 0
contains
<<PDF: pdf data: TBP>>
end type pdf_data_t
@ %def pdf_data
@
<<PDF: pdf data: TBP>>=
procedure :: init => pdf_data_init
<<PDF: procedures>>=
subroutine pdf_data_init (pdf_data, pdf_data_in)
class(pdf_data_t), intent(out) :: pdf_data
type(pdf_data_t), target, intent(in) :: pdf_data_in
pdf_data%xmin = pdf_data_in%xmin
pdf_data%xmax = pdf_data_in%xmax
pdf_data%qmin = pdf_data_in%qmin
pdf_data%qmax = pdf_data_in%qmax
pdf_data%set = pdf_data_in%set
pdf_data%type = pdf_data_in%type
if (pdf_data%type == STRF_LHAPDF6) then
if (pdf_data_in%pdf%is_associated ()) then
call lhapdf_copy_pointer (pdf_data_in%pdf, pdf_data%pdf)
else
call msg_bug ('pdf_data_init: pdf_data%pdf was not associated!')
end if
end if
end subroutine pdf_data_init
@ %def pdf_data_init
@
<<PDF: pdf data: TBP>>=
procedure :: write => pdf_data_write
<<PDF: procedures>>=
subroutine pdf_data_write (pdf_data, unit)
class(pdf_data_t), intent(in) :: pdf_data
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(3x,A,I0)") "PDF set = ", pdf_data%set
write (u, "(3x,A,I0)") "PDF type = ", pdf_data%type
end subroutine pdf_data_write
@ %def pdf_data_write
@
<<PDF: pdf data: TBP>>=
procedure :: setup => pdf_data_setup
<<PDF: procedures>>=
subroutine pdf_data_setup (pdf_data, caller, beam_structure, lhapdf_member, set)
class(pdf_data_t), intent(inout) :: pdf_data
character(len=*), intent(in) :: caller
type(beam_structure_t), intent(in) :: beam_structure
integer, intent(in) :: lhapdf_member, set
real(default) :: xmin, xmax, q2min, q2max
pdf_data%set = set
if (beam_structure%contains ("lhapdf")) then
if (LHAPDF6_AVAILABLE) then
pdf_data%type = STRF_LHAPDF6
else if (LHAPDF5_AVAILABLE) then
pdf_data%type = STRF_LHAPDF5
end if
write (msg_buffer, "(A,I0)") caller &
// ": interfacing LHAPDF set #", pdf_data%set
call msg_message ()
else if (beam_structure%contains ("pdf_builtin")) then
pdf_data%type = STRF_PDF_BUILTIN
write (msg_buffer, "(A,I0)") caller &
// ": interfacing PDF builtin set #", pdf_data%set
call msg_message ()
end if
select case (pdf_data%type)
case (STRF_LHAPDF6)
pdf_data%xmin = pdf_data%pdf%getxmin ()
pdf_data%xmax = pdf_data%pdf%getxmax ()
pdf_data%qmin = sqrt(pdf_data%pdf%getq2min ())
pdf_data%qmax = sqrt(pdf_data%pdf%getq2max ())
case (STRF_LHAPDF5)
call GetXminM (1, lhapdf_member, xmin)
call GetXmaxM (1, lhapdf_member, xmax)
call GetQ2minM (1, lhapdf_member, q2min)
call GetQ2maxM (1, lhapdf_member, q2max)
pdf_data%xmin = xmin
pdf_data%xmax = xmax
pdf_data%qmin = sqrt(q2min)
pdf_data%qmax = sqrt(q2max)
end select
end subroutine pdf_data_setup
@ %def pdf_data_setup
@ This could be overloaded with a version that only asks for a specific
flavor as it is supported by LHAPDF6.
<<PDF: pdf data: TBP>>=
procedure :: evolve => pdf_data_evolve
<<PDF: procedures>>=
subroutine pdf_data_evolve (pdf_data, x, q_in, f)
class(pdf_data_t), intent(inout) :: pdf_data
real(double), intent(in) :: x, q_in
real(double), dimension(-6:6), intent(out) :: f
real(double) :: q
select case (pdf_data%type)
case (STRF_PDF_BUILTIN)
call pdf_evolve_LHAPDF (pdf_data%set, x, q_in, f)
case (STRF_LHAPDF6)
q = min (pdf_data%qmax, q_in)
q = max (pdf_data%qmin, q)
call pdf_data%pdf%evolve_pdfm (x, q, f)
case (STRF_LHAPDF5)
q = min (pdf_data%qmax, q_in)
q = max (pdf_data%qmin, q)
call evolvePDFM (pdf_data%set, x, q, f)
case default
call msg_fatal ("PDF function: unknown PDF method.")
end select
end subroutine pdf_data_evolve
@ %def pdf_data_evolve
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Dispatch}
@
<<[[dispatch_beams.f90]]>>=
<<File header>>
module dispatch_beams
<<Use kinds>>
<<Use strings>>
use diagnostics
use os_interface, only: os_data_t
use variables, only: var_list_t
use constants, only: PI
use numeric_utils, only: vanishes
use physics_defs, only: PHOTON
use rng_base, only: rng_factory_t
use pdg_arrays
use model_data, only: model_data_t
use dispatch_rng, only: dispatch_rng_factory
use dispatch_rng, only: update_rng_seed_in_var_list
use flavors, only: flavor_t
use sm_qcd, only: qcd_t, alpha_qcd_fixed_t, alpha_qcd_from_scale_t
use sm_qcd, only: alpha_qcd_from_lambda_t
use physics_defs, only: MZ_REF, ALPHA_QCD_MZ_REF
use beam_structures
use sf_base
use sf_mappings
use sf_isr
use sf_epa
use sf_ewa
use sf_escan
use sf_gaussian
use sf_beam_events
use sf_circe1
use sf_circe2
use sf_pdf_builtin
use sf_lhapdf
<<Standard module head>>
<<Dispatch beams: public>>
<<Dispatch beams: types>>
<<Dispatch beams: variables>>
contains
<<Dispatch beams: procedures>>
end module dispatch_beams
@ %def dispatch_beams
@ This data type is a container for transferring structure-function
specific data from the [[dispatch_sf_data]] to the
[[dispatch_sf_channels]] subroutine.
<<Dispatch beams: public>>=
public :: sf_prop_t
<<Dispatch beams: types>>=
type :: sf_prop_t
real(default), dimension(2) :: isr_eps = 1
end type sf_prop_t
@ %def sf_prop_t
@
Allocate a structure-function configuration object according to the
[[sf_method]] string.
The [[sf_prop]] object can be used to transfer structure-function
specific data up and to the [[dispatch_sf_channels]] subroutine below,
so they can be used for particular mappings.
The [[var_list_global]] object is used for the RNG generator seed.
It is intent(inout) because the RNG generator seed
may change during initialization.
The [[pdg_in]] array is the array of incoming flavors, corresponding
to the upstream structure function or the beam array. This will be
checked for the structure function in question and replaced by the
outgoing flavors. The [[pdg_prc]] array is the array of incoming
flavors (beam index, component index) for the hard process.
<<Dispatch beams: public>>=
public :: dispatch_sf_data
<<Dispatch beams: procedures>>=
subroutine dispatch_sf_data (data, sf_method, i_beam, sf_prop, &
var_list, var_list_global, model, &
os_data, sqrts, pdg_in, pdg_prc, polarized)
class(sf_data_t), allocatable, intent(inout) :: data
type(string_t), intent(in) :: sf_method
integer, dimension(:), intent(in) :: i_beam
type(pdg_array_t), dimension(:), intent(inout) :: pdg_in
type(pdg_array_t), dimension(:,:), intent(in) :: pdg_prc
type(sf_prop_t), intent(inout) :: sf_prop
type(var_list_t), intent(in) :: var_list
type(var_list_t), intent(inout) :: var_list_global
integer :: next_rng_seed
class(model_data_t), target, intent(in) :: model
type(os_data_t), intent(in) :: os_data
real(default), intent(in) :: sqrts
logical, intent(in) :: polarized
type(pdg_array_t), dimension(:), allocatable :: pdg_out
real(default) :: isr_alpha, isr_q_max, isr_mass
integer :: isr_order
logical :: isr_recoil, isr_keep_energy
real(default) :: epa_alpha, epa_x_min, epa_q_min, epa_e_max, epa_mass
logical :: epa_recoil, epa_keep_energy
real(default) :: ewa_x_min, ewa_pt_max, ewa_mass
logical :: ewa_recoil, ewa_keep_energy
type(pdg_array_t), dimension(:), allocatable :: pdg_prc1
integer :: ewa_id
type(string_t) :: pdf_name
type(string_t) :: lhapdf_dir, lhapdf_file
type(string_t), dimension(13) :: lhapdf_photon_sets
integer :: lhapdf_member, lhapdf_photon_scheme
logical :: hoppet_b_matching
class(rng_factory_t), allocatable :: rng_factory
logical :: circe1_photon1, circe1_photon2, circe1_generate, &
circe1_with_radiation
real(default) :: circe1_sqrts, circe1_eps
integer :: circe1_version, circe1_chattiness, &
circe1_revision
character(6) :: circe1_accelerator
logical :: circe2_polarized
type(string_t) :: circe2_design, circe2_file
real(default), dimension(2) :: gaussian_spread
logical :: beam_events_warn_eof
type(string_t) :: beam_events_dir, beam_events_file
logical :: escan_normalize
integer :: i
lhapdf_photon_sets = [var_str ("DOG0.LHgrid"), var_str ("DOG1.LHgrid"), &
var_str ("DGG.LHgrid"), var_str ("LACG.LHgrid"), &
var_str ("GSG0.LHgrid"), var_str ("GSG1.LHgrid"), &
var_str ("GSG960.LHgrid"), var_str ("GSG961.LHgrid"), &
var_str ("GRVG0.LHgrid"), var_str ("GRVG1.LHgrid"), &
var_str ("ACFGPG.LHgrid"), var_str ("WHITG.LHgrid"), &
var_str ("SASG.LHgrid")]
select case (char (sf_method))
case ("pdf_builtin")
allocate (pdf_builtin_data_t :: data)
select type (data)
type is (pdf_builtin_data_t)
pdf_name = &
var_list%get_sval (var_str ("$pdf_builtin_set"))
hoppet_b_matching = &
var_list%get_lval (var_str ("?hoppet_b_matching"))
call data%init ( &
model, pdg_in(i_beam(1)), &
name = pdf_name, &
path = os_data%pdf_builtin_datapath, &
hoppet_b_matching = hoppet_b_matching)
end select
case ("pdf_builtin_photon")
call msg_fatal ("Currently, there are no photon PDFs built into WHIZARD,", &
[var_str ("for the photon content inside a proton or neutron use"), &
var_str ("the 'lhapdf_photon' structure function.")])
case ("lhapdf")
allocate (lhapdf_data_t :: data)
if (pdg_array_get (pdg_in(i_beam(1)), 1) == PHOTON) then
call msg_fatal ("The 'lhapdf' structure is intended only for protons and", &
[var_str ("pions, please use 'lhapdf_photon' for photon beams.")])
end if
lhapdf_dir = &
var_list%get_sval (var_str ("$lhapdf_dir"))
lhapdf_file = &
var_list%get_sval (var_str ("$lhapdf_file"))
lhapdf_member = &
var_list%get_ival (var_str ("lhapdf_member"))
lhapdf_photon_scheme = &
var_list%get_ival (var_str ("lhapdf_photon_scheme"))
hoppet_b_matching = &
var_list%get_lval (var_str ("?hoppet_b_matching"))
select type (data)
type is (lhapdf_data_t)
call data%init &
(model, pdg_in(i_beam(1)), &
lhapdf_dir, lhapdf_file, lhapdf_member, &
lhapdf_photon_scheme, hoppet_b_matching)
end select
case ("lhapdf_photon")
allocate (lhapdf_data_t :: data)
if (pdg_array_get_length (pdg_in(i_beam(1))) /= 1 .or. &
pdg_array_get (pdg_in(i_beam(1)), 1) /= PHOTON) then
call msg_fatal ("The 'lhapdf_photon' structure function is exclusively for", &
[var_str ("photon PDFs, i.e. for photons as beam particles")])
end if
lhapdf_dir = &
var_list%get_sval (var_str ("$lhapdf_dir"))
lhapdf_file = &
var_list%get_sval (var_str ("$lhapdf_photon_file"))
lhapdf_member = &
var_list%get_ival (var_str ("lhapdf_member"))
lhapdf_photon_scheme = &
var_list%get_ival (var_str ("lhapdf_photon_scheme"))
if (.not. any (lhapdf_photon_sets == lhapdf_file)) then
call msg_fatal ("This PDF set is not supported or not " // &
"intended for photon beams.")
end if
select type (data)
type is (lhapdf_data_t)
call data%init &
(model, pdg_in(i_beam(1)), &
lhapdf_dir, lhapdf_file, lhapdf_member, &
lhapdf_photon_scheme)
end select
case ("isr")
allocate (isr_data_t :: data)
isr_alpha = &
var_list%get_rval (var_str ("isr_alpha"))
if (vanishes (isr_alpha)) then
isr_alpha = (var_list%get_rval (var_str ("ee"))) &
** 2 / (4 * PI)
end if
isr_q_max = &
var_list%get_rval (var_str ("isr_q_max"))
if (vanishes (isr_q_max)) then
isr_q_max = sqrts
end if
isr_mass = var_list%get_rval (var_str ("isr_mass"))
isr_order = var_list%get_ival (var_str ("isr_order"))
isr_recoil = var_list%get_lval (var_str ("?isr_recoil"))
isr_keep_energy = var_list%get_lval (var_str ("?isr_keep_energy"))
select type (data)
type is (isr_data_t)
call data%init &
(model, pdg_in (i_beam(1)), isr_alpha, isr_q_max, &
isr_mass, isr_order, recoil = isr_recoil, keep_energy = &
isr_keep_energy)
call data%check ()
sf_prop%isr_eps(i_beam(1)) = data%get_eps ()
end select
case ("epa")
allocate (epa_data_t :: data)
epa_alpha = var_list%get_rval (var_str ("epa_alpha"))
if (vanishes (epa_alpha)) then
epa_alpha = (var_list%get_rval (var_str ("ee"))) &
** 2 / (4 * PI)
end if
epa_x_min = var_list%get_rval (var_str ("epa_x_min"))
epa_q_min = var_list%get_rval (var_str ("epa_q_min"))
epa_e_max = var_list%get_rval (var_str ("epa_e_max"))
if (vanishes (epa_e_max)) then
epa_e_max = sqrts
end if
epa_mass = var_list%get_rval (var_str ("epa_mass"))
epa_recoil = var_list%get_lval (var_str ("?epa_recoil"))
epa_keep_energy = var_list%get_lval (var_str ("?epa_keep_energy"))
select type (data)
type is (epa_data_t)
call data%init &
(model, pdg_in (i_beam(1)), epa_alpha, epa_x_min, &
epa_q_min, epa_e_max, epa_mass, recoil = epa_recoil, &
keep_energy = epa_keep_energy)
call data%check ()
end select
case ("ewa")
allocate (ewa_data_t :: data)
allocate (pdg_prc1 (size (pdg_prc, 2)))
pdg_prc1 = pdg_prc(i_beam(1),:)
if (any (pdg_array_get_length (pdg_prc1) /= 1) &
.or. any (pdg_prc1 /= pdg_prc1(1))) then
call msg_fatal &
("EWA: process incoming particle (W/Z) must be unique")
end if
ewa_id = abs (pdg_array_get (pdg_prc1(1), 1))
ewa_x_min = var_list%get_rval (var_str ("ewa_x_min"))
ewa_pt_max = var_list%get_rval (var_str ("ewa_pt_max"))
if (vanishes (ewa_pt_max)) then
ewa_pt_max = sqrts
end if
ewa_mass = var_list%get_rval (var_str ("ewa_mass"))
ewa_recoil = var_list%get_lval (&
var_str ("?ewa_recoil"))
ewa_keep_energy = var_list%get_lval (&
var_str ("?ewa_keep_energy"))
select type (data)
type is (ewa_data_t)
call data%init &
(model, pdg_in (i_beam(1)), ewa_x_min, &
ewa_pt_max, sqrts, ewa_recoil, &
ewa_keep_energy, ewa_mass)
call data%set_id (ewa_id)
call data%check ()
end select
case ("circe1")
allocate (circe1_data_t :: data)
select type (data)
type is (circe1_data_t)
circe1_photon1 = &
var_list%get_lval (var_str ("?circe1_photon1"))
circe1_photon2 = &
var_list%get_lval (var_str ("?circe1_photon2"))
circe1_sqrts = &
var_list%get_rval (var_str ("circe1_sqrts"))
circe1_eps = &
var_list%get_rval (var_str ("circe1_eps"))
if (circe1_sqrts <= 0) circe1_sqrts = sqrts
circe1_generate = &
var_list%get_lval (var_str ("?circe1_generate"))
circe1_version = &
var_list%get_ival (var_str ("circe1_ver"))
circe1_revision = &
var_list%get_ival (var_str ("circe1_rev"))
circe1_accelerator = &
char (var_list%get_sval (var_str ("$circe1_acc")))
circe1_chattiness = &
var_list%get_ival (var_str ("circe1_chat"))
circe1_with_radiation = &
var_list%get_lval (var_str ("?circe1_with_radiation"))
call data%init (model, pdg_in, circe1_sqrts, circe1_eps, &
[circe1_photon1, circe1_photon2], &
circe1_version, circe1_revision, circe1_accelerator, &
circe1_chattiness, circe1_with_radiation)
if (circe1_generate) then
call msg_message ("CIRCE1: activating generator mode")
call dispatch_rng_factory &
(rng_factory, var_list_global, next_rng_seed)
call update_rng_seed_in_var_list (var_list_global, next_rng_seed)
call data%set_generator_mode (rng_factory)
end if
end select
case ("circe2")
allocate (circe2_data_t :: data)
select type (data)
type is (circe2_data_t)
circe2_polarized = &
var_list%get_lval (var_str ("?circe2_polarized"))
circe2_file = &
var_list%get_sval (var_str ("$circe2_file"))
circe2_design = &
var_list%get_sval (var_str ("$circe2_design"))
call data%init (os_data, model, pdg_in, sqrts, &
circe2_polarized, polarized, circe2_file, circe2_design)
call msg_message ("CIRCE2: activating generator mode")
call dispatch_rng_factory &
(rng_factory, var_list_global, next_rng_seed)
call update_rng_seed_in_var_list (var_list_global, next_rng_seed)
call data%set_generator_mode (rng_factory)
end select
case ("gaussian")
allocate (gaussian_data_t :: data)
select type (data)
type is (gaussian_data_t)
gaussian_spread = &
[var_list%get_rval (var_str ("gaussian_spread1")), &
var_list%get_rval (var_str ("gaussian_spread2"))]
call dispatch_rng_factory &
(rng_factory, var_list_global, next_rng_seed)
call update_rng_seed_in_var_list (var_list_global, next_rng_seed)
call data%init (model, pdg_in, gaussian_spread, rng_factory)
end select
case ("beam_events")
allocate (beam_events_data_t :: data)
select type (data)
type is (beam_events_data_t)
beam_events_dir = os_data%whizard_beamsimpath
beam_events_file = var_list%get_sval (&
var_str ("$beam_events_file"))
beam_events_warn_eof = var_list%get_lval (&
var_str ("?beam_events_warn_eof"))
call data%init (model, pdg_in, &
beam_events_dir, beam_events_file, beam_events_warn_eof)
end select
case ("energy_scan")
escan_normalize = &
var_list%get_lval (var_str ("?energy_scan_normalize"))
allocate (escan_data_t :: data)
select type (data)
type is (escan_data_t)
if (escan_normalize) then
call data%init (model, pdg_in)
else
call data%init (model, pdg_in, sqrts)
end if
end select
case default
if (associated (dispatch_sf_data_extra)) then
call dispatch_sf_data_extra (data, sf_method, i_beam, &
sf_prop, var_list, var_list_global, model, os_data, sqrts, pdg_in, &
pdg_prc, polarized)
end if
if (.not. allocated (data)) then
call msg_fatal ("Structure function '" &
// char (sf_method) // "' not implemented")
end if
end select
if (allocated (data)) then
allocate (pdg_out (size (pdg_prc, 1)))
call data%get_pdg_out (pdg_out)
do i = 1, size (i_beam)
pdg_in(i_beam(i)) = pdg_out(i)
end do
end if
end subroutine dispatch_sf_data
@ %def dispatch_sf_data
@ This is a hook that allows us to inject further handlers for
structure-function objects, in particular a test structure function.
<<Dispatch beams: public>>=
public :: dispatch_sf_data_extra
<<Dispatch beams: variables>>=
procedure (dispatch_sf_data), pointer :: &
dispatch_sf_data_extra => null ()
@ %def dispatch_sf_data_extra
@ This is an auxiliary procedure, used by the beam-structure
expansion: tell for a given structure function name, whether it
corresponds to a pair spectrum ($n=2$), a single-particle structure
function ($n=1$), or nothing ($n=0$). Though [[energy_scan]] can
in principle also be a pair spectrum, it always has only one
parameter.
<<Dispatch beams: public>>=
public :: strfun_mode
<<Dispatch beams: procedures>>=
function strfun_mode (name) result (n)
type(string_t), intent(in) :: name
integer :: n
select case (char (name))
case ("none")
n = 0
case ("sf_test_0", "sf_test_1")
n = 1
case ("pdf_builtin","pdf_builtin_photon", &
"lhapdf","lhapdf_photon")
n = 1
case ("isr","epa","ewa")
n = 1
case ("circe1", "circe2")
n = 2
case ("gaussian")
n = 2
case ("beam_events")
n = 2
case ("energy_scan")
n = 2
case default
n = -1
call msg_bug ("Structure function '" // char (name) &
// "' not supported yet")
end select
end function strfun_mode
@ %def strfun_mode
@ Dispatch a whole structure-function chain, given beam data and beam
structure data.
This could be done generically, but we should look at the specific
combination of structure functions in order to select appropriate mappings.
The [[beam_structure]] argument gets copied because
we want to expand it to canonical form (one valid structure-function
entry per record) before proceeding further.
The [[pdg_prc]] argument is the array of incoming flavors. The first
index is the beam index, the second one the process component index.
Each element is itself a PDG array, notrivial if there is a flavor sum
for the incoming state of this component.
The dispatcher is divided in two parts. The first part configures the
structure function data themselves. After this, we can configure the
phase space for the elementary process.
<<Dispatch beams: public>>=
public :: dispatch_sf_config
<<Dispatch beams: procedures>>=
subroutine dispatch_sf_config (sf_config, sf_prop, beam_structure, &
var_list, var_list_global, model, os_data, sqrts, pdg_prc)
type(sf_config_t), dimension(:), allocatable, intent(out) :: sf_config
type(sf_prop_t), intent(out) :: sf_prop
type(beam_structure_t), intent(inout) :: beam_structure
type(var_list_t), intent(in) :: var_list
type(var_list_t), intent(inout) :: var_list_global
class(model_data_t), target, intent(in) :: model
type(os_data_t), intent(in) :: os_data
real(default), intent(in) :: sqrts
class(sf_data_t), allocatable :: sf_data
type(beam_structure_t) :: beam_structure_tmp
type(pdg_array_t), dimension(:,:), intent(in) :: pdg_prc
type(string_t), dimension(:), allocatable :: prt_in
type(pdg_array_t), dimension(:), allocatable :: pdg_in
type(flavor_t) :: flv_in
integer :: n_beam, n_record, i
beam_structure_tmp = beam_structure
call beam_structure_tmp%expand (strfun_mode)
n_record = beam_structure_tmp%get_n_record ()
allocate (sf_config (n_record))
n_beam = beam_structure_tmp%get_n_beam ()
if (n_beam > 0) then
allocate (prt_in (n_beam), pdg_in (n_beam))
prt_in = beam_structure_tmp%get_prt ()
do i = 1, n_beam
call flv_in%init (prt_in(i), model)
pdg_in(i) = flv_in%get_pdg ()
end do
else
n_beam = size (pdg_prc, 1)
allocate (pdg_in (n_beam))
pdg_in = pdg_prc(:,1)
end if
do i = 1, n_record
call dispatch_sf_data (sf_data, &
beam_structure_tmp%get_name (i), &
beam_structure_tmp%get_i_entry (i), &
sf_prop, var_list, var_list_global, model, os_data, sqrts, &
pdg_in, pdg_prc, &
beam_structure_tmp%polarized ())
call sf_config(i)%init (beam_structure_tmp%get_i_entry (i), sf_data)
deallocate (sf_data)
end do
end subroutine dispatch_sf_config
@ %def dispatch_sf_config
@
\subsection{QCD coupling}
Allocate the [[alpha]] (running coupling) component of the [[qcd]] block with
a concrete implementation, depending on the variable settings in the
[[global]] record.
If a fixed $\alpha_s$ is requested, we do not allocate the
[[qcd%alpha]] object. In this case, the matrix element code will just take
the model parameter as-is, which implies fixed $\alpha_s$. If the
object is allocated, the $\alpha_s$ value is computed and updated for
each matrix-element call.
Also fetch the [[alphas_nf]] variable from the list and store it in
the QCD record. This is not used in the $\alpha_s$ calculation, but
the QCD record thus becomes a messenger for this user parameter.
<<Dispatch beams: public>>=
public :: dispatch_qcd
<<Dispatch beams: procedures>>=
subroutine dispatch_qcd (qcd, var_list, os_data)
type(qcd_t), intent(inout) :: qcd
type(var_list_t), intent(in) :: var_list
type(os_data_t), intent(in) :: os_data
logical :: fixed, from_mz, from_pdf_builtin, from_lhapdf, from_lambda_qcd
real(default) :: mz, alpha_val, lambda
integer :: nf, order, lhapdf_member
type(string_t) :: pdfset, lhapdf_dir, lhapdf_file
call unpack_variables ()
if (allocated (qcd%alpha)) deallocate (qcd%alpha)
if (from_lhapdf .and. from_pdf_builtin) then
call msg_fatal (" Mixing alphas evolution", &
[var_str (" from LHAPDF and builtin PDF is not permitted")])
end if
select case (count ([from_mz, from_pdf_builtin, from_lhapdf, from_lambda_qcd]))
case (0)
if (fixed) then
allocate (alpha_qcd_fixed_t :: qcd%alpha)
else
call msg_fatal ("QCD alpha: no calculation mode set")
end if
case (2:)
call msg_fatal ("QCD alpha: calculation mode is ambiguous")
case (1)
if (fixed) then
call msg_fatal ("QCD alpha: use '?alphas_is_fixed = false' for " // &
"running alphas")
else if (from_mz) then
allocate (alpha_qcd_from_scale_t :: qcd%alpha)
else if (from_pdf_builtin) then
allocate (alpha_qcd_pdf_builtin_t :: qcd%alpha)
else if (from_lhapdf) then
allocate (alpha_qcd_lhapdf_t :: qcd%alpha)
else if (from_lambda_qcd) then
allocate (alpha_qcd_from_lambda_t :: qcd%alpha)
end if
call msg_message ("QCD alpha: using a running strong coupling")
end select
call init_alpha ()
qcd%n_f = var_list%get_ival (var_str ("alphas_nf"))
contains
<<Dispatch qcd: dispatch qcd: procedures>>
end subroutine dispatch_qcd
@ %def dispatch_qcd
@
<<Dispatch qcd: dispatch qcd: procedures>>=
subroutine unpack_variables ()
fixed = var_list%get_lval (var_str ("?alphas_is_fixed"))
from_mz = var_list%get_lval (var_str ("?alphas_from_mz"))
from_pdf_builtin = &
var_list%get_lval (var_str ("?alphas_from_pdf_builtin"))
from_lhapdf = &
var_list%get_lval (var_str ("?alphas_from_lhapdf"))
from_lambda_qcd = &
var_list%get_lval (var_str ("?alphas_from_lambda_qcd"))
pdfset = var_list%get_sval (var_str ("$pdf_builtin_set"))
lambda = var_list%get_rval (var_str ("lambda_qcd"))
nf = var_list%get_ival (var_str ("alphas_nf"))
order = var_list%get_ival (var_str ("alphas_order"))
lhapdf_dir = var_list%get_sval (var_str ("$lhapdf_dir"))
lhapdf_file = var_list%get_sval (var_str ("$lhapdf_file"))
lhapdf_member = var_list%get_ival (var_str ("lhapdf_member"))
if (var_list%contains (var_str ("mZ"))) then
mz = var_list%get_rval (var_str ("mZ"))
else
mz = MZ_REF
end if
if (var_list%contains (var_str ("alphas"))) then
alpha_val = var_list%get_rval (var_str ("alphas"))
else
alpha_val = ALPHA_QCD_MZ_REF
end if
end subroutine unpack_variables
@
<<Dispatch qcd: dispatch qcd: procedures>>=
subroutine init_alpha ()
select type (alpha => qcd%alpha)
type is (alpha_qcd_fixed_t)
alpha%val = alpha_val
type is (alpha_qcd_from_scale_t)
alpha%mu_ref = mz
alpha%ref = alpha_val
alpha%order = order
alpha%nf = nf
type is (alpha_qcd_from_lambda_t)
alpha%lambda = lambda
alpha%order = order
alpha%nf = nf
type is (alpha_qcd_pdf_builtin_t)
call alpha%init (pdfset, &
os_data%pdf_builtin_datapath)
type is (alpha_qcd_lhapdf_t)
call alpha%init (lhapdf_file, lhapdf_member, lhapdf_dir)
end select
end subroutine init_alpha
@
Index: trunk/src/qft/qft.nw
===================================================================
--- trunk/src/qft/qft.nw (revision 8293)
+++ trunk/src/qft/qft.nw (revision 8294)
@@ -1,15427 +1,15512 @@
%% -*- ess-noweb-default-code-mode: f90-mode; noweb-default-code-mode: f90-mode; -*-
% WHIZARD code as NOWEB source: Quantum Field Theory concepts
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Quantum Field Theory Concepts}
\includemodulegraph{qft}
The objects and methods defined here implement concepts and data for
the underlying quantum field theory that we use for computing matrix
elements and processes.
\begin{description}
\item[model\_data]
Fields and coupling parameters, operators as vertex structures, for
a specific model.
\item[model\_testbed]
Provide hooks to deal with a [[model_data]] extension without
referencing it explicitly.
\item[helicities]
Types and methods for spin density matrices.
\item[colors]
Dealing with colored particles, using the color-flow representation.
\item[flavors]
PDG codes and particle properties, depends on the model.
\item[quantum\_numbers]
Quantum numbers and density matrices for entangled particle systems.
\end{description}
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Model Data}
These data represent a specific Lagrangian in numeric terms. That is,
we have the fields with their quantum numbers, the masses, widths and
couplings as numerical values, and the vertices as arrays of fields.
We do not store the relations between coupling parameters. They
should be represented by expressions for evaluation, implemented as
Sindarin objects in a distinct data structure. Neither do we need the
algebraic structure of vertices. The field content of vertices is
required for the sole purpose of setting up phase space.
<<[[model_data.f90]]>>=
<<File header>>
module model_data
use, intrinsic :: iso_c_binding !NODEP!
<<Use kinds>>
use kinds, only: i8, i32
use kinds, only: c_default_float
<<Use strings>>
use format_defs, only: FMT_19
use io_units
use diagnostics
use md5
use hashes, only: hash
use physics_defs, only: UNDEFINED, SCALAR
<<Standard module head>>
<<Model data: public>>
<<Model data: parameters>>
<<Model data: types>>
contains
<<Model data: procedures>>
end module model_data
@ %def model_data
@
\subsection{Physics Parameters}
Couplings, masses, and widths are physics parameters. Each parameter
has a unique name (used, essentially, for diagnostics output and
debugging) and a value. The value may be a real or a complex number,
so we provide to implementations of an abstract type.
<<Model data: public>>=
public :: modelpar_data_t
<<Model data: types>>=
type, abstract :: modelpar_data_t
private
type(string_t) :: name
contains
<<Model data: par data: TBP>>
end type modelpar_data_t
type, extends (modelpar_data_t) :: modelpar_real_t
private
real(default) :: value
end type modelpar_real_t
type, extends (modelpar_data_t) :: modelpar_complex_t
private
complex(default) :: value
end type modelpar_complex_t
@ %def modelpar_data_t modelpar_real_t modelpar_complex_t
@
Output for diagnostics. Non-advancing.
<<Model data: par data: TBP>>=
procedure :: write => par_write
<<Model data: procedures>>=
subroutine par_write (par, unit)
class(modelpar_data_t), intent(in) :: par
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit)
write (u, "(1x,A,1x,A)", advance="no") char (par%name), "= "
select type (par)
type is (modelpar_real_t)
write (u, "(" // FMT_19 // ")", advance="no") par%value
type is (modelpar_complex_t)
write (u, "(" // FMT_19 // ",1x,'+',1x," // FMT_19 // ",1x,'I')", &
advance="no") par%value
end select
end subroutine par_write
@ %def par_write
@
Pretty-printed on separate line, with fixed line length
<<Model data: par data: TBP>>=
procedure :: show => par_show
<<Model data: procedures>>=
subroutine par_show (par, l, u)
class(modelpar_data_t), intent(in) :: par
integer, intent(in) :: l, u
character(len=l) :: buffer
buffer = par%name
select type (par)
type is (modelpar_real_t)
write (u, "(4x,A,1x,'=',1x," // FMT_19 // ")") buffer, par%value
type is (modelpar_complex_t)
write (u, "(4x,A,1x,'=',1x," // FMT_19 // ",1x,'+',1x," &
// FMT_19 // ",1x,'I')") buffer, par%value
end select
end subroutine par_show
@ %def par_show
@
Initialize with name and value. The type depends on the argument
type. If the type does not match, the value is converted following
Fortran rules.
<<Model data: par data: TBP>>=
generic :: init => modelpar_data_init_real, modelpar_data_init_complex
procedure, private :: modelpar_data_init_real
procedure, private :: modelpar_data_init_complex
<<Model data: procedures>>=
subroutine modelpar_data_init_real (par, name, value)
class(modelpar_data_t), intent(out) :: par
type(string_t), intent(in) :: name
real(default), intent(in) :: value
par%name = name
par = value
end subroutine modelpar_data_init_real
subroutine modelpar_data_init_complex (par, name, value)
class(modelpar_data_t), intent(out) :: par
type(string_t), intent(in) :: name
complex(default), intent(in) :: value
par%name = name
par = value
end subroutine modelpar_data_init_complex
@ %def modelpar_data_init_real modelpar_data_init_complex
@
Modify the value. We assume that the parameter has been
initialized. The type (real or complex) must not be changed, and the
name is also fixed.
<<Model data: par data: TBP>>=
generic :: assignment(=) => modelpar_data_set_real, modelpar_data_set_complex
procedure, private :: modelpar_data_set_real
procedure, private :: modelpar_data_set_complex
<<Model data: procedures>>=
elemental subroutine modelpar_data_set_real (par, value)
class(modelpar_data_t), intent(inout) :: par
real(default), intent(in) :: value
select type (par)
type is (modelpar_real_t)
par%value = value
type is (modelpar_complex_t)
par%value = value
end select
end subroutine modelpar_data_set_real
elemental subroutine modelpar_data_set_complex (par, value)
class(modelpar_data_t), intent(inout) :: par
complex(default), intent(in) :: value
select type (par)
type is (modelpar_real_t)
par%value = value
type is (modelpar_complex_t)
par%value = value
end select
end subroutine modelpar_data_set_complex
@ %def modelpar_data_set_real modelpar_data_set_complex
@
Return the parameter name.
<<Model data: par data: TBP>>=
procedure :: get_name => modelpar_data_get_name
<<Model data: procedures>>=
function modelpar_data_get_name (par) result (name)
class(modelpar_data_t), intent(in) :: par
type(string_t) :: name
name = par%name
end function modelpar_data_get_name
@ %def modelpar_data_get_name
@
Return the value. In case of a type mismatch, follow Fortran conventions.
<<Model data: par data: TBP>>=
procedure, pass :: get_real => modelpar_data_get_real
procedure, pass :: get_complex => modelpar_data_get_complex
<<Model data: procedures>>=
elemental function modelpar_data_get_real (par) result (value)
class(modelpar_data_t), intent(in), target :: par
real(default) :: value
select type (par)
type is (modelpar_real_t)
value = par%value
type is (modelpar_complex_t)
value = par%value
end select
end function modelpar_data_get_real
elemental function modelpar_data_get_complex (par) result (value)
class(modelpar_data_t), intent(in), target :: par
complex(default) :: value
select type (par)
type is (modelpar_real_t)
value = par%value
type is (modelpar_complex_t)
value = par%value
end select
end function modelpar_data_get_complex
@ %def modelpar_data_get_real
@ %def modelpar_data_get_complex
@
Return a pointer to the value. This makes sense only for matching types.
<<Model data: par data: TBP>>=
procedure :: get_real_ptr => modelpar_data_get_real_ptr
procedure :: get_complex_ptr => modelpar_data_get_complex_ptr
<<Model data: procedures>>=
function modelpar_data_get_real_ptr (par) result (ptr)
class(modelpar_data_t), intent(in), target :: par
real(default), pointer :: ptr
select type (par)
type is (modelpar_real_t)
ptr => par%value
class default
ptr => null ()
end select
end function modelpar_data_get_real_ptr
function modelpar_data_get_complex_ptr (par) result (ptr)
class(modelpar_data_t), intent(in), target :: par
complex(default), pointer :: ptr
select type (par)
type is (modelpar_complex_t)
ptr => par%value
class default
ptr => null ()
end select
end function modelpar_data_get_complex_ptr
@ %def modelpar_data_get_real_ptr
@ %def modelpar_data_get_complex_ptr
@
\subsection{Field Data}
The field-data type holds all information that pertains to a particular field
(or particle) within a particular model. Information such as spin type,
particle code etc.\ is stored within the object itself, while mass and width
are associated to parameters, otherwise assumed zero.
<<Model data: public>>=
public :: field_data_t
<<Model data: types>>=
type :: field_data_t
private
type(string_t) :: longname
integer :: pdg = UNDEFINED
logical :: visible = .true.
logical :: parton = .false.
logical :: gauge = .false.
logical :: left_handed = .false.
logical :: right_handed = .false.
logical :: has_anti = .false.
logical :: p_is_stable = .true.
logical :: p_decays_isotropically = .false.
logical :: p_decays_diagonal = .false.
logical :: p_has_decay_helicity = .false.
integer :: p_decay_helicity = 0
logical :: a_is_stable = .true.
logical :: a_decays_isotropically = .false.
logical :: a_decays_diagonal = .false.
logical :: a_has_decay_helicity = .false.
integer :: a_decay_helicity = 0
logical :: p_polarized = .false.
logical :: a_polarized = .false.
type(string_t), dimension(:), allocatable :: name, anti
type(string_t) :: tex_name, tex_anti
integer :: spin_type = UNDEFINED
integer :: isospin_type = 1
integer :: charge_type = 1
integer :: color_type = 1
real(default), pointer :: mass_val => null ()
class(modelpar_data_t), pointer :: mass_data => null ()
real(default), pointer :: width_val => null ()
class(modelpar_data_t), pointer :: width_data => null ()
integer :: multiplicity = 1
type(string_t), dimension(:), allocatable :: p_decay
type(string_t), dimension(:), allocatable :: a_decay
contains
<<Model data: field data: TBP>>
end type field_data_t
@ %def field_data_t
@ Initialize field data with PDG long name and PDG code. \TeX\
names should be initialized to avoid issues with accessing
unallocated string contents.
<<Model data: field data: TBP>>=
procedure :: init => field_data_init
<<Model data: procedures>>=
subroutine field_data_init (prt, longname, pdg)
class(field_data_t), intent(out) :: prt
type(string_t), intent(in) :: longname
integer, intent(in) :: pdg
prt%longname = longname
prt%pdg = pdg
prt%tex_name = ""
prt%tex_anti = ""
end subroutine field_data_init
@ %def field_data_init
@ Copy quantum numbers from another particle. Do not compute the multiplicity
yet, because this depends on the association of the [[mass_data]] pointer.
<<Model data: field data: TBP>>=
procedure :: copy_from => field_data_copy_from
<<Model data: procedures>>=
subroutine field_data_copy_from (prt, prt_src)
class(field_data_t), intent(inout) :: prt
class(field_data_t), intent(in) :: prt_src
prt%visible = prt_src%visible
prt%parton = prt_src%parton
prt%gauge = prt_src%gauge
prt%left_handed = prt_src%left_handed
prt%right_handed = prt_src%right_handed
prt%p_is_stable = prt_src%p_is_stable
prt%p_decays_isotropically = prt_src%p_decays_isotropically
prt%p_decays_diagonal = prt_src%p_decays_diagonal
prt%p_has_decay_helicity = prt_src%p_has_decay_helicity
prt%p_decay_helicity = prt_src%p_decay_helicity
prt%p_decays_diagonal = prt_src%p_decays_diagonal
prt%a_is_stable = prt_src%a_is_stable
prt%a_decays_isotropically = prt_src%a_decays_isotropically
prt%a_decays_diagonal = prt_src%a_decays_diagonal
prt%a_has_decay_helicity = prt_src%a_has_decay_helicity
prt%a_decay_helicity = prt_src%a_decay_helicity
prt%p_polarized = prt_src%p_polarized
prt%a_polarized = prt_src%a_polarized
prt%spin_type = prt_src%spin_type
prt%isospin_type = prt_src%isospin_type
prt%charge_type = prt_src%charge_type
prt%color_type = prt_src%color_type
prt%has_anti = prt_src%has_anti
if (allocated (prt_src%name)) then
if (allocated (prt%name)) deallocate (prt%name)
allocate (prt%name (size (prt_src%name)), source = prt_src%name)
end if
if (allocated (prt_src%anti)) then
if (allocated (prt%anti)) deallocate (prt%anti)
allocate (prt%anti (size (prt_src%anti)), source = prt_src%anti)
end if
prt%tex_name = prt_src%tex_name
prt%tex_anti = prt_src%tex_anti
if (allocated (prt_src%p_decay)) then
if (allocated (prt%p_decay)) deallocate (prt%p_decay)
allocate (prt%p_decay (size (prt_src%p_decay)), source = prt_src%p_decay)
end if
if (allocated (prt_src%a_decay)) then
if (allocated (prt%a_decay)) deallocate (prt%a_decay)
allocate (prt%a_decay (size (prt_src%a_decay)), source = prt_src%a_decay)
end if
end subroutine field_data_copy_from
@ %def field_data_copy_from
@ Set particle quantum numbers.
<<Model data: field data: TBP>>=
procedure :: set => field_data_set
<<Model data: procedures>>=
subroutine field_data_set (prt, &
is_visible, is_parton, is_gauge, is_left_handed, is_right_handed, &
p_is_stable, p_decays_isotropically, p_decays_diagonal, &
p_decay_helicity, &
a_is_stable, a_decays_isotropically, a_decays_diagonal, &
a_decay_helicity, &
p_polarized, a_polarized, &
name, anti, tex_name, tex_anti, &
spin_type, isospin_type, charge_type, color_type, &
mass_data, width_data, &
p_decay, a_decay)
class(field_data_t), intent(inout) :: prt
logical, intent(in), optional :: is_visible, is_parton, is_gauge
logical, intent(in), optional :: is_left_handed, is_right_handed
logical, intent(in), optional :: p_is_stable
logical, intent(in), optional :: p_decays_isotropically, p_decays_diagonal
integer, intent(in), optional :: p_decay_helicity
logical, intent(in), optional :: a_is_stable
logical, intent(in), optional :: a_decays_isotropically, a_decays_diagonal
integer, intent(in), optional :: a_decay_helicity
logical, intent(in), optional :: p_polarized, a_polarized
type(string_t), dimension(:), intent(in), optional :: name, anti
type(string_t), intent(in), optional :: tex_name, tex_anti
integer, intent(in), optional :: spin_type, isospin_type
integer, intent(in), optional :: charge_type, color_type
class(modelpar_data_t), intent(in), pointer, optional :: mass_data, width_data
type(string_t), dimension(:), intent(in), optional :: p_decay, a_decay
if (present (is_visible)) prt%visible = is_visible
if (present (is_parton)) prt%parton = is_parton
if (present (is_gauge)) prt%gauge = is_gauge
if (present (is_left_handed)) prt%left_handed = is_left_handed
if (present (is_right_handed)) prt%right_handed = is_right_handed
if (present (p_is_stable)) prt%p_is_stable = p_is_stable
if (present (p_decays_isotropically)) &
prt%p_decays_isotropically = p_decays_isotropically
if (present (p_decays_diagonal)) &
prt%p_decays_diagonal = p_decays_diagonal
if (present (p_decay_helicity)) then
prt%p_has_decay_helicity = .true.
prt%p_decay_helicity = p_decay_helicity
end if
if (present (a_is_stable)) prt%a_is_stable = a_is_stable
if (present (a_decays_isotropically)) &
prt%a_decays_isotropically = a_decays_isotropically
if (present (a_decays_diagonal)) &
prt%a_decays_diagonal = a_decays_diagonal
if (present (a_decay_helicity)) then
prt%a_has_decay_helicity = .true.
prt%a_decay_helicity = a_decay_helicity
end if
if (present (p_polarized)) prt%p_polarized = p_polarized
if (present (a_polarized)) prt%a_polarized = a_polarized
if (present (name)) then
if (allocated (prt%name)) deallocate (prt%name)
allocate (prt%name (size (name)), source = name)
end if
if (present (anti)) then
if (allocated (prt%anti)) deallocate (prt%anti)
allocate (prt%anti (size (anti)), source = anti)
prt%has_anti = .true.
end if
if (present (tex_name)) prt%tex_name = tex_name
if (present (tex_anti)) prt%tex_anti = tex_anti
if (present (spin_type)) prt%spin_type = spin_type
if (present (isospin_type)) prt%isospin_type = isospin_type
if (present (charge_type)) prt%charge_type = charge_type
if (present (color_type)) prt%color_type = color_type
if (present (mass_data)) then
prt%mass_data => mass_data
if (associated (mass_data)) then
prt%mass_val => mass_data%get_real_ptr ()
else
prt%mass_val => null ()
end if
end if
if (present (width_data)) then
prt%width_data => width_data
if (associated (width_data)) then
prt%width_val => width_data%get_real_ptr ()
else
prt%width_val => null ()
end if
end if
if (present (spin_type) .or. present (mass_data)) then
call prt%set_multiplicity ()
end if
if (present (p_decay)) then
if (allocated (prt%p_decay)) deallocate (prt%p_decay)
if (size (p_decay) > 0) &
allocate (prt%p_decay (size (p_decay)), source = p_decay)
end if
if (present (a_decay)) then
if (allocated (prt%a_decay)) deallocate (prt%a_decay)
if (size (a_decay) > 0) &
allocate (prt%a_decay (size (a_decay)), source = a_decay)
end if
end subroutine field_data_set
@ %def field_data_set
@ Calculate the multiplicity given spin type and mass.
<<Model data: field data: TBP>>=
procedure, private :: &
set_multiplicity => field_data_set_multiplicity
<<Model data: procedures>>=
subroutine field_data_set_multiplicity (prt)
class(field_data_t), intent(inout) :: prt
if (prt%spin_type /= SCALAR) then
if (associated (prt%mass_data)) then
prt%multiplicity = prt%spin_type
else if (prt%left_handed .or. prt%right_handed) then
prt%multiplicity = 1
else
prt%multiplicity = 2
end if
end if
end subroutine field_data_set_multiplicity
@ %def field_data_set_multiplicity
@ Set the mass/width value (not the pointer). The mass/width pointer
must be allocated.
<<Model data: field data: TBP>>=
procedure, private :: set_mass => field_data_set_mass
procedure, private :: set_width => field_data_set_width
<<Model data: procedures>>=
subroutine field_data_set_mass (prt, mass)
class(field_data_t), intent(inout) :: prt
real(default), intent(in) :: mass
if (associated (prt%mass_val)) prt%mass_val = mass
end subroutine field_data_set_mass
subroutine field_data_set_width (prt, width)
class(field_data_t), intent(inout) :: prt
real(default), intent(in) :: width
if (associated (prt%width_val)) prt%width_val = width
end subroutine field_data_set_width
@ %def field_data_set_mass field_data_set_width
@ Loose ends: name arrays should be allocated.
<<Model data: field data: TBP>>=
procedure :: freeze => field_data_freeze
<<Model data: procedures>>=
elemental subroutine field_data_freeze (prt)
class(field_data_t), intent(inout) :: prt
if (.not. allocated (prt%name)) allocate (prt%name (0))
if (.not. allocated (prt%anti)) allocate (prt%anti (0))
end subroutine field_data_freeze
@ %def field_data_freeze
@ Output
<<Model data: field data: TBP>>=
procedure :: write => field_data_write
<<Model data: procedures>>=
subroutine field_data_write (prt, unit)
class(field_data_t), intent(in) :: prt
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
write (u, "(3x,A,1x,A)", advance="no") "particle", char (prt%longname)
write (u, "(1x,I0)", advance="no") prt%pdg
if (.not. prt%visible) write (u, "(2x,A)", advance="no") "invisible"
if (prt%parton) write (u, "(2x,A)", advance="no") "parton"
if (prt%gauge) write (u, "(2x,A)", advance="no") "gauge"
if (prt%left_handed) write (u, "(2x,A)", advance="no") "left"
if (prt%right_handed) write (u, "(2x,A)", advance="no") "right"
write (u, *)
write (u, "(5x,A)", advance="no") "name"
if (allocated (prt%name)) then
do i = 1, size (prt%name)
write (u, "(1x,A)", advance="no") '"' // char (prt%name(i)) // '"'
end do
write (u, *)
if (prt%has_anti) then
write (u, "(5x,A)", advance="no") "anti"
do i = 1, size (prt%anti)
write (u, "(1x,A)", advance="no") '"' // char (prt%anti(i)) // '"'
end do
write (u, *)
end if
if (prt%tex_name /= "") then
write (u, "(5x,A)") &
"tex_name " // '"' // char (prt%tex_name) // '"'
end if
if (prt%has_anti .and. prt%tex_anti /= "") then
write (u, "(5x,A)") &
"tex_anti " // '"' // char (prt%tex_anti) // '"'
end if
else
write (u, "(A)") "???"
end if
write (u, "(5x,A)", advance="no") "spin "
select case (mod (prt%spin_type - 1, 2))
case (0); write (u, "(I0)", advance="no") (prt%spin_type-1) / 2
case default; write (u, "(I0,A)", advance="no") prt%spin_type-1, "/2"
end select
! write (u, "(2x,A,I1,A)") "! [multiplicity = ", prt%multiplicity, "]"
if (abs (prt%isospin_type) /= 1) then
write (u, "(2x,A)", advance="no") "isospin "
select case (mod (abs (prt%isospin_type) - 1, 2))
case (0); write (u, "(I0)", advance="no") &
sign (abs (prt%isospin_type) - 1, prt%isospin_type) / 2
case default; write (u, "(I0,A)", advance="no") &
sign (abs (prt%isospin_type) - 1, prt%isospin_type), "/2"
end select
end if
if (abs (prt%charge_type) /= 1) then
write (u, "(2x,A)", advance="no") "charge "
select case (mod (abs (prt%charge_type) - 1, 3))
case (0); write (u, "(I0)", advance="no") &
sign (abs (prt%charge_type) - 1, prt%charge_type) / 3
case default; write (u, "(I0,A)", advance="no") &
sign (abs (prt%charge_type) - 1, prt%charge_type), "/3"
end select
end if
if (prt%color_type /= 1) then
write (u, "(2x,A,I0)", advance="no") "color ", prt%color_type
end if
write (u, *)
if (associated (prt%mass_data)) then
write (u, "(5x,A)", advance="no") &
"mass " // char (prt%mass_data%get_name ())
if (associated (prt%width_data)) then
write (u, "(2x,A)") &
"width " // char (prt%width_data%get_name ())
else
write (u, *)
end if
end if
call prt%write_decays (u)
end subroutine field_data_write
@ %def field_data_write
@ Write decay and polarization data.
<<Model data: field data: TBP>>=
procedure :: write_decays => field_data_write_decays
<<Model data: procedures>>=
subroutine field_data_write_decays (prt, unit)
class(field_data_t), intent(in) :: prt
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit)
if (.not. prt%p_is_stable) then
if (allocated (prt%p_decay)) then
write (u, "(5x,A)", advance="no") "p_decay"
do i = 1, size (prt%p_decay)
write (u, "(1x,A)", advance="no") char (prt%p_decay(i))
end do
if (prt%p_decays_isotropically) then
write (u, "(1x,A)", advance="no") "isotropic"
else if (prt%p_decays_diagonal) then
write (u, "(1x,A)", advance="no") "diagonal"
else if (prt%p_has_decay_helicity) then
write (u, "(1x,A,I0)", advance="no") "helicity = ", &
prt%p_decay_helicity
end if
write (u, *)
end if
else if (prt%p_polarized) then
write (u, "(5x,A)") "p_polarized"
end if
if (.not. prt%a_is_stable) then
if (allocated (prt%a_decay)) then
write (u, "(5x,A)", advance="no") "a_decay"
do i = 1, size (prt%a_decay)
write (u, "(1x,A)", advance="no") char (prt%a_decay(i))
end do
if (prt%a_decays_isotropically) then
write (u, "(1x,A)", advance="no") "isotropic"
else if (prt%a_decays_diagonal) then
write (u, "(1x,A)", advance="no") "diagonal"
else if (prt%a_has_decay_helicity) then
write (u, "(1x,A,I0)", advance="no") "helicity = ", &
prt%a_decay_helicity
end if
write (u, *)
end if
else if (prt%a_polarized) then
write (u, "(5x,A)") "a_polarized"
end if
end subroutine field_data_write_decays
@ %def field_data_write_decays
@ Screen version of output.
<<Model data: field data: TBP>>=
procedure :: show => field_data_show
<<Model data: procedures>>=
subroutine field_data_show (prt, l, u)
class(field_data_t), intent(in) :: prt
integer, intent(in) :: l, u
character(len=l) :: buffer
integer :: i
type(string_t), dimension(:), allocatable :: decay
buffer = prt%get_name (.false.)
write (u, "(4x,A,1x,I8)", advance="no") buffer, &
prt%get_pdg ()
if (prt%is_polarized ()) then
write (u, "(3x,A)") "polarized"
else if (.not. prt%is_stable ()) then
write (u, "(3x,A)", advance="no") "decays:"
call prt%get_decays (decay)
do i = 1, size (decay)
write (u, "(1x,A)", advance="no") char (decay(i))
end do
write (u, *)
else
write (u, *)
end if
if (prt%has_antiparticle ()) then
buffer = prt%get_name (.true.)
write (u, "(4x,A,1x,I8)", advance="no") buffer, &
prt%get_pdg_anti ()
if (prt%is_polarized (.true.)) then
write (u, "(3x,A)") "polarized"
else if (.not. prt%is_stable (.true.)) then
write (u, "(3x,A)", advance="no") "decays:"
call prt%get_decays (decay, .true.)
do i = 1, size (decay)
write (u, "(1x,A)", advance="no") char (decay(i))
end do
write (u, *)
else
write (u, *)
end if
end if
end subroutine field_data_show
@ %def field_data_show
@ Retrieve data:
<<Model data: field data: TBP>>=
procedure :: get_pdg => field_data_get_pdg
procedure :: get_pdg_anti => field_data_get_pdg_anti
<<Model data: procedures>>=
elemental function field_data_get_pdg (prt) result (pdg)
integer :: pdg
class(field_data_t), intent(in) :: prt
pdg = prt%pdg
end function field_data_get_pdg
elemental function field_data_get_pdg_anti (prt) result (pdg)
integer :: pdg
class(field_data_t), intent(in) :: prt
if (prt%has_anti) then
pdg = - prt%pdg
else
pdg = prt%pdg
end if
end function field_data_get_pdg_anti
@ %def field_data_get_pdg field_data_get_pdg_anti
@ Predicates:
<<Model data: field data: TBP>>=
procedure :: is_visible => field_data_is_visible
procedure :: is_parton => field_data_is_parton
procedure :: is_gauge => field_data_is_gauge
procedure :: is_left_handed => field_data_is_left_handed
procedure :: is_right_handed => field_data_is_right_handed
procedure :: has_antiparticle => field_data_has_antiparticle
procedure :: is_stable => field_data_is_stable
procedure :: get_decays => field_data_get_decays
procedure :: decays_isotropically => field_data_decays_isotropically
procedure :: decays_diagonal => field_data_decays_diagonal
procedure :: has_decay_helicity => field_data_has_decay_helicity
procedure :: decay_helicity => field_data_decay_helicity
procedure :: is_polarized => field_data_is_polarized
<<Model data: procedures>>=
elemental function field_data_is_visible (prt) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
flag = prt%visible
end function field_data_is_visible
elemental function field_data_is_parton (prt) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
flag = prt%parton
end function field_data_is_parton
elemental function field_data_is_gauge (prt) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
flag = prt%gauge
end function field_data_is_gauge
elemental function field_data_is_left_handed (prt) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
flag = prt%left_handed
end function field_data_is_left_handed
elemental function field_data_is_right_handed (prt) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
flag = prt%right_handed
end function field_data_is_right_handed
elemental function field_data_has_antiparticle (prt) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
flag = prt%has_anti
end function field_data_has_antiparticle
elemental function field_data_is_stable (prt, anti) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
logical, intent(in), optional :: anti
if (present (anti)) then
if (anti) then
flag = prt%a_is_stable
else
flag = prt%p_is_stable
end if
else
flag = prt%p_is_stable
end if
end function field_data_is_stable
subroutine field_data_get_decays (prt, decay, anti)
class(field_data_t), intent(in) :: prt
type(string_t), dimension(:), intent(out), allocatable :: decay
logical, intent(in), optional :: anti
if (present (anti)) then
if (anti) then
allocate (decay (size (prt%a_decay)), source = prt%a_decay)
else
allocate (decay (size (prt%p_decay)), source = prt%p_decay)
end if
else
allocate (decay (size (prt%p_decay)), source = prt%p_decay)
end if
end subroutine field_data_get_decays
elemental function field_data_decays_isotropically &
(prt, anti) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
logical, intent(in), optional :: anti
if (present (anti)) then
if (anti) then
flag = prt%a_decays_isotropically
else
flag = prt%p_decays_isotropically
end if
else
flag = prt%p_decays_isotropically
end if
end function field_data_decays_isotropically
elemental function field_data_decays_diagonal &
(prt, anti) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
logical, intent(in), optional :: anti
if (present (anti)) then
if (anti) then
flag = prt%a_decays_diagonal
else
flag = prt%p_decays_diagonal
end if
else
flag = prt%p_decays_diagonal
end if
end function field_data_decays_diagonal
elemental function field_data_has_decay_helicity &
(prt, anti) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
logical, intent(in), optional :: anti
if (present (anti)) then
if (anti) then
flag = prt%a_has_decay_helicity
else
flag = prt%p_has_decay_helicity
end if
else
flag = prt%p_has_decay_helicity
end if
end function field_data_has_decay_helicity
elemental function field_data_decay_helicity &
(prt, anti) result (hel)
integer :: hel
class(field_data_t), intent(in) :: prt
logical, intent(in), optional :: anti
if (present (anti)) then
if (anti) then
hel = prt%a_decay_helicity
else
hel = prt%p_decay_helicity
end if
else
hel = prt%p_decay_helicity
end if
end function field_data_decay_helicity
elemental function field_data_is_polarized (prt, anti) result (flag)
logical :: flag
class(field_data_t), intent(in) :: prt
logical, intent(in), optional :: anti
logical :: a
if (present (anti)) then
a = anti
else
a = .false.
end if
if (a) then
flag = prt%a_polarized
else
flag = prt%p_polarized
end if
end function field_data_is_polarized
@ %def field_data_is_visible field_data_is_parton
@ %def field_data_is_gauge
@ %def field_data_is_left_handed field_data_is_right_handed
@ %def field_data_has_antiparticle
@ %def field_data_is_stable
@ %def field_data_decays_isotropically
@ %def field_data_decays_diagonal
@ %def field_data_has_decay_helicity
@ %def field_data_decay_helicity
@ %def field_data_polarized
@ Names. Return the first name in the list (or the first antiparticle name)
<<Model data: field data: TBP>>=
procedure :: get_longname => field_data_get_longname
procedure :: get_name => field_data_get_name
procedure :: get_name_array => field_data_get_name_array
<<Model data: procedures>>=
pure function field_data_get_longname (prt) result (name)
type(string_t) :: name
class(field_data_t), intent(in) :: prt
name = prt%longname
end function field_data_get_longname
pure function field_data_get_name (prt, is_antiparticle) result (name)
type(string_t) :: name
class(field_data_t), intent(in) :: prt
logical, intent(in) :: is_antiparticle
name = prt%longname
if (is_antiparticle) then
if (prt%has_anti) then
if (allocated (prt%anti)) then
if (size(prt%anti) > 0) name = prt%anti(1)
end if
else
if (allocated (prt%name)) then
if (size (prt%name) > 0) name = prt%name(1)
end if
end if
else
if (allocated (prt%name)) then
if (size (prt%name) > 0) name = prt%name(1)
end if
end if
end function field_data_get_name
subroutine field_data_get_name_array (prt, is_antiparticle, name)
class(field_data_t), intent(in) :: prt
logical, intent(in) :: is_antiparticle
type(string_t), dimension(:), allocatable, intent(inout) :: name
if (allocated (name)) deallocate (name)
if (is_antiparticle) then
if (prt%has_anti) then
allocate (name (size (prt%anti)))
name = prt%anti
else
allocate (name (0))
end if
else
allocate (name (size (prt%name)))
name = prt%name
end if
end subroutine field_data_get_name_array
@ %def field_data_get_name
@ Same for the \TeX\ name.
<<Model data: field data: TBP>>=
procedure :: get_tex_name => field_data_get_tex_name
<<Model data: procedures>>=
elemental function field_data_get_tex_name &
(prt, is_antiparticle) result (name)
type(string_t) :: name
class(field_data_t), intent(in) :: prt
logical, intent(in) :: is_antiparticle
if (is_antiparticle) then
if (prt%has_anti) then
name = prt%tex_anti
else
name = prt%tex_name
end if
else
name = prt%tex_name
end if
if (name == "") name = prt%get_name (is_antiparticle)
end function field_data_get_tex_name
@ %def field_data_get_tex_name
@ Check if any of the field names matches the given string.
<<Model data: field data: TBP>>=
procedure, private :: matches_name => field_data_matches_name
<<Model data: procedures>>=
function field_data_matches_name (field, name, is_antiparticle) result (flag)
class(field_data_t), intent(in) :: field
type(string_t), intent(in) :: name
logical, intent(in) :: is_antiparticle
logical :: flag
if (is_antiparticle) then
if (field%has_anti) then
flag = any (name == field%anti)
else
flag = .false.
end if
else
flag = name == field%longname .or. any (name == field%name)
end if
end function field_data_matches_name
@ %def field_data_matches_name
@ Quantum numbers
<<Model data: field data: TBP>>=
procedure :: get_spin_type => field_data_get_spin_type
procedure :: get_multiplicity => field_data_get_multiplicity
procedure :: get_isospin_type => field_data_get_isospin_type
procedure :: get_charge_type => field_data_get_charge_type
procedure :: get_color_type => field_data_get_color_type
<<Model data: procedures>>=
elemental function field_data_get_spin_type (prt) result (type)
integer :: type
class(field_data_t), intent(in) :: prt
type = prt%spin_type
end function field_data_get_spin_type
elemental function field_data_get_multiplicity (prt) result (type)
integer :: type
class(field_data_t), intent(in) :: prt
type = prt%multiplicity
end function field_data_get_multiplicity
elemental function field_data_get_isospin_type (prt) result (type)
integer :: type
class(field_data_t), intent(in) :: prt
type = prt%isospin_type
end function field_data_get_isospin_type
elemental function field_data_get_charge_type (prt) result (type)
integer :: type
class(field_data_t), intent(in) :: prt
type = prt%charge_type
end function field_data_get_charge_type
elemental function field_data_get_color_type (prt) result (type)
integer :: type
class(field_data_t), intent(in) :: prt
type = prt%color_type
end function field_data_get_color_type
@ %def field_data_get_spin_type
@ %def field_data_get_multiplicity
@ %def field_data_get_isospin_type
@ %def field_data_get_charge_type
@ %def field_data_get_color_type
@ In the MSSM, neutralinos can have a negative mass. This is
relevant for computing matrix elements. However, within the
\whizard\ main program we are interested only in kinematics, therefore
we return the absolute value of the particle mass. If desired, we can
extract the sign separately.
<<Model data: field data: TBP>>=
procedure :: get_charge => field_data_get_charge
procedure :: get_isospin => field_data_get_isospin
procedure :: get_mass => field_data_get_mass
procedure :: get_mass_sign => field_data_get_mass_sign
procedure :: get_width => field_data_get_width
<<Model data: procedures>>=
elemental function field_data_get_charge (prt) result (charge)
real(default) :: charge
class(field_data_t), intent(in) :: prt
if (prt%charge_type /= 0) then
charge = real (sign ((abs(prt%charge_type) - 1), &
prt%charge_type), default) / 3
else
charge = 0
end if
end function field_data_get_charge
elemental function field_data_get_isospin (prt) result (isospin)
real(default) :: isospin
class(field_data_t), intent(in) :: prt
if (prt%isospin_type /= 0) then
isospin = real (sign (abs(prt%isospin_type) - 1, &
prt%isospin_type), default) / 2
else
isospin = 0
end if
end function field_data_get_isospin
elemental function field_data_get_mass (prt) result (mass)
real(default) :: mass
class(field_data_t), intent(in) :: prt
if (associated (prt%mass_val)) then
mass = abs (prt%mass_val)
else
mass = 0
end if
end function field_data_get_mass
elemental function field_data_get_mass_sign (prt) result (sgn)
integer :: sgn
class(field_data_t), intent(in) :: prt
if (associated (prt%mass_val)) then
sgn = sign (1._default, prt%mass_val)
else
sgn = 0
end if
end function field_data_get_mass_sign
elemental function field_data_get_width (prt) result (width)
real(default) :: width
class(field_data_t), intent(in) :: prt
if (associated (prt%width_val)) then
width = prt%width_val
else
width = 0
end if
end function field_data_get_width
@ %def field_data_get_charge field_data_get_isospin
@ %def field_data_get_mass field_data_get_mass_sign
@ %def field_data_get_width
@ Find the [[model]] containing the [[PDG]] given two model files.
<<Model data: public>>=
public :: find_model
<<Model data: procedures>>=
subroutine find_model (model, PDG, model_A, model_B)
class(model_data_t), pointer, intent(out) :: model
integer, intent(in) :: PDG
class(model_data_t), intent(in), target :: model_A, model_B
character(len=10) :: buffer
if (model_A%test_field (PDG)) then
model => model_A
else if (model_B%test_field (PDG)) then
model => model_B
else
call model_A%write ()
call model_B%write ()
write (buffer, "(I10)") PDG
call msg_fatal ("Parton " // buffer // &
" not found in the given model files")
end if
end subroutine find_model
@ %def find_model
@
\subsection{Vertex data}
The vertex object contains an array of particle-data pointers, for
which we need a separate type. (We could use the flavor type defined
in another module.)
The program does not (yet?) make use of vertex definitions, so they
are not stored here.
<<Model data: types>>=
type :: field_data_p
type(field_data_t), pointer :: p => null ()
end type field_data_p
@ %def field_data_p
<<Model data: types>>=
type :: vertex_t
private
logical :: trilinear
integer, dimension(:), allocatable :: pdg
type(field_data_p), dimension(:), allocatable :: prt
contains
<<Model data: vertex: TBP>>
end type vertex_t
@ %def vertex_t
<<Model data: vertex: TBP>>=
procedure :: write => vertex_write
<<Model data: procedures>>=
subroutine vertex_write (vtx, unit)
class(vertex_t), intent(in) :: vtx
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit)
write (u, "(3x,A)", advance="no") "vertex"
do i = 1, size (vtx%prt)
if (associated (vtx%prt(i)%p)) then
write (u, "(1x,A)", advance="no") &
'"' // char (vtx%prt(i)%p%get_name (vtx%pdg(i) < 0)) &
// '"'
else
write (u, "(1x,I7)", advance="no") vtx%pdg(i)
end if
end do
write (u, *)
end subroutine vertex_write
@ %def vertex_write
@ Initialize using PDG codes. The model is used for finding particle
data pointers associated with the pdg codes.
<<Model data: vertex: TBP>>=
procedure :: init => vertex_init
<<Model data: procedures>>=
subroutine vertex_init (vtx, pdg, model)
class(vertex_t), intent(out) :: vtx
integer, dimension(:), intent(in) :: pdg
type(model_data_t), intent(in), target, optional :: model
integer :: i
allocate (vtx%pdg (size (pdg)))
allocate (vtx%prt (size (pdg)))
vtx%trilinear = size (pdg) == 3
vtx%pdg = pdg
if (present (model)) then
do i = 1, size (pdg)
vtx%prt(i)%p => model%get_field_ptr (pdg(i))
end do
end if
end subroutine vertex_init
@ %def vertex_init
@ Copy vertex: we must reassign the field-data pointer to a new model.
<<Model data: vertex: TBP>>=
procedure :: copy_from => vertex_copy_from
<<Model data: procedures>>=
subroutine vertex_copy_from (vtx, old_vtx, new_model)
class(vertex_t), intent(out) :: vtx
class(vertex_t), intent(in) :: old_vtx
type(model_data_t), intent(in), target, optional :: new_model
call vtx%init (old_vtx%pdg, new_model)
end subroutine vertex_copy_from
@ %def vertex_copy_from
@ Single-particle lookup: Given a particle code, we return matching
codes if present, otherwise zero. Actually, we return the
antiparticles of the matching codes, as appropriate for computing
splittings.
<<Model data: vertex: TBP>>=
procedure :: get_match => vertex_get_match
<<Model data: procedures>>=
subroutine vertex_get_match (vtx, pdg1, pdg2)
class(vertex_t), intent(in) :: vtx
integer, intent(in) :: pdg1
integer, dimension(:), allocatable, intent(out) :: pdg2
integer :: i, j
do i = 1, size (vtx%pdg)
if (vtx%pdg(i) == pdg1) then
allocate (pdg2 (size (vtx%pdg) - 1))
do j = 1, i-1
pdg2(j) = anti (j)
end do
do j = i, size (pdg2)
pdg2(j) = anti (j+1)
end do
exit
end if
end do
contains
function anti (i) result (pdg)
integer, intent(in) :: i
integer :: pdg
if (vtx%prt(i)%p%has_antiparticle ()) then
pdg = - vtx%pdg(i)
else
pdg = vtx%pdg(i)
end if
end function anti
end subroutine vertex_get_match
@ %def vertex_get_match
@ To access this from the outside, we create an iterator. The iterator has
the sole purpose of returning the matching particles for a given array of PDG
codes.
<<Model data: public>>=
public :: vertex_iterator_t
<<Model data: types>>=
type :: vertex_iterator_t
private
class(model_data_t), pointer :: model => null ()
integer, dimension(:), allocatable :: pdg
integer :: vertex_index = 0
integer :: pdg_index = 0
logical :: save_pdg_index
contains
procedure :: init => vertex_iterator_init
procedure :: get_next_match => vertex_iterator_get_next_match
end type vertex_iterator_t
@ %def vertex_iterator_t
@ We initialize the iterator for a particular model with the [[pdg]] index of
the particle we are looking at.
<<Model data: procedures>>=
subroutine vertex_iterator_init (it, model, pdg, save_pdg_index)
class(vertex_iterator_t), intent(out) :: it
class(model_data_t), intent(in), target :: model
integer, dimension(:), intent(in) :: pdg
logical, intent(in) :: save_pdg_index
it%model => model
allocate (it%pdg (size (pdg)), source = pdg)
it%save_pdg_index = save_pdg_index
end subroutine vertex_iterator_init
subroutine vertex_iterator_get_next_match (it, pdg_match)
class(vertex_iterator_t), intent(inout) :: it
integer, dimension(:), allocatable, intent(out) :: pdg_match
integer :: i, j
do i = it%vertex_index + 1, size (it%model%vtx)
do j = it%pdg_index + 1, size (it%pdg)
call it%model%vtx(i)%get_match (it%pdg(j), pdg_match)
if (it%save_pdg_index) then
if (allocated (pdg_match) .and. j < size (it%pdg)) then
it%pdg_index = j
return
else if (allocated (pdg_match) .and. j == size (it%pdg)) then
it%vertex_index = i
it%pdg_index = 0
return
end if
else if (allocated (pdg_match)) then
it%vertex_index = i
return
end if
end do
end do
it%vertex_index = 0
it%pdg_index = 0
end subroutine vertex_iterator_get_next_match
@ %def vertex_iterator_get_next_match
@
\subsection{Vertex lookup table}
The vertex lookup table is a hash table: given two particle codes, we
check which codes are allowed for the third one.
The size of the hash table should be large enough that collisions are
rare. We first select a size based on the number of vertices
(multiplied by six because all permutations count), with some margin,
and then choose the smallest integer power of two larger than this.
<<Model data: parameters>>=
integer, parameter :: VERTEX_TABLE_SCALE_FACTOR = 60
@ %def VERTEX_TABLE_SCALE_FACTOR
<<Model data: procedures>>=
function vertex_table_size (n_vtx) result (n)
integer(i32) :: n
integer, intent(in) :: n_vtx
integer :: i, s
s = VERTEX_TABLE_SCALE_FACTOR * n_vtx
n = 1
do i = 1, 31
n = ishft (n, 1)
s = ishft (s,-1)
if (s == 0) exit
end do
end function vertex_table_size
@ %def vertex_table_size
@ The specific hash function takes two particle codes (arbitrary
integers) and returns a 32-bit integer. It makes use of the universal
function [[hash]] which operates on a byte array.
<<Model data: procedures>>=
function hash2 (pdg1, pdg2)
integer(i32) :: hash2
integer, intent(in) :: pdg1, pdg2
integer(i8), dimension(1) :: mold
hash2 = hash (transfer ([pdg1, pdg2], mold))
end function hash2
@ %def hash2
@ Each entry in the vertex table stores the two particle codes and an
array of possibilities for the third code.
<<Model data: types>>=
type :: vertex_table_entry_t
private
integer :: pdg1 = 0, pdg2 = 0
integer :: n = 0
integer, dimension(:), allocatable :: pdg3
end type vertex_table_entry_t
@ %def vertex_table_entry_t
@ The vertex table:
<<Model data: types>>=
type :: vertex_table_t
type(vertex_table_entry_t), dimension(:), allocatable :: entry
integer :: n_collisions = 0
integer(i32) :: mask
contains
<<Model data: vertex table: TBP>>
end type vertex_table_t
@ %def vertex_table_t
@ Output.
<<Model data: vertex table: TBP>>=
procedure :: write => vertex_table_write
<<Model data: procedures>>=
subroutine vertex_table_write (vt, unit)
class(vertex_table_t), intent(in) :: vt
integer, intent(in), optional :: unit
integer :: u, i
character(9) :: size_pdg3
u = given_output_unit (unit)
write (u, "(A)") "vertex hash table:"
write (u, "(A,I7)") " size = ", size (vt%entry)
write (u, "(A,I7)") " used = ", count (vt%entry%n /= 0)
write (u, "(A,I7)") " coll = ", vt%n_collisions
do i = lbound (vt%entry, 1), ubound (vt%entry, 1)
if (vt%entry(i)%n /= 0) then
write (size_pdg3, "(I7)") size (vt%entry(i)%pdg3)
write (u, "(A,1x,I7,1x,A,2(1x,I7),A," // &
size_pdg3 // "(1x,I7))") &
" ", i, ":", vt%entry(i)%pdg1, &
vt%entry(i)%pdg2, "->", vt%entry(i)%pdg3
end if
end do
end subroutine vertex_table_write
@ %def vertex_table_write
@ Initializing the vertex table: This is done in two passes. First,
we scan all permutations for all vertices and count the number of
entries in each bucket of the hashtable. Then, the buckets are
allocated accordingly and filled.
Collision resolution is done by just incrementing the hash value until
an empty bucket is found. The vertex table size is fixed, since we
know from the beginning the number of entries.
<<Model data: vertex table: TBP>>=
procedure :: init => vertex_table_init
<<Model data: procedures>>=
subroutine vertex_table_init (vt, prt, vtx)
class(vertex_table_t), intent(out) :: vt
type(field_data_t), dimension(:), intent(in) :: prt
type(vertex_t), dimension(:), intent(in) :: vtx
integer :: n_vtx, vt_size, i, p1, p2, p3
integer, dimension(3) :: p
n_vtx = size (vtx)
vt_size = vertex_table_size (count (vtx%trilinear))
vt%mask = vt_size - 1
allocate (vt%entry (0:vt_size-1))
do i = 1, n_vtx
if (vtx(i)%trilinear) then
p = vtx(i)%pdg
p1 = p(1); p2 = p(2)
call create (hash2 (p1, p2))
if (p(2) /= p(3)) then
p2 = p(3)
call create (hash2 (p1, p2))
end if
if (p(1) /= p(2)) then
p1 = p(2); p2 = p(1)
call create (hash2 (p1, p2))
if (p(1) /= p(3)) then
p2 = p(3)
call create (hash2 (p1, p2))
end if
end if
if (p(1) /= p(3)) then
p1 = p(3); p2 = p(1)
call create (hash2 (p1, p2))
if (p(1) /= p(2)) then
p2 = p(2)
call create (hash2 (p1, p2))
end if
end if
end if
end do
do i = 0, vt_size - 1
allocate (vt%entry(i)%pdg3 (vt%entry(i)%n))
end do
vt%entry%n = 0
do i = 1, n_vtx
if (vtx(i)%trilinear) then
p = vtx(i)%pdg
p1 = p(1); p2 = p(2); p3 = p(3)
call register (hash2 (p1, p2))
if (p(2) /= p(3)) then
p2 = p(3); p3 = p(2)
call register (hash2 (p1, p2))
end if
if (p(1) /= p(2)) then
p1 = p(2); p2 = p(1); p3 = p(3)
call register (hash2 (p1, p2))
if (p(1) /= p(3)) then
p2 = p(3); p3 = p(1)
call register (hash2 (p1, p2))
end if
end if
if (p(1) /= p(3)) then
p1 = p(3); p2 = p(1); p3 = p(2)
call register (hash2 (p1, p2))
if (p(1) /= p(2)) then
p2 = p(2); p3 = p(1)
call register (hash2 (p1, p2))
end if
end if
end if
end do
contains
recursive subroutine create (hashval)
integer(i32), intent(in) :: hashval
integer :: h
h = iand (hashval, vt%mask)
if (vt%entry(h)%n == 0) then
vt%entry(h)%pdg1 = p1
vt%entry(h)%pdg2 = p2
vt%entry(h)%n = 1
else if (vt%entry(h)%pdg1 == p1 .and. vt%entry(h)%pdg2 == p2) then
vt%entry(h)%n = vt%entry(h)%n + 1
else
vt%n_collisions = vt%n_collisions + 1
call create (hashval + 1)
end if
end subroutine create
recursive subroutine register (hashval)
integer(i32), intent(in) :: hashval
integer :: h
h = iand (hashval, vt%mask)
if (vt%entry(h)%pdg1 == p1 .and. vt%entry(h)%pdg2 == p2) then
vt%entry(h)%n = vt%entry(h)%n + 1
vt%entry(h)%pdg3(vt%entry(h)%n) = p3
else
call register (hashval + 1)
end if
end subroutine register
end subroutine vertex_table_init
@ %def vertex_table_init
@ Return the array of particle codes that match the given pair.
<<Model data: vertex table: TBP>>=
procedure :: match => vertex_table_match
<<Model data: procedures>>=
subroutine vertex_table_match (vt, pdg1, pdg2, pdg3)
class(vertex_table_t), intent(in) :: vt
integer, intent(in) :: pdg1, pdg2
integer, dimension(:), allocatable, intent(out) :: pdg3
call match (hash2 (pdg1, pdg2))
contains
recursive subroutine match (hashval)
integer(i32), intent(in) :: hashval
integer :: h
h = iand (hashval, vt%mask)
if (vt%entry(h)%n == 0) then
allocate (pdg3 (0))
else if (vt%entry(h)%pdg1 == pdg1 .and. vt%entry(h)%pdg2 == pdg2) then
allocate (pdg3 (size (vt%entry(h)%pdg3)))
pdg3 = vt%entry(h)%pdg3
else
call match (hashval + 1)
end if
end subroutine match
end subroutine vertex_table_match
@ %def vertex_table_match
@ Return true if the triplet is represented as a vertex.
<<Model data: vertex table: TBP>>=
procedure :: check => vertex_table_check
<<Model data: procedures>>=
function vertex_table_check (vt, pdg1, pdg2, pdg3) result (flag)
class(vertex_table_t), intent(in) :: vt
integer, intent(in) :: pdg1, pdg2, pdg3
logical :: flag
flag = check (hash2 (pdg1, pdg2))
contains
recursive function check (hashval) result (flag)
integer(i32), intent(in) :: hashval
integer :: h
logical :: flag
h = iand (hashval, vt%mask)
if (vt%entry(h)%n == 0) then
flag = .false.
else if (vt%entry(h)%pdg1 == pdg1 .and. vt%entry(h)%pdg2 == pdg2) then
flag = any (vt%entry(h)%pdg3 == pdg3)
else
flag = check (hashval + 1)
end if
end function check
end function vertex_table_check
@ %def vertex_table_check
@
\subsection{Model Data Record}
This type collects the model data as defined above.
We deliberately implement the parameter arrays as pointer arrays. We
thus avoid keeping track of TARGET attributes.
The [[scheme]] identifier provides meta information. It doesn't give the
client code an extra parameter, but it tells something about the
interpretation of the parameters. If the scheme ID is left as default (zero),
it is ignored.
<<Model data: public>>=
public :: model_data_t
<<Model data: types>>=
type :: model_data_t
private
type(string_t) :: name
integer :: scheme = 0
type(modelpar_real_t), dimension(:), pointer :: par_real => null ()
type(modelpar_complex_t), dimension(:), pointer :: par_complex => null ()
type(field_data_t), dimension(:), allocatable :: field
type(vertex_t), dimension(:), allocatable :: vtx
type(vertex_table_t) :: vt
contains
<<Model data: model data: TBP>>
end type model_data_t
@ %def model_data_t
@ Finalizer, deallocate pointer arrays.
<<Model data: model data: TBP>>=
procedure :: final => model_data_final
<<Model data: procedures>>=
subroutine model_data_final (model)
class(model_data_t), intent(inout) :: model
if (associated (model%par_real)) then
deallocate (model%par_real)
end if
if (associated (model%par_complex)) then
deallocate (model%par_complex)
end if
end subroutine model_data_final
@ %def model_data_final
@ Output. The signature matches the signature of the high-level
[[model_write]] procedure, so some of the options don't actually apply.
<<Model data: model data: TBP>>=
procedure :: write => model_data_write
<<Model data: procedures>>=
subroutine model_data_write (model, unit, verbose, &
show_md5sum, show_variables, show_parameters, &
show_particles, show_vertices, show_scheme)
class(model_data_t), intent(in) :: model
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
logical, intent(in), optional :: show_md5sum
logical, intent(in), optional :: show_variables
logical, intent(in), optional :: show_parameters
logical, intent(in), optional :: show_particles
logical, intent(in), optional :: show_vertices
logical, intent(in), optional :: show_scheme
logical :: show_sch, show_par, show_prt, show_vtx
integer :: u, i
u = given_output_unit (unit)
show_sch = .false.; if (present (show_scheme)) &
show_sch = show_scheme
show_par = .true.; if (present (show_parameters)) &
show_par = show_parameters
show_prt = .true.; if (present (show_particles)) &
show_prt = show_particles
show_vtx = .true.; if (present (show_vertices)) &
show_vtx = show_vertices
if (show_sch) then
write (u, "(3x,A,1X,I0)") "scheme =", model%scheme
end if
if (show_par) then
do i = 1, size (model%par_real)
call model%par_real(i)%write (u)
write (u, "(A)")
end do
do i = 1, size (model%par_complex)
call model%par_complex(i)%write (u)
write (u, "(A)")
end do
end if
if (show_prt) then
write (u, "(A)")
call model%write_fields (u)
end if
if (show_vtx) then
write (u, "(A)")
call model%write_vertices (u, verbose)
end if
end subroutine model_data_write
@ %def model_data_write
@ Initialize, allocating pointer arrays. The second version makes a
deep copy.
<<Model data: model data: TBP>>=
generic :: init => model_data_init
procedure, private :: model_data_init
<<Model data: procedures>>=
subroutine model_data_init (model, name, &
n_par_real, n_par_complex, n_field, n_vtx)
class(model_data_t), intent(out) :: model
type(string_t), intent(in) :: name
integer, intent(in) :: n_par_real, n_par_complex
integer, intent(in) :: n_field
integer, intent(in) :: n_vtx
model%name = name
allocate (model%par_real (n_par_real))
allocate (model%par_complex (n_par_complex))
allocate (model%field (n_field))
allocate (model%vtx (n_vtx))
end subroutine model_data_init
@ %def model_data_init
@ Set the scheme ID.
<<Model data: model data: TBP>>=
procedure :: set_scheme_num => model_data_set_scheme_num
<<Model data: procedures>>=
subroutine model_data_set_scheme_num (model, scheme)
class(model_data_t), intent(inout) :: model
integer, intent(in) :: scheme
model%scheme = scheme
end subroutine model_data_set_scheme_num
@ %def model_data_set_scheme_num
@ Complete model data initialization.
<<Model data: model data: TBP>>=
procedure :: freeze_fields => model_data_freeze_fields
<<Model data: procedures>>=
subroutine model_data_freeze_fields (model)
class(model_data_t), intent(inout) :: model
call model%field%freeze ()
end subroutine model_data_freeze_fields
@ %def model_data_freeze
@ Deep copy. The new model should already be initialized, so we do
not allocate memory.
<<Model data: model data: TBP>>=
procedure :: copy_from => model_data_copy
<<Model data: procedures>>=
subroutine model_data_copy (model, src)
class(model_data_t), intent(inout), target :: model
class(model_data_t), intent(in), target :: src
class(modelpar_data_t), pointer :: data, src_data
integer :: i
model%scheme = src%scheme
model%par_real = src%par_real
model%par_complex = src%par_complex
do i = 1, size (src%field)
associate (field => model%field(i), src_field => src%field(i))
call field%init (src_field%get_longname (), src_field%get_pdg ())
call field%copy_from (src_field)
src_data => src_field%mass_data
if (associated (src_data)) then
data => model%get_par_data_ptr (src_data%get_name ())
call field%set (mass_data = data)
end if
src_data => src_field%width_data
if (associated (src_data)) then
data => model%get_par_data_ptr (src_data%get_name ())
call field%set (width_data = data)
end if
call field%set_multiplicity ()
end associate
end do
do i = 1, size (src%vtx)
call model%vtx(i)%copy_from (src%vtx(i), model)
end do
call model%freeze_vertices ()
end subroutine model_data_copy
@ %def model_data_copy
@ Return the model name and numeric scheme.
<<Model data: model data: TBP>>=
procedure :: get_name => model_data_get_name
procedure :: get_scheme_num => model_data_get_scheme_num
<<Model data: procedures>>=
function model_data_get_name (model) result (name)
class(model_data_t), intent(in) :: model
type(string_t) :: name
name = model%name
end function model_data_get_name
function model_data_get_scheme_num (model) result (scheme)
class(model_data_t), intent(in) :: model
integer :: scheme
scheme = model%scheme
end function model_data_get_scheme_num
@ %def model_data_get_name
@ %def model_data_get_scheme
@ Retrieve a MD5 sum for the current model parameter values and
decay/polarization settings. This is
done by writing them to a temporary file, using a standard format. If the
model scheme is nonzero, it is also written.
<<Model data: model data: TBP>>=
procedure :: get_parameters_md5sum => model_data_get_parameters_md5sum
<<Model data: procedures>>=
function model_data_get_parameters_md5sum (model) result (par_md5sum)
character(32) :: par_md5sum
class(model_data_t), intent(in) :: model
real(default), dimension(:), allocatable :: par
type(field_data_t), pointer :: field
integer :: unit, i
allocate (par (model%get_n_real ()))
call model%real_parameters_to_array (par)
unit = free_unit ()
open (unit, status="scratch", action="readwrite")
if (model%scheme /= 0) write (unit, "(I0)") model%scheme
write (unit, "(" // FMT_19 // ")") par
do i = 1, model%get_n_field ()
field => model%get_field_ptr_by_index (i)
if (.not. field%is_stable (.false.) .or. .not. field%is_stable (.true.) &
.or. field%is_polarized (.false.) .or. field%is_polarized (.true.))&
then
write (unit, "(3x,A)") char (field%get_longname ())
call field%write_decays (unit)
end if
end do
rewind (unit)
par_md5sum = md5sum (unit)
close (unit)
end function model_data_get_parameters_md5sum
@ %def model_get_parameters_md5sum
@ Return the MD5 sum. This is a placeholder, to be overwritten
for the complete model definition.
<<Model data: model data: TBP>>=
procedure :: get_md5sum => model_data_get_md5sum
<<Model data: procedures>>=
function model_data_get_md5sum (model) result (md5sum)
class(model_data_t), intent(in) :: model
character(32) :: md5sum
md5sum = model%get_parameters_md5sum ()
end function model_data_get_md5sum
@ %def model_data_get_md5sum
@ Initialize a real or complex parameter.
<<Model data: model data: TBP>>=
generic :: init_par => model_data_init_par_real, model_data_init_par_complex
procedure, private :: model_data_init_par_real
procedure, private :: model_data_init_par_complex
<<Model data: procedures>>=
subroutine model_data_init_par_real (model, i, name, value)
class(model_data_t), intent(inout) :: model
integer, intent(in) :: i
type(string_t), intent(in) :: name
real(default), intent(in) :: value
call model%par_real(i)%init (name, value)
end subroutine model_data_init_par_real
subroutine model_data_init_par_complex (model, i, name, value)
class(model_data_t), intent(inout) :: model
integer, intent(in) :: i
type(string_t), intent(in) :: name
complex(default), intent(in) :: value
call model%par_complex(i)%init (name, value)
end subroutine model_data_init_par_complex
@ %def model_data_init_par_real model_data_init_par_complex
@ After initialization, return size of parameter array.
<<Model data: model data: TBP>>=
procedure :: get_n_real => model_data_get_n_real
procedure :: get_n_complex => model_data_get_n_complex
<<Model data: procedures>>=
function model_data_get_n_real (model) result (n)
class(model_data_t), intent(in) :: model
integer :: n
n = size (model%par_real)
end function model_data_get_n_real
function model_data_get_n_complex (model) result (n)
class(model_data_t), intent(in) :: model
integer :: n
n = size (model%par_complex)
end function model_data_get_n_complex
@ %def model_data_get_n_real
@ %def model_data_get_n_complex
@ After initialization, extract the whole parameter array.
<<Model data: model data: TBP>>=
procedure :: real_parameters_to_array &
=> model_data_real_par_to_array
procedure :: complex_parameters_to_array &
=> model_data_complex_par_to_array
<<Model data: procedures>>=
subroutine model_data_real_par_to_array (model, array)
class(model_data_t), intent(in) :: model
real(default), dimension(:), intent(inout) :: array
array = model%par_real%get_real ()
end subroutine model_data_real_par_to_array
subroutine model_data_complex_par_to_array (model, array)
class(model_data_t), intent(in) :: model
complex(default), dimension(:), intent(inout) :: array
array = model%par_complex%get_complex ()
end subroutine model_data_complex_par_to_array
@ %def model_data_real_par_to_array
@ %def model_data_complex_par_to_array
@ After initialization, set the whole parameter array.
<<Model data: model data: TBP>>=
procedure :: real_parameters_from_array &
=> model_data_real_par_from_array
procedure :: complex_parameters_from_array &
=> model_data_complex_par_from_array
<<Model data: procedures>>=
subroutine model_data_real_par_from_array (model, array)
class(model_data_t), intent(inout) :: model
real(default), dimension(:), intent(in) :: array
model%par_real = array
end subroutine model_data_real_par_from_array
subroutine model_data_complex_par_from_array (model, array)
class(model_data_t), intent(inout) :: model
complex(default), dimension(:), intent(in) :: array
model%par_complex = array
end subroutine model_data_complex_par_from_array
@ %def model_data_real_par_from_array
@ %def model_data_complex_par_from_array
@ Analogous, for a C parameter array.
<<Model data: model data: TBP>>=
procedure :: real_parameters_to_c_array &
=> model_data_real_par_to_c_array
<<Model data: procedures>>=
subroutine model_data_real_par_to_c_array (model, array)
class(model_data_t), intent(in) :: model
real(c_default_float), dimension(:), intent(inout) :: array
array = model%par_real%get_real ()
end subroutine model_data_real_par_to_c_array
@ %def model_data_real_par_to_c_array
@ After initialization, set the whole parameter array.
<<Model data: model data: TBP>>=
procedure :: real_parameters_from_c_array &
=> model_data_real_par_from_c_array
<<Model data: procedures>>=
subroutine model_data_real_par_from_c_array (model, array)
class(model_data_t), intent(inout) :: model
real(c_default_float), dimension(:), intent(in) :: array
model%par_real = real (array, default)
end subroutine model_data_real_par_from_c_array
@ %def model_data_real_par_from_c_array
@ After initialization, get pointer to a real or complex parameter,
directly by index.
<<Model data: model data: TBP>>=
procedure :: get_par_real_ptr => model_data_get_par_real_ptr_index
procedure :: get_par_complex_ptr => model_data_get_par_complex_ptr_index
<<Model data: procedures>>=
function model_data_get_par_real_ptr_index (model, i) result (ptr)
class(model_data_t), intent(inout) :: model
integer, intent(in) :: i
class(modelpar_data_t), pointer :: ptr
ptr => model%par_real(i)
end function model_data_get_par_real_ptr_index
function model_data_get_par_complex_ptr_index (model, i) result (ptr)
class(model_data_t), intent(inout) :: model
integer, intent(in) :: i
class(modelpar_data_t), pointer :: ptr
ptr => model%par_complex(i)
end function model_data_get_par_complex_ptr_index
@ %def model_data_get_par_real_ptr model_data_get_par_complex_ptr
@ After initialization, get pointer to a parameter by name.
<<Model data: model data: TBP>>=
procedure :: get_par_data_ptr => model_data_get_par_data_ptr_name
<<Model data: procedures>>=
function model_data_get_par_data_ptr_name (model, name) result (ptr)
class(model_data_t), intent(in) :: model
type(string_t), intent(in) :: name
class(modelpar_data_t), pointer :: ptr
integer :: i
do i = 1, size (model%par_real)
if (model%par_real(i)%name == name) then
ptr => model%par_real(i)
return
end if
end do
do i = 1, size (model%par_complex)
if (model%par_complex(i)%name == name) then
ptr => model%par_complex(i)
return
end if
end do
ptr => null ()
end function model_data_get_par_data_ptr_name
@ %def model_data_get_par_data_ptr
@ Return the value by name. Again, type conversion is allowed.
<<Model data: model data: TBP>>=
procedure :: get_real => model_data_get_par_real_value
procedure :: get_complex => model_data_get_par_complex_value
<<Model data: procedures>>=
function model_data_get_par_real_value (model, name) result (value)
class(model_data_t), intent(in) :: model
type(string_t), intent(in) :: name
class(modelpar_data_t), pointer :: par
real(default) :: value
par => model%get_par_data_ptr (name)
value = par%get_real ()
end function model_data_get_par_real_value
function model_data_get_par_complex_value (model, name) result (value)
class(model_data_t), intent(in) :: model
type(string_t), intent(in) :: name
class(modelpar_data_t), pointer :: par
complex(default) :: value
par => model%get_par_data_ptr (name)
value = par%get_complex ()
end function model_data_get_par_complex_value
@ %def model_data_get_real
@ %def model_data_get_complex
@ Modify a real or complex parameter.
<<Model data: model data: TBP>>=
generic :: set_par => model_data_set_par_real, model_data_set_par_complex
procedure, private :: model_data_set_par_real
procedure, private :: model_data_set_par_complex
<<Model data: procedures>>=
subroutine model_data_set_par_real (model, name, value)
class(model_data_t), intent(inout) :: model
type(string_t), intent(in) :: name
real(default), intent(in) :: value
class(modelpar_data_t), pointer :: par
par => model%get_par_data_ptr (name)
par = value
end subroutine model_data_set_par_real
subroutine model_data_set_par_complex (model, name, value)
class(model_data_t), intent(inout) :: model
type(string_t), intent(in) :: name
complex(default), intent(in) :: value
class(modelpar_data_t), pointer :: par
par => model%get_par_data_ptr (name)
par = value
end subroutine model_data_set_par_complex
@ %def model_data_set_par_real model_data_set_par_complex
@ List all fields in the model.
<<Model data: model data: TBP>>=
procedure :: write_fields => model_data_write_fields
<<Model data: procedures>>=
subroutine model_data_write_fields (model, unit)
class(model_data_t), intent(in) :: model
integer, intent(in), optional :: unit
integer :: i
do i = 1, size (model%field)
call model%field(i)%write (unit)
end do
end subroutine model_data_write_fields
@ %def model_data_write_fields
@ After initialization, return number of fields (particles):
<<Model data: model data: TBP>>=
procedure :: get_n_field => model_data_get_n_field
<<Model data: procedures>>=
function model_data_get_n_field (model) result (n)
class(model_data_t), intent(in) :: model
integer :: n
n = size (model%field)
end function model_data_get_n_field
@ %def model_data_get_n_field
@ Return the PDG code of a field. The field is identified by name or
by index. If the field is not found, return zero.
<<Model data: model data: TBP>>=
generic :: get_pdg => &
model_data_get_field_pdg_index, &
model_data_get_field_pdg_name
procedure, private :: model_data_get_field_pdg_index
procedure, private :: model_data_get_field_pdg_name
<<Model data: procedures>>=
function model_data_get_field_pdg_index (model, i) result (pdg)
class(model_data_t), intent(in) :: model
integer, intent(in) :: i
integer :: pdg
pdg = model%field(i)%get_pdg ()
end function model_data_get_field_pdg_index
function model_data_get_field_pdg_name (model, name, check) result (pdg)
class(model_data_t), intent(in) :: model
type(string_t), intent(in) :: name
logical, intent(in), optional :: check
integer :: pdg
integer :: i
do i = 1, size (model%field)
associate (field => model%field(i))
if (field%matches_name (name, .false.)) then
pdg = field%get_pdg ()
return
else if (field%matches_name (name, .true.)) then
pdg = - field%get_pdg ()
return
end if
end associate
end do
pdg = 0
call model%field_error (check, name)
end function model_data_get_field_pdg_name
@ %def model_data_get_field_pdg
@ Return an array of all PDG codes, including antiparticles. The antiparticle
are sorted after all particles.
<<Model data: model data: TBP>>=
procedure :: get_all_pdg => model_data_get_all_pdg
<<Model data: procedures>>=
subroutine model_data_get_all_pdg (model, pdg)
class(model_data_t), intent(in) :: model
integer, dimension(:), allocatable, intent(inout) :: pdg
integer :: n0, n1, i, k
n0 = size (model%field)
n1 = n0 + count (model%field%has_antiparticle ())
allocate (pdg (n1))
pdg(1:n0) = model%field%get_pdg ()
k = n0
do i = 1, size (model%field)
associate (field => model%field(i))
if (field%has_antiparticle ()) then
k = k + 1
pdg(k) = - field%get_pdg ()
end if
end associate
end do
end subroutine model_data_get_all_pdg
@ %def model_data_get_all_pdg
@ Return pointer to the field array.
<<Model data: model data: TBP>>=
procedure :: get_field_array_ptr => model_data_get_field_array_ptr
<<Model data: procedures>>=
function model_data_get_field_array_ptr (model) result (ptr)
class(model_data_t), intent(in), target :: model
type(field_data_t), dimension(:), pointer :: ptr
ptr => model%field
end function model_data_get_field_array_ptr
@ %def model_data_get_field_array_ptr
@ Return pointer to a field. The identifier should be the unique long
name, the PDG code, or the index.
We can issue an error message, if the [[check]] flag is set. We never return
an error if the PDG code is zero, this yields just a null pointer.
<<Model data: model data: TBP>>=
generic :: get_field_ptr => &
model_data_get_field_ptr_name, &
model_data_get_field_ptr_pdg
procedure, private :: model_data_get_field_ptr_name
procedure, private :: model_data_get_field_ptr_pdg
procedure :: get_field_ptr_by_index => model_data_get_field_ptr_index
<<Model data: procedures>>=
function model_data_get_field_ptr_name (model, name, check) result (ptr)
class(model_data_t), intent(in), target :: model
type(string_t), intent(in) :: name
logical, intent(in), optional :: check
type(field_data_t), pointer :: ptr
integer :: i
do i = 1, size (model%field)
if (model%field(i)%matches_name (name, .false.)) then
ptr => model%field(i)
return
else if (model%field(i)%matches_name (name, .true.)) then
ptr => model%field(i)
return
end if
end do
ptr => null ()
call model%field_error (check, name)
end function model_data_get_field_ptr_name
function model_data_get_field_ptr_pdg (model, pdg, check) result (ptr)
class(model_data_t), intent(in), target :: model
integer, intent(in) :: pdg
logical, intent(in), optional :: check
type(field_data_t), pointer :: ptr
integer :: i, pdg_abs
if (pdg == 0) then
ptr => null ()
return
end if
pdg_abs = abs (pdg)
do i = 1, size (model%field)
if (model%field(i)%get_pdg () == pdg_abs) then
ptr => model%field(i)
return
end if
end do
ptr => null ()
call model%field_error (check, pdg=pdg)
end function model_data_get_field_ptr_pdg
function model_data_get_field_ptr_index (model, i) result (ptr)
class(model_data_t), intent(in), target :: model
integer, intent(in) :: i
type(field_data_t), pointer :: ptr
ptr => model%field(i)
end function model_data_get_field_ptr_index
@ %def model_data_get_field_ptr
@ Don't assign a pointer, just check.
<<Model data: model data: TBP>>=
procedure :: test_field => model_data_test_field_pdg
<<Model data: procedures>>=
function model_data_test_field_pdg (model, pdg, check) result (exist)
class(model_data_t), intent(in), target :: model
integer, intent(in) :: pdg
logical, intent(in), optional :: check
logical :: exist
exist = associated (model%get_field_ptr (pdg, check))
end function model_data_test_field_pdg
@ %def model_data_test_field_pdg
@ Error message, if [[check]] is set.
<<Model data: model data: TBP>>=
procedure :: field_error => model_data_field_error
<<Model data: procedures>>=
subroutine model_data_field_error (model, check, name, pdg)
class(model_data_t), intent(in) :: model
logical, intent(in), optional :: check
type(string_t), intent(in), optional :: name
integer, intent(in), optional :: pdg
if (present (check)) then
if (check) then
if (present (name)) then
write (msg_buffer, "(A,1x,A,1x,A,1x,A)") &
"No particle with name", char (name), &
"is contained in model", char (model%name)
else if (present (pdg)) then
write (msg_buffer, "(A,1x,I0,1x,A,1x,A)") &
"No particle with PDG code", pdg, &
"is contained in model", char (model%name)
else
write (msg_buffer, "(A,1x,A,1x,A)") &
"Particle missing", &
"in model", char (model%name)
end if
call msg_fatal ()
end if
end if
end subroutine model_data_field_error
@ %def model_data_field_error
@ Assign mass and width value, which are associated via pointer.
Identify the particle via pdg.
<<Model data: model data: TBP>>=
procedure :: set_field_mass => model_data_set_field_mass_pdg
procedure :: set_field_width => model_data_set_field_width_pdg
<<Model data: procedures>>=
subroutine model_data_set_field_mass_pdg (model, pdg, value)
class(model_data_t), intent(inout) :: model
integer, intent(in) :: pdg
real(default), intent(in) :: value
type(field_data_t), pointer :: field
field => model%get_field_ptr (pdg, check = .true.)
call field%set_mass (value)
end subroutine model_data_set_field_mass_pdg
subroutine model_data_set_field_width_pdg (model, pdg, value)
class(model_data_t), intent(inout) :: model
integer, intent(in) :: pdg
real(default), intent(in) :: value
type(field_data_t), pointer :: field
field => model%get_field_ptr (pdg, check = .true.)
call field%set_width (value)
end subroutine model_data_set_field_width_pdg
@ %def model_data_set_field_mass
@ %def model_data_set_field_width
@ Mark a particle as unstable and provide a list of names for its
decay processes. In contrast with the previous subroutine which is
for internal use, we address the particle by its PDG code. If the
index is negative, we address the antiparticle.
<<Model data: model data: TBP>>=
procedure :: set_unstable => model_data_set_unstable
procedure :: set_stable => model_data_set_stable
<<Model data: procedures>>=
subroutine model_data_set_unstable &
(model, pdg, decay, isotropic, diagonal, decay_helicity)
class(model_data_t), intent(inout), target :: model
integer, intent(in) :: pdg
type(string_t), dimension(:), intent(in) :: decay
logical, intent(in), optional :: isotropic, diagonal
integer, intent(in), optional :: decay_helicity
type(field_data_t), pointer :: field
field => model%get_field_ptr (pdg)
if (pdg > 0) then
call field%set ( &
p_is_stable = .false., p_decay = decay, &
p_decays_isotropically = isotropic, &
p_decays_diagonal = diagonal, &
p_decay_helicity = decay_helicity)
else
call field%set ( &
a_is_stable = .false., a_decay = decay, &
a_decays_isotropically = isotropic, &
a_decays_diagonal = diagonal, &
a_decay_helicity = decay_helicity)
end if
end subroutine model_data_set_unstable
subroutine model_data_set_stable (model, pdg)
class(model_data_t), intent(inout), target :: model
integer, intent(in) :: pdg
type(field_data_t), pointer :: field
field => model%get_field_ptr (pdg)
if (pdg > 0) then
call field%set (p_is_stable = .true.)
else
call field%set (a_is_stable = .true.)
end if
end subroutine model_data_set_stable
@ %def model_data_set_unstable
@ %def model_data_set_stable
@ Mark a particle as polarized.
<<Model data: model data: TBP>>=
procedure :: set_polarized => model_data_set_polarized
procedure :: set_unpolarized => model_data_set_unpolarized
<<Model data: procedures>>=
subroutine model_data_set_polarized (model, pdg)
class(model_data_t), intent(inout), target :: model
integer, intent(in) :: pdg
type(field_data_t), pointer :: field
field => model%get_field_ptr (pdg)
if (pdg > 0) then
call field%set (p_polarized = .true.)
else
call field%set (a_polarized = .true.)
end if
end subroutine model_data_set_polarized
subroutine model_data_set_unpolarized (model, pdg)
class(model_data_t), intent(inout), target :: model
integer, intent(in) :: pdg
type(field_data_t), pointer :: field
field => model%get_field_ptr (pdg)
if (pdg > 0) then
call field%set (p_polarized = .false.)
else
call field%set (a_polarized = .false.)
end if
end subroutine model_data_set_unpolarized
@ %def model_data_set_polarized
@ %def model_data_set_unpolarized
@ Revert all polarized (unstable) particles to unpolarized (stable)
status, respectively.
<<Model data: model data: TBP>>=
procedure :: clear_unstable => model_clear_unstable
procedure :: clear_polarized => model_clear_polarized
<<Model data: procedures>>=
subroutine model_clear_unstable (model)
class(model_data_t), intent(inout), target :: model
integer :: i
type(field_data_t), pointer :: field
do i = 1, model%get_n_field ()
field => model%get_field_ptr_by_index (i)
call field%set (p_is_stable = .true.)
if (field%has_antiparticle ()) then
call field%set (a_is_stable = .true.)
end if
end do
end subroutine model_clear_unstable
subroutine model_clear_polarized (model)
class(model_data_t), intent(inout), target :: model
integer :: i
type(field_data_t), pointer :: field
do i = 1, model%get_n_field ()
field => model%get_field_ptr_by_index (i)
call field%set (p_polarized = .false.)
if (field%has_antiparticle ()) then
call field%set (a_polarized = .false.)
end if
end do
end subroutine model_clear_polarized
@ %def model_clear_unstable
@ %def model_clear_polarized
@ List all vertices, optionally also the hash table.
<<Model data: model data: TBP>>=
procedure :: write_vertices => model_data_write_vertices
<<Model data: procedures>>=
subroutine model_data_write_vertices (model, unit, verbose)
class(model_data_t), intent(in) :: model
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose
integer :: i, u
u = given_output_unit (unit)
do i = 1, size (model%vtx)
call vertex_write (model%vtx(i), unit)
end do
if (present (verbose)) then
if (verbose) then
write (u, *)
call vertex_table_write (model%vt, unit)
end if
end if
end subroutine model_data_write_vertices
@ %def model_data_write_vertices
@ Vertex definition.
<<Model data: model data: TBP>>=
generic :: set_vertex => &
model_data_set_vertex_pdg, model_data_set_vertex_names
procedure, private :: model_data_set_vertex_pdg
procedure, private :: model_data_set_vertex_names
<<Model data: procedures>>=
subroutine model_data_set_vertex_pdg (model, i, pdg)
class(model_data_t), intent(inout), target :: model
integer, intent(in) :: i
integer, dimension(:), intent(in) :: pdg
call vertex_init (model%vtx(i), pdg, model)
end subroutine model_data_set_vertex_pdg
subroutine model_data_set_vertex_names (model, i, name)
class(model_data_t), intent(inout), target :: model
integer, intent(in) :: i
type(string_t), dimension(:), intent(in) :: name
integer, dimension(size(name)) :: pdg
integer :: j
do j = 1, size (name)
pdg(j) = model%get_pdg (name(j))
end do
call model%set_vertex (i, pdg)
end subroutine model_data_set_vertex_names
@ %def model_data_set_vertex
@ Finalize vertex definition: set up the hash table.
<<Model data: model data: TBP>>=
procedure :: freeze_vertices => model_data_freeze_vertices
<<Model data: procedures>>=
subroutine model_data_freeze_vertices (model)
class(model_data_t), intent(inout) :: model
call model%vt%init (model%field, model%vtx)
end subroutine model_data_freeze_vertices
@ %def model_data_freeze_vertices
@ Number of vertices in model
<<Model data: model data: TBP>>=
procedure :: get_n_vtx => model_data_get_n_vtx
<<Model data: procedures>>=
function model_data_get_n_vtx (model) result (n)
class(model_data_t), intent(in) :: model
integer :: n
n = size (model%vtx)
end function model_data_get_n_vtx
@ %def model_data_get_n_vtx
@ Lookup functions
<<Model data: model data: TBP>>=
procedure :: match_vertex => model_data_match_vertex
<<Model data: procedures>>=
subroutine model_data_match_vertex (model, pdg1, pdg2, pdg3)
class(model_data_t), intent(in) :: model
integer, intent(in) :: pdg1, pdg2
integer, dimension(:), allocatable, intent(out) :: pdg3
call model%vt%match (pdg1, pdg2, pdg3)
end subroutine model_data_match_vertex
@ %def model_data_match_vertex
<<Model data: model data: TBP>>=
procedure :: check_vertex => model_data_check_vertex
<<Model data: procedures>>=
function model_data_check_vertex (model, pdg1, pdg2, pdg3) result (flag)
logical :: flag
class(model_data_t), intent(in) :: model
integer, intent(in) :: pdg1, pdg2, pdg3
flag = model%vt%check (pdg1, pdg2, pdg3)
end function model_data_check_vertex
@ %def model_data_check_vertex
@
\subsection{Toy Models}
This is a stripped-down version of the (already trivial) model 'Test'.
<<Model data: model data: TBP>>=
procedure :: init_test => model_data_init_test
<<Model data: procedures>>=
subroutine model_data_init_test (model)
class(model_data_t), intent(out) :: model
type(field_data_t), pointer :: field
integer, parameter :: n_real = 4
integer, parameter :: n_field = 2
integer, parameter :: n_vertex = 2
integer :: i
call model%init (var_str ("Test"), &
n_real, 0, n_field, n_vertex)
i = 0
i = i + 1
call model%init_par (i, var_str ("gy"), 1._default)
i = i + 1
call model%init_par (i, var_str ("ms"), 125._default)
i = i + 1
call model%init_par (i, var_str ("ff"), 1.5_default)
i = i + 1
call model%init_par (i, var_str ("mf"), 1.5_default * 125._default)
i = 0
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("SCALAR"), 25)
call field%set (spin_type=1)
call field%set (mass_data=model%get_par_real_ptr (2))
call field%set (name = [var_str ("s")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("FERMION"), 6)
call field%set (spin_type=2)
call field%set (mass_data=model%get_par_real_ptr (4))
call field%set (name = [var_str ("f")], anti = [var_str ("fbar")])
call model%freeze_fields ()
i = 0
i = i + 1
call model%set_vertex (i, [var_str ("fbar"), var_str ("f"), var_str ("s")])
i = i + 1
call model%set_vertex (i, [var_str ("s"), var_str ("s"), var_str ("s")])
call model%freeze_vertices ()
end subroutine model_data_init_test
@ %def model_data_init_test
@
This procedure prepares a subset of QED for testing purposes.
<<Model data: model data: TBP>>=
procedure :: init_qed_test => model_data_init_qed_test
<<Model data: procedures>>=
subroutine model_data_init_qed_test (model)
class(model_data_t), intent(out) :: model
type(field_data_t), pointer :: field
integer, parameter :: n_real = 1
integer, parameter :: n_field = 2
integer :: i
call model%init (var_str ("QED_test"), &
n_real, 0, n_field, 0)
i = 0
i = i + 1
call model%init_par (i, var_str ("me"), 0.000510997_default)
i = 0
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("E_LEPTON"), 11)
call field%set (spin_type=2, charge_type=-4)
call field%set (mass_data=model%get_par_real_ptr (1))
call field%set (name = [var_str ("e-")], anti = [var_str ("e+")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("PHOTON"), 22)
call field%set (spin_type=3)
call field%set (name = [var_str ("A")])
call model%freeze_fields ()
call model%freeze_vertices ()
end subroutine model_data_init_qed_test
@ %def model_data_init_qed_test
@
This procedure prepares a subset of the Standard Model for testing purposes.
We can thus avoid dependencies on model I/O, which is not defined here.
<<Model data: model data: TBP>>=
procedure :: init_sm_test => model_data_init_sm_test
<<Model data: procedures>>=
subroutine model_data_init_sm_test (model)
class(model_data_t), intent(out) :: model
type(field_data_t), pointer :: field
integer, parameter :: n_real = 11
integer, parameter :: n_field = 19
integer, parameter :: n_vtx = 9
integer :: i
call model%init (var_str ("SM_test"), &
n_real, 0, n_field, n_vtx)
i = 0
i = i + 1
call model%init_par (i, var_str ("mZ"), 91.1882_default)
i = i + 1
call model%init_par (i, var_str ("mW"), 80.419_default)
i = i + 1
call model%init_par (i, var_str ("me"), 0.000510997_default)
i = i + 1
call model%init_par (i, var_str ("mmu"), 0.105658389_default)
i = i + 1
call model%init_par (i, var_str ("mb"), 4.2_default)
i = i + 1
call model%init_par (i, var_str ("mtop"), 173.1_default)
i = i + 1
call model%init_par (i, var_str ("wZ"), 2.443_default)
i = i + 1
call model%init_par (i, var_str ("wW"), 2.049_default)
i = i + 1
call model%init_par (i, var_str ("ee"), 0.3079561542961_default)
i = i + 1
call model%init_par (i, var_str ("cw"), 8.819013863636E-01_default)
i = i + 1
call model%init_par (i, var_str ("sw"), 4.714339240339E-01_default)
i = 0
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("D_QUARK"), 1)
call field%set (spin_type=2, color_type=3, charge_type=-2, isospin_type=-2)
call field%set (name = [var_str ("d")], anti = [var_str ("dbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("U_QUARK"), 2)
call field%set (spin_type=2, color_type=3, charge_type=3, isospin_type=2)
call field%set (name = [var_str ("u")], anti = [var_str ("ubar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("S_QUARK"), 3)
call field%set (spin_type=2, color_type=3, charge_type=-2, isospin_type=-2)
call field%set (name = [var_str ("s")], anti = [var_str ("sbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("C_QUARK"), 4)
call field%set (spin_type=2, color_type=3, charge_type=3, isospin_type=2)
call field%set (name = [var_str ("c")], anti = [var_str ("cbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("B_QUARK"), 5)
call field%set (spin_type=2, color_type=3, charge_type=-2, isospin_type=-2)
call field%set (mass_data=model%get_par_real_ptr (5))
call field%set (name = [var_str ("b")], anti = [var_str ("bbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("T_QUARK"), 6)
call field%set (spin_type=2, color_type=3, charge_type=3, isospin_type=2)
call field%set (mass_data=model%get_par_real_ptr (6))
call field%set (name = [var_str ("t")], anti = [var_str ("tbar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("E_LEPTON"), 11)
call field%set (spin_type=2)
call field%set (mass_data=model%get_par_real_ptr (3))
call field%set (name = [var_str ("e-")], anti = [var_str ("e+")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("E_NEUTRINO"), 12)
call field%set (spin_type=2, is_left_handed=.true.)
call field%set (name = [var_str ("nue")], anti = [var_str ("nuebar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("MU_LEPTON"), 13)
call field%set (spin_type=2)
call field%set (mass_data=model%get_par_real_ptr (4))
call field%set (name = [var_str ("mu-")], anti = [var_str ("mu+")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("MU_NEUTRINO"), 14)
call field%set (spin_type=2, is_left_handed=.true.)
call field%set (name = [var_str ("numu")], anti = [var_str ("numubar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("GLUON"), 21)
call field%set (spin_type=3, color_type=8)
call field%set (name = [var_str ("gl")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("PHOTON"), 22)
call field%set (spin_type=3)
call field%set (name = [var_str ("A")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("Z_BOSON"), 23)
call field%set (spin_type=3)
call field%set (mass_data=model%get_par_real_ptr (1))
call field%set (width_data=model%get_par_real_ptr (7))
call field%set (name = [var_str ("Z")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("W_BOSON"), 24)
call field%set (spin_type=3)
call field%set (mass_data=model%get_par_real_ptr (2))
call field%set (width_data=model%get_par_real_ptr (8))
call field%set (name = [var_str ("W+")], anti = [var_str ("W-")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("HIGGS"), 25)
call field%set (spin_type=1)
! call field%set (mass_data=model%get_par_real_ptr (2))
! call field%set (width_data=model%get_par_real_ptr (8))
call field%set (name = [var_str ("H")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("PROTON"), 2212)
call field%set (spin_type=2)
call field%set (name = [var_str ("p")], anti = [var_str ("pbar")])
! call field%set (mass_data=model%get_par_real_ptr (12))
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("HADRON_REMNANT_SINGLET"), 91)
call field%set (color_type=1)
call field%set (name = [var_str ("hr1")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("HADRON_REMNANT_TRIPLET"), 92)
call field%set (color_type=3)
call field%set (name = [var_str ("hr3")], anti = [var_str ("hr3bar")])
i = i + 1
field => model%get_field_ptr_by_index (i)
call field%init (var_str ("HADRON_REMNANT_OCTET"), 93)
call field%set (color_type=8)
call field%set (name = [var_str ("hr8")])
call model%freeze_fields ()
i = 0
i = i + 1
call model%set_vertex (i, [var_str ("dbar"), var_str ("d"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("ubar"), var_str ("u"), var_str ("A")])
i = i + 1
call model%set_vertex (i, [var_str ("gl"), var_str ("gl"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("dbar"), var_str ("d"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("ubar"), var_str ("u"), var_str ("gl")])
i = i + 1
call model%set_vertex (i, [var_str ("dbar"), var_str ("d"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("ubar"), var_str ("u"), var_str ("Z")])
i = i + 1
call model%set_vertex (i, [var_str ("ubar"), var_str ("d"), var_str ("W+")])
i = i + 1
call model%set_vertex (i, [var_str ("dbar"), var_str ("u"), var_str ("W-")])
call model%freeze_vertices ()
end subroutine model_data_init_sm_test
@ %def model_data_init_sm_test
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Model Testbed}
The standard way of defining a model uses concrete variables and expressions to
interpret the model file. Some of this is not available at the point of use. This
is no problem for the \whizard\ program as a whole, but unit tests are
kept local to their respective module and don't access all definitions.
Instead, we introduce a separate module that provides hooks, one for
initializing a model and one for finalizing a model. The main program can
assign real routines to the hooks (procedure pointers of abstract type) before
unit tests are called. The unit tests can call the abstract routines without
knowing about their implementation.
<<[[model_testbed.f90]]>>=
<<File header>>
module model_testbed
<<Use strings>>
use model_data
use var_base
<<Standard module head>>
<<Model testbed: public>>
<<Model testbed: variables>>
<<Model testbed: interfaces>>
end module model_testbed
@ %def model_testbed
@
\subsection{Abstract Model Handlers}
Both routines take a polymorphic model (data) target, which
is not allocated/deallocated inside the subroutine. The model constructor
[[prepare_model]] requires the model name as input. It can, optionally,
return a link to the variable list of the model.
<<Model testbed: public>>=
public :: prepare_model
public :: cleanup_model
<<Model testbed: variables>>=
procedure (prepare_model_proc), pointer :: prepare_model => null ()
procedure (cleanup_model_proc), pointer :: cleanup_model => null ()
<<Model testbed: interfaces>>=
abstract interface
subroutine prepare_model_proc (model, name, vars)
import
class(model_data_t), intent(inout), pointer :: model
type(string_t), intent(in) :: name
class(vars_t), pointer, intent(out), optional :: vars
end subroutine prepare_model_proc
end interface
abstract interface
subroutine cleanup_model_proc (model)
import
class(model_data_t), intent(inout), target :: model
end subroutine cleanup_model_proc
end interface
@ %def prepare_model
@ %def cleanup_model
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Helicities}
This module defines types and tools for dealing with helicity
information.
<<[[helicities.f90]]>>=
<<File header>>
module helicities
use io_units
<<Standard module head>>
<<Helicities: public>>
<<Helicities: types>>
<<Helicities: interfaces>>
contains
<<Helicities: procedures>>
end module helicities
@ %def helicities
@
\subsection{Helicity types}
Helicities may be defined or undefined, corresponding to a polarized
or unpolarized state. Each helicity is actually a pair of helicities,
corresponding to an entry in the spin density matrix. Obviously,
diagonal entries are distinguished.
<<Helicities: public>>=
public :: helicity_t
<<Helicities: types>>=
type :: helicity_t
private
logical :: defined = .false.
integer :: h1, h2
contains
<<Helicities: helicity: TBP>>
end type helicity_t
@ %def helicity_t
@ Constructor functions, for convenience:
<<Helicities: public>>=
public :: helicity
<<Helicities: interfaces>>=
interface helicity
module procedure helicity0, helicity1, helicity2
end interface helicity
<<Helicities: procedures>>=
pure function helicity0 () result (hel)
type(helicity_t) :: hel
end function helicity0
elemental function helicity1 (h) result (hel)
type(helicity_t) :: hel
integer, intent(in) :: h
call hel%init (h)
end function helicity1
elemental function helicity2 (h2, h1) result (hel)
type(helicity_t) :: hel
integer, intent(in) :: h1, h2
call hel%init (h2, h1)
end function helicity2
@ %def helicity
@ Initializers.
Note: conceptually, the argument to initializers should be INTENT(OUT).
However, Interp.\ F08/0033 prohibited this. The reason is that, in principle,
the call could result in the execution of an impure finalizer for a type
extension of [[hel]] (ugh).
<<Helicities: helicity: TBP>>=
generic :: init => helicity_init_empty, helicity_init_same, helicity_init_different
procedure, private :: helicity_init_empty
procedure, private :: helicity_init_same
procedure, private :: helicity_init_different
<<Helicities: procedures>>=
elemental subroutine helicity_init_empty (hel)
class(helicity_t), intent(inout) :: hel
hel%defined = .false.
end subroutine helicity_init_empty
elemental subroutine helicity_init_same (hel, h)
class(helicity_t), intent(inout) :: hel
integer, intent(in) :: h
hel%defined = .true.
hel%h1 = h
hel%h2 = h
end subroutine helicity_init_same
elemental subroutine helicity_init_different (hel, h2, h1)
class(helicity_t), intent(inout) :: hel
integer, intent(in) :: h1, h2
hel%defined = .true.
hel%h2 = h2
hel%h1 = h1
end subroutine helicity_init_different
@ %def helicity_init
@ Undefine:
<<Helicities: helicity: TBP>>=
procedure :: undefine => helicity_undefine
<<Helicities: procedures>>=
elemental subroutine helicity_undefine (hel)
class(helicity_t), intent(inout) :: hel
hel%defined = .false.
end subroutine helicity_undefine
@ %def helicity_undefine
@ Diagonalize by removing the second entry (use with care!)
<<Helicities: helicity: TBP>>=
procedure :: diagonalize => helicity_diagonalize
<<Helicities: procedures>>=
elemental subroutine helicity_diagonalize (hel)
class(helicity_t), intent(inout) :: hel
hel%h2 = hel%h1
end subroutine helicity_diagonalize
@ %def helicity_diagonalize
@ Flip helicity indices by sign.
<<Helicities: helicity: TBP>>=
procedure :: flip => helicity_flip
<<Helicities: procedures>>=
elemental subroutine helicity_flip (hel)
class(helicity_t), intent(inout) :: hel
hel%h1 = - hel%h1
hel%h2 = - hel%h2
end subroutine helicity_flip
@ %def helicity_flip
@
<<Helicities: helicity: TBP>>=
procedure :: get_indices => helicity_get_indices
<<Helicities: procedures>>=
subroutine helicity_get_indices (hel, h1, h2)
class(helicity_t), intent(in) :: hel
integer, intent(out) :: h1, h2
h1 = hel%h1; h2 = hel%h2
end subroutine helicity_get_indices
@ %def helicity_get_indices
@ Output (no linebreak). No output if undefined.
<<Helicities: helicity: TBP>>=
procedure :: write => helicity_write
<<Helicities: procedures>>=
subroutine helicity_write (hel, unit)
class(helicity_t), intent(in) :: hel
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
if (hel%defined) then
write (u, "(A)", advance="no") "h("
write (u, "(I0)", advance="no") hel%h1
if (hel%h1 /= hel%h2) then
write (u, "(A)", advance="no") "|"
write (u, "(I0)", advance="no") hel%h2
end if
write (u, "(A)", advance="no") ")"
end if
end subroutine helicity_write
@ %def helicity_write
@ Binary I/O. Write contents only if defined.
<<Helicities: helicity: TBP>>=
procedure :: write_raw => helicity_write_raw
procedure :: read_raw => helicity_read_raw
<<Helicities: procedures>>=
subroutine helicity_write_raw (hel, u)
class(helicity_t), intent(in) :: hel
integer, intent(in) :: u
write (u) hel%defined
if (hel%defined) then
write (u) hel%h1, hel%h2
end if
end subroutine helicity_write_raw
subroutine helicity_read_raw (hel, u, iostat)
class(helicity_t), intent(out) :: hel
integer, intent(in) :: u
integer, intent(out), optional :: iostat
read (u, iostat=iostat) hel%defined
if (hel%defined) then
read (u, iostat=iostat) hel%h1, hel%h2
end if
end subroutine helicity_read_raw
@ %def helicity_write_raw helicity_read_raw
@
\subsection{Predicates}
Check if the helicity is defined:
<<Helicities: helicity: TBP>>=
procedure :: is_defined => helicity_is_defined
<<Helicities: procedures>>=
elemental function helicity_is_defined (hel) result (defined)
logical :: defined
class(helicity_t), intent(in) :: hel
defined = hel%defined
end function helicity_is_defined
@ %def helicity_is_defined
@ Return true if the two helicities are equal or the particle is unpolarized:
<<Helicities: helicity: TBP>>=
procedure :: is_diagonal => helicity_is_diagonal
<<Helicities: procedures>>=
elemental function helicity_is_diagonal (hel) result (diagonal)
logical :: diagonal
class(helicity_t), intent(in) :: hel
if (hel%defined) then
diagonal = hel%h1 == hel%h2
else
diagonal = .true.
end if
end function helicity_is_diagonal
@ %def helicity_is_diagonal
@
\subsection{Accessing contents}
This returns a two-element array and thus cannot be elemental. The
result is unpredictable if the helicity is undefined.
<<Helicities: helicity: TBP>>=
procedure :: to_pair => helicity_to_pair
<<Helicities: procedures>>=
pure function helicity_to_pair (hel) result (h)
integer, dimension(2) :: h
class(helicity_t), intent(in) :: hel
h(1) = hel%h2
h(2) = hel%h1
end function helicity_to_pair
@ %def helicity_to_pair
@
\subsection{Comparisons}
When comparing helicities, if either one is undefined, they are
considered to match. In other words, an unpolarized particle matches
any polarization. In the [[dmatch]] variant, it matches only diagonal
helicity.
<<Helicities: helicity: TBP>>=
generic :: operator(.match.) => helicity_match
generic :: operator(.dmatch.) => helicity_match_diagonal
generic :: operator(==) => helicity_eq
generic :: operator(/=) => helicity_neq
procedure, private :: helicity_match
procedure, private :: helicity_match_diagonal
procedure, private :: helicity_eq
procedure, private :: helicity_neq
@ %def .match. .dmatch. == /=
<<Helicities: procedures>>=
elemental function helicity_match (hel1, hel2) result (eq)
logical :: eq
class(helicity_t), intent(in) :: hel1, hel2
if (hel1%defined .and. hel2%defined) then
eq = (hel1%h1 == hel2%h1) .and. (hel1%h2 == hel2%h2)
else
eq = .true.
end if
end function helicity_match
elemental function helicity_match_diagonal (hel1, hel2) result (eq)
logical :: eq
class(helicity_t), intent(in) :: hel1, hel2
if (hel1%defined .and. hel2%defined) then
eq = (hel1%h1 == hel2%h1) .and. (hel1%h2 == hel2%h2)
else if (hel1%defined) then
eq = hel1%h1 == hel1%h2
else if (hel2%defined) then
eq = hel2%h1 == hel2%h2
else
eq = .true.
end if
end function helicity_match_diagonal
@ %def helicity_match helicity_match_diagonal
<<Helicities: procedures>>=
elemental function helicity_eq (hel1, hel2) result (eq)
logical :: eq
class(helicity_t), intent(in) :: hel1, hel2
if (hel1%defined .and. hel2%defined) then
eq = (hel1%h1 == hel2%h1) .and. (hel1%h2 == hel2%h2)
else if (.not. hel1%defined .and. .not. hel2%defined) then
eq = .true.
else
eq = .false.
end if
end function helicity_eq
@ %def helicity_eq
<<Helicities: procedures>>=
elemental function helicity_neq (hel1, hel2) result (neq)
logical :: neq
class(helicity_t), intent(in) :: hel1, hel2
if (hel1%defined .and. hel2%defined) then
neq = (hel1%h1 /= hel2%h1) .or. (hel1%h2 /= hel2%h2)
else if (.not. hel1%defined .and. .not. hel2%defined) then
neq = .false.
else
neq = .true.
end if
end function helicity_neq
@ %def helicity_neq
@
\subsection{Tools}
Merge two helicity objects by taking the first entry from the first and
the second entry from the second argument. Makes sense only if the
input helicities were defined and diagonal. The handling of ghost
flags is not well-defined; one should verify beforehand that they
match.
<<Helicities: helicity: TBP>>=
generic :: operator(.merge.) => merge_helicities
procedure, private :: merge_helicities
@ %def .merge.
<<Helicities: procedures>>=
elemental function merge_helicities (hel1, hel2) result (hel)
type(helicity_t) :: hel
class(helicity_t), intent(in) :: hel1, hel2
if (hel1%defined .and. hel2%defined) then
call hel%init (hel2%h1, hel1%h1)
else if (hel1%defined) then
call hel%init (hel1%h2, hel1%h1)
else if (hel2%defined) then
call hel%init (hel2%h2, hel2%h1)
end if
end function merge_helicities
@ %def merge_helicities
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Colors}
This module defines a type and tools for dealing with color information.
Each particle can have zero or more (in practice, usually not more
than two) color indices. Color indices are positive; flow direction
can be determined from the particle nature.
While parton shower matrix elements are diagonal in color, some
special applications (e.g., subtractions for NLO matrix elements)
require non-diagonal color matrices.
<<[[colors.f90]]>>=
<<File header>>
module colors
<<Use kinds>>
<<Use strings>>
use io_units
use diagnostics
<<Standard module head>>
<<Colors: public>>
<<Colors: types>>
<<Colors: interfaces>>
contains
<<Colors: procedures>>
end module colors
@ %def colors
@
\subsection{The color type}
A particle may have an arbitrary number of color indices (in practice,
from zero to two, but more are possible). This object acts as a
container. (The current implementation has a fixed array of length two.)
The fact that color comes as an array prohibits elemental procedures
in some places. (May add interfaces and multi versions where
necessary.)
The color may be undefined.
NOTE: Due to a compiler bug in nagfor 5.2, we do not use allocatable
but fixed-size arrays with dimension 2. Only nonzero entries count.
This may be more efficient anyway, but gives up some flexibility.
However, the squaring algorithm currently works only for singlets,
(anti)triplets and octets anyway, so two components are enough.
This type has to be generalized (abstract type and specific
implementations) when trying to pursue generalized color flows or
Monte Carlo over continuous color.
<<Colors: public>>=
public :: color_t
<<Colors: types>>=
type :: color_t
private
logical :: defined = .false.
integer, dimension(2) :: c1 = 0, c2 = 0
logical :: ghost = .false.
contains
<<Colors: color: TBP>>
end type color_t
@ %def color_t
@ Initializers:
<<Colors: color: TBP>>=
generic :: init => &
color_init_trivial, color_init_trivial_ghost, &
color_init_array, color_init_array_ghost, &
color_init_arrays, color_init_arrays_ghost
procedure, private :: color_init_trivial
procedure, private :: color_init_trivial_ghost
procedure, private :: color_init_array
procedure, private :: color_init_array_ghost
procedure, private :: color_init_arrays
procedure, private :: color_init_arrays_ghost
@ Undefined color: array remains unallocated
<<Colors: procedures>>=
pure subroutine color_init_trivial (col)
class(color_t), intent(inout) :: col
col%defined = .true.
col%c1 = 0
col%c2 = 0
col%ghost = .false.
end subroutine color_init_trivial
pure subroutine color_init_trivial_ghost (col, ghost)
class(color_t), intent(inout) :: col
logical, intent(in) :: ghost
col%defined = .true.
col%c1 = 0
col%c2 = 0
col%ghost = ghost
end subroutine color_init_trivial_ghost
@ This defines color from an arbitrary length color array, suitable
for any representation. We may have two color arrays (non-diagonal
matrix elements). This cannot be elemental. The third version
assigns an array of colors, using a two-dimensional array as input.
<<Colors: procedures>>=
pure subroutine color_init_array (col, c1)
class(color_t), intent(inout) :: col
integer, dimension(:), intent(in) :: c1
col%defined = .true.
col%c1 = pack (c1, c1 /= 0, [0,0])
col%c2 = col%c1
col%ghost = .false.
end subroutine color_init_array
pure subroutine color_init_array_ghost (col, c1, ghost)
class(color_t), intent(inout) :: col
integer, dimension(:), intent(in) :: c1
logical, intent(in) :: ghost
call color_init_array (col, c1)
col%ghost = ghost
end subroutine color_init_array_ghost
pure subroutine color_init_arrays (col, c1, c2)
class(color_t), intent(inout) :: col
integer, dimension(:), intent(in) :: c1, c2
col%defined = .true.
if (size (c1) == size (c2)) then
col%c1 = pack (c1, c1 /= 0, [0,0])
col%c2 = pack (c2, c2 /= 0, [0,0])
else if (size (c1) /= 0) then
col%c1 = pack (c1, c1 /= 0, [0,0])
col%c2 = col%c1
else if (size (c2) /= 0) then
col%c1 = pack (c2, c2 /= 0, [0,0])
col%c2 = col%c1
end if
col%ghost = .false.
end subroutine color_init_arrays
pure subroutine color_init_arrays_ghost (col, c1, c2, ghost)
class(color_t), intent(inout) :: col
integer, dimension(:), intent(in) :: c1, c2
logical, intent(in) :: ghost
call color_init_arrays (col, c1, c2)
col%ghost = ghost
end subroutine color_init_arrays_ghost
@ %def color_init
@ This version is restricted to singlets, triplets, antitriplets, and
octets: The input contains the color and anticolor index, each of the
may be zero.
<<Colors: color: TBP>>=
procedure :: init_col_acl => color_init_col_acl
<<Colors: procedures>>=
elemental subroutine color_init_col_acl (col, col_in, acl_in)
class(color_t), intent(inout) :: col
integer, intent(in) :: col_in, acl_in
integer, dimension(0) :: null_array
select case (col_in)
case (0)
select case (acl_in)
case (0)
call color_init_array (col, null_array)
case default
call color_init_array (col, [-acl_in])
end select
case default
select case (acl_in)
case (0)
call color_init_array (col, [col_in])
case default
call color_init_array (col, [col_in, -acl_in])
end select
end select
end subroutine color_init_col_acl
@ %def color_init_col_acl
@ This version is used for the external interface. We convert a
fixed-size array of colors (for each particle) to the internal form by
packing only the nonzero entries.
Some of these procedures produce an arry, so they can't be all
type-bound. We implement them as ordinary procedures.
<<Colors: public>>=
public :: color_init_from_array
<<Colors: interfaces>>=
interface color_init_from_array
module procedure color_init_from_array1
module procedure color_init_from_array1g
module procedure color_init_from_array2
module procedure color_init_from_array2g
end interface color_init_from_array
@ %def color_init_from_array
<<Colors: procedures>>=
pure subroutine color_init_from_array1 (col, c1)
type(color_t), intent(inout) :: col
integer, dimension(:), intent(in) :: c1
logical, dimension(size(c1)) :: mask
mask = c1 /= 0
col%defined = .true.
col%c1 = pack (c1, mask, col%c1)
col%c2 = col%c1
col%ghost = .false.
end subroutine color_init_from_array1
pure subroutine color_init_from_array1g (col, c1, ghost)
type(color_t), intent(inout) :: col
integer, dimension(:), intent(in) :: c1
logical, intent(in) :: ghost
call color_init_from_array1 (col, c1)
col%ghost = ghost
end subroutine color_init_from_array1g
pure subroutine color_init_from_array2 (col, c1)
integer, dimension(:,:), intent(in) :: c1
type(color_t), dimension(:), intent(inout) :: col
integer :: i
do i = 1, size (c1,2)
call color_init_from_array1 (col(i), c1(:,i))
end do
end subroutine color_init_from_array2
pure subroutine color_init_from_array2g (col, c1, ghost)
integer, dimension(:,:), intent(in) :: c1
type(color_t), dimension(:), intent(out) :: col
logical, intent(in), dimension(:) :: ghost
call color_init_from_array2 (col, c1)
col%ghost = ghost
end subroutine color_init_from_array2g
@ %def color_init_from_array
@ Set the ghost property
<<Colors: color: TBP>>=
procedure :: set_ghost => color_set_ghost
<<Colors: procedures>>=
elemental subroutine color_set_ghost (col, ghost)
class(color_t), intent(inout) :: col
logical, intent(in) :: ghost
col%ghost = ghost
end subroutine color_set_ghost
@ %def color_set_ghost
@ Undefine the color state:
<<Colors: color: TBP>>=
procedure :: undefine => color_undefine
<<Colors: procedures>>=
elemental subroutine color_undefine (col, undefine_ghost)
class(color_t), intent(inout) :: col
logical, intent(in), optional :: undefine_ghost
col%defined = .false.
if (present (undefine_ghost)) then
if (undefine_ghost) col%ghost = .false.
else
col%ghost = .false.
end if
end subroutine color_undefine
@ %def color_undefine
@ Output. As dense as possible, no linebreak. If color is undefined,
no output.
The separate version for a color array suggest two distinct interfaces.
<<Colors: public>>=
public :: color_write
<<Colors: interfaces>>=
interface color_write
module procedure color_write_single
module procedure color_write_array
end interface color_write
<<Colors: color: TBP>>=
procedure :: write => color_write_single
<<Colors: procedures>>=
subroutine color_write_single (col, unit)
class(color_t), intent(in) :: col
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
if (col%ghost) then
write (u, "(A)", advance="no") "c*"
else if (col%defined) then
write (u, "(A)", advance="no") "c("
if (col%c1(1) /= 0) write (u, "(I0)", advance="no") col%c1(1)
if (any (col%c1 /= 0)) write (u, "(1x)", advance="no")
if (col%c1(2) /= 0) write (u, "(I0)", advance="no") col%c1(2)
if (.not. col%is_diagonal ()) then
write (u, "(A)", advance="no") "|"
if (col%c2(1) /= 0) write (u, "(I0)", advance="no") col%c2(1)
if (any (col%c2 /= 0)) write (u, "(1x)", advance="no")
if (col%c2(2) /= 0) write (u, "(I0)", advance="no") col%c2(2)
end if
write (u, "(A)", advance="no") ")"
end if
end subroutine color_write_single
subroutine color_write_array (col, unit)
type(color_t), dimension(:), intent(in) :: col
integer, intent(in), optional :: unit
integer :: u
integer :: i
u = given_output_unit (unit); if (u < 0) return
write (u, "(A)", advance="no") "["
do i = 1, size (col)
if (i > 1) write (u, "(1x)", advance="no")
call color_write_single (col(i), u)
end do
write (u, "(A)", advance="no") "]"
end subroutine color_write_array
@ %def color_write
@ Binary I/O. For allocatable colors, this would have to be modified.
<<Colors: color: TBP>>=
procedure :: write_raw => color_write_raw
procedure :: read_raw => color_read_raw
<<Colors: procedures>>=
subroutine color_write_raw (col, u)
class(color_t), intent(in) :: col
integer, intent(in) :: u
logical :: defined
defined = col%is_defined () .or. col%is_ghost ()
write (u) defined
if (defined) then
write (u) col%c1, col%c2
write (u) col%ghost
end if
end subroutine color_write_raw
subroutine color_read_raw (col, u, iostat)
class(color_t), intent(inout) :: col
integer, intent(in) :: u
integer, intent(out), optional :: iostat
logical :: defined
read (u, iostat=iostat) col%defined
if (col%defined) then
read (u, iostat=iostat) col%c1, col%c2
read (u, iostat=iostat) col%ghost
end if
end subroutine color_read_raw
@ %def color_write_raw color_read_raw
@
\subsection{Predicates}
Return the definition status. A color state may be defined but trivial.
<<Colors: color: TBP>>=
procedure :: is_defined => color_is_defined
procedure :: is_nonzero => color_is_nonzero
<<Colors: procedures>>=
elemental function color_is_defined (col) result (defined)
logical :: defined
class(color_t), intent(in) :: col
defined = col%defined
end function color_is_defined
elemental function color_is_nonzero (col) result (flag)
logical :: flag
class(color_t), intent(in) :: col
flag = col%defined &
.and. .not. col%ghost &
.and. any (col%c1 /= 0 .or. col%c2 /= 0)
end function color_is_nonzero
@ %def color_is_defined
@ %def color_is_nonzero
@ Diagonal color objects have only one array allocated:
<<Colors: color: TBP>>=
procedure :: is_diagonal => color_is_diagonal
<<Colors: procedures>>=
elemental function color_is_diagonal (col) result (diagonal)
logical :: diagonal
class(color_t), intent(in) :: col
if (col%defined) then
diagonal = all (col%c1 == col%c2)
else
diagonal = .true.
end if
end function color_is_diagonal
@ %def color_is_diagonal
@ Return the ghost flag
<<Colors: color: TBP>>=
procedure :: is_ghost => color_is_ghost
<<Colors: procedures>>=
elemental function color_is_ghost (col) result (ghost)
logical :: ghost
class(color_t), intent(in) :: col
ghost = col%ghost
end function color_is_ghost
@ %def color_is_ghost
@ The ghost parity: true if the color-ghost flag is set. Again, no
TBP since this is an array.
<<Colors: procedures>>=
pure function color_ghost_parity (col) result (parity)
type(color_t), dimension(:), intent(in) :: col
logical :: parity
parity = mod (count (col%ghost), 2) == 1
end function color_ghost_parity
@ %def color_ghost_parity
@ Determine the color representation, given a color object. We allow
only singlet ($1$), (anti)triplet ($\pm 3$), and octet states ($8$).
A color ghost must not have color assigned, but the color type is $8$. For
non-diagonal color, representations must match. If the color type is
undefined, return $0$. If it is invalid or unsupported, return $-1$.
Assumption: nonzero entries precede nonzero ones.
<<Colors: color: TBP>>=
procedure :: get_type => color_get_type
<<Colors: procedures>>=
elemental function color_get_type (col) result (ctype)
class(color_t), intent(in) :: col
integer :: ctype
if (col%defined) then
ctype = -1
if (col%ghost) then
if (all (col%c1 == 0 .and. col%c2 == 0)) then
ctype = 8
end if
else
if (all ((col%c1 == 0 .and. col%c2 == 0) &
& .or. (col%c1 > 0 .and. col%c2 > 0) &
& .or. (col%c1 < 0 .and. col%c2 < 0))) then
if (all (col%c1 == 0)) then
ctype = 1
else if ((col%c1(1) > 0 .and. col%c1(2) == 0)) then
ctype = 3
else if ((col%c1(1) < 0 .and. col%c1(2) == 0)) then
ctype = -3
else if ((col%c1(1) > 0 .and. col%c1(2) < 0) &
.or.(col%c1(1) < 0 .and. col%c1(2) > 0)) then
ctype = 8
end if
end if
end if
else
ctype = 0
end if
end function color_get_type
-
+
@ %def color_get_type
@
\subsection{Accessing contents}
Return the number of color indices. We assume that it is identical
for both arrays.
<<Colors: color: TBP>>=
procedure, private :: get_number_of_indices => color_get_number_of_indices
<<Colors: procedures>>=
elemental function color_get_number_of_indices (col) result (n)
integer :: n
class(color_t), intent(in) :: col
if (col%defined .and. .not. col%ghost) then
n = count (col%c1 /= 0)
else
n = 0
end if
end function color_get_number_of_indices
@ %def color_get_number_of_indices
@ Return the (first) color/anticolor entry (assuming that color is
diagonal). The result is a positive color index.
<<Colors: color: TBP>>=
procedure :: get_col => color_get_col
procedure :: get_acl => color_get_acl
<<Colors: procedures>>=
elemental function color_get_col (col) result (c)
integer :: c
class(color_t), intent(in) :: col
integer :: i
if (col%defined .and. .not. col%ghost) then
do i = 1, size (col%c1)
if (col%c1(i) > 0) then
c = col%c1(i)
return
end if
end do
end if
c = 0
end function color_get_col
elemental function color_get_acl (col) result (c)
integer :: c
class(color_t), intent(in) :: col
integer :: i
if (col%defined .and. .not. col%ghost) then
do i = 1, size (col%c1)
if (col%c1(i) < 0) then
c = - col%c1(i)
return
end if
end do
end if
c = 0
end function color_get_acl
@ %def color_get_col color_get_acl
@ Return the color index with highest absolute value
<<Colors: public>>=
public :: color_get_max_value
<<Colors: interfaces>>=
interface color_get_max_value
module procedure color_get_max_value0
module procedure color_get_max_value1
module procedure color_get_max_value2
end interface color_get_max_value
<<Colors: procedures>>=
elemental function color_get_max_value0 (col) result (cmax)
integer :: cmax
type(color_t), intent(in) :: col
if (col%defined .and. .not. col%ghost) then
cmax = maxval (abs (col%c1))
else
cmax = 0
end if
end function color_get_max_value0
pure function color_get_max_value1 (col) result (cmax)
integer :: cmax
type(color_t), dimension(:), intent(in) :: col
cmax = maxval (color_get_max_value0 (col))
end function color_get_max_value1
pure function color_get_max_value2 (col) result (cmax)
integer :: cmax
type(color_t), dimension(:,:), intent(in) :: col
integer, dimension(size(col, 2)) :: cm
integer :: i
forall (i = 1:size(col, 2))
cm(i) = color_get_max_value1 (col(:,i))
end forall
cmax = maxval (cm)
end function color_get_max_value2
@ %def color_get_max_value
@
\subsection{Comparisons}
Similar to helicities, colors match if they are equal, or if either
one is undefined.
<<Colors: color: TBP>>=
generic :: operator(.match.) => color_match
generic :: operator(==) => color_eq
generic :: operator(/=) => color_neq
procedure, private :: color_match
procedure, private :: color_eq
procedure, private :: color_neq
@ %def .match. == /=
<<Colors: procedures>>=
elemental function color_match (col1, col2) result (eq)
logical :: eq
class(color_t), intent(in) :: col1, col2
if (col1%defined .and. col2%defined) then
if (col1%ghost .and. col2%ghost) then
eq = .true.
else if (.not. col1%ghost .and. .not. col2%ghost) then
eq = all (col1%c1 == col2%c1) .and. all (col1%c2 == col2%c2)
else
eq = .false.
end if
else
eq = .true.
end if
end function color_match
elemental function color_eq (col1, col2) result (eq)
logical :: eq
class(color_t), intent(in) :: col1, col2
if (col1%defined .and. col2%defined) then
if (col1%ghost .and. col2%ghost) then
eq = .true.
else if (.not. col1%ghost .and. .not. col2%ghost) then
eq = all (col1%c1 == col2%c1) .and. all (col1%c2 == col2%c2)
else
eq = .false.
end if
else if (.not. col1%defined &
.and. .not. col2%defined) then
eq = col1%ghost .eqv. col2%ghost
else
eq = .false.
end if
end function color_eq
@ %def color_eq
<<Colors: procedures>>=
elemental function color_neq (col1, col2) result (neq)
logical :: neq
class(color_t), intent(in) :: col1, col2
if (col1%defined .and. col2%defined) then
if (col1%ghost .and. col2%ghost) then
neq = .false.
else if (.not. col1%ghost .and. .not. col2%ghost) then
neq = any (col1%c1 /= col2%c1) .or. any (col1%c2 /= col2%c2)
else
neq = .true.
end if
else if (.not. col1%defined &
.and. .not. col2%defined) then
neq = col1%ghost .neqv. col2%ghost
else
neq = .true.
end if
end function color_neq
@ %def color_neq
@
\subsection{Tools}
Shift color indices by a common offset.
<<Colors: color: TBP>>=
procedure :: add_offset => color_add_offset
<<Colors: procedures>>=
elemental subroutine color_add_offset (col, offset)
class(color_t), intent(inout) :: col
integer, intent(in) :: offset
if (col%defined .and. .not. col%ghost) then
where (col%c1 /= 0) col%c1 = col%c1 + sign (offset, col%c1)
where (col%c2 /= 0) col%c2 = col%c2 + sign (offset, col%c2)
end if
end subroutine color_add_offset
@ %def color_add_offset
@ Reassign color indices for an array of colored particle in canonical
order. The allocated size of the color map is such that two colors
per particle can be accomodated.
The algorithm works directly on the contents of the color objects, it
<<Colors: public>>=
public :: color_canonicalize
<<Colors: procedures>>=
subroutine color_canonicalize (col)
type(color_t), dimension(:), intent(inout) :: col
integer, dimension(2*size(col)) :: map
integer :: n_col, i, j, k
n_col = 0
do i = 1, size (col)
if (col(i)%defined .and. .not. col(i)%ghost) then
do j = 1, size (col(i)%c1)
if (col(i)%c1(j) /= 0) then
k = find (abs (col(i)%c1(j)), map(:n_col))
if (k == 0) then
n_col = n_col + 1
map(n_col) = abs (col(i)%c1(j))
k = n_col
end if
col(i)%c1(j) = sign (k, col(i)%c1(j))
end if
if (col(i)%c2(j) /= 0) then
k = find (abs (col(i)%c2(j)), map(:n_col))
if (k == 0) then
n_col = n_col + 1
map(n_col) = abs (col(i)%c2(j))
k = n_col
end if
col(i)%c2(j) = sign (k, col(i)%c2(j))
end if
end do
end if
end do
contains
function find (c, array) result (k)
integer :: k
integer, intent(in) :: c
integer, dimension(:), intent(in) :: array
integer :: i
k = 0
do i = 1, size (array)
if (c == array (i)) then
k = i
return
end if
end do
end function find
end subroutine color_canonicalize
@ %def color_canonicalize
@ Return an array of different color indices from an array of colors.
The last argument is a pseudo-color array, where the color entries
correspond to the position of the corresponding index entry in the
index array. The colors are assumed to be diagonal.
The algorithm works directly on the contents of the color objects.
<<Colors: procedures>>=
subroutine extract_color_line_indices (col, c_index, col_pos)
type(color_t), dimension(:), intent(in) :: col
integer, dimension(:), intent(out), allocatable :: c_index
type(color_t), dimension(size(col)), intent(out) :: col_pos
integer, dimension(:), allocatable :: c_tmp
integer :: i, j, k, n, c
allocate (c_tmp (sum (col%get_number_of_indices ())), source=0)
n = 0
SCAN1: do i = 1, size (col)
if (col(i)%defined .and. .not. col(i)%ghost) then
SCAN2: do j = 1, 2
c = abs (col(i)%c1(j))
if (c /= 0) then
do k = 1, n
if (c_tmp(k) == c) then
col_pos(i)%c1(j) = k
cycle SCAN2
end if
end do
n = n + 1
c_tmp(n) = c
col_pos(i)%c1(j) = n
end if
end do SCAN2
end if
end do SCAN1
allocate (c_index (n))
c_index = c_tmp(1:n)
end subroutine extract_color_line_indices
@ %def extract_color_line_indices
@ Given a color array, pairwise contract the color lines in all
possible ways and return the resulting array of arrays. The input
color array must be diagonal, and each color should occur exactly
twice, once as color and once as anticolor.
Gluon entries with equal color and anticolor are explicitly excluded.
This algorithm is generic, but for long arrays it is neither
efficient, nor does it avoid duplicates. It is intended for small
arrays, in particular for the state matrix of a structure-function
pair.
The algorithm works directly on the contents of the color objects, it
thus depends on the implementation.
<<Colors: public>>=
public :: color_array_make_contractions
<<Colors: procedures>>=
subroutine color_array_make_contractions (col_in, col_out)
type(color_t), dimension(:), intent(in) :: col_in
type(color_t), dimension(:,:), intent(out), allocatable :: col_out
type :: entry_t
integer, dimension(:), allocatable :: map
type(color_t), dimension(:), allocatable :: col
type(entry_t), pointer :: next => null ()
logical :: nlo_event = .false.
end type entry_t
type :: list_t
integer :: n = 0
type(entry_t), pointer :: first => null ()
type(entry_t), pointer :: last => null ()
end type list_t
type(list_t) :: list
type(entry_t), pointer :: entry
integer, dimension(:), allocatable :: c_index
type(color_t), dimension(size(col_in)) :: col_pos
integer :: n_prt, n_c_index
integer, dimension(:), allocatable :: map
integer :: i, j, c
n_prt = size (col_in)
call extract_color_line_indices (col_in, c_index, col_pos)
n_c_index = size (c_index)
allocate (map (n_c_index))
map = 0
call list_append_if_valid (list, map)
entry => list%first
do while (associated (entry))
do i = 1, n_c_index
if (entry%map(i) == 0) then
c = c_index(i)
do j = i + 1, n_c_index
if (entry%map(j) == 0) then
map = entry%map
map(i) = c
map(j) = c
call list_append_if_valid (list, map)
end if
end do
end if
end do
entry => entry%next
end do
call list_to_array (list, col_out)
contains
subroutine list_append_if_valid (list, map)
type(list_t), intent(inout) :: list
integer, dimension(:), intent(in) :: map
type(entry_t), pointer :: entry
integer :: i, j, c, p
entry => list%first
do while (associated (entry))
if (all (map == entry%map)) return
entry => entry%next
end do
allocate (entry)
allocate (entry%map (n_c_index))
entry%map = map
allocate (entry%col (n_prt))
do i = 1, n_prt
do j = 1, 2
c = col_in(i)%c1(j)
if (c /= 0) then
p = col_pos(i)%c1(j)
entry%col(i)%defined = .true.
if (map(p) /= 0) then
entry%col(i)%c1(j) = sign (map(p), c)
else
entry%col(i)%c1(j) = c
endif
entry%col(i)%c2(j) = entry%col(i)%c1(j)
end if
end do
if (any (entry%col(i)%c1 /= 0) .and. &
entry%col(i)%c1(1) == - entry%col(i)%c1(2)) return
end do
if (associated (list%last)) then
list%last%next => entry
else
list%first => entry
end if
list%last => entry
list%n = list%n + 1
end subroutine list_append_if_valid
subroutine list_to_array (list, col)
type(list_t), intent(inout) :: list
type(color_t), dimension(:,:), intent(out), allocatable :: col
type(entry_t), pointer :: entry
integer :: i
allocate (col (n_prt, list%n - 1))
do i = 0, list%n - 1
entry => list%first
list%first => list%first%next
if (i /= 0) col(:,i) = entry%col
deallocate (entry)
end do
list%last => null ()
end subroutine list_to_array
end subroutine color_array_make_contractions
@ %def color_array_make_contractions
@ Invert the color index, switching from particle to antiparticle.
For gluons, we have to swap the order of color entries.
<<Colors: color: TBP>>=
procedure :: invert => color_invert
<<Colors: procedures>>=
elemental subroutine color_invert (col)
class(color_t), intent(inout) :: col
if (col%defined .and. .not. col%ghost) then
col%c1 = - col%c1
col%c2 = - col%c2
if (col%c1(1) < 0 .and. col%c1(2) > 0) then
col%c1 = col%c1(2:1:-1)
col%c2 = col%c2(2:1:-1)
end if
end if
end subroutine color_invert
@ %def color_invert
@ Make a color map for two matching color arrays. The result is an
array of integer pairs.
<<Colors: public>>=
public :: make_color_map
<<Colors: interfaces>>=
interface make_color_map
module procedure color_make_color_map
end interface make_color_map
<<Colors: procedures>>=
subroutine color_make_color_map (map, col1, col2)
integer, dimension(:,:), intent(out), allocatable :: map
type(color_t), dimension(:), intent(in) :: col1, col2
integer, dimension(:,:), allocatable :: map1
integer :: i, j, k
allocate (map1 (2, 2 * sum (col1%get_number_of_indices ())))
k = 0
do i = 1, size (col1)
if (col1(i)%defined .and. .not. col1(i)%ghost) then
do j = 1, size (col1(i)%c1)
if (col1(i)%c1(j) /= 0 &
.and. all (map1(1,:k) /= abs (col1(i)%c1(j)))) then
k = k + 1
map1(1,k) = abs (col1(i)%c1(j))
map1(2,k) = abs (col2(i)%c1(j))
end if
if (col1(i)%c2(j) /= 0 &
.and. all (map1(1,:k) /= abs (col1(i)%c2(j)))) then
k = k + 1
map1(1,k) = abs (col1(i)%c2(j))
map1(2,k) = abs (col2(i)%c2(j))
end if
end do
end if
end do
allocate (map (2, k))
map(:,:) = map1(:,:k)
end subroutine color_make_color_map
@ %def make_color_map
@ Translate colors which have a match in the translation table (an
array of integer pairs). Color that do not match an entry are simply
transferred; this is done by first transferring all components, then
modifiying entries where appropriate.
<<Colors: public>>=
public :: color_translate
<<Colors: interfaces>>=
interface color_translate
module procedure color_translate0
module procedure color_translate0_offset
module procedure color_translate1
end interface color_translate
<<Colors: procedures>>=
subroutine color_translate0 (col, map)
type(color_t), intent(inout) :: col
integer, dimension(:,:), intent(in) :: map
type(color_t) :: col_tmp
integer :: i
if (col%defined .and. .not. col%ghost) then
col_tmp = col
do i = 1, size (map,2)
where (abs (col%c1) == map(1,i))
col_tmp%c1 = sign (map(2,i), col%c1)
end where
where (abs (col%c2) == map(1,i))
col_tmp%c2 = sign (map(2,i), col%c2)
end where
end do
col = col_tmp
end if
end subroutine color_translate0
subroutine color_translate0_offset (col, map, offset)
type(color_t), intent(inout) :: col
integer, dimension(:,:), intent(in) :: map
integer, intent(in) :: offset
logical, dimension(size(col%c1)) :: mask1, mask2
type(color_t) :: col_tmp
integer :: i
if (col%defined .and. .not. col%ghost) then
col_tmp = col
mask1 = col%c1 /= 0
mask2 = col%c2 /= 0
do i = 1, size (map,2)
where (abs (col%c1) == map(1,i))
col_tmp%c1 = sign (map(2,i), col%c1)
mask1 = .false.
end where
where (abs (col%c2) == map(1,i))
col_tmp%c2 = sign (map(2,i), col%c2)
mask2 = .false.
end where
end do
col = col_tmp
where (mask1) col%c1 = sign (abs (col%c1) + offset, col%c1)
where (mask2) col%c2 = sign (abs (col%c2) + offset, col%c2)
end if
end subroutine color_translate0_offset
subroutine color_translate1 (col, map, offset)
type(color_t), dimension(:), intent(inout) :: col
integer, dimension(:,:), intent(in) :: map
integer, intent(in), optional :: offset
integer :: i
if (present (offset)) then
do i = 1, size (col)
call color_translate0_offset (col(i), map, offset)
end do
else
do i = 1, size (col)
call color_translate0 (col(i), map)
end do
end if
end subroutine color_translate1
@ %def color_translate
@ Merge two color objects by taking the first entry from the first and
the first entry from the second argument. Makes sense only if the
input colors are defined (and diagonal). If either one is undefined,
transfer the defined one.
<<Colors: color: TBP>>=
generic :: operator(.merge.) => merge_colors
procedure, private :: merge_colors
@ %def .merge.
<<Colors: procedures>>=
elemental function merge_colors (col1, col2) result (col)
type(color_t) :: col
class(color_t), intent(in) :: col1, col2
if (color_is_defined (col1) .and. color_is_defined (col2)) then
if (color_is_ghost (col1) .and. color_is_ghost (col2)) then
call color_init_trivial_ghost (col, .true.)
else
call color_init_arrays (col, col1%c1, col2%c1)
end if
else if (color_is_defined (col1)) then
call color_init_array (col, col1%c1)
else if (color_is_defined (col2)) then
call color_init_array (col, col2%c1)
end if
end function merge_colors
@ %def merge_colors
@ Merge up to two (diagonal!) color objects. The result inherits the unmatched
color lines of the input colors. If one of the input colors is
undefined, the output is undefined as well. It must be in a supported
color representation.
A color-ghost object should not actually occur in real-particle
events, but for completeness we define its behavior. For simplicity,
it is identified as a color-octet with zero color/anticolor. It can
only couple to a triplet or antitriplet. A fusion of triplet with
matching antitriplet will yield a singlet, not a ghost, however.
If the fusion fails, the result is undefined.
NOTE: The [[select type]] casting is required by gfortran 4.8. It may not be
required by the standard.
<<Colors: color: TBP>>=
generic :: operator (.fuse.) => color_fusion
procedure, private :: color_fusion
<<Colors: procedures>>=
function color_fusion (col1, col2) result (col)
class(color_t), intent(in) :: col1, col2
type(color_t) :: col
integer, dimension(2) :: ctype
if (col1%is_defined () .and. col2%is_defined ()) then
if (col1%is_diagonal () .and. col2%is_diagonal ()) then
select type (col1)
type is (color_t)
select type (col2)
type is (color_t)
ctype = [col1%get_type (), col2%get_type ()]
select case (ctype(1))
case (1)
select case (ctype(2))
case (1,3,-3,8)
col = col2
end select
case (3)
select case (ctype(2))
case (1)
col = col1
case (-3)
call t_a (col1%get_col (), col2%get_acl ())
case (8)
call t_o (col1%get_col (), col2%get_acl (), &
& col2%get_col ())
end select
case (-3)
select case (ctype(2))
case (1)
col = col1
case (3)
call t_a (col2%get_col (), col1%get_acl ())
case (8)
call a_o (col1%get_acl (), col2%get_col (), &
& col2%get_acl ())
end select
case (8)
select case (ctype(2))
case (1)
col = col1
case (3)
call t_o (col2%get_col (), col1%get_acl (), &
& col1%get_col ())
case (-3)
call a_o (col2%get_acl (), col1%get_col (), &
& col1%get_acl ())
case (8)
call o_o (col1%get_col (), col1%get_acl (), &
& col2%get_col (), col2%get_acl ())
end select
end select
end select
end select
end if
end if
contains
subroutine t_a (c1, c2)
integer, intent(in) :: c1, c2
if (c1 == c2) then
call col%init_col_acl (0, 0)
else
call col%init_col_acl (c1, c2)
end if
end subroutine t_a
subroutine t_o (c1, c2, c3)
integer, intent(in) :: c1, c2, c3
if (c1 == c2) then
call col%init_col_acl (c3, 0)
else if (c2 == 0 .and. c3 == 0) then
call col%init_col_acl (c1, 0)
end if
end subroutine t_o
subroutine a_o (c1, c2, c3)
integer, intent(in) :: c1, c2, c3
if (c1 == c2) then
call col%init_col_acl (0, c3)
else if (c2 == 0 .and. c3 == 0) then
call col%init_col_acl (0, c1)
end if
end subroutine a_o
subroutine o_o (c1, c2, c3, c4)
integer, intent(in) :: c1, c2, c3, c4
if (all ([c1,c2,c3,c4] /= 0)) then
if (c2 == c3 .and. c4 == c1) then
call col%init_col_acl (0, 0)
else if (c2 == c3) then
call col%init_col_acl (c1, c4)
else if (c4 == c1) then
call col%init_col_acl (c3, c2)
end if
end if
end subroutine o_o
end function color_fusion
@ %def color_fusion
@ Compute the color factor, given two interfering color arrays.
<<Colors: public>>=
public :: compute_color_factor
<<Colors: procedures>>=
function compute_color_factor (col1, col2, nc) result (factor)
real(default) :: factor
type(color_t), dimension(:), intent(in) :: col1, col2
integer, intent(in), optional :: nc
type(color_t), dimension(size(col1)) :: col
integer :: ncol, nloops, nghost
ncol = 3; if (present (nc)) ncol = nc
col = col1 .merge. col2
nloops = count_color_loops (col)
nghost = count (col%is_ghost ())
factor = real (ncol, default) ** (nloops - nghost)
if (color_ghost_parity (col)) factor = - factor
end function compute_color_factor
@ %def compute_color_factor
@
We have a pair of color index arrays which corresponds to a squared
matrix element. We want to determine the number of color loops in
this square matrix element. So we first copy the colors (stored in a
single color array with a pair of color lists in each entry) to a
temporary where the color indices are shifted by some offset. We then
recursively follow each loop, starting at the first color that has the
offset, resetting the first color index to the loop index and each
further index to zero as we go. We check that (a) each color index
occurs twice within the left (right) color array, (b) the loops are
closed, so we always come back to a line which has the loop index.
In order for the algorithm to work we have to conjugate the colors of
initial state particles (one for decays, two for scatterings) into
their corresponding anticolors of outgoing particles.
<<Colors: public>>=
public :: count_color_loops
<<Colors: procedures>>=
function count_color_loops (col) result (count)
integer :: count
type(color_t), dimension(:), intent(in) :: col
type(color_t), dimension(size(col)) :: cc
integer :: i, n, offset
cc = col
n = size (cc)
offset = n
call color_add_offset (cc, offset)
count = 0
SCAN_LOOPS: do
do i = 1, n
if (color_is_nonzero (cc(i))) then
if (any (cc(i)%c1 > offset)) then
count = count + 1
call follow_line1 (pick_new_line (cc(i)%c1, count, 1))
cycle SCAN_LOOPS
end if
end if
end do
exit SCAN_LOOPS
end do SCAN_LOOPS
contains
function pick_new_line (c, reset_val, sgn) result (line)
integer :: line
integer, dimension(:), intent(inout) :: c
integer, intent(in) :: reset_val
integer, intent(in) :: sgn
integer :: i
if (any (c == count)) then
line = count
else
do i = 1, size (c)
if (sign (1, c(i)) == sgn .and. abs (c(i)) > offset) then
line = c(i)
c(i) = reset_val
return
end if
end do
call color_mismatch
end if
end function pick_new_line
subroutine reset_line (c, line)
integer, dimension(:), intent(inout) :: c
integer, intent(in) :: line
integer :: i
do i = 1, size (c)
if (c(i) == line) then
c(i) = 0
return
end if
end do
end subroutine reset_line
recursive subroutine follow_line1 (line)
integer, intent(in) :: line
integer :: i
if (line == count) return
do i = 1, n
if (any (cc(i)%c1 == -line)) then
call reset_line (cc(i)%c1, -line)
call follow_line2 (pick_new_line (cc(i)%c2, 0, sign (1, -line)))
return
end if
end do
call color_mismatch ()
end subroutine follow_line1
recursive subroutine follow_line2 (line)
integer, intent(in) :: line
integer :: i
do i = 1, n
if (any (cc(i)%c2 == -line)) then
call reset_line (cc(i)%c2, -line)
call follow_line1 (pick_new_line (cc(i)%c1, 0, sign (1, -line)))
return
end if
end do
call color_mismatch ()
end subroutine follow_line2
subroutine color_mismatch ()
call color_write (col)
print *
call msg_fatal ("Color flow mismatch: Non-closed color lines appear during ", &
[var_str ("the evaluation of color correlations. This can happen if there "), &
var_str ("are different color structures in the initial or final state of "), &
var_str ("the process definition. If so, please use separate processes for "), &
var_str ("the different initial / final states. In a future WHIZARD version "), &
var_str ("this will be fixed.")])
end subroutine color_mismatch
end function count_color_loops
@ %def count_color_loops
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[colors_ut.f90]]>>=
<<File header>>
module colors_ut
use unit_tests
use colors_uti
<<Standard module head>>
<<Colors: public test>>
contains
<<Colors: test driver>>
end module colors_ut
@ %def colors_ut
@
<<[[colors_uti.f90]]>>=
<<File header>>
module colors_uti
use colors
<<Standard module head>>
<<Colors: test declarations>>
contains
<<Colors: tests>>
end module colors_uti
@ %def colors_ut
@ API: driver for the unit tests below.
<<Colors: public test>>=
public :: color_test
<<Colors: test driver>>=
subroutine color_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Colors: execute tests>>
end subroutine color_test
@ %def color_test
@ This is a color counting test.
<<Colors: execute tests>>=
call test (color_1, "color_1", &
"check color counting", &
u, results)
<<Colors: test declarations>>=
public :: color_1
<<Colors: tests>>=
subroutine color_1 (u)
integer, intent(in) :: u
type(color_t), dimension(4) :: col1, col2, col
type(color_t), dimension(:), allocatable :: col3
type(color_t), dimension(:,:), allocatable :: col_array
integer :: count, i
call col1%init_col_acl ([1, 0, 2, 3], [0, 1, 3, 2])
col2 = col1
call color_write (col1, u)
write (u, "(A)")
call color_write (col2, u)
write (u, "(A)")
col = col1 .merge. col2
call color_write (col, u)
write (u, "(A)")
count = count_color_loops (col)
write (u, "(A,I1)") "Number of color loops (3): ", count
call col2%init_col_acl ([1, 0, 2, 3], [0, 2, 3, 1])
call color_write (col1, u)
write (u, "(A)")
call color_write (col2, u)
write (u, "(A)")
col = col1 .merge. col2
call color_write (col, u)
write (u, "(A)")
count = count_color_loops (col)
write (u, "(A,I1)") "Number of color loops (2): ", count
write (u, "(A)")
allocate (col3 (4))
call color_init_from_array (col3, &
reshape ([1, 0, 0, -1, 2, -3, 3, -2], &
[2, 4]))
call color_write (col3, u)
write (u, "(A)")
call color_array_make_contractions (col3, col_array)
write (u, "(A)") "Contractions:"
do i = 1, size (col_array, 2)
call color_write (col_array(:,i), u)
write (u, "(A)")
end do
deallocate (col3)
write (u, "(A)")
allocate (col3 (6))
call color_init_from_array (col3, &
reshape ([1, -2, 3, 0, 0, -1, 2, -4, -3, 0, 4, 0], &
[2, 6]))
call color_write (col3, u)
write (u, "(A)")
call color_array_make_contractions (col3, col_array)
write (u, "(A)") "Contractions:"
do i = 1, size (col_array, 2)
call color_write (col_array(:,i), u)
write (u, "(A)")
end do
end subroutine color_1
@ %def color_1
@ A color fusion test.
<<Colors: execute tests>>=
call test (color_2, "color_2", &
"color fusion", &
u, results)
<<Colors: test declarations>>=
public :: color_2
<<Colors: tests>>=
subroutine color_2 (u)
integer, intent(in) :: u
type(color_t) :: s1, t1, t2, a1, a2, o1, o2, o3, o4, g1
write (u, "(A)") "* Test output: color_2"
write (u, "(A)") "* Purpose: test all combinations for color-object fusion"
write (u, "(A)")
-
+
call s1%init_col_acl (0,0)
call t1%init_col_acl (1,0)
call t2%init_col_acl (2,0)
call a1%init_col_acl (0,1)
call a2%init_col_acl (0,2)
call o1%init_col_acl (1,2)
call o2%init_col_acl (1,3)
call o3%init_col_acl (2,3)
call o4%init_col_acl (2,1)
call g1%init (ghost=.true.)
call wrt ("s1", s1)
call wrt ("t1", t1)
call wrt ("t2", t2)
call wrt ("a1", a1)
call wrt ("a2", a2)
call wrt ("o1", o1)
call wrt ("o2", o2)
call wrt ("o3", o3)
call wrt ("o4", o4)
call wrt ("g1", g1)
write (u, *)
-
+
call wrt ("s1 * s1", s1 .fuse. s1)
write (u, *)
-
+
call wrt ("s1 * t1", s1 .fuse. t1)
call wrt ("s1 * a1", s1 .fuse. a1)
call wrt ("s1 * o1", s1 .fuse. o1)
write (u, *)
-
+
call wrt ("t1 * s1", t1 .fuse. s1)
call wrt ("a1 * s1", a1 .fuse. s1)
call wrt ("o1 * s1", o1 .fuse. s1)
write (u, *)
-
+
call wrt ("t1 * t1", t1 .fuse. t1)
write (u, *)
-
+
call wrt ("t1 * t2", t1 .fuse. t2)
call wrt ("t1 * a1", t1 .fuse. a1)
call wrt ("t1 * a2", t1 .fuse. a2)
call wrt ("t1 * o1", t1 .fuse. o1)
call wrt ("t2 * o1", t2 .fuse. o1)
write (u, *)
-
+
call wrt ("t2 * t1", t2 .fuse. t1)
call wrt ("a1 * t1", a1 .fuse. t1)
call wrt ("a2 * t1", a2 .fuse. t1)
call wrt ("o1 * t1", o1 .fuse. t1)
call wrt ("o1 * t2", o1 .fuse. t2)
write (u, *)
-
+
call wrt ("a1 * a1", a1 .fuse. a1)
write (u, *)
-
+
call wrt ("a1 * a2", a1 .fuse. a2)
call wrt ("a1 * o1", a1 .fuse. o1)
call wrt ("a2 * o2", a2 .fuse. o2)
write (u, *)
-
+
call wrt ("a2 * a1", a2 .fuse. a1)
call wrt ("o1 * a1", o1 .fuse. a1)
call wrt ("o2 * a2", o2 .fuse. a2)
write (u, *)
-
+
call wrt ("o1 * o1", o1 .fuse. o1)
write (u, *)
-
+
call wrt ("o1 * o2", o1 .fuse. o2)
call wrt ("o1 * o3", o1 .fuse. o3)
call wrt ("o1 * o4", o1 .fuse. o4)
write (u, *)
-
+
call wrt ("o2 * o1", o2 .fuse. o1)
call wrt ("o3 * o1", o3 .fuse. o1)
call wrt ("o4 * o1", o4 .fuse. o1)
write (u, *)
-
+
call wrt ("g1 * g1", g1 .fuse. g1)
write (u, *)
-
+
call wrt ("g1 * s1", g1 .fuse. s1)
call wrt ("g1 * t1", g1 .fuse. t1)
call wrt ("g1 * a1", g1 .fuse. a1)
call wrt ("g1 * o1", g1 .fuse. o1)
write (u, *)
-
+
call wrt ("s1 * g1", s1 .fuse. g1)
call wrt ("t1 * g1", t1 .fuse. g1)
call wrt ("a1 * g1", a1 .fuse. g1)
call wrt ("o1 * g1", o1 .fuse. g1)
write (u, "(A)")
write (u, "(A)") "* Test output end: color_2"
contains
-
+
subroutine wrt (s, col)
character(*), intent(in) :: s
class(color_t), intent(in) :: col
write (u, "(A,1x,'=',1x)", advance="no") s
call col%write (u)
write (u, *)
end subroutine wrt
end subroutine color_2
@ %def color_2
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{The Madgraph color model}
This section describes the method for matrix element and color
flow calculation within Madgraph.
For each Feynman diagram, the colorless amplitude for a specified
helicity and momentum configuration (in- and out- combined) is
computed:
\begin{equation}
A_d(p,h)
\end{equation}
Inserting color, the squared matrix element for definite helicity and
momentum is
\begin{equation}
M^2(p,h) = \sum_{dd'} A_{d}(p,h)\,C_{dd'} A_{d'}^*(p,h)
\end{equation}
where $C_{dd'}$ describes the color interference of the two diagrams
$A_d$ and $A_d'$, which is independent of momentum and helicity and
can be calculated for each Feynman diagram pair by reducing it to the
corresponding color graph. Obviously, one could combine all diagrams
with identical color structure, such that the index $d$ runs only over
different color graphs. For colorless diagrams all elements of
$C_{dd'}$ are equal to unity.
The hermitian matrix $C_{dd'}$ is diagonalized once and for all, such
that it can be written in the form
\begin{equation}
C_{dd'} = \sum_\lambda c_d^\lambda \lambda\, c_d^\lambda{}^*,
\end{equation}
where the eigenvectors $c_d$ are normalized,
\begin{equation}
\sum_d |c_d^\lambda|^2 = 1,
\end{equation}
and the $\lambda$ values are the corresponding eigenvalues. In the
colorless case, this means $c_d = 1/\sqrt{N_d}$ for all diagrams
($N_d=$ number of diagrams), and $\lambda=N_d$ is the only nonzero
eigenvalue.
Consequently, the squared matrix element for definite helicity and
momentum can also be written as
\begin{equation}
M^2(p,h) = \sum_\lambda A_\lambda(p,h)\, \lambda\, A_\lambda(p,h)^*
\end{equation}
with
\begin{equation}
A_\lambda(p,h) = \sum_d c_d^\lambda A_d(p,h).
\end{equation}
For generic spin density matrices, this is easily generalized to
\begin{equation}
M^2(p,h,h') = \sum_\lambda A_\lambda(p,h)\, \lambda\, A_\lambda(p,h')^*
\end{equation}
To determine the color flow probabilities of a given momentum-helicity
configuration, the color flow amplitudes are calculated as
\begin{equation}
a_f(p,h) = \sum_d \beta^f_d A_d(p,h),
\end{equation}
where the coefficients $\beta^f_d$ describe the amplitude for a given
Feynman diagram (or color graph) $d$ to correspond to a definite color
flow~$f$. They are computed from $C_{dd'}$ by transforming this
matrix into the color flow basis and neglecting all off-diagonal
elements. Again, these coefficients do not depend on momentum or
helicity and can therefore be calculated in advance. This gives the
color flow transition matrix
\begin{equation}
F^f(p,h,h') = a_f(p,h)\, a^*_f(p,h')
\end{equation}
which is assumed diagonal in color flow space and is separate from the
color-summed transition matrix $M^2$. They are, however, equivalent
(up to a factor) to leading order in $1/N_c$, and using the color flow
transition matrix is appropriate for matching to hadronization.
Note that the color flow transition matrix is not normalized at this
stage. To make use of it, we have to fold it with the in-state
density matrix to get a pseudo density matrix
\begin{equation}
\hat\rho_{\rm out}^f(p,h_{\rm out},h'_{\rm out})
= \sum_{h_{\rm in} h'_{\rm in}} F^f(p,h,h')\,
\rho_{\rm in}(p,h_{\rm in},h'_{\rm in})
\end{equation}
which gets a meaning only after contracted with projections on the
outgoing helicity states $k_{\rm out}$, given as linear combinations
of helicity states with the unitary coefficient matrix $c(k_{\rm out},
h_{\rm out})$. Then the probability of finding color flow $f$ when
the helicity state $k_{\rm out}$ is measured is given by
\begin{equation}
P^f(p, k_{\rm out}) = Q^f(p, k_{\rm out}) / \sum_f Q^f(p, k_{\rm out})
\end{equation}
where
\begin{equation}
Q^f(p, k_{\rm out}) = \sum_{h_{\rm out} h'_{\rm out}}
c(k_{\rm out}, h_{\rm out})\,
\hat\rho_{\rm out}^f(p,h_{\rm out},h'_{\rm out})\,
c^*(k_{\rm out}, h'_{\rm out})
\end{equation}
However, if we can assume that the out-state helicity basis is the
canonical one, we can throw away the off diagonal elements in the
color flow density matrix and normalize the ones on the diagonal to obtain
\begin{equation}
P^f(p, h_{\rm out}) =
\hat\rho_{\rm out}^f(p,h_{\rm out},h_{\rm out}) /
\sum_f \hat\rho_{\rm out}^f(p,h_{\rm out},h_{\rm out})
\end{equation}
Finally, the color-summed out-state density matrix is computed by the
scattering formula
\begin{align}
{\rho_{\rm out}(p,h_{\rm out},h'_{\rm out})}
&=
\sum_{h_{\rm in} h'_{\rm in}} M^2(p,h,h')\,
\rho_{\rm in}(p,h_{\rm in},h'_{\rm in}) \\
&= \sum_{h_{\rm in} h'_{\rm in} \lambda}
A_\lambda(p,h)\, \lambda\, A_\lambda(p,h')^*
\rho_{\rm in}(p,h_{\rm in},h'_{\rm in}),
\end{align}
The trace of $\rho_{\rm out}$ is the squared matrix element, summed
over all internal degrees of freedom. To get the squared matrix
element for a definite helicity $k_{\rm out}$ and color flow $f$, one
has to project the density matrix onto the given helicity state and
multiply with $P^f(p, k_{\rm out})$.
For diagonal helicities the out-state density reduces to
\begin{equation}
\rho_{\rm out}(p,h_{\rm out})
= \sum_{h_{\rm in}\lambda}
\lambda|A_\lambda(p,h)|^2 \rho_{\rm in}(p,h_{\rm in}).
\end{equation}
Since no basis transformation is involved, we can use the normalized
color flow probability $P^f(p, h_{\rm out})$ and express the result as
\begin{align}
\rho_{\rm out}^f(p,h_{\rm out})
&= \rho_{\rm out}(p,h_{\rm out})\,P^f(p, h_{\rm out}) \\
&= \sum_{h_{\rm in}\lambda}
\frac{|a^f(p,h)|^2}{\sum_f|a^f(p,h)|^2}
\lambda|A_\lambda(p,h)|^2 \rho_{\rm in}(p,h_{\rm in}).
\end{align}
From these considerations, the following calculation strategy can be
derived:
\begin{itemize}
\item
Before the first event is generated, the color interference matrix
$C_{dd'}$ is computed and diagonalized, so the eigenvectors
$c^\lambda_d$, eigenvalues $\lambda$ and color flow coefficients
$\beta^f_d$ are obtained. In practice, these calculations are
done when the matrix element code is generated, and the results are
hardcoded in the matrix element subroutine as [[DATA]] statements.
\item
For each event, one loops over helicities once and stores the
matrices $A_\lambda(p,h)$ and $a^f(p,h)$. The allowed color flows,
helicity combinations and eigenvalues are each labeled by integer
indices, so one has to store complex matrices of dimension
$N_\lambda\times N_h$ and $N_f\times N_h$, respectively.
\item
The further strategy depends on the requested information.
\begin{enumerate}
\item
If colorless diagonal helicity amplitudes are required, the
eigenvalues $A_\lambda(p,h)$ are squared, summed with weight
$\lambda$, and the result contracted with the in-state probability
vector $\rho_{\rm in}(p, h_{\rm in})$. The result is a
probability vector $\rho_{\rm out}(p, h_{\rm out})$.
\item
For colored diagonal helicity amplitudes, the color coefficients
$a^f(p,h)$ are also squared and used as weights to obtain the color-flow
probability vector $\rho_{\rm out}^f(p, h_{\rm out})$.
\item
For colorless non-diagonal helicity amplitudes, we contract the
tensor product of $A_\lambda(p,h)$ with $A_\lambda(p,h')$,
weighted with $\lambda$, with the correlated in-state density
matrix, to obtain a correlated out-state density matrix.
\item
In the general (colored, non-diagonal) case, we do the same as
in the colorless case, but return the un-normalized color flow
density matrix $\hat\rho_{\rm out}^f(p,h_{\rm out},h'_{\rm out})$
in addition. When the relevant helicity basis is known, the
latter can be used by the caller program to determine flow
probabilities. (In reality, we assume the canonical basis and
reduce the correlated out-state density to its diagonal immediately.)
\end{enumerate}
\end{itemize}
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Flavors: Particle properties}
This module contains a type for holding the flavor code, and all
functions that depend on the model, i.e., that determine particle
properties.
The PDG code is packed in a special [[flavor]] type. (This prohibits
meaningless operations, and it allows for a different implementation,
e.g., some non-PDG scheme internally, if appropiate at some point.)
There are lots of further particle properties that depend on the
model. Implementing a flyweight pattern, the associated field data
object is to be stored in a central area, the [[flavor]] object just
receives a pointer to this, so all queries can be delegated.
<<[[flavors.f90]]>>=
<<File header>>
module flavors
<<Use kinds>>
<<Use strings>>
use io_units
use diagnostics
use physics_defs, only: UNDEFINED
use physics_defs, only: INVALID
use physics_defs, only: HADRON_REMNANT
use physics_defs, only: HADRON_REMNANT_SINGLET
use physics_defs, only: HADRON_REMNANT_TRIPLET
use physics_defs, only: HADRON_REMNANT_OCTET
use model_data
use colors, only: color_t
<<Standard module head>>
<<Flavors: public>>
<<Flavors: types>>
<<Flavors: interfaces>>
contains
<<Flavors: procedures>>
end module flavors
@ %def flavors
@
\subsection{The flavor type}
The flavor type is an integer representing the PDG code, or
undefined (zero). Negative codes represent antiflavors. They should
be used only for particles which do have a distinct antiparticle.
The [[hard_process]] flag can be set for particles that are participating in
the hard interaction.
The [[radiated]] flag can be set for particles that are the result of
a beam-structure interaction (hadron beam remnant, ISR photon, etc.),
not of the hard interaction itself.
Further properties of the given flavor can be retrieved via the
particle-data pointer, if it is associated.
<<Flavors: public>>=
public :: flavor_t
<<Flavors: types>>=
type :: flavor_t
private
integer :: f = UNDEFINED
logical :: hard_process = .false.
logical :: radiated = .false.
type(field_data_t), pointer :: field_data => null ()
contains
<<Flavors: flavor: TBP>>
end type flavor_t
@ %def flavor_t
@ Initializer form. If the model is assigned, the procedure is
impure, therefore we have to define a separate array version.
Note: The pure elemental subroutines can't have an intent(out) CLASS
argument (because of the potential for an impure finalizer in a type
extension), so we stick to intent(inout) and (re)set all components
explicitly.
<<Flavors: flavor: TBP>>=
generic :: init => &
flavor_init_empty, &
flavor_init, &
flavor_init_field_data, &
flavor_init_model, &
flavor_init_model_alt, &
flavor_init_name_model
procedure, private :: flavor_init_empty
procedure, private :: flavor_init
procedure, private :: flavor_init_field_data
procedure, private :: flavor_init_model
procedure, private :: flavor_init_model_alt
procedure, private :: flavor_init_name_model
<<Flavors: procedures>>=
elemental subroutine flavor_init_empty (flv)
class(flavor_t), intent(inout) :: flv
flv%f = UNDEFINED
flv%hard_process = .false.
flv%radiated = .false.
flv%field_data => null ()
end subroutine flavor_init_empty
elemental subroutine flavor_init (flv, f)
class(flavor_t), intent(inout) :: flv
integer, intent(in) :: f
flv%f = f
flv%hard_process = .false.
flv%radiated = .false.
flv%field_data => null ()
end subroutine flavor_init
impure elemental subroutine flavor_init_field_data (flv, field_data)
class(flavor_t), intent(inout) :: flv
type(field_data_t), intent(in), target :: field_data
flv%f = field_data%get_pdg ()
flv%hard_process = .false.
flv%radiated = .false.
flv%field_data => field_data
end subroutine flavor_init_field_data
impure elemental subroutine flavor_init_model (flv, f, model)
class(flavor_t), intent(inout) :: flv
integer, intent(in) :: f
class(model_data_t), intent(in), target :: model
flv%f = f
flv%hard_process = .false.
flv%radiated = .false.
flv%field_data => model%get_field_ptr (f, check=.true.)
end subroutine flavor_init_model
impure elemental subroutine flavor_init_model_alt (flv, f, model, alt_model)
class(flavor_t), intent(inout) :: flv
integer, intent(in) :: f
class(model_data_t), intent(in), target :: model, alt_model
flv%f = f
flv%hard_process = .false.
flv%radiated = .false.
flv%field_data => model%get_field_ptr (f, check=.false.)
if (.not. associated (flv%field_data)) then
flv%field_data => alt_model%get_field_ptr (f, check=.false.)
if (.not. associated (flv%field_data)) then
write (msg_buffer, "(A,1x,I0,1x,A,1x,A,1x,A,1x,A)") &
"Particle with code", f, &
"found neither in model", char (model%get_name ()), &
"nor in model", char (alt_model%get_name ())
call msg_fatal ()
end if
end if
end subroutine flavor_init_model_alt
impure elemental subroutine flavor_init_name_model (flv, name, model)
class(flavor_t), intent(inout) :: flv
type(string_t), intent(in) :: name
class(model_data_t), intent(in), target :: model
flv%f = model%get_pdg (name)
flv%hard_process = .false.
flv%radiated = .false.
flv%field_data => model%get_field_ptr (name, check=.true.)
end subroutine flavor_init_name_model
@ %def flavor_init
@ Set the [[radiated]] flag.
<<Flavors: flavor: TBP>>=
procedure :: tag_radiated => flavor_tag_radiated
<<Flavors: procedures>>=
elemental subroutine flavor_tag_radiated (flv)
class(flavor_t), intent(inout) :: flv
flv%radiated = .true.
end subroutine flavor_tag_radiated
@ %def flavor_tag_radiated
@ Set the [[hard_process]] flag.
<<Flavors: flavor: TBP>>=
procedure :: tag_hard_process => flavor_tag_hard_process
<<Flavors: procedures>>=
elemental subroutine flavor_tag_hard_process (flv)
class(flavor_t), intent(inout) :: flv
flv%hard_process = .true.
end subroutine flavor_tag_hard_process
@ %def flavor_tag_hard_process
@ Undefine the flavor state:
<<Flavors: flavor: TBP>>=
procedure :: undefine => flavor_undefine
<<Flavors: procedures>>=
elemental subroutine flavor_undefine (flv)
class(flavor_t), intent(inout) :: flv
flv%f = UNDEFINED
flv%field_data => null ()
end subroutine flavor_undefine
@ %def flavor_undefine
@ Output: dense, no linebreak
<<Flavors: flavor: TBP>>=
procedure :: write => flavor_write
<<Flavors: procedures>>=
subroutine flavor_write (flv, unit)
class(flavor_t), intent(in) :: flv
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
if (associated (flv%field_data)) then
write (u, "(A)", advance="no") "f("
else
write (u, "(A)", advance="no") "p("
end if
write (u, "(I0)", advance="no") flv%f
if (flv%radiated) then
write (u, "('*')", advance="no")
end if
write (u, "(A)", advance="no") ")"
end subroutine flavor_write
@ %def flavor_write
@
<<Flavors: public>>=
public :: flavor_write_array
<<Flavors: procedures>>=
subroutine flavor_write_array (flv, unit)
type(flavor_t), intent(in), dimension(:) :: flv
integer, intent(in), optional :: unit
integer :: u, i_flv
u = given_output_unit (unit); if (u < 0) return
do i_flv = 1, size (flv)
call flv(i_flv)%write (u)
if (i_flv /= size (flv)) write (u,"(A)", advance = "no") " / "
end do
write (u,"(A)")
end subroutine flavor_write_array
@ %def flavor_write_array
@ Binary I/O. Currently, the model information is not written/read,
so after reading the particle-data pointer is empty.
<<Flavors: flavor: TBP>>=
procedure :: write_raw => flavor_write_raw
procedure :: read_raw => flavor_read_raw
<<Flavors: procedures>>=
subroutine flavor_write_raw (flv, u)
class(flavor_t), intent(in) :: flv
integer, intent(in) :: u
write (u) flv%f
write (u) flv%radiated
end subroutine flavor_write_raw
subroutine flavor_read_raw (flv, u, iostat)
class(flavor_t), intent(out) :: flv
integer, intent(in) :: u
integer, intent(out), optional :: iostat
read (u, iostat=iostat) flv%f
if (present (iostat)) then
if (iostat /= 0) return
end if
read (u, iostat=iostat) flv%radiated
end subroutine flavor_read_raw
@ %def flavor_write_raw flavor_read_raw
@
\subsubsection{Assignment}
Default assignment of flavor objects is possible, but cannot be used
in pure procedures, because a pointer assignment is involved.
Assign the particle pointer separately. This cannot be elemental, so
we define a scalar and an array version explicitly. We refer to an
array of flavors, not an array of models.
<<Flavors: flavor: TBP>>=
procedure :: set_model => flavor_set_model_single
<<Flavors: procedures>>=
impure elemental subroutine flavor_set_model_single (flv, model)
class(flavor_t), intent(inout) :: flv
class(model_data_t), intent(in), target :: model
if (flv%f /= UNDEFINED) &
flv%field_data => model%get_field_ptr (flv%f)
end subroutine flavor_set_model_single
@ %def flavor_set_model
@
\subsubsection{Predicates}
Return the definition status. By definition, the flavor object is
defined if the flavor PDG code is nonzero.
<<Flavors: flavor: TBP>>=
procedure :: is_defined => flavor_is_defined
<<Flavors: procedures>>=
elemental function flavor_is_defined (flv) result (defined)
class(flavor_t), intent(in) :: flv
logical :: defined
defined = flv%f /= UNDEFINED
end function flavor_is_defined
@ %def flavor_is_defined
@ Check for valid flavor (including undefined). This is distinct from
the [[is_defined]] status. Invalid flavor is actually a specific PDG
code.
<<Flavors: flavor: TBP>>=
procedure :: is_valid => flavor_is_valid
<<Flavors: procedures>>=
elemental function flavor_is_valid (flv) result (valid)
class(flavor_t), intent(in) :: flv
logical :: valid
valid = flv%f /= INVALID
end function flavor_is_valid
@ %def flavor_is_valid
@ Return true if the particle-data pointer is associated. (Debugging aid)
<<Flavors: flavor: TBP>>=
procedure :: is_associated => flavor_is_associated
<<Flavors: procedures>>=
elemental function flavor_is_associated (flv) result (flag)
class(flavor_t), intent(in) :: flv
logical :: flag
flag = associated (flv%field_data)
end function flavor_is_associated
@ %def flavor_is_associated
@ Check the [[radiated]] flag. A radiated particle has a definite PDG
flavor status, but it is actually a pseudoparticle (a beam remnant)
which may be subject to fragmentation.
<<Flavors: flavor: TBP>>=
procedure :: is_radiated => flavor_is_radiated
<<Flavors: procedures>>=
elemental function flavor_is_radiated (flv) result (flag)
class(flavor_t), intent(in) :: flv
logical :: flag
flag = flv%radiated
end function flavor_is_radiated
@ %def flavor_is_radiated
@ Check the [[hard_process]] flag. A particle is tagged with this flag if
it participates in the hard interaction and is not a beam remnant.
<<Flavors: flavor: TBP>>=
procedure :: is_hard_process => flavor_is_hard_process
<<Flavors: procedures>>=
elemental function flavor_is_hard_process (flv) result (flag)
class(flavor_t), intent(in) :: flv
logical :: flag
flag = flv%hard_process
end function flavor_is_hard_process
@ %def flavor_is_hard_process
@
\subsubsection{Accessing contents}
With the exception of the PDG code, all particle property enquiries are
delegated to the [[field_data]] pointer. If this is unassigned, some
access function will crash.
Return the flavor as an integer
<<Flavors: flavor: TBP>>=
procedure :: get_pdg => flavor_get_pdg
<<Flavors: procedures>>=
elemental function flavor_get_pdg (flv) result (f)
integer :: f
class(flavor_t), intent(in) :: flv
f = flv%f
end function flavor_get_pdg
@ %def flavor_get_pdg
@ Return the flavor of the antiparticle
<<Flavors: flavor: TBP>>=
procedure :: get_pdg_anti => flavor_get_pdg_anti
<<Flavors: procedures>>=
elemental function flavor_get_pdg_anti (flv) result (f)
integer :: f
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
if (flv%field_data%has_antiparticle ()) then
f = -flv%f
else
f = flv%f
end if
else
f = 0
end if
end function flavor_get_pdg_anti
@ %def flavor_get_pdg_anti
@
Absolute value:
<<Flavors: flavor: TBP>>=
procedure :: get_pdg_abs => flavor_get_pdg_abs
<<Flavors: procedures>>=
elemental function flavor_get_pdg_abs (flv) result (f)
integer :: f
class(flavor_t), intent(in) :: flv
f = abs (flv%f)
end function flavor_get_pdg_abs
@ %def flavor_get_pdg_abs
@
Generic properties
<<Flavors: flavor: TBP>>=
procedure :: is_visible => flavor_is_visible
procedure :: is_parton => flavor_is_parton
procedure :: is_beam_remnant => flavor_is_beam_remnant
procedure :: is_gauge => flavor_is_gauge
procedure :: is_left_handed => flavor_is_left_handed
procedure :: is_right_handed => flavor_is_right_handed
procedure :: is_antiparticle => flavor_is_antiparticle
procedure :: has_antiparticle => flavor_has_antiparticle
procedure :: is_stable => flavor_is_stable
procedure :: get_decays => flavor_get_decays
procedure :: decays_isotropically => flavor_decays_isotropically
procedure :: decays_diagonal => flavor_decays_diagonal
procedure :: has_decay_helicity => flavor_has_decay_helicity
procedure :: get_decay_helicity => flavor_get_decay_helicity
procedure :: is_polarized => flavor_is_polarized
<<Flavors: procedures>>=
elemental function flavor_is_visible (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%is_visible ()
else
flag = .false.
end if
end function flavor_is_visible
elemental function flavor_is_parton (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%is_parton ()
else
flag = .false.
end if
end function flavor_is_parton
elemental function flavor_is_beam_remnant (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
select case (abs (flv%f))
case (HADRON_REMNANT, &
HADRON_REMNANT_SINGLET, HADRON_REMNANT_TRIPLET, HADRON_REMNANT_OCTET)
flag = .true.
case default
flag = .false.
end select
end function flavor_is_beam_remnant
elemental function flavor_is_gauge (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%is_gauge ()
else
flag = .false.
end if
end function flavor_is_gauge
elemental function flavor_is_left_handed (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
if (flv%f > 0) then
flag = flv%field_data%is_left_handed ()
else
flag = flv%field_data%is_right_handed ()
end if
else
flag = .false.
end if
end function flavor_is_left_handed
elemental function flavor_is_right_handed (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
if (flv%f > 0) then
flag = flv%field_data%is_right_handed ()
else
flag = flv%field_data%is_left_handed ()
end if
else
flag = .false.
end if
end function flavor_is_right_handed
elemental function flavor_is_antiparticle (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
flag = flv%f < 0
end function flavor_is_antiparticle
elemental function flavor_has_antiparticle (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%has_antiparticle ()
else
flag = .false.
end if
end function flavor_has_antiparticle
elemental function flavor_is_stable (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%is_stable (anti = flv%f < 0)
else
flag = .true.
end if
end function flavor_is_stable
subroutine flavor_get_decays (flv, decay)
class(flavor_t), intent(in) :: flv
type(string_t), dimension(:), intent(out), allocatable :: decay
logical :: anti
anti = flv%f < 0
if (.not. flv%field_data%is_stable (anti)) then
call flv%field_data%get_decays (decay, anti)
end if
end subroutine flavor_get_decays
elemental function flavor_decays_isotropically (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%decays_isotropically (anti = flv%f < 0)
else
flag = .true.
end if
end function flavor_decays_isotropically
elemental function flavor_decays_diagonal (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%decays_diagonal (anti = flv%f < 0)
else
flag = .true.
end if
end function flavor_decays_diagonal
elemental function flavor_has_decay_helicity (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%has_decay_helicity (anti = flv%f < 0)
else
flag = .false.
end if
end function flavor_has_decay_helicity
elemental function flavor_get_decay_helicity (flv) result (hel)
integer :: hel
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
hel = flv%field_data%decay_helicity (anti = flv%f < 0)
else
hel = 0
end if
end function flavor_get_decay_helicity
elemental function flavor_is_polarized (flv) result (flag)
logical :: flag
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
flag = flv%field_data%is_polarized (anti = flv%f < 0)
else
flag = .false.
end if
end function flavor_is_polarized
@ %def flavor_is_visible
@ %def flavor_is_parton
@ %def flavor_is_beam_remnant
@ %def flavor_is_gauge
@ %def flavor_is_left_handed
@ %def flavor_is_right_handed
@ %def flavor_is_antiparticle
@ %def flavor_has_antiparticle
@ %def flavor_is_stable
@ %def flavor_get_decays
@ %def flavor_decays_isotropically
@ %def flavor_decays_diagonal
@ %def flavor_has_decays_helicity
@ %def flavor_get_decay_helicity
@ %def flavor_is_polarized
@ Names:
<<Flavors: flavor: TBP>>=
procedure :: get_name => flavor_get_name
procedure :: get_tex_name => flavor_get_tex_name
<<Flavors: procedures>>=
elemental function flavor_get_name (flv) result (name)
type(string_t) :: name
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
name = flv%field_data%get_name (flv%f < 0)
else
name = "?"
end if
end function flavor_get_name
elemental function flavor_get_tex_name (flv) result (name)
type(string_t) :: name
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
name = flv%field_data%get_tex_name (flv%f < 0)
else
name = "?"
end if
end function flavor_get_tex_name
@ %def flavor_get_name flavor_get_tex_name
<<Flavors: flavor: TBP>>=
procedure :: get_spin_type => flavor_get_spin_type
procedure :: get_multiplicity => flavor_get_multiplicity
procedure :: get_isospin_type => flavor_get_isospin_type
procedure :: get_charge_type => flavor_get_charge_type
procedure :: get_color_type => flavor_get_color_type
<<Flavors: procedures>>=
elemental function flavor_get_spin_type (flv) result (type)
integer :: type
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
type = flv%field_data%get_spin_type ()
else
type = 1
end if
end function flavor_get_spin_type
elemental function flavor_get_multiplicity (flv) result (type)
integer :: type
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
type = flv%field_data%get_multiplicity ()
else
type = 1
end if
end function flavor_get_multiplicity
elemental function flavor_get_isospin_type (flv) result (type)
integer :: type
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
type = flv%field_data%get_isospin_type ()
else
type = 1
end if
end function flavor_get_isospin_type
elemental function flavor_get_charge_type (flv) result (type)
integer :: type
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
type = flv%field_data%get_charge_type ()
else
type = 1
end if
end function flavor_get_charge_type
elemental function flavor_get_color_type (flv) result (type)
integer :: type
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
if (flavor_is_antiparticle (flv)) then
type = - flv%field_data%get_color_type ()
else
type = flv%field_data%get_color_type ()
end if
select case (type)
case (-1,-8); type = abs (type)
end select
else
type = 1
end if
end function flavor_get_color_type
@ %def flavor_get_spin_type
@ %def flavor_get_multiplicity
@ %def flavor_get_isospin_type
@ %def flavor_get_charge_type
@ %def flavor_get_color_type
@ These functions return real values:
<<Flavors: flavor: TBP>>=
procedure :: get_charge => flavor_get_charge
procedure :: get_mass => flavor_get_mass
procedure :: get_width => flavor_get_width
procedure :: get_isospin => flavor_get_isospin
<<Flavors: procedures>>=
elemental function flavor_get_charge (flv) result (charge)
real(default) :: charge
class(flavor_t), intent(in) :: flv
integer :: charge_type
if (associated (flv%field_data)) then
charge_type = flv%get_charge_type ()
if (charge_type == 0 .or. charge_type == 1) then
charge = 0
else
if (flavor_is_antiparticle (flv)) then
charge = - flv%field_data%get_charge ()
else
charge = flv%field_data%get_charge ()
end if
end if
else
charge = 0
end if
end function flavor_get_charge
elemental function flavor_get_mass (flv) result (mass)
real(default) :: mass
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
mass = flv%field_data%get_mass ()
else
mass = 0
end if
end function flavor_get_mass
elemental function flavor_get_width (flv) result (width)
real(default) :: width
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
width = flv%field_data%get_width ()
else
width = 0
end if
end function flavor_get_width
elemental function flavor_get_isospin (flv) result (isospin)
real(default) :: isospin
class(flavor_t), intent(in) :: flv
if (associated (flv%field_data)) then
if (flavor_is_antiparticle (flv)) then
isospin = - flv%field_data%get_isospin ()
else
isospin = flv%field_data%get_isospin ()
end if
else
isospin = 0
end if
end function flavor_get_isospin
@ %def flavor_get_charge flavor_get_mass flavor_get_width
@ %def flavor_get_isospin
@
\subsubsection{Comparisons}
If one of the flavors is undefined, the other defined, they match.
<<Flavors: flavor: TBP>>=
generic :: operator(.match.) => flavor_match
generic :: operator(==) => flavor_eq
generic :: operator(/=) => flavor_neq
procedure, private :: flavor_match
procedure, private :: flavor_eq
procedure, private :: flavor_neq
@ %def .match. == /=
<<Flavors: procedures>>=
elemental function flavor_match (flv1, flv2) result (eq)
logical :: eq
class(flavor_t), intent(in) :: flv1, flv2
if (flv1%f /= UNDEFINED .and. flv2%f /= UNDEFINED) then
eq = flv1%f == flv2%f
else
eq = .true.
end if
end function flavor_match
elemental function flavor_eq (flv1, flv2) result (eq)
logical :: eq
class(flavor_t), intent(in) :: flv1, flv2
if (flv1%f /= UNDEFINED .and. flv2%f /= UNDEFINED) then
eq = flv1%f == flv2%f
else if (flv1%f == UNDEFINED .and. flv2%f == UNDEFINED) then
eq = .true.
else
eq = .false.
end if
end function flavor_eq
@ %def flavor_match flavor_eq
<<Flavors: procedures>>=
elemental function flavor_neq (flv1, flv2) result (neq)
logical :: neq
class(flavor_t), intent(in) :: flv1, flv2
if (flv1%f /= UNDEFINED .and. flv2%f /= UNDEFINED) then
neq = flv1%f /= flv2%f
else if (flv1%f == UNDEFINED .and. flv2%f == UNDEFINED) then
neq = .false.
else
neq = .true.
end if
end function flavor_neq
@ %def flavor_neq
@
\subsubsection{Tools}
Merge two flavor indices. This works only if both are equal or either
one is undefined, because we have no off-diagonal flavor entries.
Otherwise, generate an invalid flavor.
We cannot use elemental procedures because of the pointer component.
<<Flavors: public>>=
public :: operator(.merge.)
<<Flavors: interfaces>>=
interface operator(.merge.)
module procedure merge_flavors0
module procedure merge_flavors1
end interface
@ %def .merge.
<<Flavors: procedures>>=
function merge_flavors0 (flv1, flv2) result (flv)
type(flavor_t) :: flv
type(flavor_t), intent(in) :: flv1, flv2
if (flavor_is_defined (flv1) .and. flavor_is_defined (flv2)) then
if (flv1 == flv2) then
flv = flv1
else
flv%f = INVALID
end if
else if (flavor_is_defined (flv1)) then
flv = flv1
else if (flavor_is_defined (flv2)) then
flv = flv2
end if
end function merge_flavors0
function merge_flavors1 (flv1, flv2) result (flv)
type(flavor_t), dimension(:), intent(in) :: flv1, flv2
type(flavor_t), dimension(size(flv1)) :: flv
integer :: i
do i = 1, size (flv1)
flv(i) = flv1(i) .merge. flv2(i)
end do
end function merge_flavors1
@ %def merge_flavors
@ Generate consecutive color indices for a given flavor. The indices
are counted starting with the stored value of c, so new indices are
created each time this (impure) function is called. The counter can
be reset by the optional argument [[c_seed]] if desired. The optional
flag [[reverse]] is used only for octets. If set, the color and
anticolor entries of the octet particle are exchanged.
<<Flavors: public>>=
public :: color_from_flavor
<<Flavors: interfaces>>=
interface color_from_flavor
module procedure color_from_flavor0
module procedure color_from_flavor1
end interface
<<Flavors: procedures>>=
function color_from_flavor0 (flv, c_seed, reverse) result (col)
type(color_t) :: col
type(flavor_t), intent(in) :: flv
integer, intent(in), optional :: c_seed
logical, intent(in), optional :: reverse
integer, save :: c = 1
logical :: rev
if (present (c_seed)) c = c_seed
rev = .false.; if (present (reverse)) rev = reverse
select case (flavor_get_color_type (flv))
case (1)
call col%init ()
case (3)
call col%init ([c]); c = c + 1
case (-3)
call col%init ([-c]); c = c + 1
case (8)
if (rev) then
call col%init ([c+1, -c]); c = c + 2
else
call col%init ([c, -(c+1)]); c = c + 2
end if
end select
end function color_from_flavor0
function color_from_flavor1 (flv, c_seed, reverse) result (col)
type(flavor_t), dimension(:), intent(in) :: flv
integer, intent(in), optional :: c_seed
logical, intent(in), optional :: reverse
type(color_t), dimension(size(flv)) :: col
integer :: i
col(1) = color_from_flavor0 (flv(1), c_seed, reverse)
do i = 2, size (flv)
col(i) = color_from_flavor0 (flv(i), reverse=reverse)
end do
end function color_from_flavor1
@ %def color_from_flavor
@ This procedure returns the flavor object for the antiparticle. The
antiparticle code may either be the same code or its negative.
<<Flavors: flavor: TBP>>=
procedure :: anti => flavor_anti
<<Flavors: procedures>>=
function flavor_anti (flv) result (aflv)
type(flavor_t) :: aflv
class(flavor_t), intent(in) :: flv
if (flavor_has_antiparticle (flv)) then
aflv%f = - flv%f
else
aflv%f = flv%f
end if
aflv%field_data => flv%field_data
end function flavor_anti
@ %def flavor_anti
@
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Quantum numbers}
This module collects helicity, color, and flavor in a single type and
defines procedures
<<[[quantum_numbers.f90]]>>=
<<File header>>
module quantum_numbers
use io_units
use model_data
use helicities
use colors
use flavors
<<Standard module head>>
<<Quantum numbers: public>>
<<Quantum numbers: types>>
<<Quantum numbers: interfaces>>
contains
<<Quantum numbers: procedures>>
end module quantum_numbers
@ %def quantum_numbers
@
\subsection{The quantum number type}
<<Quantum numbers: public>>=
public :: quantum_numbers_t
<<Quantum numbers: types>>=
type :: quantum_numbers_t
private
type(flavor_t) :: f
type(color_t) :: c
type(helicity_t) :: h
integer :: sub = 0
contains
<<Quantum numbers: quantum numbers: TBP>>
end type quantum_numbers_t
@ %def quantum_number_t
@ Define quantum numbers: Initializer form. All arguments may be
present or absent.
Some elemental initializers are impure because they set the [[flv]]
component. This implies transfer of a pointer behind the scenes.
<<Quantum numbers: quantum numbers: TBP>>=
generic :: init => &
quantum_numbers_init_f, &
quantum_numbers_init_c, &
quantum_numbers_init_h, &
quantum_numbers_init_fc, &
quantum_numbers_init_fh, &
quantum_numbers_init_ch, &
quantum_numbers_init_fch, &
quantum_numbers_init_fs, &
quantum_numbers_init_fhs, &
quantum_numbers_init_fcs, &
quantum_numbers_init_fhcs
procedure, private :: quantum_numbers_init_f
procedure, private :: quantum_numbers_init_c
procedure, private :: quantum_numbers_init_h
procedure, private :: quantum_numbers_init_fc
procedure, private :: quantum_numbers_init_fh
procedure, private :: quantum_numbers_init_ch
procedure, private :: quantum_numbers_init_fch
procedure, private :: quantum_numbers_init_fs
procedure, private :: quantum_numbers_init_fhs
procedure, private :: quantum_numbers_init_fcs
procedure, private :: quantum_numbers_init_fhcs
<<Quantum numbers: procedures>>=
impure elemental subroutine quantum_numbers_init_f (qn, flv)
class(quantum_numbers_t), intent(out) :: qn
type(flavor_t), intent(in) :: flv
qn%f = flv
call qn%c%undefine ()
call qn%h%undefine ()
qn%sub = 0
end subroutine quantum_numbers_init_f
impure elemental subroutine quantum_numbers_init_c (qn, col)
class(quantum_numbers_t), intent(out) :: qn
type(color_t), intent(in) :: col
call qn%f%undefine ()
qn%c = col
call qn%h%undefine ()
qn%sub = 0
end subroutine quantum_numbers_init_c
impure elemental subroutine quantum_numbers_init_h (qn, hel)
class(quantum_numbers_t), intent(out) :: qn
type(helicity_t), intent(in) :: hel
call qn%f%undefine ()
call qn%c%undefine ()
qn%h = hel
qn%sub = 0
end subroutine quantum_numbers_init_h
impure elemental subroutine quantum_numbers_init_fc (qn, flv, col)
class(quantum_numbers_t), intent(out) :: qn
type(flavor_t), intent(in) :: flv
type(color_t), intent(in) :: col
qn%f = flv
qn%c = col
call qn%h%undefine ()
qn%sub = 0
end subroutine quantum_numbers_init_fc
impure elemental subroutine quantum_numbers_init_fh (qn, flv, hel)
class(quantum_numbers_t), intent(out) :: qn
type(flavor_t), intent(in) :: flv
type(helicity_t), intent(in) :: hel
qn%f = flv
call qn%c%undefine ()
qn%h = hel
qn%sub = 0
end subroutine quantum_numbers_init_fh
impure elemental subroutine quantum_numbers_init_ch (qn, col, hel)
class(quantum_numbers_t), intent(out) :: qn
type(color_t), intent(in) :: col
type(helicity_t), intent(in) :: hel
call qn%f%undefine ()
qn%c = col
qn%h = hel
qn%sub = 0
end subroutine quantum_numbers_init_ch
impure elemental subroutine quantum_numbers_init_fch (qn, flv, col, hel)
class(quantum_numbers_t), intent(out) :: qn
type(flavor_t), intent(in) :: flv
type(color_t), intent(in) :: col
type(helicity_t), intent(in) :: hel
qn%f = flv
qn%c = col
qn%h = hel
qn%sub = 0
end subroutine quantum_numbers_init_fch
impure elemental subroutine quantum_numbers_init_fs (qn, flv, sub)
class(quantum_numbers_t), intent(out) :: qn
type(flavor_t), intent(in) :: flv
integer, intent(in) :: sub
qn%f = flv; qn%sub = sub
end subroutine quantum_numbers_init_fs
impure elemental subroutine quantum_numbers_init_fhs (qn, flv, hel, sub)
class(quantum_numbers_t), intent(out) :: qn
type(flavor_t), intent(in) :: flv
type(helicity_t), intent(in) :: hel
integer, intent(in) :: sub
qn%f = flv; qn%h = hel; qn%sub = sub
end subroutine quantum_numbers_init_fhs
impure elemental subroutine quantum_numbers_init_fcs (qn, flv, col, sub)
class(quantum_numbers_t), intent(out) :: qn
type(flavor_t), intent(in) :: flv
type(color_t), intent(in) :: col
integer, intent(in) :: sub
qn%f = flv; qn%c = col; qn%sub = sub
end subroutine quantum_numbers_init_fcs
impure elemental subroutine quantum_numbers_init_fhcs (qn, flv, hel, col, sub)
class(quantum_numbers_t), intent(out) :: qn
type(flavor_t), intent(in) :: flv
type(helicity_t), intent(in) :: hel
type(color_t), intent(in) :: col
integer, intent(in) :: sub
qn%f = flv; qn%h = hel; qn%c = col; qn%sub = sub
end subroutine quantum_numbers_init_fhcs
@ %def quantum_numbers_init
@
\subsection{I/O}
Write the quantum numbers in condensed form, enclosed by square
brackets. Color is written only if nontrivial. For convenience,
introduce also an array version.
If the [[col_verbose]] option is set, show the quantum number color also
if it is zero, but defined. Otherwise, suppress zero color.
<<Quantum numbers: public>>=
public :: quantum_numbers_write
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: write => quantum_numbers_write_single
<<Quantum numbers: interfaces>>=
interface quantum_numbers_write
module procedure quantum_numbers_write_single
module procedure quantum_numbers_write_array
end interface
<<Quantum numbers: procedures>>=
subroutine quantum_numbers_write_single (qn, unit, col_verbose)
class(quantum_numbers_t), intent(in) :: qn
integer, intent(in), optional :: unit
logical, intent(in), optional :: col_verbose
integer :: u
logical :: col_verb
u = given_output_unit (unit); if (u < 0) return
col_verb = .false.; if (present (col_verbose)) col_verb = col_verbose
write (u, "(A)", advance = "no") "["
if (qn%f%is_defined ()) then
call qn%f%write (u)
if (qn%c%is_nonzero () .or. qn%h%is_defined ()) &
write (u, "(1x)", advance = "no")
end if
if (col_verb) then
if (qn%c%is_defined () .or. qn%c%is_ghost ()) then
call color_write (qn%c, u)
if (qn%h%is_defined ()) write (u, "(1x)", advance = "no")
end if
else
if (qn%c%is_nonzero () .or. qn%c%is_ghost ()) then
call color_write (qn%c, u)
if (qn%h%is_defined ()) write (u, "(1x)", advance = "no")
end if
end if
if (qn%h%is_defined ()) then
call qn%h%write (u)
end if
if (qn%sub > 0) &
write (u, "(A,I0)", advance = "no") " SUB = ", qn%sub
write (u, "(A)", advance="no") "]"
end subroutine quantum_numbers_write_single
subroutine quantum_numbers_write_array (qn, unit, col_verbose)
type(quantum_numbers_t), dimension(:), intent(in) :: qn
integer, intent(in), optional :: unit
logical, intent(in), optional :: col_verbose
integer :: i
integer :: u
logical :: col_verb
u = given_output_unit (unit); if (u < 0) return
col_verb = .false.; if (present (col_verbose)) col_verb = col_verbose
write (u, "(A)", advance="no") "["
do i = 1, size (qn)
if (i > 1) write (u, "(A)", advance="no") " / "
if (qn(i)%f%is_defined ()) then
call qn(i)%f%write (u)
if (qn(i)%c%is_nonzero () .or. qn(i)%h%is_defined ()) &
write (u, "(1x)", advance="no")
end if
if (col_verb) then
if (qn(i)%c%is_defined () .or. qn(i)%c%is_ghost ()) then
call color_write (qn(i)%c, u)
if (qn(i)%h%is_defined ()) write (u, "(1x)", advance="no")
end if
else
if (qn(i)%c%is_nonzero () .or. qn(i)%c%is_ghost ()) then
call color_write (qn(i)%c, u)
if (qn(i)%h%is_defined ()) write (u, "(1x)", advance="no")
end if
end if
if (qn(i)%h%is_defined ()) then
call qn(i)%h%write (u)
end if
if (qn(i)%sub > 0) &
write (u, "(A,I2)", advance = "no") " SUB = ", qn(i)%sub
end do
write (u, "(A)", advance = "no") "]"
end subroutine quantum_numbers_write_array
@ %def quantum_numbers_write
@ Binary I/O.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: write_raw => quantum_numbers_write_raw
procedure :: read_raw => quantum_numbers_read_raw
<<Quantum numbers: procedures>>=
subroutine quantum_numbers_write_raw (qn, u)
class(quantum_numbers_t), intent(in) :: qn
integer, intent(in) :: u
call qn%f%write_raw (u)
call qn%c%write_raw (u)
call qn%h%write_raw (u)
end subroutine quantum_numbers_write_raw
subroutine quantum_numbers_read_raw (qn, u, iostat)
class(quantum_numbers_t), intent(out) :: qn
integer, intent(in) :: u
integer, intent(out), optional :: iostat
call qn%f%read_raw (u, iostat=iostat)
call qn%c%read_raw (u, iostat=iostat)
call qn%h%read_raw (u, iostat=iostat)
end subroutine quantum_numbers_read_raw
@ %def quantum_numbers_write_raw quantum_numbers_read_raw
@
\subsection{Accessing contents}
Color and helicity can be done by elemental functions. Flavor needs
impure elemental. We export also the functions directly, this allows
us to avoid temporaries in some places.
<<Quantum numbers: public>>=
public :: quantum_numbers_get_flavor
public :: quantum_numbers_get_color
public :: quantum_numbers_get_helicity
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: get_flavor => quantum_numbers_get_flavor
procedure :: get_color => quantum_numbers_get_color
procedure :: get_helicity => quantum_numbers_get_helicity
procedure :: get_sub => quantum_numbers_get_sub
<<Quantum numbers: procedures>>=
impure elemental function quantum_numbers_get_flavor (qn) result (flv)
type(flavor_t) :: flv
class(quantum_numbers_t), intent(in) :: qn
flv = qn%f
end function quantum_numbers_get_flavor
elemental function quantum_numbers_get_color (qn) result (col)
type(color_t) :: col
class(quantum_numbers_t), intent(in) :: qn
col = qn%c
end function quantum_numbers_get_color
elemental function quantum_numbers_get_helicity (qn) result (hel)
type(helicity_t) :: hel
class(quantum_numbers_t), intent(in) :: qn
hel = qn%h
end function quantum_numbers_get_helicity
elemental function quantum_numbers_get_sub (qn) result (sub)
integer :: sub
class(quantum_numbers_t), intent(in) :: qn
sub = qn%sub
end function quantum_numbers_get_sub
@ %def quantum_numbers_get_flavor
@ %def quantum_numbers_get_color
@ %def quantum_numbers_get_helicity
@ %def quantum_numbers_get_sub
@ This just resets the ghost property of the color part:
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: set_color_ghost => quantum_numbers_set_color_ghost
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_set_color_ghost (qn, ghost)
class(quantum_numbers_t), intent(inout) :: qn
logical, intent(in) :: ghost
call qn%c%set_ghost (ghost)
end subroutine quantum_numbers_set_color_ghost
@ %def quantum_numbers_set_color_ghost
@ Assign a model to the flavor part of quantum numbers.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: set_model => quantum_numbers_set_model
<<Quantum numbers: procedures>>=
impure elemental subroutine quantum_numbers_set_model (qn, model)
class(quantum_numbers_t), intent(inout) :: qn
class(model_data_t), intent(in), target :: model
call qn%f%set_model (model)
end subroutine quantum_numbers_set_model
@ %def quantum_numbers_set_model
@ Set the [[radiated]] flag for the flavor component.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: tag_radiated => quantum_numbers_tag_radiated
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_tag_radiated (qn)
class(quantum_numbers_t), intent(inout) :: qn
call qn%f%tag_radiated ()
end subroutine quantum_numbers_tag_radiated
@ %def quantum_numbers_tag_radiated
@ Set the [[hard_process]] flag for the flavor component.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: tag_hard_process => quantum_numbers_tag_hard_process
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_tag_hard_process (qn)
class(quantum_numbers_t), intent(inout) :: qn
call qn%f%tag_hard_process ()
end subroutine quantum_numbers_tag_hard_process
@ %def quantum_numbers_tag_hard_process
@
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: set_subtraction_index => quantum_numbers_set_subtraction_index
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_set_subtraction_index (qn, i)
class(quantum_numbers_t), intent(inout) :: qn
integer, intent(in) :: i
qn%sub = i
end subroutine quantum_numbers_set_subtraction_index
@ %def quantum_numbers_set_subtraction_index
@
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: get_subtraction_index => quantum_numbers_get_subtraction_index
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_get_subtraction_index (qn) result (sub)
integer :: sub
class(quantum_numbers_t), intent(in) :: qn
sub = qn%sub
end function quantum_numbers_get_subtraction_index
@ %def quantum_numbers_get_subtraction_index
@ This is a convenience function: return the color type for the flavor
(array).
Note: keep the public version temporarily, this will be used in a
complicated expression which triggers a compiler bug (nagfor 5.3) in
the TBP version.
<<Quantum numbers: public>>=
public :: quantum_numbers_get_color_type
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: get_color_type => quantum_numbers_get_color_type
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_get_color_type (qn) result (color_type)
integer :: color_type
class(quantum_numbers_t), intent(in) :: qn
color_type = qn%f%get_color_type ()
end function quantum_numbers_get_color_type
@ %def quantum_numbers_get_color_type
@
\subsection{Predicates}
Check if the flavor index is valid (including UNDEFINED).
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: are_valid => quantum_numbers_are_valid
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_are_valid (qn) result (valid)
logical :: valid
class(quantum_numbers_t), intent(in) :: qn
valid = qn%f%is_valid ()
end function quantum_numbers_are_valid
@ %def quantum_numbers_are_valid
@ Check if the flavor part has its particle-data pointer associated
(debugging aid).
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: are_associated => quantum_numbers_are_associated
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_are_associated (qn) result (flag)
logical :: flag
class(quantum_numbers_t), intent(in) :: qn
flag = qn%f%is_associated ()
end function quantum_numbers_are_associated
@ %def quantum_numbers_are_associated
@ Check if the helicity and color quantum numbers are
diagonal. (Unpolarized/colorless also counts as diagonal.) Flavor is
diagonal by definition.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: are_diagonal => quantum_numbers_are_diagonal
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_are_diagonal (qn) result (diagonal)
logical :: diagonal
class(quantum_numbers_t), intent(in) :: qn
diagonal = qn%h%is_diagonal () .and. qn%c%is_diagonal ()
end function quantum_numbers_are_diagonal
@ %def quantum_numbers_are_diagonal
@ Check if the color part has the ghost property.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: is_color_ghost => quantum_numbers_is_color_ghost
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_is_color_ghost (qn) result (ghost)
logical :: ghost
class(quantum_numbers_t), intent(in) :: qn
ghost = qn%c%is_ghost ()
end function quantum_numbers_is_color_ghost
@ %def quantum_numbers_is_color_ghost
@ Check if the flavor participates in the hard interaction.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: are_hard_process => quantum_numbers_are_hard_process
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_are_hard_process (qn) result (hard_process)
logical :: hard_process
class(quantum_numbers_t), intent(in) :: qn
hard_process = qn%f%is_hard_process ()
end function quantum_numbers_are_hard_process
@ %def quantum_numbers_are_hard_process
@
\subsection{Comparisons}
Matching and equality is derived from the individual quantum numbers.
The variant [[fhmatch]] matches only flavor and helicity. The variant
[[dhmatch]] matches only diagonal helicity, if the matching helicity is
undefined.
<<Quantum numbers: public>>=
public :: quantum_numbers_eq_wo_sub
<<Quantum numbers: quantum numbers: TBP>>=
generic :: operator(.match.) => quantum_numbers_match
generic :: operator(.fmatch.) => quantum_numbers_match_f
generic :: operator(.hmatch.) => quantum_numbers_match_h
generic :: operator(.fhmatch.) => quantum_numbers_match_fh
generic :: operator(.dhmatch.) => quantum_numbers_match_hel_diag
generic :: operator(==) => quantum_numbers_eq
generic :: operator(/=) => quantum_numbers_neq
procedure, private :: quantum_numbers_match
procedure, private :: quantum_numbers_match_f
procedure, private :: quantum_numbers_match_h
procedure, private :: quantum_numbers_match_fh
procedure, private :: quantum_numbers_match_hel_diag
procedure, private :: quantum_numbers_eq
procedure, private :: quantum_numbers_neq
@ %def .match. == /=
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_match (qn1, qn2) result (match)
logical :: match
class(quantum_numbers_t), intent(in) :: qn1, qn2
match = (qn1%f .match. qn2%f) .and. &
(qn1%c .match. qn2%c) .and. &
(qn1%h .match. qn2%h)
end function quantum_numbers_match
elemental function quantum_numbers_match_f (qn1, qn2) result (match)
logical :: match
class(quantum_numbers_t), intent(in) :: qn1, qn2
match = (qn1%f .match. qn2%f)
end function quantum_numbers_match_f
elemental function quantum_numbers_match_h (qn1, qn2) result (match)
logical :: match
class(quantum_numbers_t), intent(in) :: qn1, qn2
match = (qn1%h .match. qn2%h)
end function quantum_numbers_match_h
elemental function quantum_numbers_match_fh (qn1, qn2) result (match)
logical :: match
class(quantum_numbers_t), intent(in) :: qn1, qn2
match = (qn1%f .match. qn2%f) .and. &
(qn1%h .match. qn2%h)
end function quantum_numbers_match_fh
elemental function quantum_numbers_match_hel_diag (qn1, qn2) result (match)
logical :: match
class(quantum_numbers_t), intent(in) :: qn1, qn2
match = (qn1%f .match. qn2%f) .and. &
(qn1%c .match. qn2%c) .and. &
(qn1%h .dmatch. qn2%h)
end function quantum_numbers_match_hel_diag
elemental function quantum_numbers_eq_wo_sub (qn1, qn2) result (eq)
logical :: eq
type(quantum_numbers_t), intent(in) :: qn1, qn2
eq = (qn1%f == qn2%f) .and. &
(qn1%c == qn2%c) .and. &
(qn1%h == qn2%h)
end function quantum_numbers_eq_wo_sub
elemental function quantum_numbers_eq (qn1, qn2) result (eq)
logical :: eq
class(quantum_numbers_t), intent(in) :: qn1, qn2
eq = (qn1%f == qn2%f) .and. &
(qn1%c == qn2%c) .and. &
(qn1%h == qn2%h) .and. &
(qn1%sub == qn2%sub)
end function quantum_numbers_eq
elemental function quantum_numbers_neq (qn1, qn2) result (neq)
logical :: neq
class(quantum_numbers_t), intent(in) :: qn1, qn2
neq = (qn1%f /= qn2%f) .or. &
(qn1%c /= qn2%c) .or. &
(qn1%h /= qn2%h) .or. &
(qn1%sub /= qn2%sub)
end function quantum_numbers_neq
@ %def quantum_numbers_match
@ %def quantum_numbers_eq
@ %def quantum_numbers_neq
<<Quantum numbers: public>>=
public :: assignment(=)
<<Quantum numbers: interfaces>>=
interface assignment(=)
module procedure quantum_numbers_assign
end interface
<<Quantum numbers: procedures>>=
subroutine quantum_numbers_assign (qn_out, qn_in)
type(quantum_numbers_t), intent(out) :: qn_out
type(quantum_numbers_t), intent(in) :: qn_in
qn_out%f = qn_in%f
qn_out%c = qn_in%c
qn_out%h = qn_in%h
qn_out%sub = qn_in%sub
end subroutine quantum_numbers_assign
@ %def quantum_numbers_assign
@ Two sets of quantum numbers are compatible if the individual quantum numbers
are compatible, depending on the mask. Flavor has to match, regardless of the
flavor mask.
If the color flag is set, color is compatible if the ghost property is
identical. If the color flag is unset, color has to be identical. I.e., if
the flag is set, the color amplitudes can interfere. If it is not set, they
must be identical, and there must be no ghost. The latter property is used
for expanding physical color flows.
Helicity is compatible if the mask is unset, otherwise it has to match. This
determines if two amplitudes can be multiplied (no mask) or traced (mask).
<<Quantum numbers: public>>=
public :: quantum_numbers_are_compatible
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_are_compatible (qn1, qn2, mask) &
result (flag)
logical :: flag
type(quantum_numbers_t), intent(in) :: qn1, qn2
type(quantum_numbers_mask_t), intent(in) :: mask
if (mask%h .or. mask%hd) then
flag = (qn1%f .match. qn2%f) .and. (qn1%h .match. qn2%h)
else
flag = (qn1%f .match. qn2%f)
end if
if (mask%c) then
flag = flag .and. (qn1%c%is_ghost () .eqv. qn2%c%is_ghost ())
else
flag = flag .and. &
.not. (qn1%c%is_ghost () .or. qn2%c%is_ghost ()) .and. &
(qn1%c == qn2%c)
end if
end function quantum_numbers_are_compatible
@ %def quantum_numbers_are_compatible
@ This is the analog for a single quantum-number set. We just check for color
ghosts; they are excluded if the color mask is unset (color-flow expansion).
<<Quantum numbers: public>>=
public :: quantum_numbers_are_physical
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_are_physical (qn, mask) result (flag)
logical :: flag
type(quantum_numbers_t), intent(in) :: qn
type(quantum_numbers_mask_t), intent(in) :: mask
if (mask%c) then
flag = .true.
else
flag = .not. qn%c%is_ghost ()
end if
end function quantum_numbers_are_physical
@ %def quantum_numbers_are_physical
@
\subsection{Operations}
Inherited from the color component: reassign color indices in
canonical order.
<<Quantum numbers: public>>=
public :: quantum_numbers_canonicalize_color
<<Quantum numbers: procedures>>=
subroutine quantum_numbers_canonicalize_color (qn)
type(quantum_numbers_t), dimension(:), intent(inout) :: qn
call color_canonicalize (qn%c)
end subroutine quantum_numbers_canonicalize_color
@ %def quantum_numbers_canonicalize_color
@ Inherited from the color component: make a color map for two matching
quantum-number arrays.
<<Quantum numbers: public>>=
public :: make_color_map
<<Quantum numbers: interfaces>>=
interface make_color_map
module procedure quantum_numbers_make_color_map
end interface make_color_map
<<Quantum numbers: procedures>>=
subroutine quantum_numbers_make_color_map (map, qn1, qn2)
integer, dimension(:,:), intent(out), allocatable :: map
type(quantum_numbers_t), dimension(:), intent(in) :: qn1, qn2
call make_color_map (map, qn1%c, qn2%c)
end subroutine quantum_numbers_make_color_map
@ %def make_color_map
@ Inherited from the color component: translate the color part using a
color-map array
<<Quantum numbers: public>>=
public :: quantum_numbers_translate_color
<<Quantum numbers: interfaces>>=
interface quantum_numbers_translate_color
module procedure quantum_numbers_translate_color0
module procedure quantum_numbers_translate_color1
end interface
<<Quantum numbers: procedures>>=
subroutine quantum_numbers_translate_color0 (qn, map, offset)
type(quantum_numbers_t), intent(inout) :: qn
integer, dimension(:,:), intent(in) :: map
integer, intent(in), optional :: offset
call color_translate (qn%c, map, offset)
end subroutine quantum_numbers_translate_color0
subroutine quantum_numbers_translate_color1 (qn, map, offset)
type(quantum_numbers_t), dimension(:), intent(inout) :: qn
integer, dimension(:,:), intent(in) :: map
integer, intent(in), optional :: offset
call color_translate (qn%c, map, offset)
end subroutine quantum_numbers_translate_color1
@ %def quantum_numbers_translate_color
@ Inherited from the color component: return the color index with
highest absolute value.
Since the algorithm is not elemental, we keep the separate
procedures for different array rank.
<<Quantum numbers: public>>=
public :: quantum_numbers_get_max_color_value
<<Quantum numbers: interfaces>>=
interface quantum_numbers_get_max_color_value
module procedure quantum_numbers_get_max_color_value0
module procedure quantum_numbers_get_max_color_value1
module procedure quantum_numbers_get_max_color_value2
end interface
<<Quantum numbers: procedures>>=
pure function quantum_numbers_get_max_color_value0 (qn) result (cmax)
integer :: cmax
type(quantum_numbers_t), intent(in) :: qn
cmax = color_get_max_value (qn%c)
end function quantum_numbers_get_max_color_value0
pure function quantum_numbers_get_max_color_value1 (qn) result (cmax)
integer :: cmax
type(quantum_numbers_t), dimension(:), intent(in) :: qn
cmax = color_get_max_value (qn%c)
end function quantum_numbers_get_max_color_value1
pure function quantum_numbers_get_max_color_value2 (qn) result (cmax)
integer :: cmax
type(quantum_numbers_t), dimension(:,:), intent(in) :: qn
cmax = color_get_max_value (qn%c)
end function quantum_numbers_get_max_color_value2
@ Inherited from the color component: add an offset to the indices of
the color part
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: add_color_offset => quantum_numbers_add_color_offset
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_add_color_offset (qn, offset)
class(quantum_numbers_t), intent(inout) :: qn
integer, intent(in) :: offset
call qn%c%add_offset (offset)
end subroutine quantum_numbers_add_color_offset
@ %def quantum_numbers_add_color_offset
@ Given a quantum number array, return all possible color
contractions, leaving the other quantum numbers intact.
<<Quantum numbers: public>>=
public :: quantum_number_array_make_color_contractions
<<Quantum numbers: procedures>>=
subroutine quantum_number_array_make_color_contractions (qn_in, qn_out)
type(quantum_numbers_t), dimension(:), intent(in) :: qn_in
type(quantum_numbers_t), dimension(:,:), intent(out), allocatable :: qn_out
type(color_t), dimension(:,:), allocatable :: col
integer :: i
call color_array_make_contractions (qn_in%c, col)
allocate (qn_out (size (col, 1), size (col, 2)))
do i = 1, size (qn_out, 2)
qn_out(:,i)%f = qn_in%f
qn_out(:,i)%c = col(:,i)
qn_out(:,i)%h = qn_in%h
end do
end subroutine quantum_number_array_make_color_contractions
@ %def quantum_number_array_make_color_contractions
@ Inherited from the color component: invert the color, switching
particle/antiparticle.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: invert_color => quantum_numbers_invert_color
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_invert_color (qn)
class(quantum_numbers_t), intent(inout) :: qn
call qn%c%invert ()
end subroutine quantum_numbers_invert_color
@ %def quantum_numbers_invert_color
@ Flip helicity.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: flip_helicity => quantum_numbers_flip_helicity
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_flip_helicity (qn)
class(quantum_numbers_t), intent(inout) :: qn
call qn%h%flip ()
end subroutine quantum_numbers_flip_helicity
@ %def quantum_numbers_flip_helicity
@
Merge two quantum number sets: for each entry, if both are defined,
combine them to an off-diagonal entry (meaningful only if the input
was diagonal). If either entry is undefined, take the defined
one.
For flavor, off-diagonal entries are invalid, so both
flavors must be equal, otherwise an invalid flavor is inserted.
<<Quantum numbers: public>>=
public :: operator(.merge.)
<<Quantum numbers: interfaces>>=
interface operator(.merge.)
module procedure merge_quantum_numbers0
module procedure merge_quantum_numbers1
end interface
<<Quantum numbers: procedures>>=
function merge_quantum_numbers0 (qn1, qn2) result (qn3)
type(quantum_numbers_t) :: qn3
type(quantum_numbers_t), intent(in) :: qn1, qn2
qn3%f = qn1%f .merge. qn2%f
qn3%c = qn1%c .merge. qn2%c
qn3%h = qn1%h .merge. qn2%h
qn3%sub = merge_subtraction_index (qn1%sub, qn2%sub)
end function merge_quantum_numbers0
function merge_quantum_numbers1 (qn1, qn2) result (qn3)
type(quantum_numbers_t), dimension(:), intent(in) :: qn1, qn2
type(quantum_numbers_t), dimension(size(qn1)) :: qn3
qn3%f = qn1%f .merge. qn2%f
qn3%c = qn1%c .merge. qn2%c
qn3%h = qn1%h .merge. qn2%h
qn3%sub = merge_subtraction_index (qn1%sub, qn2%sub)
end function merge_quantum_numbers1
@ %def merge_quantum_numbers
@
<<Quantum numbers: procedures>>=
elemental function merge_subtraction_index (sub1, sub2) result (sub3)
integer :: sub3
integer, intent(in) :: sub1, sub2
if (sub1 > 0 .and. sub2 > 0) then
if (sub1 == sub2) then
sub3 = sub1
else
sub3 = 0
end if
else if (sub1 > 0) then
sub3 = sub1
else if (sub2 > 0) then
sub3 = sub2
else
sub3 = 0
end if
end function merge_subtraction_index
@ %def merge_subtraction_index
@
\subsection{The quantum number mask}
The quantum numbers mask is true for quantum numbers that should be
ignored or summed over. The three mandatory entries correspond to
flavor, color, and helicity, respectively.
There is an additional entry [[cg]]: If false, the color-ghosts
property should be kept even if color is ignored. This is relevant
only if [[c]] is set, otherwise it is always false.
The flag [[hd]] tells that only diagonal entries in helicity should be
kept. If [[h]] is set, [[hd]] is irrelevant and will be kept
[[.false.]]
<<Quantum numbers: public>>=
public :: quantum_numbers_mask_t
<<Quantum numbers: types>>=
type :: quantum_numbers_mask_t
private
logical :: f = .false.
logical :: c = .false.
logical :: cg = .false.
logical :: h = .false.
logical :: hd = .false.
integer :: sub = 0
contains
<<Quantum numbers: quantum numbers mask: TBP>>
end type quantum_numbers_mask_t
@ %def quantum_number_t
@ Define a quantum number mask: Constructor form
<<Quantum numbers: public>>=
public :: quantum_numbers_mask
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_mask &
(mask_f, mask_c, mask_h, mask_cg, mask_hd) result (mask)
type(quantum_numbers_mask_t) :: mask
logical, intent(in) :: mask_f, mask_c, mask_h
logical, intent(in), optional :: mask_cg
logical, intent(in), optional :: mask_hd
call quantum_numbers_mask_init &
(mask, mask_f, mask_c, mask_h, mask_cg, mask_hd)
end function quantum_numbers_mask
@ %def new_quantum_numbers_mask
@ Define quantum numbers: Initializer form
<<Quantum numbers: quantum numbers mask: TBP>>=
procedure :: init => quantum_numbers_mask_init
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_mask_init &
(mask, mask_f, mask_c, mask_h, mask_cg, mask_hd)
class(quantum_numbers_mask_t), intent(inout) :: mask
logical, intent(in) :: mask_f, mask_c, mask_h
logical, intent(in), optional :: mask_cg, mask_hd
mask%f = mask_f
mask%c = mask_c
mask%h = mask_h
mask%cg = .false.
if (present (mask_cg)) then
if (mask%c) mask%cg = mask_cg
else
mask%cg = mask_c
end if
mask%hd = .false.
if (present (mask_hd)) then
if (.not. mask%h) mask%hd = mask_hd
end if
end subroutine quantum_numbers_mask_init
@ %def quantum_numbers_mask_init
@ Write a quantum numbers mask. We need the stand-alone subroutine for the
array case.
<<Quantum numbers: public>>=
public :: quantum_numbers_mask_write
<<Quantum numbers: interfaces>>=
interface quantum_numbers_mask_write
module procedure quantum_numbers_mask_write_single
module procedure quantum_numbers_mask_write_array
end interface
<<Quantum numbers: quantum numbers mask: TBP>>=
procedure :: write => quantum_numbers_mask_write_single
<<Quantum numbers: procedures>>=
subroutine quantum_numbers_mask_write_single (mask, unit)
class(quantum_numbers_mask_t), intent(in) :: mask
integer, intent(in), optional :: unit
integer :: u
u = given_output_unit (unit); if (u < 0) return
write (u, "(A)", advance="no") "["
write (u, "(L1)", advance="no") mask%f
write (u, "(L1)", advance="no") mask%c
if (.not.mask%cg) write (u, "('g')", advance="no")
write (u, "(L1)", advance="no") mask%h
if (mask%hd) write (u, "('d')", advance="no")
write (u, "(A)", advance="no") "]"
end subroutine quantum_numbers_mask_write_single
subroutine quantum_numbers_mask_write_array (mask, unit)
type(quantum_numbers_mask_t), dimension(:), intent(in) :: mask
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit); if (u < 0) return
write (u, "(A)", advance="no") "["
do i = 1, size (mask)
if (i > 1) write (u, "(A)", advance="no") "/"
write (u, "(L1)", advance="no") mask(i)%f
write (u, "(L1)", advance="no") mask(i)%c
if (.not.mask(i)%cg) write (u, "('g')", advance="no")
write (u, "(L1)", advance="no") mask(i)%h
if (mask(i)%hd) write (u, "('d')", advance="no")
end do
write (u, "(A)", advance="no") "]"
end subroutine quantum_numbers_mask_write_array
@ %def quantum_numbers_mask_write
@
\subsection{Setting mask components}
<<Quantum numbers: quantum numbers mask: TBP>>=
procedure :: set_flavor => quantum_numbers_mask_set_flavor
procedure :: set_color => quantum_numbers_mask_set_color
procedure :: set_helicity => quantum_numbers_mask_set_helicity
procedure :: set_sub => quantum_numbers_mask_set_sub
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_mask_set_flavor (mask, mask_f)
class(quantum_numbers_mask_t), intent(inout) :: mask
logical, intent(in) :: mask_f
mask%f = mask_f
end subroutine quantum_numbers_mask_set_flavor
elemental subroutine quantum_numbers_mask_set_color (mask, mask_c, mask_cg)
class(quantum_numbers_mask_t), intent(inout) :: mask
logical, intent(in) :: mask_c
logical, intent(in), optional :: mask_cg
mask%c = mask_c
if (present (mask_cg)) then
if (mask%c) mask%cg = mask_cg
else
mask%cg = mask_c
end if
end subroutine quantum_numbers_mask_set_color
elemental subroutine quantum_numbers_mask_set_helicity (mask, mask_h, mask_hd)
class(quantum_numbers_mask_t), intent(inout) :: mask
logical, intent(in) :: mask_h
logical, intent(in), optional :: mask_hd
mask%h = mask_h
if (present (mask_hd)) then
if (.not. mask%h) mask%hd = mask_hd
end if
end subroutine quantum_numbers_mask_set_helicity
elemental subroutine quantum_numbers_mask_set_sub (mask, sub)
class(quantum_numbers_mask_t), intent(inout) :: mask
integer, intent(in) :: sub
mask%sub = sub
end subroutine quantum_numbers_mask_set_sub
@ %def quantum_numbers_mask_set_flavor
@ %def quantum_numbers_mask_set_color
@ %def quantum_numbers_mask_set_helicity
@ %def quantum_numbers_mask_set_sub
@ The following routines assign part of a mask, depending on the flags given.
<<Quantum numbers: quantum numbers mask: TBP>>=
procedure :: assign => quantum_numbers_mask_assign
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_mask_assign &
(mask, mask_in, flavor, color, helicity)
class(quantum_numbers_mask_t), intent(inout) :: mask
class(quantum_numbers_mask_t), intent(in) :: mask_in
logical, intent(in), optional :: flavor, color, helicity
if (present (flavor)) then
if (flavor) then
mask%f = mask_in%f
end if
end if
if (present (color)) then
if (color) then
mask%c = mask_in%c
mask%cg = mask_in%cg
end if
end if
if (present (helicity)) then
if (helicity) then
mask%h = mask_in%h
mask%hd = mask_in%hd
end if
end if
end subroutine quantum_numbers_mask_assign
@ %def quantum_numbers_mask_assign
@
\subsection{Mask predicates}
Return true if either one of the entries is set:
<<Quantum numbers: public>>=
public :: any
<<Quantum numbers: interfaces>>=
interface any
module procedure quantum_numbers_mask_any
end interface
<<Quantum numbers: procedures>>=
function quantum_numbers_mask_any (mask) result (match)
logical :: match
type(quantum_numbers_mask_t), intent(in) :: mask
match = mask%f .or. mask%c .or. mask%h .or. mask%hd
end function quantum_numbers_mask_any
@ %def any
@
\subsection{Operators}
The OR operation is applied to all components.
<<Quantum numbers: quantum numbers mask: TBP>>=
generic :: operator(.or.) => quantum_numbers_mask_or
procedure, private :: quantum_numbers_mask_or
@ %def .or.
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_mask_or (mask1, mask2) result (mask)
type(quantum_numbers_mask_t) :: mask
class(quantum_numbers_mask_t), intent(in) :: mask1, mask2
mask%f = mask1%f .or. mask2%f
mask%c = mask1%c .or. mask2%c
if (mask%c) mask%cg = mask1%cg .or. mask2%cg
mask%h = mask1%h .or. mask2%h
if (.not. mask%h) mask%hd = mask1%hd .or. mask2%hd
end function quantum_numbers_mask_or
@ %def quantum_numbers_mask_or
@
\subsection{Mask comparisons}
Return true if the two masks are equivalent / differ:
<<Quantum numbers: quantum numbers mask: TBP>>=
generic :: operator(.eqv.) => quantum_numbers_mask_eqv
generic :: operator(.neqv.) => quantum_numbers_mask_neqv
procedure, private :: quantum_numbers_mask_eqv
procedure, private :: quantum_numbers_mask_neqv
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_mask_eqv (mask1, mask2) result (eqv)
logical :: eqv
class(quantum_numbers_mask_t), intent(in) :: mask1, mask2
eqv = (mask1%f .eqv. mask2%f) .and. &
(mask1%c .eqv. mask2%c) .and. &
(mask1%cg .eqv. mask2%cg) .and. &
(mask1%h .eqv. mask2%h) .and. &
(mask1%hd .eqv. mask2%hd)
end function quantum_numbers_mask_eqv
elemental function quantum_numbers_mask_neqv (mask1, mask2) result (neqv)
logical :: neqv
class(quantum_numbers_mask_t), intent(in) :: mask1, mask2
neqv = (mask1%f .neqv. mask2%f) .or. &
(mask1%c .neqv. mask2%c) .or. &
(mask1%cg .neqv. mask2%cg) .or. &
(mask1%h .neqv. mask2%h) .or. &
(mask1%hd .neqv. mask2%hd)
end function quantum_numbers_mask_neqv
@ %def .eqv. .neqv.
@
\subsection{Apply a mask}
Applying a mask to the quantum number object means undefining those
entries where the mask is set. The others remain unaffected.
The [[hd]] mask has the special property that it ``diagonalizes''
helicity, i.e., the second helicity entry is dropped and the result is
a diagonal helicity quantum number.
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: undefine => quantum_numbers_undefine
procedure :: undefined => quantum_numbers_undefined0
<<Quantum numbers: public>>=
public :: quantum_numbers_undefined
<<Quantum numbers: interfaces>>=
interface quantum_numbers_undefined
module procedure quantum_numbers_undefined0
module procedure quantum_numbers_undefined1
module procedure quantum_numbers_undefined11
end interface
<<Quantum numbers: procedures>>=
elemental subroutine quantum_numbers_undefine (qn, mask)
class(quantum_numbers_t), intent(inout) :: qn
type(quantum_numbers_mask_t), intent(in) :: mask
if (mask%f) call qn%f%undefine ()
if (mask%c) call qn%c%undefine (undefine_ghost = mask%cg)
if (mask%h) then
call qn%h%undefine ()
else if (mask%hd) then
if (.not. qn%h%is_diagonal ()) then
call qn%h%diagonalize ()
end if
end if
if (mask%sub > 0) qn%sub = 0
end subroutine quantum_numbers_undefine
function quantum_numbers_undefined0 (qn, mask) result (qn_new)
class(quantum_numbers_t), intent(in) :: qn
type(quantum_numbers_mask_t), intent(in) :: mask
type(quantum_numbers_t) :: qn_new
select type (qn)
type is (quantum_numbers_t); qn_new = qn
end select
call quantum_numbers_undefine (qn_new, mask)
end function quantum_numbers_undefined0
function quantum_numbers_undefined1 (qn, mask) result (qn_new)
type(quantum_numbers_t), dimension(:), intent(in) :: qn
type(quantum_numbers_mask_t), intent(in) :: mask
type(quantum_numbers_t), dimension(size(qn)) :: qn_new
qn_new = qn
call quantum_numbers_undefine (qn_new, mask)
end function quantum_numbers_undefined1
function quantum_numbers_undefined11 (qn, mask) result (qn_new)
type(quantum_numbers_t), dimension(:), intent(in) :: qn
type(quantum_numbers_mask_t), dimension(:), intent(in) :: mask
type(quantum_numbers_t), dimension(size(qn)) :: qn_new
qn_new = qn
call quantum_numbers_undefine (qn_new, mask)
end function quantum_numbers_undefined11
@ %def quantum_numbers_undefine
@ %def quantum_numbers_undefined
@ Return true if the input quantum number set has entries that would
be removed by the applied mask, e.g., if polarization is defined but
[[mask%h]] is set:
<<Quantum numbers: quantum numbers: TBP>>=
procedure :: are_redundant => quantum_numbers_are_redundant
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_are_redundant (qn, mask) &
result (redundant)
logical :: redundant
class(quantum_numbers_t), intent(in) :: qn
type(quantum_numbers_mask_t), intent(in) :: mask
redundant = .false.
if (mask%f) then
redundant = qn%f%is_defined ()
end if
if (mask%c) then
redundant = qn%c%is_defined ()
end if
if (mask%h) then
redundant = qn%h%is_defined ()
else if (mask%hd) then
redundant = .not. qn%h%is_diagonal ()
end if
if (mask%sub > 0) redundant = qn%sub >= mask%sub
end function quantum_numbers_are_redundant
@ %def quantum_numbers_are_redundant
@ Return true if the helicity flag is set or the diagonal-helicity flag is
set.
<<Quantum numbers: quantum numbers mask: TBP>>=
procedure :: diagonal_helicity => quantum_numbers_mask_diagonal_helicity
<<Quantum numbers: procedures>>=
elemental function quantum_numbers_mask_diagonal_helicity (mask) &
result (flag)
logical :: flag
class(quantum_numbers_mask_t), intent(in) :: mask
flag = mask%h .or. mask%hd
end function quantum_numbers_mask_diagonal_helicity
@ %def quantum_numbers_mask_diagonal_helicity
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Transition Matrices and Evaluation}
The modules in this chapter implement transition matrices and calculations.
The functionality is broken down in three modules
\begin{description}
\item[state\_matrices]
represent state and transition density matrices built from particle quantum
numbers (helicity, color, flavor)
\item[interactions]
extend state matrices with the record of particle momenta. They also
distinguish in- and out-particles and store parent-child relations.
\item[evaluators]
These objects extend interaction objects by the information how to calculate
matrix elements from products and squares of other interactions. They
implement the methods to actually compute those matrix elements.
\end{description}
\clearpage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{State matrices}
This module deals with the internal state of a particle system, i.e.,
with its density matrix in flavor, color, and helicity space.
<<[[state_matrices.f90]]>>=
<<File header>>
module state_matrices
<<Use kinds>>
use io_units
use format_utils, only: pac_fmt
use format_defs, only: FMT_17, FMT_19
use diagnostics
use sorting
use model_data
use flavors
use colors
use helicities
use quantum_numbers
<<Standard module head>>
<<State matrices: public>>
<<State matrices: parameters>>
<<State matrices: types>>
<<State matrices: interfaces>>
contains
<<State matrices: procedures>>
end module state_matrices
@ %def state_matrices
@
\subsection{Nodes of the quantum state trie}
A quantum state object represents an unnormalized density matrix,
i.e., an array of possibilities for flavor, color, and helicity
indices with associated complex values. Physically, the trace of this
matrix is the summed squared matrix element for an interaction, and
the matrix elements divided by this value correspond to the
flavor-color-helicity density matrix. (Flavor and color are
diagonal.)
We store density matrices as tries, that is, as trees where each
branching represents the possible quantum numbers of a particle. The
first branching is the first particle in the system. A leaf (the node
corresponding to the last particle) contains the value of the matrix
element.
Each node contains a flavor, color, and helicity entry. Note that
each of those entries may be actually undefined, so we can also represent,
e.g., unpolarized particles.
The value is meaningful only for leaves, which have no child nodes.
There is a pointer to the parent node which allows for following the
trie downwards from a leaf, it is null for a root node. The child
nodes are implemented as a list, so there is a pointer to the first
and last child, and each node also has a [[next]] pointer to the next
sibling.
The root node does not correspond to a particle, only its children do.
The quantum numbers of the root node are irrelevant and will not be
set. However, we use a common type for the three classes (root,
branch, leaf); they may easily be distinguished by the association
status of parent and child.
\subsubsection{Node type}
The node is linked in all directions: the parent, the first and last
in the list of children, and the previous and next sibling. This allows
us for adding and removing nodes and whole branches anywhere in the
trie. (Circular links are not allowed, however.). The node holds its
associated set of quantum numbers. The integer index, which is set
only for leaf nodes, is the index of the corresponding matrix element
value within the state matrix.
Temporarily, matrix-element values may be stored within a leaf node.
This is used during state-matrix factorization. When the state matrix
is [[freeze]]d, these values are transferred to the matrix-element
array within the host state matrix.
<<State matrices: types>>=
type :: node_t
private
type(quantum_numbers_t) :: qn
type(node_t), pointer :: parent => null ()
type(node_t), pointer :: child_first => null ()
type(node_t), pointer :: child_last => null ()
type(node_t), pointer :: next => null ()
type(node_t), pointer :: previous => null ()
integer :: me_index = 0
integer, dimension(:), allocatable :: me_count
complex(default) :: me = 0
end type node_t
@ %def node_t
@
\subsubsection{Operations on nodes}
Recursively deallocate all children of the current
node. This includes any values associated with the children.
<<State matrices: procedures>>=
pure recursive subroutine node_delete_offspring (node)
type(node_t), pointer :: node
type(node_t), pointer :: child
child => node%child_first
do while (associated (child))
node%child_first => node%child_first%next
call node_delete_offspring (child)
deallocate (child)
child => node%child_first
end do
node%child_last => null ()
end subroutine node_delete_offspring
@ %def node_delete_offspring
@ Remove a node including its offspring. Adjust the pointers of
parent and siblings, if necessary.
<<State matrices: procedures>>=
pure subroutine node_delete (node)
type(node_t), pointer :: node
call node_delete_offspring (node)
if (associated (node%previous)) then
node%previous%next => node%next
else if (associated (node%parent)) then
node%parent%child_first => node%next
end if
if (associated (node%next)) then
node%next%previous => node%previous
else if (associated (node%parent)) then
node%parent%child_last => node%previous
end if
deallocate (node)
end subroutine node_delete
@ %def node_delete
@ Append a child node
<<State matrices: procedures>>=
subroutine node_append_child (node, child)
type(node_t), target, intent(inout) :: node
type(node_t), pointer :: child
allocate (child)
if (associated (node%child_last)) then
node%child_last%next => child
child%previous => node%child_last
else
node%child_first => child
end if
node%child_last => child
child%parent => node
end subroutine node_append_child
@ %def node_append_child
@
\subsubsection{I/O}
Output of a single node, no recursion. We print the quantum numbers
in square brackets, then the value (if any).
<<State matrices: procedures>>=
subroutine node_write (node, me_array, verbose, unit, col_verbose, testflag)
type(node_t), intent(in) :: node
complex(default), dimension(:), intent(in), optional :: me_array
logical, intent(in), optional :: verbose, col_verbose, testflag
integer, intent(in), optional :: unit
logical :: verb
integer :: u
character(len=7) :: fmt
call pac_fmt (fmt, FMT_19, FMT_17, testflag)
verb = .false.; if (present (verbose)) verb = verbose
u = given_output_unit (unit); if (u < 0) return
call node%qn%write (u, col_verbose)
if (node%me_index /= 0) then
write (u, "(A,I0,A)", advance="no") " => ME(", node%me_index, ")"
if (present (me_array)) then
write (u, "(A)", advance="no") " = "
write (u, "('('," // fmt // ",','," // fmt // ",')')", &
advance="no") pacify_complex (me_array(node%me_index))
end if
end if
write (u, *)
if (verb) then
call ptr_write ("parent ", node%parent)
call ptr_write ("child_first", node%child_first)
call ptr_write ("child_last ", node%child_last)
call ptr_write ("next ", node%next)
call ptr_write ("previous ", node%previous)
end if
contains
subroutine ptr_write (label, node)
character(*), intent(in) :: label
type(node_t), pointer :: node
if (associated (node)) then
write (u, "(10x,A,1x,'->',1x)", advance="no") label
call node%qn%write (u, col_verbose)
write (u, *)
end if
end subroutine ptr_write
end subroutine node_write
@ %def node_write
@ Recursive output of a node:
<<State matrices: procedures>>=
recursive subroutine node_write_rec (node, me_array, verbose, &
indent, unit, col_verbose, testflag)
type(node_t), intent(in), target :: node
complex(default), dimension(:), intent(in), optional :: me_array
logical, intent(in), optional :: verbose, col_verbose, testflag
integer, intent(in), optional :: indent
integer, intent(in), optional :: unit
type(node_t), pointer :: current
logical :: verb
integer :: i, u
verb = .false.; if (present (verbose)) verb = verbose
i = 0; if (present (indent)) i = indent
u = given_output_unit (unit); if (u < 0) return
current => node%child_first
do while (associated (current))
write (u, "(A)", advance="no") repeat (" ", i)
call node_write (current, me_array, verbose = verb, &
unit = u, col_verbose = col_verbose, testflag = testflag)
call node_write_rec (current, me_array, verbose = verb, &
indent = i + 2, unit = u, col_verbose = col_verbose, testflag = testflag)
current => current%next
end do
end subroutine node_write_rec
@ %def node_write_rec
@ Binary I/O. Matrix elements are written only for leaf nodes.
<<State matrices: procedures>>=
recursive subroutine node_write_raw_rec (node, u)
type(node_t), intent(in), target :: node
integer, intent(in) :: u
logical :: associated_child_first, associated_next
call node%qn%write_raw (u)
associated_child_first = associated (node%child_first)
write (u) associated_child_first
associated_next = associated (node%next)
write (u) associated_next
if (associated_child_first) then
call node_write_raw_rec (node%child_first, u)
else
write (u) node%me_index
write (u) node%me
end if
if (associated_next) then
call node_write_raw_rec (node%next, u)
end if
end subroutine node_write_raw_rec
recursive subroutine node_read_raw_rec (node, u, parent, iostat)
type(node_t), intent(out), target :: node
integer, intent(in) :: u
type(node_t), intent(in), optional, target :: parent
integer, intent(out), optional :: iostat
logical :: associated_child_first, associated_next
type(node_t), pointer :: child
call node%qn%read_raw (u, iostat=iostat)
read (u, iostat=iostat) associated_child_first
read (u, iostat=iostat) associated_next
if (present (parent)) node%parent => parent
if (associated_child_first) then
allocate (child)
node%child_first => child
node%child_last => null ()
call node_read_raw_rec (child, u, node, iostat=iostat)
do while (associated (child))
child%previous => node%child_last
node%child_last => child
child => child%next
end do
else
read (u, iostat=iostat) node%me_index
read (u, iostat=iostat) node%me
end if
if (associated_next) then
allocate (node%next)
call node_read_raw_rec (node%next, u, parent, iostat=iostat)
end if
end subroutine node_read_raw_rec
@ %def node_write_raw
@
\subsection{State matrix}
\subsubsection{Definition}
The quantum state object is a container that keeps and hides the root
node. For direct accessibility of values, they are stored
in a separate array. The leaf nodes of the quantum-number tree point to those
values, once the state matrix is finalized.
The [[norm]] component is redefined if a common factor is extracted from all
nodes.
<<State matrices: public>>=
public :: state_matrix_t
<<State matrices: types>>=
type :: state_matrix_t
private
type(node_t), pointer :: root => null ()
integer :: depth = 0
integer :: n_matrix_elements = 0
logical :: leaf_nodes_store_values = .false.
integer :: n_counters = 0
complex(default), dimension(:), allocatable :: me
real(default) :: norm = 1
integer :: n_sub = -1
contains
<<State matrices: state matrix: TBP>>
end type state_matrix_t
@ %def state_matrix_t
@ This initializer allocates the root node but does not fill
anything. We declare whether values are stored within the nodes
during state-matrix construction, and how many counters should be
maintained (default: none).
<<State matrices: state matrix: TBP>>=
procedure :: init => state_matrix_init
<<State matrices: procedures>>=
subroutine state_matrix_init (state, store_values, n_counters)
class(state_matrix_t), intent(out) :: state
logical, intent(in), optional :: store_values
integer, intent(in), optional :: n_counters
allocate (state%root)
if (present (store_values)) &
state%leaf_nodes_store_values = store_values
if (present (n_counters)) state%n_counters = n_counters
end subroutine state_matrix_init
@ %def state_matrix_init
@ This recursively deletes all children of the root node, restoring
the initial state. The matrix element array is not finalized, since
it does not contain physical entries, just pointers.
<<State matrices: state matrix: TBP>>=
procedure :: final => state_matrix_final
<<State matrices: procedures>>=
subroutine state_matrix_final (state)
class(state_matrix_t), intent(inout) :: state
if (allocated (state%me)) deallocate (state%me)
if (associated (state%root)) call node_delete (state%root)
state%depth = 0
state%n_matrix_elements = 0
end subroutine state_matrix_final
@ %def state_matrix_final
@ Output: Present the tree as a nested list with appropriate
indentation.
<<State matrices: state matrix: TBP>>=
procedure :: write => state_matrix_write
<<State matrices: procedures>>=
subroutine state_matrix_write (state, unit, write_value_list, &
verbose, col_verbose, testflag)
class(state_matrix_t), intent(in) :: state
logical, intent(in), optional :: write_value_list, verbose, col_verbose
logical, intent(in), optional :: testflag
integer, intent(in), optional :: unit
complex(default) :: me_dum
character(len=7) :: fmt
integer :: u
integer :: i
call pac_fmt (fmt, FMT_19, FMT_17, testflag)
u = given_output_unit (unit); if (u < 0) return
write (u, "(1x,A," // fmt // ")") "State matrix: norm = ", state%norm
if (associated (state%root)) then
if (allocated (state%me)) then
call node_write_rec (state%root, state%me, verbose = verbose, &
indent = 1, unit = u, col_verbose = col_verbose, &
testflag = testflag)
else
call node_write_rec (state%root, verbose = verbose, indent = 1, &
unit = u, col_verbose = col_verbose, testflag = testflag)
end if
end if
if (present (write_value_list)) then
if (write_value_list .and. allocated (state%me)) then
do i = 1, size (state%me)
write (u, "(1x,I0,A)", advance="no") i, ":"
me_dum = state%me(i)
if (real(state%me(i)) == -real(state%me(i))) then
me_dum = &
cmplx (0._default, aimag(me_dum), kind=default)
end if
if (aimag(me_dum) == -aimag(me_dum)) then
me_dum = &
cmplx (real(me_dum), 0._default, kind=default)
end if
write (u, "('('," // fmt // ",','," // fmt // &
",')')") me_dum
end do
end if
end if
end subroutine state_matrix_write
@ %def state_matrix_write
@ Binary I/O. The auxiliary matrix-element array is not written, but
reconstructed after reading the tree.
Note: To be checked. Might be broken, don't use (unless trivial).
<<State matrices: state matrix: TBP>>=
procedure :: write_raw => state_matrix_write_raw
procedure :: read_raw => state_matrix_read_raw
<<State matrices: procedures>>=
subroutine state_matrix_write_raw (state, u)
class(state_matrix_t), intent(in), target :: state
integer, intent(in) :: u
logical :: is_defined
integer :: depth, j
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(:), allocatable :: qn
is_defined = state%is_defined ()
write (u) is_defined
if (is_defined) then
write (u) state%get_norm ()
write (u) state%get_n_leaves ()
depth = state%get_depth ()
write (u) depth
allocate (qn (depth))
call it%init (state)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
do j = 1, depth
call qn(j)%write_raw (u)
end do
write (u) it%get_me_index ()
write (u) it%get_matrix_element ()
call it%advance ()
end do
end if
end subroutine state_matrix_write_raw
subroutine state_matrix_read_raw (state, u, iostat)
class(state_matrix_t), intent(out) :: state
integer, intent(in) :: u
integer, intent(out) :: iostat
logical :: is_defined
real(default) :: norm
integer :: n_leaves, depth, i, j
type(quantum_numbers_t), dimension(:), allocatable :: qn
integer :: me_index
complex(default) :: me
read (u, iostat=iostat) is_defined
if (iostat /= 0) goto 1
if (is_defined) then
call state%init (store_values = .true.)
read (u, iostat=iostat) norm
if (iostat /= 0) goto 1
call state_matrix_set_norm (state, norm)
read (u) n_leaves
if (iostat /= 0) goto 1
read (u) depth
if (iostat /= 0) goto 1
allocate (qn (depth))
do i = 1, n_leaves
do j = 1, depth
call qn(j)%read_raw (u, iostat=iostat)
if (iostat /= 0) goto 1
end do
read (u, iostat=iostat) me_index
if (iostat /= 0) goto 1
read (u, iostat=iostat) me
if (iostat /= 0) goto 1
call state%add_state (qn, index = me_index, value = me)
end do
call state_matrix_freeze (state)
end if
return
! Clean up on error
1 continue
call state%final ()
end subroutine state_matrix_read_raw
@ %def state_matrix_write_raw state_matrix_read_raw
@ Assign a model pointer to all flavor entries. This will become
necessary when we have read a state matrix from file.
<<State matrices: state matrix: TBP>>=
procedure :: set_model => state_matrix_set_model
<<State matrices: procedures>>=
subroutine state_matrix_set_model (state, model)
class(state_matrix_t), intent(inout), target :: state
class(model_data_t), intent(in), target :: model
type(state_iterator_t) :: it
call it%init (state)
do while (it%is_valid ())
call it%set_model (model)
call it%advance ()
end do
end subroutine state_matrix_set_model
@ %def state_matrix_set_model
@ Iterate over [[state]], get the quantum numbers array [[qn]] for each iteration, and tag
all array elements of [[qn]] with the indizes given by [[tag]] as part of the hard interaction.
Then add them to [[tagged_state]] and return it. If no [[tag]] is given, tag all [[qn]] as
part of the hard process.
<<State matrices: state matrix: TBP>>=
procedure :: tag_hard_process => state_matrix_tag_hard_process
<<State matrices: procedures>>=
subroutine state_matrix_tag_hard_process (state, tagged_state, tag)
class(state_matrix_t), intent(in), target :: state
type(state_matrix_t), intent(out) :: tagged_state
integer, dimension(:), intent(in), optional :: tag
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(:), allocatable :: qn
complex(default) :: value
integer :: i
call tagged_state%init (store_values = .true.)
call it%init (state)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
value = it%get_matrix_element ()
if (present (tag)) then
do i = 1, size (tag)
call qn(tag(i))%tag_hard_process ()
end do
else
call qn%tag_hard_process ()
end if
call tagged_state%add_state (qn, index = it%get_me_index (), value = value)
call it%advance ()
end do
call tagged_state%freeze ()
end subroutine state_matrix_tag_hard_process
@ %def state_matrix_tag_hard_process
\subsubsection{Properties of the quantum state}
A state is defined if its root is allocated:
<<State matrices: state matrix: TBP>>=
procedure :: is_defined => state_matrix_is_defined
<<State matrices: procedures>>=
elemental function state_matrix_is_defined (state) result (defined)
logical :: defined
class(state_matrix_t), intent(in) :: state
defined = associated (state%root)
end function state_matrix_is_defined
@ %def state_matrix_is_defined
@ A state is empty if its depth is zero:
<<State matrices: state matrix: TBP>>=
procedure :: is_empty => state_matrix_is_empty
<<State matrices: procedures>>=
elemental function state_matrix_is_empty (state) result (flag)
logical :: flag
class(state_matrix_t), intent(in) :: state
flag = state%depth == 0
end function state_matrix_is_empty
@ %def state_matrix_is_empty
@ Return the number of matrix-element values.
<<State matrices: state matrix: TBP>>=
generic :: get_n_matrix_elements => get_n_matrix_elements_all, get_n_matrix_elements_mask
procedure :: get_n_matrix_elements_all => state_matrix_get_n_matrix_elements_all
procedure :: get_n_matrix_elements_mask => state_matrix_get_n_matrix_elements_mask
<<State matrices: procedures>>=
pure function state_matrix_get_n_matrix_elements_all (state) result (n)
integer :: n
class(state_matrix_t), intent(in) :: state
n = state%n_matrix_elements
end function state_matrix_get_n_matrix_elements_all
@ %def state_matrix_get_n_matrix_elements_all
@
<<State matrices: procedures>>=
function state_matrix_get_n_matrix_elements_mask (state, qn_mask) result (n)
integer :: n
class(state_matrix_t), intent(in) :: state
type(quantum_numbers_mask_t), intent(in), dimension(:) :: qn_mask
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(size(qn_mask)) :: qn
type(state_matrix_t) :: state_tmp
call state_tmp%init ()
call it%init (state)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
call qn%undefine (qn_mask)
call state_tmp%add_state (qn)
call it%advance ()
end do
n = state_tmp%n_matrix_elements
call state_tmp%final ()
end function state_matrix_get_n_matrix_elements_mask
@ %def state_matrix_get_n_matrix_elments_mask
@ Return the size of the [[me]]-array for debugging purposes.
<<State matrices: state matrix: TBP>>=
procedure :: get_me_size => state_matrix_get_me_size
<<State matrices: procedures>>=
pure function state_matrix_get_me_size (state) result (n)
integer :: n
class(state_matrix_t), intent(in) :: state
if (allocated (state%me)) then
n = size (state%me)
else
n = 0
end if
end function state_matrix_get_me_size
@ %def state_matrix_get_me_size
@
<<State matrices: state matrix: TBP>>=
procedure :: compute_n_sub => state_matrix_compute_n_sub
<<State matrices: procedures>>=
function state_matrix_compute_n_sub (state) result (n_sub)
integer :: n_sub
class(state_matrix_t), intent(in) :: state
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(state%depth) :: qn
integer :: sub, sub_pos
n_sub = 0
call it%init (state)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
sub = 0
sub_pos = qn_array_sub_pos ()
if (sub_pos > 0) sub = qn(sub_pos)%get_sub ()
if (sub > n_sub) n_sub = sub
call it%advance ()
end do
contains
function qn_array_sub_pos () result (pos)
integer :: pos
integer :: i
pos = 0
do i = 1, state%depth
if (qn(i)%get_sub () > 0) then
pos = i
exit
end if
end do
end function qn_array_sub_pos
end function state_matrix_compute_n_sub
@ %def state_matrix_compute_n_sub
@
<<State matrices: state matrix: TBP>>=
procedure :: set_n_sub => state_matrix_set_n_sub
<<State matrices: procedures>>=
subroutine state_matrix_set_n_sub (state)
class(state_matrix_t), intent(inout) :: state
state%n_sub = state%compute_n_sub ()
end subroutine state_matrix_set_n_sub
@ %def state_matrix_set_n_sub
@ Return number of subtractions.
<<State matrices: state matrix: TBP>>=
procedure :: get_n_sub => state_matrix_get_n_sub
<<State matrices: procedures>>=
function state_matrix_get_n_sub (state) result (n_sub)
integer :: n_sub
class(state_matrix_t), intent(in) :: state
if (state%n_sub < 0) then
call msg_bug ("[state_matrix_get_n_sub] number of subtractions not set.")
end if
n_sub = state%n_sub
end function state_matrix_get_n_sub
@ %def state_matrix_get_n_sub
@ Return the number of leaves. This can be larger than the number of
independent matrix elements.
<<State matrices: state matrix: TBP>>=
procedure :: get_n_leaves => state_matrix_get_n_leaves
<<State matrices: procedures>>=
function state_matrix_get_n_leaves (state) result (n)
integer :: n
class(state_matrix_t), intent(in) :: state
type(state_iterator_t) :: it
n = 0
call it%init (state)
do while (it%is_valid ())
n = n + 1
call it%advance ()
end do
end function state_matrix_get_n_leaves
@ %def state_matrix_get_n_leaves
@ Return the depth:
<<State matrices: state matrix: TBP>>=
procedure :: get_depth => state_matrix_get_depth
<<State matrices: procedures>>=
pure function state_matrix_get_depth (state) result (depth)
integer :: depth
class(state_matrix_t), intent(in) :: state
depth = state%depth
end function state_matrix_get_depth
@ %def state_matrix_get_depth
@ Return the norm:
<<State matrices: state matrix: TBP>>=
procedure :: get_norm => state_matrix_get_norm
<<State matrices: procedures>>=
pure function state_matrix_get_norm (state) result (norm)
real(default) :: norm
class(state_matrix_t), intent(in) :: state
norm = state%norm
end function state_matrix_get_norm
@ %def state_matrix_get_norm
@
\subsubsection{Retrieving contents}
Return the quantum number array, using an index. We have to scan the
state matrix since there is no shortcut.
<<State matrices: state matrix: TBP>>=
procedure :: get_quantum_number => &
state_matrix_get_quantum_number
<<State matrices: procedures>>=
function state_matrix_get_quantum_number (state, i, by_me_index) result (qn)
class(state_matrix_t), intent(in), target :: state
integer, intent(in) :: i
logical, intent(in), optional :: by_me_index
logical :: opt_by_me_index
type(quantum_numbers_t), dimension(state%depth) :: qn
type(state_iterator_t) :: it
integer :: k
opt_by_me_index = .false.
if (present (by_me_index)) opt_by_me_index = by_me_index
k = 0
call it%init (state)
do while (it%is_valid ())
if (opt_by_me_index) then
k = it%get_me_index ()
else
k = k + 1
end if
if (k == i) then
qn = it%get_quantum_numbers ()
exit
end if
call it%advance ()
end do
end function state_matrix_get_quantum_number
@ %def state_matrix_get_quantum_number
<<State matrices: state matrix: TBP>>=
generic :: get_quantum_numbers => get_quantum_numbers_all, get_quantum_numbers_mask
procedure :: get_quantum_numbers_all => state_matrix_get_quantum_numbers_all
procedure :: get_quantum_numbers_mask => state_matrix_get_quantum_numbers_mask
<<State matrices: procedures>>=
subroutine state_matrix_get_quantum_numbers_all (state, qn)
class(state_matrix_t), intent(in), target :: state
type(quantum_numbers_t), intent(out), dimension(:,:), allocatable :: qn
integer :: i
allocate (qn (state%get_n_matrix_elements (), &
state%get_depth()))
do i = 1, state%get_n_matrix_elements ()
qn (i, :) = state%get_quantum_number (i)
end do
end subroutine state_matrix_get_quantum_numbers_all
@ %def state_matrix_get_quantum_numbers_all
@
<<State matrices: procedures>>=
subroutine state_matrix_get_quantum_numbers_mask (state, qn_mask, qn)
class(state_matrix_t), intent(in), target :: state
type(quantum_numbers_mask_t), intent(in), dimension(:) :: qn_mask
type(quantum_numbers_t), intent(out), dimension(:,:), allocatable :: qn
type(quantum_numbers_t), dimension(:), allocatable :: qn_tmp
type(state_matrix_t) :: state_tmp
type(state_iterator_t) :: it
integer :: i, n
n = state%get_n_matrix_elements (qn_mask)
allocate (qn (n, state%get_depth ()))
allocate (qn_tmp (state%get_depth ()))
call it%init (state)
call state_tmp%init ()
do while (it%is_valid ())
qn_tmp = it%get_quantum_numbers ()
call qn_tmp%undefine (qn_mask)
call state_tmp%add_state (qn_tmp)
call it%advance ()
end do
do i = 1, n
qn (i, :) = state_tmp%get_quantum_number (i)
end do
call state_tmp%final ()
end subroutine state_matrix_get_quantum_numbers_mask
@ %def state_matrix_get_quantum_numbers_mask
@
<<State matrices: state matrix: TBP>>=
procedure :: get_flavors => state_matrix_get_flavors
<<State matrices: procedures>>=
subroutine state_matrix_get_flavors (state, only_elementary, qn_mask, flv)
class(state_matrix_t), intent(in), target :: state
logical, intent(in) :: only_elementary
type(quantum_numbers_mask_t), intent(in), dimension(:), optional :: qn_mask
integer, intent(out), dimension(:,:), allocatable :: flv
type(quantum_numbers_t), dimension(:,:), allocatable :: qn
integer :: i_flv, n_partons
type(flavor_t), dimension(:), allocatable :: flv_flv
if (present (qn_mask)) then
call state%get_quantum_numbers (qn_mask, qn)
else
call state%get_quantum_numbers (qn)
end if
allocate (flv_flv (size (qn, dim=2)))
if (only_elementary) then
flv_flv = qn(1, :)%get_flavor ()
n_partons = count (is_elementary (flv_flv%get_pdg ()))
end if
allocate (flv (n_partons, size (qn, dim=1)))
associate (n_flv => size (qn, dim=1))
do i_flv = 1, size (qn, dim=1)
flv_flv = qn(i_flv, :)%get_flavor ()
flv(:, i_flv) = pack (flv_flv%get_pdg (), is_elementary(flv_flv%get_pdg()))
end do
end associate
contains
elemental function is_elementary (pdg)
logical :: is_elementary
integer, intent(in) :: pdg
is_elementary = abs(pdg) /= 2212 .and. abs(pdg) /= 92 .and. abs(pdg) /= 93
end function is_elementary
end subroutine state_matrix_get_flavors
@ %def state_matrix_get_flavors
@ Return a single matrix element using its index. Works only if the
shortcut array is allocated.
<<State matrices: state matrix: TBP>>=
generic :: get_matrix_element => get_matrix_element_single
generic :: get_matrix_element => get_matrix_element_array
procedure :: get_matrix_element_single => &
state_matrix_get_matrix_element_single
procedure :: get_matrix_element_array => &
state_matrix_get_matrix_element_array
<<State matrices: procedures>>=
elemental function state_matrix_get_matrix_element_single (state, i) result (me)
complex(default) :: me
class(state_matrix_t), intent(in) :: state
integer, intent(in) :: i
if (allocated (state%me)) then
me = state%me(i)
else
me = 0
end if
end function state_matrix_get_matrix_element_single
@ %def state_matrix_get_matrix_element_single
@
<<State matrices: procedures>>=
function state_matrix_get_matrix_element_array (state) result (me)
complex(default), dimension(:), allocatable :: me
class(state_matrix_t), intent(in) :: state
if (allocated (state%me)) then
allocate (me (size (state%me)))
me = state%me
else
me = 0
end if
end function state_matrix_get_matrix_element_array
@ %def state_matrix_get_matrix_element_array
@ Return the color index with maximum absolute value that is present within
the state matrix.
<<State matrices: state matrix: TBP>>=
procedure :: get_max_color_value => state_matrix_get_max_color_value
<<State matrices: procedures>>=
function state_matrix_get_max_color_value (state) result (cmax)
integer :: cmax
class(state_matrix_t), intent(in) :: state
if (associated (state%root)) then
cmax = node_get_max_color_value (state%root)
else
cmax = 0
end if
contains
recursive function node_get_max_color_value (node) result (cmax)
integer :: cmax
type(node_t), intent(in), target :: node
type(node_t), pointer :: current
cmax = quantum_numbers_get_max_color_value (node%qn)
current => node%child_first
do while (associated (current))
cmax = max (cmax, node_get_max_color_value (current))
current => current%next
end do
end function node_get_max_color_value
end function state_matrix_get_max_color_value
@ %def state_matrix_get_max_color_value
@
\subsubsection{Building the quantum state}
The procedure generates a branch associated to the input array of
quantum numbers. If the branch exists already, it is used.
Optionally, we set the matrix-element index, a value (which may be
added to the previous one), and increment one of the possible
counters. We may also return the matrix element index of the current
node.
<<State matrices: state matrix: TBP>>=
procedure :: add_state => state_matrix_add_state
<<State matrices: procedures>>=
subroutine state_matrix_add_state (state, qn, index, value, &
- sum_values, counter_index, ignore_sub, me_index)
+ sum_values, counter_index, ignore_sub_for_qn, me_index)
class(state_matrix_t), intent(inout) :: state
type(quantum_numbers_t), dimension(:), intent(in) :: qn
integer, intent(in), optional :: index
complex(default), intent(in), optional :: value
logical, intent(in), optional :: sum_values
integer, intent(in), optional :: counter_index
- logical, intent(in), optional :: ignore_sub
+ logical, intent(in), optional :: ignore_sub_for_qn
integer, intent(out), optional :: me_index
logical :: set_index, get_index, add
set_index = present (index)
get_index = present (me_index)
add = .false.; if (present (sum_values)) add = sum_values
if (state%depth == 0) then
state%depth = size (qn)
else if (state%depth /= size (qn)) then
call state%write ()
call msg_bug ("State matrix: depth mismatch")
end if
if (size (qn) > 0) call node_make_branch (state%root, qn)
contains
recursive subroutine node_make_branch (parent, qn)
type(node_t), pointer :: parent
type(quantum_numbers_t), dimension(:), intent(in) :: qn
type(node_t), pointer :: child
logical :: match
match = .false.
child => parent%child_first
SCAN_CHILDREN: do while (associated (child))
- if (present (ignore_sub)) then
- if (ignore_sub) then
+ if (present (ignore_sub_for_qn)) then
+ if (ignore_sub_for_qn) then
match = quantum_numbers_eq_wo_sub (child%qn, qn(1))
else
match = child%qn == qn(1)
end if
else
match = child%qn == qn(1)
end if
if (match) exit SCAN_CHILDREN
child => child%next
end do SCAN_CHILDREN
if (.not. match) then
call node_append_child (parent, child)
child%qn = qn(1)
end if
select case (size (qn))
case (1)
if (.not. match) then
state%n_matrix_elements = state%n_matrix_elements + 1
child%me_index = state%n_matrix_elements
end if
if (set_index) then
child%me_index = index
end if
if (get_index) then
me_index = child%me_index
end if
if (present (counter_index)) then
if (.not. allocated (child%me_count)) then
allocate (child%me_count (state%n_counters))
child%me_count = 0
end if
child%me_count(counter_index) = child%me_count(counter_index) + 1
end if
if (present (value)) then
if (add) then
child%me = child%me + value
else
child%me = value
end if
end if
case (2:)
call node_make_branch (child, qn(2:))
end select
end subroutine node_make_branch
end subroutine state_matrix_add_state
@ %def state_matrix_add_state
@ Remove irrelevant flavor/color/helicity labels and the corresponding
branchings. The masks indicate which particles are affected; the
masks length should coincide with the depth of the trie (without the
root node). Recursively scan the whole tree, starting from the leaf
nodes and working up to the root node. If a mask entry is set for the
current tree level, scan the children there. For each child within
that level make a new empty branch where the masked quantum number is
undefined. Then recursively combine all following children with
matching quantum number into this new node and move on.
<<State matrices: state matrix: TBP>>=
procedure :: collapse => state_matrix_collapse
<<State matrices: procedures>>=
subroutine state_matrix_collapse (state, mask)
class(state_matrix_t), intent(inout) :: state
type(quantum_numbers_mask_t), dimension(:), intent(in) :: mask
type(state_matrix_t) :: red_state
if (state%is_defined ()) then
call state%reduce (mask, red_state)
call state%final ()
state = red_state
end if
end subroutine state_matrix_collapse
@ %def state_matrix_collapse
@ Transform the given state matrix into a reduced state matrix where
some quantum numbers are removed, as indicated by the mask. The
procedure creates a new state matrix, so the old one can be deleted
after this if it is no longer used.
It is said that the matrix element ordering is lost afterwards. We allow to keep
the original matrix element index in the new state matrix. If the matrix
element indices are kept, we do not freeze the state matrix. After reordering
the matrix element indices by [[state_matrix_reorder_me]], the state matrix can
-be frozen.
+be frozen.
<<State matrices: state matrix: TBP>>=
procedure :: reduce => state_matrix_reduce
<<State matrices: procedures>>=
subroutine state_matrix_reduce (state, mask, red_state, keep_me_index)
class(state_matrix_t), intent(in), target :: state
type(quantum_numbers_mask_t), dimension(:), intent(in) :: mask
type(state_matrix_t), intent(out) :: red_state
logical, optional, intent(in) :: keep_me_index
logical :: opt_keep_me_index
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(size(mask)) :: qn
opt_keep_me_index = .false.
if (present (keep_me_index)) opt_keep_me_index = keep_me_index
call red_state%init ()
call it%init (state)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
call qn%undefine (mask)
if (opt_keep_me_index) then
call red_state%add_state (qn, index = it%get_me_index ())
else
call red_state%add_state (qn)
end if
call it%advance ()
end do
if (.not. opt_keep_me_index) then
call red_state%freeze ()
end if
end subroutine state_matrix_reduce
@ %def state_matrix_reduce
@ Reorder the matrix elements -- not the tree itself. The procedure is necessary
in case the matrix element indices were kept when reducing over quantum numbers
and one wants to reintroduce the previous order of the matrix elements.
<<State matrices: state matrix: TBP>>=
procedure :: reorder_me => state_matrix_reorder_me
<<State matrices: procedures>>=
subroutine state_matrix_reorder_me (state, ordered_state)
class(state_matrix_t), intent(in), target :: state
type(state_matrix_t), intent(out) :: ordered_state
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(state%depth) :: qn
integer, dimension(:), allocatable :: me_index
integer :: i
call ordered_state%init ()
call get_me_index_sorted (state, me_index)
i = 1; call it%init (state)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
call ordered_state%add_state (qn, index = me_index(i))
i = i + 1; call it%advance ()
end do
call ordered_state%freeze ()
contains
subroutine get_me_index_sorted (state, me_index)
class(state_matrix_t), intent(in), target :: state
integer, dimension(:), allocatable, intent(out) :: me_index
type(state_iterator_t) :: it
integer :: i, j
integer, dimension(:), allocatable :: me_index_unsorted, me_index_sorted
associate (n_matrix_elements => state%get_n_matrix_elements ())
allocate (me_index(n_matrix_elements), source = 0)
allocate (me_index_sorted(n_matrix_elements), source = 0)
allocate (me_index_unsorted(n_matrix_elements), source = 0)
i = 1; call it%init (state)
do while (it%is_valid ())
me_index_unsorted(i) = it%get_me_index ()
i = i + 1
call it%advance ()
end do
me_index_sorted = sort (me_index_unsorted)
! We do not care about efficiency at this point.
UNSORTED: do i = 1, n_matrix_elements
SORTED: do j = 1, n_matrix_elements
if (me_index_unsorted(i) == me_index_sorted(j)) then
me_index(i) = j
cycle UNSORTED
end if
end do SORTED
end do UNSORTED
end associate
end subroutine get_me_index_sorted
end subroutine state_matrix_reorder_me
@ %def state_matrix_order_by_flavors
@ This subroutine sets up the matrix-element array. The leaf nodes
aquire the index values that point to the appropriate matrix-element
entry.
We recursively scan the trie. Once we arrive at a leaf node, the
index is increased and associated to that node. Finally, we allocate
the matrix-element array with the appropriate size.
If matrix element values are temporarily stored within the leaf nodes,
we scan the state again and transfer them to the matrix-element array.
<<State matrices: state matrix: TBP>>=
procedure :: freeze => state_matrix_freeze
<<State matrices: procedures>>=
subroutine state_matrix_freeze (state)
class(state_matrix_t), intent(inout), target :: state
type(state_iterator_t) :: it
if (associated (state%root)) then
if (allocated (state%me)) deallocate (state%me)
allocate (state%me (state%n_matrix_elements))
state%me = 0
call state%set_n_sub ()
end if
if (state%leaf_nodes_store_values) then
call it%init (state)
do while (it%is_valid ())
state%me(it%get_me_index ()) = it%get_matrix_element ()
call it%advance ()
end do
state%leaf_nodes_store_values = .false.
end if
end subroutine state_matrix_freeze
@ %def state_matrix_freeze
@
\subsubsection{Direct access to the value array}
Several methods for setting a value directly are summarized in this
generic:
<<State matrices: state matrix: TBP>>=
generic :: set_matrix_element => set_matrix_element_qn
generic :: set_matrix_element => set_matrix_element_all
generic :: set_matrix_element => set_matrix_element_array
generic :: set_matrix_element => set_matrix_element_single
generic :: set_matrix_element => set_matrix_element_clone
procedure :: set_matrix_element_qn => state_matrix_set_matrix_element_qn
procedure :: set_matrix_element_all => state_matrix_set_matrix_element_all
procedure :: set_matrix_element_array => &
state_matrix_set_matrix_element_array
procedure :: set_matrix_element_single => &
state_matrix_set_matrix_element_single
procedure :: set_matrix_element_clone => &
state_matrix_set_matrix_element_clone
@ %def state_matrix_set_matrix_element
@ Set a value that corresponds to a quantum number array:
<<State matrices: procedures>>=
subroutine state_matrix_set_matrix_element_qn (state, qn, value)
class(state_matrix_t), intent(inout), target :: state
type(quantum_numbers_t), dimension(:), intent(in) :: qn
complex(default), intent(in) :: value
type(state_iterator_t) :: it
if (.not. allocated (state%me)) then
allocate (state%me (size(qn)))
end if
call it%init (state)
call it%go_to_qn (qn)
call it%set_matrix_element (value)
end subroutine state_matrix_set_matrix_element_qn
@ %def state_matrix_set_matrix_element_qn
@ Set all matrix elements to a single value
<<State matrices: procedures>>=
subroutine state_matrix_set_matrix_element_all (state, value)
class(state_matrix_t), intent(inout) :: state
complex(default), intent(in) :: value
if (.not. allocated (state%me)) then
allocate (state%me (state%n_matrix_elements))
end if
state%me = value
end subroutine state_matrix_set_matrix_element_all
@ %def state_matrix_set_matrix_element_all
@ Set the matrix-element array directly.
<<State matrices: procedures>>=
subroutine state_matrix_set_matrix_element_array (state, value, range)
class(state_matrix_t), intent(inout) :: state
complex(default), intent(in), dimension(:) :: value
integer, intent(in), dimension(:), optional :: range
- integer :: i, n_me, n_val, i_first, i_last
if (present (range)) then
state%me(range) = value
else
if (.not. allocated (state%me)) &
allocate (state%me (size (value)))
state%me(:) = value
end if
end subroutine state_matrix_set_matrix_element_array
+@ %def state_matrix_set_matrix_element_array
+@ Set a matrix element at position [[i]] to [[value]].
+<<State matrices: procedures>>=
pure subroutine state_matrix_set_matrix_element_single (state, i, value)
class(state_matrix_t), intent(inout) :: state
integer, intent(in) :: i
complex(default), intent(in) :: value
if (.not. allocated (state%me)) then
allocate (state%me (state%n_matrix_elements))
end if
state%me(i) = value
end subroutine state_matrix_set_matrix_element_single
-@ %def state_matrix_set_matrix_element_array
@ %def state_matrix_set_matrix_element_single
@ Clone the matrix elements from another (matching) state matrix.
<<State matrices: procedures>>=
subroutine state_matrix_set_matrix_element_clone (state, state1)
class(state_matrix_t), intent(inout) :: state
type(state_matrix_t), intent(in) :: state1
if (.not. allocated (state1%me)) return
if (.not. allocated (state%me)) allocate (state%me (size (state1%me)))
state%me = state1%me
end subroutine state_matrix_set_matrix_element_clone
@ %def state_matrix_set_matrix_element_clone
@ Add a value to a matrix element
<<State matrices: state matrix: TBP>>=
procedure :: add_to_matrix_element => state_matrix_add_to_matrix_element
<<State matrices: procedures>>=
subroutine state_matrix_add_to_matrix_element (state, qn, value, match_only_flavor)
class(state_matrix_t), intent(inout), target :: state
type(quantum_numbers_t), dimension(:), intent(in) :: qn
complex(default), intent(in) :: value
logical, intent(in), optional :: match_only_flavor
type(state_iterator_t) :: it
call it%init (state)
call it%go_to_qn (qn, match_only_flavor)
if (it%is_valid ()) then
call it%add_to_matrix_element (value)
else
call msg_fatal ("Cannot add to matrix element - it%node not allocated")
end if
end subroutine state_matrix_add_to_matrix_element
@ %def state_matrix_add_to_matrix_element
@
\subsection{State iterators}
Accessing the quantum state from outside is best done using a
specialized iterator, i.e., a pointer to a particular branch of the
quantum state trie. Technically, the iterator contains a pointer to a
leaf node, but via parent pointers it allows to access the whole
branch where the leaf is attached. For quick access, we also keep the
branch depth (which is assumed to be universal for a quantum state).
<<State matrices: public>>=
public :: state_iterator_t
<<State matrices: types>>=
type :: state_iterator_t
private
integer :: depth = 0
type(state_matrix_t), pointer :: state => null ()
type(node_t), pointer :: node => null ()
contains
<<State matrices: state iterator: TBP>>
end type state_iterator_t
@ %def state_iterator
@ The initializer: Point at the first branch. Note that this cannot
be pure, thus not be elemental, because the iterator can be used to
manipulate data in the state matrix.
<<State matrices: state iterator: TBP>>=
procedure :: init => state_iterator_init
<<State matrices: procedures>>=
subroutine state_iterator_init (it, state)
class(state_iterator_t), intent(out) :: it
type(state_matrix_t), intent(in), target :: state
it%state => state
it%depth = state%depth
if (state%is_defined ()) then
it%node => state%root
do while (associated (it%node%child_first))
it%node => it%node%child_first
end do
else
it%node => null ()
end if
end subroutine state_iterator_init
@ %def state_iterator_init
@ Go forward. Recursively programmed: if the next node does not
exist, go back to the parent node and look at its successor (if
present), etc.
There is a possible pitfall in the implementation: If the dummy
pointer argument to the [[find_next]] routine is used directly, we
still get the correct result for the iterator, but calling the
recursion on [[node%parent]] means that we manipulate a parent pointer
in the original state in addition to the iterator. Making a local
copy of the pointer avoids this. Using pointer intent would be
helpful, but we do not yet rely on this F2003 feature.
<<State matrices: state iterator: TBP>>=
procedure :: advance => state_iterator_advance
<<State matrices: procedures>>=
subroutine state_iterator_advance (it)
class(state_iterator_t), intent(inout) :: it
call find_next (it%node)
contains
recursive subroutine find_next (node_in)
type(node_t), intent(in), target :: node_in
type(node_t), pointer :: node
node => node_in
if (associated (node%next)) then
node => node%next
do while (associated (node%child_first))
node => node%child_first
end do
it%node => node
else if (associated (node%parent)) then
call find_next (node%parent)
else
it%node => null ()
end if
end subroutine find_next
end subroutine state_iterator_advance
@ %def state_iterator_advance
@ If all has been scanned, the iterator is at an undefined state.
Check for this:
<<State matrices: state iterator: TBP>>=
procedure :: is_valid => state_iterator_is_valid
<<State matrices: procedures>>=
function state_iterator_is_valid (it) result (defined)
logical :: defined
class(state_iterator_t), intent(in) :: it
defined = associated (it%node)
end function state_iterator_is_valid
@ %def state_iterator_is_valid
@ Return the matrix-element index that corresponds to the current node
<<State matrices: state iterator: TBP>>=
procedure :: get_me_index => state_iterator_get_me_index
<<State matrices: procedures>>=
function state_iterator_get_me_index (it) result (n)
integer :: n
class(state_iterator_t), intent(in) :: it
n = it%node%me_index
end function state_iterator_get_me_index
@ %def state_iterator_get_me_index
@ Return the number of times this quantum-number state has been added
(noting that it is physically inserted only the first time). Note
that for each state, there is an array of counters.
<<State matrices: state iterator: TBP>>=
procedure :: get_me_count => state_iterator_get_me_count
<<State matrices: procedures>>=
function state_iterator_get_me_count (it) result (n)
integer, dimension(:), allocatable :: n
class(state_iterator_t), intent(in) :: it
if (allocated (it%node%me_count)) then
allocate (n (size (it%node%me_count)))
n = it%node%me_count
else
allocate (n (0))
end if
end function state_iterator_get_me_count
@ %def state_iterator_get_me_count
@
<<State matrices: state iterator: TBP>>=
procedure :: get_depth => state_iterator_get_depth
<<State matrices: procedures>>=
pure function state_iterator_get_depth (state_iterator) result (depth)
integer :: depth
class(state_iterator_t), intent(in) :: state_iterator
depth = state_iterator%depth
end function state_iterator_get_depth
@ %def state_iterator_get_depth
@ Proceed to the state associated with the quantum numbers [[qn]].
<<State matrices: state iterator: TBP>>=
procedure :: go_to_qn => state_iterator_go_to_qn
<<State matrices: procedures>>=
subroutine state_iterator_go_to_qn (it, qn, match_only_flavor)
class(state_iterator_t), intent(inout) :: it
type(quantum_numbers_t), dimension(:), intent(in) :: qn
logical, intent(in), optional :: match_only_flavor
logical :: match_flv
match_flv = .false.; if (present (match_only_flavor)) match_flv = .true.
do while (it%is_valid ())
if (match_flv) then
if (all (qn .fmatch. it%get_quantum_numbers ())) then
return
else
call it%advance ()
end if
else
if (all (qn == it%get_quantum_numbers ())) then
return
else
call it%advance ()
end if
end if
end do
end subroutine state_iterator_go_to_qn
@ %def state_iterator_go_to_qn
@ Use the iterator to retrieve quantum-number information:
<<State matrices: state iterator: TBP>>=
generic :: get_quantum_numbers => get_qn_multi, get_qn_slice, &
get_qn_range, get_qn_single
generic :: get_flavor => get_flv_multi, get_flv_slice, &
get_flv_range, get_flv_single
generic :: get_color => get_col_multi, get_col_slice, &
get_col_range, get_col_single
generic :: get_helicity => get_hel_multi, get_hel_slice, &
get_hel_range, get_hel_single
<<State matrices: state iterator: TBP>>=
procedure :: get_qn_multi => state_iterator_get_qn_multi
procedure :: get_qn_slice => state_iterator_get_qn_slice
procedure :: get_qn_range => state_iterator_get_qn_range
procedure :: get_qn_single => state_iterator_get_qn_single
procedure :: get_flv_multi => state_iterator_get_flv_multi
procedure :: get_flv_slice => state_iterator_get_flv_slice
procedure :: get_flv_range => state_iterator_get_flv_range
procedure :: get_flv_single => state_iterator_get_flv_single
procedure :: get_col_multi => state_iterator_get_col_multi
procedure :: get_col_slice => state_iterator_get_col_slice
procedure :: get_col_range => state_iterator_get_col_range
procedure :: get_col_single => state_iterator_get_col_single
procedure :: get_hel_multi => state_iterator_get_hel_multi
procedure :: get_hel_slice => state_iterator_get_hel_slice
procedure :: get_hel_range => state_iterator_get_hel_range
procedure :: get_hel_single => state_iterator_get_hel_single
@ These versions return the whole quantum number array
<<State matrices: procedures>>=
function state_iterator_get_qn_multi (it) result (qn)
class(state_iterator_t), intent(in) :: it
type(quantum_numbers_t), dimension(it%depth) :: qn
type(node_t), pointer :: node
integer :: i
node => it%node
do i = it%depth, 1, -1
qn(i) = node%qn
node => node%parent
end do
end function state_iterator_get_qn_multi
function state_iterator_get_flv_multi (it) result (flv)
class(state_iterator_t), intent(in) :: it
type(flavor_t), dimension(it%depth) :: flv
flv = quantum_numbers_get_flavor &
(it%get_quantum_numbers ())
end function state_iterator_get_flv_multi
function state_iterator_get_col_multi (it) result (col)
class(state_iterator_t), intent(in) :: it
type(color_t), dimension(it%depth) :: col
col = quantum_numbers_get_color &
(it%get_quantum_numbers ())
end function state_iterator_get_col_multi
function state_iterator_get_hel_multi (it) result (hel)
class(state_iterator_t), intent(in) :: it
type(helicity_t), dimension(it%depth) :: hel
hel = quantum_numbers_get_helicity &
(it%get_quantum_numbers ())
end function state_iterator_get_hel_multi
@ An array slice (derived from the above).
<<State matrices: procedures>>=
function state_iterator_get_qn_slice (it, index) result (qn)
class(state_iterator_t), intent(in) :: it
integer, dimension(:), intent(in) :: index
type(quantum_numbers_t), dimension(size(index)) :: qn
type(quantum_numbers_t), dimension(it%depth) :: qn_tmp
qn_tmp = state_iterator_get_qn_multi (it)
qn = qn_tmp(index)
end function state_iterator_get_qn_slice
function state_iterator_get_flv_slice (it, index) result (flv)
class(state_iterator_t), intent(in) :: it
integer, dimension(:), intent(in) :: index
type(flavor_t), dimension(size(index)) :: flv
flv = quantum_numbers_get_flavor &
(it%get_quantum_numbers (index))
end function state_iterator_get_flv_slice
function state_iterator_get_col_slice (it, index) result (col)
class(state_iterator_t), intent(in) :: it
integer, dimension(:), intent(in) :: index
type(color_t), dimension(size(index)) :: col
col = quantum_numbers_get_color &
(it%get_quantum_numbers (index))
end function state_iterator_get_col_slice
function state_iterator_get_hel_slice (it, index) result (hel)
class(state_iterator_t), intent(in) :: it
integer, dimension(:), intent(in) :: index
type(helicity_t), dimension(size(index)) :: hel
hel = quantum_numbers_get_helicity &
(it%get_quantum_numbers (index))
end function state_iterator_get_hel_slice
@ An array range (implemented directly).
<<State matrices: procedures>>=
function state_iterator_get_qn_range (it, k1, k2) result (qn)
class(state_iterator_t), intent(in) :: it
integer, intent(in) :: k1, k2
type(quantum_numbers_t), dimension(k2-k1+1) :: qn
type(node_t), pointer :: node
integer :: i
node => it%node
SCAN: do i = it%depth, 1, -1
if (k1 <= i .and. i <= k2) then
qn(i-k1+1) = node%qn
else
node => node%parent
end if
end do SCAN
end function state_iterator_get_qn_range
function state_iterator_get_flv_range (it, k1, k2) result (flv)
class(state_iterator_t), intent(in) :: it
integer, intent(in) :: k1, k2
type(flavor_t), dimension(k2-k1+1) :: flv
flv = quantum_numbers_get_flavor &
(it%get_quantum_numbers (k1, k2))
end function state_iterator_get_flv_range
function state_iterator_get_col_range (it, k1, k2) result (col)
class(state_iterator_t), intent(in) :: it
integer, intent(in) :: k1, k2
type(color_t), dimension(k2-k1+1) :: col
col = quantum_numbers_get_color &
(it%get_quantum_numbers (k1, k2))
end function state_iterator_get_col_range
function state_iterator_get_hel_range (it, k1, k2) result (hel)
class(state_iterator_t), intent(in) :: it
integer, intent(in) :: k1, k2
type(helicity_t), dimension(k2-k1+1) :: hel
hel = quantum_numbers_get_helicity &
(it%get_quantum_numbers (k1, k2))
end function state_iterator_get_hel_range
@ Just a specific single element
<<State matrices: procedures>>=
function state_iterator_get_qn_single (it, k) result (qn)
class(state_iterator_t), intent(in) :: it
integer, intent(in) :: k
type(quantum_numbers_t) :: qn
type(node_t), pointer :: node
integer :: i
node => it%node
SCAN: do i = it%depth, 1, -1
if (i == k) then
qn = node%qn
exit SCAN
else
node => node%parent
end if
end do SCAN
end function state_iterator_get_qn_single
function state_iterator_get_flv_single (it, k) result (flv)
class(state_iterator_t), intent(in) :: it
integer, intent(in) :: k
type(flavor_t) :: flv
flv = quantum_numbers_get_flavor &
(it%get_quantum_numbers (k))
end function state_iterator_get_flv_single
function state_iterator_get_col_single (it, k) result (col)
class(state_iterator_t), intent(in) :: it
integer, intent(in) :: k
type(color_t) :: col
col = quantum_numbers_get_color &
(it%get_quantum_numbers (k))
end function state_iterator_get_col_single
function state_iterator_get_hel_single (it, k) result (hel)
class(state_iterator_t), intent(in) :: it
integer, intent(in) :: k
type(helicity_t) :: hel
hel = quantum_numbers_get_helicity &
(it%get_quantum_numbers (k))
end function state_iterator_get_hel_single
@ %def state_iterator_get_quantum_numbers
@ %def state_iterator_get_flavor
@ %def state_iterator_get_color
@ %def state_iterator_get_helicity
@ Assign a model pointer to the current flavor entries.
<<State matrices: state iterator: TBP>>=
procedure :: set_model => state_iterator_set_model
<<State matrices: procedures>>=
subroutine state_iterator_set_model (it, model)
class(state_iterator_t), intent(inout) :: it
class(model_data_t), intent(in), target :: model
type(node_t), pointer :: node
integer :: i
node => it%node
do i = it%depth, 1, -1
call node%qn%set_model (model)
node => node%parent
end do
end subroutine state_iterator_set_model
@ %def state_iterator_set_model
@ Retrieve the matrix element value associated with the current node.
<<State matrices: state iterator: TBP>>=
procedure :: get_matrix_element => state_iterator_get_matrix_element
<<State matrices: procedures>>=
function state_iterator_get_matrix_element (it) result (me)
complex(default) :: me
class(state_iterator_t), intent(in) :: it
if (it%state%leaf_nodes_store_values) then
me = it%node%me
else if (it%node%me_index /= 0) then
me = it%state%me(it%node%me_index)
else
me = 0
end if
end function state_iterator_get_matrix_element
@ %def state_iterator_get_matrix_element
@ Set the matrix element value using the state iterator.
<<State matrices: state iterator: TBP>>=
procedure :: set_matrix_element => state_iterator_set_matrix_element
<<State matrices: procedures>>=
subroutine state_iterator_set_matrix_element (it, value)
class(state_iterator_t), intent(inout) :: it
complex(default), intent(in) :: value
if (it%node%me_index /= 0) it%state%me(it%node%me_index) = value
end subroutine state_iterator_set_matrix_element
@ %def state_iterator_set_matrix_element
@
<<State matrices: state iterator: TBP>>=
procedure :: add_to_matrix_element => state_iterator_add_to_matrix_element
<<State matrices: procedures>>=
subroutine state_iterator_add_to_matrix_element (it, value)
class(state_iterator_t), intent(inout) :: it
complex(default), intent(in) :: value
if (it%node%me_index /= 0) &
it%state%me(it%node%me_index) = it%state%me(it%node%me_index) + value
end subroutine state_iterator_add_to_matrix_element
@ %def state_iterator_add_to_matrix_element
@
\subsection{Operations on quantum states}
Return a deep copy of a state matrix.
<<State matrices: public>>=
public :: assignment(=)
<<State matrices: interfaces>>=
interface assignment(=)
module procedure state_matrix_assign
end interface
<<State matrices: procedures>>=
subroutine state_matrix_assign (state_out, state_in)
type(state_matrix_t), intent(out) :: state_out
type(state_matrix_t), intent(in), target :: state_in
type(state_iterator_t) :: it
if (.not. state_in%is_defined ()) return
call state_out%init ()
call it%init (state_in)
do while (it%is_valid ())
call state_out%add_state (it%get_quantum_numbers (), &
it%get_me_index ())
call it%advance ()
end do
if (allocated (state_in%me)) then
allocate (state_out%me (size (state_in%me)))
state_out%me = state_in%me
end if
state_out%n_sub = state_in%n_sub
end subroutine state_matrix_assign
@ %def state_matrix_assign
@ Determine the indices of all diagonal matrix elements.
<<State matrices: state matrix: TBP>>=
procedure :: get_diagonal_entries => state_matrix_get_diagonal_entries
<<State matrices: procedures>>=
subroutine state_matrix_get_diagonal_entries (state, i)
class(state_matrix_t), intent(in) :: state
integer, dimension(:), allocatable, intent(out) :: i
integer, dimension(state%n_matrix_elements) :: tmp
integer :: n
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(:), allocatable :: qn
n = 0
call it%init (state)
allocate (qn (it%depth))
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
if (all (qn%are_diagonal ())) then
n = n + 1
tmp(n) = it%get_me_index ()
end if
call it%advance ()
end do
allocate (i(n))
if (n > 0) i = tmp(:n)
end subroutine state_matrix_get_diagonal_entries
@ %def state_matrices_get_diagonal_entries
@ Normalize all matrix elements, i.e., multiply by a common factor.
Assuming that the factor is nonzero, of course.
<<State matrices: state matrix: TBP>>=
procedure :: renormalize => state_matrix_renormalize
<<State matrices: procedures>>=
subroutine state_matrix_renormalize (state, factor)
class(state_matrix_t), intent(inout) :: state
complex(default), intent(in) :: factor
state%me = state%me * factor
end subroutine state_matrix_renormalize
@ %def state_matrix_renormalize
@ Renormalize the state matrix by its trace, if nonzero. The renormalization
is reflected in the state-matrix norm.
<<State matrices: state matrix: TBP>>=
procedure :: normalize_by_trace => state_matrix_normalize_by_trace
<<State matrices: procedures>>=
subroutine state_matrix_normalize_by_trace (state)
class(state_matrix_t), intent(inout) :: state
real(default) :: trace
trace = state%trace ()
if (trace /= 0) then
state%me = state%me / trace
state%norm = state%norm * trace
end if
end subroutine state_matrix_normalize_by_trace
@ %def state_matrix_renormalize_by_trace
@ Analogous, but renormalize by maximal (absolute) value.
<<State matrices: state matrix: TBP>>=
procedure :: normalize_by_max => state_matrix_normalize_by_max
<<State matrices: procedures>>=
subroutine state_matrix_normalize_by_max (state)
class(state_matrix_t), intent(inout) :: state
real(default) :: m
m = maxval (abs (state%me))
if (m /= 0) then
state%me = state%me / m
state%norm = state%norm * m
end if
end subroutine state_matrix_normalize_by_max
@ %def state_matrix_renormalize_by_max
@ Explicitly set the norm of a state matrix.
<<State matrices: state matrix: TBP>>=
procedure :: set_norm => state_matrix_set_norm
<<State matrices: procedures>>=
subroutine state_matrix_set_norm (state, norm)
class(state_matrix_t), intent(inout) :: state
real(default), intent(in) :: norm
state%norm = norm
end subroutine state_matrix_set_norm
@ %def state_matrix_set_norm
@ Return the sum of all matrix element values.
<<State matrices: state matrix: TBP>>=
procedure :: sum => state_matrix_sum
<<State matrices: procedures>>=
pure function state_matrix_sum (state) result (value)
complex(default) :: value
class(state_matrix_t), intent(in) :: state
value = sum (state%me)
end function state_matrix_sum
@ %def state_matrix_sum
@ Return the trace of a state matrix, i.e., the sum over all diagonal
-values.
+values.
If [[qn_in]] is provided, only branches that match this
quantum-numbers array in flavor and helicity are considered. (This mode is
used for selecting a color state.)
<<State matrices: state matrix: TBP>>=
procedure :: trace => state_matrix_trace
<<State matrices: procedures>>=
function state_matrix_trace (state, qn_in) result (trace)
complex(default) :: trace
class(state_matrix_t), intent(in), target :: state
type(quantum_numbers_t), dimension(:), intent(in), optional :: qn_in
type(quantum_numbers_t), dimension(:), allocatable :: qn
type(state_iterator_t) :: it
allocate (qn (state%get_depth ()))
trace = 0
call it%init (state)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
if (present (qn_in)) then
if (.not. all (qn .fhmatch. qn_in)) then
call it%advance (); cycle
end if
end if
if (all (qn%are_diagonal ())) then
trace = trace + it%get_matrix_element ()
end if
call it%advance ()
end do
end function state_matrix_trace
@ %def state_matrix_trace
@ Append new states which are color-contracted versions of the
existing states. The matrix element index of each color contraction
coincides with the index of its origin, so no new matrix elements are
generated. After this operation, no [[freeze]] must be performed
anymore.
<<State matrices: state matrix: TBP>>=
procedure :: add_color_contractions => state_matrix_add_color_contractions
<<State matrices: procedures>>=
subroutine state_matrix_add_color_contractions (state)
class(state_matrix_t), intent(inout), target :: state
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(:,:), allocatable :: qn
type(quantum_numbers_t), dimension(:,:), allocatable :: qn_con
integer, dimension(:), allocatable :: me_index
integer :: depth, n_me, i, j
depth = state%get_depth ()
n_me = state%get_n_matrix_elements ()
allocate (qn (depth, n_me))
allocate (me_index (n_me))
i = 0
call it%init (state)
do while (it%is_valid ())
i = i + 1
qn(:,i) = it%get_quantum_numbers ()
me_index(i) = it%get_me_index ()
call it%advance ()
end do
do i = 1, n_me
call quantum_number_array_make_color_contractions (qn(:,i), qn_con)
do j = 1, size (qn_con, 2)
call state%add_state (qn_con(:,j), index = me_index(i))
end do
end do
end subroutine state_matrix_add_color_contractions
@ %def state_matrix_add_color_contractions
@ This procedure merges two state matrices of equal depth. For each
quantum number (flavor, color, helicity), we take the entry from the
first argument where defined, otherwise the second one. (If both are
defined, we get an off-diagonal matrix.) The resulting
trie combines the information of the input tries in all possible ways.
Note that values are ignored, all values in the result are zero.
<<State matrices: public>>=
public :: merge_state_matrices
<<State matrices: procedures>>=
subroutine merge_state_matrices (state1, state2, state3)
type(state_matrix_t), intent(in), target :: state1, state2
type(state_matrix_t), intent(out) :: state3
type(state_iterator_t) :: it1, it2
type(quantum_numbers_t), dimension(state1%depth) :: qn1, qn2
if (state1%depth /= state2%depth) then
call state1%write ()
call state2%write ()
call msg_bug ("State matrices merge impossible: incompatible depths")
end if
call state3%init ()
call it1%init (state1)
do while (it1%is_valid ())
qn1 = it1%get_quantum_numbers ()
call it2%init (state2)
do while (it2%is_valid ())
qn2 = it2%get_quantum_numbers ()
call state3%add_state (qn1 .merge. qn2)
call it2%advance ()
end do
call it1%advance ()
end do
call state3%freeze ()
end subroutine merge_state_matrices
@ %def merge_state_matrices
@ Multiply matrix elements from two state matrices. Choose the elements
as given by the integer index arrays, multiply them and store the sum
of products in the indicated matrix element. The suffixes mean:
c=conjugate first factor; f=include weighting factor.
Note that the [[dot_product]] intrinsic function conjugates its first
complex argument. This is intended for the [[c]] suffix case, but
must be reverted for the plain-product case.
We provide analogous subroutines for just summing over state matrix
entries. The [[evaluate_sum]] variant includes the state-matrix norm
in the evaluation, the [[evaluate_me_sum]] takes into account just the
matrix elements proper.
<<State matrices: state matrix: TBP>>=
procedure :: evaluate_product => state_matrix_evaluate_product
procedure :: evaluate_product_cf => state_matrix_evaluate_product_cf
procedure :: evaluate_square_c => state_matrix_evaluate_square_c
procedure :: evaluate_sum => state_matrix_evaluate_sum
procedure :: evaluate_me_sum => state_matrix_evaluate_me_sum
<<State matrices: procedures>>=
pure subroutine state_matrix_evaluate_product &
(state, i, state1, state2, index1, index2)
class(state_matrix_t), intent(inout) :: state
integer, intent(in) :: i
type(state_matrix_t), intent(in) :: state1, state2
integer, dimension(:), intent(in) :: index1, index2
state%me(i) = &
dot_product (conjg (state1%me(index1)), state2%me(index2))
state%norm = state1%norm * state2%norm
end subroutine state_matrix_evaluate_product
pure subroutine state_matrix_evaluate_product_cf &
(state, i, state1, state2, index1, index2, factor)
class(state_matrix_t), intent(inout) :: state
integer, intent(in) :: i
type(state_matrix_t), intent(in) :: state1, state2
integer, dimension(:), intent(in) :: index1, index2
complex(default), dimension(:), intent(in) :: factor
state%me(i) = &
dot_product (state1%me(index1), factor * state2%me(index2))
state%norm = state1%norm * state2%norm
end subroutine state_matrix_evaluate_product_cf
pure subroutine state_matrix_evaluate_square_c (state, i, state1, index1)
class(state_matrix_t), intent(inout) :: state
integer, intent(in) :: i
type(state_matrix_t), intent(in) :: state1
integer, dimension(:), intent(in) :: index1
state%me(i) = &
dot_product (state1%me(index1), state1%me(index1))
state%norm = abs (state1%norm) ** 2
end subroutine state_matrix_evaluate_square_c
pure subroutine state_matrix_evaluate_sum (state, i, state1, index1)
class(state_matrix_t), intent(inout) :: state
integer, intent(in) :: i
type(state_matrix_t), intent(in) :: state1
integer, dimension(:), intent(in) :: index1
state%me(i) = &
sum (state1%me(index1)) * state1%norm
end subroutine state_matrix_evaluate_sum
pure subroutine state_matrix_evaluate_me_sum (state, i, state1, index1)
class(state_matrix_t), intent(inout) :: state
integer, intent(in) :: i
type(state_matrix_t), intent(in) :: state1
integer, dimension(:), intent(in) :: index1
state%me(i) = sum (state1%me(index1))
end subroutine state_matrix_evaluate_me_sum
@ %def state_matrix_evaluate_product
@ %def state_matrix_evaluate_product_cf
@ %def state_matrix_evaluate_square_c
@ %def state_matrix_evaluate_sum
@ %def state_matrix_evaluate_me_sum
@ Outer product (of states and matrix elements):
<<State matrices: public>>=
public :: outer_multiply
<<State matrices: interfaces>>=
interface outer_multiply
module procedure outer_multiply_pair
module procedure outer_multiply_array
end interface
@ %def outer_multiply
@ This procedure constructs the outer product of two state matrices.
<<State matrices: procedures>>=
subroutine outer_multiply_pair (state1, state2, state3)
type(state_matrix_t), intent(in), target :: state1, state2
type(state_matrix_t), intent(out) :: state3
type(state_iterator_t) :: it1, it2
type(quantum_numbers_t), dimension(state1%depth) :: qn1
type(quantum_numbers_t), dimension(state2%depth) :: qn2
type(quantum_numbers_t), dimension(state1%depth+state2%depth) :: qn3
complex(default) :: val1, val2
call state3%init (store_values = .true.)
call it1%init (state1)
do while (it1%is_valid ())
qn1 = it1%get_quantum_numbers ()
val1 = it1%get_matrix_element ()
call it2%init (state2)
do while (it2%is_valid ())
qn2 = it2%get_quantum_numbers ()
val2 = it2%get_matrix_element ()
qn3(:state1%depth) = qn1
qn3(state1%depth+1:) = qn2
call state3%add_state (qn3, value=val1 * val2)
call it2%advance ()
end do
call it1%advance ()
end do
call state3%freeze ()
end subroutine outer_multiply_pair
@ %def outer_multiply_state_pair
@ This executes the above routine iteratively for an arbitrary number
of state matrices.
<<State matrices: procedures>>=
subroutine outer_multiply_array (state_in, state_out)
type(state_matrix_t), dimension(:), intent(in), target :: state_in
type(state_matrix_t), intent(out) :: state_out
type(state_matrix_t), dimension(:), allocatable, target :: state_tmp
integer :: i, n
n = size (state_in)
select case (n)
case (0)
call state_out%init ()
case (1)
state_out = state_in(1)
case (2)
call outer_multiply_pair (state_in(1), state_in(2), state_out)
case default
allocate (state_tmp (n-2))
call outer_multiply_pair (state_in(1), state_in(2), state_tmp(1))
do i = 2, n - 2
call outer_multiply_pair (state_tmp(i-1), state_in(i+1), state_tmp(i))
end do
call outer_multiply_pair (state_tmp(n-2), state_in(n), state_out)
do i = 1, size(state_tmp)
call state_tmp(i)%final ()
end do
end select
end subroutine outer_multiply_array
@ %def outer_multiply_pair
@ %def outer_multiply_array
@
\subsection{Factorization}
In physical events, the state matrix is factorized into
single-particle state matrices. This is essentially a measurement.
In a simulation, we select one particular branch of the state matrix
with a probability that is determined by the matrix elements at the
leaves. (This makes sense only if the state matrix represents a
squared amplitude.) The selection is based on a (random) value [[x]]
between 0 and one that is provided as the third argument.
For flavor and color, we select a unique value for each particle. For
polarization, we have three options (modes). Option 1 is to drop
helicity information altogether and sum over all diagonal helicities.
Option 2 is to select a unique diagonal helicity in the same way as
flavor and color. Option 3 is, for each particle, to trace over all
remaining helicities in order to obtain an array of independent
single-particle helicity matrices.
Only branches that match the given quantum-number array [[qn_in]], if
present, are considered. For this array, color is ignored.
If the optional [[correlated_state]] is provided, it is assigned the
correlated density matrix for the selected flavor-color branch, so
multi-particle spin correlations remain available even if they are
dropped in the single-particle density matrices. This should be
done by the caller for the choice [[FM_CORRELATED_HELICITY]], which otherwise
is handled as [[FM_IGNORE_HELICITY]].
The algorithm is as follows: First, we determine the normalization by
summing over all diagonal matrix elements. In a second scan, we
select one of the diagonal matrix elements by a cumulative comparison
with the normalized random number. In the corresponding quantum
number array, we undefine the helicity entries. Then, we scan the
third time. For each branch that matches the selected quantum number
array (i.e., definite flavor and color, arbitrary helicity), we
determine its contribution to any of the single-particle state
matrices. The matrix-element value is added if all other quantum
numbers are diagonal, while the helicity of the chosen particle may be
arbitrary; this helicity determines the branch in the single-particle
state.
As a result, flavor and color quantum numbers are selected with the
correct probability. Within this subset of states, each
single-particle state matrix results from tracing over all other
particles. Note that the single-particle state matrices are not
normalized.
The flag [[ok]] is set to false if the matrix element sum is zero, so
factorization is not possible. This can happen if an event did not pass
cuts.
<<State matrices: parameters>>=
integer, parameter, public :: FM_IGNORE_HELICITY = 1
integer, parameter, public :: FM_SELECT_HELICITY = 2
integer, parameter, public :: FM_FACTOR_HELICITY = 3
integer, parameter, public :: FM_CORRELATED_HELICITY = 4
@ %def FM_IGNORE_HELICITY FM_SELECT_HELICITY FM_FACTOR_HELICITY
@ %def FM_CORRELATED_HELICITY
<<State matrices: state matrix: TBP>>=
procedure :: factorize => state_matrix_factorize
<<State matrices: procedures>>=
subroutine state_matrix_factorize &
(state, mode, x, ok, single_state, correlated_state, qn_in)
class(state_matrix_t), intent(in), target :: state
integer, intent(in) :: mode
real(default), intent(in) :: x
logical, intent(out) :: ok
type(state_matrix_t), &
dimension(:), allocatable, intent(out) :: single_state
type(state_matrix_t), intent(out), optional :: correlated_state
type(quantum_numbers_t), dimension(:), intent(in), optional :: qn_in
type(state_iterator_t) :: it
real(default) :: s, xt
complex(default) :: value
integer :: i, depth
type(quantum_numbers_t), dimension(:), allocatable :: qn, qn1
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask
logical, dimension(:), allocatable :: diagonal
logical, dimension(:,:), allocatable :: mask
ok = .true.
if (x /= 0) then
xt = x * abs (state%trace (qn_in))
else
xt = 0
end if
s = 0
depth = state%get_depth ()
allocate (qn (depth), qn1 (depth), diagonal (depth))
call it%init (state)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
if (present (qn_in)) then
if (.not. all (qn .fhmatch. qn_in)) then
call it%advance (); cycle
end if
end if
if (all (qn%are_diagonal ())) then
value = abs (it%get_matrix_element ())
s = s + value
if (s > xt) exit
end if
call it%advance ()
end do
if (.not. it%is_valid ()) then
if (s == 0) ok = .false.
call it%init (state)
end if
allocate (single_state (depth))
do i = 1, depth
call single_state(i)%init (store_values = .true.)
end do
if (present (correlated_state)) &
call correlated_state%init (store_values = .true.)
qn = it%get_quantum_numbers ()
select case (mode)
case (FM_SELECT_HELICITY) ! single branch selected; shortcut
do i = 1, depth
call single_state(i)%add_state ([qn(i)], value=value)
end do
if (.not. present (correlated_state)) then
do i = 1, size(single_state)
call single_state(i)%freeze ()
end do
return
end if
end select
allocate (qn_mask (depth))
call qn_mask%init (.false., .false., .false., .true.)
call qn%undefine (qn_mask)
select case (mode)
case (FM_FACTOR_HELICITY)
allocate (mask (depth, depth))
mask = .false.
forall (i = 1:depth) mask(i,i) = .true.
end select
call it%init (state)
do while (it%is_valid ())
qn1 = it%get_quantum_numbers ()
if (all (qn .match. qn1)) then
diagonal = qn1%are_diagonal ()
value = it%get_matrix_element ()
select case (mode)
case (FM_IGNORE_HELICITY, FM_CORRELATED_HELICITY)
!!! trace over diagonal states that match qn
if (all (diagonal)) then
do i = 1, depth
call single_state(i)%add_state &
([qn(i)], value=value, sum_values=.true.)
end do
end if
case (FM_FACTOR_HELICITY) !!! trace over all other particles
do i = 1, depth
if (all (diagonal .or. mask(:,i))) then
call single_state(i)%add_state &
([qn1(i)], value=value, sum_values=.true.)
end if
end do
end select
if (present (correlated_state)) &
call correlated_state%add_state (qn1, value=value)
end if
call it%advance ()
end do
do i = 1, depth
call single_state(i)%freeze ()
end do
if (present (correlated_state)) &
call correlated_state%freeze ()
end subroutine state_matrix_factorize
@ %def state_matrix_factorize
-@
-\subsubsection{Auxiliary functions}
+@ \subsubsection{Auxiliary functions}
<<State matrices: state matrix: TBP>>=
procedure :: get_polarization_density_matrix &
=> state_matrix_get_polarization_density_matrix
<<State matrices: procedures>>=
function state_matrix_get_polarization_density_matrix (state) result (pol_matrix)
real(default), dimension(:,:), allocatable :: pol_matrix
class(state_matrix_t), intent(in) :: state
type(node_t), pointer :: current => null ()
!!! What's the generic way to allocate the matrix?
allocate (pol_matrix (4,4)); pol_matrix = 0
if (associated (state%root%child_first)) then
current => state%root%child_first
do while (associated (current))
call current%qn%write ()
current => current%next
end do
else
call msg_fatal ("Polarization state not allocated!")
end if
end function state_matrix_get_polarization_density_matrix
@ %def state_matrix_get_polarization_density_matrix
@
\subsubsection{Quantum-number matching}
This feature allows us to check whether a given string of PDG values
matches, in any ordering, any of the flavor combinations that the
state matrix provides. We will also request the permutation of the
successful match.
This type provides an account of the state's flavor content. We store
all flavor combinations, as [[pdg]] values, in an array, assuming that
the length is uniform.
We check only the entries selected by [[mask_match]]. Among those,
only the entries selected by [[mask_sort]] are sorted and thus matched
without respecting array element order. The entries that correspond to
a true value in the associated [[mask]] are sorted. The mapping from
the original state to the sorted state is given by the index array
[[map]].
<<State matrices: public>>=
public :: state_flv_content_t
<<State matrices: types>>=
type :: state_flv_content_t
private
integer, dimension(:,:), allocatable :: pdg
integer, dimension(:,:), allocatable :: map
logical, dimension(:), allocatable :: mask
contains
<<State matrices: state flv content: TBP>>
end type state_flv_content_t
@ %def state_matrix_flavor_content
@ Output (debugging aid).
<<State matrices: state flv content: TBP>>=
procedure :: write => state_flv_content_write
<<State matrices: procedures>>=
subroutine state_flv_content_write (state_flv, unit)
class(state_flv_content_t), intent(in), target :: state_flv
integer, intent(in), optional :: unit
integer :: u, n, d, i, j
u = given_output_unit (unit)
d = size (state_flv%pdg, 1)
n = size (state_flv%pdg, 2)
do i = 1, n
write (u, "(2x,'PDG =')", advance="no")
do j = 1, d
write (u, "(1x,I0)", advance="no") state_flv%pdg(j,i)
end do
write (u, "(' :: map = (')", advance="no")
do j = 1, d
write (u, "(1x,I0)", advance="no") state_flv%map(j,i)
end do
write (u, "(' )')")
end do
end subroutine state_flv_content_write
@ %def state_flv_content_write
@ Initialize with table length and mask. Each row of the [[map]]
array, of length $d$, is initialized with $(0,1,\ldots,d)$.
<<State matrices: state flv content: TBP>>=
procedure :: init => state_flv_content_init
<<State matrices: procedures>>=
subroutine state_flv_content_init (state_flv, n, mask)
class(state_flv_content_t), intent(out) :: state_flv
integer, intent(in) :: n
logical, dimension(:), intent(in) :: mask
integer :: d, i
d = size (mask)
allocate (state_flv%pdg (d, n), source = 0)
allocate (state_flv%map (d, n), source = spread ([(i, i = 1, d)], 2, n))
allocate (state_flv%mask (d), source = mask)
end subroutine state_flv_content_init
@ %def state_flv_content_init
@ Manually fill the entries, one flavor set and mapping at a time.
<<State matrices: state flv content: TBP>>=
procedure :: set_entry => state_flv_content_set_entry
<<State matrices: procedures>>=
subroutine state_flv_content_set_entry (state_flv, i, pdg, map)
class(state_flv_content_t), intent(inout) :: state_flv
integer, intent(in) :: i
integer, dimension(:), intent(in) :: pdg, map
state_flv%pdg(:,i) = pdg
where (map /= 0)
state_flv%map(:,i) = map
end where
end subroutine state_flv_content_set_entry
@ %def state_flv_content_set_entry
@ Given a state matrix, determine the flavor content. That is, scan
the state matrix and extract flavor only, build a new state matrix
from that.
<<State matrices: state flv content: TBP>>=
procedure :: fill => state_flv_content_fill
<<State matrices: procedures>>=
subroutine state_flv_content_fill &
(state_flv, state_full, mask)
class(state_flv_content_t), intent(out) :: state_flv
type(state_matrix_t), intent(in), target :: state_full
logical, dimension(:), intent(in) :: mask
type(state_matrix_t), target :: state_tmp
type(state_iterator_t) :: it
type(flavor_t), dimension(:), allocatable :: flv
integer, dimension(:), allocatable :: pdg, pdg_subset
integer, dimension(:), allocatable :: idx, map_subset, idx_subset, map
type(quantum_numbers_t), dimension(:), allocatable :: qn
integer :: n, d, c, i, j
call state_tmp%init ()
d = state_full%get_depth ()
allocate (flv (d), qn (d), pdg (d), idx (d), map (d))
idx = [(i, i = 1, d)]
c = count (mask)
allocate (pdg_subset (c), map_subset (c), idx_subset (c))
call it%init (state_full)
do while (it%is_valid ())
flv = it%get_flavor ()
call qn%init (flv)
call state_tmp%add_state (qn)
call it%advance ()
end do
n = state_tmp%get_n_leaves ()
call state_flv%init (n, mask)
i = 0
call it%init (state_tmp)
do while (it%is_valid ())
i = i + 1
flv = it%get_flavor ()
pdg = flv%get_pdg ()
idx_subset = pack (idx, mask)
pdg_subset = pack (pdg, mask)
map_subset = order_abs (pdg_subset)
map = unpack (idx_subset (map_subset), mask, idx)
call state_flv%set_entry (i, &
unpack (pdg_subset(map_subset), mask, pdg), &
order (map))
call it%advance ()
end do
call state_tmp%final ()
end subroutine state_flv_content_fill
@ %def state_flv_content_fill
@ Match a given flavor string against the flavor content. We sort the
input string and check whether it matches any of the stored strings.
If yes, return the mapping.
Only PDG entries under the preset mask are sorted before matching. The
other entries must match exactly (i.e., without reordering). A zero
entry matches anything. In any case, the length of the PDG string
must be equal to the length $d$ of the individual flavor-state entries.
<<State matrices: state flv content: TBP>>=
procedure :: match => state_flv_content_match
<<State matrices: procedures>>=
subroutine state_flv_content_match (state_flv, pdg, success, map)
class(state_flv_content_t), intent(in) :: state_flv
integer, dimension(:), intent(in) :: pdg
logical, intent(out) :: success
integer, dimension(:), intent(out) :: map
integer, dimension(:), allocatable :: pdg_subset, pdg_sorted, map1, map2
integer, dimension(:), allocatable :: idx, map_subset, idx_subset
integer :: i, n, c, d
c = count (state_flv%mask)
d = size (state_flv%pdg, 1)
n = size (state_flv%pdg, 2)
allocate (idx (d), source = [(i, i = 1, d)])
allocate (idx_subset (c), pdg_subset (c), map_subset (c))
allocate (pdg_sorted (d), map1 (d), map2 (d))
idx_subset = pack (idx, state_flv%mask)
pdg_subset = pack (pdg, state_flv%mask)
map_subset = order_abs (pdg_subset)
pdg_sorted = unpack (pdg_subset(map_subset), state_flv%mask, pdg)
success = .false.
do i = 1, n
if (all (pdg_sorted == state_flv%pdg(:,i) &
.or. pdg_sorted == 0)) then
success = .true.
exit
end if
end do
if (success) then
map1 = state_flv%map(:,i)
map2 = unpack (idx_subset(map_subset), state_flv%mask, idx)
map = map2(map1)
where (pdg == 0) map = 0
end if
end subroutine state_flv_content_match
@ %def state_flv_content_match
@
<<State matrices: procedures>>=
elemental function pacify_complex (c_in) result (c_pac)
complex(default), intent(in) :: c_in
complex(default) :: c_pac
c_pac = c_in
if (real(c_pac) == -real(c_pac)) then
c_pac = &
cmplx (0._default, aimag(c_pac), kind=default)
end if
if (aimag(c_pac) == -aimag(c_pac)) then
c_pac = &
cmplx (real(c_pac), 0._default, kind=default)
end if
end function pacify_complex
@ %def pacify_complex
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[state_matrices_ut.f90]]>>=
<<File header>>
module state_matrices_ut
use unit_tests
use state_matrices_uti
<<Standard module head>>
<<State matrices: public test>>
contains
<<State matrices: test driver>>
end module state_matrices_ut
@ %def state_matrices_ut
@
<<[[state_matrices_uti.f90]]>>=
<<File header>>
module state_matrices_uti
<<Use kinds>>
use io_units
use format_defs, only: FMT_19
use flavors
use colors
use helicities
use quantum_numbers
use state_matrices
<<Standard module head>>
<<State matrices: test declarations>>
contains
<<State matrices: tests>>
end module state_matrices_uti
@ %def state_matrices_ut
@ API: driver for the unit tests below.
<<State matrices: public test>>=
public :: state_matrix_test
<<State matrices: test driver>>=
subroutine state_matrix_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<State matrices: execute tests>>
end subroutine state_matrix_test
@ %def state_matrix_test
@ Create two quantum states of equal depth and merge them.
<<State matrices: execute tests>>=
call test (state_matrix_1, "state_matrix_1", &
"check merge of quantum states of equal depth", &
u, results)
<<State matrices: test declarations>>=
public :: state_matrix_1
<<State matrices: tests>>=
subroutine state_matrix_1 (u)
integer, intent(in) :: u
type(state_matrix_t) :: state1, state2, state3
type(flavor_t), dimension(3) :: flv
type(color_t), dimension(3) :: col
type(quantum_numbers_t), dimension(3) :: qn
write (u, "(A)") "* Test output: state_matrix_1"
write (u, "(A)") "* Purpose: create and merge two quantum states"
write (u, "(A)")
write (u, "(A)") "* Initialization"
write (u, "(A)")
write (u, "(A)") "* State matrix 1"
write (u, "(A)")
call state1%init ()
call flv%init ([1, 2, 11])
call qn%init (flv, helicity ([ 1, 1, 1]))
call state1%add_state (qn)
call qn%init (flv, helicity ([ 1, 1, 1], [-1, 1, -1]))
call state1%add_state (qn)
call state1%freeze ()
call state1%write (u)
write (u, "(A)")
write (u, "(A)") "* State matrix 2"
write (u, "(A)")
call state2%init ()
call col(1)%init ([501])
call col(2)%init ([-501])
call col(3)%init ([0])
call qn%init (col, helicity ([-1, -1, 0]))
call state2%add_state (qn)
call col(3)%init ([99])
call qn%init (col, helicity ([-1, -1, 0]))
call state2%add_state (qn)
call state2%freeze ()
call state2%write (u)
write (u, "(A)")
write (u, "(A)") "* Merge the state matrices"
write (u, "(A)")
call merge_state_matrices (state1, state2, state3)
call state3%write (u)
write (u, "(A)")
write (u, "(A)") "* Collapse the state matrix"
write (u, "(A)")
call state3%collapse (quantum_numbers_mask (.false., .false., &
[.true.,.false.,.false.]))
call state3%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
write (u, "(A)")
call state1%final ()
call state2%final ()
call state3%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: state_matrix_1"
write (u, "(A)")
end subroutine state_matrix_1
@ %def state_matrix_1
@ Create a correlated three-particle state matrix and factorize it.
<<State matrices: execute tests>>=
call test (state_matrix_2, "state_matrix_2", &
"check factorizing 3-particle state matrix", &
u, results)
<<State matrices: test declarations>>=
public :: state_matrix_2
<<State matrices: tests>>=
subroutine state_matrix_2 (u)
integer, intent(in) :: u
type(state_matrix_t) :: state
type(state_matrix_t), dimension(:), allocatable :: single_state
type(state_matrix_t) :: correlated_state
integer :: f, h11, h12, h21, h22, i, mode
type(flavor_t), dimension(2) :: flv
type(color_t), dimension(2) :: col
type(helicity_t), dimension(2) :: hel
type(quantum_numbers_t), dimension(2) :: qn
logical :: ok
write (u, "(A)")
write (u, "(A)") "* Test output: state_matrix_2"
write (u, "(A)") "* Purpose: factorize correlated 3-particle state"
write (u, "(A)")
write (u, "(A)") "* Initialization"
write (u, "(A)")
call state%init ()
do f = 1, 2
do h11 = -1, 1, 2
do h12 = -1, 1, 2
do h21 = -1, 1, 2
do h22 = -1, 1, 2
call flv%init ([f, -f])
call col(1)%init ([1])
call col(2)%init ([-1])
call hel%init ([h11,h12], [h21, h22])
call qn%init (flv, col, hel)
call state%add_state (qn)
end do
end do
end do
end do
end do
call state%freeze ()
call state%write (u)
write (u, "(A)")
write (u, "(A,'('," // FMT_19 // ",','," // FMT_19 // ",')')") &
"* Trace = ", state%trace ()
write (u, "(A)")
do mode = 1, 3
write (u, "(A)")
write (u, "(A,I1)") "* Mode = ", mode
call state%factorize &
(mode, 0.15_default, ok, single_state, correlated_state)
do i = 1, size (single_state)
write (u, "(A)")
call single_state(i)%write (u)
write (u, "(A,'('," // FMT_19 // ",','," // FMT_19 // ",')')") &
"Trace = ", single_state(i)%trace ()
end do
write (u, "(A)")
call correlated_state%write (u)
write (u, "(A,'('," // FMT_19 // ",','," // FMT_19 // ",')')") &
"Trace = ", correlated_state%trace ()
do i = 1, size(single_state)
call single_state(i)%final ()
end do
call correlated_state%final ()
end do
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call state%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: state_matrix_2"
end subroutine state_matrix_2
@ %def state_matrix_2
@ Create a colored state matrix and add color contractions.
<<State matrices: execute tests>>=
call test (state_matrix_3, "state_matrix_3", &
"check factorizing 3-particle state matrix", &
u, results)
<<State matrices: test declarations>>=
public :: state_matrix_3
<<State matrices: tests>>=
subroutine state_matrix_3 (u)
use physics_defs, only: HADRON_REMNANT_TRIPLET, HADRON_REMNANT_OCTET
integer, intent(in) :: u
type(state_matrix_t) :: state
type(flavor_t), dimension(4) :: flv
type(color_t), dimension(4) :: col
type(quantum_numbers_t), dimension(4) :: qn
write (u, "(A)") "* Test output: state_matrix_3"
write (u, "(A)") "* Purpose: add color connections to colored state"
write (u, "(A)")
write (u, "(A)") "* Initialization"
write (u, "(A)")
call state%init ()
call flv%init ([ 1, -HADRON_REMNANT_TRIPLET, -1, HADRON_REMNANT_TRIPLET ])
call col(1)%init ([17])
call col(2)%init ([-17])
call col(3)%init ([-19])
call col(4)%init ([19])
call qn%init (flv, col)
call state%add_state (qn)
call flv%init ([ 1, -HADRON_REMNANT_TRIPLET, 21, HADRON_REMNANT_OCTET ])
call col(1)%init ([17])
call col(2)%init ([-17])
call col(3)%init ([3, -5])
call col(4)%init ([5, -3])
call qn%init (flv, col)
call state%add_state (qn)
call state%freeze ()
write (u, "(A)") "* State:"
write (u, "(A)")
call state%write (u)
call state%add_color_contractions ()
write (u, "(A)") "* State with contractions:"
write (u, "(A)")
call state%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call state%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: state_matrx_3"
end subroutine state_matrix_3
@ %def state_matrix_3
@ Create a correlated three-particle state matrix, write it to file
and read again.
<<State matrices: execute tests>>=
call test (state_matrix_4, "state_matrix_4", &
"check raw I/O", &
u, results)
<<State matrices: test declarations>>=
public :: state_matrix_4
<<State matrices: tests>>=
subroutine state_matrix_4 (u)
integer, intent(in) :: u
type(state_matrix_t), allocatable :: state
integer :: f, h11, h12, h21, h22, i
type(flavor_t), dimension(2) :: flv
type(color_t), dimension(2) :: col
type(helicity_t), dimension(2) :: hel
type(quantum_numbers_t), dimension(2) :: qn
integer :: unit, iostat
write (u, "(A)")
write (u, "(A)") "* Test output: state_matrix_4"
write (u, "(A)") "* Purpose: raw I/O for correlated 3-particle state"
write (u, "(A)")
write (u, "(A)") "* Initialization"
write (u, "(A)")
allocate (state)
call state%init ()
do f = 1, 2
do h11 = -1, 1, 2
do h12 = -1, 1, 2
do h21 = -1, 1, 2
do h22 = -1, 1, 2
call flv%init ([f, -f])
call col(1)%init ([1])
call col(2)%init ([-1])
call hel%init ([h11, h12], [h21, h22])
call qn%init (flv, col, hel)
call state%add_state (qn)
end do
end do
end do
end do
end do
call state%freeze ()
call state%set_norm (3._default)
do i = 1, state%get_n_leaves ()
call state%set_matrix_element (i, cmplx (2 * i, 2 * i + 1, default))
end do
call state%write (u)
write (u, "(A)")
write (u, "(A)") "* Write to file and read again "
write (u, "(A)")
unit = free_unit ()
open (unit, action="readwrite", form="unformatted", status="scratch")
call state%write_raw (unit)
call state%final ()
deallocate (state)
allocate(state)
rewind (unit)
call state%read_raw (unit, iostat=iostat)
close (unit)
call state%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call state%final ()
deallocate (state)
write (u, "(A)")
write (u, "(A)") "* Test output end: state_matrix_4"
end subroutine state_matrix_4
@ %def state_matrix_4
@
Create a flavor-content object for a given state matrix and match it
against trial flavor (i.e., PDG) strings.
<<State matrices: execute tests>>=
call test (state_matrix_5, "state_matrix_5", &
"check flavor content", &
u, results)
<<State matrices: test declarations>>=
public :: state_matrix_5
<<State matrices: tests>>=
subroutine state_matrix_5 (u)
integer, intent(in) :: u
type(state_matrix_t), allocatable, target :: state
type(state_iterator_t) :: it
type(state_flv_content_t), allocatable :: state_flv
type(flavor_t), dimension(4) :: flv1, flv2, flv3, flv4
type(color_t), dimension(4) :: col1, col2
type(helicity_t), dimension(4) :: hel1, hel2, hel3
type(quantum_numbers_t), dimension(4) :: qn
logical, dimension(4) :: mask
write (u, "(A)") "* Test output: state_matrix_5"
write (u, "(A)") "* Purpose: check flavor-content state"
write (u, "(A)")
write (u, "(A)") "* Set up arbitrary state matrix"
write (u, "(A)")
call flv1%init ([1, 4, 2, 7])
call flv2%init ([1, 3,-3, 8])
call flv3%init ([5, 6, 3, 7])
call flv4%init ([6, 3, 5, 8])
call hel1%init ([0, 1, -1, 0])
call hel2%init ([0, 1, 1, 1])
call hel3%init ([1, 0, 0, 0])
call col1(1)%init ([0])
call col1(2)%init ([0])
call col1(3)%init ([0])
call col1(4)%init ([0])
call col2(1)%init ([5, -6])
call col2(2)%init ([0])
call col2(3)%init ([6, -5])
call col2(4)%init ([0])
allocate (state)
call state%init ()
call qn%init (flv1, col1, hel1)
call state%add_state (qn)
call qn%init (flv1, col1, hel2)
call state%add_state (qn)
call qn%init (flv3, col1, hel3)
call state%add_state (qn)
call qn%init (flv4, col1, hel3)
call state%add_state (qn)
call qn%init (flv1, col2, hel3)
call state%add_state (qn)
call qn%init (flv2, col2, hel2)
call state%add_state (qn)
call qn%init (flv2, col2, hel1)
call state%add_state (qn)
call qn%init (flv2, col1, hel1)
call state%add_state (qn)
call qn%init (flv3, col1, hel1)
call state%add_state (qn)
call qn%init (flv3, col2, hel3)
call state%add_state (qn)
call qn%init (flv1, col1, hel1)
call state%add_state (qn)
write (u, "(A)") "* Quantum number content"
write (u, "(A)")
call it%init (state)
do while (it%is_valid ())
call quantum_numbers_write (it%get_quantum_numbers (), u)
write (u, *)
call it%advance ()
end do
write (u, "(A)")
write (u, "(A)") "* Extract the flavor content"
write (u, "(A)")
mask = [.true., .true., .true., .false.]
allocate (state_flv)
call state_flv%fill (state, mask)
call state_flv%write (u)
write (u, "(A)")
write (u, "(A)") "* Match trial sets"
write (u, "(A)")
call check ([1, 2, 3, 0])
call check ([1, 4, 2, 0])
call check ([4, 2, 1, 0])
call check ([1, 3, -3, 0])
call check ([1, -3, 3, 0])
call check ([6, 3, 5, 0])
write (u, "(A)")
write (u, "(A)") "* Determine the flavor content with mask"
write (u, "(A)")
mask = [.false., .true., .true., .false.]
call state_flv%fill (state, mask)
call state_flv%write (u)
write (u, "(A)")
write (u, "(A)") "* Match trial sets"
write (u, "(A)")
call check ([1, 2, 3, 0])
call check ([1, 4, 2, 0])
call check ([4, 2, 1, 0])
call check ([1, 3, -3, 0])
call check ([1, -3, 3, 0])
call check ([6, 3, 5, 0])
write (u, "(A)")
write (u, "(A)") "* Cleanup"
deallocate (state_flv)
call state%final ()
deallocate (state)
write (u, "(A)")
write (u, "(A)") "* Test output end: state_matrix_5"
contains
subroutine check (pdg)
integer, dimension(4), intent(in) :: pdg
integer, dimension(4) :: map
logical :: success
call state_flv%match (pdg, success, map)
write (u, "(2x,4(1x,I0),':',1x,L1)", advance="no") pdg, success
if (success) then
write (u, "(2x,'map = (',4(1x,I0),' )')") map
else
write (u, *)
end if
end subroutine check
end subroutine state_matrix_5
@ %def state_matrix_5
@
Create a state matrix with full flavor, color and helicity information.
Afterwards, reduce such that it is only differential in flavor and
initial-state helicities. This is used when preparing states for beam-
polarized computations with external matrix element providers.
<<State matrices: execute tests>>=
call test (state_matrix_6, "state_matrix_6", &
"check state matrix reduction", &
u, results)
<<State matrices: test declarations>>=
public :: state_matrix_6
<<State matrices: tests>>=
subroutine state_matrix_6 (u)
integer, intent(in) :: u
type(state_matrix_t), allocatable :: state_orig, state_reduced
type(flavor_t), dimension(4) :: flv
type(helicity_t), dimension(4) :: hel
type(color_t), dimension(4) :: col
type(quantum_numbers_t), dimension(4) :: qn
type(quantum_numbers_mask_t), dimension(4) :: qn_mask
integer :: h1, h2, h3 , h4
integer :: n_states = 0
write (u, "(A)") "* Test output: state_matrix_6"
write (u, "(A)") "* Purpose: Check state matrix reduction"
write (u, "(A)")
write (u, "(A)") "* Set up helicity-diagonal state matrix"
write (u, "(A)")
allocate (state_orig)
call state_orig%init ()
call flv%init ([11, -11, 1, -1])
call col(3)%init ([1])
call col(4)%init ([-1])
do h1 = -1, 1, 2
do h2 = -1, 1, 2
do h3 = -1, 1, 2
do h4 = -1, 1, 2
n_states = n_states + 1
call hel%init ([h1, h2, h3, h4], [h1, h2, h3, h4])
call qn%init (flv, col, hel)
call state_orig%add_state (qn)
end do
end do
end do
end do
call state_orig%freeze ()
write (u, "(A)") "* Original state: "
write (u, "(A)")
call state_orig%write (u)
write (u, "(A)")
write (u, "(A)") "* Setup quantum mask: "
call qn_mask%init ([.false., .false., .false., .false.], &
[.true., .true., .true., .true.], &
[.false., .false., .true., .true.])
call quantum_numbers_mask_write (qn_mask, u)
write (u, "(A)")
write (u, "(A)") "* Reducing the state matrix using above mask"
write (u, "(A)")
allocate (state_reduced)
call state_orig%reduce (qn_mask, state_reduced)
write (u, "(A)") "* Reduced state matrix: "
call state_reduced%write (u)
write (u, "(A)") "* Test output end: state_matrix_6"
end subroutine state_matrix_6
@ %def state_matrix_6
@
Create a state matrix with full flavor, color and helicity information.
Afterwards, reduce such that it is only differential in flavor and
initial-state helicities, and keeping old indices. Afterwards reorder the
reduced state matrix in accordance to the original state matrix.
<<State matrices: execute tests>>=
call test (state_matrix_7, "state_matrix_7", &
"check ordered state matrix reduction", &
u, results)
<<State matrices: test declarations>>=
public :: state_matrix_7
<<State matrices: tests>>=
subroutine state_matrix_7 (u)
integer, intent(in) :: u
type(state_matrix_t), allocatable :: state_orig, state_reduced, &
state_ordered
type(flavor_t), dimension(4) :: flv
type(helicity_t), dimension(4) :: hel
type(color_t), dimension(4) :: col
type(quantum_numbers_t), dimension(4) :: qn
type(quantum_numbers_mask_t), dimension(4) :: qn_mask
integer :: h1, h2, h3 , h4
integer :: n_states = 0
write (u, "(A)") "* Test output: state_matrix_7"
write (u, "(A)") "* Purpose: Check ordered state matrix reduction"
write (u, "(A)")
write (u, "(A)") "* Set up helicity-diagonal state matrix"
write (u, "(A)")
allocate (state_orig)
call state_orig%init ()
call flv%init ([11, -11, 1, -1])
call col(3)%init ([1])
call col(4)%init ([-1])
do h1 = -1, 1, 2
do h2 = -1, 1, 2
do h3 = -1, 1, 2
do h4 = -1, 1, 2
n_states = n_states + 1
call hel%init ([h1, h2, h3, h4], [h1, h2, h3, h4])
call qn%init (flv, col, hel)
call state_orig%add_state (qn)
end do
end do
end do
end do
call state_orig%freeze ()
write (u, "(A)") "* Original state: "
write (u, "(A)")
call state_orig%write (u)
write (u, "(A)")
write (u, "(A)") "* Setup quantum mask: "
call qn_mask%init ([.false., .false., .false., .false.], &
[.true., .true., .true., .true.], &
[.false., .false., .true., .true.])
call quantum_numbers_mask_write (qn_mask, u)
write (u, "(A)")
write (u, "(A)") "* Reducing the state matrix using above mask and keeping the old indices:"
write (u, "(A)")
allocate (state_reduced)
call state_orig%reduce (qn_mask, state_reduced, keep_me_index = .true.)
write (u, "(A)") "* Reduced state matrix with kept indices: "
call state_reduced%write (u)
write (u, "(A)")
write (u, "(A)") "* Reordering reduced state matrix:"
write (u, "(A)")
allocate (state_ordered)
call state_reduced%reorder_me (state_ordered)
write (u, "(A)") "* Reduced and ordered state matrix:"
call state_ordered%write (u)
write (u, "(A)") "* Test output end: state_matrix_6"
end subroutine state_matrix_7
@ %def state_matrix_7
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Interactions}
This module defines the [[interaction_t]] type. It is an extension of
the [[state_matrix_t]] type.
The state matrix is a representation of a multi-particle density
matrix. It implements all possible flavor, color, and quantum-number
assignments of the entries in a generic density matrix, and it can
hold a complex matrix element for each entry. (Note that this matrix
can hold non-diagonal entries in color and helicity space.) The
[[interaction_t]] object associates this with a list of momenta, such
that the whole object represents a multi-particle state.
The [[interaction_t]] holds information about which particles are
incoming, virtual (i.e., kept for the records), or outgoing. Each
particle can be associated to a source within another interaction.
This allows us to automatically fill those interaction momenta which
have been computed or defined elsewhere. It also contains internal
parent-child relations and flags for (virtual) particles which are to
be treated as resonances.
A quantum-number mask array summarizes, for each particle within the
interaction, the treatment of flavor, color, or helicity (expose or
ignore). A list of locks states which particles are bound to have an
identical quantum-number mask. This is useful when the mask is
changed at one place.
<<[[interactions.f90]]>>=
<<File header>>
module interactions
<<Use kinds>>
use io_units
use diagnostics
use sorting
use lorentz
use flavors
use colors
use helicities
use quantum_numbers
use state_matrices
<<Standard module head>>
<<Interactions: public>>
<<Interactions: types>>
<<Interactions: interfaces>>
contains
<<Interactions: procedures>>
end module interactions
@ %def interactions
@ Given a ordered list of quantum numbers (without any subtraction index) map
these list to a state matrix, such that each list index corresponds to index of a set of
quantum numbers in the state matrix, hence, the matrix element.
The (unphysical) subtraction index is not a genuine quantum number and as
such handled specially.
<<Interactions: public>>=
public :: qn_index_map_t
<<Interactions: types>>=
type :: qn_index_map_t
private
type(quantum_numbers_t), dimension(:, :), allocatable :: qn_flv
type(quantum_numbers_t), dimension(:, :), allocatable :: qn_hel
logical :: flip_hel = .false.
integer :: n_flv = 0, n_hel = 0, n_sub = 0
integer, dimension(:, :, :), allocatable :: index
+ integer, dimension(:,:), allocatable :: sf_index_born, sf_index_real
contains
<<Interactions: qn index map: TBP>>
end type qn_index_map_t
@ %def qn_index_map_t
@ Construct a mapping from interaction to an array of (sorted) quantum numbers.
We strip all non-elementary particles (like beam) from the quantum numbers which
we retrieve from the interaction.
We consider helicity matrix elements only, when [[qn_hel]] is allocated.
Else the helicity index is handled trivially as [[1]].
<<Interactions: qn index map: TBP>>=
generic :: init => qn_index_map_init
procedure, private :: qn_index_map_init
<<Interactions: procedures>>=
- subroutine qn_index_map_init (self, int, qn_flv, n_sub, qn_hel)
+ subroutine qn_index_map_init (self, int, qn_flv, n_sub, qn_hel)
class(qn_index_map_t), intent(out) :: self
- class(interaction_t), intent(in) :: int
+ type(interaction_t), intent(in) :: int
type(quantum_numbers_t), dimension(:, :), intent(in) :: qn_flv
integer, intent(in) :: n_sub
type(quantum_numbers_t), dimension(:, :), intent(in), optional :: qn_hel
type(quantum_numbers_t), dimension(:), allocatable :: qn, qn_int
integer :: i, i_flv, i_hel, i_sub
self%qn_flv = qn_flv
self%n_flv = size (qn_flv, dim=2)
self%n_sub = n_sub
if (present (qn_hel)) then
if (size (qn_flv, dim=1) /= size (qn_hel, dim=1)) then
call msg_bug ("[qn_index_map_init] number of particles does not match.")
end if
self%qn_hel = qn_hel
self%n_hel = size (qn_hel, dim=2)
else
self%n_hel = 1
end if
allocate (self%index (self%n_flv, self%n_hel, 0:self%n_sub), source=0)
associate (n_me => int%get_n_matrix_elements ())
do i = 1, n_me
qn_int = int%get_quantum_numbers (i, by_me_index = .true.)
qn = pack (qn_int, qn_int%are_hard_process ())
i_flv = find_flv_index (self, qn)
i_hel = 1; if (allocated (self%qn_hel)) &
i_hel = find_hel_index (self, qn)
i_sub = find_sub_index (self, qn)
self%index(i_flv, i_hel, i_sub) = i
end do
end associate
contains
integer function find_flv_index (self, qn) result (i_flv)
type(qn_index_map_t), intent(in) :: self
type(quantum_numbers_t), dimension(:), intent(in) :: qn
integer :: j
i_flv = 0
do j = 1, self%n_flv
if (.not. all (qn .fmatch. self%qn_flv(:, j))) cycle
i_flv = j
exit
end do
if (i_flv < 1) then
call msg_message ("QN:")
call quantum_numbers_write (qn)
call msg_message ("")
call msg_message ("QN_FLV:")
do j = 1, self%n_flv
call quantum_numbers_write (self%qn_flv(:, j))
call msg_message ("")
end do
call msg_bug ("[find_flv_index] could not find flv in qn_flv.")
end if
end function find_flv_index
integer function find_hel_index (self, qn) result (i_hel)
type(qn_index_map_t), intent(in) :: self
type(quantum_numbers_t), dimension(:), intent(in) :: qn
integer :: j
i_hel = 0
do j = 1, self%n_hel
if (.not. all (qn .hmatch. self%qn_hel(:, j))) cycle
i_hel = j
exit
end do
if (i_hel < 1) then
call msg_message ("QN:")
call quantum_numbers_write (qn)
call msg_message ("")
call msg_message ("QN_HEL:")
do j = 1, self%n_hel
call quantum_numbers_write (self%qn_hel(:, j))
call msg_message ("")
end do
call msg_bug ("[find_hel_index] could not find hel in qn_hel.")
end if
end function find_hel_index
integer function find_sub_index (self, qn) result (i_sub)
type(qn_index_map_t), intent(in) :: self
type(quantum_numbers_t), dimension(:), intent(in) :: qn
integer :: s
i_sub = -1
do s = 0, self%n_sub
if ((all (pack(qn%get_sub (), qn%get_sub () > 0) == s)) &
.or. (all (qn%get_sub () == 0) .and. s == 0)) then
i_sub = s
exit
end if
end do
if (i_sub < 0) then
call msg_message ("QN:")
call quantum_numbers_write (qn)
call msg_bug ("[find_sub_index] could not find sub in qn.")
end if
end function find_sub_index
end subroutine qn_index_map_init
@ %def qn_index_map_init
@ Construct a trivial mapping.
<<Interactions: qn index map: TBP>>=
generic :: init => qn_index_map_init_trivial
procedure, private :: qn_index_map_init_trivial
<<Interactions: procedures>>=
subroutine qn_index_map_init_trivial (self, int)
class(qn_index_map_t), intent(out) :: self
class(interaction_t), intent(in) :: int
integer :: qn
self%n_flv = int%get_n_matrix_elements ()
self%n_hel = 1
self%n_sub = 0
allocate (self%index(self%n_flv, self%n_hel, 0:self%n_sub), source = 0)
do qn = 1, self%n_flv
self%index(qn, 1, 0) = qn
end do
end subroutine qn_index_map_init_trivial
@ %def qn_index_map_init_trivial
@ Write the index map to unit.
<<Interactions: qn index map: TBP>>=
procedure :: write => qn_index_map_write
<<Interactions: procedures>>=
subroutine qn_index_map_write (self, unit)
class(qn_index_map_t), intent(in) :: self
integer, intent(in), optional :: unit
integer :: u, i_flv, i_hel, i_sub
u = given_output_unit (unit); if (u < 0) return
write (u, *) "flip_hel: ", self%flip_hel
do i_flv = 1, self%n_flv
if (allocated (self%qn_flv)) &
call quantum_numbers_write (self%qn_flv(:, i_flv))
write (u, *)
do i_hel = 1, self%n_hel
if (allocated (self%qn_hel)) then
call quantum_numbers_write (self%qn_hel(:, i_hel))
write (u, *)
end if
do i_sub = 0, self%n_sub
write (u, *) &
"(", i_flv, ",", i_hel, ",", i_sub, ") => ", self%index(i_flv, i_hel, i_sub)
end do
end do
end do
end subroutine qn_index_map_write
@ %def qn_index_map_write
@ Set helicity convention. If [[flip]], then we flip the helicities of anti-particles
and we remap the indices accordingly.
<<Interactions: qn index map: TBP>>=
procedure :: set_helicity_flip => qn_index_map_set_helicity_flip
<<Interactions: procedures>>=
subroutine qn_index_map_set_helicity_flip (self, yorn)
class(qn_index_map_t), intent(inout) :: self
logical, intent(in) :: yorn
integer :: i, i_flv, i_hel, i_hel_new
type(quantum_numbers_t), dimension(:, :), allocatable :: qn_hel_flip
integer, dimension(:, :, :), allocatable :: index
if (.not. allocated (self%qn_hel)) then
call msg_bug ("[qn_index_map_set_helicity_flip] &
&cannot flip not-given helicity.")
end if
! Workaround for ifort (allocate-on-assignmet)
allocate (qn_hel_flip (size (self%qn_hel, dim=1),&
size (self%qn_hel, dim=2)))
allocate (index (self%n_flv, self%n_hel, 0:self%n_sub),&
source=self%index)
self%flip_hel = yorn
if (self%flip_hel) then
do i_flv = 1, self%n_flv
qn_hel_flip = self%qn_hel
do i_hel = 1, self%n_hel
do i = 1, size (self%qn_flv, dim=1)
if (is_anti_particle (self%qn_flv(i, i_flv))) then
call qn_hel_flip(i, i_hel)%flip_helicity ()
end if
end do
end do
do i_hel = 1, self%n_hel
i_hel_new = find_hel_index (qn_hel_flip, self%qn_hel(:, i_hel))
self%index(i_flv, i_hel_new, :) = index(i_flv, i_hel, :)
end do
end do
end if
contains
logical function is_anti_particle (qn) result (yorn)
type(quantum_numbers_t), intent(in) :: qn
type(flavor_t) :: flv
flv = qn%get_flavor ()
yorn = flv%get_pdg () < 0
end function is_anti_particle
integer function find_hel_index (qn_sort, qn) result (i_hel)
type(quantum_numbers_t), dimension(:, :), intent(in) :: qn_sort
type(quantum_numbers_t), dimension(:), intent(in) :: qn
integer :: j
do j = 1, size(qn_sort, dim=2)
if (.not. all (qn .hmatch. qn_sort(:, j))) cycle
i_hel = j
exit
end do
end function find_hel_index
end subroutine qn_index_map_set_helicity_flip
@ %def qn_index_map_set_helicity_flip
@ Map from the previously given quantum number and subtraction
index (latter ranging from 0 to [[n_sub]]) to the (interaction) matrix element.
<<Interactions: qn index map: TBP>>=
procedure :: get_index => qn_index_map_get_index
<<Interactions: procedures>>=
integer function qn_index_map_get_index (self, i_flv, i_hel, i_sub) result (index)
class(qn_index_map_t), intent(in) :: self
integer, intent(in) :: i_flv
integer, intent(in), optional :: i_hel
integer, intent(in), optional :: i_sub
integer :: i_sub_opt, i_hel_opt
i_sub_opt = 0; if (present (i_sub)) &
i_sub_opt = i_sub
i_hel_opt = 1; if (present (i_hel)) &
i_hel_opt = i_hel
index = 0
if (.not. allocated (self%index)) then
call msg_bug ("[qn_index_map_get_index] The index map is not allocated.")
end if
index = self%index(i_flv, i_hel_opt, i_sub_opt)
if (index <= 0) then
call self%write ()
call msg_bug ("[qn_index_map_get_index] The index for the given quantum numbers could not be retrieved.")
end if
end function qn_index_map_get_index
@ %def qn_index_map_get_i_flv
@ Get [[n_flv]].
<<Interactions: qn index map: TBP>>=
procedure :: get_n_flv => qn_index_map_get_n_flv
<<Interactions: procedures>>=
integer function qn_index_map_get_n_flv (self) result (n_flv)
class(qn_index_map_t), intent(in) :: self
n_flv = self%n_flv
end function qn_index_map_get_n_flv
@ %def qn_index_map_get_n_flv
@ Get [[n_hel]].
<<Interactions: qn index map: TBP>>=
procedure :: get_n_hel => qn_index_map_get_n_hel
<<Interactions: procedures>>=
integer function qn_index_map_get_n_hel (self) result (n_hel)
class(qn_index_map_t), intent(in) :: self
n_hel = self%n_hel
end function qn_index_map_get_n_hel
@ %def qn_index_map_get_n_flv
@ Get [[n_sub]].
<<Interactions: qn index map: TBP>>=
procedure :: get_n_sub => qn_index_map_get_n_sub
<<Interactions: procedures>>=
integer function qn_index_map_get_n_sub (self) result (n_sub)
class(qn_index_map_t), intent(in) :: self
n_sub = self%n_sub
end function qn_index_map_get_n_sub
@ %def qn_index_map_get_n_sub
+@ For the rescaling of the structure functions in the real subtraction
+and DGLAP components we need a mapping from the real and born flavor structure
+indices to the structure function chain interaction matrix element with the
+correct initial state quantum numbers. This is stored in [[sf_index_born]]
+and [[sf_index_real]]. The array [[index]] is only needed for the initialisation
+of the Born and real index arrays and is therefore deallocated again.
+<<Interactions: qn index map: TBP>>=
+ procedure :: init_sf => qn_index_map_init_sf
+<<Interactions: procedures>>=
+ subroutine qn_index_map_init_sf (self, int, qn_flv, n_flv_born, n_flv_real)
+ class(qn_index_map_t), intent(out) :: self
+ type(interaction_t), intent(in) :: int
+ integer, intent(in) :: n_flv_born, n_flv_real
+ type(quantum_numbers_t), dimension(:,:), intent(in) :: qn_flv
+ type(quantum_numbers_t), dimension(:,:), allocatable :: qn_int
+ type(quantum_numbers_t), dimension(:), allocatable :: qn_int_tmp
+ integer :: i, i_sub, n_flv, n_hard
+ n_flv = int%get_n_matrix_elements ()
+ qn_int_tmp = int%get_quantum_numbers (1, by_me_index = .true.)
+ n_hard = count (qn_int_tmp%are_hard_process ())
+ allocate (qn_int(n_hard, n_flv))
+ do i = 1, n_flv
+ qn_int_tmp = int%get_quantum_numbers (i, by_me_index = .true.)
+ qn_int(:, i) = pack (qn_int_tmp, qn_int_tmp%are_hard_process ())
+ end do
+ call self%init (int, qn_int, int%get_n_sub ())
+ allocate (self%sf_index_born(n_flv_born, 0:self%n_sub))
+ allocate (self%sf_index_real(n_flv_real, 0:self%n_sub))
+ do i_sub = 0, self%n_sub
+ do i = 1, n_flv_born
+ self%sf_index_born(i, i_sub) = self%get_index_by_qn (qn_flv(:,i), i_sub)
+ end do
+ do i = 1, n_flv_real
+ self%sf_index_real(i, i_sub) = &
+ self%get_index_by_qn (qn_flv(:,n_flv_born + i), i_sub)
+ end do
+ end do
+ deallocate (self%index)
+ end subroutine qn_index_map_init_sf
+
+@ %def qn_index_map_init_sf
+@ Gets the index for the matrix element corresponding to a set of quantum numbers.
+So far, it ignores helicity (and color) indices.
+<<Interactions: qn index map: TBP>>=
+ procedure :: get_index_by_qn => qn_index_map_get_index_by_qn
+<<Interactions: procedures>>=
+ integer function qn_index_map_get_index_by_qn (self, qn, i_sub) result (index)
+ class(qn_index_map_t), intent(in) :: self
+ type(quantum_numbers_t), dimension(:), intent(in) :: qn
+ integer, intent(in), optional :: i_sub
+ integer :: i_qn
+ if (size (qn) /= size (self%qn_flv, dim = 1)) &
+ call msg_bug ("[qn_index_map_get_index_by_qn] number of particles does not match.")
+ do i_qn = 1, self%n_flv
+ if (all (qn .fmatch. self%qn_flv(:, i_qn))) then
+ index = self%get_index (i_qn, i_sub = i_sub)
+ return
+ end if
+ end do
+ call msg_bug ("[qn_index_map_get_index] The index for the given quantum &
+ & numbers could not be retrieved.")
+ end function qn_index_map_get_index_by_qn
+
+@ %def qn_index_map_get_index_by_qn
+@
+<<Interactions: qn index map: TBP>>=
+ procedure :: get_sf_index_born => qn_index_map_get_sf_index_born
+<<Interactions: procedures>>=
+ integer function qn_index_map_get_sf_index_born (self, i_born, i_sub) result (index)
+ class(qn_index_map_t), intent(in) :: self
+ integer, intent(in) :: i_born, i_sub
+ index = self%sf_index_born(i_born, i_sub)
+ end function qn_index_map_get_sf_index_born
+
+@ %def qn_index_map_get_sf_index_born
+@
+<<Interactions: qn index map: TBP>>=
+ procedure :: get_sf_index_real => qn_index_map_get_sf_index_real
+<<Interactions: procedures>>=
+ integer function qn_index_map_get_sf_index_real (self, i_real, i_sub) result (index)
+ class(qn_index_map_t), intent(in) :: self
+ integer, intent(in) :: i_real, i_sub
+ index = self%sf_index_real(i_real, i_sub)
+ end function qn_index_map_get_sf_index_real
+
+@ %def qn_index_map_get_sf_index_real
@
\subsection{External interaction links}
Each particle in an interaction can have a link to a corresponding
particle in another interaction. This allows to fetch the momenta of
incoming or virtual particles from the interaction where they are
defined. The link object consists of a pointer to the interaction and
an index.
<<Interactions: types>>=
type :: external_link_t
private
type(interaction_t), pointer :: int => null ()
integer :: i
end type external_link_t
@ %def external_link_t
@ Set an external link.
<<Interactions: procedures>>=
subroutine external_link_set (link, int, i)
type(external_link_t), intent(out) :: link
type(interaction_t), target, intent(in) :: int
integer, intent(in) :: i
if (i /= 0) then
link%int => int
link%i = i
end if
end subroutine external_link_set
@ %def external_link_set
@ Reassign an external link to a new interaction (which should be an
image of the original target).
<<Interactions: procedures>>=
subroutine external_link_reassign (link, int_src, int_target)
type(external_link_t), intent(inout) :: link
type(interaction_t), intent(in) :: int_src
type(interaction_t), intent(in), target :: int_target
if (associated (link%int)) then
if (link%int%tag == int_src%tag) link%int => int_target
end if
end subroutine external_link_reassign
@ %def external_link_reassign
@ Return true if the link is set
<<Interactions: procedures>>=
function external_link_is_set (link) result (flag)
logical :: flag
type(external_link_t), intent(in) :: link
flag = associated (link%int)
end function external_link_is_set
@ %def external_link_is_set
@ Return the interaction pointer.
<<Interactions: public>>=
public :: external_link_get_ptr
<<Interactions: procedures>>=
function external_link_get_ptr (link) result (int)
type(interaction_t), pointer :: int
type(external_link_t), intent(in) :: link
int => link%int
end function external_link_get_ptr
@ %def external_link_get_ptr
@ Return the index within that interaction
<<Interactions: public>>=
public :: external_link_get_index
<<Interactions: procedures>>=
function external_link_get_index (link) result (i)
integer :: i
type(external_link_t), intent(in) :: link
i = link%i
end function external_link_get_index
@ %def external_link_get_index
@ Return a pointer to the momentum of the corresponding particle. If
there is no association, return a null pointer.
<<Interactions: procedures>>=
function external_link_get_momentum_ptr (link) result (p)
type(vector4_t), pointer :: p
type(external_link_t), intent(in) :: link
if (associated (link%int)) then
p => link%int%p(link%i)
else
p => null ()
end if
end function external_link_get_momentum_ptr
@ %def external_link_get_momentum_ptr
@
\subsection{Internal relations}
In addition to the external links, particles within the interaction
have parent-child relations. Here, more than one link is possible,
and we set up an array.
<<Interactions: types>>=
type :: internal_link_list_t
private
integer :: length = 0
integer, dimension(:), allocatable :: link
contains
<<Interactions: internal link list: TBP>>
end type internal_link_list_t
@ %def internal_link_t internal_link_list_t
@ Output, non-advancing.
<<Interactions: internal link list: TBP>>=
procedure :: write => internal_link_list_write
<<Interactions: procedures>>=
subroutine internal_link_list_write (object, unit)
class(internal_link_list_t), intent(in) :: object
integer, intent(in), optional :: unit
integer :: u, i
u = given_output_unit (unit)
do i = 1, object%length
write (u, "(1x,I0)", advance="no") object%link(i)
end do
end subroutine internal_link_list_write
@ %def internal_link_list_write
@ Append an item. Start with an array size of 2 and double the size
if necessary.
Make sure that the indices are stored in ascending order. To this
end, shift the existing entries right, starting from the end, as long
as they are larger than the new entry.
<<Interactions: internal link list: TBP>>=
procedure :: append => internal_link_list_append
<<Interactions: procedures>>=
subroutine internal_link_list_append (link_list, link)
class(internal_link_list_t), intent(inout) :: link_list
integer, intent(in) :: link
integer :: l, j
integer, dimension(:), allocatable :: tmp
l = link_list%length
if (allocated (link_list%link)) then
if (l == size (link_list%link)) then
allocate (tmp (2 * l))
tmp(:l) = link_list%link
call move_alloc (from = tmp, to = link_list%link)
end if
else
allocate (link_list%link (2))
end if
link_list%link(l+1) = link
SHIFT_LINK_IN_PLACE: do j = l, 1, -1
if (link >= link_list%link(j)) then
exit SHIFT_LINK_IN_PLACE
else
link_list%link(j+1) = link_list%link(j)
link_list%link(j) = link
end if
end do SHIFT_LINK_IN_PLACE
link_list%length = l + 1
end subroutine internal_link_list_append
@ %def internal_link_list_append
@ Return true if the link list is nonempty:
<<Interactions: internal link list: TBP>>=
procedure :: has_entries => internal_link_list_has_entries
<<Interactions: procedures>>=
function internal_link_list_has_entries (link_list) result (flag)
class(internal_link_list_t), intent(in) :: link_list
logical :: flag
flag = link_list%length > 0
end function internal_link_list_has_entries
@ %def internal_link_list_has_entries
@ Return the list length
<<Interactions: internal link list: TBP>>=
procedure :: get_length => internal_link_list_get_length
<<Interactions: procedures>>=
function internal_link_list_get_length (link_list) result (length)
class(internal_link_list_t), intent(in) :: link_list
integer :: length
length = link_list%length
end function internal_link_list_get_length
@ %def internal_link_list_get_length
@ Return an entry.
<<Interactions: internal link list: TBP>>=
procedure :: get_link => internal_link_list_get_link
<<Interactions: procedures>>=
function internal_link_list_get_link (link_list, i) result (link)
class(internal_link_list_t), intent(in) :: link_list
integer, intent(in) :: i
integer :: link
if (i <= link_list%length) then
link = link_list%link(i)
else
call msg_bug ("Internal link list: out of bounds")
end if
end function internal_link_list_get_link
@ %def internal_link_list_get_link
@
\subsection{The interaction type}
An interaction is an entangled system of particles. Thus, the
interaction object consists of two parts: the subevent, and the
quantum state which technically is a trie. The subnode levels beyond
the trie root node are in correspondence to the subevent, so
both should be traversed in parallel.
The subevent is implemented as an allocatable array of
four-momenta. The first [[n_in]] particles are incoming, [[n_vir]]
particles in-between can be kept for bookkeeping, and the last
[[n_out]] particles are outgoing.
Distinct interactions are linked by their particles: for each
particle, we have the possibility of links to corresponding particles
in other interactions. Furthermore, for bookkeeping purposes we have
a self-link array [[relations]] where the parent-child relations are
kept, and a flag array [[resonant]] which is set for an intermediate
resonance.
Each momentum is associated with masks for flavor, color, and
helicity. If a mask entry is set, the associated quantum number is to
be ignored for that particle. If any mask has changed, the flag
[[update]] is set.
We can have particle pairs locked together. If this is the case, the
corresponding mask entries are bound to be equal. This is useful for
particles that go through the interaction.
The interaction tag serves bookkeeping purposes. In particular, it
identifies links in printout.
<<Interactions: public>>=
public :: interaction_t
<<Interactions: types>>=
type :: interaction_t
private
integer :: tag = 0
type(state_matrix_t) :: state_matrix
integer :: n_in = 0
integer :: n_vir = 0
integer :: n_out = 0
integer :: n_tot = 0
logical, dimension(:), allocatable :: p_is_known
type(vector4_t), dimension(:), allocatable :: p
type(external_link_t), dimension(:), allocatable :: source
type(internal_link_list_t), dimension(:), allocatable :: parents
type(internal_link_list_t), dimension(:), allocatable :: children
logical, dimension(:), allocatable :: resonant
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask
integer, dimension(:), allocatable :: hel_lock
logical :: update_state_matrix = .false.
logical :: update_values = .false.
contains
<<Interactions: interaction: TBP>>
end type interaction_t
@ %def interaction_particle_p interaction_t
@ Initialize the particle array with a fixed size. The first [[n_in]]
particles are incoming, the rest outgoing. Masks are optional. There
is also an optional tag. The interaction still needs fixing the
values, but that is to be done after all branches have been added.
Interaction tags are assigned consecutively, using a [[save]]d
variable local to this procedure. If desired, we can provide a seed
for the interaction tags. Such a seed should be positive. The
default seed is one. [[tag=0]] indicates an empty interaction.
If [[set_relations]] is set and true, we establish parent-child
relations for all incoming and outgoing particles. Virtual particles
are skipped; this option is normally used only for interations without
virtual particles.
<<Interactions: interaction: TBP>>=
procedure :: basic_init => interaction_init
<<Interactions: procedures>>=
subroutine interaction_init &
(int, n_in, n_vir, n_out, &
tag, resonant, mask, hel_lock, set_relations, store_values)
class(interaction_t), intent(out) :: int
integer, intent(in) :: n_in, n_vir, n_out
integer, intent(in), optional :: tag
logical, dimension(:), intent(in), optional :: resonant
type(quantum_numbers_mask_t), dimension(:), intent(in), optional :: mask
integer, dimension(:), intent(in), optional :: hel_lock
logical, intent(in), optional :: set_relations, store_values
logical :: set_rel
integer :: i, j
set_rel = .false.; if (present (set_relations)) set_rel = set_relations
call interaction_set_tag (int, tag)
call int%state_matrix%init (store_values)
int%n_in = n_in
int%n_vir = n_vir
int%n_out = n_out
int%n_tot = n_in + n_vir + n_out
allocate (int%p_is_known (int%n_tot))
int%p_is_known = .false.
allocate (int%p (int%n_tot))
allocate (int%source (int%n_tot))
allocate (int%parents (int%n_tot))
allocate (int%children (int%n_tot))
allocate (int%resonant (int%n_tot))
if (present (resonant)) then
int%resonant = resonant
else
int%resonant = .false.
end if
allocate (int%mask (int%n_tot))
allocate (int%hel_lock (int%n_tot))
if (present (mask)) then
int%mask = mask
end if
if (present (hel_lock)) then
int%hel_lock = hel_lock
else
int%hel_lock = 0
end if
int%update_state_matrix = .false.
int%update_values = .true.
if (set_rel) then
do i = 1, n_in
do j = 1, n_out
call int%relate (i, n_in + j)
end do
end do
end if
end subroutine interaction_init
@ %def interaction_init
@ Set or create a unique tag for the interaction. Without
interaction, reset the tag counter.
<<Interactions: procedures>>=
subroutine interaction_set_tag (int, tag)
type(interaction_t), intent(inout), optional :: int
integer, intent(in), optional :: tag
integer, save :: stored_tag = 1
if (present (int)) then
if (present (tag)) then
int%tag = tag
else
int%tag = stored_tag
stored_tag = stored_tag + 1
end if
else if (present (tag)) then
stored_tag = tag
else
stored_tag = 1
end if
end subroutine interaction_set_tag
@ %def interaction_set_tag
@ The public interface for the previous procedure only covers the
reset functionality.
<<Interactions: public>>=
public :: reset_interaction_counter
<<Interactions: procedures>>=
subroutine reset_interaction_counter (tag)
integer, intent(in), optional :: tag
call interaction_set_tag (tag=tag)
end subroutine reset_interaction_counter
@ %def reset_interaction_counter
@ Finalizer: The state-matrix object contains pointers.
<<Interactions: interaction: TBP>>=
procedure :: final => interaction_final
<<Interactions: procedures>>=
subroutine interaction_final (object)
class(interaction_t), intent(inout) :: object
call object%state_matrix%final ()
end subroutine interaction_final
@ %def interaction_final
@ Output. The [[verbose]] option refers to the state matrix output.
<<Interactions: interaction: TBP>>=
procedure :: basic_write => interaction_write
<<Interactions: procedures>>=
subroutine interaction_write &
(int, unit, verbose, show_momentum_sum, show_mass, show_state, &
col_verbose, testflag)
class(interaction_t), intent(in) :: int
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose, show_momentum_sum, show_mass
logical, intent(in), optional :: show_state, col_verbose, testflag
integer :: u
integer :: i, index_link
type(interaction_t), pointer :: int_link
logical :: show_st
u = given_output_unit (unit); if (u < 0) return
show_st = .true.; if (present (show_state)) show_st = show_state
if (int%tag /= 0) then
write (u, "(1x,A,I0)") "Interaction: ", int%tag
do i = 1, int%n_tot
if (i == 1 .and. int%n_in > 0) then
write (u, "(1x,A)") "Incoming:"
else if (i == int%n_in + 1 .and. int%n_vir > 0) then
write (u, "(1x,A)") "Virtual:"
else if (i == int%n_in + int%n_vir + 1 .and. int%n_out > 0) then
write (u, "(1x,A)") "Outgoing:"
end if
write (u, "(1x,A,1x,I0)", advance="no") "Particle", i
if (allocated (int%resonant)) then
if (int%resonant(i)) then
write (u, "(A)") "[r]"
else
write (u, *)
end if
else
write (u, *)
end if
if (allocated (int%p)) then
if (int%p_is_known(i)) then
call vector4_write (int%p(i), u, show_mass, testflag)
else
write (u, "(A)") " [momentum undefined]"
end if
else
write (u, "(A)") " [momentum not allocated]"
end if
if (allocated (int%mask)) then
write (u, "(1x,A)", advance="no") "mask [fch] = "
call int%mask(i)%write (u)
write (u, *)
end if
if (int%parents(i)%has_entries () &
.or. int%children(i)%has_entries ()) then
write (u, "(1x,A)", advance="no") "internal links:"
call int%parents(i)%write (u)
if (int%parents(i)%has_entries ()) &
write (u, "(1x,A)", advance="no") "=>"
write (u, "(1x,A)", advance="no") "X"
if (int%children(i)%has_entries ()) &
write (u, "(1x,A)", advance="no") "=>"
call int%children(i)%write (u)
write (u, *)
end if
if (allocated (int%hel_lock)) then
if (int%hel_lock(i) /= 0) then
write (u, "(1x,A,1x,I0)") "helicity lock:", int%hel_lock(i)
end if
end if
if (external_link_is_set (int%source(i))) then
write (u, "(1x,A)", advance="no") "source:"
int_link => external_link_get_ptr (int%source(i))
index_link = external_link_get_index (int%source(i))
write (u, "(1x,'(',I0,')',I0)", advance="no") &
int_link%tag, index_link
write (u, *)
end if
end do
if (present (show_momentum_sum)) then
if (allocated (int%p) .and. show_momentum_sum) then
write (u, "(1x,A)") "Incoming particles (sum):"
call vector4_write &
(sum (int%p(1 : int%n_in)), u, show_mass = show_mass)
write (u, "(1x,A)") "Outgoing particles (sum):"
call vector4_write &
(sum (int%p(int%n_in + int%n_vir + 1 : )), &
u, show_mass = show_mass)
write (u, *)
end if
end if
if (show_st) then
call int%write_state_matrix (write_value_list = verbose, &
verbose = verbose, unit = unit, col_verbose = col_verbose, &
testflag = testflag)
end if
else
write (u, "(1x,A)") "Interaction: [empty]"
end if
end subroutine interaction_write
@ %def interaction_write
@
<<Interactions: interaction: TBP>>=
procedure :: write_state_matrix => interaction_write_state_matrix
<<Interactions: procedures>>=
subroutine interaction_write_state_matrix (int, unit, write_value_list, &
verbose, col_verbose, testflag)
class(interaction_t), intent(in) :: int
logical, intent(in), optional :: write_value_list, verbose, col_verbose
logical, intent(in), optional :: testflag
integer, intent(in), optional :: unit
call int%state_matrix%write (write_value_list = verbose, &
verbose = verbose, unit = unit, col_verbose = col_verbose, &
testflag = testflag)
end subroutine interaction_write_state_matrix
@ %def interaction_write_state_matrix
@ Reduce the [[state_matrix]] over the quantum mask. During the reduce procedure
the iterator does not conserve the order of the matrix element respective their
quantum numbers. Setting the [[keep_order]] results in a reorder state matrix
-with reintroduced matrix element indices.
+with reintroduced matrix element indices.
<<Interactions: interaction: TBP>>=
procedure :: reduce_state_matrix => interaction_reduce_state_matrix
<<Interactions: procedures>>=
subroutine interaction_reduce_state_matrix (int, qn_mask, keep_order)
class(interaction_t), intent(inout) :: int
type(quantum_numbers_mask_t), intent(in), dimension(:) :: qn_mask
logical, optional, intent(in) :: keep_order
type(state_matrix_t) :: state
logical :: opt_keep_order
opt_keep_order = .false.
if (present (keep_order)) opt_keep_order = keep_order
call int%state_matrix%reduce (qn_mask, state, keep_me_index = keep_order)
int%state_matrix = state
if (opt_keep_order) then
call int%state_matrix%reorder_me (state)
int%state_matrix = state
end if
end subroutine interaction_reduce_state_matrix
@ %def interaction_reduce_state_matrix
@ Assignment: We implement this as a deep copy. This applies, in
particular, to the state-matrix and internal-link components.
Furthermore, the new interaction acquires a new tag.
<<Interactions: public>>=
public :: assignment(=)
<<Interactions: interfaces>>=
interface assignment(=)
module procedure interaction_assign
end interface
<<Interactions: procedures>>=
subroutine interaction_assign (int_out, int_in)
type(interaction_t), intent(out) :: int_out
type(interaction_t), intent(in), target :: int_in
call interaction_set_tag (int_out)
int_out%state_matrix = int_in%state_matrix
int_out%n_in = int_in%n_in
int_out%n_out = int_in%n_out
int_out%n_vir = int_in%n_vir
int_out%n_tot = int_in%n_tot
if (allocated (int_in%p_is_known)) then
allocate (int_out%p_is_known (size (int_in%p_is_known)))
int_out%p_is_known = int_in%p_is_known
end if
if (allocated (int_in%p)) then
allocate (int_out%p (size (int_in%p)))
int_out%p = int_in%p
end if
if (allocated (int_in%source)) then
allocate (int_out%source (size (int_in%source)))
int_out%source = int_in%source
end if
if (allocated (int_in%parents)) then
allocate (int_out%parents (size (int_in%parents)))
int_out%parents = int_in%parents
end if
if (allocated (int_in%children)) then
allocate (int_out%children (size (int_in%children)))
int_out%children = int_in%children
end if
if (allocated (int_in%resonant)) then
allocate (int_out%resonant (size (int_in%resonant)))
int_out%resonant = int_in%resonant
end if
if (allocated (int_in%mask)) then
allocate (int_out%mask (size (int_in%mask)))
int_out%mask = int_in%mask
end if
if (allocated (int_in%hel_lock)) then
allocate (int_out%hel_lock (size (int_in%hel_lock)))
int_out%hel_lock = int_in%hel_lock
end if
int_out%update_state_matrix = int_in%update_state_matrix
int_out%update_values = int_in%update_values
end subroutine interaction_assign
@ %def interaction_assign
@
\subsection{Methods inherited from the state matrix member}
Until F2003 is standard, we cannot implement inheritance directly.
Therefore, we need wrappers for ``inherited'' methods.
Make a new branch in the state matrix if it does not yet exist. This
is not just a wrapper but it introduces the interaction mask: where a
quantum number is masked, it is not transferred but set undefined.
After this, the value array has to be updated.
<<Interactions: interaction: TBP>>=
procedure :: add_state => interaction_add_state
<<Interactions: procedures>>=
subroutine interaction_add_state &
- (int, qn, index, value, sum_values, counter_index, ignore_sub, me_index)
+ (int, qn, index, value, sum_values, counter_index, ignore_sub_for_qn, me_index)
class(interaction_t), intent(inout) :: int
type(quantum_numbers_t), dimension(:), intent(in) :: qn
integer, intent(in), optional :: index
complex(default), intent(in), optional :: value
logical, intent(in), optional :: sum_values
integer, intent(in), optional :: counter_index
- logical, intent(in), optional :: ignore_sub
+ logical, intent(in), optional :: ignore_sub_for_qn
integer, intent(out), optional :: me_index
type(quantum_numbers_t), dimension(size(qn)) :: qn_tmp
qn_tmp = qn
call qn_tmp%undefine (int%mask)
call int%state_matrix%add_state (qn_tmp, index, value, sum_values, &
- counter_index, ignore_sub, me_index)
+ counter_index, ignore_sub_for_qn, me_index)
int%update_values = .true.
end subroutine interaction_add_state
@ %def interaction_add_state
@ Freeze the quantum state: First collapse the quantum state, i.e.,
remove quantum numbers if any mask has changed, then fix the array of
value pointers.
<<Interactions: interaction: TBP>>=
procedure :: freeze => interaction_freeze
<<Interactions: procedures>>=
subroutine interaction_freeze (int)
class(interaction_t), intent(inout) :: int
if (int%update_state_matrix) then
call int%state_matrix%collapse (int%mask)
int%update_state_matrix = .false.
int%update_values = .true.
end if
if (int%update_values) then
call int%state_matrix%freeze ()
int%update_values = .false.
end if
end subroutine interaction_freeze
@ %def interaction_freeze
@ Return true if the state matrix is empty.
<<Interactions: interaction: TBP>>=
procedure :: is_empty => interaction_is_empty
<<Interactions: procedures>>=
pure function interaction_is_empty (int) result (flag)
logical :: flag
class(interaction_t), intent(in) :: int
flag = int%state_matrix%is_empty ()
end function interaction_is_empty
@ %def interaction_is_empty
@ Get the number of values stored in the state matrix:
<<Interactions: interaction: TBP>>=
procedure :: get_n_matrix_elements => &
interaction_get_n_matrix_elements
<<Interactions: procedures>>=
pure function interaction_get_n_matrix_elements (int) result (n)
integer :: n
class(interaction_t), intent(in) :: int
n = int%state_matrix%get_n_matrix_elements ()
end function interaction_get_n_matrix_elements
@ %def interaction_get_n_matrix_elements
@
<<Interactions: interaction: TBP>>=
procedure :: get_state_depth => interaction_get_state_depth
<<Interactions: procedures>>=
function interaction_get_state_depth (int) result (n)
integer :: n
class(interaction_t), intent(in) :: int
n = int%state_matrix%get_depth ()
end function interaction_get_state_depth
@ %def interaction_get_state_depth
@
<<Interactions: interaction: TBP>>=
procedure :: get_n_in_helicities => interaction_get_n_in_helicities
<<Interactions: procedures>>=
function interaction_get_n_in_helicities (int) result (n_hel)
integer :: n_hel
class(interaction_t), intent(in) :: int
type(interaction_t) :: int_copy
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask
type(quantum_numbers_t), dimension(:,:), allocatable :: qn
integer :: i
allocate (qn_mask (int%n_tot))
do i = 1, int%n_tot
if (i <= int%n_in) then
call qn_mask(i)%init (.true., .true., .false.)
else
call qn_mask(i)%init (.true., .true., .true.)
end if
end do
int_copy = int
call int_copy%set_mask (qn_mask)
call int_copy%freeze ()
allocate (qn (int_copy%state_matrix%get_n_matrix_elements (), &
int_copy%state_matrix%get_depth ()))
qn = int_copy%get_quantum_numbers ()
n_hel = 0
do i = 1, size (qn, dim=1)
- if (all (qn(i,:)%get_subtraction_index () == 0)) n_hel = n_hel + 1
+ if (all (qn(:, i)%get_subtraction_index () == 0)) n_hel = n_hel + 1
end do
call int_copy%final ()
deallocate (qn_mask)
deallocate (qn)
end function interaction_get_n_in_helicities
@ %def interaction_get_n_in_helicities
@ Get the size of the [[me]]-array of the associated state matrix
for debugging purposes
<<Interactions: interaction: TBP>>=
procedure :: get_me_size => interaction_get_me_size
<<Interactions: procedures>>=
pure function interaction_get_me_size (int) result (n)
integer :: n
class(interaction_t), intent(in) :: int
n = int%state_matrix%get_me_size ()
end function interaction_get_me_size
@ %def interaction_get_me_size
@ Get the norm of the state matrix (if the norm has been taken out, otherwise
this would be unity).
<<Interactions: interaction: TBP>>=
procedure :: get_norm => interaction_get_norm
<<Interactions: procedures>>=
pure function interaction_get_norm (int) result (norm)
real(default) :: norm
class(interaction_t), intent(in) :: int
norm = int%state_matrix%get_norm ()
end function interaction_get_norm
@ %def interaction_get_norm
@
<<Interactions: interaction: TBP>>=
procedure :: get_n_sub => interaction_get_n_sub
<<Interactions: procedures>>=
function interaction_get_n_sub (int) result (n_sub)
integer :: n_sub
class(interaction_t), intent(in) :: int
n_sub = int%state_matrix%get_n_sub ()
end function interaction_get_n_sub
@ %def interaction_get_n_sub
@ Get the quantum number array that corresponds to a given index.
<<Interactions: interaction: TBP>>=
generic :: get_quantum_numbers => get_quantum_numbers_single, &
get_quantum_numbers_all, &
get_quantum_numbers_all_qn_mask
procedure :: get_quantum_numbers_single => &
interaction_get_quantum_numbers_single
procedure :: get_quantum_numbers_all => &
interaction_get_quantum_numbers_all
procedure :: get_quantum_numbers_all_qn_mask => &
interaction_get_quantum_numbers_all_qn_mask
<<Interactions: procedures>>=
function interaction_get_quantum_numbers_single (int, i, by_me_index) result (qn)
type(quantum_numbers_t), dimension(:), allocatable :: qn
class(interaction_t), intent(in), target :: int
integer, intent(in) :: i
logical, intent(in), optional :: by_me_index
allocate (qn (int%state_matrix%get_depth ()))
qn = int%state_matrix%get_quantum_number (i, by_me_index)
end function interaction_get_quantum_numbers_single
function interaction_get_quantum_numbers_all (int) result (qn)
type(quantum_numbers_t), dimension(:,:), allocatable :: qn
class(interaction_t), intent(in), target :: int
integer :: i
<<Interactions: get quantum numbers all>>
<<Interactions: get quantum numbers all>>=
- allocate (qn (int%state_matrix%get_n_matrix_elements (), &
- int%state_matrix%get_depth()))
+ allocate (qn (int%state_matrix%get_depth(), &
+ int%state_matrix%get_n_matrix_elements ()))
do i = 1, int%state_matrix%get_n_matrix_elements ()
- qn (i, :) = int%state_matrix%get_quantum_number (i)
+ qn (:, i) = int%state_matrix%get_quantum_number (i)
end do
<<Interactions: procedures>>=
end function interaction_get_quantum_numbers_all
function interaction_get_quantum_numbers_all_qn_mask (int, qn_mask) &
result (qn)
type(quantum_numbers_t), dimension(:,:), allocatable :: qn
class(interaction_t), intent(in) :: int
type(quantum_numbers_mask_t), intent(in) :: qn_mask
integer :: n_redundant, n_all, n_me
integer :: i
type(quantum_numbers_t), dimension(:,:), allocatable :: qn_all
<<Interactions: get quantum numbers all qn mask>>
<<Interactions: get quantum numbers all qn mask>>=
call int%state_matrix%get_quantum_numbers (qn_all)
n_redundant = count (qn_all%are_redundant (qn_mask))
n_all = size (qn_all)
!!! Number of matrix elements = survivors / n_particles
n_me = (n_all - n_redundant) / int%state_matrix%get_depth ()
- allocate (qn (n_me, int%state_matrix%get_depth()))
+ allocate (qn (int%state_matrix%get_depth(), n_me))
do i = 1, n_me
if (.not. any (qn_all(i, :)%are_redundant (qn_mask))) &
- qn (i, :) = qn_all (i, :)
+ qn (:, i) = qn_all (i, :)
end do
<<Interactions: procedures>>=
end function interaction_get_quantum_numbers_all_qn_mask
@ %def interaction_get_quantum_numbers_single
@ %def interaction_get_quantum_numbers_all
@ %def interaction_get_quantum_numbers_all_qn_mask
@
@
<<Interactions: interaction: TBP>>=
procedure :: get_quantum_numbers_all_sub => interaction_get_quantum_numbers_all_sub
<<Interactions: procedures>>=
subroutine interaction_get_quantum_numbers_all_sub (int, qn)
class(interaction_t), intent(in) :: int
type(quantum_numbers_t), dimension(:,:), allocatable, intent(out) :: qn
integer :: i
<<Interactions: get quantum numbers all>>
end subroutine interaction_get_quantum_numbers_all_sub
@ %def interaction_get_quantum_numbers_all
@
<<Interactions: interaction: TBP>>=
procedure :: get_flavors => interaction_get_flavors
<<Interactions: procedures>>=
subroutine interaction_get_flavors (int, only_elementary, qn_mask, flv)
class(interaction_t), intent(in), target :: int
logical, intent(in) :: only_elementary
type(quantum_numbers_mask_t), intent(in), dimension(:), optional :: qn_mask
integer, intent(out), dimension(:,:), allocatable :: flv
call int%state_matrix%get_flavors (only_elementary, qn_mask, flv)
end subroutine interaction_get_flavors
@ %def interaction_get_flavors
@
<<Interactions: interaction: TBP>>=
procedure :: get_quantum_numbers_mask => interaction_get_quantum_numbers_mask
<<Interactions: procedures>>=
subroutine interaction_get_quantum_numbers_mask (int, qn_mask, qn)
class(interaction_t), intent(in) :: int
type(quantum_numbers_mask_t), intent(in) :: qn_mask
type(quantum_numbers_t), dimension(:,:), allocatable, intent(out) :: qn
integer :: n_redundant, n_all, n_me
integer :: i
type(quantum_numbers_t), dimension(:,:), allocatable :: qn_all
<<Interactions: get quantum numbers all qn mask>>
end subroutine interaction_get_quantum_numbers_mask
@ %def interaction_get_quantum_numbers_mask
@ Get the matrix element that corresponds to a set of quantum
numbers, a given index, or return the whole array.
<<Interactions: interaction: TBP>>=
generic :: get_matrix_element => get_matrix_element_single
generic :: get_matrix_element => get_matrix_element_array
procedure :: get_matrix_element_single => &
interaction_get_matrix_element_single
procedure :: get_matrix_element_array => &
interaction_get_matrix_element_array
<<Interactions: procedures>>=
elemental function interaction_get_matrix_element_single (int, i) result (me)
complex(default) :: me
class(interaction_t), intent(in) :: int
integer, intent(in) :: i
me = int%state_matrix%get_matrix_element (i)
end function interaction_get_matrix_element_single
@ %def interaction_get_matrix_element_single
<<Interactions: procedures>>=
function interaction_get_matrix_element_array (int) result (me)
complex(default), dimension(:), allocatable :: me
class(interaction_t), intent(in) :: int
allocate (me (int%get_n_matrix_elements ()))
me = int%state_matrix%get_matrix_element ()
end function interaction_get_matrix_element_array
@ %def interaction_get_matrix_element_array
@ Set the complex value(s) stored in the quantum state.
<<Interactions: interaction: TBP>>=
generic :: set_matrix_element => interaction_set_matrix_element_qn, &
interaction_set_matrix_element_all, &
interaction_set_matrix_element_array, &
interaction_set_matrix_element_single, &
interaction_set_matrix_element_clone
procedure :: interaction_set_matrix_element_qn
procedure :: interaction_set_matrix_element_all
procedure :: interaction_set_matrix_element_array
procedure :: interaction_set_matrix_element_single
procedure :: interaction_set_matrix_element_clone
@ %def interaction_set_matrix_element
@ Indirect access via the quantum number array:
<<Interactions: procedures>>=
subroutine interaction_set_matrix_element_qn (int, qn, val)
class(interaction_t), intent(inout) :: int
type(quantum_numbers_t), dimension(:), intent(in) :: qn
complex(default), intent(in) :: val
call int%state_matrix%set_matrix_element (qn, val)
end subroutine interaction_set_matrix_element_qn
@ %def interaction_set_matrix_element
@ Set all entries of the matrix-element array to a given value.
<<Interactions: procedures>>=
subroutine interaction_set_matrix_element_all (int, value)
class(interaction_t), intent(inout) :: int
complex(default), intent(in) :: value
call int%state_matrix%set_matrix_element (value)
end subroutine interaction_set_matrix_element_all
@ %def interaction_set_matrix_element_all
@ Set the matrix-element array directly.
<<Interactions: procedures>>=
subroutine interaction_set_matrix_element_array (int, value, range)
class(interaction_t), intent(inout) :: int
complex(default), intent(in), dimension(:) :: value
integer, intent(in), dimension(:), optional :: range
call int%state_matrix%set_matrix_element (value, range)
end subroutine interaction_set_matrix_element_array
pure subroutine interaction_set_matrix_element_single (int, i, value)
class(interaction_t), intent(inout) :: int
integer, intent(in) :: i
complex(default), intent(in) :: value
call int%state_matrix%set_matrix_element (i, value)
end subroutine interaction_set_matrix_element_single
@ %def interaction_set_matrix_element_array
@ %def interaction_set_matrix_element_single
@ Clone from another (matching) interaction.
<<Interactions: procedures>>=
subroutine interaction_set_matrix_element_clone (int, int1)
class(interaction_t), intent(inout) :: int
class(interaction_t), intent(in) :: int1
call int%state_matrix%set_matrix_element (int1%state_matrix)
end subroutine interaction_set_matrix_element_clone
@ %def interaction_set_matrix_element_clone
@
<<Interactions: interaction: TBP>>=
procedure :: set_only_matrix_element => interaction_set_only_matrix_element
<<Interactions: procedures>>=
subroutine interaction_set_only_matrix_element (int, i, value)
class(interaction_t), intent(inout) :: int
integer, intent(in) :: i
complex(default), intent(in) :: value
call int%set_matrix_element (cmplx (0, 0, default))
call int%set_matrix_element (i, value)
end subroutine interaction_set_only_matrix_element
@ %def interaction_set_only_matrix_element
@
<<Interactions: interaction: TBP>>=
procedure :: add_to_matrix_element => interaction_add_to_matrix_element
<<Interactions: procedures>>=
subroutine interaction_add_to_matrix_element (int, qn, value, match_only_flavor)
class(interaction_t), intent(inout) :: int
type(quantum_numbers_t), dimension(:), intent(in) :: qn
complex(default), intent(in) :: value
logical, intent(in), optional :: match_only_flavor
call int%state_matrix%add_to_matrix_element (qn, value, match_only_flavor)
end subroutine interaction_add_to_matrix_element
@ %def interaction_add_to_matrix_element
@ Get the indices of any diagonal matrix elements.
<<Interactions: interaction: TBP>>=
procedure :: get_diagonal_entries => interaction_get_diagonal_entries
<<Interactions: procedures>>=
subroutine interaction_get_diagonal_entries (int, i)
class(interaction_t), intent(in) :: int
integer, dimension(:), allocatable, intent(out) :: i
call int%state_matrix%get_diagonal_entries (i)
end subroutine interaction_get_diagonal_entries
@ %def interaction_get_diagonal_entries
@ Renormalize the state matrix by its trace, if nonzero. The renormalization
is reflected in the state-matrix norm.
<<Interactions: interaction: TBP>>=
procedure :: normalize_by_trace => interaction_normalize_by_trace
<<Interactions: procedures>>=
subroutine interaction_normalize_by_trace (int)
class(interaction_t), intent(inout) :: int
call int%state_matrix%normalize_by_trace ()
end subroutine interaction_normalize_by_trace
@ %def interaction_normalize_by_trace
@ Analogous, but renormalize by maximal (absolute) value.
<<Interactions: interaction: TBP>>=
procedure :: normalize_by_max => interaction_normalize_by_max
<<Interactions: procedures>>=
subroutine interaction_normalize_by_max (int)
class(interaction_t), intent(inout) :: int
call int%state_matrix%normalize_by_max ()
end subroutine interaction_normalize_by_max
@ %def interaction_normalize_by_max
@ Explicitly set the norm value (of the state matrix).
<<Interactions: interaction: TBP>>=
procedure :: set_norm => interaction_set_norm
<<Interactions: procedures>>=
subroutine interaction_set_norm (int, norm)
class(interaction_t), intent(inout) :: int
real(default), intent(in) :: norm
call int%state_matrix%set_norm (norm)
end subroutine interaction_set_norm
@ %def interaction_set_norm
@
<<Interactions: interaction: TBP>>=
procedure :: set_state_matrix => interaction_set_state_matrix
<<Interactions: procedures>>=
subroutine interaction_set_state_matrix (int, state)
class(interaction_t), intent(inout) :: int
type(state_matrix_t), intent(in) :: state
int%state_matrix = state
end subroutine interaction_set_state_matrix
@ %def interaction_set_state_matrix
@ Return the maximum absolute value of color indices.
<<Interactions: interaction: TBP>>=
procedure :: get_max_color_value => &
interaction_get_max_color_value
<<Interactions: procedures>>=
function interaction_get_max_color_value (int) result (cmax)
class(interaction_t), intent(in) :: int
integer :: cmax
cmax = int%state_matrix%get_max_color_value ()
end function interaction_get_max_color_value
@ %def interaction_get_max_color_value
@ Factorize the state matrix into single-particle state matrices, the
branch selection depending on a (random) value between 0 and 1;
optionally also return a correlated state matrix.
<<Interactions: interaction: TBP>>=
procedure :: factorize => interaction_factorize
<<Interactions: procedures>>=
subroutine interaction_factorize &
(int, mode, x, ok, single_state, correlated_state, qn_in)
class(interaction_t), intent(in), target :: int
integer, intent(in) :: mode
real(default), intent(in) :: x
logical, intent(out) :: ok
type(state_matrix_t), &
dimension(:), allocatable, intent(out) :: single_state
type(state_matrix_t), intent(out), optional :: correlated_state
type(quantum_numbers_t), dimension(:), intent(in), optional :: qn_in
call int%state_matrix%factorize &
(mode, x, ok, single_state, correlated_state, qn_in)
end subroutine interaction_factorize
@ %def interaction_factorize
@ Sum all matrix element values
<<Interactions: interaction: TBP>>=
procedure :: sum => interaction_sum
<<Interactions: procedures>>=
function interaction_sum (int) result (value)
class(interaction_t), intent(in) :: int
complex(default) :: value
value = int%state_matrix%sum ()
end function interaction_sum
@ %def interaction_sum
@ Append new states which are color-contracted versions of the
existing states. The matrix element index of each color contraction
coincides with the index of its origin, so no new matrix elements are
generated. After this operation, no [[freeze]] must be performed
anymore.
<<Interactions: interaction: TBP>>=
procedure :: add_color_contractions => &
interaction_add_color_contractions
<<Interactions: procedures>>=
subroutine interaction_add_color_contractions (int)
class(interaction_t), intent(inout) :: int
call int%state_matrix%add_color_contractions ()
end subroutine interaction_add_color_contractions
@ %def interaction_add_color_contractions
@ Multiply matrix elements from two interactions. Choose the elements
as given by the integer index arrays, multiply them and store the sum
of products in the indicated matrix element. The suffixes mean:
c=conjugate first factor; f=include weighting factor.
<<Interactions: interaction: TBP>>=
procedure :: evaluate_product => interaction_evaluate_product
procedure :: evaluate_product_cf => interaction_evaluate_product_cf
procedure :: evaluate_square_c => interaction_evaluate_square_c
procedure :: evaluate_sum => interaction_evaluate_sum
procedure :: evaluate_me_sum => interaction_evaluate_me_sum
<<Interactions: procedures>>=
pure subroutine interaction_evaluate_product &
(int, i, int1, int2, index1, index2)
class(interaction_t), intent(inout) :: int
integer, intent(in) :: i
type(interaction_t), intent(in) :: int1, int2
integer, dimension(:), intent(in) :: index1, index2
call int%state_matrix%evaluate_product &
(i, int1%state_matrix, int2%state_matrix, &
index1, index2)
end subroutine interaction_evaluate_product
pure subroutine interaction_evaluate_product_cf &
(int, i, int1, int2, index1, index2, factor)
class(interaction_t), intent(inout) :: int
integer, intent(in) :: i
type(interaction_t), intent(in) :: int1, int2
integer, dimension(:), intent(in) :: index1, index2
complex(default), dimension(:), intent(in) :: factor
call int%state_matrix%evaluate_product_cf &
(i, int1%state_matrix, int2%state_matrix, &
index1, index2, factor)
end subroutine interaction_evaluate_product_cf
pure subroutine interaction_evaluate_square_c (int, i, int1, index1)
class(interaction_t), intent(inout) :: int
integer, intent(in) :: i
type(interaction_t), intent(in) :: int1
integer, dimension(:), intent(in) :: index1
call int%state_matrix%evaluate_square_c (i, int1%state_matrix, index1)
end subroutine interaction_evaluate_square_c
pure subroutine interaction_evaluate_sum (int, i, int1, index1)
class(interaction_t), intent(inout) :: int
integer, intent(in) :: i
type(interaction_t), intent(in) :: int1
integer, dimension(:), intent(in) :: index1
call int%state_matrix%evaluate_sum (i, int1%state_matrix, index1)
end subroutine interaction_evaluate_sum
pure subroutine interaction_evaluate_me_sum (int, i, int1, index1)
class(interaction_t), intent(inout) :: int
integer, intent(in) :: i
type(interaction_t), intent(in) :: int1
integer, dimension(:), intent(in) :: index1
call int%state_matrix%evaluate_me_sum (i, int1%state_matrix, index1)
end subroutine interaction_evaluate_me_sum
@ %def interaction_evaluate_product
@ %def interaction_evaluate_product_cf
@ %def interaction_evaluate_square_c
@ %def interaction_evaluate_sum
@ %def interaction_evaluate_me_sum
@ Tag quantum numbers of the state matrix als part of the hard process, according
to the indices specified in [[tag]]. If no [[tag]] is given, all quantum numbers are
tagged as part of the hard process.
<<Interactions: interaction: TBP>>=
procedure :: tag_hard_process => interaction_tag_hard_process
<<Interactions: procedures>>=
subroutine interaction_tag_hard_process (int, tag)
class(interaction_t), intent(inout) :: int
integer, dimension(:), intent(in), optional :: tag
type(state_matrix_t) :: state
call int%state_matrix%tag_hard_process (state, tag)
call int%state_matrix%final ()
int%state_matrix = state
end subroutine interaction_tag_hard_process
@ %def interaction_tag_hard_process
\subsection{Accessing contents}
Return the integer tag.
<<Interactions: interaction: TBP>>=
procedure :: get_tag => interaction_get_tag
<<Interactions: procedures>>=
function interaction_get_tag (int) result (tag)
class(interaction_t), intent(in) :: int
integer :: tag
tag = int%tag
end function interaction_get_tag
@ %def interaction_get_tag
@ Return the number of particles.
<<Interactions: interaction: TBP>>=
procedure :: get_n_tot => interaction_get_n_tot
procedure :: get_n_in => interaction_get_n_in
procedure :: get_n_vir => interaction_get_n_vir
procedure :: get_n_out => interaction_get_n_out
<<Interactions: procedures>>=
pure function interaction_get_n_tot (object) result (n_tot)
class(interaction_t), intent(in) :: object
integer :: n_tot
n_tot = object%n_tot
end function interaction_get_n_tot
pure function interaction_get_n_in (object) result (n_in)
class(interaction_t), intent(in) :: object
integer :: n_in
n_in = object%n_in
end function interaction_get_n_in
pure function interaction_get_n_vir (object) result (n_vir)
class(interaction_t), intent(in) :: object
integer :: n_vir
n_vir = object%n_vir
end function interaction_get_n_vir
pure function interaction_get_n_out (object) result (n_out)
class(interaction_t), intent(in) :: object
integer :: n_out
n_out = object%n_out
end function interaction_get_n_out
@ %def interaction_get_n_tot
@ %def interaction_get_n_in interaction_get_n_vir interaction_get_n_out
@ Return a momentum index. The flags specify whether to keep/drop
incoming, virtual, or outgoing momenta. Check for illegal values.
<<Interactions: procedures>>=
function idx (int, i, outgoing)
integer :: idx
type(interaction_t), intent(in) :: int
integer, intent(in) :: i
logical, intent(in), optional :: outgoing
logical :: in, vir, out
if (present (outgoing)) then
in = .not. outgoing
vir = .false.
out = outgoing
else
in = .true.
vir = .true.
out = .true.
end if
idx = 0
if (in) then
if (vir) then
if (out) then
if (i <= int%n_tot) idx = i
else
if (i <= int%n_in + int%n_vir) idx = i
end if
else if (out) then
if (i <= int%n_in) then
idx = i
else if (i <= int%n_in + int%n_out) then
idx = int%n_vir + i
end if
else
if (i <= int%n_in) idx = i
end if
else if (vir) then
if (out) then
if (i <= int%n_vir + int%n_out) idx = int%n_in + i
else
if (i <= int%n_vir) idx = int%n_in + i
end if
else if (out) then
if (i <= int%n_out) idx = int%n_in + int%n_vir + i
end if
if (idx == 0) then
call int%basic_write ()
print *, i, in, vir, out
call msg_bug (" Momentum index is out of range for this interaction")
end if
end function idx
@ %def idx
@ Return all or just a specific four-momentum.
<<Interactions: interaction: TBP>>=
generic :: get_momenta => get_momenta_all, get_momenta_idx
procedure :: get_momentum => interaction_get_momentum
procedure :: get_momenta_all => interaction_get_momenta_all
procedure :: get_momenta_idx => interaction_get_momenta_idx
<<Interactions: procedures>>=
function interaction_get_momenta_all (int, outgoing) result (p)
class(interaction_t), intent(in) :: int
type(vector4_t), dimension(:), allocatable :: p
logical, intent(in), optional :: outgoing
integer :: i
if (present (outgoing)) then
if (outgoing) then
allocate (p (int%n_out))
else
allocate (p (int%n_in))
end if
else
allocate (p (int%n_tot))
end if
do i = 1, size (p)
p(i) = int%p(idx (int, i, outgoing))
end do
end function interaction_get_momenta_all
function interaction_get_momenta_idx (int, jj) result (p)
class(interaction_t), intent(in) :: int
type(vector4_t), dimension(:), allocatable :: p
integer, dimension(:), intent(in) :: jj
allocate (p (size (jj)))
p = int%p(jj)
end function interaction_get_momenta_idx
function interaction_get_momentum (int, i, outgoing) result (p)
class(interaction_t), intent(in) :: int
type(vector4_t) :: p
integer, intent(in) :: i
logical, intent(in), optional :: outgoing
p = int%p(idx (int, i, outgoing))
end function interaction_get_momentum
@ %def interaction_get_momenta interaction_get_momentum
@ This is a variant as a subroutine. Redundant, but the function
above fails at times for gfortran 4.5.0 (double allocation, compiler
bug).
<<Interactions: interaction: TBP>>=
procedure :: get_momenta_sub => interaction_get_momenta_sub
<<Interactions: procedures>>=
subroutine interaction_get_momenta_sub (int, p, outgoing)
class(interaction_t), intent(in) :: int
type(vector4_t), dimension(:), intent(out) :: p
logical, intent(in), optional :: outgoing
integer :: i
do i = 1, size (p)
p(i) = int%p(idx (int, i, outgoing))
end do
end subroutine interaction_get_momenta_sub
@ %def interaction_get_momenta_sub
@ Return a shallow copy of the state matrix:
<<Interactions: interaction: TBP>>=
procedure :: get_state_matrix_ptr => &
interaction_get_state_matrix_ptr
<<Interactions: procedures>>=
function interaction_get_state_matrix_ptr (int) result (state)
class(interaction_t), intent(in), target :: int
type(state_matrix_t), pointer :: state
state => int%state_matrix
end function interaction_get_state_matrix_ptr
@ %def interaction_get_state_matrix_ptr
@ Return the array of resonance flags
<<Interactions: interaction: TBP>>=
procedure :: get_resonance_flags => interaction_get_resonance_flags
<<Interactions: procedures>>=
function interaction_get_resonance_flags (int) result (resonant)
class(interaction_t), intent(in) :: int
logical, dimension(size(int%resonant)) :: resonant
resonant = int%resonant
end function interaction_get_resonance_flags
@ %def interaction_get_resonance_flags
@ Return the quantum-numbers mask (or part of it)
<<Interactions: interaction: TBP>>=
generic :: get_mask => get_mask_all, get_mask_slice
procedure :: get_mask_all => interaction_get_mask_all
procedure :: get_mask_slice => interaction_get_mask_slice
<<Interactions: procedures>>=
function interaction_get_mask_all (int) result (mask)
class(interaction_t), intent(in) :: int
type(quantum_numbers_mask_t), dimension(size(int%mask)) :: mask
mask = int%mask
end function interaction_get_mask_all
function interaction_get_mask_slice (int, index) result (mask)
class(interaction_t), intent(in) :: int
integer, dimension(:), intent(in) :: index
type(quantum_numbers_mask_t), dimension(size(index)) :: mask
mask = int%mask(index)
end function interaction_get_mask_slice
@ %def interaction_get_mask
@ Compute the invariant mass squared of the incoming particles (if any,
otherwise outgoing).
<<Interactions: public>>=
public :: interaction_get_s
<<Interactions: procedures>>=
function interaction_get_s (int) result (s)
real(default) :: s
type(interaction_t), intent(in) :: int
if (int%n_in /= 0) then
s = sum (int%p(:int%n_in)) ** 2
else
s = sum (int%p(int%n_vir + 1 : )) ** 2
end if
end function interaction_get_s
@ %def interaction_get_s
@ Compute the Lorentz transformation that transforms the incoming
particles from the center-of-mass frame to the lab frame where they
are given. If the c.m. mass squared is negative or zero, return the
identity.
<<Interactions: public>>=
public :: interaction_get_cm_transformation
<<Interactions: procedures>>=
function interaction_get_cm_transformation (int) result (lt)
type(lorentz_transformation_t) :: lt
type(interaction_t), intent(in) :: int
type(vector4_t) :: p_cm
real(default) :: s
if (int%n_in /= 0) then
p_cm = sum (int%p(:int%n_in))
else
p_cm = sum (int%p(int%n_vir+1:))
end if
s = p_cm ** 2
if (s > 0) then
lt = boost (p_cm, sqrt (s))
else
lt = identity
end if
end function interaction_get_cm_transformation
@ %def interaction_get_cm_transformation
@ Return flavor, momentum, and position of the first outgoing
unstable particle present in the interaction. Note that we need not
iterate through the state matrix; if there is an unstable particle, it
will be present in all state-matrix entries.
<<Interactions: public>>=
public :: interaction_get_unstable_particle
<<Interactions: procedures>>=
subroutine interaction_get_unstable_particle (int, flv, p, i)
type(interaction_t), intent(in), target :: int
type(flavor_t), intent(out) :: flv
type(vector4_t), intent(out) :: p
integer, intent(out) :: i
type(state_iterator_t) :: it
type(flavor_t), dimension(int%n_tot) :: flv_array
call it%init (int%state_matrix)
flv_array = it%get_flavor ()
do i = int%n_in + int%n_vir + 1, int%n_tot
if (.not. flv_array(i)%is_stable ()) then
flv = flv_array(i)
p = int%p(i)
return
end if
end do
end subroutine interaction_get_unstable_particle
@ %def interaction_get_unstable_particle
@ Return the complete set of \emph{outgoing} flavors, assuming that
the flavor quantum number is not suppressed.
<<Interactions: public>>=
public :: interaction_get_flv_out
<<Interactions: procedures>>=
subroutine interaction_get_flv_out (int, flv)
type(interaction_t), intent(in), target :: int
type(flavor_t), dimension(:,:), allocatable, intent(out) :: flv
type(state_iterator_t) :: it
type(flavor_t), dimension(:), allocatable :: flv_state
integer :: n_in, n_vir, n_out, n_tot, n_state, i
n_in = int%get_n_in ()
n_vir = int%get_n_vir ()
n_out = int%get_n_out ()
n_tot = int%get_n_tot ()
n_state = int%get_n_matrix_elements ()
allocate (flv (n_out, n_state))
allocate (flv_state (n_tot))
i = 1
call it%init (int%get_state_matrix_ptr ())
do while (it%is_valid ())
flv_state = it%get_flavor ()
flv(:,i) = flv_state(n_in + n_vir + 1 : )
i = i + 1
call it%advance ()
end do
end subroutine interaction_get_flv_out
@ %def interaction_get_flv_out
@ Determine the flavor content of the interaction. We analyze the
state matrix for this, and we select the outgoing particles of the
hard process only for the required mask, which indicates the particles
that can appear in any order in a matching event record.
We have to assume that any radiated particles (beam remnants) appear
at the beginning of the particles marked as outgoing.
<<Interactions: public>>=
public :: interaction_get_flv_content
<<Interactions: procedures>>=
subroutine interaction_get_flv_content (int, state_flv, n_out_hard)
type(interaction_t), intent(in), target :: int
type(state_flv_content_t), intent(out) :: state_flv
integer, intent(in) :: n_out_hard
logical, dimension(:), allocatable :: mask
integer :: n_tot
n_tot = int%get_n_tot ()
allocate (mask (n_tot), source = .false.)
mask(n_tot-n_out_hard + 1 : ) = .true.
call state_flv%fill (int%get_state_matrix_ptr (), mask)
end subroutine interaction_get_flv_content
@ %def interaction_get_flv_content
@
\subsection{Modifying contents}
Set the quantum numbers mask.
<<Interactions: interaction: TBP>>=
procedure :: set_mask => interaction_set_mask
<<Interactions: procedures>>=
subroutine interaction_set_mask (int, mask)
class(interaction_t), intent(inout) :: int
type(quantum_numbers_mask_t), dimension(:), intent(in) :: mask
if (size (int%mask) /= size (mask)) &
call msg_fatal ("Attempting to set mask with unfitting size!")
int%mask = mask
int%update_state_matrix = .true.
end subroutine interaction_set_mask
@ %def interaction_set_mask
@ Merge a particular mask entry, respecting a possible helicity lock for this
entry. We apply an OR relation, which means that quantum numbers are
summed over if either of the two masks requires it.
<<Interactions: procedures>>=
subroutine interaction_merge_mask_entry (int, i, mask)
type(interaction_t), intent(inout) :: int
integer, intent(in) :: i
type(quantum_numbers_mask_t), intent(in) :: mask
type(quantum_numbers_mask_t) :: mask_tmp
integer :: ii
ii = idx (int, i)
if (int%mask(ii) .neqv. mask) then
int%mask(ii) = int%mask(ii) .or. mask
if (int%hel_lock(ii) /= 0) then
call mask_tmp%assign (mask, helicity=.true.)
int%mask(int%hel_lock(ii)) = int%mask(int%hel_lock(ii)) .or. mask_tmp
end if
end if
int%update_state_matrix = .true.
end subroutine interaction_merge_mask_entry
@ %def interaction_merge_mask_entry
@ Fill the momenta array, do not care about the quantum numbers of
particles.
<<Interactions: interaction: TBP>>=
procedure :: reset_momenta => interaction_reset_momenta
procedure :: set_momenta => interaction_set_momenta
procedure :: set_momentum => interaction_set_momentum
<<Interactions: procedures>>=
subroutine interaction_reset_momenta (int)
class(interaction_t), intent(inout) :: int
int%p = vector4_null
int%p_is_known = .true.
end subroutine interaction_reset_momenta
subroutine interaction_set_momenta (int, p, outgoing)
class(interaction_t), intent(inout) :: int
type(vector4_t), dimension(:), intent(in) :: p
logical, intent(in), optional :: outgoing
integer :: i, index
do i = 1, size (p)
index = idx (int, i, outgoing)
int%p(index) = p(i)
int%p_is_known(index) = .true.
end do
end subroutine interaction_set_momenta
subroutine interaction_set_momentum (int, p, i, outgoing)
class(interaction_t), intent(inout) :: int
type(vector4_t), intent(in) :: p
integer, intent(in) :: i
logical, intent(in), optional :: outgoing
integer :: index
index = idx (int, i, outgoing)
int%p(index) = p
int%p_is_known(index) = .true.
end subroutine interaction_set_momentum
@ %def interaction_reset_momenta
@ %def interaction_set_momenta interaction_set_momentum
@ This more sophisticated version of setting values is used for
structure functions, in particular if nontrivial flavor, color, and
helicity may be present: set values selectively for the given flavors.
If there is more than one flavor, scan the interaction and check for a
matching flavor at the specified particle location. If it matches,
insert the value that corresponds to this flavor.
<<Interactions: public>>=
public :: interaction_set_flavored_values
<<Interactions: procedures>>=
subroutine interaction_set_flavored_values (int, value, flv_in, pos)
type(interaction_t), intent(inout) :: int
complex(default), dimension(:), intent(in) :: value
type(flavor_t), dimension(:), intent(in) :: flv_in
integer, intent(in) :: pos
type(state_iterator_t) :: it
type(flavor_t) :: flv
integer :: i
if (size (value) == 1) then
call int%set_matrix_element (value(1))
else
call it%init (int%state_matrix)
do while (it%is_valid ())
flv = it%get_flavor (pos)
SCAN_FLV: do i = 1, size (value)
if (flv == flv_in(i)) then
call it%set_matrix_element (value(i))
exit SCAN_FLV
end if
end do SCAN_FLV
call it%advance ()
end do
end if
end subroutine interaction_set_flavored_values
@ %def interaction_set_flavored_values
@
\subsection{Handling Linked interactions}
Store relations between corresponding particles within one
interaction. The first particle is the parent, the second one the
child. Links are established in both directions.
These relations have no effect on the propagation of momenta etc.,
they are rather used for mother-daughter relations in event output.
<<Interactions: interaction: TBP>>=
procedure :: relate => interaction_relate
<<Interactions: procedures>>=
subroutine interaction_relate (int, i1, i2)
class(interaction_t), intent(inout), target :: int
integer, intent(in) :: i1, i2
if (i1 /= 0 .and. i2 /= 0) then
call int%children(i1)%append (i2)
call int%parents(i2)%append (i1)
end if
end subroutine interaction_relate
@ %def interaction_relate
@ Transfer internal parent-child relations defined within interaction
[[int1]] to a new interaction [[int]] where the particle indices are
mapped to. Some particles in [[int1]] may have no image in [[int]].
In that case, a child entry maps to zero, and we skip this relation.
Also transfer resonance flags.
<<Interactions: interaction: TBP>>=
procedure :: transfer_relations => interaction_transfer_relations
<<Interactions: procedures>>=
subroutine interaction_transfer_relations (int1, int2, map)
class(interaction_t), intent(in) :: int1
class(interaction_t), intent(inout), target :: int2
integer, dimension(:), intent(in) :: map
integer :: i, j, k
do i = 1, size (map)
do j = 1, int1%parents(i)%get_length ()
k = int1%parents(i)%get_link (j)
call int2%relate (map(k), map(i))
end do
if (map(i) /= 0) then
int2%resonant(map(i)) = int1%resonant(i)
end if
end do
end subroutine interaction_transfer_relations
@ %def interaction_transfer_relations
@ Make up internal parent-child relations for the particle(s) that are
connected to a new interaction [[int]].
If [[resonant]] is defined and true, the connections are marked as
resonant in the result interaction
<<Interactions: interaction: TBP>>=
procedure :: relate_connections => interaction_relate_connections
<<Interactions: procedures>>=
subroutine interaction_relate_connections &
(int, int_in, connection_index, &
map, map_connections, resonant)
class(interaction_t), intent(inout), target :: int
class(interaction_t), intent(in) :: int_in
integer, dimension(:), intent(in) :: connection_index
integer, dimension(:), intent(in) :: map, map_connections
logical, intent(in), optional :: resonant
logical :: reson
integer :: i, j, i2, k2
reson = .false.; if (present (resonant)) reson = resonant
do i = 1, size (map_connections)
k2 = connection_index(i)
do j = 1, int_in%children(k2)%get_length ()
i2 = int_in%children(k2)%get_link (j)
call int%relate (map_connections(i), map(i2))
end do
int%resonant(map_connections(i)) = reson
end do
end subroutine interaction_relate_connections
@ %def interaction_relate_connections.
@ Return the number of source/target links of the internal connections of
particle [[i]].
<<Interactions: public>>=
public :: interaction_get_n_children
public :: interaction_get_n_parents
<<Interactions: procedures>>=
function interaction_get_n_children (int, i) result (n)
integer :: n
type(interaction_t), intent(in) :: int
integer, intent(in) :: i
n = int%children(i)%get_length ()
end function interaction_get_n_children
function interaction_get_n_parents (int, i) result (n)
integer :: n
type(interaction_t), intent(in) :: int
integer, intent(in) :: i
n = int%parents(i)%get_length ()
end function interaction_get_n_parents
@ %def interaction_get_n_children interaction_get_n_parents
@ Return the source/target links of the internal connections of
particle [[i]] as an array.
<<Interactions: public>>=
public :: interaction_get_children
public :: interaction_get_parents
<<Interactions: procedures>>=
function interaction_get_children (int, i) result (idx)
integer, dimension(:), allocatable :: idx
type(interaction_t), intent(in) :: int
integer, intent(in) :: i
integer :: k, l
l = int%children(i)%get_length ()
allocate (idx (l))
do k = 1, l
idx(k) = int%children(i)%get_link (k)
end do
end function interaction_get_children
function interaction_get_parents (int, i) result (idx)
integer, dimension(:), allocatable :: idx
type(interaction_t), intent(in) :: int
integer, intent(in) :: i
integer :: k, l
l = int%parents(i)%get_length ()
allocate (idx (l))
do k = 1, l
idx(k) = int%parents(i)%get_link (k)
end do
end function interaction_get_parents
@ %def interaction_get_children interaction_get_parents
@ Add a source link from an interaction to a corresponding particle
within another interaction. These links affect the propagation of
particles: the two linked particles are considered as the same
particle, outgoing and incoming.
<<Interactions: interaction: TBP>>=
procedure :: set_source_link => interaction_set_source_link
<<Interactions: procedures>>=
subroutine interaction_set_source_link (int, i, int1, i1)
class(interaction_t), intent(inout) :: int
integer, intent(in) :: i
class(interaction_t), intent(in), target :: int1
integer, intent(in) :: i1
if (i /= 0) call external_link_set (int%source(i), int1, i1)
end subroutine interaction_set_source_link
@ %def interaction_set_source_link
@ Reassign links to a new interaction (which is an image of the
current interaction).
<<Interactions: public>>=
public :: interaction_reassign_links
<<Interactions: procedures>>=
subroutine interaction_reassign_links (int, int_src, int_target)
type(interaction_t), intent(inout) :: int
type(interaction_t), intent(in) :: int_src
type(interaction_t), intent(in), target :: int_target
integer :: i
if (allocated (int%source)) then
do i = 1, size (int%source)
call external_link_reassign (int%source(i), int_src, int_target)
end do
end if
end subroutine interaction_reassign_links
@ %def interaction_reassign_links
@ Since links are one-directional, if we want to follow them backwards
we have to scan all possibilities. This procedure returns the index
of the particle within [[int]] which points to the particle [[i1]]
within interaction [[int1]]. If unsuccessful, return zero.
<<Interactions: public>>=
public :: interaction_find_link
<<Interactions: procedures>>=
function interaction_find_link (int, int1, i1) result (i)
integer :: i
type(interaction_t), intent(in) :: int, int1
integer, intent(in) :: i1
type(interaction_t), pointer :: int_tmp
do i = 1, int%n_tot
int_tmp => external_link_get_ptr (int%source(i))
if (int_tmp%tag == int1%tag) then
if (external_link_get_index (int%source(i)) == i1) return
end if
end do
i = 0
end function interaction_find_link
@ %def interaction_find_link
@ The inverse: return interaction pointer and index for the ultimate source of
[[i]] within [[int]].
<<Interactions: interaction: TBP>>=
procedure :: find_source => interaction_find_source
<<Interactions: procedures>>=
subroutine interaction_find_source (int, i, int1, i1)
class(interaction_t), intent(in) :: int
integer, intent(in) :: i
type(interaction_t), intent(out), pointer :: int1
integer, intent(out) :: i1
type(external_link_t) :: link
link = interaction_get_ultimate_source (int, i)
int1 => external_link_get_ptr (link)
i1 = external_link_get_index (link)
end subroutine interaction_find_source
@ %def interaction_find_source
@ Follow source links recursively to return the ultimate source of a particle.
<<Interactions: procedures>>=
function interaction_get_ultimate_source (int, i) result (link)
type(external_link_t) :: link
type(interaction_t), intent(in) :: int
integer, intent(in) :: i
type(interaction_t), pointer :: int_src
integer :: i_src
link = int%source(i)
if (external_link_is_set (link)) then
do
int_src => external_link_get_ptr (link)
i_src = external_link_get_index (link)
if (external_link_is_set (int_src%source(i_src))) then
link = int_src%source(i_src)
else
exit
end if
end do
end if
end function interaction_get_ultimate_source
@ %def interaction_get_ultimate_source
@ Update mask entries by merging them with corresponding masks in
interactions linked to the current one. The mask determines quantum
numbers which are summed over.
Note that both the mask of the current interaction and the mask of the
linked interaction are updated (side effect!). This ensures that both
agree for the linked particle.
<<Interactions: public>>=
public :: interaction_exchange_mask
<<Interactions: procedures>>=
subroutine interaction_exchange_mask (int)
type(interaction_t), intent(inout) :: int
integer :: i, index_link
type(interaction_t), pointer :: int_link
do i = 1, int%n_tot
if (external_link_is_set (int%source(i))) then
int_link => external_link_get_ptr (int%source(i))
index_link = external_link_get_index (int%source(i))
call interaction_merge_mask_entry &
(int, i, int_link%mask(index_link))
call interaction_merge_mask_entry &
(int_link, index_link, int%mask(i))
end if
end do
call int%freeze ()
end subroutine interaction_exchange_mask
@ %def interaction_exchange_mask
@ Copy momenta from interactions linked to the current one.
<<Interactions: interaction: TBP>>=
procedure :: receive_momenta => interaction_receive_momenta
<<Interactions: procedures>>=
subroutine interaction_receive_momenta (int)
class(interaction_t), intent(inout) :: int
integer :: i, index_link
type(interaction_t), pointer :: int_link
do i = 1, int%n_tot
if (external_link_is_set (int%source(i))) then
int_link => external_link_get_ptr (int%source(i))
index_link = external_link_get_index (int%source(i))
call int%set_momentum (int_link%p(index_link), i)
end if
end do
end subroutine interaction_receive_momenta
@ %def interaction_receive_momenta
@ The inverse operation: Copy momenta back to the interactions linked
to the current one.
<<Interactions: public>>=
public :: interaction_send_momenta
<<Interactions: procedures>>=
subroutine interaction_send_momenta (int)
type(interaction_t), intent(in) :: int
integer :: i, index_link
type(interaction_t), pointer :: int_link
do i = 1, int%n_tot
if (external_link_is_set (int%source(i))) then
int_link => external_link_get_ptr (int%source(i))
index_link = external_link_get_index (int%source(i))
call int_link%set_momentum (int%p(i), index_link)
end if
end do
end subroutine interaction_send_momenta
@ %def interaction_send_momenta
@ For numerical comparisons: pacify all momenta in an interaction.
<<Interactions: public>>=
public :: interaction_pacify_momenta
<<Interactions: procedures>>=
subroutine interaction_pacify_momenta (int, acc)
type(interaction_t), intent(inout) :: int
real(default), intent(in) :: acc
integer :: i
do i = 1, int%n_tot
call pacify (int%p(i), acc)
end do
end subroutine interaction_pacify_momenta
@ %def interaction_pacify_momenta
@ For each subtraction entry starting from [[SUB = 0]], we duplicate the original state matrix entries as is.
<<Interactions: interaction: TBP>>=
procedure :: declare_subtraction => interaction_declare_subtraction
<<Interactions: procedures>>=
subroutine interaction_declare_subtraction (int, n_sub)
class(interaction_t), intent(inout), target :: int
integer, intent(in) :: n_sub
integer :: i_sub
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(:), allocatable :: qn
type(state_matrix_t) :: state_matrix
call state_matrix%init (store_values = .true.)
allocate (qn (int%get_state_depth ()))
do i_sub = 0, n_sub
call it%init (int%state_matrix)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
call qn%set_subtraction_index (i_sub)
call state_matrix%add_state (qn, value = it%get_matrix_element ())
call it%advance ()
end do
end do
call state_matrix%freeze ()
call state_matrix%set_n_sub ()
call int%state_matrix%final ()
int%state_matrix = state_matrix
end subroutine interaction_declare_subtraction
@ %def interaction_declare_subtraction
@
\subsection{Recovering connections}
When creating an evaluator for two interactions, we have to know by
which particles they are connected. The connection indices can be
determined if we have two linked interactions. We assume that
[[int1]] is the source and [[int2]] the target, so the connections of
interest are stored within [[int2]]. A connection is found if either the
source is [[int1]], or the (ultimate)
source of a particle within [[int2]] coincides with the (ultimate) source of a
aparticle within [[int1]]. The result is an array of
index pairs.
To make things simple, we scan the interaction twice,
once for counting hits, then allocate the array, then scan again and
store the connections.
The connections are scanned for [[int2]], which has sources in [[int1]]. It
may happen that the order of connections is interchanged (crossed). We
require the indices in [[int1]] to be sorted, so we reorder both index arrays
correspondingly before returning them. (After this, the indices in [[int2]]
may be out of order.)
<<Interactions: public>>=
public :: find_connections
<<Interactions: procedures>>=
subroutine find_connections (int1, int2, n, connection_index)
class(interaction_t), intent(in) :: int1, int2
integer, intent(out) :: n
integer, dimension(:,:), intent(out), allocatable :: connection_index
integer, dimension(:,:), allocatable :: conn_index_tmp
integer, dimension(:), allocatable :: ordering
integer :: i, j, k
type(external_link_t) :: link1, link2
type(interaction_t), pointer :: int_link1, int_link2
n = 0
do i = 1, size (int2%source)
link2 = interaction_get_ultimate_source (int2, i)
if (external_link_is_set (link2)) then
int_link2 => external_link_get_ptr (link2)
if (int_link2%tag == int1%tag) then
n = n + 1
else
k = external_link_get_index (link2)
do j = 1, size (int1%source)
link1 = interaction_get_ultimate_source (int1, j)
if (external_link_is_set (link1)) then
int_link1 => external_link_get_ptr (link1)
if (int_link1%tag == int_link2%tag) then
if (external_link_get_index (link1) == k) &
n = n + 1
end if
end if
end do
end if
end if
end do
allocate (conn_index_tmp (n, 2))
n = 0
do i = 1, size (int2%source)
link2 = interaction_get_ultimate_source (int2, i)
if (external_link_is_set (link2)) then
int_link2 => external_link_get_ptr (link2)
if (int_link2%tag == int1%tag) then
n = n + 1
conn_index_tmp(n,1) = external_link_get_index (int2%source(i))
conn_index_tmp(n,2) = i
else
k = external_link_get_index (link2)
do j = 1, size (int1%source)
link1 = interaction_get_ultimate_source (int1, j)
if (external_link_is_set (link1)) then
int_link1 => external_link_get_ptr (link1)
if (int_link1%tag == int_link2%tag) then
if (external_link_get_index (link1) == k) then
n = n + 1
conn_index_tmp(n,1) = j
conn_index_tmp(n,2) = i
end if
end if
end if
end do
end if
end if
end do
allocate (connection_index (n, 2))
if (n > 1) then
allocate (ordering (n))
ordering = order (conn_index_tmp(:,1))
connection_index = conn_index_tmp(ordering,:)
else
connection_index = conn_index_tmp
end if
end subroutine find_connections
@ %def find_connections
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[interactions_ut.f90]]>>=
<<File header>>
module interactions_ut
use unit_tests
use interactions_uti
<<Standard module head>>
<<Interactions: public test>>
contains
<<Interactions: test driver>>
end module interactions_ut
@ %def interactions_ut
@
<<[[interactions_uti.f90]]>>=
<<File header>>
module interactions_uti
<<Use kinds>>
use lorentz
use flavors
use colors
use helicities
use quantum_numbers
use state_matrices
use interactions
<<Standard module head>>
<<Interactions: test declarations>>
contains
<<Interactions: tests>>
end module interactions_uti
@ %def interactions_ut
@ API: driver for the unit tests below.
<<Interactions: public test>>=
public :: interaction_test
<<Interactions: test driver>>=
subroutine interaction_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Interactions: execute tests>>
end subroutine interaction_test
@ %def interaction_test
@ Generate an interaction of a polarized virtual photon and a colored
quark which may be either up or down. Remove the quark polarization.
Generate another interaction for the quark radiating a photon and link
this to the first interation. The radiation ignores polarization;
transfer this information to the first interaction to simplify it.
Then, transfer the momentum to the radiating quark and perform a
splitting.
<<Interactions: execute tests>>=
call test (interaction_1, "interaction_1", &
"check interaction setup", &
u, results)
<<Interactions: test declarations>>=
public :: interaction_1
<<Interactions: tests>>=
subroutine interaction_1 (u)
integer, intent(in) :: u
type(interaction_t), target :: int, rad
type(vector4_t), dimension(3) :: p
type(quantum_numbers_mask_t), dimension(3) :: mask
p(2) = vector4_moving (500._default, 500._default, 1)
p(3) = vector4_moving (500._default,-500._default, 1)
p(1) = p(2) + p(3)
write (u, "(A)") "* Test output: interaction"
write (u, "(A)") "* Purpose: check routines for interactions"
write (u, "(A)")
call int%basic_init (1, 0, 2, set_relations=.true., &
store_values = .true. )
call int_set (int, 1, -1, 1, 1, &
cmplx (0.3_default, 0.1_default, kind=default))
call int_set (int, 1, -1,-1, 1, &
cmplx (0.5_default,-0.7_default, kind=default))
call int_set (int, 1, 1, 1, 1, &
cmplx (0.1_default, 0._default, kind=default))
call int_set (int, -1, 1, -1, 2, &
cmplx (0.4_default, -0.1_default, kind=default))
call int_set (int, 1, 1, 1, 2, &
cmplx (0.2_default, 0._default, kind=default))
call int%freeze ()
call int%set_momenta (p)
mask = quantum_numbers_mask (.false.,.false., [.true.,.true.,.true.])
call rad%basic_init (1, 0, 2, &
mask=mask, set_relations=.true., store_values = .true.)
call rad_set (1)
call rad_set (2)
call rad%set_source_link (1, int, 2)
call interaction_exchange_mask (rad)
call rad%receive_momenta ()
p(1) = rad%get_momentum (1)
p(2) = 0.4_default * p(1)
p(3) = p(1) - p(2)
call rad%set_momenta (p(2:3), outgoing=.true.)
call int%freeze ()
call rad%freeze ()
call rad%set_matrix_element &
(cmplx (0._default, 0._default, kind=default))
call int%basic_write (u)
write (u, "(A)")
call rad%basic_write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call int%final ()
call rad%final ()
write (u, "(A)")
write (u, "(A)") "* Test interaction_1: successful."
contains
subroutine int_set (int, h1, h2, hq, q, val)
type(interaction_t), target, intent(inout) :: int
integer, intent(in) :: h1, h2, hq, q
type(flavor_t), dimension(3) :: flv
type(color_t), dimension(3) :: col
type(helicity_t), dimension(3) :: hel
type(quantum_numbers_t), dimension(3) :: qn
complex(default), intent(in) :: val
call flv%init ([21, q, -q])
call col(2)%init_col_acl (5, 0)
call col(3)%init_col_acl (0, 5)
call hel%init ([h1, hq, -hq], [h2, hq, -hq])
call qn%init (flv, col, hel)
call int%add_state (qn)
call int%set_matrix_element (val)
end subroutine int_set
subroutine rad_set (q)
integer, intent(in) :: q
type(flavor_t), dimension(3) :: flv
type(quantum_numbers_t), dimension(3) :: qn
call flv%init ([ q, q, 21 ])
call qn%init (flv)
call rad%add_state (qn)
end subroutine rad_set
end subroutine interaction_1
@ %def interaction_1
@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Matrix element evaluation}
The [[evaluator_t]] type is an extension of the [[interaction_t]]
type. It represents either a density matrix as the square of a
transition matrix element, or the product of two density matrices.
Usually, some quantum numbers are summed over in the result.
The [[interaction_t]] subobject represents a multi-particle
interaction with incoming, virtual, and outgoing particles and the
associated (not necessarily diagonal) density matrix of quantum
state. When the evaluator is initialized, this interaction is
constructed from the input interaction(s).
In addition, the initialization process sets up a multiplication
table. For each matrix element of the result, it states which matrix
elements are to be taken from the input interaction(s), multiplied
(optionally, with an additional weight factor) and summed over.
Eventually, to a processes we associate a chain of evaluators which
are to be evaluated sequentially. The physical event and its matrix
element value(s) can be extracted from the last evaluator in such a
chain.
Evaluators are constructed only once (as long as this is possible)
during an initialization step. Then, for each event, momenta
are computed and transferred among evaluators using the links within
the interaction subobject. The multiplication tables enable fast
evaluation of the result without looking at quantum numbers anymore.
<<[[evaluators.f90]]>>=
<<File header>>
module evaluators
<<Use kinds>>
<<Use strings>>
use io_units
use format_defs, only: FMT_19
- use physics_defs, only: n_beam_structure_int
+ use physics_defs, only: n_beams_rescaled
use diagnostics
use lorentz
use flavors
use colors
use helicities
use quantum_numbers
use state_matrices
use interactions
<<Standard module head>>
<<Evaluators: public>>
<<Evaluators: parameters>>
<<Evaluators: types>>
<<Evaluators: interfaces>>
contains
<<Evaluators: procedures>>
end module evaluators
@ %def evaluators
@
\subsection{Array of pairings}
The evaluator contains an array of [[pairing_array]] objects. This
makes up the multiplication table.
Each pairing array contains two list of matrix element indices and a
list of numerical factors. The matrix element indices correspond to
the input interactions. The corresponding matrix elements are to be
multiplied and optionally multiplied by a factor. The results are
summed over to yield one specific matrix element of the result
evaluator.
<<Evaluators: types>>=
type :: pairing_array_t
integer, dimension(:), allocatable :: i1, i2
complex(default), dimension(:), allocatable :: factor
end type pairing_array_t
@ %def pairing_array_t
<<Evaluators: procedures>>=
elemental subroutine pairing_array_init (pa, n, has_i2, has_factor)
type(pairing_array_t), intent(out) :: pa
integer, intent(in) :: n
logical, intent(in) :: has_i2, has_factor
allocate (pa%i1 (n))
if (has_i2) allocate (pa%i2 (n))
if (has_factor) allocate (pa%factor (n))
end subroutine pairing_array_init
@ %def pairing_array_init
@
<<Evaluators: public>>=
public :: pairing_array_write
<<Evaluators: procedures>>=
subroutine pairing_array_write (pa, unit)
type(pairing_array_t), intent(in) :: pa
integer, intent(in), optional :: unit
integer :: i, u
u = given_output_unit (unit); if (u < 0) return
write (u, "(A)", advance = "no") "["
if (allocated (pa%i1)) then
write (u, "(I0,A)", advance = "no") pa%i1, ","
else
write (u, "(A)", advance = "no") "x,"
end if
if (allocated (pa%i2)) then
write (u, "(I0,A)", advance = "no") pa%i1, ","
else
write (u, "(A)", advance = "no") "x,"
end if
write (u, "(A)", advance = "no") "]"
if (allocated (pa%factor)) then
write (u, "(A,F5.4,A,F5.4,A)") ";(", &
real(pa%factor), ",", aimag(pa%factor), ")]"
else
write (u, "(A)") ""
end if
end subroutine pairing_array_write
@ %def pairing_array_write
@
\subsection{The evaluator type}
Possible variants of evaluators:
<<Evaluators: parameters>>=
integer, parameter :: &
EVAL_UNDEFINED = 0, &
EVAL_PRODUCT = 1, &
EVAL_SQUARED_FLOWS = 2, &
EVAL_SQUARE_WITH_COLOR_FACTORS = 3, &
EVAL_COLOR_CONTRACTION = 4, &
EVAL_IDENTITY = 5, &
EVAL_QN_SUM = 6
@ %def EVAL_PRODUCT EVAL_SQUARED_FLOWS EVAL_SQUARE_WITH_COLOR_FACTORS
@ %def EVAL_COLOR_CONTRACTION EVAL_QN_SUM
@ The evaluator type contains the result interaction and an array of
pairing lists, one for each matrix element in the result interaction.
<<Evaluators: public>>=
public :: evaluator_t
<<Evaluators: types>>=
type, extends (interaction_t) :: evaluator_t
private
integer :: type = EVAL_UNDEFINED
class(interaction_t), pointer :: int_in1 => null ()
class(interaction_t), pointer :: int_in2 => null ()
type(pairing_array_t), dimension(:), allocatable :: pairing_array
contains
<<Evaluators: evaluator: TBP>>
end type evaluator_t
@ %def evaluator_t
@ Output.
<<Evaluators: evaluator: TBP>>=
procedure :: write => evaluator_write
<<Evaluators: procedures>>=
subroutine evaluator_write (eval, unit, &
verbose, show_momentum_sum, show_mass, show_state, show_table, &
col_verbose, testflag)
class(evaluator_t), intent(in) :: eval
integer, intent(in), optional :: unit
logical, intent(in), optional :: verbose, show_momentum_sum, show_mass
logical, intent(in), optional :: show_state, show_table, col_verbose
logical, intent(in), optional :: testflag
logical :: conjugate, square, show_tab
integer :: u
u = given_output_unit (unit); if (u < 0) return
show_tab = .true.; if (present (show_table)) show_tab = .false.
call eval%basic_write &
(unit, verbose, show_momentum_sum, show_mass, &
show_state, col_verbose, testflag)
if (show_tab) then
write (u, "(1x,A)") "Matrix-element multiplication"
write (u, "(2x,A)", advance="no") "Input interaction 1:"
if (associated (eval%int_in1)) then
write (u, "(1x,I0)") eval%int_in1%get_tag ()
else
write (u, "(A)") " [undefined]"
end if
write (u, "(2x,A)", advance="no") "Input interaction 2:"
if (associated (eval%int_in2)) then
write (u, "(1x,I0)") eval%int_in2%get_tag ()
else
write (u, "(A)") " [undefined]"
end if
select case (eval%type)
case (EVAL_SQUARED_FLOWS, EVAL_SQUARE_WITH_COLOR_FACTORS)
conjugate = .true.
square = .true.
case (EVAL_IDENTITY)
write (u, "(1X,A)") "Identity evaluator, pairing array unused"
return
case default
conjugate = .false.
square = .false.
end select
call eval%write_pairing_array (conjugate, square, u)
end if
end subroutine evaluator_write
@ %def evaluator_write
@
<<Evaluators: evaluator: TBP>>=
procedure :: write_pairing_array => evaluator_write_pairing_array
<<Evaluators: procedures>>=
subroutine evaluator_write_pairing_array (eval, conjugate, square, unit)
class(evaluator_t), intent(in) :: eval
logical, intent(in) :: conjugate, square
integer, intent(in), optional :: unit
integer :: u, i, j
u = given_output_unit (unit); if (u < 0) return
if (allocated (eval%pairing_array)) then
do i = 1, size (eval%pairing_array)
write (u, "(2x,A,I0,A)") "ME(", i, ") = "
do j = 1, size (eval%pairing_array(i)%i1)
write (u, "(4x,A)", advance="no") "+"
if (allocated (eval%pairing_array(i)%i2)) then
write (u, "(1x,A,I0,A)", advance="no") &
"ME1(", eval%pairing_array(i)%i1(j), ")"
if (conjugate) then
write (u, "(A)", advance="no") "* x"
else
write (u, "(A)", advance="no") " x"
end if
write (u, "(1x,A,I0,A)", advance="no") &
"ME2(", eval%pairing_array(i)%i2(j), ")"
else if (square) then
write (u, "(1x,A)", advance="no") "|"
write (u, "(A,I0,A)", advance="no") &
"ME1(", eval%pairing_array(i)%i1(j), ")"
write (u, "(A)", advance="no") "|^2"
else
write (u, "(1x,A,I0,A)", advance="no") &
"ME1(", eval%pairing_array(i)%i1(j), ")"
end if
if (allocated (eval%pairing_array(i)%factor)) then
write (u, "(1x,A)", advance="no") "x"
write (u, "(1x,'('," // FMT_19 // ",','," // FMT_19 // &
",')')") eval%pairing_array(i)%factor(j)
else
write (u, *)
end if
end do
end do
end if
end subroutine evaluator_write_pairing_array
@ %def evaluator_write_pairing_array
@ Assignment: Deep copy of the interaction component.
<<Evaluators: public>>=
public :: assignment(=)
<<Evaluators: interfaces>>=
interface assignment(=)
module procedure evaluator_assign
end interface
<<Evaluators: procedures>>=
subroutine evaluator_assign (eval_out, eval_in)
type(evaluator_t), intent(out) :: eval_out
type(evaluator_t), intent(in) :: eval_in
eval_out%type = eval_in%type
eval_out%int_in1 => eval_in%int_in1
eval_out%int_in2 => eval_in%int_in2
eval_out%interaction_t = eval_in%interaction_t
if (allocated (eval_in%pairing_array)) then
allocate (eval_out%pairing_array (size (eval_in%pairing_array)))
eval_out%pairing_array = eval_in%pairing_array
end if
end subroutine evaluator_assign
@ %def evaluator_assign
@
\subsection{Auxiliary structures for evaluator creation}
Creating an evaluator that properly handles all quantum numbers requires some
bookkeeping. In this section, we define several auxiliary types and methods
that organize and simplify this task. More structures are defined within the
specific initializers (as local types and internal subroutines).
These types are currently implemented in a partial object-oriented way: We
define some basic methods for initialization etc.\ here, but the evaluator
routines below do access their internals as well. This simplifies some things
such as index addressing using array slices, at the expense of losing some
clarity.
\subsubsection{Index mapping}
Index mapping are abundant when constructing an evaluator. To have arrays of
index mappings, we define this:
<<Evaluators: types>>=
type :: index_map_t
integer, dimension(:), allocatable :: entry
end type index_map_t
@ %def index_map_t
<<Evaluators: procedures>>=
elemental subroutine index_map_init (map, n)
type(index_map_t), intent(out) :: map
integer, intent(in) :: n
allocate (map%entry (n))
map%entry = 0
end subroutine index_map_init
@ %def index_map_init
<<Evaluators: procedures>>=
function index_map_exists (map) result (flag)
logical :: flag
type(index_map_t), intent(in) :: map
flag = allocated (map%entry)
end function index_map_exists
@ %def index_map_exists
<<Evaluators: interfaces>>=
interface size
module procedure index_map_size
end interface
@ %def size
<<Evaluators: procedures>>=
function index_map_size (map) result (s)
integer :: s
type(index_map_t), intent(in) :: map
if (allocated (map%entry)) then
s = size (map%entry)
else
s = 0
end if
end function index_map_size
@ %def index_map_size
<<Evaluators: interfaces>>=
interface assignment(=)
module procedure index_map_assign_int
module procedure index_map_assign_array
end interface
@ %def =
<<Evaluators: procedures>>=
elemental subroutine index_map_assign_int (map, ival)
type(index_map_t), intent(inout) :: map
integer, intent(in) :: ival
map%entry = ival
end subroutine index_map_assign_int
subroutine index_map_assign_array (map, array)
type(index_map_t), intent(inout) :: map
integer, dimension(:), intent(in) :: array
map%entry = array
end subroutine index_map_assign_array
@ %def index_map_assign_int index_map_assign_array
<<Evaluators: procedures>>=
elemental subroutine index_map_set_entry (map, i, ival)
type(index_map_t), intent(inout) :: map
integer, intent(in) :: i
integer, intent(in) :: ival
map%entry(i) = ival
end subroutine index_map_set_entry
@ %def index_map_set_entry
<<Evaluators: procedures>>=
elemental function index_map_get_entry (map, i) result (ival)
integer :: ival
type(index_map_t), intent(in) :: map
integer, intent(in) :: i
ival = map%entry(i)
end function index_map_get_entry
@ %def index_map_get_entry
@
\subsubsection{Index mapping (two-dimensional)}
This is a variant with a square matrix instead of an array.
<<Evaluators: types>>=
type :: index_map2_t
integer :: s = 0
integer, dimension(:,:), allocatable :: entry
end type index_map2_t
@ %def index_map2_t
<<Evaluators: procedures>>=
elemental subroutine index_map2_init (map, n)
type(index_map2_t), intent(out) :: map
integer, intent(in) :: n
map%s = n
allocate (map%entry (n, n))
end subroutine index_map2_init
@ %def index_map2_init
<<Evaluators: procedures>>=
function index_map2_exists (map) result (flag)
logical :: flag
type(index_map2_t), intent(in) :: map
flag = allocated (map%entry)
end function index_map2_exists
@ %def index_map2_exists
<<Evaluators: interfaces>>=
interface size
module procedure index_map2_size
end interface
@ %def size
<<Evaluators: procedures>>=
function index_map2_size (map) result (s)
integer :: s
type(index_map2_t), intent(in) :: map
s = map%s
end function index_map2_size
@ %def index_map2_size
<<Evaluators: interfaces>>=
interface assignment(=)
module procedure index_map2_assign_int
end interface
@ %def =
<<Evaluators: procedures>>=
elemental subroutine index_map2_assign_int (map, ival)
type(index_map2_t), intent(inout) :: map
integer, intent(in) :: ival
map%entry = ival
end subroutine index_map2_assign_int
@ %def index_map2_assign_int
<<Evaluators: procedures>>=
elemental subroutine index_map2_set_entry (map, i, j, ival)
type(index_map2_t), intent(inout) :: map
integer, intent(in) :: i, j
integer, intent(in) :: ival
map%entry(i,j) = ival
end subroutine index_map2_set_entry
@ %def index_map2_set_entry
<<Evaluators: procedures>>=
elemental function index_map2_get_entry (map, i, j) result (ival)
integer :: ival
type(index_map2_t), intent(in) :: map
integer, intent(in) :: i, j
ival = map%entry(i,j)
end function index_map2_get_entry
@ %def index_map2_get_entry
@
\subsubsection{Auxiliary structures: particle mask}
This is a simple container of a logical array.
<<Evaluators: types>>=
type :: prt_mask_t
logical, dimension(:), allocatable :: entry
end type prt_mask_t
@ %def prt_mask_t
<<Evaluators: procedures>>=
subroutine prt_mask_init (mask, n)
type(prt_mask_t), intent(out) :: mask
integer, intent(in) :: n
allocate (mask%entry (n))
end subroutine prt_mask_init
@ %def prt_mask_init
<<Evaluators: interfaces>>=
interface size
module procedure prt_mask_size
end interface
@ %def size
<<Evaluators: procedures>>=
function prt_mask_size (mask) result (s)
integer :: s
type(prt_mask_t), intent(in) :: mask
s = size (mask%entry)
end function prt_mask_size
@ %def prt_mask_size
@
\subsubsection{Quantum number containers}
Trivial transparent containers:
<<Evaluators: types>>=
type :: qn_list_t
type(quantum_numbers_t), dimension(:,:), allocatable :: qn
end type qn_list_t
type :: qn_mask_array_t
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask
end type qn_mask_array_t
@ %def qn_list_t qn_mask_array_t
@
\subsubsection{Auxiliary structures: connection entries}
This type is used as intermediate storage when computing the product of two
evaluators or the square of an evaluator. The quantum-number array [[qn]]
corresponds to the particles common to both interactions, but irrelevant
quantum numbers (color) masked out. The index arrays [[index_in]] determine
the entries in the input interactions that contribute to this connection.
[[n_index]] is the size of these arrays, and [[count]] is used while filling
the entries. Finally, the quantum-number arrays [[qn_in_list]] are the actual
entries in the input interaction that contribute. In the product case, they
exclude the connected quantum numbers.
Each evaluator has its own [[connection_table]] which contains an array of
[[connection_entry]] objects, but also has stuff that specifically applies to
the evaluator type. Hence, we do not generalize the [[connection_table_t]]
type.
The filling procedure [[connection_entry_add_state]] is specific to the
various evaluator types.
<<Evaluators: types>>=
type :: connection_entry_t
type(quantum_numbers_t), dimension(:), allocatable :: qn_conn
integer, dimension(:), allocatable :: n_index
integer, dimension(:), allocatable :: count
type(index_map_t), dimension(:), allocatable :: index_in
type(qn_list_t), dimension(:), allocatable :: qn_in_list
end type connection_entry_t
@ %def connection_entry_t
<<Evaluators: procedures>>=
subroutine connection_entry_init &
(entry, n_count, n_map, qn_conn, count, n_rest)
type(connection_entry_t), intent(out) :: entry
integer, intent(in) :: n_count, n_map
type(quantum_numbers_t), dimension(:), intent(in) :: qn_conn
integer, dimension(n_count), intent(in) :: count
integer, dimension(n_count), intent(in) :: n_rest
integer :: i
allocate (entry%qn_conn (size (qn_conn)))
allocate (entry%n_index (n_count))
allocate (entry%count (n_count))
allocate (entry%index_in (n_map))
allocate (entry%qn_in_list (n_count))
entry%qn_conn = qn_conn
entry%n_index = count
entry%count = 0
if (size (entry%index_in) == size (count)) then
call index_map_init (entry%index_in, count)
else
call index_map_init (entry%index_in, count(1))
end if
do i = 1, n_count
allocate (entry%qn_in_list(i)%qn (n_rest(i), count(i)))
end do
end subroutine connection_entry_init
@ %def connection_entry_init
<<Evaluators: procedures>>=
subroutine connection_entry_write (entry, unit)
type(connection_entry_t), intent(in) :: entry
integer, intent(in), optional :: unit
integer :: i, j
integer :: u
u = given_output_unit (unit)
call quantum_numbers_write (entry%qn_conn, unit)
write (u, *)
do i = 1, size (entry%n_index)
write (u, *) "Input interaction", i
do j = 1, entry%n_index(i)
if (size (entry%n_index) == size (entry%index_in)) then
write (u, "(2x,I0,4x,I0,2x)", advance = "no") &
j, index_map_get_entry (entry%index_in(i), j)
else
write (u, "(2x,I0,4x,I0,2x,I0,2x)", advance = "no") &
j, index_map_get_entry (entry%index_in(1), j), &
index_map_get_entry (entry%index_in(2), j)
end if
call quantum_numbers_write (entry%qn_in_list(i)%qn(:,j), unit)
write (u, *)
end do
end do
end subroutine connection_entry_write
@ %def connection_entry_write
@
\subsubsection{Color handling}
For managing color-factor computation, we introduce this local type. The
[[index]] is the index in the color table that corresponds to a given matrix
element index in the input interaction. The [[col]] array stores the color
assignments in rows. The [[factor]] array associates a complex number with
each pair of arrays in the color table. The [[factor_is_known]] array reveals
whether a given factor is known already or still has to be computed.
<<Evaluators: types>>=
type :: color_table_t
integer, dimension(:), allocatable :: index
type(color_t), dimension(:,:), allocatable :: col
logical, dimension(:,:), allocatable :: factor_is_known
complex(default), dimension(:,:), allocatable :: factor
end type color_table_t
@ %def color_table_t
@ This is the initializer. We extract the color states from the given state
matrices, establish index mappings between the two states (implemented by the
array [[me_index]]), make an array of color states, and initialize the
color-factor table. The latter is two-dimensional (includes interference) and
not yet filled.
<<Evaluators: procedures>>=
subroutine color_table_init (color_table, state, n_tot)
type(color_table_t), intent(out) :: color_table
type(state_matrix_t), intent(in) :: state
integer, intent(in) :: n_tot
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(:), allocatable :: qn
type(state_matrix_t) :: state_col
integer :: index, n_col_state
allocate (color_table%index (state%get_n_matrix_elements ()))
color_table%index = 0
allocate (qn (n_tot))
call state_col%init ()
call it%init (state)
do while (it%is_valid ())
index = it%get_me_index ()
call qn%init (col = it%get_color ())
call state_col%add_state (qn, me_index = color_table%index(index))
call it%advance ()
end do
n_col_state = state_col%get_n_matrix_elements ()
allocate (color_table%col (n_tot, n_col_state))
call it%init (state_col)
do while (it%is_valid ())
index = it%get_me_index ()
color_table%col(:,index) = it%get_color ()
call it%advance ()
end do
call state_col%final ()
allocate (color_table%factor_is_known (n_col_state, n_col_state))
allocate (color_table%factor (n_col_state, n_col_state))
color_table%factor_is_known = .false.
end subroutine color_table_init
@ %def color_table_init
@ Output (debugging use):
<<Evaluators: procedures>>=
subroutine color_table_write (color_table, unit)
type(color_table_t), intent(in) :: color_table
integer, intent(in), optional :: unit
integer :: i, j
integer :: u
u = given_output_unit (unit)
write (u, *) "Color table:"
if (allocated (color_table%index)) then
write (u, *) " Index mapping state => color table:"
do i = 1, size (color_table%index)
write (u, "(3x,I0,2x,I0,2x)") i, color_table%index(i)
end do
write (u, *) " Color table:"
do i = 1, size (color_table%col, 2)
write (u, "(3x,I0,2x)", advance = "no") i
call color_write (color_table%col(:,i), unit)
write (u, *)
end do
write (u, *) " Defined color factors:"
do i = 1, size (color_table%factor, 1)
do j = 1, size (color_table%factor, 2)
if (color_table%factor_is_known(i,j)) then
write (u, *) i, j, color_table%factor(i,j)
end if
end do
end do
end if
end subroutine color_table_write
@ %def color_table_write
@ This subroutine sets color factors, based on information from the hard
matrix element: the list of pairs of color-flow indices (in the basis of the
matrix element code), the list of corresponding factors, and the list of
mappings from the matrix element index in the input interaction to the
color-flow index in the hard matrix element object.
We first determine the mapping of color-flow indices from the hard matrix
element code to the current color table. The mapping could be nontrivial
because the latter is derived from iterating over a state matrix, which may
return states in non-canonical order. The translation table can be determined
because we have, for the complete state matrix, both the mapping to the hard
interaction (the input [[col_index_hi]]) and the mapping to the current
color table (the component [[color_table%index]]).
Once this mapping is known, we scan the list of index pairs
[[color_flow_index]] and translate them to valid color-table index pairs. For
this pair, the color factor is set using the corresponding entry in the list
[[col_factor]].
<<Evaluators: procedures>>=
subroutine color_table_set_color_factors (color_table, &
col_flow_index, col_factor, col_index_hi)
type(color_table_t), intent(inout) :: color_table
integer, dimension(:,:), intent(in) :: col_flow_index
complex(default), dimension(:), intent(in) :: col_factor
integer, dimension(:), intent(in) :: col_index_hi
integer, dimension(:), allocatable :: hi_to_ct
integer :: n_cflow
integer :: hi_index, me_index, ct_index, cf_index
integer, dimension(2) :: hi_index_pair, ct_index_pair
n_cflow = size (col_index_hi)
if (size (color_table%index) /= n_cflow) &
call msg_bug ("Mismatch between hard matrix element and color table")
allocate (hi_to_ct (n_cflow))
do me_index = 1, size (color_table%index)
ct_index = color_table%index(me_index)
hi_index = col_index_hi(me_index)
hi_to_ct(hi_index) = ct_index
end do
do cf_index = 1, size (col_flow_index, 2)
hi_index_pair = col_flow_index(:,cf_index)
ct_index_pair = hi_to_ct(hi_index_pair)
color_table%factor(ct_index_pair(1), ct_index_pair(2)) = &
col_factor(cf_index)
color_table%factor_is_known(ct_index_pair(1), ct_index_pair(2)) = .true.
end do
end subroutine color_table_set_color_factors
@ %def color_table_set_color_factors
@ This function returns a color factor, given two indices which point to the
matrix elements of the initial state matrix. Internally, we can map them to
the corresponding indices in the color table. As a side effect, we store the
color factor in the color table for later lookup. (I.e., this function is
impure.)
<<Evaluators: procedures>>=
function color_table_get_color_factor (color_table, index1, index2, nc) &
result (factor)
real(default) :: factor
type(color_table_t), intent(inout) :: color_table
integer, intent(in) :: index1, index2
integer, intent(in), optional :: nc
integer :: i1, i2
i1 = color_table%index(index1)
i2 = color_table%index(index2)
if (color_table%factor_is_known(i1,i2)) then
factor = real(color_table%factor(i1,i2), kind=default)
else
factor = compute_color_factor &
(color_table%col(:,i1), color_table%col(:,i2), nc)
color_table%factor(i1,i2) = factor
color_table%factor_is_known(i1,i2) = .true.
end if
end function color_table_get_color_factor
@ %def color_table_get_color_factor
@
\subsection{Creating an evaluator: Matrix multiplication}
The evaluator for matrix multiplication is the most complicated
variant.
The initializer takes two input interactions and constructs the result
evaluator, which consists of the interaction and the multiplication
table for the product (or convolution) of the two. Normally, the
input interactions are connected by one or more common particles
(e.g., decay, structure function convolution).
In the result interaction, quantum numbers of the connections can be
summed over. This is determined by the [[qn_mask_conn]] argument.
The [[qn_mask_rest]] argument is its analog for the other particles
within the result interaction. (E.g., for the trace of the state
matrix, all quantum numbers are summed over.) Finally, the
[[connections_are_resonant]] argument tells whether the connecting
particles should be marked as resonant in the final event record.
This is useful for decays.
The algorithm consists of the following steps:
\begin{enumerate}
\item
[[find_connections]]: Find the particles which are connected, i.e.,
common to both input interactions. Either they are directly linked,
or both are linked to a common source.
\item
[[compute_index_bounds_and_mappings]]: Compute the mappings of
particle indices from the input interactions to the result
interaction. There is a separate mapping for the connected
particles.
\item
[[accumulate_connected_states]]: Create an auxiliary state matrix
which lists the possible quantum numbers for the connected
particles. When building this matrix, count the number of times
each assignment is contained in any of the input states and, for
each of the input states, record the index of the matrix element
within the new state matrix. For the connected particles, reassign
color indices such that no color state is present twice in different
color-index assignment. Note that helicity assignments of the
connected state can be (and will be) off-diagonal, so no spin
correlations are lost in decays.
Do this for both input interactions.
\item
[[allocate_connection_entries]]: Allocate a table of connections.
Each table row corresponds to one state in the auxiliary matrix, and
to multiple states of the input interactions. It collects all
states of the unconnected particles in the two input interactions
that are associated with the particular state (quantum-number
assignment) of the connected particles.
\item
[[fill_connection_table]]: Fill the table of connections by scanning
both input interactions. When copying states, reassign color
indices for the unconnected particles such that they match between
all involved particle sets (interaction 1, interaction 2, and
connected particles).
\item
[[make_product_interaction]]: Scan the table of connections we have
just built. For each entry, construct all possible pairs of states
of the unconnected particles and combine them with the specific
connected-particle state. This is a possible quantum-number
assignment of the result interaction. Now mask all quantum numbers
that should be summed over, and append this to the result state
matrix. Record the matrix element index of the result. We now have
the result interaction.
\item
[[make_pairing_array]]: First allocate the pairing array with the
number of entries of the result interaction. Then scan the table of
connections again. For each entry, record the indices of the matrix
elements which have to be multiplied and summed over in order to
compute this particular matrix element. This makes up the
multiplication table.
\item
[[record_links]]: Transfer all source pointers from the input
interactions to the result interaction. Do the same for the
internal parent-child relations and resonance assignments. For the
connected particles, make up appropriate additional parent-child
relations. This allows for fetching momenta from other interactions
when a new event is filled, and to reconstruct the event history
when the event is analyzed.
\end{enumerate}
After all this is done, for each event, we just have to evaluate the
pairing arrays (multiplication tables) in order to compute the result
matrix elements in their proper positions. The quantum-number
assignments remain fixed from now on.
<<Evaluators: evaluator: TBP>>=
procedure :: init_product => evaluator_init_product
<<Evaluators: procedures>>=
subroutine evaluator_init_product &
(eval, int_in1, int_in2, qn_mask_conn, qn_filter_conn, qn_mask_rest, &
- connections_are_resonant, ignore_sub)
+ connections_are_resonant, ignore_sub_for_qn)
class(evaluator_t), intent(out), target :: eval
class(interaction_t), intent(in), target :: int_in1, int_in2
type(quantum_numbers_mask_t), intent(in) :: qn_mask_conn
type(quantum_numbers_t), intent(in), optional :: qn_filter_conn
type(quantum_numbers_mask_t), intent(in), optional :: qn_mask_rest
logical, intent(in), optional :: connections_are_resonant
- logical, intent(in), optional :: ignore_sub
+ logical, intent(in), optional :: ignore_sub_for_qn
type(qn_mask_array_t), dimension(2) :: qn_mask_in
type(state_matrix_t), pointer :: state_in1, state_in2
type :: connection_table_t
integer :: n_conn = 0
integer, dimension(2) :: n_rest = 0
integer :: n_tot = 0
integer :: n_me_conn = 0
type(state_matrix_t) :: state
type(index_map_t), dimension(:), allocatable :: index_conn
type(connection_entry_t), dimension(:), allocatable :: entry
type(index_map_t) :: index_result
end type connection_table_t
type(connection_table_t) :: connection_table
integer :: n_in, n_vir, n_out, n_tot
integer, dimension(2) :: n_rest
integer :: n_conn
integer, dimension(:,:), allocatable :: connection_index
type(index_map_t), dimension(2) :: prt_map_in
type(index_map_t) :: prt_map_conn
type(prt_mask_t), dimension(2) :: prt_is_connected
type(quantum_numbers_mask_t), dimension(:), allocatable :: &
qn_mask_conn_initial, int_in1_mask, int_in2_mask
integer :: i
eval%type = EVAL_PRODUCT
eval%int_in1 => int_in1
eval%int_in2 => int_in2
state_in1 => int_in1%get_state_matrix_ptr ()
state_in2 => int_in2%get_state_matrix_ptr ()
call find_connections (int_in1, int_in2, n_conn, connection_index)
if (n_conn == 0) then
call msg_message ("First interaction:")
call int_in1%basic_write (col_verbose=.true.)
call msg_message ("Second interaction:")
call int_in2%basic_write (col_verbose=.true.)
call msg_fatal ("Evaluator product: no connections found between factors")
end if
call compute_index_bounds_and_mappings &
(int_in1, int_in2, n_conn, &
n_in, n_vir, n_out, n_tot, &
n_rest, prt_map_in, prt_map_conn)
call prt_mask_init (prt_is_connected(1), int_in1%get_n_tot ())
call prt_mask_init (prt_is_connected(2), int_in2%get_n_tot ())
do i = 1, 2
prt_is_connected(i)%entry = .true.
prt_is_connected(i)%entry(connection_index(:,i)) = .false.
end do
allocate (qn_mask_conn_initial (n_conn), &
int_in1_mask (n_conn), int_in2_mask (n_conn))
int_in1_mask = int_in1%get_mask (connection_index(:,1))
int_in2_mask = int_in2%get_mask (connection_index(:,2))
do i = 1, n_conn
qn_mask_conn_initial(i) = int_in1_mask(i) .or. int_in2_mask(i)
end do
allocate (qn_mask_in(1)%mask (int_in1%get_n_tot ()))
allocate (qn_mask_in(2)%mask (int_in2%get_n_tot ()))
qn_mask_in(1)%mask = int_in1%get_mask ()
qn_mask_in(2)%mask = int_in2%get_mask ()
call connection_table_init (connection_table, &
state_in1, state_in2, &
qn_mask_conn_initial, &
n_conn, connection_index, n_rest, &
- qn_filter_conn, ignore_sub)
+ qn_filter_conn, ignore_sub_for_qn)
call connection_table_fill (connection_table, &
state_in1, state_in2, &
connection_index, prt_is_connected)
call make_product_interaction (eval%interaction_t, &
n_in, n_vir, n_out, &
connection_table, &
prt_map_in, prt_is_connected, &
qn_mask_in, qn_mask_conn_initial, &
qn_mask_conn, qn_filter_conn, qn_mask_rest)
call make_pairing_array (eval%pairing_array, &
eval%get_n_matrix_elements (), &
connection_table)
call record_links (eval%interaction_t, &
int_in1, int_in2, connection_index, prt_map_in, prt_map_conn, &
prt_is_connected, connections_are_resonant)
call connection_table_final (connection_table)
if (eval%get_n_matrix_elements () == 0) then
print *, "Evaluator product"
print *, "First interaction"
call int_in1%basic_write (col_verbose=.true.)
print *
print *, "Second interaction"
call int_in2%basic_write (col_verbose=.true.)
print *
call msg_fatal ("Product of density matrices is empty", &
[var_str (" --------------------------------------------"), &
var_str ("This happens when two density matrices are convoluted "), &
var_str ("but the processes they belong to (e.g., production "), &
var_str ("and decay) do not match. This could happen if the "), &
var_str ("beam specification does not match the hard "), &
var_str ("process. Or it may indicate a WHIZARD bug.")])
end if
contains
subroutine compute_index_bounds_and_mappings &
(int1, int2, n_conn, &
n_in, n_vir, n_out, n_tot, &
n_rest, prt_map_in, prt_map_conn)
class(interaction_t), intent(in) :: int1, int2
integer, intent(in) :: n_conn
integer, intent(out) :: n_in, n_vir, n_out, n_tot
integer, dimension(2), intent(out) :: n_rest
type(index_map_t), dimension(2), intent(out) :: prt_map_in
type(index_map_t), intent(out) :: prt_map_conn
integer, dimension(:), allocatable :: index
integer :: n_in1, n_vir1, n_out1
integer :: n_in2, n_vir2, n_out2
integer :: k
n_in1 = int1%get_n_in ()
n_vir1 = int1%get_n_vir ()
n_out1 = int1%get_n_out () - n_conn
n_rest(1) = n_in1 + n_vir1 + n_out1
n_in2 = int2%get_n_in () - n_conn
n_vir2 = int2%get_n_vir ()
n_out2 = int2%get_n_out ()
n_rest(2) = n_in2 + n_vir2 + n_out2
n_in = n_in1 + n_in2
n_vir = n_vir1 + n_vir2 + n_conn
n_out = n_out1 + n_out2
n_tot = n_in + n_vir + n_out
call index_map_init (prt_map_in, n_rest)
call index_map_init (prt_map_conn, n_conn)
allocate (index (n_tot))
index = [ (i, i = 1, n_tot) ]
prt_map_in(1)%entry(1 : n_in1) = index( 1 : n_in1)
k = n_in1
prt_map_in(2)%entry(1 : n_in2) = index(k + 1 : k + n_in2)
k = k + n_in2
prt_map_in(1)%entry(n_in1 + 1 : n_in1 + n_vir1) = index(k + 1 : k + n_vir1)
k = k + n_vir1
prt_map_in(2)%entry(n_in2 + 1 : n_in2 + n_vir2) = index(k + 1 : k + n_vir2)
k = k + n_vir2
prt_map_conn%entry = index(k + 1 : k + n_conn)
k = k + n_conn
prt_map_in(1)%entry(n_in1 + n_vir1 + 1 : n_rest(1)) = index(k + 1 : k + n_out1)
k = k + n_out1
prt_map_in(2)%entry(n_in2 + n_vir2 + 1 : n_rest(2)) = index(k + 1 : k + n_out2)
end subroutine compute_index_bounds_and_mappings
subroutine connection_table_init &
(connection_table, state_in1, state_in2, qn_mask_conn, &
n_conn, connection_index, n_rest, &
- qn_filter_conn, is_real_sub)
+ qn_filter_conn, ignore_sub_for_qn_in)
type(connection_table_t), intent(out) :: connection_table
type(state_matrix_t), intent(in), target :: state_in1, state_in2
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask_conn
integer, intent(in) :: n_conn
integer, dimension(:,:), intent(in) :: connection_index
integer, dimension(2), intent(in) :: n_rest
type(quantum_numbers_t), intent(in), optional :: qn_filter_conn
- logical, intent(in), optional :: is_real_sub
+ logical, intent(in), optional :: ignore_sub_for_qn_in
integer, dimension(2) :: n_me_in
type(state_iterator_t) :: it
type(quantum_numbers_t), dimension(n_conn) :: qn
integer :: i, me_index_in, me_index_conn, n_me_conn
integer, dimension(2) :: me_count
- logical :: is_sub, has_sub_qn
+ logical :: ignore_sub_for_qn, has_sub_qn
integer :: i_beam_sub
connection_table%n_conn = n_conn
connection_table%n_rest = n_rest
n_me_in(1) = state_in1%get_n_matrix_elements ()
n_me_in(2) = state_in2%get_n_matrix_elements ()
allocate (connection_table%index_conn (2))
call index_map_init (connection_table%index_conn, n_me_in)
connection_table%index_conn = 0
call connection_table%state%init (n_counters = 2)
do i = 1, 2
select case (i)
case (1); call it%init (state_in1)
case (2); call it%init (state_in2)
end select
do while (it%is_valid ())
qn = it%get_quantum_numbers (connection_index(:,i))
call qn%undefine (qn_mask_conn)
if (present (qn_filter_conn)) then
if (.not. all (qn .match. qn_filter_conn)) then
call it%advance (); cycle
end if
end if
call quantum_numbers_canonicalize_color (qn)
me_index_in = it%get_me_index ()
- is_sub = .false.; if (present (is_real_sub)) is_sub = is_real_sub
+ ignore_sub_for_qn = .false.; if (present (ignore_sub_for_qn_in)) ignore_sub_for_qn = ignore_sub_for_qn_in
has_sub_qn = .false.
- do i_beam_sub = 1, n_beam_structure_int
+ do i_beam_sub = 1, n_beams_rescaled
has_sub_qn = has_sub_qn .or. any (qn%get_sub () == i_beam_sub)
end do
call connection_table%state%add_state (qn, &
counter_index = i, &
- ignore_sub = .not. (is_sub .and. has_sub_qn), &
+ ignore_sub_for_qn = .not. (ignore_sub_for_qn .and. has_sub_qn), &
me_index = me_index_conn)
call index_map_set_entry (connection_table%index_conn(i), &
me_index_in, me_index_conn)
call it%advance ()
end do
end do
n_me_conn = connection_table%state%get_n_matrix_elements ()
connection_table%n_me_conn = n_me_conn
allocate (connection_table%entry (n_me_conn))
call it%init (connection_table%state)
do while (it%is_valid ())
i = it%get_me_index ()
me_count = it%get_me_count ()
call connection_entry_init (connection_table%entry(i), 2, 2, &
it%get_quantum_numbers (), me_count, n_rest)
call it%advance ()
end do
end subroutine connection_table_init
subroutine connection_table_final (connection_table)
type(connection_table_t), intent(inout) :: connection_table
call connection_table%state%final ()
end subroutine connection_table_final
subroutine connection_table_write (connection_table, unit)
type(connection_table_t), intent(in) :: connection_table
integer, intent(in), optional :: unit
integer :: i, j
integer :: u
u = given_output_unit (unit)
write (u, *) "Connection table:"
call connection_table%state%write (unit)
if (allocated (connection_table%index_conn)) then
write (u, *) " Index mapping input => connection table:"
do i = 1, size (connection_table%index_conn)
write (u, *) " Input state", i
do j = 1, size (connection_table%index_conn(i))
write (u, *) j, &
index_map_get_entry (connection_table%index_conn(i), j)
end do
end do
end if
if (allocated (connection_table%entry)) then
write (u, *) " Connection table contents:"
do i = 1, size (connection_table%entry)
call connection_entry_write (connection_table%entry(i), unit)
end do
end if
if (index_map_exists (connection_table%index_result)) then
write (u, *) " Index mapping connection table => output:"
do i = 1, size (connection_table%index_result)
write (u, *) i, &
index_map_get_entry (connection_table%index_result, i)
end do
end if
end subroutine connection_table_write
subroutine connection_table_fill &
(connection_table, state_in1, state_in2, &
connection_index, prt_is_connected)
type(connection_table_t), intent(inout) :: connection_table
type(state_matrix_t), intent(in), target :: state_in1, state_in2
integer, dimension(:,:), intent(in) :: connection_index
type(prt_mask_t), dimension(2), intent(in) :: prt_is_connected
type(state_iterator_t) :: it
integer :: index_in, index_conn
integer :: color_offset
integer :: n_result_entries
integer :: i, k
color_offset = connection_table%state%get_max_color_value ()
do i = 1, 2
select case (i)
case (1); call it%init (state_in1)
case (2); call it%init (state_in2)
end select
do while (it%is_valid ())
index_in = it%get_me_index ()
index_conn = index_map_get_entry &
(connection_table%index_conn(i), index_in)
if (index_conn /= 0) then
call connection_entry_add_state &
(connection_table%entry(index_conn), i, &
index_in, it%get_quantum_numbers (), &
connection_index(:,i), prt_is_connected(i), &
color_offset)
end if
call it%advance ()
end do
color_offset = color_offset + state_in1%get_max_color_value ()
end do
n_result_entries = 0
do k = 1, size (connection_table%entry)
n_result_entries = &
n_result_entries + product (connection_table%entry(k)%n_index)
end do
call index_map_init (connection_table%index_result, n_result_entries)
end subroutine connection_table_fill
subroutine connection_entry_add_state &
(entry, i, index_in, qn_in, connection_index, prt_is_connected, &
color_offset)
type(connection_entry_t), intent(inout) :: entry
integer, intent(in) :: i
integer, intent(in) :: index_in
type(quantum_numbers_t), dimension(:), intent(in) :: qn_in
integer, dimension(:), intent(in) :: connection_index
type(prt_mask_t), intent(in) :: prt_is_connected
integer, intent(in) :: color_offset
integer :: c
integer, dimension(:,:), allocatable :: color_map
entry%count(i) = entry%count(i) + 1
c = entry%count(i)
call make_color_map (color_map, &
qn_in(connection_index), entry%qn_conn)
call index_map_set_entry (entry%index_in(i), c, index_in)
entry%qn_in_list(i)%qn(:,c) = pack (qn_in, prt_is_connected%entry)
call quantum_numbers_translate_color &
(entry%qn_in_list(i)%qn(:,c), color_map, color_offset)
end subroutine connection_entry_add_state
subroutine make_product_interaction (int, &
n_in, n_vir, n_out, &
connection_table, &
prt_map_in, prt_is_connected, &
qn_mask_in, qn_mask_conn_initial, &
qn_mask_conn, qn_filter_conn, qn_mask_rest)
type(interaction_t), intent(out), target :: int
integer, intent(in) :: n_in, n_vir, n_out
type(connection_table_t), intent(inout), target :: connection_table
type(index_map_t), dimension(2), intent(in) :: prt_map_in
type(prt_mask_t), dimension(2), intent(in) :: prt_is_connected
type(qn_mask_array_t), dimension(2), intent(in) :: qn_mask_in
type(quantum_numbers_mask_t), dimension(:), intent(in) :: &
qn_mask_conn_initial
type(quantum_numbers_mask_t), intent(in) :: qn_mask_conn
type(quantum_numbers_t), intent(in), optional :: qn_filter_conn
type(quantum_numbers_mask_t), intent(in), optional :: qn_mask_rest
type(index_map_t), dimension(2) :: prt_index_in
type(index_map_t) :: prt_index_conn
integer :: n_tot, n_conn
integer, dimension(2) :: n_rest
integer :: i, j, k, m
type(quantum_numbers_t), dimension(:), allocatable :: qn
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask
type(connection_entry_t), pointer :: entry
integer :: result_index
n_conn = connection_table%n_conn
n_rest = connection_table%n_rest
n_tot = sum (n_rest) + n_conn
allocate (qn (n_tot), qn_mask (n_tot))
do i = 1, 2
call index_map_init (prt_index_in(i), n_rest(i))
prt_index_in(i) = &
prt_map_in(i)%entry ([ (j, j = 1, n_rest(i)) ])
end do
call index_map_init (prt_index_conn, n_conn)
prt_index_conn = prt_map_conn%entry ([ (j, j = 1, n_conn) ])
do i = 1, 2
if (present (qn_mask_rest)) then
qn_mask(prt_index_in(i)%entry) = &
pack (qn_mask_in(i)%mask, prt_is_connected(i)%entry) &
.or. qn_mask_rest
else
qn_mask(prt_index_in(i)%entry) = &
pack (qn_mask_in(i)%mask, prt_is_connected(i)%entry)
end if
end do
qn_mask(prt_index_conn%entry) = qn_mask_conn_initial .or. qn_mask_conn
call eval%interaction_t%basic_init (n_in, n_vir, n_out, mask = qn_mask)
m = 1
do i = 1, connection_table%n_me_conn
entry => connection_table%entry(i)
qn(prt_index_conn%entry) = &
quantum_numbers_undefined (entry%qn_conn, qn_mask_conn)
if (present (qn_filter_conn)) then
if (.not. all (qn(prt_index_conn%entry) .match. qn_filter_conn)) &
cycle
end if
do j = 1, entry%n_index(1)
qn(prt_index_in(1)%entry) = entry%qn_in_list(1)%qn(:,j)
do k = 1, entry%n_index(2)
qn(prt_index_in(2)%entry) = entry%qn_in_list(2)%qn(:,k)
call int%add_state (qn, me_index = result_index)
call index_map_set_entry &
(connection_table%index_result, m, result_index)
m = m + 1
end do
end do
end do
call int%freeze ()
end subroutine make_product_interaction
subroutine make_pairing_array (pa, n_matrix_elements, connection_table)
type(pairing_array_t), dimension(:), intent(out), allocatable :: pa
integer, intent(in) :: n_matrix_elements
type(connection_table_t), intent(in), target :: connection_table
type(connection_entry_t), pointer :: entry
integer, dimension(:), allocatable :: n_entries
integer :: i, j, k, m, r
allocate (pa (n_matrix_elements))
allocate (n_entries (n_matrix_elements))
n_entries = 0
do m = 1, size (connection_table%index_result)
r = index_map_get_entry (connection_table%index_result, m)
n_entries(r) = n_entries(r) + 1
end do
call pairing_array_init &
(pa, n_entries, has_i2=.true., has_factor=.false.)
m = 1
n_entries = 0
do i = 1, connection_table%n_me_conn
entry => connection_table%entry(i)
do j = 1, entry%n_index(1)
do k = 1, entry%n_index(2)
r = index_map_get_entry (connection_table%index_result, m)
n_entries(r) = n_entries(r) + 1
pa(r)%i1(n_entries(r)) = &
index_map_get_entry (entry%index_in(1), j)
pa(r)%i2(n_entries(r)) = &
index_map_get_entry (entry%index_in(2), k)
m = m + 1
end do
end do
end do
end subroutine make_pairing_array
subroutine record_links (int, &
int_in1, int_in2, connection_index, prt_map_in, prt_map_conn, &
prt_is_connected, connections_are_resonant)
class(interaction_t), intent(inout) :: int
class(interaction_t), intent(in), target :: int_in1, int_in2
integer, dimension(:,:), intent(in) :: connection_index
type(index_map_t), dimension(2), intent(in) :: prt_map_in
type(index_map_t), intent(in) :: prt_map_conn
type(prt_mask_t), dimension(2), intent(in) :: prt_is_connected
logical, intent(in), optional :: connections_are_resonant
type(index_map_t), dimension(2) :: prt_map_all
integer :: i, j, k, ival
call index_map_init (prt_map_all(1), size (prt_is_connected(1)))
k = 0
j = 0
do i = 1, size (prt_is_connected(1))
if (prt_is_connected(1)%entry(i)) then
j = j + 1
ival = index_map_get_entry (prt_map_in(1), j)
call index_map_set_entry (prt_map_all(1), i, ival)
else
k = k + 1
ival = index_map_get_entry (prt_map_conn, k)
call index_map_set_entry (prt_map_all(1), i, ival)
end if
call int%set_source_link (ival, int_in1, i)
end do
call int_in1%transfer_relations (int, prt_map_all(1)%entry)
call index_map_init (prt_map_all(2), size (prt_is_connected(2)))
j = 0
do i = 1, size (prt_is_connected(2))
if (prt_is_connected(2)%entry(i)) then
j = j + 1
ival = index_map_get_entry (prt_map_in(2), j)
call index_map_set_entry (prt_map_all(2), i, ival)
call int%set_source_link (ival, int_in2, i)
else
call index_map_set_entry (prt_map_all(2), i, 0)
end if
end do
call int_in2%transfer_relations (int, prt_map_all(2)%entry)
call int%relate_connections &
(int_in2, connection_index(:,2), prt_map_all(2)%entry, &
prt_map_conn%entry, connections_are_resonant)
end subroutine record_links
end subroutine evaluator_init_product
@ %def evaluator_init_product
@
\subsection{Creating an evaluator: square}
The generic initializer for an evaluator that squares a matrix element.
Depending on the provided mask, we select the appropriate specific initializer
for either diagonal or non-diagonal helicity density matrices.
<<Evaluators: evaluator: TBP>>=
procedure :: init_square => evaluator_init_square
<<Evaluators: procedures>>=
subroutine evaluator_init_square (eval, int_in, qn_mask, &
col_flow_index, col_factor, col_index_hi, expand_color_flows, nc)
class(evaluator_t), intent(out), target :: eval
class(interaction_t), intent(in), target :: int_in
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
integer, dimension(:,:), intent(in), optional :: col_flow_index
complex(default), dimension(:), intent(in), optional :: col_factor
integer, dimension(:), intent(in), optional :: col_index_hi
logical, intent(in), optional :: expand_color_flows
integer, intent(in), optional :: nc
if (all (qn_mask%diagonal_helicity ())) then
call eval%init_square_diag (int_in, qn_mask, &
col_flow_index, col_factor, col_index_hi, expand_color_flows, nc)
else
call eval%init_square_nondiag (int_in, qn_mask, &
col_flow_index, col_factor, col_index_hi, expand_color_flows, nc)
end if
end subroutine evaluator_init_square
@ %def evaluator_init_square
@
\subsubsection{Color-summed squared matrix (diagonal helicities)}
The initializer for an evaluator that squares a matrix element,
including color factors. The mask must be such that off-diagonal matrix
elements are excluded.
If [[color_flows]] is set, the evaluator keeps color-flow entries
separate and drops all interfering color structures. The color factors are
set to unity in this case.
There is only one input interaction. The quantum-number mask is an
array, one entry for each particle, so they can be treated
individually. For academic purposes, we allow for the number of
colors being different from three (but 3 is the default).
The algorithm is analogous to multiplication, with a few notable
differences:
\begin{enumerate}
\item
The connected particles are known, the correspondence is
one-to-one. All particles are connected, and the mapping of indices
is trivial, which simplifies the following steps.
\item
[[accumulate_connected_states]]: The matrix of connected states
encompasses all particles, but color indices are removed. However,
ghost states are still kept separate from physical color states. No
color-index reassignment is necessary.
\item
The table of connections contains single index and quantum-number
arrays instead of pairs of them. They are paired with themselves
in all possible ways.
\item
[[make_squared_interaction]]: Now apply the predefined
quantum-numbers mask, which usually collects all color states
(physical and ghosts), and possibly a helicity sum.
\item
[[make_pairing_array]]: For each pair of input states, compute the
color factor (including a potential ghost-parity sign) and store
this in the pairing array together with the matrix-element indices
for multiplication.
\item
[[record_links]]: This is again trivial due to the one-to-one
correspondence.
\end{enumerate}
<<Evaluators: evaluator: TBP>>=
procedure :: init_square_diag => evaluator_init_square_diag
<<Evaluators: procedures>>=
subroutine evaluator_init_square_diag (eval, int_in, qn_mask, &
col_flow_index, col_factor, col_index_hi, expand_color_flows, nc)
class(evaluator_t), intent(out), target :: eval
class(interaction_t), intent(in), target :: int_in
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
integer, dimension(:,:), intent(in), optional :: col_flow_index
complex(default), dimension(:), intent(in), optional :: col_factor
integer, dimension(:), intent(in), optional :: col_index_hi
logical, intent(in), optional :: expand_color_flows
integer, intent(in), optional :: nc
integer :: n_in, n_vir, n_out, n_tot
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask_initial
type(state_matrix_t), pointer :: state_in
type :: connection_table_t
integer :: n_tot = 0
integer :: n_me_conn = 0
type(state_matrix_t) :: state
type(index_map_t) :: index_conn
type(connection_entry_t), dimension(:), allocatable :: entry
type(index_map_t) :: index_result
end type connection_table_t
type(connection_table_t) :: connection_table
logical :: sum_colors
type(color_table_t) :: color_table
if (present (expand_color_flows)) then
sum_colors = .not. expand_color_flows
else
sum_colors = .true.
end if
if (sum_colors) then
eval%type = EVAL_SQUARE_WITH_COLOR_FACTORS
else
eval%type = EVAL_SQUARED_FLOWS
end if
eval%int_in1 => int_in
n_in = int_in%get_n_in ()
n_vir = int_in%get_n_vir ()
n_out = int_in%get_n_out ()
n_tot = int_in%get_n_tot ()
state_in => int_in%get_state_matrix_ptr ()
allocate (qn_mask_initial (n_tot))
qn_mask_initial = int_in%get_mask ()
call qn_mask_initial%set_color (sum_colors, mask_cg=.false.)
if (sum_colors) then
call color_table_init (color_table, state_in, n_tot)
if (present (col_flow_index) .and. present (col_factor) &
.and. present (col_index_hi)) then
call color_table_set_color_factors &
(color_table, col_flow_index, col_factor, col_index_hi)
end if
end if
call connection_table_init (connection_table, state_in, &
qn_mask_initial, qn_mask, n_tot)
call connection_table_fill (connection_table, state_in)
call make_squared_interaction (eval%interaction_t, &
n_in, n_vir, n_out, n_tot, &
connection_table, sum_colors, qn_mask_initial .or. qn_mask)
call make_pairing_array (eval%pairing_array, &
eval%get_n_matrix_elements (), &
connection_table, sum_colors, color_table, n_in, n_tot, nc)
call record_links (eval, int_in, n_tot)
call connection_table_final (connection_table)
contains
subroutine connection_table_init &
(connection_table, state_in, qn_mask_in, qn_mask, n_tot)
type(connection_table_t), intent(out) :: connection_table
type(state_matrix_t), intent(in), target :: state_in
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask_in
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
integer, intent(in) :: n_tot
type(quantum_numbers_t), dimension(n_tot) :: qn
type(state_iterator_t) :: it
integer :: i, n_me_in, me_index_in
integer :: me_index_conn, n_me_conn
integer, dimension(1) :: me_count
logical :: qn_passed
connection_table%n_tot = n_tot
n_me_in = state_in%get_n_matrix_elements ()
call index_map_init (connection_table%index_conn, n_me_in)
connection_table%index_conn = 0
call connection_table%state%init (n_counters=1)
call it%init (state_in)
do while (it%is_valid ())
qn = it%get_quantum_numbers ()
if (all (quantum_numbers_are_physical (qn, qn_mask))) then
call qn%undefine (qn_mask_in)
qn_passed = .true.
if (qn_passed) then
me_index_in = it%get_me_index ()
call connection_table%state%add_state (qn, &
counter_index = 1, me_index = me_index_conn)
call index_map_set_entry (connection_table%index_conn, &
me_index_in, me_index_conn)
end if
end if
call it%advance ()
end do
n_me_conn = connection_table%state%get_n_matrix_elements ()
connection_table%n_me_conn = n_me_conn
allocate (connection_table%entry (n_me_conn))
call it%init (connection_table%state)
do while (it%is_valid ())
i = it%get_me_index ()
me_count = it%get_me_count ()
call connection_entry_init (connection_table%entry(i), 1, 2, &
it%get_quantum_numbers (), me_count, [n_tot])
call it%advance ()
end do
end subroutine connection_table_init
subroutine connection_table_final (connection_table)
type(connection_table_t), intent(inout) :: connection_table
call connection_table%state%final ()
end subroutine connection_table_final
subroutine connection_table_write (connection_table, unit)
type(connection_table_t), intent(in) :: connection_table
integer, intent(in), optional :: unit
integer :: i
integer :: u
u = given_output_unit (unit)
write (u, *) "Connection table:"
call connection_table%state%write (unit)
if (index_map_exists (connection_table%index_conn)) then
write (u, *) " Index mapping input => connection table:"
do i = 1, size (connection_table%index_conn)
write (u, *) i, &
index_map_get_entry (connection_table%index_conn, i)
end do
end if
if (allocated (connection_table%entry)) then
write (u, *) " Connection table contents"
do i = 1, size (connection_table%entry)
call connection_entry_write (connection_table%entry(i), unit)
end do
end if
if (index_map_exists (connection_table%index_result)) then
write (u, *) " Index mapping connection table => output"
do i = 1, size (connection_table%index_result)
write (u, *) i, &
index_map_get_entry (connection_table%index_result, i)
end do
end if
end subroutine connection_table_write
subroutine connection_table_fill (connection_table, state)
type(connection_table_t), intent(inout) :: connection_table
type(state_matrix_t), intent(in), target :: state
integer :: index_in, index_conn, n_result_entries
type(state_iterator_t) :: it
integer :: k
call it%init (state)
do while (it%is_valid ())
index_in = it%get_me_index ()
index_conn = &
index_map_get_entry (connection_table%index_conn, index_in)
if (index_conn /= 0) then
call connection_entry_add_state &
(connection_table%entry(index_conn), &
index_in, it%get_quantum_numbers ())
end if
call it%advance ()
end do
n_result_entries = 0
do k = 1, size (connection_table%entry)
n_result_entries = &
n_result_entries + connection_table%entry(k)%n_index(1) ** 2
end do
call index_map_init (connection_table%index_result, n_result_entries)
connection_table%index_result = 0
end subroutine connection_table_fill
subroutine connection_entry_add_state (entry, index_in, qn_in)
type(connection_entry_t), intent(inout) :: entry
integer, intent(in) :: index_in
type(quantum_numbers_t), dimension(:), intent(in) :: qn_in
integer :: c
entry%count = entry%count + 1
c = entry%count(1)
call index_map_set_entry (entry%index_in(1), c, index_in)
entry%qn_in_list(1)%qn(:,c) = qn_in
end subroutine connection_entry_add_state
subroutine make_squared_interaction (int, &
n_in, n_vir, n_out, n_tot, &
connection_table, sum_colors, qn_mask)
type(interaction_t), intent(out), target :: int
integer, intent(in) :: n_in, n_vir, n_out, n_tot
type(connection_table_t), intent(inout), target :: connection_table
logical, intent(in) :: sum_colors
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
type(connection_entry_t), pointer :: entry
integer :: result_index, n_contrib
integer :: i, m
type(quantum_numbers_t), dimension(n_tot) :: qn
call eval%interaction_t%basic_init (n_in, n_vir, n_out, mask=qn_mask)
m = 0
do i = 1, connection_table%n_me_conn
entry => connection_table%entry(i)
qn = quantum_numbers_undefined (entry%qn_conn, qn_mask)
if (.not. sum_colors) call qn(1:n_in)%invert_color ()
call int%add_state (qn, me_index = result_index)
n_contrib = entry%n_index(1) ** 2
connection_table%index_result%entry(m+1:m+n_contrib) = result_index
m = m + n_contrib
end do
call int%freeze ()
end subroutine make_squared_interaction
subroutine make_pairing_array (pa, &
n_matrix_elements, connection_table, sum_colors, color_table, &
n_in, n_tot, nc)
type(pairing_array_t), dimension(:), intent(out), allocatable :: pa
integer, intent(in) :: n_matrix_elements
type(connection_table_t), intent(in), target :: connection_table
logical, intent(in) :: sum_colors
type(color_table_t), intent(inout) :: color_table
type(connection_entry_t), pointer :: entry
integer, intent(in) :: n_in, n_tot
integer, intent(in), optional :: nc
integer, dimension(:), allocatable :: n_entries
integer :: i, k, l, ks, ls, m, r
integer :: color_multiplicity_in
allocate (pa (n_matrix_elements))
allocate (n_entries (n_matrix_elements))
n_entries = 0
do m = 1, size (connection_table%index_result)
r = index_map_get_entry (connection_table%index_result, m)
n_entries(r) = n_entries(r) + 1
end do
call pairing_array_init &
(pa, n_entries, has_i2 = sum_colors, has_factor = sum_colors)
m = 1
n_entries = 0
do i = 1, connection_table%n_me_conn
entry => connection_table%entry(i)
do k = 1, entry%n_index(1)
if (sum_colors) then
color_multiplicity_in = &
product (abs (quantum_numbers_get_color_type &
(entry%qn_in_list(1)%qn(:n_in, k))))
do l = 1, entry%n_index(1)
r = index_map_get_entry (connection_table%index_result, m)
n_entries(r) = n_entries(r) + 1
ks = index_map_get_entry (entry%index_in(1), k)
ls = index_map_get_entry (entry%index_in(1), l)
pa(r)%i1(n_entries(r)) = ks
pa(r)%i2(n_entries(r)) = ls
pa(r)%factor(n_entries(r)) = &
color_table_get_color_factor (color_table, ks, ls, nc) &
/ color_multiplicity_in
m = m + 1
end do
else
r = index_map_get_entry (connection_table%index_result, m)
n_entries(r) = n_entries(r) + 1
ks = index_map_get_entry (entry%index_in(1), k)
pa(r)%i1(n_entries(r)) = ks
m = m + 1
end if
end do
end do
end subroutine make_pairing_array
subroutine record_links (int, int_in, n_tot)
class(interaction_t), intent(inout) :: int
class(interaction_t), intent(in), target :: int_in
integer, intent(in) :: n_tot
integer, dimension(n_tot) :: map
integer :: i
do i = 1, n_tot
call int%set_source_link (i, int_in, i)
end do
map = [ (i, i = 1, n_tot) ]
call int_in%transfer_relations (int, map)
end subroutine record_links
end subroutine evaluator_init_square_diag
@ %def evaluator_init_square_diag
@
\subsubsection{Color-summed squared matrix (support nodiagonal helicities)}
The initializer for an evaluator that squares a matrix element,
including color factors. Unless requested otherwise by the
quantum-number mask, the result contains off-diagonal matrix elements.
(The input interaction must be diagonal since it represents an
amplitude, not a density matrix.)
There is only one input interaction. The quantum-number mask is an
array, one entry for each particle, so they can be treated
individually. For academic purposes, we allow for the number of
colors being different from three (but 3 is the default).
The algorithm is analogous to the previous one, with some additional
complications due to the necessity to loop over two helicity indices.
<<Evaluators: evaluator: TBP>>=
procedure :: init_square_nondiag => evaluator_init_square_nondiag
<<Evaluators: procedures>>=
subroutine evaluator_init_square_nondiag (eval, int_in, qn_mask, &
col_flow_index, col_factor, col_index_hi, expand_color_flows, nc)
class(evaluator_t), intent(out), target :: eval
class(interaction_t), intent(in), target :: int_in
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
integer, dimension(:,:), intent(in), optional :: col_flow_index
complex(default), dimension(:), intent(in), optional :: col_factor
integer, dimension(:), intent(in), optional :: col_index_hi
logical, intent(in), optional :: expand_color_flows
integer, intent(in), optional :: nc
integer :: n_in, n_vir, n_out, n_tot
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask_initial
type(state_matrix_t), pointer :: state_in
type :: connection_table_t
integer :: n_tot = 0
integer :: n_me_conn = 0
type(state_matrix_t) :: state
type(index_map2_t) :: index_conn
type(connection_entry_t), dimension(:), allocatable :: entry
type(index_map_t) :: index_result
end type connection_table_t
type(connection_table_t) :: connection_table
logical :: sum_colors
type(color_table_t) :: color_table
if (present (expand_color_flows)) then
sum_colors = .not. expand_color_flows
else
sum_colors = .true.
end if
if (sum_colors) then
eval%type = EVAL_SQUARE_WITH_COLOR_FACTORS
else
eval%type = EVAL_SQUARED_FLOWS
end if
eval%int_in1 => int_in
n_in = int_in%get_n_in ()
n_vir = int_in%get_n_vir ()
n_out = int_in%get_n_out ()
n_tot = int_in%get_n_tot ()
state_in => int_in%get_state_matrix_ptr ()
allocate (qn_mask_initial (n_tot))
qn_mask_initial = int_in%get_mask ()
call qn_mask_initial%set_color (sum_colors, mask_cg=.false.)
if (sum_colors) then
call color_table_init (color_table, state_in, n_tot)
if (present (col_flow_index) .and. present (col_factor) &
.and. present (col_index_hi)) then
call color_table_set_color_factors &
(color_table, col_flow_index, col_factor, col_index_hi)
end if
end if
call connection_table_init (connection_table, state_in, &
qn_mask_initial, qn_mask, n_tot)
call connection_table_fill (connection_table, state_in)
call make_squared_interaction (eval%interaction_t, &
n_in, n_vir, n_out, n_tot, &
connection_table, sum_colors, qn_mask_initial .or. qn_mask)
call make_pairing_array (eval%pairing_array, &
eval%get_n_matrix_elements (), &
connection_table, sum_colors, color_table, n_in, n_tot, nc)
call record_links (eval, int_in, n_tot)
call connection_table_final (connection_table)
contains
subroutine connection_table_init &
(connection_table, state_in, qn_mask_in, qn_mask, n_tot)
type(connection_table_t), intent(out) :: connection_table
type(state_matrix_t), intent(in), target :: state_in
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask_in
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
integer, intent(in) :: n_tot
type(quantum_numbers_t), dimension(n_tot) :: qn1, qn2, qn
type(state_iterator_t) :: it1, it2, it
integer :: i, n_me_in, me_index_in1, me_index_in2
integer :: me_index_conn, n_me_conn
integer, dimension(1) :: me_count
logical :: qn_passed
connection_table%n_tot = n_tot
n_me_in = state_in%get_n_matrix_elements ()
call index_map2_init (connection_table%index_conn, n_me_in)
connection_table%index_conn = 0
call connection_table%state%init (n_counters=1)
call it1%init (state_in)
do while (it1%is_valid ())
qn1 = it1%get_quantum_numbers ()
me_index_in1 = it1%get_me_index ()
call it2%init (state_in)
do while (it2%is_valid ())
qn2 = it2%get_quantum_numbers ()
if (all (quantum_numbers_are_compatible (qn1, qn2, qn_mask))) then
qn = qn1 .merge. qn2
call qn%undefine (qn_mask_in)
qn_passed = .true.
if (qn_passed) then
me_index_in2 = it2%get_me_index ()
call connection_table%state%add_state (qn, &
counter_index = 1, me_index = me_index_conn)
call index_map2_set_entry (connection_table%index_conn, &
me_index_in1, me_index_in2, me_index_conn)
end if
end if
call it2%advance ()
end do
call it1%advance ()
end do
n_me_conn = connection_table%state%get_n_matrix_elements ()
connection_table%n_me_conn = n_me_conn
allocate (connection_table%entry (n_me_conn))
call it%init (connection_table%state)
do while (it%is_valid ())
i = it%get_me_index ()
me_count = it%get_me_count ()
call connection_entry_init (connection_table%entry(i), 1, 2, &
it%get_quantum_numbers (), me_count, [n_tot])
call it%advance ()
end do
end subroutine connection_table_init
subroutine connection_table_final (connection_table)
type(connection_table_t), intent(inout) :: connection_table
call connection_table%state%final ()
end subroutine connection_table_final
subroutine connection_table_write (connection_table, unit)
type(connection_table_t), intent(in) :: connection_table
integer, intent(in), optional :: unit
integer :: i, j
integer :: u
u = given_output_unit (unit)
write (u, *) "Connection table:"
call connection_table%state%write (unit)
if (index_map2_exists (connection_table%index_conn)) then
write (u, *) " Index mapping input => connection table:"
do i = 1, size (connection_table%index_conn)
do j = 1, size (connection_table%index_conn)
write (u, *) i, j, &
index_map2_get_entry (connection_table%index_conn, i, j)
end do
end do
end if
if (allocated (connection_table%entry)) then
write (u, *) " Connection table contents"
do i = 1, size (connection_table%entry)
call connection_entry_write (connection_table%entry(i), unit)
end do
end if
if (index_map_exists (connection_table%index_result)) then
write (u, *) " Index mapping connection table => output"
do i = 1, size (connection_table%index_result)
write (u, *) i, &
index_map_get_entry (connection_table%index_result, i)
end do
end if
end subroutine connection_table_write
subroutine connection_table_fill (connection_table, state)
type(connection_table_t), intent(inout), target :: connection_table
type(state_matrix_t), intent(in), target :: state
integer :: index1_in, index2_in, index_conn, n_result_entries
type(state_iterator_t) :: it1, it2
integer :: k
call it1%init (state)
do while (it1%is_valid ())
index1_in = it1%get_me_index ()
call it2%init (state)
do while (it2%is_valid ())
index2_in = it2%get_me_index ()
index_conn = index_map2_get_entry &
(connection_table%index_conn, index1_in, index2_in)
if (index_conn /= 0) then
call connection_entry_add_state &
(connection_table%entry(index_conn), &
index1_in, index2_in, &
it1%get_quantum_numbers () &
.merge. &
it2%get_quantum_numbers ())
end if
call it2%advance ()
end do
call it1%advance ()
end do
n_result_entries = 0
do k = 1, size (connection_table%entry)
n_result_entries = &
n_result_entries + connection_table%entry(k)%n_index(1)
end do
call index_map_init (connection_table%index_result, n_result_entries)
connection_table%index_result = 0
end subroutine connection_table_fill
subroutine connection_entry_add_state (entry, index1_in, index2_in, qn_in)
type(connection_entry_t), intent(inout) :: entry
integer, intent(in) :: index1_in, index2_in
type(quantum_numbers_t), dimension(:), intent(in) :: qn_in
integer :: c
entry%count = entry%count + 1
c = entry%count(1)
call index_map_set_entry (entry%index_in(1), c, index1_in)
call index_map_set_entry (entry%index_in(2), c, index2_in)
entry%qn_in_list(1)%qn(:,c) = qn_in
end subroutine connection_entry_add_state
subroutine make_squared_interaction (int, &
n_in, n_vir, n_out, n_tot, &
connection_table, sum_colors, qn_mask)
type(interaction_t), intent(out), target :: int
integer, intent(in) :: n_in, n_vir, n_out, n_tot
type(connection_table_t), intent(inout), target :: connection_table
logical, intent(in) :: sum_colors
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
type(connection_entry_t), pointer :: entry
integer :: result_index
integer :: i, k, m
type(quantum_numbers_t), dimension(n_tot) :: qn
call eval%interaction_t%basic_init (n_in, n_vir, n_out, mask=qn_mask)
m = 0
do i = 1, connection_table%n_me_conn
entry => connection_table%entry(i)
do k = 1, size (entry%qn_in_list(1)%qn, 2)
qn = quantum_numbers_undefined &
(entry%qn_in_list(1)%qn(:,k), qn_mask)
if (.not. sum_colors) call qn(1:n_in)%invert_color ()
call int%add_state (qn, me_index = result_index)
call index_map_set_entry (connection_table%index_result, m + 1, &
result_index)
m = m + 1
end do
end do
call int%freeze ()
end subroutine make_squared_interaction
subroutine make_pairing_array (pa, &
n_matrix_elements, connection_table, sum_colors, color_table, &
n_in, n_tot, nc)
type(pairing_array_t), dimension(:), intent(out), allocatable :: pa
integer, intent(in) :: n_matrix_elements
type(connection_table_t), intent(in), target :: connection_table
logical, intent(in) :: sum_colors
type(color_table_t), intent(inout) :: color_table
type(connection_entry_t), pointer :: entry
integer, intent(in) :: n_in, n_tot
integer, intent(in), optional :: nc
integer, dimension(:), allocatable :: n_entries
integer :: i, k, k1s, k2s, m, r
integer :: color_multiplicity_in
allocate (pa (n_matrix_elements))
allocate (n_entries (n_matrix_elements))
n_entries = 0
do m = 1, size (connection_table%index_result)
r = index_map_get_entry (connection_table%index_result, m)
n_entries(r) = n_entries(r) + 1
end do
call pairing_array_init &
(pa, n_entries, has_i2 = sum_colors, has_factor = sum_colors)
m = 1
n_entries = 0
do i = 1, connection_table%n_me_conn
entry => connection_table%entry(i)
do k = 1, entry%n_index(1)
r = index_map_get_entry (connection_table%index_result, m)
n_entries(r) = n_entries(r) + 1
if (sum_colors) then
k1s = index_map_get_entry (entry%index_in(1), k)
k2s = index_map_get_entry (entry%index_in(2), k)
pa(r)%i1(n_entries(r)) = k1s
pa(r)%i2(n_entries(r)) = k2s
color_multiplicity_in = &
product (abs (quantum_numbers_get_color_type &
(entry%qn_in_list(1)%qn(:n_in, k))))
pa(r)%factor(n_entries(r)) = &
color_table_get_color_factor (color_table, k1s, k2s, nc) &
/ color_multiplicity_in
else
k1s = index_map_get_entry (entry%index_in(1), k)
pa(r)%i1(n_entries(r)) = k1s
end if
m = m + 1
end do
end do
end subroutine make_pairing_array
subroutine record_links (int, int_in, n_tot)
class(interaction_t), intent(inout) :: int
class(interaction_t), intent(in), target :: int_in
integer, intent(in) :: n_tot
integer, dimension(n_tot) :: map
integer :: i
do i = 1, n_tot
call int%set_source_link (i, int_in, i)
end do
map = [ (i, i = 1, n_tot) ]
call int_in%transfer_relations (int, map)
end subroutine record_links
end subroutine evaluator_init_square_nondiag
@ %def evaluator_init_square_nondiag
@
\subsubsection{Copy with additional contracted color states}
This evaluator involves no square or multiplication, its matrix
elements are just copies of the (single) input interaction. However,
the state matrix of the interaction contains additional states that
have color indices contracted. This is used for copies of the beam or
structure-function interactions that need to match the hard
interaction also in the case where its color indices coincide.
<<Evaluators: evaluator: TBP>>=
procedure :: init_color_contractions => evaluator_init_color_contractions
<<Evaluators: procedures>>=
subroutine evaluator_init_color_contractions (eval, int_in)
class(evaluator_t), intent(out), target :: eval
type(interaction_t), intent(in), target :: int_in
integer :: n_in, n_vir, n_out, n_tot
type(state_matrix_t) :: state_with_contractions
integer, dimension(:), allocatable :: me_index
integer, dimension(:), allocatable :: result_index
eval%type = EVAL_COLOR_CONTRACTION
eval%int_in1 => int_in
n_in = int_in%get_n_in ()
n_vir = int_in%get_n_vir ()
n_out = int_in%get_n_out ()
n_tot = int_in%get_n_tot ()
state_with_contractions = int_in%get_state_matrix_ptr ()
call state_with_contractions%add_color_contractions ()
call make_contracted_interaction (eval%interaction_t, &
me_index, result_index, &
n_in, n_vir, n_out, n_tot, &
state_with_contractions, int_in%get_mask ())
call make_pairing_array (eval%pairing_array, me_index, result_index)
call record_links (eval, int_in, n_tot)
call state_with_contractions%final ()
contains
subroutine make_contracted_interaction (int, &
me_index, result_index, &
n_in, n_vir, n_out, n_tot, state, qn_mask)
type(interaction_t), intent(out), target :: int
integer, dimension(:), intent(out), allocatable :: me_index
integer, dimension(:), intent(out), allocatable :: result_index
integer, intent(in) :: n_in, n_vir, n_out, n_tot
type(state_matrix_t), intent(in) :: state
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
type(state_iterator_t) :: it
integer :: n_me, i
type(quantum_numbers_t), dimension(n_tot) :: qn
call int%basic_init (n_in, n_vir, n_out, mask=qn_mask)
n_me = state%get_n_leaves ()
allocate (me_index (n_me))
allocate (result_index (n_me))
call it%init (state)
i = 0
do while (it%is_valid ())
i = i + 1
me_index(i) = it%get_me_index ()
qn = it%get_quantum_numbers ()
call int%add_state (qn, me_index = result_index(i))
call it%advance ()
end do
call int%freeze ()
end subroutine make_contracted_interaction
subroutine make_pairing_array (pa, me_index, result_index)
type(pairing_array_t), dimension(:), intent(out), allocatable :: pa
integer, dimension(:), intent(in) :: me_index, result_index
integer, dimension(:), allocatable :: n_entries
integer :: n_matrix_elements, r, i
n_matrix_elements = size (me_index)
allocate (pa (n_matrix_elements))
allocate (n_entries (n_matrix_elements))
n_entries = 1
call pairing_array_init &
(pa, n_entries, has_i2=.false., has_factor=.false.)
do i = 1, n_matrix_elements
r = result_index(i)
pa(r)%i1(1) = me_index(i)
end do
end subroutine make_pairing_array
subroutine record_links (int, int_in, n_tot)
class(interaction_t), intent(inout) :: int
class(interaction_t), intent(in), target :: int_in
integer, intent(in) :: n_tot
integer, dimension(n_tot) :: map
integer :: i
do i = 1, n_tot
call int%set_source_link (i, int_in, i)
end do
map = [ (i, i = 1, n_tot) ]
call int_in%transfer_relations (int, map)
end subroutine record_links
end subroutine evaluator_init_color_contractions
@ %def evaluator_init_color_contractions
@
\subsubsection{Auxiliary procedure for initialization}
This will become a standard procedure in F2008. The result is true if
the number of true values in the mask is odd. We use the function for
determining the ghost parity of a quantum-number array.
[tho:] It's not used anymore and [[mod (count (mask), 2) == 1]] is
a cooler implementation anyway.
<<(UNUSED) Evaluators: procedures>>=
function parity (mask)
logical :: parity
logical, dimension(:) :: mask
integer :: i
parity = .false.
do i = 1, size (mask)
if (mask(i)) parity = .not. parity
end do
end function parity
@ %def parity
@ Reassign external source links from one to another.
<<Evaluators: public>>=
public :: evaluator_reassign_links
<<Evaluators: interfaces>>=
interface evaluator_reassign_links
module procedure evaluator_reassign_links_eval
module procedure evaluator_reassign_links_int
end interface
<<Evaluators: procedures>>=
subroutine evaluator_reassign_links_eval (eval, eval_src, eval_target)
type(evaluator_t), intent(inout) :: eval
type(evaluator_t), intent(in) :: eval_src
type(evaluator_t), intent(in), target :: eval_target
if (associated (eval%int_in1)) then
if (eval%int_in1%get_tag () == eval_src%get_tag ()) then
eval%int_in1 => eval_target%interaction_t
end if
end if
if (associated (eval%int_in2)) then
if (eval%int_in2%get_tag () == eval_src%get_tag ()) then
eval%int_in2 => eval_target%interaction_t
end if
end if
call interaction_reassign_links &
(eval%interaction_t, eval_src%interaction_t, &
eval_target%interaction_t)
end subroutine evaluator_reassign_links_eval
subroutine evaluator_reassign_links_int (eval, int_src, int_target)
type(evaluator_t), intent(inout) :: eval
type(interaction_t), intent(in) :: int_src
type(interaction_t), intent(in), target :: int_target
if (associated (eval%int_in1)) then
if (eval%int_in1%get_tag () == int_src%get_tag ()) then
eval%int_in1 => int_target
end if
end if
if (associated (eval%int_in2)) then
if (eval%int_in2%get_tag () == int_src%get_tag ()) then
eval%int_in2 => int_target
end if
end if
call interaction_reassign_links (eval%interaction_t, int_src, int_target)
end subroutine evaluator_reassign_links_int
@ %def evaluator_reassign_links
@ Return flavor, momentum, and position of the first unstable particle
present in the interaction.
<<Evaluators: public>>=
public :: evaluator_get_unstable_particle
<<Evaluators: procedures>>=
subroutine evaluator_get_unstable_particle (eval, flv, p, i)
type(evaluator_t), intent(in) :: eval
type(flavor_t), intent(out) :: flv
type(vector4_t), intent(out) :: p
integer, intent(out) :: i
call interaction_get_unstable_particle (eval%interaction_t, flv, p, i)
end subroutine evaluator_get_unstable_particle
@ %def evaluator_get_unstable_particle
@
<<Evaluators: public>>=
public :: evaluator_get_int_in_ptr
<<Evaluators: procedures>>=
function evaluator_get_int_in_ptr (eval, i) result (int_in)
class(interaction_t), pointer :: int_in
type(evaluator_t), intent(in), target :: eval
integer, intent(in) :: i
if (i == 1) then
int_in => eval%int_in1
else if (i == 2) then
int_in => eval%int_in2
else
int_in => null ()
end if
end function evaluator_get_int_in_ptr
@ %def evaluator_get_int_in_ptr
@
\subsection{Creating an evaluator: identity}
The identity evaluator creates a copy of the first input evaluator; the second
input is not used.
All particles link back to the input evaluatorand the internal
relations are copied. As evaluation does take a shortcut by cloning the matrix
elements, the pairing array is not used and does not have to be set up.
<<Evaluators: evaluator: TBP>>=
procedure :: init_identity => evaluator_init_identity
<<Evaluators: procedures>>=
subroutine evaluator_init_identity (eval, int)
class(evaluator_t), intent(out), target :: eval
class(interaction_t), intent(in), target :: int
integer :: n_in, n_out, n_vir, n_tot
integer :: i
integer, dimension(:), allocatable :: map
type(state_matrix_t), pointer :: state
type(state_iterator_t) :: it
eval%type = EVAL_IDENTITY
eval%int_in1 => int
nullify (eval%int_in2)
n_in = int%get_n_in ()
n_out = int%get_n_out ()
n_vir = int%get_n_vir ()
n_tot = int%get_n_tot ()
call eval%interaction_t%basic_init (n_in, n_vir, n_out, &
mask = int%get_mask (), &
resonant = int%get_resonance_flags ())
do i = 1, n_tot
call eval%set_source_link (i, int, i)
end do
allocate (map(n_tot))
map = [(i, i = 1, n_tot)]
call int%transfer_relations (eval, map)
state => int%get_state_matrix_ptr ()
call it%init (state)
do while (it%is_valid ())
call eval%add_state (it%get_quantum_numbers (), &
it%get_me_index ())
call it%advance ()
end do
call eval%freeze ()
end subroutine evaluator_init_identity
@ %def evaluator_init_identity
@
\subsection {Creating an evaluator: quantum number sum}
This evaluator operates on the diagonal of a density matrix and sums over the
quantum numbers specified by the mask. The optional argument [[drop]] allows to
drop a particle from the resulting density matrix. The handling of virtuals is
not completely sane, especially in connection with dropping particles.
When summing over matrix element entries, we keep the separation into
entries and normalization (in the corresponding evaluation routine below).
<<Evaluators: evaluator: TBP>>=
procedure :: init_qn_sum => evaluator_init_qn_sum
<<Evaluators: procedures>>=
subroutine evaluator_init_qn_sum (eval, int, qn_mask, drop)
class(evaluator_t), intent(out), target :: eval
class(interaction_t), target, intent(in) :: int
type(quantum_numbers_mask_t), dimension(:), intent(in) :: qn_mask
logical, intent(in), optional, dimension(:) :: drop
type(state_iterator_t) :: it_old, it_new
integer, dimension(:), allocatable :: pairing_size, pairing_target, i_new
integer, dimension(:), allocatable :: map
integer :: n_in, n_out, n_vir, n_tot, n_me_old, n_me_new
integer :: i, j
type(state_matrix_t), pointer :: state_new, state_old
type(quantum_numbers_t), dimension(:), allocatable :: qn
logical :: matched
logical, dimension(size (qn_mask)) :: dropped
integer :: ndropped
integer, dimension(:), allocatable :: inotdropped
type(quantum_numbers_mask_t), dimension(:), allocatable :: mask
logical, dimension(:), allocatable :: resonant
eval%type = EVAL_QN_SUM
eval%int_in1 => int
nullify (eval%int_in2)
if (present (drop)) then
dropped = drop
else
dropped = .false.
end if
ndropped = count (dropped)
n_in = int%get_n_in ()
n_out = int%get_n_out () - ndropped
n_vir = int%get_n_vir ()
n_tot = int%get_n_tot () - ndropped
allocate (inotdropped (n_tot))
i = 1
do j = 1, n_tot + ndropped
if (dropped (j)) cycle
inotdropped(i) = j
i = i + 1
end do
allocate (mask(n_tot + ndropped))
mask = int%get_mask ()
allocate (resonant(n_tot + ndropped))
resonant = int%get_resonance_flags ()
call eval%interaction_t%basic_init (n_in, n_vir, n_out, &
mask = mask(inotdropped) .or. qn_mask(inotdropped), &
resonant = resonant(inotdropped))
i = 1
do j = 1, n_tot + ndropped
if (dropped(j)) cycle
call eval%set_source_link (i, int, j)
i = i + 1
end do
allocate (map(n_tot + ndropped))
i = 1
do j = 1, n_tot + ndropped
if (dropped (j)) then
map(j) = 0
else
map(j) = i
i = i + 1
end if
end do
call int%transfer_relations (eval, map)
n_me_old = int%get_n_matrix_elements ()
allocate (pairing_size (n_me_old), source = 0)
allocate (pairing_target (n_me_old), source = 0)
pairing_size = 0
state_old => int%get_state_matrix_ptr ()
state_new => eval%get_state_matrix_ptr ()
call it_old%init (state_old)
allocate (qn(n_tot + ndropped))
do while (it_old%is_valid ())
qn = it_old%get_quantum_numbers ()
if (.not. all (qn%are_diagonal ())) then
call it_old%advance ()
cycle
end if
matched = .false.
call it_new%init (state_new)
if (eval%get_n_matrix_elements () > 0) then
do while (it_new%is_valid ())
if (all (qn(inotdropped) .match. &
it_new%get_quantum_numbers ())) &
then
matched = .true.
i = it_new%get_me_index ()
exit
end if
call it_new%advance ()
end do
end if
if (.not. matched) then
call eval%add_state (qn(inotdropped))
i = eval%get_n_matrix_elements ()
end if
pairing_size(i) = pairing_size(i) + 1
pairing_target(it_old%get_me_index ()) = i
call it_old%advance ()
end do
call eval%freeze ()
n_me_new = eval%get_n_matrix_elements ()
allocate (eval%pairing_array (n_me_new))
do i = 1, n_me_new
call pairing_array_init (eval%pairing_array(i), &
pairing_size(i), .false., .false.)
end do
allocate (i_new (n_me_new), source = 0)
do i = 1, n_me_old
j = pairing_target(i)
if (j > 0) then
i_new(j) = i_new(j) + 1
eval%pairing_array(j)%i1(i_new(j)) = i
end if
end do
end subroutine evaluator_init_qn_sum
@ %def evaluator_init_qn_sum
@
\subsection{Evaluation}
When the input interactions (which are pointed to in the pairings
stored within the evaluator) are filled with values, we can activate
the evaluator, i.e., calculate the result values which are stored in
the interaction.
The evaluation of matrix elements can be done in parallel. A
[[forall]] construct is not appropriate, however. We would need
[[do concurrent]] here. Nevertheless, the evaluation functions are
marked as [[pure]].
<<Evaluators: evaluator: TBP>>=
procedure :: evaluate => evaluator_evaluate
<<Evaluators: procedures>>=
subroutine evaluator_evaluate (eval)
class(evaluator_t), intent(inout), target :: eval
integer :: i
select case (eval%type)
case (EVAL_PRODUCT)
do i = 1, size(eval%pairing_array)
call eval%evaluate_product (i, &
eval%int_in1, eval%int_in2, &
eval%pairing_array(i)%i1, eval%pairing_array(i)%i2)
if (debug2_active (D_QFT)) then
print *, 'eval%pairing_array(i)%i1, eval%pairing_array(i)%i2 = ', &
eval%pairing_array(i)%i1, eval%pairing_array(i)%i2
print *, 'MEs = ', &
eval%int_in1%get_matrix_element (eval%pairing_array(i)%i1), &
eval%int_in2%get_matrix_element (eval%pairing_array(i)%i2)
end if
end do
case (EVAL_SQUARE_WITH_COLOR_FACTORS)
do i = 1, size(eval%pairing_array)
call eval%evaluate_product_cf (i, &
eval%int_in1, eval%int_in1, &
eval%pairing_array(i)%i1, eval%pairing_array(i)%i2, &
eval%pairing_array(i)%factor)
end do
case (EVAL_SQUARED_FLOWS)
do i = 1, size(eval%pairing_array)
call eval%evaluate_square_c (i, &
eval%int_in1, &
eval%pairing_array(i)%i1)
end do
case (EVAL_COLOR_CONTRACTION)
do i = 1, size(eval%pairing_array)
call eval%evaluate_sum (i, &
eval%int_in1, &
eval%pairing_array(i)%i1)
end do
case (EVAL_IDENTITY)
call eval%set_matrix_element (eval%int_in1)
case (EVAL_QN_SUM)
do i = 1, size (eval%pairing_array)
call eval%evaluate_me_sum (i, &
eval%int_in1, eval%pairing_array(i)%i1)
call eval%set_norm (eval%int_in1%get_norm ())
end do
end select
end subroutine evaluator_evaluate
@ %def evaluator_evaluate
@
\subsection{Unit tests}
Test module, followed by the corresponding implementation module.
<<[[evaluators_ut.f90]]>>=
<<File header>>
module evaluators_ut
use unit_tests
use evaluators_uti
<<Standard module head>>
<<Evaluators: public test>>
contains
<<Evaluators: test driver>>
end module evaluators_ut
@ %def evaluators_ut
@
<<[[evaluators_uti.f90]]>>=
<<File header>>
module evaluators_uti
<<Use kinds>>
use lorentz
use flavors
use colors
use helicities
use quantum_numbers
use interactions
use model_data
use evaluators
<<Standard module head>>
<<Evaluators: test declarations>>
contains
<<Evaluators: tests>>
end module evaluators_uti
@ %def evaluators_ut
@ API: driver for the unit tests below.
<<Evaluators: public test>>=
public :: evaluator_test
<<Evaluators: test driver>>=
subroutine evaluator_test (u, results)
integer, intent(in) :: u
type(test_results_t), intent(inout) :: results
<<Evaluators: execute tests>>
end subroutine evaluator_test
@ %def evaluator_test
@ Test: Create two interactions. The interactions are twofold
connected. The first connection has a helicity index that is kept,
the second connection has a helicity index that is summed over.
Concatenate the interactions in an evaluator, which thus contains a
result interaction. Fill the input interactions with values, activate
the evaluator and print the result.
<<Evaluators: execute tests>>=
call test (evaluator_1, "evaluator_1", &
"check evaluators (1)", &
u, results)
<<Evaluators: test declarations>>=
public :: evaluator_1
<<Evaluators: tests>>=
subroutine evaluator_1 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(interaction_t), target :: int_qqtt, int_tbw, int1, int2
type(flavor_t), dimension(:), allocatable :: flv
type(color_t), dimension(:), allocatable :: col
type(helicity_t), dimension(:), allocatable :: hel
type(quantum_numbers_t), dimension(:), allocatable :: qn
integer :: f, c, h1, h2, h3
type(vector4_t), dimension(4) :: p
type(vector4_t), dimension(2) :: q
type(quantum_numbers_mask_t) :: qn_mask_conn
type(quantum_numbers_mask_t), dimension(:), allocatable :: qn_mask2
type(evaluator_t), target :: eval, eval2, eval3
call model%init_sm_test ()
write (u, "(A)") "*** Evaluator for matrix product"
write (u, "(A)") "*** Construct interaction for qq -> tt"
write (u, "(A)")
call int_qqtt%basic_init (2, 0, 2, set_relations=.true.)
allocate (flv (4), col (4), hel (4), qn (4))
allocate (qn_mask2 (4))
do c = 1, 2
select case (c)
case (1)
call col%init_col_acl ([1, 0, 1, 0], [0, 2, 0, 2])
case (2)
call col%init_col_acl ([1, 0, 2, 0], [0, 1, 0, 2])
end select
do f = 1, 2
call flv%init ([f, -f, 6, -6], model)
do h1 = -1, 1, 2
call hel(3)%init (h1)
do h2 = -1, 1, 2
call hel(4)%init (h2)
call qn%init (flv, col, hel)
call int_qqtt%add_state (qn)
end do
end do
end do
end do
call int_qqtt%freeze ()
deallocate (flv, col, hel, qn)
write (u, "(A)") "*** Construct interaction for t -> bW"
call int_tbw%basic_init (1, 0, 2, set_relations=.true.)
allocate (flv (3), col (3), hel (3), qn (3))
call flv%init ([6, 5, 24], model)
call col%init_col_acl ([1, 1, 0], [0, 0, 0])
do h1 = -1, 1, 2
call hel(1)%init (h1)
do h2 = -1, 1, 2
call hel(2)%init (h2)
do h3 = -1, 1
call hel(3)%init (h3)
call qn%init (flv, col, hel)
call int_tbw%add_state (qn)
end do
end do
end do
call int_tbw%freeze ()
deallocate (flv, col, hel, qn)
write (u, "(A)") "*** Link interactions"
call int_tbw%set_source_link (1, int_qqtt, 3)
qn_mask_conn = quantum_numbers_mask (.false.,.false.,.true.)
write (u, "(A)")
write (u, "(A)") "*** Show input"
call int_qqtt%basic_write (unit = u)
write (u, "(A)")
call int_tbw%basic_write (unit = u)
write (u, "(A)")
write (u, "(A)") "*** Evaluate product"
call eval%init_product (int_qqtt, int_tbw, qn_mask_conn)
call eval%write (unit = u)
call int1%basic_init (2, 0, 2, set_relations=.true.)
call int2%basic_init (1, 0, 2, set_relations=.true.)
p(1) = vector4_moving (1000._default, 1000._default, 3)
p(2) = vector4_moving (200._default, 200._default, 2)
p(3) = vector4_moving (100._default, 200._default, 1)
p(4) = p(1) - p(2) - p(3)
call int1%set_momenta (p)
q(1) = vector4_moving (50._default,-50._default, 3)
q(2) = p(2) + p(4) - q(1)
call int2%set_momenta (q, outgoing=.true.)
call int1%set_matrix_element ([(2._default,0._default), &
(4._default,1._default), (-3._default,0._default)])
call int2%set_matrix_element ([(-3._default,0._default), &
(0._default,1._default), (1._default,2._default)])
call eval%receive_momenta ()
call eval%evaluate ()
call int1%basic_write (unit = u)
write (u, "(A)")
call int2%basic_write (unit = u)
write (u, "(A)")
call eval%write (unit = u)
write (u, "(A)")
call int1%final ()
call int2%final ()
call eval%final ()
write (u, "(A)")
write (u, "(A)") "*** Evaluator for matrix square"
allocate (flv(4), col(4), qn(4))
call int1%basic_init (2, 0, 2, set_relations=.true.)
call flv%init ([1, -1, 21, 21], model)
call col(1)%init ([1])
call col(2)%init ([-2])
call col(3)%init ([2, -3])
call col(4)%init ([3, -1])
call qn%init (flv, col)
call int1%add_state (qn)
call col(3)%init ([3, -1])
call col(4)%init ([2, -3])
call qn%init (flv, col)
call int1%add_state (qn)
call col(3)%init ([2, -1])
call col(4)%init (.true.)
call qn%init (flv, col)
call int1%add_state (qn)
call int1%freeze ()
! [qn_mask2 not set since default is false]
call eval%init_square (int1, qn_mask2, nc=3)
call eval2%init_square_nondiag (int1, qn_mask2)
qn_mask2 = quantum_numbers_mask (.false., .true., .true.)
call eval3%init_square_diag (eval, qn_mask2)
call int1%set_matrix_element &
([(2._default,0._default), &
(4._default,1._default), (-3._default,0._default)])
call int1%set_momenta (p)
call int1%basic_write (unit = u)
write (u, "(A)")
call eval%receive_momenta ()
call eval%evaluate ()
call eval%write (unit = u)
write (u, "(A)")
call eval2%receive_momenta ()
call eval2%evaluate ()
call eval2%write (unit = u)
write (u, "(A)")
call eval3%receive_momenta ()
call eval3%evaluate ()
call eval3%write (unit = u)
call int1%final ()
call eval%final ()
call eval2%final ()
call eval3%final ()
call model%final ()
end subroutine evaluator_1
@ %def evaluator_1
@
<<Evaluators: execute tests>>=
call test (evaluator_2, "evaluator_2", &
"check evaluators (2)", &
u, results)
<<Evaluators: test declarations>>=
public :: evaluator_2
<<Evaluators: tests>>=
subroutine evaluator_2 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(interaction_t), target :: int
integer :: h1, h2, h3, h4
type(helicity_t), dimension(4) :: hel
type(color_t), dimension(4) :: col
type(flavor_t), dimension(4) :: flv
type(quantum_numbers_t), dimension(4) :: qn
type(vector4_t), dimension(4) :: p
type(evaluator_t) :: eval
integer :: i
call model%init_sm_test ()
write (u, "(A)") "*** Creating interaction for e+ e- -> W+ W-"
write (u, "(A)")
call flv%init ([11, -11, 24, -24], model)
do i = 1, 4
call col(i)%init ()
end do
call int%basic_init (2, 0, 2, set_relations=.true.)
do h1 = -1, 1, 2
call hel(1)%init (h1)
do h2 = -1, 1, 2
call hel(2)%init (h2)
do h3 = -1, 1
call hel(3)%init (h3)
do h4 = -1, 1
call hel(4)%init (h4)
call qn%init (flv, col, hel)
call int%add_state (qn)
end do
end do
end do
end do
call int%freeze ()
call int%set_matrix_element &
([(cmplx (i, kind=default), i = 1, 36)])
p(1) = vector4_moving (1000._default, 1000._default, 3)
p(2) = vector4_moving (1000._default, -1000._default, 3)
p(3) = vector4_moving (1000._default, &
sqrt (1E6_default - 80._default**2), 3)
p(4) = p(1) + p(2) - p(3)
call int%set_momenta (p)
write (u, "(A)") "*** Setting up evaluator"
write (u, "(A)")
call eval%init_identity (int)
write (u, "(A)") "*** Transferring momenta and evaluating"
write (u, "(A)")
call eval%receive_momenta ()
call eval%evaluate ()
write (u, "(A)") "*******************************************************"
write (u, "(A)") " Interaction dump"
write (u, "(A)") "*******************************************************"
call int%basic_write (unit = u)
write (u, "(A)")
write (u, "(A)") "*******************************************************"
write (u, "(A)") " Evaluator dump"
write (u, "(A)") "*******************************************************"
call eval%write (unit = u)
write (u, "(A)")
write (u, "(A)") "*** cleaning up"
call int%final ()
call eval%final ()
call model%final ()
end subroutine evaluator_2
@ %def evaluator_2
@
<<Evaluators: execute tests>>=
call test (evaluator_3, "evaluator_3", &
"check evaluators (3)", &
u, results)
<<Evaluators: test declarations>>=
public :: evaluator_3
<<Evaluators: tests>>=
subroutine evaluator_3 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(interaction_t), target :: int
integer :: h1, h2, h3, h4
type(helicity_t), dimension(4) :: hel
type(color_t), dimension(4) :: col
type(flavor_t), dimension(4) :: flv1, flv2
type(quantum_numbers_t), dimension(4) :: qn
type(vector4_t), dimension(4) :: p
type(evaluator_t) :: eval1, eval2, eval3
type(quantum_numbers_mask_t), dimension(4) :: qn_mask
integer :: i
call model%init_sm_test ()
write (u, "(A)") "*** Creating interaction for e+/mu+ e-/mu- -> W+ W-"
call flv1%init ([11, -11, 24, -24], model)
call flv2%init ([13, -13, 24, -24], model)
do i = 1, 4
call col (i)%init ()
end do
call int%basic_init (2, 0, 2, set_relations=.true.)
do h1 = -1, 1, 2
call hel(1)%init (h1)
do h2 = -1, 1, 2
call hel(2)%init (h2)
do h3 = -1, 1
call hel(3)%init (h3)
do h4 = -1, 1
call hel(4)%init (h4)
call qn%init (flv1, col, hel)
call int%add_state (qn)
call qn%init (flv2, col, hel)
call int%add_state (qn)
end do
end do
end do
end do
call int%freeze ()
call int%set_matrix_element &
([(cmplx (1, kind=default), i = 1, 72)])
p(1) = vector4_moving (1000._default, 1000._default, 3)
p(2) = vector4_moving (1000._default, -1000._default, 3)
p(3) = vector4_moving (1000._default, &
sqrt (1E6_default - 80._default**2), 3)
p(4) = p(1) + p(2) - p(3)
call int%set_momenta (p)
write (u, "(A)") "*** Setting up evaluators"
call qn_mask%init (.false., .true., .true.)
call eval1%init_qn_sum (int, qn_mask)
call qn_mask%init (.true., .true., .true.)
call eval2%init_qn_sum (int, qn_mask)
call qn_mask%init (.false., .true., .false.)
call eval3%init_qn_sum (int, qn_mask, &
[.false., .false., .false., .true.])
write (u, "(A)") "*** Transferring momenta and evaluating"
call eval1%receive_momenta ()
call eval1%evaluate ()
call eval2%receive_momenta ()
call eval2%evaluate ()
call eval3%receive_momenta ()
call eval3%evaluate ()
write (u, "(A)") "*******************************************************"
write (u, "(A)") " Interaction dump"
write (u, "(A)") "*******************************************************"
call int%basic_write (unit = u)
write (u, "(A)")
write (u, "(A)") "*******************************************************"
write (u, "(A)") " Evaluator dump --- spin sum"
write (u, "(A)") "*******************************************************"
call eval1%write (unit = u)
call eval1%basic_write (unit = u)
write (u, "(A)") "*******************************************************"
write (u, "(A)") " Evaluator dump --- spin / flavor sum"
write (u, "(A)") "*******************************************************"
call eval2%write (unit = u)
call eval2%basic_write (unit = u)
write (u, "(A)") "*******************************************************"
write (u, "(A)") " Evaluator dump --- flavor sum, drop last W"
write (u, "(A)") "*******************************************************"
call eval3%write (unit = u)
call eval3%basic_write (unit = u)
write (u, "(A)")
write (u, "(A)") "*** cleaning up"
call int%final ()
call eval1%final ()
call eval2%final ()
call eval3%final ()
call model%final ()
end subroutine evaluator_3
@ %def evaluator_3
@ This test evaluates a product with different quantum-number masks and
filters for the linked entry.
<<Evaluators: execute tests>>=
call test (evaluator_4, "evaluator_4", &
"check evaluator product with filter", &
u, results)
<<Evaluators: test declarations>>=
public :: evaluator_4
<<Evaluators: tests>>=
subroutine evaluator_4 (u)
integer, intent(in) :: u
type(model_data_t), target :: model
type(interaction_t), target :: int1, int2
integer :: h1, h2, h3
type(helicity_t), dimension(3) :: hel
type(color_t), dimension(3) :: col
type(flavor_t), dimension(2) :: flv1, flv2
type(flavor_t), dimension(3) :: flv3, flv4
type(quantum_numbers_t), dimension(3) :: qn
type(evaluator_t) :: eval1, eval2, eval3, eval4
type(quantum_numbers_mask_t) :: qn_mask
type(flavor_t) :: flv_filter
type(helicity_t) :: hel_filter
type(color_t) :: col_filter
type(quantum_numbers_t) :: qn_filter
integer :: i
write (u, "(A)") "* Test output: evaluator_4"
write (u, "(A)") "* Purpose: test evaluator products &
&with mask and filter"
write (u, "(A)")
call model%init_sm_test ()
write (u, "(A)") "* Creating interaction for e- -> W+/Z"
write (u, "(A)")
call flv1%init ([11, 24], model)
call flv2%init ([11, 23], model)
do i = 1, 3
call col(i)%init ()
end do
call int1%basic_init (1, 0, 1, set_relations=.true.)
do h1 = -1, 1, 2
call hel(1)%init (h1)
do h2 = -1, 1
call hel(2)%init (h2)
call qn(:2)%init (flv1, col(:2), hel(:2))
call int1%add_state (qn(:2))
call qn(:2)%init (flv2, col(:2), hel(:2))
call int1%add_state (qn(:2))
end do
end do
call int1%freeze ()
call int1%basic_write (u)
write (u, "(A)")
write (u, "(A)") "* Creating interaction for W+/Z -> u ubar/dbar"
write (u, "(A)")
call flv3%init ([24, 2, -1], model)
call flv4%init ([23, 2, -2], model)
call int2%basic_init (1, 0, 2, set_relations=.true.)
do h1 = -1, 1
call hel(1)%init (h1)
do h2 = -1, 1, 2
call hel(2)%init (h2)
do h3 = -1, 1, 2
call hel(3)%init (h3)
call qn(:3)%init (flv3, col(:3), hel(:3))
call int2%add_state (qn(:3))
call qn(:3)%init (flv4, col(:3), hel(:3))
call int2%add_state (qn(:3))
end do
end do
end do
call int2%freeze ()
call int2%set_source_link (1, int1, 2)
call int2%basic_write (u)
write (u, "(A)")
write (u, "(A)") "* Product evaluator"
write (u, "(A)")
call qn_mask%init (.false., .false., .false.)
call eval1%init_product (int1, int2, qn_mask_conn = qn_mask)
call eval1%write (u)
write (u, "(A)")
write (u, "(A)") "* Product evaluator with helicity mask"
write (u, "(A)")
call qn_mask%init (.false., .false., .true.)
call eval2%init_product (int1, int2, qn_mask_conn = qn_mask)
call eval2%write (u)
write (u, "(A)")
write (u, "(A)") "* Product with flavor filter and helicity mask"
write (u, "(A)")
call qn_mask%init (.false., .false., .true.)
call flv_filter%init (24, model)
call hel_filter%init ()
call col_filter%init ()
call qn_filter%init (flv_filter, col_filter, hel_filter)
call eval3%init_product (int1, int2, &
qn_mask_conn = qn_mask, qn_filter_conn = qn_filter)
call eval3%write (u)
write (u, "(A)")
write (u, "(A)") "* Product with helicity filter and mask"
write (u, "(A)")
call qn_mask%init (.false., .false., .true.)
call flv_filter%init ()
call hel_filter%init (0)
call col_filter%init ()
call qn_filter%init (flv_filter, col_filter, hel_filter)
call eval4%init_product (int1, int2, &
qn_mask_conn = qn_mask, qn_filter_conn = qn_filter)
call eval4%write (u)
write (u, "(A)")
write (u, "(A)") "* Cleanup"
call eval1%final ()
call eval2%final ()
call eval3%final ()
call eval4%final ()
call int1%final ()
call int2%final ()
call model%final ()
write (u, "(A)")
write (u, "(A)") "* Test output end: evaluator_4"
end subroutine evaluator_4
@ %def evaluator_4
-
-

File Metadata

Mime Type
application/octet-stream
Expires
Sun, Apr 28, 2:24 AM (1 d, 23 h)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
H8B80_IQ3e9F
Default Alt Text
(4 MB)

Event Timeline