smodels 3.0.0__tar.gz → 3.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. {smodels-3.0.0/smodels.egg-info → smodels-3.0.2}/PKG-INFO +2 -2
  2. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/runtime.py +12 -4
  3. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/parameters_default.ini +0 -1
  4. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/databaseObj.py +28 -25
  5. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/infoObj.py +1 -1
  6. smodels-3.0.2/smodels/matching/clusterTools.py +476 -0
  7. {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/modelTester.py +23 -11
  8. {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/theoryPrediction.py +8 -4
  9. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/BANNER +2 -2
  10. {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/basicStats.py +4 -4
  11. {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/pyhfInterface.py +81 -57
  12. {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/simplifiedLikelihoods.py +1 -1
  13. smodels-3.0.2/smodels/statistics/speyTools.py +650 -0
  14. {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/statsTools.py +12 -1
  15. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/xsecResummino.py +67 -44
  16. smodels-3.0.2/smodels/version +1 -0
  17. {smodels-3.0.0 → smodels-3.0.2/smodels.egg-info}/PKG-INFO +2 -2
  18. {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/SOURCES.txt +1 -0
  19. smodels-3.0.0/smodels/matching/clusterTools.py +0 -521
  20. smodels-3.0.0/smodels/version +0 -1
  21. {smodels-3.0.0 → smodels-3.0.2}/MANIFEST.in +0 -0
  22. {smodels-3.0.0 → smodels-3.0.2}/Makefile +0 -0
  23. {smodels-3.0.0 → smodels-3.0.2}/README.rst +0 -0
  24. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/lhe/gluino_squarks.lhe +0 -0
  25. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/lhe/simplyGluino.lhe +0 -0
  26. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/TRV1_1800_300_300.slha +0 -0
  27. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/complicated.slha +0 -0
  28. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/ew_ymi2l51r.slha +0 -0
  29. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/gluinoToTops.slha +0 -0
  30. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/gluino_squarks.slha +0 -0
  31. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/higgsinoStop.slha +0 -0
  32. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/higgsino_spectrum_520_125_dm_10.slha +0 -0
  33. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/higgsino_spectrum_520_125_dm_4.slha +0 -0
  34. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/idm_example.slha +0 -0
  35. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/lightEWinos.slha +0 -0
  36. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/lightEWinos_simple.slha +0 -0
  37. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/longLived.slha +0 -0
  38. {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/simplyGluino.slha +0 -0
  39. {smodels-3.0.0 → smodels-3.0.2}/setup.cfg +0 -0
  40. {smodels-3.0.0 → smodels-3.0.2}/setup.py +0 -0
  41. {smodels-3.0.0 → smodels-3.0.2}/smodels/COPYING +0 -0
  42. {smodels-3.0.0 → smodels-3.0.2}/smodels/INSTALLATION.rst +0 -0
  43. {smodels-3.0.0 → smodels-3.0.2}/smodels/README.rst +0 -0
  44. {smodels-3.0.0 → smodels-3.0.2}/smodels/__init__.py +0 -0
  45. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/__init__.py +0 -0
  46. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/crossSection.py +0 -0
  47. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/exceptions.py +0 -0
  48. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/genericSMS.py +0 -0
  49. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/inclusiveObjects.py +0 -0
  50. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/lheReader.py +0 -0
  51. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/model.py +0 -0
  52. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/particle.py +0 -0
  53. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/particleNode.py +0 -0
  54. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/physicsUnits.py +0 -0
  55. {smodels-3.0.0 → smodels-3.0.2}/smodels/base/smodelsLogging.py +0 -0
  56. {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/__init__.py +0 -0
  57. {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/decomposer.py +0 -0
  58. {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/exceptions.py +0 -0
  59. {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/theorySMS.py +0 -0
  60. {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/topologyDict.py +0 -0
  61. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/input_resummino.in +0 -0
  62. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/iplots_parameters.py +0 -0
  63. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/iplots_parameters_IDM.py +0 -0
  64. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/logging.conf +0 -0
  65. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/logging_consoleonly.conf +0 -0
  66. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/logging_file.conf +0 -0
  67. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/pythia.card +0 -0
  68. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/pythia8.cfg +0 -0
  69. {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/resummino.py +0 -0
  70. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/__init__.py +0 -0
  71. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/datasetObj.py +0 -0
  72. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/defaultFinalStates.py +0 -0
  73. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/exceptions.py +0 -0
  74. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/expAuxiliaryFuncs.py +0 -0
  75. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/expResultObj.py +0 -0
  76. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/expSMS.py +0 -0
  77. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/expSMSDict.py +0 -0
  78. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/graphMatching.py +0 -0
  79. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/metaObj.py +0 -0
  80. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/reweighting.py +0 -0
  81. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/txnameDataObj.py +0 -0
  82. {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/txnameObj.py +0 -0
  83. {smodels-3.0.0 → smodels-3.0.2}/smodels/installation.py +0 -0
  84. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/Makefile +0 -0
  85. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/Makefile +0 -0
  86. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/Makefile +0 -0
  87. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gdcpl_nllnlo_cteq6.grid +0 -0
  88. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gdcpl_nllnlo_mstw2008.grid +0 -0
  89. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gg_nllnlo_cteq6.grid +0 -0
  90. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gg_nllnlo_hm_cteq6.grid +0 -0
  91. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gg_nllnlo_hm_mstw2008.grid +0 -0
  92. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gg_nllnlo_mstw2008.grid +0 -0
  93. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/nllfast-1.2.f +0 -0
  94. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sb_nllnlo_cteq6.grid +0 -0
  95. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sb_nllnlo_mstw2008.grid +0 -0
  96. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sdcpl_nllnlo_cteq6.grid +0 -0
  97. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sdcpl_nllnlo_mstw2008.grid +0 -0
  98. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sg_nllnlo_cteq6.grid +0 -0
  99. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sg_nllnlo_hm_cteq6.grid +0 -0
  100. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sg_nllnlo_hm_mstw2008.grid +0 -0
  101. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sg_nllnlo_mstw2008.grid +0 -0
  102. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/ss_nllnlo_cteq6.grid +0 -0
  103. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/ss_nllnlo_mstw2008.grid +0 -0
  104. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/st_nllnlo_cteq6.grid +0 -0
  105. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/st_nllnlo_mstw2008.grid +0 -0
  106. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/tot_nllnlo_cteq6.grid +0 -0
  107. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/tot_nllnlo_mstw2008.grid +0 -0
  108. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/Makefile +0 -0
  109. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gdcpl_nllnlo_cteq6.grid +0 -0
  110. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gdcpl_nllnlo_mstw2008.grid +0 -0
  111. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gg_nllnlo_cteq6.grid +0 -0
  112. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gg_nllnlo_hm_cteq6.grid +0 -0
  113. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gg_nllnlo_hm_mstw2008.grid +0 -0
  114. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gg_nllnlo_mstw2008.grid +0 -0
  115. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/nllfast-2.1.f +0 -0
  116. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sb_nllnlo_cteq6.grid +0 -0
  117. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sb_nllnlo_mstw2008.grid +0 -0
  118. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sdcpl_nllnlo_cteq6.grid +0 -0
  119. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sdcpl_nllnlo_mstw2008.grid +0 -0
  120. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sg_nllnlo_cteq6.grid +0 -0
  121. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sg_nllnlo_hm_cteq6.grid +0 -0
  122. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sg_nllnlo_hm_mstw2008.grid +0 -0
  123. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sg_nllnlo_mstw2008.grid +0 -0
  124. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/ss_nllnlo_cteq6.grid +0 -0
  125. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/ss_nllnlo_mstw2008.grid +0 -0
  126. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/st_nllnlo_cteq6.grid +0 -0
  127. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/st_nllnlo_mstw2008.grid +0 -0
  128. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/Makefile +0 -0
  129. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gdcpl_nllnlo_cteq66_13TeV.grid +0 -0
  130. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gdcpl_nllnlo_mstw2008_13TeV.grid +0 -0
  131. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gdcpl_nllnlo_nnpdf30_13TeV.grid +0 -0
  132. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_cteq66_13TeV.grid +0 -0
  133. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_hm_cteq66_13TeV.grid +0 -0
  134. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_hm_mstw2008_13TeV.grid +0 -0
  135. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_hm_nnpdf30_13TeV.grid +0 -0
  136. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_mstw2008_13TeV.grid +0 -0
  137. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_nnpdf30_13TeV.grid +0 -0
  138. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/nllfast-3.1-13TeV.f +0 -0
  139. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sb_nllnlo_cteq66_13TeV.grid +0 -0
  140. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sb_nllnlo_mstw2008_13TeV.grid +0 -0
  141. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sb_nllnlo_nnpdf30_13TeV.grid +0 -0
  142. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sdcpl_nllnlo_cteq66_13TeV.grid +0 -0
  143. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sdcpl_nllnlo_mstw2008_13TeV.grid +0 -0
  144. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sdcpl_nllnlo_nnpdf30_13TeV.grid +0 -0
  145. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_cteq66_13TeV.grid +0 -0
  146. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_hm_cteq66_13TeV.grid +0 -0
  147. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_hm_mstw2008_13TeV.grid +0 -0
  148. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_hm_nnpdf30_13TeV.grid +0 -0
  149. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_mstw2008_13TeV.grid +0 -0
  150. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_nnpdf30_13TeV.grid +0 -0
  151. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/ss_nllnlo_cteq66_13TeV.grid +0 -0
  152. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/ss_nllnlo_mstw2008_13TeV.grid +0 -0
  153. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/ss_nllnlo_nnpdf30_13TeV.grid +0 -0
  154. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/st_nllnlo_cteq66_13TeV.grid +0 -0
  155. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/st_nllnlo_mstw2008_13TeV.grid +0 -0
  156. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/st_nllnlo_nnpdf30_13TeV.grid +0 -0
  157. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia6/Makefile +0 -0
  158. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia6/pythia-6.4.27.f +0 -0
  159. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia6/pythia_lhe.f +0 -0
  160. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/Makefile +0 -0
  161. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/installer.py +0 -0
  162. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/parseXml.py +0 -0
  163. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/pythia8.cc +0 -0
  164. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/pythiaversion +0 -0
  165. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/xml.doc +0 -0
  166. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/resummino/Makefile +0 -0
  167. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/resummino/clean.sh +0 -0
  168. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/resummino/install.sh +0 -0
  169. {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/resummino/versions.txt +0 -0
  170. {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/__init__.py +0 -0
  171. {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/exceptions.py +0 -0
  172. {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/matcherAuxiliaryFuncs.py +0 -0
  173. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/__init__.py +0 -0
  174. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/SMparticles.py +0 -0
  175. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/__init__.py +0 -0
  176. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/dgmssm.py +0 -0
  177. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/idm.py +0 -0
  178. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/mssm.py +0 -0
  179. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/mssmQNumbers.slha +0 -0
  180. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/nmssm.py +0 -0
  181. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/zprime.slha +0 -0
  182. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/pythia_test.card +0 -0
  183. {smodels-3.0.0 → smodels-3.0.2}/smodels/share/requirements.txt +0 -0
  184. {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/__init__.py +0 -0
  185. {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/analysesCombinations.py +0 -0
  186. {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/exceptions.py +0 -0
  187. {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/truncatedGaussians.py +0 -0
  188. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/__init__.py +0 -0
  189. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/asciiGraph.py +0 -0
  190. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/caching.py +0 -0
  191. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/coverage.py +0 -0
  192. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/crashReport.py +0 -0
  193. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/databaseBrowser.py +0 -0
  194. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/databaseClient.py +0 -0
  195. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/databaseServer.py +0 -0
  196. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/externalPythonTools.py +0 -0
  197. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/interactivePlots.py +0 -0
  198. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/interactivePlotsHelpers.py +0 -0
  199. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/ioObjects.py +0 -0
  200. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/lheChecks.py +0 -0
  201. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/nllFastWrapper.py +0 -0
  202. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/particlesLoader.py +0 -0
  203. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/printerTools.py +0 -0
  204. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/pythia6Wrapper.py +0 -0
  205. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/pythia8Wrapper.py +0 -0
  206. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/pythia8particles.py +0 -0
  207. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/runSModelS.py +0 -0
  208. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/slhaChecks.py +0 -0
  209. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/smodelsTools.py +0 -0
  210. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/timeOut.py +0 -0
  211. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/toolBox.py +0 -0
  212. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/wrapperBase.py +0 -0
  213. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/xsecBase.py +0 -0
  214. {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/xsecComputer.py +0 -0
  215. {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/dependency_links.txt +0 -0
  216. {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/entry_points.txt +0 -0
  217. {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/requires.txt +0 -0
  218. {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/top_level.txt +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: smodels
3
- Version: 3.0.0
3
+ Version: 3.0.2
4
4
  Summary: A tool for interpreting simplified-model results from the LHC
5
5
  Home-page: http://smodels.github.io/
6
- Author: The SModelS collaboration, smodels-users@lists.oeaw.ac.at Current members: Mohammad Mahdi Altakach, Sabine Kraml, Andre Lessa, Sahana Narashima, Timothee Pascal, Camila Ramos, Humberto Reyes-Gonzalez, Yoxara Villamizar, Wolfgang Waltenberger Previously involved in SModelS: Gael Alguero, Federico Ambrogi, Juhi Dutta, Jan Heisig, Charanjit K. Khosa, Suchita Kulkarni, Ursula Laa, Veronika Magerl, Wolfgang Magerl, Philipp Neuhuber, Doris Proschofsky, Jory Sonneveld, Michael Traub, Matthias Wolf, Alicia Wongel
6
+ Author: The SModelS collaboration, smodels-users@lists.oeaw.ac.at Current members: Mohammad Mahdi Altakach, Sabine Kraml, Andre Lessa, Sahana Narashima, Timothee Pascal, Camila Ramos, Humberto Reyes-Gonzalez, Theo Reymermier, Yoxara Villamizar, Wolfgang Waltenberger Previously involved in SModelS: Gael Alguero, Federico Ambrogi, Juhi Dutta, Jan Heisig, Charanjit K. Khosa, Suchita Kulkarni, Ursula Laa, Veronika Magerl, Wolfgang Magerl, Philipp Neuhuber, Doris Proschofsky, Jory Sonneveld, Michael Traub, Matthias Wolf, Alicia Wongel
7
7
  Author-email: smodels-developers@lists.oeaw.ac.at
8
8
  License: GPLv3
9
9
  Keywords: simplified models LHC BSM theories interpretation supersymmetry UEDs
@@ -10,10 +10,13 @@
10
10
 
11
11
  """
12
12
 
13
+ from typing import Union
14
+
13
15
  ## place to keep the pointer to the model file (default = mssm)
14
16
  modelFile="smodels.share.models.mssm"
15
17
 
16
- _experimental = False ## turn on experimental features
18
+ _experimental = { "truncatedgaussians": False,
19
+ "spey": False } ## experimental features
17
20
 
18
21
  _deltas_rel_default = .2 ## the default relative error on the signal strength
19
22
 
@@ -51,11 +54,16 @@ def filetype ( filename ):
51
54
  return None
52
55
 
53
56
 
54
- def experimentalFeatures():
55
- """ a simple boolean flag to turn experimental features on/off,
57
+ def experimentalFeature( feature : str ) -> Union[None,bool]:
58
+ """ method to check if a certain experimental feature is enabled.
56
59
  can be turned on and off via options:experimental in parameters.ini.
60
+ :param feature: ask for feature
61
+
62
+ :returns: None if feature does not exist, else boolean
57
63
  """
58
- return _experimental
64
+ if not feature in _experimental:
65
+ return None
66
+ return _experimental[feature]
59
67
 
60
68
  def nCPUs():
61
69
  """ obtain the number of *available* CPU cores on the machine, for several
@@ -9,7 +9,6 @@ combineSRs = False
9
9
  model=share.models.mssm
10
10
  promptWidth = 1e-11
11
11
  stableWidth = 1e-25
12
- ignorePromptQNumbers = spin,eCharge,colordim
13
12
  [parameters]
14
13
  sigmacut = 0.005
15
14
  minmassgap = 5
@@ -58,7 +58,7 @@ def _getSHA1(filename):
58
58
 
59
59
  # some mechanism to remove lock files if the download got interrupted
60
60
  import atexit
61
- lockfiles = set()
61
+ lockfiles = set()
62
62
 
63
63
  def removeLockFiles( lockfiles ):
64
64
  """ remove cruft lockfiles """
@@ -73,13 +73,13 @@ def removeLockFiles( lockfiles ):
73
73
  atexit.register ( removeLockFiles, lockfiles )
74
74
 
75
75
  class Database(object):
76
- """
76
+ """
77
77
  Database object. Holds a list of SubDatabases and the ExpSMS map.
78
78
  Delegates all calls to SubDatabases.
79
79
  """
80
80
 
81
81
  def __init__(self, base=None, force_load=None,
82
- progressbar=False, subpickle=True,
82
+ progressbar=False, subpickle=True,
83
83
  combinationsmatrix=None):
84
84
  """
85
85
  :param base: path to the database, or pickle file (string), or http
@@ -99,17 +99,15 @@ class Database(object):
99
99
  optionally specifying signal regions, e.g. { "anaid1:SR1":
100
100
  ( "anaid2:SR2", "anaid3" ) }
101
101
  """
102
-
103
102
  self.subs = []
104
-
105
103
  if "_fastlim" in base: # for backwards compatibility
106
104
  base = base.replace("_fastlim", "+fastlim")
107
105
  sstrings = base.split("+")
108
106
  for ss in sstrings:
109
107
  self.subs.append(SubDatabase(ss, force_load,
110
- progressbar, subpickle,
108
+ progressbar, subpickle,
111
109
  combinationsmatrix))
112
-
110
+
113
111
 
114
112
  # Compute SMS dict with all results
115
113
  self._allExpSMSDict = ExpSMSDict(self.expResultList)
@@ -120,7 +118,7 @@ class Database(object):
120
118
  @property
121
119
  def expResultList(self):
122
120
  """
123
- The combined list of results, compiled from the
121
+ The combined list of results, compiled from the
124
122
  the active results in each subdatabase.
125
123
  """
126
124
 
@@ -235,11 +233,11 @@ class Database(object):
235
233
  Select (filter) the results within the database satisfying the restrictions set by the arguments and returns the corresponding results.
236
234
  """
237
235
 
238
- self.selectExpResults(analysisIDs=analysisIDs, datasetIDs=datasetIDs,
239
- txnames=txnames, dataTypes=dataTypes,
236
+ self.selectExpResults(analysisIDs=analysisIDs, datasetIDs=datasetIDs,
237
+ txnames=txnames, dataTypes=dataTypes,
240
238
  useNonValidated=useNonValidated,
241
239
  onlyWithExpected=onlyWithExpected)
242
-
240
+
243
241
  return self.expResultList[:]
244
242
 
245
243
  def selectExpResults(self, analysisIDs=['all'], datasetIDs=['all'], txnames=['all'],
@@ -420,8 +418,8 @@ class SubDatabase(object):
420
418
  self._setParticles()
421
419
  self.txt_meta.printFastlimBanner()
422
420
  return
423
- logger.error("when initialising database: force_load=%s is not "
424
- "recognized. Valid values are: pcl, txt, None." % force_load)
421
+ logger.error( f"when initialising database: force_load={force_load} is not "
422
+ "recognized. Valid values are: pcl, txt, None." )
425
423
  raise SModelSError()
426
424
 
427
425
  def __eq__(self, other):
@@ -462,16 +460,21 @@ class SubDatabase(object):
462
460
  it needs update, create new binary file, in
463
461
  case it does need an update.
464
462
  """
465
- if not os.path.exists(self.pcl_meta.pathname):
466
- logger.info("Creating binary database ")
467
- logger.info("(this may take a few minutes, but it's done only once!)")
468
- self.loadTextDatabase()
469
- self.createBinaryFile()
470
- else:
471
- if self.needsUpdate():
463
+ try:
464
+ if not os.path.exists(self.pcl_meta.pathname):
465
+ logger.info("Creating binary database ")
466
+ logger.info("(this may take a few minutes, but it's done only once!)")
467
+ self.loadTextDatabase()
472
468
  self.createBinaryFile()
473
469
  else:
474
- self.loadBinaryFile(lastm_only=False)
470
+ if self.needsUpdate():
471
+ self.createBinaryFile()
472
+ else:
473
+ self.loadBinaryFile(lastm_only=False)
474
+ except Exception as e:
475
+ import traceback
476
+ logger.error ( "when loading database: {e}, {traceback.format_exc()}" )
477
+ sys.exit(-1)
475
478
 
476
479
  def loadTextDatabase(self):
477
480
  """ simply loads the textdabase """
@@ -557,7 +560,7 @@ class SubDatabase(object):
557
560
  t1 = time.time()-t0
558
561
  logger.info("Loaded database from %s in %.1f secs." %
559
562
  (self.pcl_meta.pathname, t1))
560
- self.databaseParticles = None
563
+ self.databaseParticles = None
561
564
  try:
562
565
  self.databaseParticles = serializer.load(f)
563
566
  except EOFError as e:
@@ -669,7 +672,7 @@ class SubDatabase(object):
669
672
  def lockFile ( self, filename : os.PathLike ):
670
673
  """ lock the file <filename>
671
674
  """
672
- lockfile = os.path.join ( os.path.dirname ( filename ),
675
+ lockfile = os.path.join ( os.path.dirname ( filename ),
673
676
  ".lock_"+ os.path.basename ( filename ) )
674
677
  ctr = 0
675
678
  while ( ctr < 5 ):
@@ -700,7 +703,7 @@ class SubDatabase(object):
700
703
  def unlockFile ( self, filename : os.PathLike ):
701
704
  """ unlock the file <filename>
702
705
  """
703
- lockfile = os.path.join ( os.path.dirname ( filename ),
706
+ lockfile = os.path.join ( os.path.dirname ( filename ),
704
707
  ".lock_"+ os.path.basename ( filename ) )
705
708
  if lockfile in lockfiles:
706
709
  lockfiles.remove( lockfile )
@@ -1079,7 +1082,7 @@ class SubDatabase(object):
1079
1082
  """
1080
1083
 
1081
1084
  self._activeResults = self.getExpResults(analysisIDs, datasetIDs, txnames,
1082
- dataTypes, useNonValidated,
1085
+ dataTypes, useNonValidated,
1083
1086
  onlyWithExpected)
1084
1087
 
1085
1088
  def getExpResults(self, analysisIDs=['all'], datasetIDs=['all'], txnames=['all'],
@@ -45,7 +45,7 @@ class Info(object):
45
45
  for i, tag in enumerate(tags):
46
46
  if not tag:
47
47
  continue
48
- if tag.startswith("# "): # a comment!
48
+ if tag.startswith("#"): # a comment!
49
49
  continue
50
50
  line = content[i]
51
51
  value = line.split(':', 1)[1].strip()
@@ -0,0 +1,476 @@
1
+ """
2
+ .. module:: clusterTools
3
+ :synopsis: Module holding the SMSCluster class and cluster methods used to combine similar SMS according
4
+ to the analysis.
5
+
6
+ .. moduleauthor:: Andre Lessa <lessa.a.p@gmail.com>
7
+
8
+ """
9
+
10
+ from smodels.base.particleNode import ParticleNode
11
+ from smodels.base.particle import MultiParticle
12
+ from smodels.base.physicsUnits import fb
13
+ from smodels.decomposition.theorySMS import TheorySMS
14
+ from smodels.experiment.datasetObj import DataSet, CombinedDataSet
15
+ from smodels.matching.exceptions import SModelSMatcherError as SModelSError
16
+ from smodels.matching.matcherAuxiliaryFuncs import average
17
+ from smodels.base.smodelsLogging import logger
18
+ import numpy as np
19
+
20
+
21
+
22
+ class AverageSMS(TheorySMS):
23
+ """
24
+ Represents an SMS or list of SMS containing only
25
+ the basic attributes required for clustering or computing efficiencies/upper limits.
26
+ Its properties are given by the average properties of the SMS
27
+ it represents and its weight is given by the total weight of
28
+ all SMS.
29
+ """
30
+
31
+ def __init__(self, smsList=[]):
32
+ if any(not isinstance(sms, TheorySMS) for sms in smsList):
33
+ raise SModelSError("An AverageSMS must be created from a list of TheorySMS objects.")
34
+
35
+ TheorySMS.__init__(self)
36
+
37
+ # Get the relevant properties needed by the txnames
38
+ # (in addition to mass, totalwidth and isSM)
39
+ attrList = ['mass', 'totalwidth', 'isSM']
40
+ for sms in smsList:
41
+ dataMap = sms.txname.dataMap
42
+ attrList += [attr for _, attr,_ in dataMap.values()]
43
+ attrList = list(set(attrList))
44
+
45
+ # Define base SMS to copy (common) global properties from
46
+ smsBase = smsList[0]
47
+
48
+ # Define relevant properties to be stored and averaged over:
49
+ self.properties = attrList
50
+ self.txname = smsBase.txname
51
+ #If the average corresponds to a single SMS, we
52
+ # can directly use its upperLimit value (if defined).
53
+ if (len(smsList) == 1) and hasattr(smsBase,'_upperLimit'):
54
+ self._upperLimit = smsBase._upperLimit
55
+
56
+ # Consistency checks:
57
+ if any(sms.canonName != smsBase.canonName for sms in smsList):
58
+ logger.error("Can not build average SMS from SMS with distinct topologies.")
59
+ raise SModelSError()
60
+ if any(sms.txname != self.txname for sms in smsList):
61
+ logger.error("Can not build average SMS from SMS for distinct txnames.")
62
+ raise SModelSError()
63
+
64
+
65
+ # Create new node objects holding particles
66
+ # with the average attributes
67
+ avgNodesDict = {}
68
+ if len(smsList) == 1:
69
+ avgNodesDict = {n : node for n,node in zip(smsBase.nodeIndices,smsBase.nodes)}
70
+ else:
71
+ weights = [sms.weight.asNumber(fb) for sms in smsList]
72
+ for nodeIndex in smsBase.nodeIndices:
73
+ if nodeIndex == smsBase.rootIndex:
74
+ # For the root node make a dummy copy:
75
+ avgNode = smsBase.indexToNode(nodeIndex).copy()
76
+ else:
77
+ # For all the other nodes compute the average
78
+ allParticles = [sms.indexToNode(nodeIndex).particle for sms in smsList]
79
+ attrDict = {}
80
+ for attr in self.properties:
81
+ values = [getattr(ptc, attr) for ptc in allParticles]
82
+ avgAttr = average(values, weights, nround=5)
83
+ attrDict[attr] = avgAttr
84
+ mp = allParticles[0]
85
+ for ptc in allParticles[1:]:
86
+ mp = mp + ptc
87
+ if isinstance(mp,MultiParticle):
88
+ avgParticle = MultiParticle(particles=mp.particles,
89
+ attributesDict=attrDict)
90
+ else:
91
+ avgParticle = MultiParticle(particles=[mp],
92
+ attributesDict=attrDict)
93
+ avgNode = ParticleNode(particle=avgParticle)
94
+ avgNodesDict[nodeIndex] = avgNode
95
+
96
+ # Copy the tree structure from smsBase,
97
+ # but replacing the node objects by the average nodes
98
+ self.copyTreeFrom(smsBase,nodesObjDict=avgNodesDict)
99
+ # Compute the weight
100
+ self.weight = smsBase.weight
101
+ for sms in smsList[1:]:
102
+ self.weight = self.weight + sms.weight
103
+
104
+ def __eq__(self, other):
105
+ """
106
+ Compares the SMS with other. Only the properties
107
+ defined in self.properties are used for comparison.
108
+ :param other: SMS to be compared (SMS object)
109
+ :return: True/False.
110
+ """
111
+
112
+ if not isinstance(other, TheorySMS):
113
+ return False
114
+
115
+ if self.canonName != other.canonName:
116
+ return False
117
+
118
+ for nodeIndex in self.nodeIndices:
119
+ nodeA = self.indexToNode(nodeIndex)
120
+ nodeB = other.indexToNode(nodeIndex)
121
+ if nodeA.isSM and nodeB.isSM:
122
+ continue
123
+ for attr in self.properties:
124
+ attrA = getattr(nodeA,attr)
125
+ attrB = getattr(nodeB,attr)
126
+ if attrA != attrB:
127
+ return False
128
+ return True
129
+
130
+ def __ne__(self, other):
131
+ return not self.__eq__(other)
132
+
133
+ class SMSCluster(object):
134
+ """
135
+ An instance of this class represents a cluster of SMS,
136
+ which holds the averageSMS.
137
+ This class is used to store the relevant information about a cluster of
138
+ SMS and to manipulate this information.
139
+ """
140
+
141
+ def __init__(self, smsList=[], dataset=None):
142
+
143
+ self.smsList = smsList[:]
144
+ self.dataset = dataset
145
+ # Compute average SMS
146
+ self.averageSMS = self.computeAverageSMS()
147
+
148
+ def __str__(self):
149
+ return str(self.averageSMS)
150
+
151
+ def __repr__(self):
152
+ return str(self.averageSMS)
153
+
154
+ def getTotalXSec(self):
155
+ """
156
+ Return the sum over the cross sections of all SMS belonging to
157
+ the cluster.
158
+
159
+ :returns: sum of weights of all the SMS in the cluster (XSectionList object)
160
+ """
161
+ totxsec = 0.0*fb
162
+ for sms in self.smsList:
163
+ totxsec += sms.weight
164
+ return totxsec
165
+
166
+ def computeAverageSMS(self):
167
+ """
168
+ Computes the average SMS for the cluster.
169
+ The average SMS has generic ParticleNodes
170
+ with the attributes set to the average values of self.smsList.
171
+ It can only be defined if all SMS share the same canonical name
172
+ and the same txname. Otherwise, returns None
173
+
174
+ :return: AverageSMS object or None (if it can not be defined)
175
+ """
176
+
177
+ # Check if an average SMS can be defined:
178
+ cName = self.smsList[0].canonName
179
+ tx = self.smsList[0].txname
180
+ if any(sms.canonName != cName for sms in self.smsList):
181
+ avgSMS = None
182
+ elif any(sms.txname != tx for sms in self.smsList):
183
+ avgSMS = None
184
+ else:
185
+ # Define the average SMS with the required properties averaged over:
186
+ avgSMS = AverageSMS(self.smsList[:])
187
+
188
+ # If the averageSMS can be defined,
189
+ # compute the averageSMS upper limit
190
+ if avgSMS is not None and self.dataset is not None:
191
+ ul = self.dataset.getUpperLimitFor(avgSMS,txnames=tx)
192
+ avgSMS._upperLimit = ul
193
+
194
+ return avgSMS
195
+
196
+ def distanceTo(self,sms):
197
+ """
198
+ Defines the relative distance between the cluster
199
+ and SMS object or another cluster.
200
+ The distance is computed using the upper limits for
201
+ the averageSMS (for cluster objects) or the SMS upper limit.
202
+ The distance is defined as d = 2*|ul1-ul2|/(ul1+ul2).
203
+
204
+ :parameter sms: SMS object or SMSCluster object
205
+
206
+ :returns: relative distance
207
+ """
208
+
209
+ sms1 = self.averageSMS
210
+
211
+ if isinstance(sms,SMSCluster):
212
+ sms2 = sms.averageSMS
213
+ else:
214
+ sms2 = sms
215
+
216
+ if not hasattr(sms2, '_upperLimit'):
217
+ sms2._upperLimit = self.dataset.getUpperLimitFor(sms2,
218
+ txnames=sms2.txname)
219
+
220
+ ul1 = sms1._upperLimit
221
+ ul2 = sms2._upperLimit
222
+
223
+ if ul1 is None or ul2 is None:
224
+ return None
225
+ if (ul1+ul2).asNumber(fb) == 0.:
226
+ return 0.
227
+ ulDistance = 2.*abs(ul1 - ul2)/(ul1 + ul2)
228
+
229
+ return ulDistance
230
+
231
+ def isValid(self,maxDist):
232
+ """
233
+ Checks if the SMSCluster is a valid cluster,
234
+ i.e. if its AverageSMS has a well defined UL
235
+ and the distance between any SMS in the cluster
236
+ and the cluster AverageSMS is smaller than maxDist.
237
+
238
+ :param dataset: Dataset object used to check the UL
239
+ :parameter maxDist: maximum distance between the averageSMS and
240
+ all the SMS in the cluster
241
+
242
+ :return: True/False
243
+ """
244
+
245
+ if self.averageSMS is None:
246
+ return False
247
+ if self.averageSMS._upperLimit is None:
248
+ return False
249
+
250
+ # Compute distances between averageSMS and SMS in cluster
251
+ dists = [self.distanceTo(sms) for sms in self.smsList]
252
+
253
+ # Check if any distance could not be computed:
254
+ if any(d is None for d in dists):
255
+ return False
256
+
257
+ # Compute maximal distance between averageSMS and SMS
258
+ dist = max(dists,default=0.0)
259
+ if dist > maxDist:
260
+ return False
261
+
262
+ return True
263
+
264
+
265
+
266
+ def clusterSMS(smsList, maxDist, dataset):
267
+ """
268
+ Cluster the original SMS according to their distance in upper limit space.
269
+
270
+ :parameter smsList: list of sms (TheorySMS objects)
271
+ :parameter dataset: Dataset object to be used when computing distances in upper limit space
272
+ :parameter maxDist: maximum distance for clustering two SMS
273
+
274
+ :returns: list of clusters (SMSCluster objects)
275
+ """
276
+ if len(smsList) == 0:
277
+ return []
278
+
279
+ if any(not isinstance(sms, TheorySMS) for sms in smsList):
280
+ raise SModelSError("Asked to cluster non TheorySMS objects")
281
+ if not isinstance(dataset, (DataSet, CombinedDataSet)):
282
+ raise SModelSError("A dataset object must be defined for clustering")
283
+
284
+ # Make sure only unique SMS are clustered together (avoids double counting weights)
285
+ # Sort SMS, so the ones with highest contribution (weight) come first:
286
+ smsList = sorted(smsList, key=lambda sms: sms.weight, reverse=True)
287
+
288
+ # Remove duplicated SMS:
289
+ smsUnique = []
290
+ for sms in smsList:
291
+ # Skip the SMS if it is related to any another SMS in the list
292
+ if any(sms.isRelatedTo(smsB) for smsB in smsUnique):
293
+ continue
294
+ smsUnique.append(sms)
295
+
296
+ # Get txname list only with the txnames from unique SMS used for clustering
297
+ txnames = list(set([sms.txname for sms in smsUnique]))
298
+ if dataset.getType() == 'upperLimit' and len(txnames) != 1:
299
+ logger.error("Clustering SMS with different Txnames for an UL result.")
300
+ raise SModelSError()
301
+
302
+ if dataset.getType() == 'upperLimit': # Group according to upper limit values
303
+ clusters = doCluster(smsUnique, dataset, maxDist)
304
+ elif dataset.getType() == 'efficiencyMap': # Group all SMS together
305
+ cluster = SMSCluster(smsUnique)
306
+ clusters = [cluster]
307
+
308
+ for cluster in clusters:
309
+ cluster.txnames = txnames
310
+ return clusters
311
+
312
+
313
+ def doCluster(smsList, dataset, maxDist):
314
+ """
315
+ Cluster algorithm to cluster SMS using a modified minimal spanning tree method.
316
+
317
+ :parameter smsList: list of all SMS to be clustered
318
+ :parameter dataset: Dataset object to be used when computing distances in upper limit space
319
+ :parameter maxDist: maximum distance for clustering two SMS
320
+
321
+ :returns: a list of SMSCluster objects containing the SMS
322
+ belonging to the cluster
323
+ """
324
+
325
+
326
+ # First make sure all SMS contain their upper limits
327
+ for sms in smsList:
328
+ if not hasattr(sms, '._upperLimit'):
329
+ sms._upperLimit = dataset.getUpperLimitFor(sms, txnames=sms.txname)
330
+ if sms._upperLimit is None:
331
+ raise SModelSError("Trying to cluster SMS (id = %i, txname = %s) outside the grid for dataset %s."
332
+ %(sms.smsID,sms.txname,dataset.longStr()))
333
+
334
+ # Sort SMS by upperLimit
335
+ sortedSMSList = sorted(smsList, key = lambda sms: sms._upperLimit)
336
+
337
+ # Cluster all identical SMS into clusters
338
+ clusterList = groupSMS(sortedSMSList,dataset)
339
+
340
+ # Sort clusters by proximity in upperLimit
341
+ clusterList = sorted(clusterList,
342
+ key=lambda cluster: cluster.averageSMS._upperLimit)
343
+
344
+ # Compute the distance matrix for the clusters:
345
+ # (diagonal entries are set to None)
346
+ dMatrix = np.full((len(clusterList),len(clusterList)),fill_value=None)
347
+ for iA,clusterA in enumerate(clusterList):
348
+ for iB,clusterB in enumerate(clusterList):
349
+ if iA >= iB: continue
350
+ dMatrix[iA,iB] = clusterA.distanceTo(clusterB)
351
+ dMatrix[iB,iA] = dMatrix[iA,iB] # the matrix is symmetric
352
+
353
+ # Check minimal distance between two distinct clusters
354
+ # (set default to 2*maxDist in case len(clusterList) == 1, i.e. dMatrix is empty)
355
+ minDist = min([d for d in dMatrix.flatten() if d is not None],
356
+ default=2*maxDist)
357
+ # Merge closest cluster up to the point when
358
+ # no more merges are possible (all clusters have distances larger than maxDist)
359
+ while (len(clusterList) > 1) and (minDist < maxDist):
360
+ # Get indices of first pair of clusters with minimum distance
361
+ mergeIndices = np.argwhere(dMatrix == minDist)[0]
362
+ # Merge clusters with indices in mergeIndices
363
+ newCluster = mergeClusters(np.take(clusterList,mergeIndices))
364
+ # If new cluster is valid (averageSMS is close to the cluster's SMS),
365
+ # replace the two orignal clusters by it.
366
+ if newCluster.isValid(maxDist):
367
+ # Update cluster list:
368
+ # Remove cluster with largest index
369
+ # and replace cluster with smallest index by new cluster
370
+ delete_index = max(mergeIndices)
371
+ replace_index = min(mergeIndices)
372
+ clusterList.pop(delete_index)
373
+ clusterList[replace_index] = newCluster
374
+ # Remove row and column for cluster with largest index
375
+ dMatrix = np.delete(dMatrix,delete_index,0)
376
+ dMatrix = np.delete(dMatrix,delete_index,1)
377
+ # Compute the distances between the new cluster and all the
378
+ # other ones.
379
+ # Recompute distances using the new cluster:
380
+ for iA,clusterA in enumerate(clusterList):
381
+ if iA >= replace_index: continue
382
+ dMatrix[iA,replace_index] = clusterA.distanceTo(newCluster)
383
+ dMatrix[replace_index,iA] = dMatrix[iA,replace_index] # the matrix is symmetric
384
+
385
+ # Otherwise keep the original clusters, but set
386
+ # their distance above maxDist, so they will no longer be allowed to merge
387
+ else:
388
+ dMatrix[mergeIndices] = 2*maxDist
389
+ dMatrix[np.flip(mergeIndices)] = 2*maxDist
390
+
391
+
392
+ minDist = min([d for d in dMatrix.flatten() if d is not None],
393
+ default=2*maxDist)
394
+
395
+
396
+ # Finally sort clusters by total xsection and length
397
+ clusters = sorted(clusterList,
398
+ key = lambda c: (c.getTotalXSec(),len(c.smsList)),
399
+ reverse=True)
400
+
401
+ return clusters
402
+
403
+
404
+ def groupSMS(smsList,dataset):
405
+ """
406
+ Group SMS into clusters where the average SMS is
407
+ identical to all the SMS in cluster.
408
+ Each cluster contains all SMS which share the same mass,width and upper limit
409
+ and can be replaced by their average SMS.
410
+
411
+ :parameter smsList: list of all SMS to be grouped
412
+
413
+ :returns: a list of SMSCluster objects
414
+ which represents a group of SMS with same mass, width and upper limit.
415
+ """
416
+
417
+ # Group SMS if they have the same UL
418
+ # and give the same average SMS (same BSM attributes)
419
+ smsClusterList = []
420
+ nonClusteredSMS = smsList[:]
421
+ while nonClusteredSMS:
422
+ smsA = nonClusteredSMS.pop(0)
423
+ smsCluster = SMSCluster([smsA],dataset=dataset)
424
+ # Keep track of the SMS which could not
425
+ # be clustered with smsA to use in the next iteration
426
+ notClustered = []
427
+ while nonClusteredSMS:
428
+ smsB = nonClusteredSMS.pop(0)
429
+ # If it can not be clustered with smsA, add to notClustered list
430
+ if (smsA._upperLimit != smsB._upperLimit) or (smsCluster.averageSMS != smsB):
431
+ notClustered.append(smsB)
432
+ continue
433
+ smsCluster.smsList.append(smsB)
434
+ smsCluster.averageSMS.weight += smsB.weight
435
+
436
+ smsClusterList.append(smsCluster)
437
+ nonClusteredSMS = notClustered[:]
438
+
439
+ # Make sure each SMS belongs to a cluster:
440
+ nclustered = sum([len(c.smsList) for c in smsClusterList])
441
+ nsms = len(smsList)
442
+ if nclustered != nsms:
443
+ raise SModelSError(f"Error grouping SMS. {nclustered}/{nsms} SMS were clustered")
444
+
445
+ return smsClusterList
446
+
447
+ def mergeClusters(clusterList,useAverage=False):
448
+ """
449
+ Merge a list of SMSCluster objects using
450
+ the averageSMS of each cluster if useAverage is True.
451
+ Otherwise cluster the list of all SMS from all clusters.
452
+
453
+ :param clusterList: List of SMSCluster objects
454
+ :param useAverage: True/False. If True,
455
+ will cluster the averageSMS in each cluster.
456
+ """
457
+
458
+ if len(clusterList) == 0:
459
+ return None
460
+
461
+ smsList = []
462
+ for cluster in clusterList:
463
+ smsList += cluster.smsList[:]
464
+
465
+ if not useAverage:
466
+ newCluster = SMSCluster(smsList,
467
+ clusterList[0].dataset)
468
+ else:
469
+ avgSMSList = [cluster.averageSMS
470
+ for cluster in clusterList]
471
+ newCluster = SMSCluster(avgSMSList,
472
+ clusterList[0].dataset)
473
+ # Make sure the cluster SMS list contains the original SMS
474
+ newCluster.smsList = smsList[:]
475
+
476
+ return newCluster