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.
- {smodels-3.0.0/smodels.egg-info → smodels-3.0.2}/PKG-INFO +2 -2
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/runtime.py +12 -4
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/parameters_default.ini +0 -1
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/databaseObj.py +28 -25
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/infoObj.py +1 -1
- smodels-3.0.2/smodels/matching/clusterTools.py +476 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/modelTester.py +23 -11
- {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/theoryPrediction.py +8 -4
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/BANNER +2 -2
- {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/basicStats.py +4 -4
- {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/pyhfInterface.py +81 -57
- {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/simplifiedLikelihoods.py +1 -1
- smodels-3.0.2/smodels/statistics/speyTools.py +650 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/statsTools.py +12 -1
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/xsecResummino.py +67 -44
- smodels-3.0.2/smodels/version +1 -0
- {smodels-3.0.0 → smodels-3.0.2/smodels.egg-info}/PKG-INFO +2 -2
- {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/SOURCES.txt +1 -0
- smodels-3.0.0/smodels/matching/clusterTools.py +0 -521
- smodels-3.0.0/smodels/version +0 -1
- {smodels-3.0.0 → smodels-3.0.2}/MANIFEST.in +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/README.rst +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/lhe/gluino_squarks.lhe +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/lhe/simplyGluino.lhe +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/TRV1_1800_300_300.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/complicated.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/ew_ymi2l51r.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/gluinoToTops.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/gluino_squarks.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/higgsinoStop.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/higgsino_spectrum_520_125_dm_10.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/higgsino_spectrum_520_125_dm_4.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/idm_example.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/lightEWinos.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/lightEWinos_simple.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/longLived.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/inputFiles/slha/simplyGluino.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/setup.cfg +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/setup.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/COPYING +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/INSTALLATION.rst +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/README.rst +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/crossSection.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/exceptions.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/genericSMS.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/inclusiveObjects.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/lheReader.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/model.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/particle.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/particleNode.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/physicsUnits.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/base/smodelsLogging.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/decomposer.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/exceptions.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/theorySMS.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/decomposition/topologyDict.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/input_resummino.in +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/iplots_parameters.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/iplots_parameters_IDM.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/logging.conf +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/logging_consoleonly.conf +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/logging_file.conf +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/pythia.card +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/pythia8.cfg +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/etc/resummino.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/datasetObj.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/defaultFinalStates.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/exceptions.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/expAuxiliaryFuncs.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/expResultObj.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/expSMS.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/expSMSDict.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/graphMatching.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/metaObj.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/reweighting.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/txnameDataObj.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/experiment/txnameObj.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/installation.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gdcpl_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gdcpl_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gg_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gg_nllnlo_hm_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gg_nllnlo_hm_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/gg_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/nllfast-1.2.f +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sb_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sb_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sdcpl_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sdcpl_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sg_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sg_nllnlo_hm_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sg_nllnlo_hm_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/sg_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/ss_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/ss_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/st_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/st_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/tot_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-1.2/tot_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gdcpl_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gdcpl_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gg_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gg_nllnlo_hm_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gg_nllnlo_hm_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/gg_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/nllfast-2.1.f +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sb_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sb_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sdcpl_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sdcpl_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sg_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sg_nllnlo_hm_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sg_nllnlo_hm_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/sg_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/ss_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/ss_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/st_nllnlo_cteq6.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-2.1/st_nllnlo_mstw2008.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gdcpl_nllnlo_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gdcpl_nllnlo_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gdcpl_nllnlo_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_hm_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_hm_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_hm_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/gg_nllnlo_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/nllfast-3.1-13TeV.f +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sb_nllnlo_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sb_nllnlo_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sb_nllnlo_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sdcpl_nllnlo_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sdcpl_nllnlo_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sdcpl_nllnlo_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_hm_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_hm_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_hm_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/sg_nllnlo_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/ss_nllnlo_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/ss_nllnlo_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/ss_nllnlo_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/st_nllnlo_cteq66_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/st_nllnlo_mstw2008_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/nllfast/nllfast-3.1/st_nllnlo_nnpdf30_13TeV.grid +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia6/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia6/pythia-6.4.27.f +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia6/pythia_lhe.f +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/installer.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/parseXml.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/pythia8.cc +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/pythiaversion +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/pythia8/xml.doc +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/resummino/Makefile +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/resummino/clean.sh +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/resummino/install.sh +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/lib/resummino/versions.txt +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/exceptions.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/matching/matcherAuxiliaryFuncs.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/SMparticles.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/dgmssm.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/idm.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/mssm.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/mssmQNumbers.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/nmssm.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/models/zprime.slha +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/pythia_test.card +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/share/requirements.txt +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/analysesCombinations.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/exceptions.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/statistics/truncatedGaussians.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/__init__.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/asciiGraph.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/caching.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/coverage.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/crashReport.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/databaseBrowser.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/databaseClient.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/databaseServer.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/externalPythonTools.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/interactivePlots.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/interactivePlotsHelpers.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/ioObjects.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/lheChecks.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/nllFastWrapper.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/particlesLoader.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/printerTools.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/pythia6Wrapper.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/pythia8Wrapper.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/pythia8particles.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/runSModelS.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/slhaChecks.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/smodelsTools.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/timeOut.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/toolBox.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/wrapperBase.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/xsecBase.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels/tools/xsecComputer.py +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/dependency_links.txt +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/entry_points.txt +0 -0
- {smodels-3.0.0 → smodels-3.0.2}/smodels.egg-info/requires.txt +0 -0
- {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.
|
|
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 =
|
|
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
|
|
55
|
-
"""
|
|
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
|
-
|
|
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
|
|
@@ -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
|
|
424
|
-
"recognized. Valid values are: pcl, txt, None."
|
|
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
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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.
|
|
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'],
|
|
@@ -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
|