diff --git a/Testing/SystemTests/tests/analysis/ISIS_LETReduction.py b/Testing/SystemTests/tests/analysis/ISIS_LETReduction.py
index 4127e682ade24ee907d32007b5a8f53ba4c27dd7..ea33a9ac66c3077994a558cb77e09736d1a7511c 100644
--- a/Testing/SystemTests/tests/analysis/ISIS_LETReduction.py
+++ b/Testing/SystemTests/tests/analysis/ISIS_LETReduction.py
@@ -12,10 +12,13 @@ try:
     import reduce_vars as web_var
 except ImportError:
     web_var = None
-
-#
+try:
+    import mantidplot as mpl
+except:
+    mpl = None
 
 
+#
 def find_binning_range(energy,ebin):
     """ function finds the binning range used in multirep mode
         for merlin ls=11.8,lm2=10. mult=2.8868 dt_DAE=1
@@ -95,7 +98,7 @@ class ReduceLET_OneRep(ReductionWrapper):
         prop['detector_van_range']=[0.5,200]
         prop['load_monitors_with_workspace']=True
         return prop
-        #
+    #
 
     @iliad
     def reduce(self,input_file=None,output_directory=None):
@@ -223,6 +226,51 @@ class ReduceLET_MultiRep2015(ReductionWrapper):
     def __init__(self,web_var=None):
         """Sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'LET',web_var)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
+    #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+    #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
 
     def set_custom_output_filename(self):
         """Define custom name of output files if standard one is not satisfactory
@@ -249,7 +297,65 @@ class ReduceLET_MultiRep2015(ReductionWrapper):
         #return lambda : custom_name(self.reducer.prop_man)
         # use this method to use standard file name generating function
         return None
+        #
+
+    def eval_absorption_corrections(self,test_ws=None):
+        """ The method to evaluate the speed and efficiency of the absorption corrections procedure,
+            before applying your corrections to the whole workspace and all sample runs.
+
+            The absorption correction procedure invoked with excessive accuracy can run for too
+            long providing no real improvements in accuracy. This is why it is recommended to
+            run this procedure evaluating absorption on selected detectors and
+            deploy the corrections to the whole runs only after achieving satisfactory accuracy
+            and execution time.
+
+            The procedure evaluate and prints the expected time to run the absorption corrections
+            on the whole run.
+
+            Input:
+            If provided, the pointer or the name of the workspace available in analysis data service.
+            If it is not, the workspace is taken from PropertyManager.sample_run property
+
+            Usage:
+            Reduce single run and uncomment this method in the __main__ area to evaluate
+            absorption corrections.
 
+            Change absorption corrections parameters below to achieve best speed and
+            acceptable accuracy
+        """
+
+        # Gain access to the property manager:
+        propman =  rd.reducer.prop_man
+        # Set up Sample as one of:
+        # 1) Cylinder([Chem_formula],[Height,Radius])
+        # 2) FlatPlate([Chem_formula],[Height,Width,Thick])
+        # 3) HollowCylinder([Chem_formula],[Height,InnerRadius,OuterRadius])
+        # 4) Sphere([[Chem_formula],Radius)
+        # The units are in cm
+        propman.correct_absorption_on = Cylinder('Fe',[10,2]) # Will be taken from def_advanced_properties
+        #                                prop['correct_absorption_on'] =  if not defined here
+        #
+        # Use Monte-Carlo integration.  Take sparse energy points and a few integration attempts
+        # to increase initial speed. Increase these numbers to achieve better accuracy.
+        propman.abs_corr_info = {'EventsPerPoint':3000}#,'NumberOfWavelengthPoints':30}
+        # See MonteCarloAbsorption for all possible properties description and possibility to define
+        # a sparse instrument for speed.
+        #
+        # Gain access to the workspace. The workspace should contain Ei log, containing incident energy
+        # (or be reduced)
+        if test_ws is None:
+            test_ws = PropertyManager.sample_run.get_workspace()
+        # Define spectra list to test absorption on
+        check_spectra = [1,200]
+        # Evaluate corrections on the selected spectra of the workspace and the time to obtain
+        # the corrections on the whole workspace.
+        corrections,time_to_correct_abs = self.evaluate_abs_corrections(test_ws,check_spectra)
+        # When accuracy and speed of the corrections is satisfactory, copy chosen abs_corr_info
+        # properties from above to the advanced_porperties area to run in reduction.
+        if mpl:
+            mpl.plotSpectrum(corrections,range(0,len(check_spectra)))
+        #
+        return corrections
 #----------------------------------------------------------------------------------------------------------------------
 
 
@@ -297,10 +403,5 @@ if __name__=="__main__":
     # usual way to go is to reduce workspace and save it internally
     rd.run_reduction()
 
-
-#### Validate reduction result against known result, obtained earlier  ###
-    #rez,mess=rd.validate_result()
-    #if not rez:
-    #   raise RuntimeError("validation failed with error: {0}".format(mess))
-    #else:
-    #   print "ALL Fine"
+###### Test absorption corrections to find optimal settings for corrections algorithm
+#     corr = rd.eval_absorption_corrections()
diff --git a/Testing/SystemTests/tests/analysis/ISIS_MAPS_DGSReduction.py b/Testing/SystemTests/tests/analysis/ISIS_MAPS_DGSReduction.py
index 0f7c1eea5d5eee747a105d0ab81316130a92dc48..799b7744141568205c3f301923e0b83c2c4c3c1e 100644
--- a/Testing/SystemTests/tests/analysis/ISIS_MAPS_DGSReduction.py
+++ b/Testing/SystemTests/tests/analysis/ISIS_MAPS_DGSReduction.py
@@ -11,6 +11,10 @@ try:
     import reduce_vars as web_var
 except:
     web_var = None
+try:
+    import mantidplot as mpl
+except:
+    mpl = None
 
 
 class ReduceMAPS(ReductionWrapper):
@@ -73,8 +77,52 @@ class ReduceMAPS(ReductionWrapper):
     def __init__(self,web_var=None):
         """ sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'MAP',web_var)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
+    #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
     #
 
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
+
     def set_custom_output_filename(self):
         """ define custom name of output files if standard one is not satisfactory
           In addition to that, example of accessing reduction properties
@@ -100,7 +148,65 @@ class ReduceMAPS(ReductionWrapper):
         #return lambda : custom_name(self.reducer.prop_man)
         # use this method to use standard file name generating function
         return None
+    #
+
+    def eval_absorption_corrections(self,test_ws=None):
+        """ The method to evaluate the speed and efficiency of the absorption corrections procedure,
+            before applying your corrections to the whole workspace and all sample runs.
+
+            The absorption correction procedure invoked with excessive accuracy can run for too
+            long providing no real improvements in accuracy. This is why it is recommended to
+            run this procedure evaluating absorption on selected detectors and
+            deploy the corrections to the whole runs only after achieving satisfactory accuracy
+            and execution time.
+
+            The procedure evaluate and prints the expected time to run the absorption corrections
+            on the whole run.
 
+            Input:
+            If provided, the pointer or the name of the workspace available in analysis data service.
+            If it is not, the workspace is taken from PropertyManager.sample_run property
+
+            Usage:
+            Reduce single run and uncomment this method in the __main__ area to evaluate
+            absorption corrections.
+
+            Change absorption corrections parameters below to achieve best speed and
+            acceptable accuracy
+        """
+
+        # Gain access to the property manager:
+        propman =  rd.reducer.prop_man
+        # Set up Sample as one of:
+        # 1) Cylinder([Chem_formula],[Height,Radius])
+        # 2) FlatPlate([Chem_formula],[Height,Width,Thick])
+        # 3) HollowCylinder([Chem_formula],[Height,InnerRadius,OuterRadius])
+        # 4) Sphere([[Chem_formula],Radius)
+        # The units are in cm
+        propman.correct_absorption_on = Cylinder('Fe',[10,2]) # Will be taken from def_advanced_properties
+        #                                prop['correct_absorption_on'] =  if not defined here
+        #
+        # Use Monte-Carlo integration.  Take sparse energy points and a few integration attempts
+        # to increase initial speed. Increase these numbers to achieve better accuracy.
+        propman.abs_corr_info = {'EventsPerPoint':3000}#,'NumberOfWavelengthPoints':30}
+        # See MonteCarloAbsorption for all possible properties description and possibility to define
+        # a sparse instrument for speed.
+        #
+        # Gain access to the workspace. The workspace should contain Ei log, containing incident energy
+        # (or be reduced)
+        if test_ws is None:
+            test_ws = PropertyManager.sample_run.get_workspace()
+        # Define spectra list to test absorption on
+        check_spectra = [1,200]
+        # Evaluate corrections on the selected spectra of the workspace and the time to obtain
+        # the corrections on the whole workspace.
+        corrections,time_to_correct_abs = self.evaluate_abs_corrections(test_ws,check_spectra)
+        # When accuracy and speed of the corrections is satisfactory, copy chosen abs_corr_info
+        # properties from above to the advanced_porperties area to run in reduction.
+        if mpl:
+            mpl.plotSpectrum(corrections,range(0,len(check_spectra)))
+        #
+        return corrections
 
 #----------------------------------------------------------------------------------------------------------------------
 if __name__ == "__main__":
@@ -151,10 +257,5 @@ if __name__ == "__main__":
     # usual way to go is to reduce workspace and save it internally
     rd.run_reduction()
 
-
-#### Validate reduction result against known result, obtained earlier  ###
-    #rez,mess=rd.validate_result()
-    #if not rez:
-    #   raise RuntimeError("validation failed with error: {0}".format(mess))
-    #else:
-    #   print "ALL Fine"
+###### Test absorption corrections to find optimal settings for corrections algorithm
+#     corr = rd.eval_absorption_corrections()
diff --git a/Testing/SystemTests/tests/analysis/ISIS_MERLINReduction.py b/Testing/SystemTests/tests/analysis/ISIS_MERLINReduction.py
index a33969c7547e86296754b7ad026380ba7fb97373..e3c79a0ffbb440db361412c17733c53b6ac8b5d3 100644
--- a/Testing/SystemTests/tests/analysis/ISIS_MERLINReduction.py
+++ b/Testing/SystemTests/tests/analysis/ISIS_MERLINReduction.py
@@ -11,6 +11,10 @@ try:
     import reduce_vars as web_var
 except:
     web_var = None
+try:
+    import mantidplot as mpl
+except:
+    mpl = None
 
 
 class ReduceMERLIN(ReductionWrapper):
@@ -55,33 +59,161 @@ class ReduceMERLIN(ReductionWrapper):
         outWS = ReductionWrapper.reduce(self,input_file,output_directory)
      #SaveNexus(ws,Filename = 'MARNewReduction.nxs')
         return outWS
+      #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+    #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
+    #
+
+    def eval_absorption_corrections(self,test_ws=None):
+        """ The method to evaluate the speed and efficiency of the absorption corrections procedure,
+            before applying your corrections to the whole workspace and all sample runs.
+
+            The absorption correction procedure invoked with excessive accuracy can run for too
+            long providing no real improvements in accuracy. This is why it is recommended to
+            run this procedure evaluating absorption on selected detectors and
+            deploy the corrections to the whole runs only after achieving satisfactory accuracy
+            and execution time.
+
+            The procedure evaluate and prints the expected time to run the absorption corrections
+            on the whole run.
+
+            Input:
+            If provided, the pointer or the name of the workspace available in analysis data service.
+            If it is not, the workspace is taken from PropertyManager.sample_run property
+
+            Usage:
+            Reduce single run and uncomment this method in the __main__ area to evaluate
+            absorption corrections.
+
+            Change absorption corrections parameters below to achieve best speed and
+            acceptable accuracy
+        """
+
+        # Gain access to the property manager:
+        propman =  rd.reducer.prop_man
+        # Set up Sample as one of:
+        # 1) Cylinder([Chem_formula],[Height,Radius])
+        # 2) FlatPlate([Chem_formula],[Height,Width,Thick])
+        # 3) HollowCylinder([Chem_formula],[Height,InnerRadius,OuterRadius])
+        # 4) Sphere([[Chem_formula],Radius)
+        # The units are in cm
+        propman.correct_absorption_on = Cylinder('Fe',[10,2]) # Will be taken from def_advanced_properties
+        #                                prop['correct_absorption_on'] =  if not defined here
+        #
+        # Use Monte-Carlo integration.  Take sparse energy points and a few integration attempts
+        # to increase initial speed. Increase these numbers to achieve better accuracy.
+        propman.abs_corr_info = {'EventsPerPoint':3000}#,'NumberOfWavelengthPoints':30}
+        # See MonteCarloAbsorption for all possible properties description and possibility to define
+        # a sparse instrument for speed.
+        #
+        # Gain access to the workspace. The workspace should contain Ei log, containing incident energy
+        # (or be reduced)
+        if test_ws is None:
+            test_ws = PropertyManager.sample_run.get_workspace()
+        # Define spectra list to test absorption on
+        check_spectra = [1,200]
+        # Evaluate corrections on the selected spectra of the workspace and the time to obtain
+        # the corrections on the whole workspace.
+        corrections,time_to_correct_abs = self.evaluate_abs_corrections(test_ws,check_spectra)
+        # When accuracy and speed of the corrections is satisfactory, copy chosen abs_corr_info
+        # properties from above to the advanced_porperties area to run in reduction.
+        if mpl:
+            mpl.plotSpectrum(corrections,range(0,len(check_spectra)))
+        #
+        return corrections
 
     def __init__(self,web_var=None):
         """ sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'MER',web_var)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
 #----------------------------------------------------------------------------------------------------------------------
 
 
 if __name__=="__main__":
-    #import os
-    #os.environ["PATH"] = r"c:/Mantid/Code/builds/br_master/bin/Release;"+os.environ["PATH"]
-
-    #maps_dir = 'd:/Data/MantidSystemTests/Data'
-    #data_dir ='d:/Data/Mantid_Testing/14_11_27'
-    #ref_data_dir = 'd:/Data/MantidSystemTests/SystemTests/AnalysisTests/ReferenceResults'
+    #maps_dir = r'd:\Data\MantidDevArea\Datastore\DataCopies\Testing\Data\SystemTest'
+    #data_dir = r'd:\Data\Mantid_Testing\15_03_01'
+    #ref_data_dir = r'd:\Data\MantidDevArea\Datastore\DataCopies\Testing\SystemTests\tests\analysis\reference'
     #config.setDataSearchDirs('{0};{1};{2}'.format(data_dir,maps_dir,ref_data_dir))
     #config.appendDataSearchDir('d:/Data/Mantid_GIT/Test/AutoTestData')
     #config['defaultsave.directory'] = data_dir # folder to save resulting spe/nxspe files. Defaults are in
 
-     # execute stuff from Mantid
+    # execute stuff from Mantid
     rd = ReduceMERLIN()
+    #rd = ReduceLET_OneRep()
     rd.def_advanced_properties()
     rd.def_main_properties()
 
-     #using_web_data = False
-     #if not using_web_data:
-     #   run_dir=os.path.dirname(os.path.realpath(__file__))
-     #   file = os.path.join(run_dir,'reduce_vars.py')
-     #   rd.export_changed_values(file)
+#### uncomment rows below to generate web variables and save then to transfer to   ###
+    ## web services.
+    #run_dir = os.path.dirname(os.path.realpath(__file__))
+    #file = os.path.join(run_dir,'reduce_vars.py')
+    #rd.save_web_variables(file)
 
