diff --git a/Examples/python/fitting/ex08_SimultaneousFitOfTwoDatasets/SimultaneousFitOfTwoDatasets.py b/Examples/python/fitting/ex08_SimultaneousFitOfTwoDatasets/SimultaneousFitOfTwoDatasets.py
index ed2c227f83886e1c25980964644143b5ad66aeca..8821793f3fcec6b5f3340afda7ecb2e39d9bc4eb 100644
--- a/Examples/python/fitting/ex08_SimultaneousFitOfTwoDatasets/SimultaneousFitOfTwoDatasets.py
+++ b/Examples/python/fitting/ex08_SimultaneousFitOfTwoDatasets/SimultaneousFitOfTwoDatasets.py
@@ -80,31 +80,23 @@ class DrawObserver(ba.IFitObserver):
         self.fig.canvas.draw()
         plt.ion()
 
-    def plot_colormap(self, data, title, min=1.0, max=1e6):
-        im = plt.imshow(
-            data.getArray(),
-            norm=matplotlib.colors.LogNorm(min, max),
-            extent=[data.getXmin()/deg, data.getXmax()/deg,
-                    data.getYmin()/deg, data.getYmax()/deg],
-            aspect='auto')
-        plt.colorbar(im)
-        plt.title(title)
-
     def plot_datasets(self, fit_suite, canvas):
         for i_dataset in range(0, fit_suite.numberOfFitObjects()):
-            real_data = fit_suite.getRealData(i_dataset)
-            simul_data = fit_suite.getSimulationData(i_dataset)
-            chi2_map = fit_suite.getChiSquaredMap(i_dataset)
+            real_data = fit_suite.experimentalData(i_dataset)
+            simul_data = fit_suite.simulationResult(i_dataset)
+            chi2_map = fit_suite.relativeDifference(i_dataset)
+
+            zmax = real_data.histogram2d().getMaximum()
 
             plt.subplot(canvas[i_dataset*3])
-            self.plot_colormap(real_data, "\"Real\" data - #"+str(i_dataset+1),
-                               min=1.0, max=real_data.getMaximum())
+            ba.plot_colormap(real_data, title="\"Real\" data - #"+str(i_dataset+1),
+                               zmin=1.0, zmax=zmax, zlabel="")
             plt.subplot(canvas[1+i_dataset*3])
-            self.plot_colormap(simul_data, "Simulated data - #"+str(i_dataset+1),
-                               min=1.0, max=real_data.getMaximum())
+            ba.plot_colormap(simul_data, title="Simulated data - #"+str(i_dataset+1),
+                               zmin=1.0, zmax=zmax, zlabel="")
             plt.subplot(canvas[2+i_dataset*3])
-            self.plot_colormap(chi2_map, "Chi2 map - #"+str(i_dataset+1),
-                               min=0.001, max=10.0)
+            ba.plot_colormap(chi2_map, title="Chi2 map - #"+str(i_dataset+1),
+                               zmin=0.001, zmax=10.0, zlabel="")
 
     def plot_fit_parameters(self, fit_suite, canvas):
         # fit parameters
@@ -125,7 +117,7 @@ class DrawObserver(ba.IFitObserver):
         # most of the space
         canvas = matplotlib.gridspec.GridSpec(
             3, 3, width_ratios=[1, 1, 1], height_ratios=[4, 4, 1])
-        canvas.update(left=0.05, right=0.95, hspace=0.4, wspace=0.2)
+        canvas.update(left=0.05, right=0.95, hspace=0.5, wspace=0.2)
 
         self.plot_datasets(fit_suite, canvas)
         self.plot_fit_parameters(fit_suite, canvas)
