1515
1616from __future__ import print_function
1717
18+ import copy
1819import sys
1920from pathlib import Path
2021
@@ -109,6 +110,21 @@ def morph_error(self, msg, error):
109110 action = "store_true" ,
110111 help = "Print additional header details to saved files." ,
111112 )
113+ parser .add_option (
114+ "-u" ,
115+ "--uncertainty" ,
116+ "--estimate-uncertainty" ,
117+ dest = "estimate_uncertainty" ,
118+ action = "store_true" ,
119+ help = (
120+ "Estimate uncertainties for each refined morphing parameter. "
121+ "This is done by estimating the Hessian of the fit. "
122+ "Caution should be taken as this is not the true uncertainty "
123+ "of the fit, and the user should make their own judgement "
124+ "about what measure of uncertainty to use for their particular "
125+ "use case."
126+ ),
127+ )
112128 parser .add_option (
113129 "--xmin" ,
114130 type = "float" ,
@@ -721,6 +737,7 @@ def single_morph(
721737 refiner .residual = refiner ._pearson
722738 if opts .addpearson :
723739 refiner .residual = refiner ._add_pearson
740+ unc = None
724741 if opts .refine and refpars :
725742 try :
726743 # This works better when we adjust scale and smear first.
@@ -730,17 +747,39 @@ def single_morph(
730747 rptemp .append ("scale" )
731748 refiner .refine (* rptemp )
732749 # Adjust all parameters
733- refiner .refine (* refpars )
750+ unc = refiner .refine (* refpars , estimate_uncertainty = True )
751+ # If one parameter is causing trouble with uncertainty estimate
752+ # compute all uncertainties individually
753+ if unc is None :
754+ unc = {}
755+ for param in refpars :
756+ refiner_single_param = type (refiner )(
757+ refiner .chain ,
758+ refiner .x_morph ,
759+ refiner .y_morph ,
760+ refiner .x_target ,
761+ refiner .y_target ,
762+ tolerance = refiner .tolerance ,
763+ )
764+ refiner_single_param .chain .config = copy .deepcopy (config )
765+ unc_param = refiner_single_param .refine (
766+ * [param ], estimate_uncertainty = True
767+ )
768+ if unc_param is not None :
769+ unc .update (unc_param )
734770 except ValueError as e :
735771 parser .morph_error (str (e ), ValueError )
736772 # Smear is not being refined, but baselineslope needs to refined to apply
737773 # smear
738774 # Note that baselineslope is only added to the refine list if smear is
739775 # applied
740776 elif "baselineslope" in refpars :
777+ # Note, you cannot estimate uncertainty here as the baselineslope
778+ # does not change the fit
741779 try :
742780 refiner .refine (
743- "baselineslope" , baselineslope = config ["baselineslope" ]
781+ "baselineslope" ,
782+ baselineslope = config ["baselineslope" ],
744783 )
745784 except ValueError as e :
746785 parser .morph_error (str (e ), ValueError )
@@ -825,6 +864,7 @@ def single_morph(
825864 io .single_morph_output (
826865 morph_inputs ,
827866 morph_results ,
867+ uncertainties = None if opts .estimate_uncertainty is None else unc ,
828868 save_file = opts .slocation ,
829869 morph_file = pargs [0 ],
830870 xy_out = xy_save ,
@@ -866,10 +906,12 @@ def single_morph(
866906 # Return different things depending on whether it is python interfaced
867907 if python_wrap :
868908 morph_info = morph_results
909+ if opts .estimate_uncertainty is not None and unc is not None :
910+ morph_info .update ({"Uncertainties" : unc })
869911 morph_table = numpy .array (xy_save ).T
870912 return morph_info , morph_table
871913 else :
872- return morph_results
914+ return morph_results , unc
873915
874916
875917def multiple_targets (parser , opts , pargs , stdout_flag = True , python_wrap = False ):
@@ -979,6 +1021,7 @@ def multiple_targets(parser, opts, pargs, stdout_flag=True, python_wrap=False):
9791021
9801022 # Morph morph_file against all other files in target_directory
9811023 morph_results = {}
1024+ uncs = {}
9821025 for target_file in target_list :
9831026 if target_file .is_file :
9841027 # Set the save file destination to be a file within the SLOC
@@ -988,13 +1031,11 @@ def multiple_targets(parser, opts, pargs, stdout_flag=True, python_wrap=False):
9881031 opts .slocation = Path (save_morphs_here ).joinpath (save_as )
9891032 # Perform a morph of morph_file against target_file
9901033 pargs = [morph_file , target_file ]
991- morph_results .update (
992- {
993- target_file .name : single_morph (
994- parser , opts , pargs , stdout_flag = False
995- ),
996- }
1034+ morph_result , unc = single_morph (
1035+ parser , opts , pargs , stdout_flag = False
9971036 )
1037+ morph_results .update ({target_file .name : morph_result })
1038+ uncs .update ({target_file .name : unc })
9981039
9991040 target_file_names = []
10001041 for key in morph_results .keys ():
@@ -1016,6 +1057,7 @@ def multiple_targets(parser, opts, pargs, stdout_flag=True, python_wrap=False):
10161057 morph_inputs ,
10171058 morph_results ,
10181059 target_file_names ,
1060+ uncertainties_dict = uncs ,
10191061 save_directory = save_directory ,
10201062 morph_file = morph_file ,
10211063 target_directory = target_directory ,
@@ -1173,6 +1215,7 @@ def multiple_morphs(parser, opts, pargs, stdout_flag=True, python_wrap=False):
11731215
11741216 # Morph morph_file against all other files in target_directory
11751217 morph_results = {}
1218+ uncs = {}
11761219 for morph_file in morph_list :
11771220 if morph_file .is_file :
11781221 # Set the save file destination to be a file within the SLOC
@@ -1182,13 +1225,11 @@ def multiple_morphs(parser, opts, pargs, stdout_flag=True, python_wrap=False):
11821225 opts .slocation = Path (save_morphs_here ).joinpath (save_as )
11831226 # Perform a morph of morph_file against target_file
11841227 pargs = [morph_file , target_file ]
1185- morph_results .update (
1186- {
1187- morph_file .name : single_morph (
1188- parser , opts , pargs , stdout_flag = False
1189- ),
1190- }
1228+ morph_result , unc = single_morph (
1229+ parser , opts , pargs , stdout_flag = False
11911230 )
1231+ morph_results .update ({morph_file .name : morph_result })
1232+ uncs .update ({morph_file .name : unc })
11921233
11931234 morph_file_names = []
11941235 for key in morph_results .keys ():
@@ -1210,6 +1251,7 @@ def multiple_morphs(parser, opts, pargs, stdout_flag=True, python_wrap=False):
12101251 morph_inputs ,
12111252 morph_results ,
12121253 morph_file_names ,
1254+ uncertainties_dict = uncs ,
12131255 save_directory = save_directory ,
12141256 morph_file = target_file ,
12151257 target_directory = morph_directory ,
0 commit comments