Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve user experience #151

Merged
merged 7 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading