1515
1616from __future__ import print_function
1717
18+ import copy
1819import sys
1920from pathlib import Path
2021
@@ -122,6 +123,21 @@ def morph_error(self, msg, error):
122123 action = "store_true" ,
123124 help = "Print additional header details to saved files." ,
124125 )
126+ parser .add_option (
127+ "-u" ,
128+ "--uncertainty" ,
129+ "--estimate-uncertainty" ,
130+ dest = "estimate_uncertainty" ,
131+ action = "store_true" ,
132+ help = (
133+ "Estimate uncertainties for each refined morphing parameter. "
134+ "This is done by estimating the Hessian of the fit. "
135+ "Caution should be taken as this is not the true uncertainty "
136+ "of the fit, and the user should make their own judgement "
137+ "about what measure of uncertainty to use for their particular "
138+ "use case."
139+ ),
140+ )
125141 parser .add_option (
126142 "--xmin" ,
127143 type = "float" ,
@@ -734,6 +750,7 @@ def single_morph(
734750 refiner .residual = refiner ._pearson
735751 if opts .addpearson :
736752 refiner .residual = refiner ._add_pearson
753+ unc = None
737754 if opts .refine and refpars :
738755 try :
739756 # This works better when we adjust scale and smear first.
@@ -743,17 +760,39 @@ def single_morph(
743760 rptemp .append ("scale" )
744761 refiner .refine (* rptemp )
745762 # Adjust all parameters
746- refiner .refine (* refpars )
763+ unc = refiner .refine (* refpars , estimate_uncertainty = True )
764+ # If one parameter is causing trouble with uncertainty estimate
765+ # compute all uncertainties individually
766+ if unc is None :
767+ unc = {}
768+ for param in refpars :
769+ refiner_single_param = type (refiner )(
770+ refiner .chain ,
771+ refiner .x_morph ,
772+ refiner .y_morph ,
773+ refiner .x_target ,
774+ refiner .y_target ,
775+ tolerance = refiner .tolerance ,
776+ )
777+ refiner_single_param .chain .config = copy .deepcopy (config )
778+ unc_param = refiner_single_param .refine (
779+ * [param ], estimate_uncertainty = True
780+ )
781+ if unc_param is not None :
782+ unc .update (unc_param )
747783 except ValueError as e :
748784 parser .morph_error (str (e ), ValueError )
749785 # Smear is not being refined, but baselineslope needs to refined to apply
750786 # smear
751787 # Note that baselineslope is only added to the refine list if smear is
752788 # applied
753789 elif "baselineslope" in refpars :
790+ # Note, you cannot estimate uncertainty here as the baselineslope
791+ # does not change the fit
754792 try :
755793 refiner .refine (
756- "baselineslope" , baselineslope = config ["baselineslope" ]
794+ "baselineslope" ,
795+ baselineslope = config ["baselineslope" ],
757796 )
758797 except ValueError as e :
759798 parser .morph_error (str (e ), ValueError )
@@ -839,6 +878,7 @@ def single_morph(
839878 io .single_morph_output (
840879 morph_inputs ,
841880 morph_results ,
881+ uncertainties = None if opts .estimate_uncertainty is None else unc ,
842882 save_file = opts .slocation ,
843883 morph_file = pargs [0 ],
844884 xy_out = xy_save ,
@@ -880,10 +920,12 @@ def single_morph(
880920 # Return different things depending on whether it is python interfaced
881921 if python_wrap :
882922 morph_info = morph_results
923+ if opts .estimate_uncertainty is not None and unc is not None :
924+ morph_info .update ({"Uncertainties" : unc })
883925 morph_table = numpy .array (xy_save ).T
884926 return morph_info , morph_table
885927 else :
886- return morph_results
928+ return morph_results , unc
887929
888930
889931def multiple_targets (parser , opts , pargs , stdout_flag = True , python_wrap = False ):
@@ -993,6 +1035,7 @@ def multiple_targets(parser, opts, pargs, stdout_flag=True, python_wrap=False):
9931035
9941036 # Morph morph_file against all other files in target_directory
9951037 morph_results = {}
1038+ uncs = {}
9961039 for target_file in target_list :
9971040 if target_file .is_file :
9981041 # Set the save file destination to be a file within the SLOC
@@ -1002,13 +1045,11 @@ def multiple_targets(parser, opts, pargs, stdout_flag=True, python_wrap=False):
10021045 opts .slocation = Path (save_morphs_here ).joinpath (save_as )
10031046 # Perform a morph of morph_file against target_file
10041047 pargs = [morph_file , target_file ]
1005- morph_results .update (
1006- {
1007- target_file .name : single_morph (
1008- parser , opts , pargs , stdout_flag = False
1009- ),
1010- }
1048+ morph_result , unc = single_morph (
1049+ parser , opts , pargs , stdout_flag = False
10111050 )
1051+ morph_results .update ({target_file .name : morph_result })
1052+ uncs .update ({target_file .name : unc })
10121053
10131054 target_file_names = []
10141055 for key in morph_results .keys ():
@@ -1030,6 +1071,7 @@ def multiple_targets(parser, opts, pargs, stdout_flag=True, python_wrap=False):
10301071 morph_inputs ,
10311072 morph_results ,
10321073 target_file_names ,
1074+ uncertainties_dict = uncs ,
10331075 save_directory = save_directory ,
10341076 morph_file = morph_file ,
10351077 target_directory = target_directory ,
@@ -1187,6 +1229,7 @@ def multiple_morphs(parser, opts, pargs, stdout_flag=True, python_wrap=False):
11871229
11881230 # Morph morph_file against all other files in target_directory
11891231 morph_results = {}
1232+ uncs = {}
11901233 for morph_file in morph_list :
11911234 if morph_file .is_file :
11921235 # Set the save file destination to be a file within the SLOC
@@ -1196,13 +1239,11 @@ def multiple_morphs(parser, opts, pargs, stdout_flag=True, python_wrap=False):
11961239 opts .slocation = Path (save_morphs_here ).joinpath (save_as )
11971240 # Perform a morph of morph_file against target_file
11981241 pargs = [morph_file , target_file ]
1199- morph_results .update (
1200- {
1201- morph_file .name : single_morph (
1202- parser , opts , pargs , stdout_flag = False
1203- ),
1204- }
1242+ morph_result , unc = single_morph (
1243+ parser , opts , pargs , stdout_flag = False
12051244 )
1245+ morph_results .update ({morph_file .name : morph_result })
1246+ uncs .update ({morph_file .name : unc })
12061247
12071248 morph_file_names = []
12081249 for key in morph_results .keys ():
@@ -1224,6 +1265,7 @@ def multiple_morphs(parser, opts, pargs, stdout_flag=True, python_wrap=False):
12241265 morph_inputs ,
12251266 morph_results ,
12261267 morph_file_names ,
1268+ uncertainties_dict = uncs ,
12271269 save_directory = save_directory ,
12281270 morph_file = target_file ,
12291271 target_directory = morph_directory ,
0 commit comments