-    rd.reduce()
+#### Set up time interval (sec) for reducer to check for input data file.         ####
+    #  If this file is not present and this value is 0,reduction fails
+    #  if this value >0 the reduction wait until file appears on the data
+    #  search path checking after time specified below.
+    rd.wait_for_file = 0  # waiting time interval
+
+####get reduction parameters from properties above, override what you want locally ###
+   # and run reduction. Overriding would have form:
+   # rd.reducer.property_name (from the dictionary above) = new value e.g.
+   # rd.reducer.energy_bins = [-40,2,40]
+   # or
+   ## rd.reducer.sum_runs = False
+
+###### Run reduction over all run numbers or files assigned to                   ######
+    # sample_run  variable
+
+    # return output workspace only if you are going to do
+    # something with it here. Running range of runs will return the array
+    # of workspace pointers.
+    #red_ws = rd.run_reduction()
+    # usual way to go is to reduce workspace and save it internally
+    rd.run_reduction()
+
+###### Test absorption corrections to find optimal settings for corrections algorithm
+#     corr = rd.eval_absorption_corrections()
+    #import os
+    #os.environ["PATH"] = r"c:/Mantid/Code/builds/br_master/bin/Release;"+os.environ["PATH"]
diff --git a/Testing/SystemTests/tests/analysis/ISIS_MariReduction.py b/Testing/SystemTests/tests/analysis/ISIS_MariReduction.py
index 03d5c1cf79f2e7c825d5df296914db9dd014be95..61a86f5f8267625b8e25a037416c54434bea5796 100644
--- a/Testing/SystemTests/tests/analysis/ISIS_MariReduction.py
+++ b/Testing/SystemTests/tests/analysis/ISIS_MariReduction.py
@@ -13,6 +13,10 @@ try:
     import reduce_vars as web_var
 except:
     web_var = None
+try:
+    import mantidplot as mpl
+except:
+    mpl = None
 
 
 class ReduceMARIFromFile(ReductionWrapper):
@@ -83,10 +87,55 @@ class ReduceMARIFromFile(ReductionWrapper):
         #return lambda : custom_name(self.reducer.prop_man)
         # use this method to use standard file name generating function
         return None
+    #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+    #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
 
     def __init__(self,web_var_val=None):
         """ sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'MAR',web_var_val)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
 #-------------------------------------------------------------------------------------------------#
 #-------------------------------------------------------------------------------------------------#
 #-------------------------------------------------------------------------------------------------#
@@ -157,10 +206,55 @@ class ReduceMARIFromWorkspace(ReductionWrapper):
         ws = ReductionWrapper.reduce(self,input_file,output_directory)
      #SaveNexus(ws,Filename = 'MARNewReduction.nxs')
         return ws
+    #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+    #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
 
     def __init__(self,web_var_val=None):
         """ sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'MAR',web_var_val)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
 #----------------------------------------------------------------------------------------------------------------------
 
 
@@ -212,10 +306,55 @@ class ReduceMARIMon2Norm(ReductionWrapper):
         outWS = ReductionWrapper.reduce(self,input_file,output_directory)
      #SaveNexus(ws,Filename = 'MARNewReduction.nxs')
         return outWS
+    #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+    #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
 
     def __init__(self,web_var_val=None):
         """ sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'MAR',web_var_val)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
 #----------------------------------------------------------------------------------------------------------------------
 
 
@@ -260,10 +399,55 @@ class MARIReductionSum(ReductionWrapper):
         ws = ReductionWrapper.reduce(self,input_file,output_directory)
      #SaveNexus(ws,Filename = 'MARNewReduction.nxs')
         return ws
+    #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+    #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
 
     def __init__(self,web_var_val=None):
         """ sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'MAR',web_var_val)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
 #----------------------------------------------------------------------------------------------------------------------
 
 
@@ -317,10 +501,55 @@ class ReduceMARIMonitorsSeparate(ReductionWrapper):
         outWS = ReductionWrapper.reduce(self,input_file,output_directory)
      #SaveNexus(outWS,Filename = 'MARNewReduction.nxs')
         return outWS