diff --git a/Examples/python/simulation/ex03_InterferenceFunctions/Interference1DLattice.py b/Examples/python/simulation/ex03_InterferenceFunctions/Interference1DLattice.py
index 2d4d2c65f482096b7c69516df2a5fcf4e59090d2..b478a6d1307605e39f82f994b05d12110f4c515f 100644
--- a/Examples/python/simulation/ex03_InterferenceFunctions/Interference1DLattice.py
+++ b/Examples/python/simulation/ex03_InterferenceFunctions/Interference1DLattice.py
@@ -1,34 +1,37 @@
 """
-Long boxes on a 1D lattice
+Simulation of grating using very long boxes and 1D lattice.
+Monte-carlo integration is used to get rid of
+large-particle form factor oscillations.
 """
-import numpy
 import bornagain as ba
 from bornagain import deg, angstrom, nm
 
 
-def get_sample():
+def get_sample(lattice_rotation_angle=45*deg):
     """
-    Returns a sample with a grating on a substrate.
-    The structure is modelled by infinitely long boxes forming a 1D lattice.
+    Returns a sample with a grating on a substrate,
+    modelled by very long boxes forming a 1D lattice with Cauchy correlations.
     """
     # defining materials
     m_ambience = ba.HomogeneousMaterial("Air", 0.0, 0.0)
     m_substrate = ba.HomogeneousMaterial("Substrate", 6e-6, 2e-8)
     m_particle = ba.HomogeneousMaterial("Particle", 6e-4, 2e-8)
 
+    box_length, box_width, box_height = 10*nm, 10000*nm, 10*nm
+    lattice_length = 30*nm
+
     # collection of particles
-    lattice_length = 30.0*nm
-    lattice_rotation_angle = 0.0*deg
     interference = ba.InterferenceFunction1DLattice(
         lattice_length, lattice_rotation_angle)
-    pdf = ba.FTDecayFunction1DCauchy(20./2./numpy.pi*nm)
+    pdf = ba.FTDecayFunction1DCauchy(1000.0)
     interference.setDecayFunction(pdf)
 
-    box_ff = ba.FormFactorBox(1000*nm, 10*nm, 15.0*nm)
+    box_ff = ba.FormFactorBox(box_length, box_width, box_height)
     box = ba.Particle(m_particle, box_ff)
-    transform = ba.RotationZ(25.0*deg)
+
     particle_layout = ba.ParticleLayout()
-    particle_layout.addParticle(box, 1.0, ba.kvector_t(0.0, 0.0, 0.0), transform)
+    particle_layout.addParticle(
+        box, 1.0, ba.kvector_t(0.0, 0.0, 0.0), ba.RotationZ(lattice_rotation_angle))
     particle_layout.setInterferenceFunction(interference)
 
     # assembling the sample
@@ -49,7 +52,8 @@ def get_simulation():
     simulation = ba.GISASSimulation()
     simulation.setDetectorParameters(200, -1.0*deg, 1.0*deg,
                                      200, 0.0*deg, 2.0*deg)
-    simulation.setBeamParameters(24.0*angstrom, 0.2*deg, 0.0*deg)
+    simulation.setBeamParameters(1.0*angstrom, 0.2*deg, 0.0*deg)
+    simulation.getOptions().setMonteCarloIntegration(True, 100)
     return simulation
 
 
@@ -59,10 +63,11 @@ def run_simulation():
     """
     simulation = get_simulation()
     simulation.setSample(get_sample())
+    simulation.setTerminalProgressMonitor()
     simulation.runSimulation()
     return simulation.result()
 
 
 if __name__ == '__main__':
     result = run_simulation()
-    ba.plot_simulation_result(result)
+    ba.plot_simulation_result(result, 1e-03)
diff --git a/Examples/python/simulation/ex03_InterferenceFunctions/RectangularGrating.py b/Examples/python/simulation/ex03_InterferenceFunctions/RectangularGrating.py
index bdb63ec0fa301151fc5cbed1fc875249d7fd1354..c5e7b472662ddb33dec040db2d7e38fa4aeb6c2c 100644
--- a/Examples/python/simulation/ex03_InterferenceFunctions/RectangularGrating.py
+++ b/Examples/python/simulation/ex03_InterferenceFunctions/RectangularGrating.py
@@ -4,30 +4,31 @@ Monte-carlo integration is used to get rid of
 large-particle form factor oscillations.
 """
 import bornagain as ba
