Skip to content

Commit

Permalink
Merge pull request #151 from mantidproject/better-user-experience
Browse files Browse the repository at this point in the history
Improve user experience
  • Loading branch information
GuiMacielPereira authored Jan 7, 2025
2 parents 1321891 + 67e1255 commit ade7d8f
Show file tree
Hide file tree
Showing 20 changed files with 275 additions and 222 deletions.
215 changes: 119 additions & 96 deletions src/mvesuvio/analysis_fitting.py

Large diffs are not rendered by default.

90 changes: 50 additions & 40 deletions src/mvesuvio/config/analysis_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,32 @@ class BackwardAnalysisInputs(SampleParameters):
runs = "43066-43076"
empty_runs = "41876-41923"
mode = "DoubleDifference"
ipfile = "ip2019.par"
firstSpec = 3 # 3
lastSpec = 134 # 134
maskedSpecAllNo = [18, 34, 42, 43, 59, 60, 62, 118, 119, 133]
tofBinning = "275.,1.,420" # Binning of ToF spectra
maskTOFRange = None
subEmptyFromRaw = True # Flag to control wether empty ws gets subtracted from raw
scaleEmpty = 1 # None or scaling factor
scaleRaw = 1
instrument_parameters_file = "ip2019.par"
detectors = "3-134"
mask_detectors = [18, 34, 42, 43, 59, 60, 62, 118, 119, 133]
time_of_flight_binning = "275.,1.,420"
mask_time_of_flight_range = None
subtract_empty_workspace_from_raw = True
scale_empty_workspace = 1 # None or scaling factor
scale_raw_workspace = 1