+    #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+    #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
 
     def __init__(self,web_var_val=None):
         """ sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'MAR',web_var_val)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
 
 
 class ReduceMARIAutoEi(ReductionWrapper):
@@ -402,10 +631,113 @@ class ReduceMARIAutoEi(ReductionWrapper):
         #return lambda : custom_name(self.reducer.prop_man)
         # use this method to use standard file name generating function
         return None
+    #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+    #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
+
+    def eval_absorption_corrections(self,test_ws=None):
+        """ The method to evaluate the speed and efficiency of the absorption corrections procedure,
+            before applying your corrections to the whole workspace and all sample runs.
+
+            The absorption correction procedure invoked with excessive accuracy can run for too
+            long providing no real improvements in accuracy. This is why it is recommended to
+            run this procedure evaluating absorption on selected detectors and
+            deploy the corrections to the whole runs only after achieving satisfactory accuracy
+            and execution time.
+
+            The procedure evaluate and prints the expected time to run the absorption corrections
+            on the whole run.
+
+            Input:
+            If provided, the pointer or the name of the workspace available in analysis data service.
+            If it is not, the workspace is taken from PropertyManager.sample_run property
+
+            Usage:
+            Reduce single run and uncomment this method in the __main__ area to evaluate
+            absorption corrections.
+
+            Change absorption corrections parameters below to achieve best speed and
+            acceptable accuracy
+        """
+
+        # Gain access to the property manager:
+        propman =  rd.reducer.prop_man
+        # Set up Sample as one of:
+        # 1) Cylinder([Chem_formula],[Height,Radius])
+        # 2) FlatPlate([Chem_formula],[Height,Width,Thick])
+        # 3) HollowCylinder([Chem_formula],[Height,InnerRadius,OuterRadius])
+        # 4) Sphere([[Chem_formula],Radius)
+        # The units are in cm
+        propman.correct_absorption_on = Cylinder('Fe',[10,2]) # Will be taken from def_advanced_properties
+        #                                prop['correct_absorption_on'] =  if not defined here
+        #
+        # Use Monte-Carlo integration.  Take sparse energy points and a few integration attempts
+        # to increase initial speed. Increase these numbers to achieve better accuracy.
+        propman.abs_corr_info = {'EventsPerPoint':3000}#,'NumberOfWavelengthPoints':30}
+        # See MonteCarloAbsorption for all possible properties description and possibility to define
+        # a sparse instrument for speed.
+        #
+        # Gain access to the workspace. The workspace should contain Ei log, containing incident energy
+        # (or be reduced)
+        if test_ws is None:
+            test_ws = PropertyManager.sample_run.get_workspace()
+        # Define spectra list to test absorption on
+        check_spectra = [1,200]
+        # Evaluate corrections on the selected spectra of the workspace and the time to obtain
+        # the corrections on the whole workspace.
+        corrections,time_to_correct_abs = self.evaluate_abs_corrections(test_ws,check_spectra)
+        # When accuracy and speed of the corrections is satisfactory, copy chosen abs_corr_info
+        # properties from above to the advanced_porperties area to run in reduction.
+        if mpl:
+            mpl.plotSpectrum(corrections,range(0,len(check_spectra)))
+        #
+        return corrections
 
     def __init__(self,web_var_val=None):
         """ sets properties defaults for the instrument with Name"""
         ReductionWrapper.__init__(self,'MAR',web_var_val)
+        Mt = MethodType(self.do_preprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+        Mt = MethodType(self.do_postprocessing, self.reducer)
+        DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
 #-------------------------------------------------------------------------------------------------#
 
 
@@ -439,19 +771,6 @@ if __name__ == "__main__":
     #  search path checking after time specified below.
     rd.wait_for_file = 0  # waiting time interval
 
-### Define a run number to validate reduction against future changes    #############
-    # Take a run number with good reduced results and build validation run
-    # for this result. Then place the validation run together with the reduction script.
-    # Next time, the script will run reduction and compare the reduction results against
-    # the results obtained earlier.
-    #rd.validate_run_number = 21968  # Enabling this property disables normal reduction
-    # and forces reduction to reduce run specified here and compares results against
-    # validation file, processed earlier or calculate this file if run for the first time.
-    #This would ensure that reduction script have not changed,
-    #allow to identify reason of changes if it was and would allow to recover the script,
-    #used to produce initial reduction if changes are unacceptable.
-
-
 ####get reduction parameters from properties above, override what you want locally ###
    # and run reduction.  Overriding would have form:
    # rd.reducer.property_name (from the dictionary above) = new value e.g.
@@ -468,3 +787,6 @@ if __name__ == "__main__":
     #red_ws = rd.run_reduction()
     # usual way to go is to reduce workspace and save it internally
     rd.run_reduction()
+
+###### Test absorption corrections to find optimal settings for corrections algorithm
+#     corr = rd.eval_absorption_corrections()
diff --git a/instrument/LET_Parameters.xml b/instrument/LET_Parameters.xml
index 229217281a13df47c75ef1c78878e36bfd13d214..307130c66405b8ddcd611c0ab272da79325062c8 100644
--- a/instrument/LET_Parameters.xml
+++ b/instrument/LET_Parameters.xml
@@ -414,17 +414,39 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation 
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation 
      (psi in Horace) -->
 <parameter name="motor_log_names"  type="string">
    <value val="CCR_ROT;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections)  This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
+
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
      the reducer keywords are the leftmost values of the keyword assignments below
diff --git a/instrument/LET_Parameters_dr1to12.xml b/instrument/LET_Parameters_dr1to12.xml
index 7dc8b5fa8b6a0429ad2879ad1c4327c118a7bc00..f6b1279703b658dfd2e6848d022c8616f52302d7 100644
--- a/instrument/LET_Parameters_dr1to12.xml
+++ b/instrument/LET_Parameters_dr1to12.xml
@@ -414,18 +414,41 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation 
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation 
      (psi in Horace) -->
 <parameter name="motor_log_names"  type="string">
    <value val="CCR_ROT;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections)  This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
+
+
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
      the reducer keywords are the leftmost values of the keyword assignments below
diff --git a/instrument/LET_Parameters_dr2to12.xml b/instrument/LET_Parameters_dr2to12.xml
index b1907b5869f18bb7824c8a24c58cda21270c9211..43569aa3eca466950fd00f0dcb99eb31c9ed1b84 100644
--- a/instrument/LET_Parameters_dr2to12.xml
+++ b/instrument/LET_Parameters_dr2to12.xml
@@ -414,17 +414,38 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation 
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation 
      (psi in Horace) -->
 <parameter name="motor_log_names"  type="string">
    <value val="CCR_ROT;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections)  This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
 
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
diff --git a/instrument/LET_Parameters_dr2to9.xml b/instrument/LET_Parameters_dr2to9.xml
index 58c8e9bf70e0167ccd3b8095a26e5aed5aedc52a..32f59fc4a728dcbaefb71e52fffbde61ac02ca7d 100644
--- a/instrument/LET_Parameters_dr2to9.xml
+++ b/instrument/LET_Parameters_dr2to9.xml
@@ -414,17 +414,38 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation 
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation 
      (psi in Horace) -->
 <parameter name="motor_log_names"  type="string">
    <value val="CCR_ROT;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
 
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
diff --git a/instrument/LET_Parameters_dr3to10.xml b/instrument/LET_Parameters_dr3to10.xml
index 256fe193272545719da00adcd985617bb08baa5e..cdb528b460db049f9607b8e7e08f797b253ec42a 100644
--- a/instrument/LET_Parameters_dr3to10.xml
+++ b/instrument/LET_Parameters_dr3to10.xml
@@ -414,17 +414,39 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation 
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation 
      (psi in Horace) -->
 <parameter name="motor_log_names"  type="string">
    <value val="CCR_ROT;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
+
 
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
diff --git a/instrument/LET_Parameters_dr3to11.xml b/instrument/LET_Parameters_dr3to11.xml
index c46297bf739324d3e8269370ad57503c1c243b01..b1441ee05437f76af37e6669a11d9fa77c36e5d1 100644
--- a/instrument/LET_Parameters_dr3to11.xml
+++ b/instrument/LET_Parameters_dr3to11.xml
@@ -414,18 +414,40 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation 
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation 
      (psi in Horace) -->
 <parameter name="motor_log_names"  type="string">
    <value val="CCR_ROT;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
+
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
      the reducer keywords are the leftmost values of the keyword assignments below
diff --git a/instrument/MAPS_Parameters.xml b/instrument/MAPS_Parameters.xml
index c68ec39f93028ca2030dc16e93146fc6a2d97ffa..4c2557b4016eba27a1444f0e48e230d658bdc71a 100644
--- a/instrument/MAPS_Parameters.xml
+++ b/instrument/MAPS_Parameters.xml
@@ -419,17 +419,39 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation
      (psi in Horace) -->
 <parameter name="motor_log_namesmotor_log_names"  type="string">
    <value val="wccr;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
+
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
      the reducer keywords are the leftmost values of the keyword assignments below
diff --git a/instrument/MAPS_Parameters_2017_06_02.xml b/instrument/MAPS_Parameters_2017_06_02.xml
index 0d807fa36bb81c8c97ebf89b1335b26abb68a0a1..80cce2bb583f3293b1be9be1ea99eb694cafd702 100644
--- a/instrument/MAPS_Parameters_2017_06_02.xml
+++ b/instrument/MAPS_Parameters_2017_06_02.xml
@@ -419,17 +419,39 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation
      (psi in Horace) -->
 <parameter name="motor_log_namesmotor_log_names"  type="string">
    <value val="wccr;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
+
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
      the reducer keywords are the leftmost values of the keyword assignments below
diff --git a/instrument/MARI_Parameters.xml b/instrument/MARI_Parameters.xml
index 406d41f85af810440ace64de2c1ee643d88d679e..2ed1c1badc285a6806ff0b5de4c17ccf97dfcdd6 100644
--- a/instrument/MARI_Parameters.xml
+++ b/instrument/MARI_Parameters.xml
@@ -427,12 +427,35 @@
 <parameter name="motor_log_names"  type="string">
    <value val="wccr;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
+
+
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
      the reducer keywords are the leftmost values of the keyword assignments below
diff --git a/instrument/MARI_Parameters_19900101_20160911.xml b/instrument/MARI_Parameters_19900101_20160911.xml
index 1162fdd091ea9980a26aff0e3b4216e9d12f24ed..35549b66f57f226a843106aab83363ecf41cdd8a 100644
--- a/instrument/MARI_Parameters_19900101_20160911.xml
+++ b/instrument/MARI_Parameters_19900101_20160911.xml
@@ -415,18 +415,39 @@
    <value val="False"/>
 </parameter>
 
-<!-- The semicolon separated list of possible log names, containing information on crystl rotation.
-     First found log will be used togethere with motor_offset to identify crystal rotation 
+<!-- The semicolon separated list of possible log names, containing information on crystal rotation.
+     First found log will be used together with motor_offset to identify crystal rotation 
      (psi in Horace) MARI of course not uses it and probably not writes such log, but 
-     it is goot to have it here for testing purtposes -->
+     it is goot to have it here for testing purposes -->
 <parameter name="motor_log_names"  type="string">
    <value val="wccr;Rot"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
 
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
diff --git a/instrument/MERLIN_Parameters.xml b/instrument/MERLIN_Parameters.xml
index 88bdbdc9deb9d4998ac1dc70eae58f000471b206..4b68b5e9d889a3b4b77fcf166d82a45a164db684 100644
--- a/instrument/MERLIN_Parameters.xml
+++ b/instrument/MERLIN_Parameters.xml
@@ -420,11 +420,32 @@
 <parameter name="motor_log_names"  type="string">
    <value val="CCR_ROT;wccr"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
 
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
diff --git a/instrument/MERLIN_Parameters_2017_02.xml b/instrument/MERLIN_Parameters_2017_02.xml
index 391803ba18a83613da8a359f2f34769880ac8932..f7bdd996c6ed22eba344f64b861083510b712a82 100644
--- a/instrument/MERLIN_Parameters_2017_02.xml
+++ b/instrument/MERLIN_Parameters_2017_02.xml
@@ -425,6 +425,26 @@
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
 
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
diff --git a/instrument/MERLIN_Parameters_2018_03.xml b/instrument/MERLIN_Parameters_2018_03.xml
index 8ae55cbec86913160257fc38f556d76d6d3b2cf9..8478a8e79e4a37331c2e99db15e236bbcf87f5c6 100644
--- a/instrument/MERLIN_Parameters_2018_03.xml
+++ b/instrument/MERLIN_Parameters_2018_03.xml
@@ -425,6 +425,29 @@
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
+
+
 
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
diff --git a/instrument/MERLIN_Parameters_after2013_4.xml b/instrument/MERLIN_Parameters_after2013_4.xml
index 3a47b550dcef5803c01630ba1964c22e4d916c05..e70bd2ae309383f5aefd4e3ea133e122539e7a81 100644
--- a/instrument/MERLIN_Parameters_after2013_4.xml
+++ b/instrument/MERLIN_Parameters_after2013_4.xml
@@ -420,11 +420,32 @@
 <parameter name="motor_log_names"  type="string">
    <value val="CCR_ROT;wccr"/>
 </parameter>
-<!-- Initial value used to identify crytsal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
+<!-- Initial value used to identify crystal rotation angle psi=motor_offset+wccr.timeAverageValue()  -->
 <parameter name="motor_offset">
    <value val="None"/>
 </parameter>
 
+<parameter name="correct_absorption_on"  type="string">
+  <value val="None"/>
+  <description is="If defined, the property would initiate the run of the absorption corrections procedure, specific for the measured sample. 
+  The corrections themselves are controlled by one of the absorption shapes classes, defined for Direct inelastic reduction. 
+  These shapes are Cylinder, Plate, HollowCylinder and other defined in the AbsorptionShapes module within the direct inelastic 
+  reduction scripts folder"/>
+</parameter>
+<parameter name="abs_corr_info"  type="string">
+  <value val="None"/>
+  <description is="The property describes the type of the absorption corrections to run and, if necessary, 
+  additional properties of the absorption algorithm to use. The acceptable types are:
+  is_mc:True (for Monte-Carlo corrections) This option is default option if neither is_mc nor is_fast key is present.
+  is_fast:True (for numerical and, when possible, analytical corrections)
+  The property string may have a form:
+  is_mc:True,NumberOfWavelengthPoints:50,EventsPerPoint:100 or any other non-sample related properties, accepted by Monte-Carlo corrections algorithm
+  or
+  is_fast:True,ExpMethod:FastApprox,ElementSize:10 or any other non-sample related accepted by AbsorptionCorrection algorithm
+  If set up in a Python script, the info can be provided both as a string above and as a Python dictionary. 
+  "/>
+</parameter>
+
 
 <!-- List of the words which can be used as a command line arguments to define reducer keywords
      the form is reducer_keword1=synonim1=synonim2=synonim3;reducer_keword1=synonim1a, so, 
diff --git a/scripts/Inelastic/Direct/AbsorptionShapes.py b/scripts/Inelastic/Direct/AbsorptionShapes.py
new file mode 100644
index 0000000000000000000000000000000000000000..48d6d18a47975ad683f8ba3d7d4046a703cfc555
--- /dev/null
+++ b/scripts/Inelastic/Direct/AbsorptionShapes.py
@@ -0,0 +1,692 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+
+from __future__ import (absolute_import, division, print_function)
+from mantid.kernel import funcinspect
+from mantid.simpleapi import *
+
+import random
+import types
+import ast
+import collections
+
+
+class anAbsorptionShape(object):
+    """ The parent class for all shapes, used to perform various absorption corrections
+    in direct inelastic analysis.
+
+    Contains material and environment properties necessary for absorption corrections calculations,
+    as recognized by SetSampleMaterial algorithm.
+
+    See SetSampleMaterial for full list of properties available to set.
+
+    Usage:
+    Instantiate as:
+    anShape = anAbsorptionShape([ChemicalFormula,NumberDensity])
+        where:
+        -- ChemicalFormula is the string, describing the sample formula
+        -- NumberDensity   is the number density of the sample in number
+           of atoms or formula units per cubic Angstrom. If provided, will be used
+           instead of the one, calculated from the Chemical formula
+        e.g:
+        anShape = anAbsorptionShape(['Li',0.1])
+    or
+    anShape = anAbsorptionShape({'ChemicalFormula':'Formula','NumberDensity':Number})
+        e.g:
+        anShape = anAbsorptionShape({'ChemicalFormula':'(Li7)2-C-H4-N-Cl6','NumberDensity':0.8})
+    or
+    anShape = anAdsrbShape(Dictionary with any number of key-value pairs recognized by SetSampleMaterial algorithm)
+    e.g:
+    anShape = anAbsorptionShape({'AtomicNumber':12,'AttenuationXSection':0.5,'SampleMassDensity':120})
+
+    The Material definition should unambiguously define sample material for absorption calculations.
+
+    The set material value may be accessed or changes through the property material: e.g.
+    mat = anShape.material
+    or
+    anShape.material = {'ChemicalFormula':'Br'} (change existing ChemicalFormula)
+
+    Note:
+    Adding dictionary appends or modifies existing properties, but adding a list, clears up all properties, previously
+    set-up on class.
+    The list can contain up two members where first corresponds to the ChemicalFormula and the second one -- to the
+    material's number density
+    """
+
+    # The list of the shapes defined in the module and used as the basis of the
+    # absorption shapes factory
+    _Defined_Shapes = {}
+
+    def __init__(self,MaterialValue=None):
+        # environment according to existing definitions of the environment
+        self._Environment = {}
+        # default sample material (formula)
+        self._Material = {}
+        # the shape holder.  Not defined in this class but the holder for all
+        # other classes
+        self._ShapeDescription = {}
+
+        # the workspace used for testing correct properties settings
+        rhash = random.randint(1,100000)
+        self._testWorkspace = CreateSampleWorkspace(OutputWorkspace=
+            '_adsShape_' + str(rhash),NumBanks=1,BankPixelWidth=1) # noqa: E127
+
+        if MaterialValue is not None:
+            self.material = MaterialValue
+        # If true, SetSample algorithm can set-up this shape. If not, a generic
+        # CreateSampleShape algorithm should be used
+        self._CanSetSample = True
+        # some shapes have axis, some does not. This to distinguish between them and
+        # make default axis specific to instrument
+        self._shape_has_axis = False
+       # Property describes if a shape axis direction have been changed.
+        # Some instruments (e.g. MARI) have non-standart default axis directions
+        self._axis_is_default=True
+    #
+
+    def __del__(self):
+        DeleteWorkspace(self._testWorkspace)
+    #
+
+    def __str__(self):
+        """ Convert an absorption shape into a string representation"""
+        return str(self._ShapeDescription) + '!' + str(self._Material)
+
+    #
+    @property
+    def material(self):
+        """ Contains the material, used in absorbtion correction calculations"""
+        return self._Material
+
+    @material.setter
+    def material(self,value):
+        if value is None:
+            self._Material = {}
+            return
+        if isinstance(value,str):
+            value = [value]
+        if isinstance(value,(list,tuple)):
+            if len(value) == 2:
+                self._Material = {'ChemicalFormula':value[0],'SampleNumberDensity':float(value[1])}
+            elif len(value) == 1:
+                self._Material = {'ChemicalFormula':value[0]}
+            else:
+                raise TypeError(
+                    '*** If material is defined by list or tuple,'
+                    ' it may consist of 1 or 2 members,'
+                    ' defining the material formula and the material number density')
+        elif isinstance(value,dict):
+            for key,val in value.items():
+                self._Material[key] = val
+        else:
+            raise TypeError(
+                '*** Material accepts only list or tuple containing up to 2 values'
+                ' corresponding to material formula and material number density'
+                ' or dictionary with keys, recognized by SetSampleMaterial algorithm')
+        #
+        Mater_properties = self._Material
+        if 'ChemicalFormula' in Mater_properties:
+            if not isinstance(Mater_properties['ChemicalFormula'],str):
+                raise TypeError('*** The chemical formula for the material must be described by a string')
+        # Test if the material property are recognized by SetSampleMaterial
+        # algorithm.
+        SetSampleMaterial(self._testWorkspace,**Mater_properties)
+
+    #
+    def correct_absorption(self,ws,*args,**kwargs):
+        """ The generic method, which switches between fast and Monte-Carlo absorption corrections
+        depending on the type and properties variable provided as second input
+
+        kwargs is a dictionary, with at least one key, describing type of
+               corrections (fast or Monte-Carlo) and other keys (if any)
+               containing additional properties of the correspondent correction
+               algorithm.
+
+        Returns:
+        1) absorption-corrected workspace
+        2) if two output arguments are provided, second would be workspace with absorption corrections coefficients
+        """
+        n_outputs,var_names = funcinspect.lhs_info('both')
+
+        if args is None or len(args) == 0:
+            if kwargs is None:
+                corr_properties = {}
+            else:
+                corr_properties = kwargs
+        else:
+            corr_properties = args[0]
+            if corr_properties is None:
+                corr_properties = {}
+            else:
+                if not isinstance(corr_properties,dict):
+                    raise TypeError(
+                        '*** Second non-keyword argument of the correct_absorption routine'
+                        ' (if present) should be a dictionary containing '
+                        ' additional parameters for selected AbsorptionCorrections algorithm')
+
+        correction_base_ws = ConvertUnits(ws,'Wavelength',EMode='Direct')
+        Mater_properties = self._Material
+        SetSampleMaterial(correction_base_ws,**Mater_properties)
+
+        if self._shape_has_axis:
+            self._check_MARI_axis_(ws)
+
+        if self._CanSetSample:
+            shape_description = self._ShapeDescription
+            SetSample(correction_base_ws,Geometry=shape_description)
+
+        mc_corrections = corr_properties.pop('is_mc', False)
+        fast_corrections = corr_properties.pop('is_fast',False)
+        if  not(mc_corrections or fast_corrections) or (mc_corrections and fast_corrections):
+            fast_corrections = False # Case when both keys are true or false reverts to default
+
+        if fast_corrections:
+            #raise RuntimeError('Analytical absorption corrections are not currently implemented in Direct mode')
+            abs_corrections = self._fast_abs_corrections(correction_base_ws,corr_properties)
+        else:
+            abs_corrections = self._mc_abs_corrections(correction_base_ws,corr_properties)
+
+        abs_corrections = ConvertUnits(abs_corrections,'DeltaE',EMode='Direct')
+        ws = ws / abs_corrections
+
+        DeleteWorkspace(correction_base_ws)
+        if ws.name() != var_names[0]:
+            RenameWorkspace(ws,var_names[0])
+        if n_outputs == 1:
+            #DeleteWorkspace(abs_corrections)
+            return ws
+        elif n_outputs == 2:
+            if abs_corrections.name() != var_names[1]:
+                RenameWorkspace(abs_corrections,var_names[1])
+            return (ws,abs_corrections)
+
+    def _fast_abs_corrections(self,correction_base_ws,kwarg={}):
+        """ Method to correct absorption on a shape using fast (Numerical Integration) method
+            if such method is available for the shape
+
+        Not available on arbitrary shapes
+        Inputs:
+         ws     -- workspace to correct. Should be in the units of wavelength
+        **kwarg -- dictionary of the additional keyword arguments to provide as input for
+                the absorption corrections algorithm
+                These arguments should not be related to the sample as the sample should already be defined
+        Returns:
+            workspace with absorption corrections.
+        """
+        adsrbtn_correctios = AbsorptionCorrection(correction_base_ws,**kwarg)
+        return adsrbtn_correctios
+
+    #
+    def _mc_abs_corrections(self,correction_base_ws,kwarg={}):
+        """ Method to correct absorption on a shape using Mont-Carlo integration
+        Inputs:
+         ws     -- workspace to correct. Should be in the units of wavelength
+        **kwarg -- dictionary of the additional keyword arguments to provide as input for
+                the absorption corrections algorithm
+                These arguments should not be related to the sample as the sample should
+                already be defined.
+        Returns:
+            workspace with absorption corrections.
+        """
+        adsrbtn_correctios = MonteCarloAbsorption(correction_base_ws,**kwarg)
+        return adsrbtn_correctios
+
+    #
+    @staticmethod
+    def from_str(str_val):
+        """ Retrieve absorption shape from a string representation
+
+        Implements shapes factory, so every new shape class should be subscribed to it
+        """
+        if len(anAbsorptionShape._Defined_Shapes) == 0:
+            anAbsorptionShape._Defined_Shapes = \
+                {'Cylinder':     Cylinder(),      'FlatPlate':FlatPlate(),
+                'HollowCylinder':HollowCylinder(),'Sphere':   Sphere()}  # noqa: E127
+
+        if not isinstance(str_val,str):
+            raise ValueError(
+                'The input of the "from_str" function should be a string representing a diary.'
+                ' Actually it is: {0}'.format(type(str_val)))
+        str_list = str_val.split('!')
+        shape_par = ast.literal_eval(str_list[0])
+        mater_par = ast.literal_eval(str_list[1])
+
+        the_shape_id = shape_par.pop('Shape',None)
+        if the_shape_id is None:
+            raise ValueError(
+                'The input of the "from_str" function = {0} but does not contain the '
+                'Shape description e.g. the Key Shape:ShapeName'.format(str_val))
+        theShape = anAbsorptionShape._Defined_Shapes[the_shape_id]
+        theShape.material = mater_par
+        theShape.shape = shape_par
+        return theShape
+
+    #
+    def _set_list_property(self,value,shape_name,mandatory_prop_list,opt_prop_list,opt_val_list):
+        """ General function to build list property for various absorption corrections algorithms
+            taking it from various forms of users input and converting it into standard key-value
+            dictionary.
+        """
+        if value is None or not value:
+            self._axis_is_default = True
+            return {}
+        if not isinstance(value, collections.Iterable):
+            value = [value]
+        n_elements = len(value)
+        if n_elements < len(mandatory_prop_list):
+            raise TypeError(
+                '*** {0} shape parameter needes at least {1} imput parameters namely: {2}'
+                .format(shape_name,len(mandatory_prop_list),mandatory_prop_list))
+
+        shape_dict = {'Shape':shape_name}
+        all_prop = mandatory_prop_list + opt_prop_list
+        if isinstance(value,(list,tuple)):
+            for i in range(0,n_elements):
+                val = value[i]
+                if isinstance(val,(list,tuple)):
+                    val = [float(x) for x in val]
+                else:
+                    val = float(val)
+                shape_dict[all_prop[i]] = val
+        elif isinstance(value,dict):
+            for key,val in value.items():
+                if isinstance(val,(list,tuple)):
+                    val = [float(x) for x in val]
+                else:
+                    val = float(val)
+                shape_dict[key] = val
+        else:
+            raise TypeError(
+                '*** {0} shape parameter accepts only list or tuple containing from {1} to {2}'
+                ' values corresponding to the {0} parameters: {3} or the dictionary with these,'
+                ' as recognized by SetSample algorithm Geometry property'.
+                format( shape_name,len(mandatory_prop_list),len(all_prop),all_prop))
+        #
+        if 'Axis' in shape_dict:
+            self._axis_is_default = False
+        for ik in range(0,len(opt_prop_list)):
+            if not opt_prop_list[ik] in shape_dict:
+                opt_val = opt_val_list[ik]
+                if isinstance(opt_val,types.FunctionType):
+                    shape_dict[opt_prop_list[ik]] = opt_val(shape_dict)
+                else:
+                    shape_dict[opt_prop_list[ik]] = opt_val
+
+        return shape_dict
+    #
+
+    def _check_MARI_axis_(self,workspace):
+        """ method verifies, if default axis needs to be changed for MARI"""
+        if self._axis_is_default:
+            instrument = workspace.getInstrument()
+            instr_name = instrument.getName()
+            short_n = instr_name[0:3]
+            if short_n.lower() == 'mar':
+                self._ShapeDescription['Axis'] = [1.,0.,0.]
+
+
+##---------------------------------------------------------------------------------------------------
+class Cylinder(anAbsorptionShape):
+    """Define the absorbing cylinder and calculate absorption corrections for this cylinder
+
+    Usage:
+    abs = Cylinder(SampleMaterial,CylinderParameters)
+    The SampleMaterial is the list or dictionary as described on anAbsorptionShape class
+
+    and
+
+    CylinderParameters can have the form:
+
+    a) The list consisting of 2 to 4 members.:
+    CylinderParameters = [Height,Radius,[[Axis],[Center]]
+    where Height, Radius are the cylinder height and radius in cm and
+    Axis, if present is the direction of the cylinder wrt to the beam direction [0,0,1]
+    e.g.:
+    abs = Cylinder(['Al',0.1],[10,2,[1,0,0],[0,0,0]])
+    b) The diary:
+    CylinderParameters = {Height:Height_value,Radius:Radius_value,[Axis:axisValue],[Center:TheSampleCentre]}
+    e.g:
+    abs = Cylinder(['Al',0.1],[Height:10,Radius:2,Axis:[1,0,0]])
+
+    Usage of the defined Cylinder class instance:
+    Correct absorption on the cylinder using CylinderAbsorption algorithm:
+    ws = abs.correct_absorption(ws) % it is default
+
+    Correct absorption on the defined cylinder using Monte-Carlo Absorption algorithm:
+    ws = ads.correct_absorption(ws,{is_mc:True,AdditionalMonte-Carlo Absorption parameters});
+    """
+    def __init__(self,Material=None,CylinderParams=None):
+
+        anAbsorptionShape.__init__(self,Material)
+        self.shape = CylinderParams
+        self._shape_has_axis = True
+
+    @property
+    def shape(self):
+        return self._ShapeDescription
+
+    @shape.setter
+    def shape(self,value):
+        shape_dict = self._set_list_property(value,
+             'Cylinder',['Height','Radius'],         # noqa
+            ['Axis','Center'],[[0.,1.,0.],[0.,0.,0.]])  # noqa
+
+        self._ShapeDescription = shape_dict
+
+        if len(shape_dict) != 0:
+            # Test if the shape property is recognized by CreateSampleShape
+            # algorithm.
+            SetSample(self._testWorkspace,Geometry=shape_dict)
+    #
+
+    def _fast_abs_corrections(self,correction_base_ws,kwarg={}):
+        """ Method to correct absorption on a shape using fast (Numerical Integration) method
+        """
+        kw = kwarg.copy()
+        elem_size = kw.pop('NumberOfSlices',None)
+        if elem_size is not None:
+            shape_dic = self.shape
+            n_slices = int(shape_dic['Height']/elem_size)
+            if n_slices <1:
+                n_slices = 1
+            n_annul  = int(shape_dic['Radius']*2*3.1415926/elem_size)
+            if n_annul < 1:
+                n_annul = 1
+            kw['NumberOfSlices'] = n_slices
+            kw['NumberOfAnnuli'] = n_annul
+        if 'Emode' not in kw:
+            kw['Emode'] = 'Direct'
+        adsrbtn_correctios = AbsorptionCorrection(correction_base_ws,**kw)
+        return adsrbtn_correctios
+
+
+##---------------------------------------------------------------------------------------------------
+class FlatPlate(anAbsorptionShape):
+    """Define the absorbing plate and calculate absorption corrections from this plate
+
+    Usage:
+    abs = FlatPlate(SampleMaterial,PlateParameters)
+    The SampleMaterial is the list or dictionary as described on anAbsorptionShape class
+
+    and
+
+    PlateParameters can have the form:
+
+    a) The list consisting of 3 to 5 members e.g.:
+    PlateParameters = [Height,Width,Thickness,[Centre,[Angle]]]
+    where Height,Width,Thickness are the plate sizes in cm,
+    the Center, if present, a list of three values indicating the [X,Y,Z] position of the center.
+    he reference frame of the defined instrument is used to set the coordinate system for the shape
+
+    The Angle argument for a flat plate shape is expected to be in degrees and is defined as the
+    angle between the positive beam axis and the normal to the face perpendicular to the beam axis
+    when it is not rotated, increasing in an anti-clockwise sense. The rotation is performed about
+    the vertical axis of the instrument's reference frame.
+
+    e.g.:
+    abs = FlatPlate(['Al',0.1],[10,2,0.2,[1,0,0],5])
+    b) The diary:
+    PlateParameters = {Height:Height_value,Width:Width_value,Thickness:Thickness_value etc}
+    e.g:
+    abs = FlatPlate(['Al',0.1],['Height':10,'Width':4,'Thickness':2,'Angle':30)
+
+    Usage of the defined Plate class instance:
+    Correct absorption on the defined plate using AbsorptionCorrections algorithm:
+    ws = abs.correct_absorption(ws,{'is_fast':True,Additiona FlatPlateAbsorption algorithm parameters})
+
+    Correct absorption on the defined Plate using Monte-Carlo Absorption algorithm:
+    ws = ads.correct_absorption(ws,{'is_mc':True,AdditionalMonte-Carlo Absorption parameters});
+    """
+    def __init__(self,Material=None,PlateParams=None):
+
+        anAbsorptionShape.__init__(self,Material)
+        self.shape = PlateParams
+
+    @property
+    def shape(self):
+        return self._ShapeDescription
+
+    @shape.setter
+    def shape(self,value):
+        shape_dict = self._set_list_property(value,
+            'FlatPlate',['Height','Width','Thick'],
+            ['Center','Angle'],[[0.,0.,0.],0.])  # noqa: E127
+
+        self._ShapeDescription = shape_dict
+
+        if len(shape_dict) != 0:
+            # Test if the shape property is recognized by CreateSampleShape
+            # algorithm.
+            SetSample(self._testWorkspace,Geometry=shape_dict)
+
+    #
+    def _fast_abs_corrections(self,correction_base_ws,kwarg={}):
+        """ Method to correct absorption on the FlatPlate using fast (Numerical Integration) method
+        """
+        kw = kwarg.copy()
+        prop_dict = {'Height':'SampleHeight','Width':'SampleWidth','Thick':'SampleThickness'}
+        for key,val in prop_dict.items():
+            kw[val] = self._ShapeDescription[key]
+        if 'Emode' not in kw:
+            kw['Emode'] = 'Direct'
+        adsrbtn_correctios = FlatPlateAbsorption(correction_base_ws,**kw)
+        return adsrbtn_correctios
+
+
+##---------------------------------------------------------------------------------------------------
+class HollowCylinder(anAbsorptionShape):
+    """Define the Hollow absorbing cylinder and calculate absorption corrections for this cylinder
+
+    Usage:
+    abs = HollowCylinder(SampleMaterial,CylinderParameters)
+    The SampleMaterial is the list or dictionary as described on anAbsorptionShape class
+
+    and
+
+    CylinderParameters can have the form:
+
+    a) The list consisting of 3 to 5 members.:
+    CylinderParameters = [Height,InnerRadus,OuterRadus,[[Axis],[Center]]
+    where Height, InnerRadus and OuterRadus are the cylinder height and radius-es in cm and
+    Axis, if present is the direction of the cylinder wrt. to the beam direction [0,0,1]
+    e.g.:
+    abs = HollowCylinder(['Al',0.1],[10,2,4,[0,1,0],[0,0,0]])
+    b) The diary:
+    CylinderParameters = {'Height':Height_value,'InnerRadus':value1,
+    'OuterRadus':value2,[Axis:axisValue],[Center:TheSampleCentre]}
+    e.g:
+    abs = HollowCylinder(['Al',0.1],[Height:10,InnerRadus:2,OuterRadus:2,Axis:[1,0,0]])
+
+    Usage of the defined HollowCylinder class instance:
+    Correct absorption on the cylinder using CylinderAbsorption algorithm:
+    ws = abs.correct_absorption(ws) % it is default
+
+    Correct absorption on the defined cylinder using Monte-Carlo Absorption algorithm:
+    ws = ads.correct_absorption(ws,{is_mc:True,AdditionalMonte-Carlo Absorption parameters});
+    """
+    def __init__(self,Material=None,CylinderParams=None):
+
+        anAbsorptionShape.__init__(self,Material)
+        self.shape = CylinderParams
+        self._CanSetSample = False
+        self._shape_has_axis = True
+    #
+
+    @property
+    def shape(self):
+        return self._ShapeDescription
+
+    @shape.setter
+    def shape(self,value):
+        shape_dict = self._set_list_property(value,
+            'HollowCylinder',
+            ['Height','InnerRadius','OuterRadius'],
+            ['Axis','Center'],[[0.,1.,0.],[0.,0.,0.]])  # noqa: E127
+        #
+        self._ShapeDescription = shape_dict
+        if len(shape_dict) != 0:
+            # Test if the shape property is recognized by CreateSampleShape
+            # algorithm.
+            self._add_xml_hollow_cylinder(self._testWorkspace)
+
+    #
+    def _add_xml_hollow_cylinder(self,ws):
+        # xml shape is normaly defined in meters
+        sample_xml_template = """<hollow-cylinder id="HOLL_CYL">
+            <centre-of-bottom-base x="{0}" y="{1}" z="{2}" />
+            <axis x="{3}" y="{4}" z="{5}" />
+            <inner-radius val="{6}" />
+            <outer-radius val="{7}" />
+            <height val="{8}" />
+         </hollow-cylinder>"""""
+        shape_dic = self._ShapeDescription
+        Cenr = [c * 0.01 for c in shape_dic['Center']]
+        Axis = shape_dic['Axis']
+        sample_shape = \
+            sample_xml_template.format(Cenr[0],Cenr[1],Cenr[2],
+                                       Axis[0],Axis[1],Axis[2],
+                                       0.01 * shape_dic['InnerRadius'],
+                                       0.01 * shape_dic['OuterRadius'],
+                                       0.01 * shape_dic['Height'])
+        CreateSampleShape(ws,sample_shape)
+
+    #
+    def _fast_abs_corrections(self,correction_base_ws,kwarg={}):
+        """ Method to correct absorption on the HollowCylinder using fast (Numerical Integration) method
+        """
+        self._add_xml_hollow_cylinder(correction_base_ws)
+
+        if 'Emode' not in kwarg:
+            kwarg['Emode'] = 'Direct'
+        adsrbtn_correctios = AbsorptionCorrection(correction_base_ws,**kwarg)
+        return adsrbtn_correctios
+
+    #
+    def _mc_abs_corrections(self,correction_base_ws,kwarg={}):
+        """ Method to correct absorption on the HollowCylinder using Monte-Carlo integration
+        Inputs:
+         ws     -- workspace to correct. Should be in the units of wavelength
+        **kwarg -- dictionary of the additional keyword arguments to provide as input for
+                the absorption corrections algorithm
+                These arguments should not be related to the sample as the sample should already be defined.
+        Returns:
+            workspace with absorption corrections.
+        """
+        self._add_xml_hollow_cylinder(correction_base_ws)
+        adsrbtn_correctios = MonteCarloAbsorption(correction_base_ws,**kwarg)
+        return adsrbtn_correctios
+
+
+##---------------------------------------------------------------------------------------------------
+class Sphere(anAbsorptionShape):
+    """Define the absorbing sphere and calculate absorption corrections from this sphere
+
+    Usage:
+    abs = Sphere(SampleMaterial,SphereParameters)
+    The SampleMaterial is the list or dictionary as described on anAbsorptionShape class
+
+    and
+
+    SphereParameters can have the form:
+
+    a) The list consisting of 1 to 2 members e.g.:
+    SphereParameters = [Radius,[Center]]
+    where Radius is the sphere radius in cm,
+    the Center, if present, a list of three values indicating the [X,Y,Z] position of the center.
+    The reference frame of the defined instrument is used to set the coordinate system
+    for the shape.
+
+    e.g.:
+    abs = Sphere(['Al',0.1],[10,[1,0,0]])
+    b) The diary:
+    SphereParameters = {Radius:value,Centre:[1,1,1] etc}
+    e.g:
+    abs = Sphere(['Al',0.1],['Radius':10)
+
+    Usage of the defined Sphere class instance:
+
+    Correct absorption on the defined Sphere using AbsorptionCorrections algorithm:
+    ws = abs.correct_absorption(ws)
+
+    Correct absorption on the defined Sphere using Monte-Carlo Absorption algorithm:
+    ws = ads.correct_absorption(ws,{'is_mc':True,AdditionalMonte-Carlo Absorption parameters});
+    """
+    def __init__(self,Material=None,SphereParams=None):
+
+        anAbsorptionShape.__init__(self,Material)
+        self.shape = SphereParams
+        self._CanSetSample = False
+
+    #
+    @property
+    def shape(self):
+        return self._ShapeDescription
+
+    #
+    @shape.setter
+    def shape(self,value):
+        shape_dict = self._set_list_property(value,
+            'Sphere',['Radius'],
+            ['Center'],[[0.,0.,0.]])  # noqa: E127
+
+        self._ShapeDescription = shape_dict
+        if len(shape_dict) != 0:
+            # Test if the shape property is recognized by CreateSampleShape
+            # algorithm.
+            self._add_xml_sphere(self._testWorkspace)
+
+    def _add_xml_sphere(self,ws):
+        # xml shape is normaly defined in meters
+        sample_xml_template = """<sphere id="WHOLE_SPHERE">
+            <centre x="{0}" y="{1}" z="{2}" />
+            <radius val="{3}" />
+         </sphere>"""""
+        shape_dic = self._ShapeDescription
+        Cenr = [c * 0.01 for c in shape_dic['Center']]
+        sample_shape = \
+            sample_xml_template.format(Cenr[0],Cenr[1],Cenr[2],
+                                       0.01 * shape_dic['Radius'])
+        CreateSampleShape(ws,sample_shape)
+
+    #
+    def _fast_abs_corrections(self,correction_base_ws,kwarg={}):
+        """ Method to correct absorption on the Sphere using fast (Numerical Integration) method
+            (Analytical integration) method.
+            If the method is invoked without parameters, optimized SphericalAbsorption algorithm is
+            deployed to calculate corrections. If there are some parameters, more general
+            AbsorptionCorrections method is invoked with the parameters provided.
+        """
+        kw = kwarg.copy()
+        if 'Emode' not in kwarg:
+            kw['Emode'] = 'Direct'
+        if kw['Emode'].lower() == 'elastic':
+            adsrbtn_correctios = SphericalAbsorption(
+                correction_base_ws,SphericalSampleRadius=self._ShapeDescription['Radius'])
+        else:
+            self._add_xml_sphere(correction_base_ws)
+            adsrbtn_correctios = AbsorptionCorrection(correction_base_ws,**kw)
+        return adsrbtn_correctios
+    #
+
+    def _mc_abs_corrections(self,correction_base_ws,kwarg={}):
+        """ Method to correct absorption on the Sphere using Monte-Carlo integration
+        Inputs:
+         ws     -- workspace to correct. Should be in the units of wavelength
+        **kwarg -- dictionary of the additional keyword arguments to provide as input for
+                the absorption corrections algorithm
+                These arguments should not be related to the sample as the sample should already be defined.
+        Returns:
+            workspace with absorption corrections.
+        """
+        self._add_xml_sphere(correction_base_ws)
+        adsrbtn_correctios = MonteCarloAbsorption(correction_base_ws,**kwarg)
+        return adsrbtn_correctios
+
+
+##---------------------------------------------------------------------------------------------------
+if __name__ == "__main__":
+    pass
diff --git a/scripts/Inelastic/Direct/DirectEnergyConversion.py b/scripts/Inelastic/Direct/DirectEnergyConversion.py
index 429e13e4bb22390accc6682607984b8028362261..8bd1155159366221ef71cb0d01d1338cff5752ee 100644
--- a/scripts/Inelastic/Direct/DirectEnergyConversion.py
+++ b/scripts/Inelastic/Direct/DirectEnergyConversion.py
@@ -152,7 +152,6 @@ class DirectEnergyConversion(object):
 #-------------------------------------------------------------------------------
 #pylint: disable=too-many-branches
 #pylint: disable=too-many-locals
-
     def diagnose(self, white,diag_sample=None,**kwargs):
         """run diagnostics on the provided workspaces.
 
@@ -362,7 +361,7 @@ class DirectEnergyConversion(object):
 #pylint: disable=too-many-branches
 #pylint: disable=too-many-locals
 #pylint: disable=W0621
-
+# flake8: noqa 
     def convert_to_energy(self,wb_run=None,sample_run=None,ei_guess=None,rebin=None,map_file=None,
                           monovan_run=None,wb_for_monovan_run=None,**kwargs):
         """ One step conversion of run into workspace containing information about energy transfer
@@ -376,8 +375,8 @@ class DirectEnergyConversion(object):
 
         # output workspace name.
         try:
-            _,r = funcinspect.lhs_info('both')
-            out_ws_name = r[0]
+            names = funcinspect.lhs_info('names')
+            out_ws_name = names[0]
 #pylint: disable=bare-except
         except:
             out_ws_name = None
@@ -518,6 +517,14 @@ class DirectEnergyConversion(object):
             else:
                 # single energy uses single workspace and all TOF are used
                 tof_range = None
+
+            # Do custom preprocessing if such operation is defined
+            try:
+                ws_to_preprocess = PropertyManager.sample_run.get_workspace()
+                ws_to_preprocess = self.do_preprocessing(ws_to_preprocess)
+                PropertyManager.sample_run.synchronize_ws(ws_to_preprocess)
+            except AttributeError:
+                pass
             #---------------
             #
             #Run the conversion first on the sample
@@ -538,10 +545,19 @@ class DirectEnergyConversion(object):
                                                                          ei_guess,mono_ws_base,tof_range, cut_ind,num_ei_cuts)
             else:
                 pass # no absolute units corrections
-            # ensure that the sample_run name is intact with workspace
+            # ensure that the sample_run name is intact with the sample workspace
             PropertyManager.sample_run.synchronize_ws(deltaE_ws_sample)
+            if prop_man.correct_absorption_on is not None:
+                abs_shape = prop_man.correct_absorption_on
+                deltaE_ws_sample = abs_shape.correct_absorption(deltaE_ws_sample,prop_man.abs_corr_info)
             #
             #
+            # Do custom post-processing if such operation is defined
+            try:
+                deltaE_ws_sample = self.do_postprocessing(deltaE_ws_sample)
+                PropertyManager.sample_run.synchronize_ws(deltaE_ws_sample)
+            except AttributeError:
+                pass
             self.save_results(deltaE_ws_sample)
 
             # prepare output workspace
@@ -550,10 +566,9 @@ class DirectEnergyConversion(object):
                 if self._multirep_mode:
                     result.append(deltaE_ws_sample)
                 else:
-                    if results_name != out_ws_name:
-                        RenameWorkspace(InputWorkspace=results_name,OutputWorkspace=out_ws_name)
-                        results_name = out_ws_name
-                        result = mtd[results_name]
+                    if results_name != out_ws_name: # This actually returns deltaE_ws_sample.name()
+                        # to the state, defined in ADS. Intentionally skip renaming here.
+                        result = PropertyManager.sample_run.synchronize_ws(deltaE_ws_sample)
                     else:
                         result = deltaE_ws_sample
             else: # delete workspace if no output is requested
@@ -610,7 +625,6 @@ class DirectEnergyConversion(object):
                 return False
         else:
             return True
-
 #------------------------------------------------------------------------------------------
 #pylint: disable=too-many-arguments
 
@@ -1902,8 +1916,8 @@ class DirectEnergyConversion(object):
         low,upp = self.wb_integr_range
         white_tag = 'NormBy:{0}_IntergatedIn:{1:0>10.2f}:{2:0>10.2f}'.format(self.normalise_method,low,upp)
         return white_tag
-    #
 
+    #
     def _clear_old_results(self):
         """Remove workspaces, processed earlier and not used any more"""
         ws_list = self._old_runs_list
@@ -1911,7 +1925,15 @@ class DirectEnergyConversion(object):
             if ws_name in mtd:
                 DeleteWorkspace(ws_name)
         object.__setattr__(self,'_old_runs_list',[])
+
     #
+    def do_preprocessing(self,ws,*argi):
+        """ stub for custom preprocessing function. Should be redefined in the reduction script."""
+        return ws
+
+    def do_postprocessing(self,ws,*argi):
+        """ stub for custom postprocessing function. Should be redefined in the reduction script."""
+        return ws
 
 
 def get_failed_spectra_list_from_masks(masked_wksp,prop_man):
diff --git a/scripts/Inelastic/Direct/NonIDF_Properties.py b/scripts/Inelastic/Direct/NonIDF_Properties.py
index 5bb5a59c0cc67f87a048909658c327a76c5ccd2b..53c0c3bc54efe1f2cd4763e4dd0f5c7928745017 100644
--- a/scripts/Inelastic/Direct/NonIDF_Properties.py
+++ b/scripts/Inelastic/Direct/NonIDF_Properties.py
@@ -110,7 +110,7 @@ class NonIDF_Properties(object):
                          If not explicitly set, white beam for sample run is used.""")
     # TODO: do something about it.  Second white is explicitly used in
     # diagnostics but not accessed at all