-from bornagain import deg, angstrom, nm
+from bornagain import deg, angstrom, nm, micrometer
 
 
-def get_sample(lattice_rotation_angle=45*deg):
+def get_sample(lattice_rotation_angle=0.0*deg):
     """
-    Returns a sample with a grating on a substrate,
-    modelled by very long boxes forming a 1D lattice with Cauchy correlations.
+    Returns a sample with a grating on a substrate.
+    lattice_rotation_angle = 0 - beam parallel to grating lines
+    lattice_rotation_angle = 90*deg - beam perpendicular to grating lines
     """
     # defining materials
     m_ambience = ba.HomogeneousMaterial("Air", 0.0, 0.0)
-    m_substrate = ba.HomogeneousMaterial("Substrate", 6e-6, 2e-8)
-    m_particle = ba.HomogeneousMaterial("Particle", 6e-4, 2e-8)
+    m_si = ba.HomogeneousMaterial("Si", 5.78164736e-6, 1.02294578e-7)
 
-    box_length, box_width, box_height = 10*nm, 10000*nm, 10*nm
-    lattice_length = 30*nm
+    box_length, box_width, box_height = 50*micrometer, 70*nm, 50*nm
+    lattice_length = 150*nm
 
     # collection of particles
     interference = ba.InterferenceFunction1DLattice(
-        lattice_length, lattice_rotation_angle)
-    pdf = ba.FTDecayFunction1DCauchy(1000.0)
+        lattice_length, 90.0*deg - lattice_rotation_angle)
+
+    pdf = ba.ba.FTDecayFunction1DGauss(450.0)
     interference.setDecayFunction(pdf)
 
