Skip to content

Commit

Permalink
Fix system tests to run new analysis
Browse files Browse the repository at this point in the history
Added a system test for the new AnalysisReduction object.
All system tests are passing. The system tests have a coverage of
95% of analysis_reduction.py and are very stringent, so it is
fairly safe to say that this transition to OOP has not introduced
any new bugs.
  • Loading branch information
GuiMacielPereira committed Aug 1, 2024
1 parent 3927498 commit 9ab2044
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 59 deletions.
122 changes: 68 additions & 54 deletions src/mvesuvio/oop/AnalysisRoutine.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class AnalysisRoutine:

def __init__(self, workspace, ip_file, number_of_iterations, mask_spectra,
multiple_scattering_correction, vertical_width, horizontal_width, thickness,
gamma_correction, transmission_guess=None, multiple_scattering_order=None,
gamma_correction, mode_running, transmission_guess=None, multiple_scattering_order=None,
number_of_events=None, results_path=None):

self._workspace_to_fit = workspace
self._workspace_being_fit = workspace
self._name = workspace.name()
self._ip_file = ip_file
self._number_of_iterations = number_of_iterations
Expand All @@ -32,18 +32,19 @@ def __init__(self, workspace, ip_file, number_of_iterations, mask_spectra,
self._vertical_width = vertical_width
self._horizontal_width = horizontal_width
self._thickness = thickness
self._mode_running = mode_running

self._multiple_scattering_correction = multiple_scattering_correction
self._gamma_correction = gamma_correction

self._save_results_path = results_path

self._h_ratio = None
self._constraints = []
self._constraints = ()
self._profiles = {}

# Only used for system tests, remove once tests are updated
self._run_hist_data = False #True
self._run_hist_data = True
self._run_norm_voigt = False

# Links to another AnalysisRoutine object:
Expand Down Expand Up @@ -91,15 +92,9 @@ def profiles(self, incoming_profiles):
self._profiles = {**self._profiles, **common_keys_profiles}


def _preprocess(self):
def _set_const_methods(self):
# Set up variables used during fitting
self._masses = np.array([p.mass for p in self._profiles.values()])
self._dataX, self._dataY, self._dataE = extractWS(self._workspace_to_fit)
resolutionPars, instrPars, kinematicArrays, ySpacesForEachMass = self.prepareFitArgs()
self._resolution_params = resolutionPars
self._instrument_params = instrPars
self._kinematic_arrays = kinematicArrays
self._y_space_arrays = ySpacesForEachMass

self._initial_fit_parameters = []
self._bounds = []
Expand All @@ -111,19 +106,33 @@ def _preprocess(self):
self._bounds.append(p._width_bounds)
self._bounds.append(p._center_bounds)

# self._intensities = np.array([p.intensity for p in self._profiles.values()])[:, np.newaxis]
# self._widths = np.array([p.width for p in self._profiles.values()])[:, np.newaxis]
# self._centers = np.array([p.center for p in self._profiles.values()])[:, np.newaxis]
self._fig_save_path = None


def _update_workspace_data(self):
self._dataX, self._dataY, self._dataE = extractWS(self._workspace_being_fit)

if self._run_hist_data: # Converts point data from workspaces to histogram data
self._dataY, self._dataX, self._dataE = histToPointData(self._dataY, self._dataX, self._dataE)

self._set_up_kinematic_arrays()

self._fit_parameters = np.zeros((len(self._dataY), 3 * len(self._profiles) + 3))

self._fig_save_path = None


def _set_up_kinematic_arrays(self):
resolutionPars, instrPars, kinematicArrays, ySpacesForEachMass = self.prepareFitArgs()
self._resolution_params = resolutionPars
self._instrument_params = instrPars
self._kinematic_arrays = kinematicArrays
self._y_space_arrays = ySpacesForEachMass


def run(self):

assert len(self.profiles) > 0, "Add profiles before atempting to run the routine!"

self._preprocess()
self._set_const_methods()

self.createTableInitialParameters()

Expand All @@ -133,21 +142,23 @@ def run(self):
# InputWorkspace=ic.sampleWS, OutputWorkspace=initialWs.name()
# )

self._workspace_to_fit = CloneWorkspace(
InputWorkspace=self._workspace_to_fit,
CloneWorkspace(
InputWorkspace=self._workspace_being_fit,
OutputWorkspace=self._name + "0"
)

for iteration in range(self._number_of_iterations + 1):
# Workspace from previous iteration
wsToBeFitted = mtd[self._name + str(iteration)]
self._workspace_being_fit = mtd[self._name + str(iteration)]

self._update_workspace_data()

ncpTotal = self.fitNcpToWorkspace(wsToBeFitted)
ncpTotal = self.fitNcpToWorkspace()

mWidths, stdWidths, mIntRatios, stdIntRatios = self.extractMeans(wsToBeFitted.name())
mWidths, stdWidths, mIntRatios, stdIntRatios = self.extractMeans(self._workspace_being_fit.name())

self.createMeansAndStdTableWS(
wsToBeFitted.name(), mWidths, stdWidths, mIntRatios, stdIntRatios
self._workspace_being_fit.name(), mWidths, stdWidths, mIntRatios, stdIntRatios
)

# When last iteration, skip MS and GC
Expand Down Expand Up @@ -180,10 +191,9 @@ def run(self):
InputWorkspace="tmpNameWs", OutputWorkspace=self._name + str(iteration + 1)
)

wsFinal = mtd[self._name + str(self._number_of_iterations)]
self._create_results_mehtods()
self._set_up_results_mehtods()
self.save_results()
return wsFinal, self
return self


@staticmethod
Expand Down Expand Up @@ -234,28 +244,27 @@ def createTableInitialParameters(self):
print("\n")


def fitNcpToWorkspace(self, ws):
def fitNcpToWorkspace(self):
"""
Performs the fit of ncp to the workspace.
Firtly the arrays required for the fit are prepared and then the fit is performed iteratively
on a spectrum by spectrum basis.
"""

if self._run_hist_data: # Converts point data from workspaces to histogram data
self._dataY, self._dataX, self._dataE = histToPointData(self._dataY, self._dataX, self._dataE)

print("\nFitting NCP:\n")

arrFitPars = self.fitNcpToArray()

self.createTableWSForFitPars(ws.name(), len(self._profiles), arrFitPars)
self.createTableWSForFitPars(len(self._profiles), arrFitPars)

arrBestFitPars = arrFitPars[:, 1:-2]

ncpForEachMass, ncpTotal = self.calculateNcpArr(arrBestFitPars)
ncpSumWSs = self.createNcpWorkspaces(ncpForEachMass, ncpTotal, ws)
ncpSumWSs = self.createNcpWorkspaces(ncpForEachMass, ncpTotal)

wsDataSum = SumSpectra(InputWorkspace=ws, OutputWorkspace=ws.name() + "_Sum")
wsDataSum = SumSpectra(
InputWorkspace=self._workspace_being_fit.name(),
OutputWorkspace=self._workspace_being_fit.name() + "_Sum")
self.plotSumNCPFits(wsDataSum, *ncpSumWSs)
return ncpTotal

Expand Down Expand Up @@ -378,9 +387,9 @@ def fitNcpToArray(self):
return self._fit_parameters


def createTableWSForFitPars(self, wsName, noOfMasses, arrFitPars):
def createTableWSForFitPars(self, noOfMasses, arrFitPars):
tableWS = CreateEmptyTableWorkspace(
OutputWorkspace=wsName + "_Best_Fit_NCP_Parameters"
OutputWorkspace=self._workspace_being_fit.name()+ "_Best_Fit_NCP_Parameters"
)
tableWS.setTitle("SCIPY Fit")
tableWS.addColumn(type="float", name="Spec Idx")
Expand Down Expand Up @@ -422,25 +431,27 @@ def calculateNcpRow(self, initPars, row):
return ncpForEachMass


def createNcpWorkspaces(self, ncpForEachMass, ncpTotal, ws):
def createNcpWorkspaces(self, ncpForEachMass, ncpTotal):
"""Creates workspaces from ncp array data"""

# Need to rearrage array of yspaces into seperate arrays for each mass
ncpForEachMass = switchFirstTwoAxis(ncpForEachMass)

# Use ws dataX to match with histogram data
dataX = ws.extractX()[
:, : ncpTotal.shape[1]
] # Make dataX match ncp shape automatically
dataX = self._dataX
# dataX = ws.extractX()[
# :, : ncpTotal.shape[1]
# ] # Make dataX match ncp shape automatically
assert (
ncpTotal.shape == dataX.shape
), "DataX and DataY in ws need to be the same shape."

ncpTotWS = CloneWorkspace(InputWorkspace=ws.name(), OutputWorkspace=ws.name() + "_TOF_Fitted_Profiles")
passDataIntoWS(dataX, ncpTotal, np.zeros_like(dataX), ncpTotWS)
# ncpTotWS = createWS(
# dataX, ncpTotal, np.zeros(dataX.shape), ws.name() + "_TOF_Fitted_Profiles"
# )
# ncpTotWS = CloneWorkspace(InputWorkspace=ws.name(), OutputWorkspace=ws.name() + "_TOF_Fitted_Profiles")
# passDataIntoWS(dataX, ncpTotal, np.zeros_like(dataX), ncpTotWS)
ncpTotWS = createWS(
dataX, ncpTotal, np.zeros(dataX.shape), self._workspace_being_fit.name() + "_TOF_Fitted_Profiles",
parentWorkspace=self._workspace_being_fit
)
# MaskDetectors(Workspace=ncpTotWS, WorkspaceIndexList=ic.maskedDetectorIdx)
MaskDetectors(Workspace=ncpTotWS, SpectraList=self._mask_spectra)
wsTotNCPSum = SumSpectra(
Expand All @@ -450,14 +461,15 @@ def createNcpWorkspaces(self, ncpForEachMass, ncpTotal, ws):
# Individual ncp workspaces
wsMNCPSum = []
for i, ncp_m in enumerate(ncpForEachMass):
ncpMWS = CloneWorkspace(InputWorkspace=ws.name(), OutputWorkspace=ws.name()+"_TOF_Fitted_Profile_" + str(i))
passDataIntoWS(dataX, ncp_m, np.zeros_like(dataX), ncpMWS)
# ncpMWS = createWS(
# dataX,
# ncp_m,
# np.zeros(dataX.shape),
# ws.name() + "_TOF_Fitted_Profile_" + str(i),
# )
# ncpMWS = CloneWorkspace(InputWorkspace=ws.name(), OutputWorkspace=ws.name()+"_TOF_Fitted_Profile_" + str(i))
# passDataIntoWS(dataX, ncp_m, np.zeros_like(dataX), ncpMWS)
ncpMWS = createWS(
dataX,
ncp_m,
np.zeros(dataX.shape),
self._workspace_being_fit.name() + "_TOF_Fitted_Profile_" + str(i),
parentWorkspace=self._workspace_being_fit
)
MaskDetectors(Workspace=ncpMWS, SpectraList=self._mask_spectra)
wsNCPSum = SumSpectra(
InputWorkspace=ncpMWS, OutputWorkspace=ncpMWS.name() + "_Sum"
Expand Down Expand Up @@ -623,7 +635,7 @@ def fitNcpToSingleSpec(self, row):
)
fitPars = result["x"]

noDegreesOfFreedom = len(self._dataY) - len(fitPars)
noDegreesOfFreedom = len(self._dataY[row]) - len(fitPars)
specFitPars = np.append(self._instrument_params[row, 0], fitPars)
return np.append(specFitPars, [result["fun"] / noDegreesOfFreedom, result["nit"]])

Expand Down Expand Up @@ -957,9 +969,11 @@ def calcGammaCorrectionProfiles(self, meanWidths, meanIntensityRatios):
return profiles


def _create_results_mehtods(self):
def _set_up_results_mehtods(self):
"""Used to collect results from workspaces and store them in .npz files for testing."""

self.wsFinal = mtd[self._name + str(self._number_of_iterations)]

allIterNcp = []
allFitWs = []
allTotNcp = []
Expand Down
3 changes: 2 additions & 1 deletion src/mvesuvio/oop/analysis_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,13 @@ def switchFirstTwoAxis(A):
return np.stack(np.split(A, len(A), axis=0), axis=2)[0]


def createWS(dataX, dataY, dataE, wsName):
def createWS(dataX, dataY, dataE, wsName, parentWorkspace=None):
ws = CreateWorkspace(
DataX=dataX.flatten(),
DataY=dataY.flatten(),
DataE=dataE.flatten(),
Nspec=len(dataY),
OutputWorkspace=wsName,
ParentWorkspace=parentWorkspace
)
return ws
12 changes: 8 additions & 4 deletions src/mvesuvio/oop/run_routine.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def run_analysis():
ws = loadRawAndEmptyWsFromUserPath(
userWsRawPath='/home/ljg28444/Documents/user_0/some_new_experiment/some_new_exp/input_ws/some_new_exp_raw_forward.nxs',
userWsEmptyPath='/home/ljg28444/Documents/user_0/some_new_experiment/some_new_exp/input_ws/some_new_exp_empty_forward.nxs',
tofBinning = "275.,1.,420",
tofBinning = "110.,1.,420",
name='exp',
scaleRaw=1,
scaleEmpty=1,
Expand All @@ -24,11 +24,15 @@ def run_analysis():

AR = AnalysisRoutine(cropedWs,
ip_file='/home/ljg28444/.mvesuvio/ip_files/ip2018_3.par',
number_of_iterations=0,
number_of_iterations=1,
mask_spectra=[173, 174, 179],
multiple_scattering_correction=False,
multiple_scattering_correction=True,
vertical_width=0.1, horizontal_width=0.1, thickness=0.001,
gamma_correction=False)
gamma_correction=True,
mode_running='FORWARD',
transmission_guess=0.853,
multiple_scattering_order=2,
number_of_events=1.0e5)

H = NeutronComptonProfile('H', mass=1.0079, intensity=1, width=4.7, center=0,
intensity_bounds=[0, np.nan], width_bounds=[3, 6], center_bounds=[-3, 1])
Expand Down
Loading

0 comments on commit 9ab2044

Please sign in to comment.