-    second_white  = RunDescriptor("""Second white beam run resutlts currently unused in the workflow
+    second_white  = RunDescriptor("""Second white beam run results currently unused in the workflow
                     despite being referred to in Diagnostics.
                     In a future it should be enabled.""")
     #
diff --git a/scripts/Inelastic/Direct/PropertiesDescriptors.py b/scripts/Inelastic/Direct/PropertiesDescriptors.py
index a8ce205c0719d75fd4156beae0ea557c5a19113c..4c22949de19aaaa4f1dacf372371686afdefa7c5 100644
--- a/scripts/Inelastic/Direct/PropertiesDescriptors.py
+++ b/scripts/Inelastic/Direct/PropertiesDescriptors.py
@@ -13,12 +13,14 @@ from __future__ import (absolute_import, division, print_function)
 import os
 import numpy as np
 import math
+import re
 from collections import Iterable
 
 import mantid.simpleapi as mantid
 from mantid import api
 
 import Direct.ReductionHelpers as prop_helpers
+from Direct.AbsorptionShapes import *
 import collections
 
 
@@ -328,15 +330,16 @@ class IncidentEnergy(PropDescriptor):
         self._autoEiCalculated = False
         if ei_mon_spec is None:
             ei_mon_spec = instance.ei_mon_spectra
-        guess_ei_ws = mantid.GetAllEi(Workspace=monitor_ws, Monitor1SpecID=ei_mon_spec[0],
-                                      Monitor2SpecID=ei_mon_spec[1])
+        guess_ei_ws = GetAllEi(
+            Workspace=monitor_ws, Monitor1SpecID=ei_mon_spec[0],
+            Monitor2SpecID=ei_mon_spec[1])
         guessEi = guess_ei_ws.readX(0)
         fin_ei = []
         for ei in guessEi:
             try:
-                ei_ref, _, _, _ = mantid.GetEi(InputWorkspace=monitor_ws,
-                                               Monitor1Spec=ei_mon_spec[0], Monitor2Spec=ei_mon_spec[1],
-                                               EnergyEstimate=ei)
+                ei_ref, _, _, _ = GetEi(InputWorkspace=monitor_ws,
+                                        Monitor1Spec=ei_mon_spec[0], Monitor2Spec=ei_mon_spec[1],
+                                        EnergyEstimate=ei)
                 fin_ei.append(ei_ref)
             # pylint: disable=bare-except
             except:
@@ -350,7 +353,7 @@ class IncidentEnergy(PropDescriptor):
         self._num_energies = len(fin_ei)
         self._cur_iter_en = 0
         # Clear dataservice from unnecessary workspace
-        mantid.DeleteWorkspace(guess_ei_ws)
+        DeleteWorkspace(guess_ei_ws)
 
     def validate(self, instance, owner=None):
         #
@@ -368,8 +371,6 @@ class IncidentEnergy(PropDescriptor):
                 return (False, 2, "Incident energy have to be positive number or list of positive numbers.\n"
                                   "Got single negative incident energy {0} ".format(inc_en))
         return (True, 0, '')
-
-
 # end IncidentEnergy
 # -----------------------------------------------------------------------------------------
 
@@ -480,10 +481,7 @@ class EnergyBins(PropDescriptor):
             if not owner.incident_energy.autoEi_mode() and ebin[2] > ei:
                 return (False, 2, 'Max rebin range {0:f} exceeds incident energy {1:f}'.format(ebin[2], ei))
         return (True, 0, '')
-
-
 # -----------------------------------------------------------------------------------------
-
 # end EnergyBins
 # -----------------------------------------------------------------------------------------
 
@@ -548,8 +546,6 @@ class SaveFileName(PropDescriptor):
 
     def set_custom_print(self, routine):
         self._custom_print = routine
-
-
 # end SaveFileName
 # -----------------------------------------------------------------------------------------
 
@@ -574,8 +570,6 @@ class InstrumentDependentProp(PropDescriptor):
 
     def __set__(self, instance, values):
         raise AttributeError("Property {0} can not be assigned. It defined by instrument".format(self._prop_name))
-
-
 # end InstrumentDependentProp
 # -----------------------------------------------------------------------------------------
 
@@ -590,8 +584,6 @@ class VanadiumRMM(PropDescriptor):
 
     def __set__(self, instance, value):
         raise AttributeError("Can not change vanadium rmm")
-
-
 # end VanadiumRMM
 # -----------------------------------------------------------------------------------------
 # END Descriptors for NonIDF_Properties class
@@ -695,8 +687,6 @@ class mon2NormalizationEnergyRange(PropDescriptor):
                 return (False, 2, message)
 
         return result
-
-
 # -----------------------------------------------------------------------------------------
 
 
@@ -838,8 +828,6 @@ class DetCalFile(PropDescriptor):
             pass
         self._det_cal_file = file_name
         return (True, file_name)
-
-
 # end DetCalFile
 # -----------------------------------------------------------------------------------------
 
@@ -884,8 +872,6 @@ class MapMaskFile(PropDescriptor):
         else:
             self._file_name = file_name
             return (True, file_name)
-
-
 # end MapMaskFile
 
 # -----------------------------------------------------------------------------------------
@@ -980,8 +966,6 @@ class HardMaskOnly(prop_helpers.ComplexProperty):
         # pylint: disable=bare-except
         except:
             pass
-
-
 # end HardMaskOnly
 # -----------------------------------------------------------------------------------------
 
@@ -1079,8 +1063,6 @@ class MonovanIntegrationRange(prop_helpers.ComplexProperty):
             return False, 1, 'monovan integration is suspiciously wide: [{0}:{1}]. ' \
                              'This may be incorrect'.format(the_range[0], the_range[1])
         return True, 0, ''
-
-
 # end MonovanIntegrationRange
 
 
@@ -1238,8 +1220,6 @@ class SpectraToMonitorsList(PropDescriptor):
             else:
                 result = [int(spectra_list)]
         return result
-
-
 # end SpectraToMonitorsList
 
 # -----------------------------------------------------------------------------------------
@@ -1308,8 +1288,6 @@ class SaveFormat(PropDescriptor):
             return (False, 1, 'No internal save format is defined. Results may be lost')
         else:
             return (True, 0, '')
-
-
 # end SaveFormat
 
 # -----------------------------------------------------------------------------------------
@@ -1358,8 +1336,6 @@ class DiagSpectra(PropDescriptor):
             return bank_spectra
         else:
             raise ValueError("Spectra For diagnostics can be a string inthe form (num1,num2);(num3,num4) etc. or None")
-
-
 # end class DiagSpectra
 
 # -----------------------------------------------------------------------------------------
@@ -1407,8 +1383,6 @@ class BackbgroundTestRange(PropDescriptor):
             return False, 1, ' Background test range is TOF range, its max value looks suspiciously big={0}'.format(
                    test_range[1])
         return True, 0, ''
-
-
 # end BackbgroundTestRange
 
 # -----------------------------------------------------------------------------------------
@@ -1446,8 +1420,6 @@ class MultirepTOFSpectraList(PropDescriptor):
         else:
             rez = [int(value)]
         self._spectra_list = rez
-
-
 # end MultirepTOFSpectraList
 
 
@@ -1529,8 +1501,6 @@ class MonoCorrectionFactor(PropDescriptor):
         if self._cor_factor <= 0:
             return (False, 2, 'Mono-correction factor has to be positive if specified: {0}'.format(self._cor_factor))
         return (True, 0, '')
-
-
 # end MonoCorrectionFactor
 
 
@@ -1561,8 +1531,6 @@ class MotorLogName(PropDescriptor):
         else:
             val_list = [str(value)]
         self._log_names = val_list
-
-
 # end MotorLogName
 
 
@@ -1587,8 +1555,6 @@ class MotorOffset(PropDescriptor):
             self._offset = None
         else:
             self._offset = float(value)
-
-
 # end MotorOffset
 
 
@@ -1680,10 +1646,177 @@ class RotationAngle(PropDescriptor):
 
     def dependencies(self):
         return ['motor_log_names', 'motor_offset']
-
 # end RotationAngle
 
 
+class AbsCorrInfo(PropDescriptor):
+    """ Class responsible for providing additional values for
+        absorption corrections algorithms.
+
+        The values should contain of the algorithm selector currently
+        is_fast:True for AbsorptionCorrection algorithms family
+        or
+        is_mc:True for MonteCarloAbsorption correction algorithms family
+
+        accompanied by the dictionary of all non-sample related properties
+        accepted by these algorithms.
+
+        If this key is missing, the class assumes that MonteCarloAbsorption
+        algorithm (is_mc:True) is selected.
+
+        The value for the property can be provided as dictionary or as
+        string representation of this dictionary.
+        The whole contents of the previous dictionary is removed
+        on assignment of the new dictionary.
+    """
+    # The set of properties acceptable by MonteCarloAbsorption algorithm:
+    _MC_corrections_accepts = {
+        'NumberOfWavelengthPoints':lambda x: int(x),
+        'EventsPerPoint':lambda x : int(x),
+        'SeedValue':lambda x : int(x),
+        'Interpolation':lambda x : list_checker(x,('Linear','CSpline'),'MonteCarloAbsorptions "Interpolation"'),
+        'SparseInstrument':lambda x : bool(x),'NumberOfDetectorRows':lambda x: int(x),
+        'NumberOfDetectorColumns':lambda x: int(x),'MaxScatterPtAttempts':lambda x: int(x)}
+    # The set of properties acceptable by AbsorptionCorrection algorithm:
+    _Fast_corrections_accepts = {
+        'ScatterFrom':lambda x : list_checker(x,('Sample', 'Container', 'Environment'),'AbsorptionCorrections "ScatterFrom"'),
+        'NumberOfWavelengthPoints':lambda x: float(x),
+        'ExpMethod':lambda x : list_checker(x,('Normal', 'FastApprox'),'AbsorptionCorrections "ExpMethod"'),
+        'EMode':lambda x: list_checker(x,('Direct', 'Indirect', 'Elastic'),'AbsorptionCorrections "EMode"'),
+        'EFixed':lambda x: float(x),'ElementSize':lambda x: float(x)}
+
+    def __init__(self):
+        self._alg_prop = {}
+        # Use Monte-Carlo corrections by default
+        self._is_fast  = False
+
+    def __get__(self, instance, owner=None):
+        if instance is None:
+            return self
+        alg_prop = self._alg_prop
+        if self._is_fast:
+            alg_prop['is_fast'] = True
+        else:
+            alg_prop['is_mc'] = True
+        return alg_prop
+
+    def __set__(self, instance, value):
+        if value is None or len(value) == 0:
+            # Clear all previous values and set up the defaults
+            self._alg_prop = {'is_mc':True}
+            self._is_fast  = False
+            return
+        val_dict =  {}
+        if isinstance(value, str):
+            val = re.sub('[{}\[\]"=:;,\']', ' ', value)
+            val_list = re.split('\s+',val)
+            ik = 0
+            while ik < len(val_list):
+                key = val_list[ik]
+                if len(key)==0:
+                    ik+=1
+                    continue
+                val = val_list[ik+1]
+                if key in ('is_fast','is_mc'):
+                    self._algo_selector(key,val)
+                    ik +=2
+                    continue
+                val_dict[key] = val
+                ik +=2
+        elif isinstance(value, dict):
+            val_dict = value
+            is_fast=val_dict.pop('is_fast',None)
+            if is_fast is not None:
+                self._algo_selector('is_fast',is_fast)
+            is_mc = val_dict.pop('is_mc',None)
+            if is_mc is not None:
+                self._algo_selector('is_mc',is_mc)
+
+        else:
+            raise(KeyError(
+                'AbsCorrInfo accepts only a dictionary '  # noqa
+                'with AbsorptionCorrections algorithm properties '  # noqa
+                'or string representation of such dictionary'))  # noqa
+
+        if self._is_fast:
+            algo_name = 'AdsorptionCorrection'
+            acceptable_prop = AbsCorrInfo._Fast_corrections_accepts
+        else:
+            algo_name = 'MonteCarloAbsorption'
+            acceptable_prop = AbsCorrInfo._MC_corrections_accepts
+
+        for key,val in val_dict.items():
+            check_normalizer = acceptable_prop.get(key,None)
+            if check_normalizer is None:
+                raise KeyError('The key {0} is not acceptable key for {1} algorithm'.format(key,algo_name))
+            val_dict[key] = check_normalizer(val)
+        # Store new dictionary in the property
+        self._alg_prop = val_dict
+
+    def __str__(self):
+        alg_prop = self.__get__(self,AbsCorrInfo)
+        return str(alg_prop)
+
+    def _algo_selector(self,algo_key,key_val):
+        if algo_key == 'is_fast':
+            self._is_fast = bool(key_val)
+        elif algo_key == 'is_mc':
+            self._is_fast = not bool(key_val)
+
+
+class AbsorptionShapesContainer(PropDescriptor):
+    """ Class to keep AbsorptionShape classes,
+        responsible for making absorption corrections
+        for the absorption on correspondent shapes.
+
+        NOT IMPLEMENTED: should also handle conversion
+        from a shape to its text representaton and v.v.
+        (using AbsorptionShape appropriate classes)
+
+    """
+    def __init__(self):
+        self._theShapeHolder = None
+
+    #
+    def __get__(self, instance, owner=None):
+        if instance is None:
+            return self
+
+        return self._theShapeHolder
+
+    def __set__(self, instance, value):
+        if value is None:
+            self._theShapeHolder = None
+            return
+        if isinstance(value,str):
+            self._theShapeHolder = anAbsorptionShape.from_str(value)
+        elif isinstance(value,anAbsorptionShape):
+            self._theShapeHolder = value
+        else:
+            raise ValueError('The property accepts only anAbsorptionShape type classes or their string representation')
+
+    def __str__(self):
+        if self._theShapeHolder is None:
+            return 'None'
+        else:
+            return self._theShapeHolder.str()
+
+
+def list_checker(val,list_in,mess_base):
+    """ Helper function to check the value val (first input) belongs to the
+        set, specified as the second input.
+
+        If this is not true, raise ValueError indicating what property is wrong.
+        To do indication, mess_base should contain the string, referring to
+        the invalid property.
+    """
+    if val in list_in:
+        return val
+    else:
+        raise ValueError(
+            '{0} property can only have values from the set of: {1}'  # noqa
+            .format(mess_base,str(list_in)))  # noqa
+
 # -----------------------------------------------------------------------------------------
 # END Descriptors for PropertyManager itself
 # -----------------------------------------------------------------------------------------
diff --git a/scripts/Inelastic/Direct/PropertyManager.py b/scripts/Inelastic/Direct/PropertyManager.py
index 1be8e2931801bed4d5b7cf12d82f770337fbdea3..58e7b988000a1d952e249aef68202f20cad096e5 100644
--- a/scripts/Inelastic/Direct/PropertyManager.py
+++ b/scripts/Inelastic/Direct/PropertyManager.py
@@ -255,6 +255,13 @@ class PropertyManager(NonIDF_Properties):
     motor_log_names= MotorLogName()
     motor_offset   = MotorOffset()
     psi = RotationAngle(motor_log_names,motor_offset)
+    # Properties responsible for the Absorption Corrections
+    # The property, containing information about additional properties of selected
+    # adsorption corrections algorithm
+    abs_corr_info = AbsCorrInfo()
+    # The property, containing the adsorption shapes class which would
+    # perform actual adsorption corrections on the selected shape
+    correct_absorption_on = AbsorptionShapesContainer()
 #----------------------------------------------------------------------------------------------------------------
 
     def getChangedProperties(self):
diff --git a/scripts/Inelastic/Direct/ReductionHelpers.py b/scripts/Inelastic/Direct/ReductionHelpers.py
index ed73fe02e5340569c6eb02b46ad8a445d7ac0e54..5dd9017d74c6a12a851ef04661352b045c11a721 100644
--- a/scripts/Inelastic/Direct/ReductionHelpers.py
+++ b/scripts/Inelastic/Direct/ReductionHelpers.py
@@ -10,6 +10,7 @@ from mantid import config
 import os
 import re
 from six.moves import range
+
 """
 Set of functions to assist with processing instrument parameters relevant to reduction.
 """
@@ -32,7 +33,8 @@ class ComplexProperty(object):
         rez = list()
         for key in self._other_prop:
             rez.append(spec_dict[key])
-        # process very important case of property dependent on two other properties. Make it tuple
+        # process very important case of property dependent on two other
+        # properties.  Make it tuple
         if len(rez) == 2:
             return (rez[0],rez[1])
         else:
@@ -48,13 +50,13 @@ class ComplexProperty(object):
             raise KeyError("Complex property values can be set equal to the same length values list")
 
         if isinstance(instance,dict):
-            spec_dict  = instance
+            spec_dict = instance
         else:
             spec_dict = instance.__dict__
 
         #changed_prop=[];
         for key,val in zip(self._other_prop,value):
-            spec_dict[key] =val
+            spec_dict[key] = val
                 #changed_prop.append(key);
         #return changed_prop;
 
@@ -119,7 +121,7 @@ def get_default_idf_param_list(pInstrument,synonims_list=None):
     for name in params:
         if synonims_list:
             if name in synonims_list:
-                key_name=synonims_list[name]
+                key_name = synonims_list[name]
             else:
                 key_name = name
         else:
@@ -149,7 +151,7 @@ def build_properties_dict(param_map,synonims,descr_list=[]) :
             final_name = str(synonims[name])
         else:
             final_name = str(name)
-        prelim_dict[final_name]=None
+        prelim_dict[final_name] = None
 
     param_keys = list(prelim_dict.keys())
     properties_dict = dict()
@@ -169,8 +171,8 @@ def build_properties_dict(param_map,synonims,descr_list=[]) :
             keys_candidates = val.split(":")
             n_keys = len(keys_candidates)
                #
-            if n_keys>1 : # this is the property we want to modify
-                result=list()
+            if n_keys > 1 : # this is the property we want to modify
+                result = list()
                 for key in keys_candidates :
                     if key in synonims:
                         rkey = str(synonims[key])
@@ -183,17 +185,17 @@ def build_properties_dict(param_map,synonims,descr_list=[]) :
                 if is_descriptor:
                     descr_dict[final_name] = result
                 else:
-                    properties_dict['_'+final_name]=ComplexProperty(result)
+                    properties_dict['_' + final_name] = ComplexProperty(result)
             else:
                 if is_descriptor:
                     descr_dict[final_name] = keys_candidates[0]
                 else:
-                    properties_dict[final_name] =keys_candidates[0]
+                    properties_dict[final_name] = keys_candidates[0]
         else:
             if is_descriptor:
                 descr_dict[final_name] = val
             else:
-                properties_dict[final_name]=val
+                properties_dict[final_name] = val
 
     return (properties_dict,descr_dict)
 
@@ -226,12 +228,13 @@ def build_subst_dictionary(synonims_list=None) :
         return synonims_list
     if not isinstance(synonims_list, str) :
         raise AttributeError("The synonyms field of Reducer object has to be special format string or the dictionary")
-        # we are in the right place and going to transform string into dictionary
+        # we are in the right place and going to transform string into
+        # dictionary
 
     subst_lines = synonims_list.split(";")
-    rez  = dict()
+    rez = dict()
     for lin in subst_lines :
-        lin=lin.strip()
+        lin = lin.strip()
         keys = lin.split("=")
         if len(keys) < 2 :
             raise AttributeError("The pairs in the synonyms fields have to have form key1=key2=key3 with at least two values present")
@@ -241,9 +244,9 @@ def build_subst_dictionary(synonims_list=None) :
         for i in range(1,len(keys)) :
             if len(keys[i]) == 0 :
                 raise AttributeError("The pairs in the synonyms fields have to have form key1=key2=key3 with at least two values present, "
-                                     "but the key"+str(i)+" is empty")
+                                     "but the key" + str(i) + " is empty")
             kkk = keys[i].strip()
-            rez[kkk]=keys[0].strip()
+            rez[kkk] = keys[0].strip()
 
     return rez
 
@@ -256,13 +259,13 @@ def gen_getter(keyval_dict,key):
         and gen_getter(keyval_dict,C) == [10,20];
     """
     if key not in keyval_dict:
-        name = '_'+key
+        name = '_' + key
         if name not in keyval_dict:
             raise KeyError('Property with name: {0} is not among the class properties '.format(key))
     else:
         name = key
 
-    a_val= keyval_dict[name]
+    a_val = keyval_dict[name]
     if isinstance(a_val,ComplexProperty):
         return a_val.__get__(keyval_dict)
     else:
@@ -282,7 +285,7 @@ def gen_setter(keyval_dict,key,val):
     """
 
     if key not in keyval_dict:
-        name = '_'+key
+        name = '_' + key
         if name not in keyval_dict:
             raise KeyError(' Property name: {0} is not defined'.format(key))
     else:
@@ -311,8 +314,8 @@ def check_instrument_name(old_name,new_name):
         return (None,None,None)
 
     # Instrument name might be a prefix, query Mantid for the full name
-    short_name=''
-    full_name=''
+    short_name = ''
+    full_name = ''
     try :
         instrument = config.getFacility().instrument(new_name)
         short_name = instrument.shortName()
@@ -327,13 +330,13 @@ def check_instrument_name(old_name,new_name):
                 instrument = facility.instrument(new_name)
                 short_name = instrument.shortName()
                 full_name = instrument.name()
-                if len(short_name)>0 :
+                if len(short_name) > 0 :
                     break
             except:
                 pass
         #config.setString('default.facility',old_facility)
-        if len(short_name)==0 :
-            raise KeyError(" Can not find/set-up the instrument: "+new_name+' in any supported facility')
+        if len(short_name) == 0 :
+            raise KeyError(" Can not find/set-up the instrument: " + new_name + ' in any supported facility')
 
     new_name = short_name
 
@@ -345,25 +348,25 @@ def parse_single_name(filename):
     """ Process single run name into """
     filepath,fname = os.path.split(filename)
     if ':' in fname:
-        fl,fr=fname.split(':')
-        path1,ind1,ext1=parse_single_name(fl)
-        path2,ind2,ext2=parse_single_name(fr)
-        if ind1>ind2:
+        fl,fr = fname.split(':')
+        path1,ind1,ext1 = parse_single_name(fl)
+        path2,ind2,ext2 = parse_single_name(fr)
+        if ind1 > ind2:
             raise ValueError('Invalid file number defined using colon : left run number '
                              '{0} has to be large then right {1}'.format(ind1,ind2))
-        number = list(range(ind1[0],ind2[0]+1))
-        if len(filepath)>0:
-            filepath=[filepath]*len(number)
+        number = list(range(ind1[0],ind2[0] + 1))
+        if len(filepath) > 0:
+            filepath = [filepath] * len(number)
         else:
-            filepath=path1*len(number)
-        if len(ext2[0])>0:
-            fext = ext2*len(number)
+            filepath = path1 * len(number)
+        if len(ext2[0]) > 0:
+            fext = ext2 * len(number)
         else:
-            fext = ext1*len(number)
+            fext = ext1 * len(number)
 
         return (filepath,number,fext)
 
-    fname,fext  = os.path.splitext(fname)
+    fname,fext = os.path.splitext(fname)
     fnumber = re.findall('\d+', fname)
     if len(fnumber) == 0:
         number = 0
@@ -379,17 +382,17 @@ def parse_run_file_name(run_string):
 
     runs = run_string.split(',')
     filepath = []
-    filenum  = []
-    fext     = []
-    anExt    = ''
+    filenum = []
+    fext = []
+    anExt = ''
     for run in runs:
         path,ind,ext1 = parse_single_name(run)
         filepath+=path
         filenum+=ind
         fext+=ext1
 
-    non_empty = [x for x in fext if len(x) >0]
-    if len(non_empty)>0:
+    non_empty = [x for x in fext if len(x) > 0]
+    if len(non_empty) > 0:
         anExt = non_empty[-1]
         for i,val in enumerate(fext):
             if len(val) == 0:
@@ -397,8 +400,8 @@ def parse_run_file_name(run_string):
 
     if len(filenum) == 1:
         filepath = filepath[0]
-        filenum  = filenum[0]
-        fext     = fext[0]
+        filenum = filenum[0]
+        fext = fext[0]
     # extensions should be either all the same or all defined
     return (filepath,filenum,fext)
 #
diff --git a/scripts/Inelastic/Direct/ReductionWrapper.py b/scripts/Inelastic/Direct/ReductionWrapper.py
index bbd8923831f8e2909cb10bf54d9eff289c321f84..2a8ea37df779c22f709387636914ffa305299379 100644
--- a/scripts/Inelastic/Direct/ReductionWrapper.py
+++ b/scripts/Inelastic/Direct/ReductionWrapper.py
@@ -13,10 +13,12 @@ from mantid.kernel import funcinspect
 from Direct.PropertyManager import PropertyManager
 # this import is used by children
 from Direct.DirectEnergyConversion import DirectEnergyConversion
+from types import MethodType # noqa
 import os
 import re
 import time
 from six import iteritems
+
 try:
     import h5py
     h5py_installed = True
@@ -248,6 +250,46 @@ class ReductionWrapper(object):
           User expected to overload this method within class instantiation """
         return None
 
+    def evaluate_abs_corrections(self,test_ws,spectra_to_correct):
+        """ Evaluate absorption corrections from the input workspace
+            Input:
+            test_ws -- the workspace to calculate corrections for.
+                       The corrections themselves should be defined by
+                       the following data reduction properties:
+                       propmen.correct_absorption_on = TheShapeOfTheSample -- define sample parameters
+                       propmen.abs_corr_info   = {} Dictionary with additional correction parameters
+                       (can be empty)
+             spectra_to_correct -- list of the spectra to correct absorption for.
+             If this list is empty, the corrections are calculated for the whole workspace,
+             which can cause problems for plotting.
+
+             Returns:
+             corrections -- the workspace containing the absorption corrections
+             for the spectra, specified in spectra_to_correct variable.
+        """
+
+        n_spectra = test_ws.getNumberHistograms()
+        decrement = len(spectra_to_correct)
+        if decrement>0:
+            red_ws = ExtractSpectra(test_ws,WorkspaceIndexList=spectra_to_correct)
+        else:
+            decrement = n_spectra
+
+        prop_man = self.reducer.prop_man
+        abs_shape = prop_man.correct_absorption_on
+        start_time = time.time()
+        ws,corrections = abs_shape.correct_absorption(red_ws,prop_man.abs_corr_info)
+        end_time = time.time()
+        estimated_time = (end_time-start_time)*n_spectra/decrement
+        prop_man.log(
+            "**************************************************************************************************",'notice')
+        prop_man.log(
+            "*** Estimated time to run absorption corrections on the final workspace is: {0:.1f}sec".
+            format(estimated_time),'notice')
+        prop_man.log(
+            "**************************************************************************************************",'notice')
+        return (corrections,estimated_time)
+
 #pylint: disable=too-many-branches
     def build_or_validate_result(self,Error=1.e-6,ToleranceRelErr=True):
         """ Method validates results of the reduction against reference file or workspace.
@@ -675,8 +717,8 @@ def iliad(reduce):
         #seq = inspect.stack()
         # output workspace name.
         try:
-            _,r = funcinspect.lhs_info('both')
-            out_ws_name = r[0]
+            name = funcinspect.lhs_info('names')
+            out_ws_name = name[0]
 # no-exception-type(s) specified. Who knows what exception this internal procedure rises...
 #pylint: disable=W0702
         except:
@@ -727,14 +769,27 @@ def iliad(reduce):
             if isinstance(rez, list):
               # multirep run, just return as it is
                 return rez
-            if not(rez is None) and out_ws_name and rez.name() != out_ws_name:
+            if rez is not None and out_ws_name and rez.name() != out_ws_name:
             # the function does not return None, pylint is wrong
             #pylint: disable=W1111
-                rez = RenameWorkspace(InputWorkspace=rez, OutputWorkspace=out_ws_name)
-
+                rez = PropertyManager.sample_run.synchronize_ws(rez,out_ws_name)
         return rez
 
     return iliad_wrapper
+#
+
+
+def custom_operation(custom_fun):
+    DirectEnergyConversion.__setattr__()
+
+    def custom_fun_wrapper(*args):
+            # execute decorated function
+            ws = custom_fun(*args)
+            #print "in decorator: ",properties
+            #host = args[0]
+            return ws
+
+    return custom_fun_wrapper
 
 
 if __name__ == "__main__":
diff --git a/scripts/Inelastic/Direct/RunDescriptor.py b/scripts/Inelastic/Direct/RunDescriptor.py
index ca71ccb4f4116a3b2272e108f64314469c63125a..1f7f76e14db687fa4c47eb7fdb965a528b899db5 100644
--- a/scripts/Inelastic/Direct/RunDescriptor.py
+++ b/scripts/Inelastic/Direct/RunDescriptor.py
@@ -742,7 +742,7 @@ class RunDescriptor(PropDescriptor):
         return self._build_ws_name()
 #--------------------------------------------------------------------------------------------------------------------
 
-    def synchronize_ws(self,workspace=None):
+    def synchronize_ws(self,workspace=None,new_ws_name = None):
         """Synchronize workspace name (after workspace may have changed due to algorithm)
            with internal run holder name. Accounts for the situation when
 
@@ -753,8 +753,10 @@ class RunDescriptor(PropDescriptor):
         """
         if not workspace:
             workspace = mtd[self._ws_name]
-
-        new_name = self._build_ws_name()
+        if new_ws_name:
+            new_name = new_ws_name
+        else:
+            new_name = self._build_ws_name()
         old_name = workspace.name()
         if new_name != old_name:
             # Compatibility with old test code comes here:
@@ -767,6 +769,8 @@ class RunDescriptor(PropDescriptor):
                     workspace.setMonitorWorkspace(mon_ws)
             RenameWorkspace(InputWorkspace=old_name,OutputWorkspace=new_name,RenameMonitors=True)
         self._ws_name = new_name
+
+        return mtd[new_name]
 #--------------------------------------------------------------------------------------------------------------------
 
     @staticmethod
diff --git a/scripts/test/AbsorptionShapesTest.py b/scripts/test/AbsorptionShapesTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..67d6370bb0d217a7422ba944c9925ad8e9052480
--- /dev/null
+++ b/scripts/test/AbsorptionShapesTest.py
@@ -0,0 +1,245 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import (absolute_import, division, print_function)
+import os
+from mantid.simpleapi import *
+from mantid import api
+import unittest
+import numpy as np
+from Direct.AbsorptionShapes import (anAbsorptionShape,Cylinder,FlatPlate,HollowCylinder,Sphere)
+
+class AdsorbtionShapesTest(unittest.TestCase):
+    def __init__(self, methodName):
+        return super(AdsorbtionShapesTest, self).__init__(methodName)
+
+    def test_an_Absrpn_shape_parent(self):
+        ash = anAbsorptionShape(['V']);
+        res = ash.material;
+        self.assertEqual(res['ChemicalFormula'],'V')
+
+        ash.material = 'Cr'
+        res = ash.material;
+        self.assertEqual(res['ChemicalFormula'],'Cr')
+
+
+        ash.material = ['Br',10]
+        res = ash.material;
+        self.assertEqual(res['ChemicalFormula'],'Br')
+        self.assertEqual(res['SampleNumberDensity'],10)
+
+
+        ash.material = {'ChemicalFormula':'Al','SampleNumberDensity':0.5}
+        res = ash.material;
+        self.assertEqual(res['ChemicalFormula'],'Al')
+        self.assertEqual(res['SampleNumberDensity'],0.5)
+
+        self.assertRaises(TypeError,anAbsorptionShape.material.__set__,ash,[1,2,3])
+        self.assertRaises(TypeError,anAbsorptionShape.material.__set__,ash,[1,2])
+
+        ash = anAbsorptionShape({'AtomicNumber':12,'AttenuationXSection':0.5,'SampleMassDensity':120}) 
+        res = ash.material;
+        self.assertEqual(res['AtomicNumber'],12)
+        self.assertEqual(res['AttenuationXSection'],0.5)
+        self.assertEqual(res['SampleMassDensity'],120)
+
+        # Add extra material property, consistent with other properties.
+        ash.material = {'ScatteringXSection':20}
+        res = ash.material;
+        self.assertEqual(res['AttenuationXSection'],0.5)
+        self.assertEqual(res['ScatteringXSection'],20)
+        self.assertEqual(len(res),4)
+
+    def test_adsrp_cylinder(self):
+        ash = Cylinder('V',[10,2])
+        res = ash.shape
+        self.assertEqual(res['Height'],10)
+        self.assertEqual(res['Radius'],2)
+        self.assertTrue(ash._axis_is_default)
+
+        ash.shape = [5,1,[0,1,0],[0.,0.,-0.5]]
+        res = ash.shape
+        self.assertEqual(res['Height'],5)
+        self.assertEqual(res['Radius'],1)
+        self.assertEqual(res['Axis'],[0,1,0])
+        self.assertEqual(res['Center'],[0,0,-0.5])
+        self.assertFalse(ash._axis_is_default)
+
+
+        ash.shape = {'Height':5,'Radius':2,'Axis':[1,0,0],'Center':[0.,0.,0.]}
+        res = ash.shape;
+        self.assertEqual(res['Height'],5)
+        self.assertEqual(res['Radius'],2)
+        self.assertEqual(res['Axis'],[1,0,0])
+        self.assertEqual(res['Center'],[0,0,0])
+
+        test_ws = CreateSampleWorkspace(NumBanks=1,BankPixelWidth=1)
+        test_ws = ConvertUnits(test_ws,'DeltaE',Emode='Direct',EFixed=2000)
+        cor_ws,corrections = ash.correct_absorption(test_ws,is_fast=True)
+        n_bins = corrections.blocksize()
+        corr_ranges = [n_bins,corrections.readY(0)[0],corrections.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(corr_ranges,[97,0.0,0.0],4)
+        mccor_ws,mc_corr = ash.correct_absorption(test_ws,NumberOfWavelengthPoints=20)
+        n_bins = mc_corr.blocksize()
+        mccorr_ranges = [n_bins,mc_corr.readY(0)[0],mc_corr.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(mccorr_ranges ,[97,0.2657,0.0271],4)
+
+    def test_MARI_axis_cylinder(self):
+        """ Test that default axis for MARI is different"""
+        ash = Cylinder('Fe',[10,2])
+        res = ash.shape
+        self.assertEqual(res['Height'],10)
+        self.assertEqual(res['Radius'],2)
+        self.assertTrue(ash._axis_is_default)
+        test_ws = CreateSampleWorkspace(NumBanks=1,BankPixelWidth=1)
+        test_ws = ConvertUnits(test_ws,'DeltaE',Emode='Direct',EFixed=2000)
+        cor_ws,corrections = ash.correct_absorption(test_ws,is_fast=True)
+        res = ash.shape
+        self.assertEqual(res['Axis'],[0.,1.,0])
+
+        EditInstrumentGeometry (test_ws,[1],[0],InstrumentName='MARI')
+        cor_ws,corrections = ash.correct_absorption(test_ws,is_fast=True)
+        res = ash.shape
+        self.assertEqual(res['Axis'],[1.,0.,0])
+
+
+    def test_adsrp_Plate(self):
+        ash = FlatPlate('V',[10,2,0.1])
+        res = ash.shape
+        self.assertEqual(res['Height'],10)
+        self.assertEqual(res['Width'],2)
+        self.assertEqual(res['Thick'],0.1)
+
+        ash.shape = [5,1,0.2,[0,1,0],10]
+        res = ash.shape;
+        self.assertEqual(res['Height'],5)
+        self.assertEqual(res['Width'],1)
+        self.assertEqual(res['Thick'],0.2)
+        self.assertEqual(res['Center'],[0,1,0])
+        self.assertEqual(res['Angle'],10)
+
+        ash.shape = {'Height':5,'Width':1,'Thick':2,'Center':[0.,0.,0.],'Angle':20}
+        res = ash.shape;
+        self.assertEqual(res['Height'],5)
+        self.assertEqual(res['Width'],1)
+        self.assertEqual(res['Thick'],2)
+        self.assertEqual(res['Center'],[0,0,0])
+        self.assertEqual(res['Angle'],20)
+
+        test_ws = CreateSampleWorkspace(NumBanks=1,BankPixelWidth=1)
+        test_ws = ConvertUnits(test_ws,'DeltaE',Emode='Direct',EFixed=2000)
+
+        cor_ws,corrections = ash.correct_absorption(test_ws,is_fast=True,ElementSize=5)
+        n_bins = corrections.blocksize()
+        corr_ranges = [n_bins,corrections.readY(0)[0],corrections.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(corr_ranges,[97, 0., 0.],4)
+
+        mccor_ws,mc_corr = ash.correct_absorption(test_ws,is_mc=True,NumberOfWavelengthPoints=20)
+        n_bins = mc_corr.blocksize()
+        mccorr_ranges = [n_bins,mc_corr.readY(0)[0],mc_corr.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(mccorr_ranges ,[97,0.5253,0.1296],4)
+
+
+    def test_adsrp_hollow_cylinder(self):
+        ash = HollowCylinder('V',[10,2,4])
+        res = ash.shape
+        self.assertEqual(res['Height'],10)
+        self.assertEqual(res['InnerRadius'],2)
+        self.assertEqual(res['OuterRadius'],4)
+
+        ash.shape = [5,1,2,[1,0,0],[0,0,0]]
+        res = ash.shape;
+        self.assertEqual(res['Height'],5)
+        self.assertEqual(res['InnerRadius'],1)
+        self.assertEqual(res['OuterRadius'],2)
+        self.assertEqual(res['Axis'],[1,0,0])
+        self.assertEqual(res['Center'],[0,0,0])
+
+
+        ash.shape = {'Height':5,'InnerRadius':0.01,'OuterRadius':2,'Center':[0.,0.,0.],'Axis':[0,1,0]}
+        res = ash.shape;
+        self.assertEqual(res['Height'],5)
+        self.assertEqual(res['InnerRadius'],0.01)
+        self.assertEqual(res['OuterRadius'],2)
+        self.assertEqual(res['Axis'],[0,1,0])
+        self.assertEqual(res['Center'],[0,0,0])
+
+
+        test_ws =  CreateSampleWorkspace(NumBanks=1,BankPixelWidth=1)
+        test_ws = ConvertUnits(test_ws,'DeltaE',Emode='Direct',EFixed=2000)
+        cor_ws,corrections = ash.correct_absorption(test_ws,is_fast=True,ElementSize=5)
+        n_bins = corrections.blocksize()
+        corr_ranges = [n_bins,corrections.readY(0)[0],corrections.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(corr_ranges,[97,0.0,0.000],4)
+
+        mccor_ws,mc_corr = ash.correct_absorption(test_ws,is_mc=True,NumberOfWavelengthPoints=20)
+        n_bins = mc_corr.blocksize()
+        mccorr_ranges = [n_bins,mc_corr.readY(0)[0],mc_corr.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(mccorr_ranges ,[97,0.2657,0.0303],4)
+    #
+    def test_string_conversion(self):
+        """ check if shape conversion to string representation works"""
+        ash = HollowCylinder('V',[10,2,4])
+        ash_str = str(ash)
+        ash_rec = anAbsorptionShape.from_str(ash_str)
+
+        self.assertTrue(isinstance(ash_rec,HollowCylinder))
+        self.assertDictEqual(ash.material,ash_rec.material)
+        self.assertDictEqual(ash.shape,ash_rec.shape)
+
+        ash = Sphere(['Al',10],10)
+        ash_str = str(ash)
+        ash_rec = anAbsorptionShape.from_str(ash_str)
+
+        self.assertTrue(isinstance(ash_rec,Sphere))
+        self.assertDictEqual(ash.material,ash_rec.material)
+        self.assertDictEqual(ash.shape,ash_rec.shape)
+
+    #
+    def test_adsrp_sphere(self):
+        ash = Sphere('Al',10)
+        res = ash.shape
+        self.assertEqual(res['Radius'],10)
+
+        ash.shape = [5,[1,0,0]]
+        res = ash.shape;
+        rad = res['Radius']
+        self.assertEqual(rad,5)
+        cen = res['Center']
+        self.assertEqual(cen,[1,0,0])
+
+
+        ash.shape = {'Radius':3,'Center':[0.,0.,0.]}
+        res = ash.shape;
+        rad = res['Radius']
+        self.assertEqual(rad,3)
+        cen = res['Center']
+        self.assertEqual(cen,[0,0,0])
+
+
+        test_ws =  CreateSampleWorkspace(NumBanks=1,BankPixelWidth=1)
+        test_ws = ConvertUnits(test_ws,'DeltaE',Emode='Direct',EFixed=2000)
+        cor_ws,corrections = ash.correct_absorption(test_ws,is_fast=True,ElementSize=6)
+        n_bins = corrections.blocksize()
+        corr_ranges = [n_bins,corrections.readY(0)[0],corrections.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(corr_ranges,[97,0.0,0.0],4)
+
+        cor_ws,corrections = ash.correct_absorption(test_ws,is_fast=True)
+        n_bins = corrections.blocksize()
+        corr_ranges = [n_bins,corrections.readY(0)[0],corrections.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(corr_ranges,[97,0.0,0.0],4)
+ 
+        mccor_ws,mc_corr = ash.correct_absorption(test_ws,is_mc=True,NumberOfWavelengthPoints=20)
+        n_bins = mc_corr.blocksize()
+        mccorr_ranges = [n_bins,mc_corr.readY(0)[0],mc_corr.readY(0)[n_bins-1]]
+        np.testing.assert_almost_equal(mccorr_ranges ,[97,0.6645,0.5098],4)
+
+
+
+if __name__=="__main__":
+    #ast = AdsorbtionShapesTest('test_adsrp_cylinder')
+    #ast.test_adsrp_cylinder()
+    unittest.main()
diff --git a/scripts/test/CMakeLists.txt b/scripts/test/CMakeLists.txt
index 0e0ada5db78e398284794f0b5d3e3fa22e912276..3bc1e07fd377d880236a3b64e6fc8907ddc59a55 100644
--- a/scripts/test/CMakeLists.txt
+++ b/scripts/test/CMakeLists.txt
@@ -14,6 +14,7 @@ set(TEST_PY_FILES
     AbinsLoadDMOL3Test.py
     AbinsLoadGAUSSIANTest.py
     AbinsPowderDataTest.py
+    AbsorptionShapesTest.py 	
     ConvertToWavelengthTest.py
     CrystalFieldMultiSiteTest.py
     CrystalFieldTest.py
diff --git a/scripts/test/DirectPropertyManagerTest.py b/scripts/test/DirectPropertyManagerTest.py
index d7aea504a0a7d723d792fd5068d5d95fcd1538b2..7d39ac68ff5506c5ec7205eeb241a54f3018a6f6 100644
--- a/scripts/test/DirectPropertyManagerTest.py
+++ b/scripts/test/DirectPropertyManagerTest.py
@@ -14,6 +14,7 @@ import numpy as np
 import sys
 import copy
 from Direct.PropertyManager import PropertyManager
+from Direct.AbsorptionShapes import *
 
 
 #-----------------------------------------------------------------------------------------------------------------------------------------
@@ -440,7 +441,7 @@ class DirectPropertyManagerTest(unittest.TestCase):
 
         propman.log_changed_values()
 
-    def test_set_defailts_from_instrument(self) :
+    def test_set_defaults_from_instrument(self) :
         ws = CreateSampleWorkspace(NumBanks=1, BankPixelWidth=4, NumEvents=100)
 
         SetInstrumentParameter(ws,ParameterName="TestParam1",Value="3.5",ParameterType="Number")
@@ -478,7 +479,7 @@ class DirectPropertyManagerTest(unittest.TestCase):
         self.assertTrue('TestParam2' in changes)
         self.assertTrue('TestParam3' in changes)
 
-    def test_set_complex_defailts_from_instrument(self) :
+    def test_set_complex_defaults_from_instrument(self) :
         ws = CreateSampleWorkspace(NumBanks=1, BankPixelWidth=4, NumEvents=10)
 
         SetInstrumentParameter(ws,ParameterName="Param1",Value="BaseParam1:BaseParam2",ParameterType="String")
@@ -1228,8 +1229,81 @@ class DirectPropertyManagerTest(unittest.TestCase):
         for valExp,valReal in zip(exp_rez,rez):
             self.assertAlmostEqual(valExp,valReal)
 
+    def test_abs_corr_info(self):
+        propman = self.prop_man
+
+        defaults = propman.abs_corr_info
+        self.assertTrue(defaults['is_mc'])
+        # the algorithm sets up the properties but does not verifis if the prperties
+        # are acceptable by the corrections algorithm
+        propman.abs_corr_info = "{is_mc: True, NumberOfWavelengthPoints: 200; MaxScatterPtAttempts=20, SparseInstrument=True}"
+
+        propss = propman.abs_corr_info
+        self.assertTrue(propss['is_mc'])
+        self.assertEqual(propss['NumberOfWavelengthPoints'],200)
+        self.assertEqual(propss['MaxScatterPtAttempts'],20)
+        self.assertEqual(propss['SparseInstrument'],True)
+
+        # Properties acceptable by MoneCarloAbsorption algorithm
+        propman.abs_corr_info = {'is_mc':True,'NumberOfWavelengthPoints':20,'EventsPerPoint':'200','SeedValue':31090,\
+            'Interpolation':'CSpline','SparseInstrument':'True','NumberOfDetectorRows':20,'NumberOfDetectorColumns':'10',\
+            'MaxScatterPtAttempts':200}
+
+        propss = propman.abs_corr_info
+        self.assertTrue(propss['is_mc'])
+        self.assertTrue(propss['SparseInstrument'])
+        self.assertEqual(propss['Interpolation'],'CSpline')
+
+        str_val = str(propman.abs_corr_info)
+
+        propman.abs_corr_info = str_val
+
+        rec_prop = propman.abs_corr_info
+
+        self.assertDictEqual(propss,rec_prop)
+        # Properties acceptable by AbsorptionCorrection algorithm
+        ac_properties = {'ScatterFrom':'Sample','NumberOfWavelengthPoints':10,'ExpMethod':'FastApprox',\
+        'EMode':'Direct','EFixed':10,'ElementSize':2}
+        ac_properties['is_fast'] = True
+        propman.abs_corr_info = str(ac_properties)
+
+        rec_prop = propman.abs_corr_info
+        self.assertDictEqual(ac_properties,rec_prop)
+
+        propman.abs_corr_info = None
+        rec_prop = propman.abs_corr_info
+        self.assertDictEqual(rec_prop,{'is_mc':True})
+
+        self.assertRaises(KeyError,PropertyManager.abs_corr_info.__set__,propman.abs_corr_info,[1,2,3])
+        self.assertRaises(KeyError,PropertyManager.abs_corr_info.__set__,propman.abs_corr_info,{'TheKeyNotRecognizedByAlgorithm':10})
+    #
+    def test_abs_shapes_container(self):
+        propman = self.prop_man
+
+        cyl = Cylinder(['Al',0.1],[10,1])
+        propman.correct_absorption_on = cyl
+
+        got = propman.correct_absorption_on
+        self.assertEqual(got,cyl)
+
+        str_rep_cyl = str(propman.correct_absorption_on)
+        is_in = 'Shape' in str_rep_cyl
+        self.assertTrue(is_in)
+        is_in = 'Cylinder' in str_rep_cyl
+        self.assertTrue(is_in)
+
+        propman.correct_absorption_on = str_rep_cyl
+
+        got = propman.correct_absorption_on
+
+        self.assertTrue(isinstance(got,Cylinder))
+        self.assertEqual(got.material,cyl.material)
+        self.assertEqual(got.shape,cyl.shape)
+
+
+
 
 if __name__ == "__main__":
-    #tester = DirectPropertyManagerTest('test_average_accuracy')
+    #tester = DirectPropertyManagerTest('test_abs_shapes_container')
     #tester.run()
     unittest.main()
diff --git a/scripts/test/MariReduction.py b/scripts/test/MariReduction.py
index b46051330b1c675033121fab566a1a7b1282ae6c..72a8082ca46f60856266de9acd40f8ad3ea11d9e 100644
--- a/scripts/test/MariReduction.py
+++ b/scripts/test/MariReduction.py
@@ -6,12 +6,17 @@
 # SPDX - License - Identifier: GPL - 3.0 +
 """ Sample MARI reduction scrip used in testing ReductionWrapper """
 import os
+from Direct.AbsorptionShapes import *
 
 from Direct.ReductionWrapper import *
 try:
     import reduce_vars as web_var
 except:
     web_var = None
+try:
+    import mantidplot as mpl
+except:
+    mpl = None
 
 
 
@@ -46,38 +51,230 @@ class ReduceMARI(ReductionWrapper):
       prop['hard_mask_file'] = "mar11015.msk"
       prop['det_cal_file'] = 11060
       prop['save_format'] = ''
+      # Uncomment two following properties to correct for absorption
+      # on sample or sample container during the experiment.
+      # 1) Define the sample material and sample shape:
+      #
+      #prop['correct_absorption_on'] = Cylinder(['Fe'],{'Height':10,'Radius':2})
+      #
+      #  The shapes currently available are:
+      #  Cylinder, FlatPlate, HollowCylinder and Sphere
+      # Each class accepts two parameters, namely dictionary or list describing
+      # sample material, as accepted by SetSampleMaterial algorithm
+      # and Shape parameters as accepted by SetSample algorithm.
+
+      # Go to iPython window, type from AbsorptionShapes import * and type
+      # help(Cylinder),
+      # help(Sphere) or help(anAbsorptionShape) to learn more about different
+      # ways of setting the material and shape parameters:
+      ##----------------------------
+      # 2) Provide additional parameters defining speed and accuracy of the
+      #    absorption corrections algorithm in abs_corr_info dictionary.
+      #
+      #prop['abs_corr_info'] = {'is_mc':True}
+      #
+      #Two generic algorithms are currently available to
+      #    correct for absorption:
+      # a) MonteCarloAbsorption (invoked by 'is_mc':True key of abs_corr_info
+      #    property and by default)
+      # and
+      # b) AbsorptionCorrections (invoked by 'is_fast':True key of
+      #    abs_corr_info property). This algorithm has number of optimizations
+      #    for the special shapes cases and need further scientific validations
+
+      # All other properties, to be provided in the input dictionary of this
+      # property are the non-sample related properties of the
+      # correspondent correction algorithm.
+      #
+      # Test the speed and the accuracy of the selected algorithm
+      # reducing all your data by running eval_absorption_corrections method below.
+      ##-------------------------------
+
       return prop
       #
 #-------------------------------------------------------------------------------------------------#
     @iliad
     def reduce(self,input_file=None,output_directory=None):
       """ Method executes reduction over single file
-          Overload only if custom reduction is needed
+          Modify only if custom pre or post-processing is needed, namely:
       """
-      ws = ReductionWrapper.reduce(self,input_file,output_directory)
-      #SaveNexus(ws,Filename = 'MARNewReduction.nxs')
-      return ws
-#------------------------------------------------------------------------------------------------ #
-    def validate_result(self,build_validation=False):
-      """ Change this method to verify different results     """
-
-      # Two commented code rows below define location of the validation file in this script folder 
-      # (which is incorrect for Mantid development, as the files are on the data search path or
-      # mantid distribution, as the files are not there)
-      #run_dir = os.path.dirname(os.path.realpath(__file__))
-      #validation_file = os.path.join(run_dir,"MARIReduction.nxs")
-
-      # build_validation -- if true, build and overwrite new workspace rather then validating the old one
-      # if file is missing, the validation tries to build it
-      rez,message = ReductionWrapper.build_or_validate_result(self,11001,"MARIReduction.nxs",
-                                                              build_validation,1.e-3)
-      return rez,message
+
+      """
+        Define custom preprocessing procedure, to be applied to the whole
+        run, obtained from the instrument before diagnostics is applied
+      1) Get access to the pointer to the input workspace
+         (the workspace will be loaded  and calibrated at this point if it has not been done yet)
+          using sample_run property class:
+      run_ws = PropertyManager.sample_run.get_worksapce()
+      2) Get access to any property defined in Instrument_Properties.xml file
+         or redefined in the reduction script:
+      properties = self.reducer.prop_man
+        e.g:
+      RunNumber = properties.sample_run
+      ei = properties.incident_energy
+        Perform custom preprocessing procedure (with the values you specified)
+      preprocessed_ws = custom_preprocessing_procedure(run_ws,RunNumber,ei,...)
+      3) Store preprocessed workspace in the sample_run property for further analysis from preprocessing
+         for the case where the workspace name have changed in the Analysis Data Service
+        (this situation is difficult to predict so better always do this operation)
+      PropertyManager.sample_run.synchronize_ws(preprocessed_ws)
+      """
+
+      WS = ReductionWrapper.reduce(self,input_file,output_directory)
+
+      """ Defined custrom post-processing procedure, in the way, similar to
+      the preprocessing procedure, using the WS pointer, returned by the reduction procedure
+      above. If the run is reduced in multirep mode, the WS is the list of the reduced
+      workspace pointers, so the procedure should be applied to each workspace.
+
+      At this stage standard saving had already been done, so save your results if you need them
+      in a future:
+      for i in range(0:len(WS)):
+        SaveNexus(WS[i],Filename = 'CustomFilenameN%d.nxs'.format(i))
+      """
+      return WS
+      #
+
+    def do_preprocessing(self,reducer,ws):
+        """ Custom function, applied to each run or every workspace, the run is divided to
+            in multirep mode
+            Applied after diagnostics but before any further reduction is invoked.
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       to preprocess
+
+            By default, does nothing.
+            Add code to do custom preprocessing.
+            Must return pointer to the preprocessed workspace
+        """
+        return ws
+      #
+
+    def do_postprocessing(self,reducer,ws):
+        """ Custom function, applied to each reduced run or every reduced workspace,
+            the run is divided into, in multirep mode.
+            Applied after reduction is completed but before saving the result.
+
+            Inputs:
+            self    -- initialized instance of the instrument reduction class
+            reducer -- initialized instance of the reducer
+                       (DirectEnergyConversion class initialized for specific reduction)
+            ws         the workspace, describing the run or partial run in multirep mode
+                       after reduction to postprocess
+
+
+            By default, does nothing.
+            Add code to do custom postprocessing.
+            Must return pointer to the postprocessed workspace.
+
+            The postprocessed workspace should be consistent with selected save method.
+            (E.g. if you decide to convert workspace units to wavelength, you can not save result as nxspe)
+        """
+        return ws
+    #
+    def set_custom_output_filename(self):
+        """define custom name of output files if standard one is not satisfactory
+
+          In addition to that, example of accessing complex reduction properties
+          Simple reduction properties can be accessed as e.g.: value= prop_man.sum_runs
+        """
+        def custom_name(prop_man):
+            """Sample function which builds filename from
+              incident energy and run number and adds some auxiliary information
+              to it.
+            """
+            # Note -- properties have the same names as the list of advanced
+            # and   main properties
+            # Note: the properties are stored in prop_man class and accessed as
+            # below.
+            ei = PropertyManager.incident_energy.get_current()
+            # sample run is more then just list of runs, so we use
+            # the formalization below to access its methods
+            if self.reducer.prop_man.filename_prefix:
+                return reduced_filename(0, ei, False, self.reducer.prop_man.filename_prefix)
+            else:
+                runs_list = PropertyManager.sample_run.get_run_list()
+                return reduced_filename(runs_list, ei, self.reducer.prop_man.sum_runs)
+        # Uncomment this to use custom filename function
+        #return lambda : custom_name(self.reducer.prop_man)
+        # Uncomment this to use standard file name generating function
+        return None
+
+    #
+    def eval_absorption_corrections(self,test_ws=None):
+        """ The method to evaluate the speed and efficiency of the absorption corrections procedure,
+            before applying your corrections to the whole workspace and all sample runs.
+
+            The absorption correction procedure invoked with excessive accuracy can run for too
+            long providing no real improvements in accuracy. This is why it is recommended to
+            run this procedure evaluating absorption on selected detectors and
+            deploy the corrections to the whole runs only after achieving satisfactory accuracy
+            and execution time.
+
+            The procedure evaluate and prints the expected time to run the absorption corrections
+            on the whole run.
+
+            Input:
+            If provided, the pointer or the name of the workspace available in analysis data service.
+            If it is not, the workspace is taken from PropertyManager.sample_run property
+
+            Usage:
+            Reduce single run and uncomment this method in the __main__ area to evaluate
+            adsorption corrections.
+
+            Change adsorption corrections parameters below to achieve best speed and
+            acceptable accuracy
+        """
+
+        # Gain access to the property manager:
+        propman =  rd.reducer.prop_man
+        # Set up Sample as one of:
+        # 1) Cylinder([Chem_formula],[Height,Radius])
+        # 2) FlatPlate([Chem_formula],[Height,Width,Thick])
+        # 3) HollowCylinder([Chem_formula],[Height,InnerRadius,OuterRadius])
+        # 4) Sphere([[Chem_formula],Radius)
+        # The units are in cm
+        propman.correct_absorption_on = Cylinder('Fe',[10,2]) # Will be taken from def_advanced_properties
+        #                                prop['correct_absorption_on'] =  if not defined here
+        #
+        # Use Monte-Carlo integration.  Take sparse energy points and a few integration attempts
+        # to increase initial speed. Increase these numbers to achieve better accuracy.
+        propman.abs_corr_info = {'EventsPerPoint':3000}#,'NumberOfWavelengthPoints':30}
+        # See MonteCarloAbsorption for all possible properties description and possibility to define
+        # a sparse instrument for speed.
+        #
+        # Gain access to the workspace. The workspace should contain Ei log, containing incident energy
+        # (or be reduced)
+        if test_ws is None:
+            test_ws = PropertyManager.sample_run.get_workspace()
+        # Define spectra list to test absorption on. Empty list will
+        # define absorption on the whole workspace.
+        check_spectra = [1,200]
+        # Evaluate corrections on the selected spectra of the workspace and the time to obtain
+        # the corrections on the whole workspace.
+        corrections,time_to_correct_abs = self.evaluate_abs_corrections(test_ws,check_spectra)
+        # When accuracy and speed of the corrections is satisfactory, copy chosen abs_corr_info
+        # properties from above to the advanced_porperties area to run in reduction.
+        if mpl is not None:
+            n_spectra = len(check_spectra)
+            if n_spectra == 0:
+                n_specra = corrections.getNumberHistograms()
+            mpl.plotSpectrum(corrections,range(0,n_spectra))
+        #
+        return corrections
+
+#------------------------------------------------------------------------------------------------
 
     def __init__(self,web_var=None):
        """ sets properties defaults for the instrument with Name"""
        ReductionWrapper.__init__(self,'MAR',web_var)
-#-------------------------------------------------------------------------------------------------#
-#-------------------------------------------------------------------------------------------------#
+       Mt = MethodType(self.do_preprocessing, self.reducer)
+       DirectEnergyConversion.__setattr__(self.reducer,'do_preprocessing',Mt)
+       Mt = MethodType(self.do_postprocessing, self.reducer)
+       DirectEnergyConversion.__setattr__(self.reducer,'do_postprocessing',Mt)
 #-------------------------------------------------------------------------------------------------#
 def main(input_file=None,output_directory=None):
     """ This method is used to run code from web service
