From ac68d47d6298488f9b8529d0d4cdaa7beffd16a4 Mon Sep 17 00:00:00 2001 From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de> Date: Tue, 8 Mar 2022 17:36:21 +0100 Subject: [PATCH] Explain abbreviated vs expanded script --- Examples/specular/AlternatingLayers1.py | 52 ++++++++++++ Tests/Examples/CMakeLists.txt | 11 ++- Tests/Examples/PyPersistence.py.in | 5 +- hugo/content/py/start/modify-script.md | 59 +++++++++++++ hugo/content/py/start/syntax.md | 105 ++++++------------------ 5 files changed, 148 insertions(+), 84 deletions(-) create mode 100755 Examples/specular/AlternatingLayers1.py create mode 100644 hugo/content/py/start/modify-script.md diff --git a/Examples/specular/AlternatingLayers1.py b/Examples/specular/AlternatingLayers1.py new file mode 100755 index 00000000000..48df8feec18 --- /dev/null +++ b/Examples/specular/AlternatingLayers1.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +Basic example of specular reflectometry simulation with BornAgain. +The sample consists of 20 alternating Ti and Ni layers. +Explicit variant without using std_samples and std_simulation. +""" + +import bornagain as ba +from bornagain import deg, angstrom + +def get_sample(): + """ + Sample consisting of 20 alternating Ti and Ni layers. + """ + + # Define materials + m_ambient = ba.MaterialBySLD("Vacuum", 0, 0) + m_ti = ba.MaterialBySLD("Ti", -1.9493e-06, 0) + m_ni = ba.MaterialBySLD("Ni", 9.4245e-06, 0) + m_substrate = ba.MaterialBySLD("SiSubstrate", 2.0704e-06, 0) + + # Define layers + ambient_layer = ba.Layer(m_ambient) + ti_layer = ba.Layer(m_ti, 30*angstrom) + ni_layer = ba.Layer(m_ni, 70*angstrom) + substrate_layer = ba.Layer(m_substrate) + + # Define sample + sample = ba.MultiLayer() + sample.addLayer(ambient_layer) + for _ in range(10): + sample.addLayer(ti_layer) + sample.addLayer(ni_layer) + sample.addLayer(substrate_layer) + + return sample + +def get_simulation(sample, scan_size=500): + """ + A standard specular simulation setup. + """ + simulation = ba.SpecularSimulation() + scan = ba.AlphaScan(1.54*angstrom, scan_size, 0, 2*deg) + simulation.setScan(scan) + simulation.setSample(sample) + return simulation + +if __name__ == '__main__': + from bornagain import ba_plot + sample = get_sample() + simulation = get_simulation(sample) + ba_plot.run_and_plot(simulation) diff --git a/Tests/Examples/CMakeLists.txt b/Tests/Examples/CMakeLists.txt index 60badd7a436..f4215683a7a 100644 --- a/Tests/Examples/CMakeLists.txt +++ b/Tests/Examples/CMakeLists.txt @@ -39,11 +39,12 @@ function(run_noplot example) ${Python3_EXECUTABLE} ${script_path} ${TEST_OUTPUT_DIR_PY_EXAMPLES}) endfunction() -# Python persistence test: run modified example, and compare with reference data. -function(test_example example tolerance) +# Python equality test: run modified example, and compare with reference data of another example +function(test_equality example reference tolerance) set(script_path ${EXAMPLES_DIR}/${example}.py) get_filename_component(EXAMPLE_NAME ${script_path} NAME_WE) get_filename_component(EXAMPLE_DIR ${script_path} DIRECTORY) + get_filename_component(REFERENCE_NAME ${reference} NAME_WE) set(test_name Example.persist.${EXAMPLE_NAME}) set(PYPERSIST_TOLERANCE ${tolerance}) @@ -54,6 +55,11 @@ function(test_example example tolerance) add_test(NAME ${test_name} COMMAND ${Python3_EXECUTABLE} -B ${example_mod}) endfunction() +# Python persistence test: run modified example, and compare with reference data. +function(test_example example tolerance) + test_equality(${example} ${example} ${tolerance}) +endfunction() + #################################################################################################### # Persistence tests #################################################################################################### @@ -100,6 +106,7 @@ test_example(scatter2d/TriangularRipple 2e-10) # test_example(scatter2d/TwoTypesOfCylindersWithSizeDistribution 2e-10) test_example(specular/AlternatingLayers 2e-10) +test_equality(specular/AlternatingLayers1 specular/AlternatingLayers 2e-10) test_example(specular/BeamAngularDivergence 2e-10) test_example(specular/BeamFullDivergence 2e-10) test_example(specular/TOFRWithResolution 2e-10) diff --git a/Tests/Examples/PyPersistence.py.in b/Tests/Examples/PyPersistence.py.in index edbefe43a7a..a0834b27ff5 100644 --- a/Tests/Examples/PyPersistence.py.in +++ b/Tests/Examples/PyPersistence.py.in @@ -14,6 +14,7 @@ import bornagain as ba REFERENCE_DIR = "@TEST_REFERENCE_DIR_EXAMPLES_MINI@" EXAMPLE_DIR = "@EXAMPLE_DIR@" EXAMPLE_NAME = "@EXAMPLE_NAME@" +REFERENCE_NAME = "@REFERENCE_NAME@" OUTPUT_DIR = "@TEST_OUTPUT_DIR_PY_PERSIST@" TOLERANCE = @PYPERSIST_TOLERANCE@ @@ -147,9 +148,9 @@ def process_example(): nfailures = 0 if type(result) is dict: for dict_key, subresult in result.items(): - nfailures += check_result(subresult, EXAMPLE_NAME + "." + str(dict_key)) + nfailures += check_result(subresult, REFERENCE_NAME + "." + str(dict_key)) else: - nfailures += check_result(result, EXAMPLE_NAME) + nfailures += check_result(result, REFERENCE_NAME) return nfailures diff --git a/hugo/content/py/start/modify-script.md b/hugo/content/py/start/modify-script.md new file mode 100644 index 00000000000..51958068850 --- /dev/null +++ b/hugo/content/py/start/modify-script.md @@ -0,0 +1,59 @@ ++++ +title = "Modify the script" +weight = 40 ++++ + +## Expanded simulation script + +As a first step towards writing sample and simulation specifications +of your own, let us expand the simulation script +[AlternatingLayers.py]({{% ref-src "Examples/specular/AlternatingLayers.py" %}}) +from the preceding pages. +Instead of the shorthand calls to modules +[std_samples]({{% ref-src "Wrap/Python/std_samples.py" %}}) and +[std_simulations]({{% ref-src "Wrap/Python/std_simulations.py" %}}), +we provide explicit code for the functions `get_sample` and `get_simulation`: +{{< highlightfile file="Examples/specular/AlternatingLayers1.py">}} +<p> + +### Sample + +`get_sample` is a function without arguments. +It returns an object of type [MultiLayer]({{% ref-api "classMultiLayer" %}}). + +The return statement is preceded by three stances. +Each stance starts with a comment line, +{{< highlight python >}} + # comment extends from hash character to end of line +{{< /highlight >}} + +BornAgain functions that start with a capital letter, +like `MaterialBySLD` or `Layer` are _constructors_ or +constructor-like global functions. +They return new _objects_. An object is an instance of a _class_. +The function `MaterialBySLD` instantiates an object of type +[Material]({{% ref-api "classMaterial" %}}) +the function `Layer` an object of type +[Layer]({{% ref-api "classLayer" %}}). + +Function like `addLayer` is a member function of class +[MultiLayer]({{% ref-api "classMultiLayer" %}}). +This can be seen from the two lines +{{< highlight python >}} + sample = ba.MultiLayer() + sample.addLayer(ambient_layer) +{{< /highlight >}} +where `sample` is created as a new instance of class `MultiLayer`. + +### Simulation + +`get_simulation(sample, scan_size=500)` is a function with one +required argument (`sample`) and one optional keyword argument +(`scan_size`). If the function is called with only one argument, +then `scan_size` is assigned the default value 500. + +`angstrom` and `deg` are numeric constants. They are used to +convert physical quantities to internal units nanometer and radian. + +The function returns an object of type +[SpecularSimulation]({{% ref-api "classSpecularSimulation" %}}). diff --git a/hugo/content/py/start/syntax.md b/hugo/content/py/start/syntax.md index caf150481ae..5eba7b1516c 100644 --- a/hugo/content/py/start/syntax.md +++ b/hugo/content/py/start/syntax.md @@ -1,5 +1,5 @@ +++ -title = "Syntax" +title = "Understand the syntax" weight = 30 +++ @@ -14,9 +14,7 @@ For easy reference, here again the full script: {{< highlightfile file="Examples/specular/AlternatingLayers.py">}} <p> -We shall now explain the code section by section. - -### Front matter +We shall now explain the code. The [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) line {{< highlight python >}} @@ -34,92 +32,39 @@ Text, text, text {{< /highlight >}} are comments. -The line -{{< highlight python >}} -import bornagain as ba -{{< /highlight >}} -imports the Python module `bornagain` (small caps, see chapter on - [how to find]({{% ref-py "start/find.md" %}}) BornAgain), -and assigns it the alias `ba` for brevity. -Classes and functions from the BornAgain API are -now available with the prefix `ba.`. - -The line -{{< highlight python >}} -from bornagain import deg, angstrom -{{< /highlight >}} -makes a few frequently used symbols, namely the unit multipliers for -degree and angstrom, available without prefix. - -### Sample - -`get_sample` is a function without arguments. -It returns an object of type [MultiLayer]({{% ref-api "classMultiLayer" %}}). - -The return statement is preceded by three stances. -Each stance starts with a comment line, +The block {{< highlight python >}} - # comment extends from hash character to end of line +def get_sample(): + from bornagain import std_samples + return std_samples.alternating_layers() {{< /highlight >}} +defines the function `get_sample` that +imports the BornAgain submodule [std_samples]({{% ref-src "Wrap/Python/std_samples.py" %}}), +and returns the alternating layers sample model. -BornAgain functions that start with a capital letter, -like `MaterialBySLD` or `Layer` are _constructors_ or -constructor-like global functions. -They return new _objects_. An object is an instance of a _class_. -The function `MaterialBySLD` instantiates an object of type -[Material]({{% ref-api "classMaterial" %}}) -the function `Layer` an object of type -[Layer]({{% ref-api "classLayer" %}}). - -Function like `addLayer` is a member function of class -[MultiLayer]({{% ref-api "classMultiLayer" %}}). -This can be seen from the two lines +Similarly, the next block {{< highlight python >}} - sample = ba.MultiLayer() - sample.addLayer(ambient_layer) +def get_simulation(sample, scan_size=500): + from bornagain import std_simulations + return std_simulations.specular(sample, scan_size) {{< /highlight >}} -where `sample` is created as a new instance of class `MultiLayer`. - -### Simulation - -`get_simulation(sample, scan_size=500)` is a function with one -required argument (`sample`) and one optional keyword argument -(`scan_size`). If the function is called with only one argument, -then `scan_size` is assigned the default value 500. - -`angstrom` and `deg` are numeric constants. They are used to -convert physical quantities to internal units nanometer and radian. - -The function returns an object of type -[SpecularSimulation]({{% ref-api "classSpecularSimulation" %}}). +defines the function `get_simulation` that) +imports the BornAgain submodule [std_simulations]({{% ref-src "Wrap/Python/std_simulations.py" %}}), +and returns a standard setup to simulate a specular reflectivity scan. -### Main program - -The line +The clause {{< highlight python >}} if __name__ == '__main__': {{< /highlight >}} -is standard Python idiom. -It ensures that the following is executed if and only if this script -is executed directly, as a _main program_, -but not if this script is included as a submodule in some other code. - -The lines -{{< highlight python >}} - sample = get_sample() - simulation = get_simulation(sample) -{{< /highlight >}} -call the two functions defined above -in order to obtain the sample model -and then the full simulation setup, comprising sample and instrument model. +ensures that the following statements are only executed +if the script is called directly, as a "main" program. +This precaution is required by the GUI, where scripts can be imported without +being immediately executed. -Finally, the single line +In the main block, the statement {{< highlight python >}} ba_plot.run_and_plot(simulation) {{< /highlight >}} -runs the simulation and plots the result, using MatPlotLib. -It uses the module -[ba_plot]({{% ref-src "Wrap/Python/ba_plot.py" %}}), -imported a few lines before. -See section [Plot and export]({{% ref-py "result/_index.md" %}}) -for other ways of running simulations and plotting or exporting results. +runs the previously defined simulation, and calls MatPlotLib to display the results. +The function `run_and_plot` is implemented in the module +[ba_plot]({{% ref-src "Wrap/Python/ba_plot.py" %}}). -- GitLab