-    box_ff = ba.FormFactorBox(box_length, box_width, box_height)
-    box = ba.Particle(m_particle, box_ff)
+    box_ff = ba.FormFactorLongBoxLorentz(box_length, box_width, box_height)
+    box = ba.Particle(m_si, box_ff)
 
     particle_layout = ba.ParticleLayout()
     particle_layout.addParticle(
@@ -37,11 +38,16 @@ def get_sample(lattice_rotation_angle=45*deg):
     # assembling the sample
     air_layer = ba.Layer(m_ambience)
     air_layer.addLayout(particle_layout)
-    substrate_layer = ba.Layer(m_substrate)
+    substrate_layer = ba.Layer(m_si)
+
+    roughness = ba.LayerRoughness()
+    roughness.setSigma(5.0 * nm)
+    roughness.setHurstParameter(0.5)
+    roughness.setLatteralCorrLength(10.0 * nm)
 
     multi_layer = ba.MultiLayer()
     multi_layer.addLayer(air_layer)
-    multi_layer.addLayer(substrate_layer)
+    multi_layer.addLayerWithTopRoughness(substrate_layer, roughness)
     return multi_layer
 
 
@@ -50,9 +56,10 @@ def get_simulation():
     Create and return GISAXS simulation with beam and detector defined
     """
     simulation = ba.GISASSimulation()
-    simulation.setDetectorParameters(200, -1.0*deg, 1.0*deg,
-                                     200, 0.0*deg, 2.0*deg)
-    simulation.setBeamParameters(1.0*angstrom, 0.2*deg, 0.0*deg)
+    simulation.setDetectorParameters(200, -0.5*deg, 0.5*deg,
+                                     200, 0.0*deg, 0.6*deg)
+    simulation.setBeamParameters(1.34*angstrom, 0.4*deg, 0.0*deg)
+    simulation.setBeamIntensity(1e+08)
     simulation.getOptions().setMonteCarloIntegration(True, 100)
     return simulation
 
diff --git a/GUI/coregui/Views/widgetbox/images/Lattice2DFinite.png b/GUI/coregui/Views/widgetbox/images/Lattice2DFinite.png
new file mode 100644
index 0000000000000000000000000000000000000000..87b601e84ef60aabda663b6413bcb2c4df39d02b
Binary files /dev/null and b/GUI/coregui/Views/widgetbox/images/Lattice2DFinite.png differ
diff --git a/GUI/coregui/Views/widgetbox/widgetbox.qrc b/GUI/coregui/Views/widgetbox/widgetbox.qrc
index 848b11fc1f23c61d21ba202020a1b637089f66ca..861427053d77c33a56363eb93c39b6c933db03a3 100644
--- a/GUI/coregui/Views/widgetbox/widgetbox.qrc
+++ b/GUI/coregui/Views/widgetbox/widgetbox.qrc
@@ -36,5 +36,6 @@
         <file>images/SizeDistribution.png</file>
         <file>images/Lattice1D.png</file>
         <file>images/sample_layers2.png</file>
+        <file>images/Lattice2DFinite.png</file>
     </qresource>
 </RCC>
diff --git a/GUI/coregui/Views/widgetbox/widgetbox.xml b/GUI/coregui/Views/widgetbox/widgetbox.xml
index 066096c1b45aabf4c9271d50a62971b3fa4f6693..f26b6eefa82af3b55430d4d94dd7a51978ebeb7c 100644
--- a/GUI/coregui/Views/widgetbox/widgetbox.xml
+++ b/GUI/coregui/Views/widgetbox/widgetbox.xml
@@ -67,7 +67,7 @@
             </widget>
         </categoryentry>
 
-        <categoryentry  name="Finite 2D lattice" icon="images/Lattice2D.png">
+        <categoryentry  name="Finite 2D lattice" icon="images/Lattice2DFinite.png">
             <widget class="InterferenceFinite2DLattice">
                 <property name="objectName">
                     <string notr="true">somestring</string>
diff --git a/Tests/Functional/Python/PyPersistence/CMakeLists.txt b/Tests/Functional/Python/PyPersistence/CMakeLists.txt
index 41ddb1ff5942b87d021be16168f689c3b17a2b6a..48f340e77a09cc06a937731f52a9f76734ba0ae0 100644
--- a/Tests/Functional/Python/PyPersistence/CMakeLists.txt
+++ b/Tests/Functional/Python/PyPersistence/CMakeLists.txt
@@ -36,7 +36,7 @@ test_example("simulation/ex03_InterferenceFunctions/ApproximationDA" 2e-10)
 test_example("simulation/ex03_InterferenceFunctions/ApproximationLMA" 2e-10)
 test_example("simulation/ex03_InterferenceFunctions/ApproximationSSCA" 2e-10)
 test_example("simulation/ex03_InterferenceFunctions/CosineRipplesAtRectLattice" 2e-10)
-test_example("simulation/ex03_InterferenceFunctions/Interference1DLattice" 2e-10)
+test_example("simulation/ex03_InterferenceFunctions/Interference1DLattice" 1.5)
 test_example("simulation/ex03_InterferenceFunctions/Interference1DRadialParaCrystal" 2e-10)
 test_example("simulation/ex03_InterferenceFunctions/Interference2DCenteredSquareLattice" 2e-10)
 test_example("simulation/ex03_InterferenceFunctions/Interference2DParaCrystal" 2e-10)
diff --git a/Tests/ReferenceData/Python/Interference1DLattice.ref.int.gz b/Tests/ReferenceData/Python/Interference1DLattice.ref.int.gz
index e8001e5bf71180634769eaa2dd9c85c8742252d1..834d93bd01543fa71c32f7a1e2f70d19e73741e6 100644
Binary files a/Tests/ReferenceData/Python/Interference1DLattice.ref.int.gz and b/Tests/ReferenceData/Python/Interference1DLattice.ref.int.gz differ
diff --git a/Tests/ReferenceData/Python/RectangularGrating.ref.int.gz b/Tests/ReferenceData/Python/RectangularGrating.ref.int.gz
index 3ba9799af330a01a4d84949b0204c5d66be280fb..0d7e955b23bf83f827995fe6c6d65f4d71d854a4 100644
Binary files a/Tests/ReferenceData/Python/RectangularGrating.ref.int.gz and b/Tests/ReferenceData/Python/RectangularGrating.ref.int.gz differ
diff --git a/dev-tools/code-tools/update-website.py b/dev-tools/code-tools/update-website.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e0982b1ccf286d8833f80c660880f35ceeea43a
--- /dev/null
+++ b/dev-tools/code-tools/update-website.py
@@ -0,0 +1,120 @@
+"""
+To update examples on website:
+
+1) Run in your build directory
+ctest -R PyExamples
+This will run all existing examples and generate intensity images for web site.
+
+2) Modify variables in this script
+WEBSITE_DIR, BORNAGAIN_SOURCE, EXAMPLE_IMAGES
+
+3) Run this script
+python update-examples.py
+
+TODO: move the script under ctest control
+"""
+import os
+import shutil
+
+WEBSITE_DIR = "/home/pospelov/development/BornAgain-website"
+BORNAGAIN_SOURCE = "/home/pospelov/development/BornAgainHub/BornAgain"
+EXAMPLE_IMAGES = "/home/pospelov/development/BornAgainHub/build/test_output/Functional/Python/PyExamples"
+
+
+def copy_file_to_file(source, destination):
+    """
+    Copies file to another.
+    """
+    shutil.copyfile(source, destination)
+
+
+def find_files(dir_name):
+    """
+    Yield recursive list of files in given dir_name
+    """
+    for subdir, dirs, files in os.walk(dir_name):
+        for filename in files:
+            yield subdir, filename
+
+
+def get_files(dir_name, extension):
+    """
+    Return list of all files in subdirectories of dir_name with given extension
+    """
+    result = []
+    for subdir, filename in find_files(dir_name):
+            name, ext = os.path.splitext(filename)
+            if ext in extension:
+                result.append(os.sep.join([subdir, filename]))
+    return result
+
+
+def find_files_with_same_name(filename_list, name_to_find):
+    same_names = []
+    for filename in filename_list:
+        if os.path.basename(filename) == name_to_find:
+            same_names.append(filename)
+
+    return same_names
+
+
+def copy_files(source_list, destination_list):
+    """
+    Every file in destination_list will be replaced by the file with the same
+    basename found in source_list
+    """
+    missed_files = []
+    updated_files = []
+    for f in destination_list:
+        found_source = find_files_with_same_name(source_list, os.path.basename(f))
+        if len(found_source) == 1:
+            updated_files.append(f)
+            copy_file_to_file(found_source[0], f)
+        else:
+            print(len(found_source), "Problem with ", f)
+            missed_files.append(f)
+
+    print("Summary:")
+
+    print("Following files have been updated on website:")
+    for f in updated_files:
+        print(f)
+
+    if len(missed_files):
+        print("Following files exist on website but not in source dir:")
+        for f in missed_files:
+            print(f)
+
+
+def update_example_images():
+    """
+    Copies example intensity images from build directory to website directory
+    """
+    website = os.path.join(WEBSITE_DIR, "content/documentation/sample-models")
+    source = EXAMPLE_IMAGES
+    website_files = get_files(website, ".png")
+    source_files = get_files(source, ".png")
+
+    copy_files(source_files, website_files)
+
+
+def update_example_scripts():
+    """
+    Copies example scripts from BornAgain directory to website directory
+    """
+    website = os.path.join(WEBSITE_DIR, "static/files/python")
+    source = os.path.join(BORNAGAIN_SOURCE, "Examples/python")
+    website_files = get_files(website, ".py")
+    source_files = get_files(source, ".py")
+
+    copy_files(source_files, website_files)
+
+
+def update_website():
+    update_example_images()
+    update_example_scripts()
+
+
+if __name__ == '__main__':
+    update_website()
+