@@ -94,7 +291,7 @@ def main(input_file=None,output_directory=None):
     output_folder = ''
     return output_folder
 
-if __name__ == "__main__":
+if __name__ == "__main__" or __name__ == "__builtin__":
 #-------------------------------------------------------------------------------------------------#
 # SECTION USED TO RUN REDUCTION FROM MANTID SCRIPT WINDOW #
 #-------------------------------------------------------------------------------------------------#
@@ -126,5 +323,8 @@ if __name__ == "__main__":
 
      # Web-like reduction over sequence of files
      #rd.reduce()
-###### Run reduction on all files provided as parameters ######
-     red_ws = rd.run_reduction()
+###### Run reduction on all files provided as parameters                                      ######
+     rd.run_reduction()
+
+###### Test absorption corrections to find optimal settings for corrections algorithm         ######
+#     corr = rd.eval_absorption_corrections()
\ No newline at end of file
diff --git a/scripts/test/ReductionWrapperTest.py b/scripts/test/ReductionWrapperTest.py
index 800b73fa1cdc4003f3e333a0ddd3c3120c2dd686..377fd94a020879e97403b918bbc829416f1019fe 100644
--- a/scripts/test/ReductionWrapperTest.py
+++ b/scripts/test/ReductionWrapperTest.py
@@ -130,8 +130,8 @@ class ReductionWrapperTest(unittest.TestCase):
 
         import reduce_vars as rv
 
-        self.assertEqual(rv.standard_vars,main_prop)
-        self.assertEqual(rv.advanced_vars,adv_prop)
+        self.assertDictEqual(rv.standard_vars,main_prop)
+        self.assertDictEqual(rv.advanced_vars,adv_prop)
         self.assertTrue(hasattr(rv,'variable_help'))
 
         imp.reload(mr)