masses = [12, 16, 27]
# Intensities, NCP widths, NCP centers
initPars = [1, 12, 0.0, 1, 12, 0.0, 1, 12.5, 0.0]
bounds = [
initial_fitting_parameters = [1, 12, 0.0, 1, 12, 0.0, 1, 12.5, 0.0]
fitting_bounds = [
[0, None], [8, 16], [-3, 1],
[0, None], [8, 16], [-3, 1],
[0, None], [11, 14], [-3, 1],
]
constraints = ()

noOfMSIterations = 0 # 4
MSCorrectionFlag = True
HToMassIdxRatio = 19.0620008206 # Set to zero when H is not present
number_of_iterations_for_corrections = 0 # 4
do_multiple_scattering_correction = True
intensity_ratio_of_hydrogen_to_lowest_mass = 19.0620008206 # Set to zero to disable
transmission_guess = 0.8537 # Experimental value from VesuvioTransmission
multiple_scattering_order = 2
number_of_events = 1.0e5
GammaCorrectionFlag = False
multiple_scattering_number_of_events = 1.0e5
do_gamma_correction = False


@dataclass
Expand All @@ -54,46 +53,57 @@ class ForwardAnalysisInputs(SampleParameters):
runs = "43066-43076"
empty_runs = "43868-43911"
mode = "SingleDifference"
ipfile = "ip2018_3.par"
firstSpec = 144 # 144
lastSpec = 182 # 182
maskedSpecAllNo = [173, 174, 179]
tofBinning = "110,1,430" # Binning of ToF spectra
maskTOFRange = None
subEmptyFromRaw = False # Flag to control wether empty ws gets subtracted from raw
scaleEmpty = 1 # None or scaling factor
scaleRaw = 1
instrument_parameters_file = "ip2018_3.par"
detectors = '144-182'
mask_detectors = [173, 174, 179]
time_of_flight_binning = "110,1,430"
mask_time_of_flight_range = None
subtract_empty_workspace_from_raw = False
scale_empty_workspace = 1 # None or scaling factor
scale_raw_workspace = 1

masses = 1.0079, 12, 16, 27
# Intensities, NCP widths, NCP centers
initPars = [1, 4.7, 0, 1, 12.71, 0.0, 1, 8.76, 0.0, 1, 13.897, 0.0]
bounds = [
initial_fitting_parameters = [1, 4.7, 0, 1, 12.71, 0.0, 1, 8.76, 0.0, 1, 13.897, 0.0]
fitting_bounds = [
[0, None], [3, 6], [-3, 1],
[0, None], [12.71, 12.71], [-3, 1],
[0, None], [8.76, 8.76], [-3, 1],
[0, None], [13.897, 13.897], [-3, 1],
]
constraints = ()

noOfMSIterations = 0 # 4
MSCorrectionFlag = True
number_of_iterations_for_corrections = 0 # 4
do_multiple_scattering_correction = True
transmission_guess = 0.8537 # Experimental value from VesuvioTransmission
multiple_scattering_order = 2
number_of_events = 1.0e5
GammaCorrectionFlag = True
multiple_scattering_number_of_events = 1.0e5
do_gamma_correction = True


@dataclass
class YSpaceFitInputs:
showPlots = True
symmetrisationFlag = False
subtractFSE = True
rebinParametersForYSpaceFit = "-25, 0.5, 25" # Needs to be symetric
fitModel = "SINGLE_GAUSSIAN" # Options: 'SINGLE_GAUSSIAN', 'GC_C4', 'GC_C6', 'GC_C4_C6', 'DOUBLE_WELL', 'ANSIO_GAUSSIAN', 'Gaussian3D'
runMinos = True
globalFit = True # Performs global fit with Minuit by default
nGlobalFitGroups = 4 # Number or string "ALL"
maskTypeProcedure = "NAN" # Options: 'NCP', 'NAN', None
show_plots = True
do_symmetrisation = False
subtract_calculated_fse_from_data = True
range_for_rebinning_in_y_space = "-25, 0.5, 25" # Needs to be symetric
# Fitting model options
# 'gauss': Single Gaussian
# 'gcc4': Gram-Charlier with C4 parameter
# 'gcc6': Gram-Charlier with C6 parameter
# 'doublewell': Double Well function
# 'gauss3D': 3-Dimensional Gaussian
fitting_model = "gauss"
run_minos = True
do_global_fit = True # Performs global fit with Minuit by default
# Number of groups of detectors to perform global (simultaneous) fit on
# Either an integer less than the number of detectors
# or option 'all', which does not form groups and fits all spectra simultaneously and individualy
number_of_global_fit_groups = 4
# Type of masking
# 'nan': Zeros in workspace being fit are ignored
# 'ncp': Zeros in workspace being fit are replaced by the fitted neutron compton profile
mask_zeros_with = "nan"


########################
Expand Down
59 changes: 30 additions & 29 deletions src/mvesuvio/main/run_routine.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def setup(self):
self.classes_to_fit_y_space = []
for ai_cls in [self.bckwd_ai, self.fwd_ai]:
if ai_cls.fit_in_y_space:
self.ws_to_fit_y_space.append(name_for_starting_ws(ai_cls) + '_' + str(ai_cls.noOfMSIterations))
self.ws_to_fit_y_space.append(name_for_starting_ws(ai_cls) + '_' + str(ai_cls.number_of_iterations_for_corrections))
self.classes_to_fit_y_space.append(ai_cls)

self.analysis_result = None
Expand All @@ -49,7 +49,7 @@ def setup(self):
inputs_script_path = Path(handle_config.read_config_var("caching.inputs"))
script_name = handle_config.get_script_name()
self.experiment_path = inputs_script_path.parent / script_name
self.input_ws_path = self.experiment_path / "input_ws"
self.input_ws_path = self.experiment_path / "input_workspaces"
self.input_ws_path.mkdir(parents=True, exist_ok=True)

# TODO: Output paths should probably not be set like this
Expand Down Expand Up @@ -103,14 +103,14 @@ def runAnalysisRoutine(self):

if self.bckwd_ai.run_this_scattering_type:

if is_hydrogen_present(self.fwd_ai.masses) & (self.bckwd_ai.HToMassIdxRatio==0):
if is_hydrogen_present(self.fwd_ai.masses) & (self.bckwd_ai.intensity_ratio_of_hydrogen_to_lowest_mass==0):
self.run_estimate_h_ratio()
return

# TODO: make this automatic
assert is_hydrogen_present(self.fwd_ai.masses) != (
self.bckwd_ai.HToMassIdxRatio==0
), "No Hydrogen detected, HToMassIdxRatio has to be set to 0"
self.bckwd_ai.intensity_ratio_of_hydrogen_to_lowest_mass==0
), "No Hydrogen detected, intensity_ratio_of_hydrogen_to_lowest_mass has to be set to 0"

if self.bckwd_ai.run_this_scattering_type and self.fwd_ai.run_this_scattering_type:
self.run_joint_analysis()
Expand Down Expand Up @@ -220,37 +220,38 @@ def _create_analysis_algorithm(self, ai):
ws = loadRawAndEmptyWsFromUserPath(
userWsRawPath=raw_path,
userWsEmptyPath=empty_path,
tofBinning=ai.tofBinning,
tofBinning=ai.time_of_flight_binning,
name=name_for_starting_ws(ai),
scaleRaw=ai.scaleRaw,
scaleEmpty=ai.scaleEmpty,
subEmptyFromRaw=ai.subEmptyFromRaw
scaleRaw=ai.scale_raw_workspace,
scaleEmpty=ai.scale_empty_workspace,
subEmptyFromRaw=ai.subtract_empty_workspace_from_raw
)
first_detector, last_detector = [int(s) for s in ai.detectors.split('-')]
cropedWs = cropAndMaskWorkspace(
ws,
firstSpec=ai.firstSpec,
lastSpec=ai.lastSpec,
maskedDetectors=ai.maskedSpecAllNo,
maskTOFRange=ai.maskTOFRange
firstSpec=first_detector,
lastSpec=last_detector,
maskedDetectors=ai.mask_detectors,
maskTOFRange=ai.mask_time_of_flight_range
)
profiles_table = create_profiles_table(cropedWs.name()+"_initial_parameters", ai)
ipFilesPath = Path(handle_config.read_config_var("caching.ipfolder"))
kwargs = {
"InputWorkspace": cropedWs.name(),
"InputProfiles": profiles_table.name(),
"InstrumentParametersFile": str(ipFilesPath / ai.ipfile),
"HRatioToLowestMass": ai.HToMassIdxRatio if hasattr(ai, 'HRatioToLowestMass') else 0,
"NumberOfIterations": int(ai.noOfMSIterations),
"InvalidDetectors": [int(det) for det in ai.maskedSpecAllNo],
"MultipleScatteringCorrection": ai.MSCorrectionFlag,
"InstrumentParametersFile": str(ipFilesPath / ai.instrument_parameters_file),
"HRatioToLowestMass": ai.intensity_ratio_of_hydrogen_to_lowest_mass if hasattr(ai, 'intensity_ratio_of_hydrogen_to_lowest_mass') else 0,
"NumberOfIterations": int(ai.number_of_iterations_for_corrections),
"InvalidDetectors": [int(det) for det in ai.mask_detectors],
"MultipleScatteringCorrection": ai.do_multiple_scattering_correction,
"SampleVerticalWidth": ai.vertical_width,
"SampleHorizontalWidth": ai.horizontal_width,
"SampleThickness": ai.thickness,
"GammaCorrection": ai.GammaCorrectionFlag,
"GammaCorrection": ai.do_gamma_correction,
"ModeRunning": scattering_type(ai),
"TransmissionGuess": ai.transmission_guess,
"MultipleScatteringOrder": int(ai.multiple_scattering_order),
"NumberOfEvents": int(ai.number_of_events),
"NumberOfEvents": int(ai.multiple_scattering_number_of_events),
"Constraints": str(dill.dumps(ai.constraints)),
"ResultsPath": str(self.results_save_path),
"FiguresPath": str(self.fig_save_path),
Expand All @@ -271,8 +272,8 @@ def _create_analysis_algorithm(self, ai):
def _make_neutron_profiles(cls, ai):
profiles = []
for mass, intensity, width, center, intensity_bound, width_bound, center_bound in zip(
ai.masses, ai.initPars[::3], ai.initPars[1::3], ai.initPars[2::3],
ai.bounds[::3], ai.bounds[1::3], ai.bounds[2::3]
ai.masses, ai.initial_fitting_parameters[::3], ai.initial_fitting_parameters[1::3], ai.initial_fitting_parameters[2::3],
ai.fitting_bounds[::3], ai.fitting_bounds[1::3], ai.fitting_bounds[2::3]
):
profiles.append(NeutronComptonProfile(
label=str(float(mass)), mass=float(mass), intensity=float(intensity), width=float(width), center=float(center),
Expand All @@ -294,11 +295,11 @@ def _save_ws_if_not_on_path(self, load_ai):

ipFilesPath = Path(handle_config.read_config_var("caching.ipfolder"))

if not ws_history_matches_inputs(load_ai.runs, load_ai.mode, load_ai.ipfile, rawPath):
save_ws_from_load_vesuvio(load_ai.runs, load_ai.mode, str(ipFilesPath/load_ai.ipfile), rawPath)
if not ws_history_matches_inputs(load_ai.runs, load_ai.mode, load_ai.instrument_parameters_file, rawPath):
save_ws_from_load_vesuvio(load_ai.runs, load_ai.mode, str(ipFilesPath/load_ai.instrument_parameters_file), rawPath)

if not ws_history_matches_inputs(load_ai.empty_runs, load_ai.mode, load_ai.ipfile, emptyPath):
save_ws_from_load_vesuvio(load_ai.empty_runs, load_ai.mode, str(ipFilesPath/load_ai.ipfile), emptyPath)
if not ws_history_matches_inputs(load_ai.empty_runs, load_ai.mode, load_ai.instrument_parameters_file, emptyPath):
save_ws_from_load_vesuvio(load_ai.empty_runs, load_ai.mode, str(ipFilesPath/load_ai.instrument_parameters_file), emptyPath)
return rawPath, emptyPath


Expand All @@ -309,13 +310,13 @@ def _set_output_paths(self, ai):

# Build Filename based on ic
corr = ""
if ai.GammaCorrectionFlag & (ai.noOfMSIterations > 0):
if ai.do_gamma_correction & (ai.number_of_iterations_for_corrections > 0):
corr += "_GC"
if ai.MSCorrectionFlag & (ai.noOfMSIterations > 0):
if ai.do_multiple_scattering_correction & (ai.number_of_iterations_for_corrections > 0):
corr += "_MS"

fileName = (
f"spec_{ai.firstSpec}-{ai.lastSpec}_iter_{ai.noOfMSIterations}{corr}" + ".npz"
f"spec_{ai.detectors.strip()}_iter_{ai.number_of_iterations_for_corrections}{corr}" + ".npz"
)
fileNameYSpace = fileName + "_ySpaceFit" + ".npz"

Expand Down
10 changes: 5 additions & 5 deletions src/mvesuvio/util/analysis_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ def create_profiles_table(name, ai):
table.addColumn(type="float", name="center")
table.addColumn(type="str", name="center_bounds")
for mass, intensity, width, center, intensity_bound, width_bound, center_bound in zip(
ai.masses, ai.initPars[::3], ai.initPars[1::3], ai.initPars[2::3],
ai.bounds[::3], ai.bounds[1::3], ai.bounds[2::3]
ai.masses, ai.initial_fitting_parameters[::3], ai.initial_fitting_parameters[1::3], ai.initial_fitting_parameters[2::3],
ai.fitting_bounds[::3], ai.fitting_bounds[1::3], ai.fitting_bounds[2::3]
):
table.addRow(
[str(float(mass)), float(mass), float(intensity), str(list(intensity_bound)),
Expand Down Expand Up @@ -203,13 +203,13 @@ def cropAndMaskWorkspace(ws, firstSpec, lastSpec, maskedDetectors, maskTOFRange)
OutputWorkspace=newWsName,
)

maskBinsWithZeros(wsCrop, maskTOFRange) # Used to mask resonance peaks
mask_time_of_flight_bins_with_zeros(wsCrop, maskTOFRange) # Used to mask resonance peaks

MaskDetectors(Workspace=wsCrop, SpectraList=maskedDetectors)
return wsCrop


def maskBinsWithZeros(ws, maskTOFRange):
def mask_time_of_flight_bins_with_zeros(ws, maskTOFRange):
"""
Masks a given TOF range on ws with zeros on dataY.
Leaves errors dataE unchanged, as they are used by later treatments.
Expand All @@ -220,7 +220,7 @@ def maskBinsWithZeros(ws, maskTOFRange):
return

dataX, dataY, dataE = extractWS(ws)
start, end = [int(s) for s in maskTOFRange.split(",")]
start, end = [float(s) for s in maskTOFRange.split("-")]
assert (
start <= end
), "Start value for masking needs to be smaller or equal than end."
Expand Down
Loading

0 comments on commit ade7d8f

Please sign in to comment.