diff --git a/docs/src/dictionay.rst b/docs/src/dictionay.rst new file mode 100644 index 00000000..e213d43d --- /dev/null +++ b/docs/src/dictionay.rst @@ -0,0 +1,20 @@ +Dictionary +========== +The following serves to clarify what we mean by the terms we use in this project. + +Sample +------ +A sample is an ideal representation of a the full physical setup. +This includes the layer(s) under investigation, the surrounding superphase, and the subphase. + +Calculator +---------- +A calculator is the physics engine which calculates the reflectivity curve from our inputted sample parameters. +We rely on third party software to provide the necessary calculators. +Different calculators might have different capabilities and limitations. + +Model +----- +A model combines a sample and calculator. +The model is also responsible for including instrumental effects such as background, scale, and resolution. + diff --git a/docs/src/index.rst b/docs/src/index.rst index bb6d5d89..140fe5d4 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -11,6 +11,7 @@ calculators experiment/experiment tutorials/tutorials + dictionary contributing authors api/api diff --git a/docs/src/sample/material_library.rst b/docs/src/sample/material_library.rst index 684b0185..7007675d 100644 --- a/docs/src/sample/material_library.rst +++ b/docs/src/sample/material_library.rst @@ -22,7 +22,7 @@ The construction of a :py:class:`Material` is achieved as shown below. name='Boron' ) -The above object will have the properties of :py:attr:`sld` and :py:attr:`isld`, which will have values of :code:`6.908 1 / angstrom ** 2` and :code:`-0.278 1 / angstrom ** 2` respectively. +The above object will have the properties of :py:attr:`sld` and :py:attr:`isld`, which will have values of :code:`6.908 1/angstrom^2` and :code:`-0.278 1/angstrom^2` respectively. As is shown in the `tutorials`_, a material can be used to construct a :py:class:`Layer` from which `slab models`_ are created. :py:class:`MaterialDensity` @@ -78,7 +78,7 @@ So to produce a :py:class:`MaterialSolvated` that is 20 % D2O in a polymer, the name='Solvated Polymer' ) -For the :py:attr:`solvated_polymer` object, the :py:attr:`sld` will be :code:`2.872 1 / angstrom ** 2` (the weighted average of the two scattering length densities). +For the :py:attr:`solvated_polymer` object, the :py:attr:`sld` will be :code:`2.872 1/angstrom^2` (the weighted average of the two scattering length densities). The :py:class:`MaterialSolvated` includes a constraint such that if the value of either constituent scattering length densities (both real and imaginary components) or the fraction changes, then the resulting material :py:attr:`sld` and :py:attr:`isld` will change appropriately. .. _`assemblies`: ./assemblies_library.html diff --git a/docs/src/tutorials/fitting/repeating.ipynb b/docs/src/tutorials/fitting/repeating.ipynb index 5d0e2514..074fed86 100644 --- a/docs/src/tutorials/fitting/repeating.ipynb +++ b/docs/src/tutorials/fitting/repeating.ipynb @@ -35,8 +35,11 @@ "\n", "import numpy as np\n", "import scipp as sc\n", - "import easyreflectometry\n", + "import pooch\n", "import refl1d\n", + "\n", + "import easyreflectometry\n", + "\n", "from easyreflectometry.data import load\n", "from easyreflectometry.sample import Layer\n", "from easyreflectometry.sample import Sample\n", @@ -46,7 +49,8 @@ "from easyreflectometry.experiment import PercentageFhwm\n", "from easyreflectometry.calculators import CalculatorFactory\n", "from easyreflectometry.fitting import Fitter\n", - "from easyreflectometry.plot import plot" + "from easyreflectometry.plot import plot\n", + "from easyscience.fitting import AvailableMinimizers" ] }, { @@ -78,18 +82,22 @@ "## Reading in experimental data\n", "\n", "The data that we will investigate in this tutorial was generated with [GenX](https://aglavic.github.io/genx/) and is stored in an `.ort` [format file](https://github.com/reflectivity/file_format/blob/master/specification.md).\n", - "Use link to [download](repeating_layers.ort) the ort data." + "We use `pooch` to fetch the file from the repository." ] }, { "cell_type": "code", "execution_count": null, - "id": "609174e5-1371-412d-a29f-cb05bfe36df0", + "id": "7121c7e9", "metadata": {}, "outputs": [], "source": [ - "data = load('repeating_layers.ort')\n", - "data" + "file_path = pooch.retrieve(\n", + " # URL to one of Pooch's test files\n", + " url=\"https://raw.githubusercontent.com/EasyScience/EasyReflectometryLib/master/docs/src/tutorials/fitting/repeating_layers.ort\",\n", + " known_hash=\"a5ffca9fd24f1d362266251723aec7ce9f34f123e39a38dfc4d829c758e6bf90\",\n", + ")\n", + "data = load(file_path)" ] }, { @@ -265,7 +273,8 @@ "outputs": [], "source": [ "fitter = Fitter(model)\n", - "analysed = fitter.fit(data, method='differential_evolution')\n", + "fitter.switch_minimizer(AvailableMinimizers.LMFit_differential_evolution)\n", + "analysed = fitter.fit(data)\n", "analysed" ] }, @@ -316,8 +325,22 @@ } ], "metadata": { + "kernelspec": { + "display_name": "erl_1_311", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } }, "nbformat": 4, diff --git a/docs/src/tutorials/fitting/simple_fitting.ipynb b/docs/src/tutorials/fitting/simple_fitting.ipynb index 136a98ad..dab3008d 100644 --- a/docs/src/tutorials/fitting/simple_fitting.ipynb +++ b/docs/src/tutorials/fitting/simple_fitting.ipynb @@ -29,8 +29,11 @@ "source": [ "%matplotlib inline\n", "\n", - "import easyreflectometry\n", + "import pooch\n", "import refnx\n", + "\n", + "import easyreflectometry\n", + "\n", "from easyreflectometry.data import load\n", "from easyreflectometry.sample import Layer\n", "from easyreflectometry.sample import Sample\n", @@ -71,7 +74,7 @@ "\n", "`easyreflectometry` has support for the `.ort` file format, a [standard file format for reduced reflectivity data developed by the Open Reflectometry Standards Organisation](https://www.reflectometry.org/working_groups/file_formats/).\n", "To load in a dataset, we use the `load` function.\n", - "Use link to [download](example.ort) the ort data." + "We use `pooch` to fetch the file from the repository." ] }, { @@ -81,7 +84,12 @@ "metadata": {}, "outputs": [], "source": [ - "data = load('example.ort')" + "file_path = pooch.retrieve(\n", + " # URL to one of Pooch's test files\n", + " url=\"https://raw.githubusercontent.com/EasyScience/EasyReflectometryLib/master/docs/src/tutorials/fitting/example.ort\",\n", + " known_hash=\"82d0c95c069092279a799a8131ad3710335f601d9f1080754b387f42e407dfab\",\n", + ")\n", + "data = load(file_path)" ] }, { @@ -514,8 +522,22 @@ } ], "metadata": { + "kernelspec": { + "display_name": "erl_1_311", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } }, "nbformat": 4, diff --git a/docs/src/tutorials/magnetism.ipynb b/docs/src/tutorials/magnetism.ipynb new file mode 100644 index 00000000..28589ddc --- /dev/null +++ b/docs/src/tutorials/magnetism.ipynb @@ -0,0 +1,577 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a60117e3-d089-4375-ac7c-12a52ed47271", + "metadata": {}, + "source": [ + "# Magnetism\n", + "\n", + "Magntism is only available in Refl1d and it does not support RepeatingMultilayer\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "f5d0bd58", + "metadata": {}, + "source": [ + "## Setup\n", + "First configure matplotlib to place figures in notebook and import needed modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29d5d62d-af4a-416d-bbe2-1338d32b30f5", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import scipp as sc\n", + "import refl1d\n", + "import refl1d.names\n", + "\n", + "import easyreflectometry\n", + "\n", + "from easyreflectometry.calculators import CalculatorFactory\n", + "from easyreflectometry.experiment import Model\n", + "from easyreflectometry.experiment import PercentageFhwm\n", + "from easyreflectometry.sample import Layer\n", + "from easyreflectometry.sample import Material\n", + "from easyreflectometry.sample import Multilayer\n", + "from easyreflectometry.sample import Sample" + ] + }, + { + "cell_type": "markdown", + "id": "8fd3e8f7-84ac-41c4-a89d-922ed82a001e", + "metadata": {}, + "source": [ + "For reference we fetch the version of the software packages we are using. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "549734c1-bbd9-41f3-8a20-d7a8ded37802", + "metadata": {}, + "outputs": [], + "source": [ + "print(f'numpy: {np.__version__}')\n", + "print(f'scipp: {sc.__version__}')\n", + "print(f'easyreflectometry: {easyreflectometry.__version__}')\n", + "print(f'refl1d: {refl1d.__version__}')" + ] + }, + { + "cell_type": "markdown", + "id": "dad78ccc-1e6f-47cd-8557-c4fa6c736f4b", + "metadata": {}, + "source": [ + "## Building our model\n", + "\n", + "The system that was used to produce the data shown above is based on a silicon subphase with two layers upon it. \n", + "These two layers are charachterized by having a scattering length density (SLD) of respectively 4 and 8.\n", + "Both layers have a rougness of 0 but their thicknesses are 100 and 150 angstrom respectively.\n", + "We show the model that will be used graphically below. \n", + "\n", + "
\n", + " A slab model description of the two_layers system.\n", + "
\n", + "
\n", + " A slab model description of the two layer.\n", + "
\n", + "\n", + "To construct such a layer structure, first we create each of the materials, the associated layers, and the sub and super phases. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f95d620-35b7-4b47-a3b4-9e33d5525b50", + "metadata": {}, + "outputs": [], + "source": [ + "sld_4 = Material(sld=4.0, isld=0, name='Sld 4')\n", + "sld_4_layer = Layer(material=sld_4, thickness=100, roughness=0, name='SLD 4 Layer')\n", + "\n", + "sld_8 = Material(sld=8.0, isld=0, name='Sld 8')\n", + "sld_8_layer = Layer(material=sld_8, thickness=150, roughness=0, name='SLD 8 Layer')\n", + "\n", + "vacuum = Material(sld=0, isld=0, name='Vacuum')\n", + "superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase')\n", + "\n", + "si = Material(sld=2.047, isld=0, name='Si')\n", + "subphase = Layer(material=si, thickness=0, roughness=0, name='Si Subphase')" + ] + }, + { + "cell_type": "markdown", + "id": "f63ec440-089f-46cf-8ff5-be5012ad8dc8", + "metadata": {}, + "source": [ + "We then create a model for the two layered structures." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2af8c30b", + "metadata": {}, + "outputs": [], + "source": [ + "two_layers = Multilayer([sld_4_layer, sld_8_layer], name='SLD 4/8 Layer')\n", + "sample = Sample(superphase, two_layers, subphase, name='Two Layer Sample')\n", + "model = Model(\n", + " sample=sample,\n", + " scale=1,\n", + " background=0,\n", + " name='Two Layer Model',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "a1d3ad93", + "metadata": {}, + "source": [ + "We also need a Refl1d sample" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0259cd0", + "metadata": {}, + "outputs": [], + "source": [ + "refl1d_sld_4 = refl1d.names.SLD(name=\"Sld 4\", rho=4.0, irho=0)\n", + "refl1d_sld_8 = refl1d.names.SLD(name=\"Sld 8\", rho=8.0, irho=0)\n", + "refl1d_vacuum = refl1d.names.SLD(name=\"Vacuum\", rho=0, irho=0)\n", + "refl1d_si = refl1d.names.SLD(name=\"Si\", rho=2.047, irho=0)\n", + "\n", + "# Refl1d model is inverted as compared to EasyReflectometry, so the order of the layers is reversed\n", + "refl1d_sample = (\n", + " refl1d_si(0, 0) | \n", + " refl1d_sld_8(150, 0) |\n", + " refl1d_sld_4(100, 0) | \n", + " refl1d_vacuum(0, 0)\n", + ") " + ] + }, + { + "cell_type": "markdown", + "id": "8f0581b9-7690-4b17-9a4f-766ed92aaba2", + "metadata": {}, + "source": [ + "## Prepare interface factory\n", + "\n", + "We will use the [Ref1d](https://refl1d.readthedocs.io/en/latest/) and [Refnx](https://refnx.readthedocs.io/) calculator for our analysis. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1500603-d85d-4e16-b697-e1bf16502991", + "metadata": {}, + "outputs": [], + "source": [ + "interface = CalculatorFactory()" + ] + }, + { + "cell_type": "markdown", + "id": "0481604e-3973-4e5d-a0ee-2f5915461d71", + "metadata": {}, + "source": [ + "## Comparisons\n", + "To validate the implementation we do some comparisons of the reflectevity determined in EasyReflectometry using different calculators or directly in Refl1d." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18010202", + "metadata": {}, + "outputs": [], + "source": [ + "model_coords = np.linspace(\n", + " start=0.001,\n", + " stop=0.3,\n", + " num=1000,\n", + ")\n", + "\n", + "def plot_apply_makeup():\n", + " ax = plt.gca()\n", + " ax.set_xlim([-0.01, 0.35])\n", + " ax.set_ylim([1e-8, 2.5])\n", + " plt.legend()\n", + " plt.yscale('log')\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "61bae570", + "metadata": {}, + "source": [ + "### Refl1d and EasyReflectometry without magnetism\n", + "First we will ensure that the Refl1d calculator is correctly implemented in EasyReflectometry when no magnetic effects are present." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cdf959c8", + "metadata": {}, + "outputs": [], + "source": [ + "# Refl1d\n", + "probe = refl1d.names.QProbe(\n", + " Q=model_coords,\n", + " dQ=np.zeros(len(model_coords)),\n", + " intensity=1,\n", + " background=0,\n", + " )\n", + "experiment = refl1d.names.Experiment(probe=probe, sample=refl1d_sample)\n", + "model_data_no_magnetism_ref1d_raw = experiment.reflectivity()[1]\n", + "\n", + "plt.plot(model_coords, model_data_no_magnetism_ref1d_raw, '-k', label='Refl1d', linewidth=4)\n", + "\n", + "# EasyReflectometry\n", + "interface.switch('refl1d')\n", + "model.interface = interface\n", + "model.resolution_function = PercentageFhwm(0)\n", + "model_interface = model.interface()\n", + "model_interface.magnetism = False\n", + "model_data_no_magnetism_ref1d_easy = model.interface().fit_func(\n", + " model_coords,\n", + " model.unique_name,\n", + ")\n", + "plt.plot(model_coords, model_data_no_magnetism_ref1d_easy, 'r-', label=f'EasyReflectometry ({model_interface.name})', linewidth=2)\n", + "\n", + "plot_apply_makeup()" + ] + }, + { + "cell_type": "markdown", + "id": "60d1896d-06ca-4bd2-b44c-d3788304220c", + "metadata": {}, + "source": [ + "### EasyReflectometry with and without magnetic layers\n", + "We have now reached the point where we can do sa simulation for a sample with magnetic layers. For this sample we should see a difference in the determined reflectivity." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf311973", + "metadata": {}, + "outputs": [], + "source": [ + "# Without magnetic layers\n", + "interface.switch('refl1d')\n", + "model.interface = interface\n", + "model_interface = model.interface()\n", + "model_interface.include_magnetism = True\n", + "model_data_magnetism = model.interface().fit_func(\n", + " model_coords,\n", + " model.unique_name,\n", + ")\n", + "plt.plot(model_coords, model_data_magnetism, '-k', label=f'Without magnetic layers ({model_interface.name})', linewidth=4)\n", + "\n", + "# With magnetic layers\n", + "interface.switch('refl1d')\n", + "model.interface = interface\n", + "model_interface = model.interface()\n", + "model_interface.include_magnetism = True\n", + "model_interface._wrapper.update_layer(list(model_interface._wrapper.storage['layer'].keys())[1], magnetism_rhoM=10, magnetism_thetaM=70)\n", + "model_interface._wrapper.update_layer(list(model_interface._wrapper.storage['layer'].keys())[2], magnetism_rhoM=5, magnetism_thetaM=175)\n", + "model_data_magnetism_layer_1 = model.interface().fit_func(\n", + " model_coords,\n", + " model.unique_name,\n", + ")\n", + "plt.plot(model_coords, model_data_magnetism_layer_1, 'r-', label=f'With magnetic layers ({model_interface.name})', linewidth=2)\n", + "\n", + "plot_apply_makeup()" + ] + }, + { + "cell_type": "markdown", + "id": "e3659a17", + "metadata": {}, + "source": [ + "As expected do we see a difference in the reflectivity profile for a sample with and without magnetic layers." + ] + }, + { + "cell_type": "markdown", + "id": "8320f6e8", + "metadata": {}, + "source": [ + "### EasyReflectometry and Refl1d with magnetism\n", + "The final comparison is to confirm that that we are able to reproduce the raw Refl1d reflectometry in EasyReflectometry when acocunting for magnetism." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18cb7037", + "metadata": {}, + "outputs": [], + "source": [ + "# Refl1d model is inverted as compared to EasyReflectometry, so the order of the layers is reversed\n", + "refl1d_sample = (\n", + " refl1d_si(0, 0) | \n", + " refl1d_sld_8(150, 0, magnetism=refl1d.names.Magnetism(rhoM=5, thetaM=175)) |\n", + " refl1d_sld_4(100, 0, magnetism=refl1d.names.Magnetism(rhoM=10, thetaM=70)) | \n", + " refl1d_vacuum(0, 0)\n", + ") \n", + "probe = refl1d.names.QProbe(\n", + " Q=model_coords,\n", + " dQ=np.zeros(len(model_coords)),\n", + " intensity=1,\n", + " background=0,\n", + " )\n", + "\n", + "four_probes = [probe, None, None, None]\n", + "polarized_probe = refl1d.names.PolarizedQProbe(xs=four_probes, name='polarized')\n", + "experiment = refl1d.names.Experiment(probe=polarized_probe, sample=refl1d_sample)\n", + "model_data_magnetism_ref1d = experiment.reflectivity()[0][1]\n", + "plt.plot(model_coords, model_data_magnetism_ref1d, '-k', label='Refl1d', linewidth=4)\n", + "\n", + "# EasyReflectometry\n", + "interface.switch('refl1d')\n", + "model.interface = interface\n", + "model_interface = model.interface()\n", + "model_interface.include_magnetism = True\n", + "model_interface._wrapper.update_layer(list(model_interface._wrapper.storage['layer'].keys())[1], magnetism_rhoM=10, magnetism_thetaM=70)\n", + "model_interface._wrapper.update_layer(list(model_interface._wrapper.storage['layer'].keys())[2], magnetism_rhoM=5, magnetism_thetaM=175)\n", + "model_data_magnetism_easy = model.interface().fit_func(\n", + " model_coords,\n", + " model.unique_name,\n", + ")\n", + "plt.plot(model_coords, model_data_magnetism_easy, 'r-', label=f'EasyReflect ({model_interface.name})', linewidth=2)\n", + "\n", + "plot_apply_makeup()" + ] + }, + { + "cell_type": "markdown", + "id": "d52efccc", + "metadata": {}, + "source": [ + "The two models agree when the magnetic parameters are the same." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7033f755", + "metadata": {}, + "outputs": [], + "source": [ + "print(max(abs(model_data_magnetism_easy - model_data_magnetism_ref1d)))" + ] + }, + { + "cell_type": "markdown", + "id": "971baee6", + "metadata": {}, + "source": [ + "# Afterthoughts \n", + "Just for completion we will do a few additional calculations and comparisons of reflectivity." + ] + }, + { + "cell_type": "markdown", + "id": "7af84a69", + "metadata": {}, + "source": [ + "## Refl1d polarized probe for a single layer sample\n", + " This study is done with magnetism to show the results for polarized probe." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "352c35e9", + "metadata": {}, + "outputs": [], + "source": [ + "# The magnetism is set to 8. \n", + "# This would double (pp) and cancel out (mm) the magnitude of the reflectivity oscillations when its angle is set to 90.\n", + "# This would give the strongest spin-flipping (pm and mp) when its angle is set to 0.\n", + "# However we set the angle to 45, so the reflectivity oscillations are not doubled or cancelled out, and the spin-flipping is not maximized.\n", + "refl1d_sample = (\n", + " refl1d_si(0, 0) | \n", + " refl1d_sld_8(150, 0, magnetism=refl1d.names.Magnetism(rhoM=8, thetaM=45)) |\n", + " refl1d_vacuum(0, 0)\n", + ") \n", + "\n", + "probe_pp = refl1d.names.QProbe(\n", + " Q=model_coords,\n", + " dQ=np.zeros(len(model_coords)),\n", + " intensity=1,\n", + " background=0,\n", + " )\n", + "probe_pm = refl1d.names.QProbe(\n", + " Q=model_coords,\n", + " dQ=np.zeros(len(model_coords)),\n", + " intensity=1,\n", + " background=0,\n", + " )\n", + "probe_mp = refl1d.names.QProbe(\n", + " Q=model_coords,\n", + " dQ=np.zeros(len(model_coords)),\n", + " intensity=1,\n", + " background=0,\n", + " )\n", + "probe_mm = refl1d.names.QProbe(\n", + " Q=model_coords,\n", + " dQ=np.zeros(len(model_coords)),\n", + " intensity=1,\n", + " background=0,\n", + " )\n", + "\n", + "four_probes = [probe_pp, probe_pm, probe_mp, probe_mm]\n", + "polarized_probe = refl1d.names.PolarizedQProbe(xs=four_probes, name='polarized')\n", + "experiment = refl1d.names.Experiment(probe=polarized_probe, sample=refl1d_sample)\n", + "model_data_magnetism_ref1d_raw_pp = experiment.reflectivity()[0][1]\n", + "model_data_magnetism_ref1d_raw_pm = experiment.reflectivity()[1][1]\n", + "model_data_magnetism_ref1d_raw_mp = experiment.reflectivity()[2][1]\n", + "model_data_magnetism_ref1d_raw_mm = experiment.reflectivity()[3][1]\n", + "\n", + "plt.plot(model_coords, model_data_magnetism_ref1d_raw_pp, '-k', label='Refl1d pp', linewidth=4)\n", + "plt.plot(model_coords, model_data_magnetism_ref1d_raw_mm, '-r', label='Refl1d mm', linewidth=2)\n", + "plt.plot(model_coords, model_data_magnetism_ref1d_raw_pm, ':k', label='Refl1d pm', linewidth=4)\n", + "plt.plot(model_coords, model_data_magnetism_ref1d_raw_mp, ':r', label='Refl1d mp', linewidth=2)\n", + "\n", + "plot_apply_makeup()" + ] + }, + { + "cell_type": "markdown", + "id": "ac52936c", + "metadata": {}, + "source": [ + "## Refl1 and Refnx in EasyReflectometry.\n", + "This study is done without magnetism as Refnx does not support this yet." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e59d3153-f0da-4fce-a4f0-a424010acbec", + "metadata": {}, + "outputs": [], + "source": [ + "# Refnx\n", + "interface.switch('refnx')\n", + "model.interface = interface\n", + "model_interface = model.interface()\n", + "model_data_no_magnetism_refnx = model.interface().fit_func(\n", + " model_coords,\n", + " model.unique_name,\n", + ")\n", + "plt.plot(model_coords, model_data_no_magnetism_refnx, 'k-', label=f'EasyReflectometry ({model_interface.name})', linewidth=5)\n", + "\n", + "# Refl1d\n", + "interface.switch('refl1d')\n", + "model.interface = interface\n", + "model_interface = model.interface()\n", + "model_data_no_magnetism_ref1d = model.interface().fit_func(\n", + " model_coords,\n", + " model.unique_name,\n", + ")\n", + "plt.plot(model_coords, model_data_no_magnetism_ref1d, 'r-', label=f'EasyReflectometry ({model_interface.name})', linewidth=2)\n", + "\n", + "plot_apply_makeup()" + ] + }, + { + "cell_type": "markdown", + "id": "97e3094a", + "metadata": {}, + "source": [ + "## EasyReflectometry with and without magnetism but no magnetic layers\n", + "We also want to confirm that we can enable the ability to account for magnetism without causing any significant changes to the reflectivity as determined for a sample without any magnetic layers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b087e848", + "metadata": {}, + "outputs": [], + "source": [ + "# With Magnitism\n", + "interface.switch('refl1d')\n", + "model.interface = interface\n", + "model_interface = model.interface()\n", + "model_interface.magnetism = True\n", + "model_data_magnetism = model.interface().fit_func(\n", + " model_coords,\n", + " model.unique_name,\n", + ")\n", + "plt.plot(model_coords, model_data_magnetism, '-k', label=f'With magnetism ({model_interface.name})', linewidth=4)\n", + "\n", + "# Without Magnitism\n", + "interface.switch('refl1d')\n", + "model.interface = interface\n", + "model_interface = model.interface()\n", + "model_interface.magnetism = False\n", + "model_data_no_magnetism = model.interface().fit_func(\n", + " model_coords,\n", + " model.unique_name,\n", + ")\n", + "plt.plot(model_coords, model_data_no_magnetism, 'r-', label=f'Without magnetism ({model_interface.name})', linewidth=2)\n", + "\n", + "plot_apply_makeup()" + ] + }, + { + "cell_type": "markdown", + "id": "d1b41ed2", + "metadata": {}, + "source": [ + "We don't see any significant change in the determined reflectivity when enabling the ability to account for magnetism. However, there is a small difference, which is due to the fact that we are using `PolarizedQProbe` (Refl1d) when handling magnetic samples whereas non-magnetic samples are handled with a `QProbe` (Refl1d)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00c25554", + "metadata": {}, + "outputs": [], + "source": [ + "print(max(abs(model_data_no_magnetism - model_data_magnetism)))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "erl_311", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/src/tutorials/sample/example.ort b/docs/src/tutorials/sample/example.ort deleted file mode 100644 index 968fa7bb..00000000 --- a/docs/src/tutorials/sample/example.ort +++ /dev/null @@ -1,448 +0,0 @@ -# # ORSO reflectivity data file | 0.1 standard | YAML encoding | https://www.reflectometry.org/ -# data_source: -# owner: -# name: Andrew Nelson -# affiliation: ANSTO -# contact: Andrew.Nelson@ansto.gov.au -# experiment: -# facility: ANSTO -# start_date: 2021-05-12 -# title: Example data file from refnx docs -# instrument: platypus -# probe: neutron -# sample: -# name: Polymer Film -# category: solid / liquid -# composition: Si / SiO2 / Film / D2O -# measurement: -# instrument_settings: -# wavelength: -# magnitude: 12 -# unit: angstrom -# incident_angle: -# magnitude: 3 -# unit: deg -# data_files: -# - Unknown.nxs -# scheme: angle-dispersive -# reduction: -# software: ess -# timestamp: 2022-01-27T15:33:59+01:00 -# corrections: -# - footprint -# - incident intensity -# - detector efficiency -# columns: -# - {name: Qz, unit: 1/angstrom, dimension: WW transfer} -# - {name: R, dimension: reflectivity} -# - {name: sR, dimension: error-reflectivity} -# - {name: sQz, unit: 1/angstrom, dimension: resolution-WW transfer} -## Qz RQz sR sQz -8.060220000e-03 7.095810000e-01 8.506760000e-02 1.407419648e-04 -8.136620000e-03 8.622810000e-01 1.123700000e-01 1.420996057e-04 -8.263750000e-03 9.086470000e-01 7.900470000e-02 1.443588017e-04 -8.370670000e-03 7.732920000e-01 7.927280000e-02 1.462583099e-04 -8.450330000e-03 1.057970000e+00 1.259590000e-01 1.476732801e-04 -8.530830000e-03 1.015660000e+00 1.132950000e-01 1.491031133e-04 -8.612170000e-03 7.347170000e-01 6.115660000e-02 1.505473850e-04 -8.694370000e-03 7.692160000e-01 6.170580000e-02 1.520069445e-04 -8.777430000e-03 1.115740000e+00 1.127300000e-01 1.534813672e-04 -8.861360000e-03 9.723030000e-01 8.971600000e-02 1.549710776e-04 -8.946160000e-03 7.512140000e-01 5.493930000e-02 1.564760759e-04 -9.031850000e-03 7.976490000e-01 5.671220000e-02 1.579963619e-04 -9.118440000e-03 9.221890000e-01 6.858410000e-02 1.595327850e-04 -9.205930000e-03 9.757550000e-01 7.293950000e-02 1.610844959e-04 -9.294320000e-03 8.195040000e-01 5.216170000e-02 1.626523440e-04 -9.383640000e-03 7.883200000e-01 4.794730000e-02 1.642367538e-04 -9.473890000e-03 7.947010000e-01 4.602240000e-02 1.658368761e-04 -9.565080000e-03 8.744000000e-01 5.151640000e-02 1.674535601e-04 -9.657210000e-03 8.396620000e-01 4.742850000e-02 1.690872306e-04 -9.750300000e-03 8.008720000e-01 4.399580000e-02 1.707370382e-04 -9.844360000e-03 1.117100000e+00 7.373300000e-02 1.724042569e-04 -9.939390000e-03 8.884110000e-01 4.954100000e-02 1.740884620e-04 -1.003540000e-02 7.791290000e-01 3.898730000e-02 1.757896536e-04 -1.013240000e-02 7.999680000e-01 3.899740000e-02 1.775086809e-04 -1.023040000e-02 8.431240000e-01 4.159800000e-02 1.792451193e-04 -1.032940000e-02 9.613320000e-01 4.925360000e-02 1.809989689e-04 -1.048680000e-02 8.805440000e-01 2.990830000e-02 1.837851690e-04 -1.063270000e-02 7.557350000e-01 3.208510000e-02 1.863700799e-04 -1.073590000e-02 9.712310000e-01 4.534850000e-02 1.881969711e-04 -1.084010000e-02 8.955490000e-01 3.905420000e-02 1.900425474e-04 -1.094540000e-02 8.625890000e-01 3.580720000e-02 1.919068087e-04 -1.105180000e-02 8.909920000e-01 3.612570000e-02 1.937906045e-04 -1.115930000e-02 9.003480000e-01 3.679240000e-02 1.956930853e-04 -1.126790000e-02 8.459270000e-01 3.218810000e-02 1.976151006e-04 -1.137760000e-02 9.431520000e-01 3.655330000e-02 1.995570749e-04 -1.148840000e-02 9.956310000e-01 3.901160000e-02 2.015185836e-04 -1.160040000e-02 9.695940000e-01 3.636230000e-02 2.034996267e-04 -1.171350000e-02 9.051810000e-01 3.204100000e-02 2.055014781e-04 -1.182780000e-02 8.933810000e-01 3.061190000e-02 2.075232887e-04 -1.194330000e-02 9.196020000e-01 3.146770000e-02 2.095659076e-04 -1.205990000e-02 9.189980000e-01 3.060970000e-02 2.116293349e-04 -1.217770000e-02 7.810560000e-01 2.355420000e-02 2.137135706e-04 -1.229680000e-02 8.649150000e-01 2.720250000e-02 2.158190394e-04 -1.241700000e-02 8.435160000e-01 2.550150000e-02 2.179457412e-04 -1.253850000e-02 9.984180000e-01 3.197220000e-02 2.200945253e-04 -1.266120000e-02 8.812600000e-01 2.602730000e-02 2.222645425e-04 -1.278520000e-02 8.835690000e-01 2.558620000e-02 2.244570667e-04 -1.291050000e-02 9.376700000e-01 2.746510000e-02 2.266716733e-04 -1.303700000e-02 1.019200000e+00 3.051070000e-02 2.289087870e-04 -1.316480000e-02 8.455260000e-01 2.262690000e-02 2.311684076e-04 -1.329390000e-02 8.738040000e-01 2.324680000e-02 2.334509599e-04 -1.342430000e-02 8.659530000e-01 2.249060000e-02 2.357568686e-04 -1.355610000e-02 8.779820000e-01 2.244910000e-02 2.380861337e-04 -1.368920000e-02 9.475450000e-01 2.489850000e-02 2.404387551e-04 -1.382370000e-02 8.881540000e-01 2.209620000e-02 2.428155821e-04 -1.395950000e-02 8.913620000e-01 2.181480000e-02 2.452161902e-04 -1.409670000e-02 8.884560000e-01 2.150920000e-02 2.476410039e-04 -1.423530000e-02 9.137170000e-01 2.225860000e-02 2.500908727e-04 -1.437530000e-02 8.103640000e-01 1.882890000e-02 2.525649471e-04 -1.451680000e-02 7.385480000e-01 1.688270000e-02 2.550645011e-04 -1.465970000e-02 6.865100000e-01 1.597420000e-02 2.575891102e-04 -1.480400000e-02 5.822400000e-01 1.350210000e-02 2.601396235e-04 -1.494980000e-02 4.468550000e-01 1.006260000e-02 2.627156166e-04 -1.509700000e-02 3.924610000e-01 9.155490000e-03 2.653175139e-04 -1.524580000e-02 3.205170000e-01 7.319590000e-03 2.679461649e-04 -1.539610000e-02 2.810060000e-01 6.399090000e-03 2.706011448e-04 -1.554790000e-02 2.401000000e-01 5.442390000e-03 2.732828784e-04 -1.570120000e-02 2.208810000e-01 5.024370000e-03 2.759917903e-04 -1.585610000e-02 1.920330000e-01 4.314410000e-03 2.787278805e-04 -1.601260000e-02 1.798490000e-01 4.051590000e-03 2.814919983e-04 -1.617070000e-02 1.600690000e-01 3.562020000e-03 2.842837190e-04 -1.633030000e-02 1.531290000e-01 3.467770000e-03 2.871038920e-04 -1.649160000e-02 1.342200000e-01 3.016000000e-03 2.899525174e-04 -1.665450000e-02 1.283300000e-01 2.888530000e-03 2.928300196e-04 -1.681900000e-02 1.247940000e-01 2.861820000e-03 2.957363988e-04 -1.698530000e-02 1.091270000e-01 2.483100000e-03 2.986720796e-04 -1.715320000e-02 1.044290000e-01 2.353920000e-03 3.016374867e-04 -1.732280000e-02 9.468300000e-02 2.091620000e-03 3.046326200e-04 -1.758810000e-02 8.969110000e-02 1.439380000e-03 3.093191777e-04 -1.784190000e-02 8.091440000e-02 1.872780000e-03 3.138010489e-04 -1.801850000e-02 7.465440000e-02 1.714220000e-03 3.169189092e-04 -1.819680000e-02 7.036610000e-02 1.634230000e-03 3.200686191e-04 -1.837700000e-02 6.904450000e-02 1.605300000e-03 3.232497539e-04 -1.855890000e-02 6.270550000e-02 1.461460000e-03 3.264631629e-04 -1.874270000e-02 5.939150000e-02 1.400070000e-03 3.297088462e-04 -1.892840000e-02 5.754770000e-02 1.360950000e-03 3.329872283e-04 -1.911590000e-02 5.138330000e-02 1.226680000e-03 3.362987340e-04 -1.930540000e-02 4.922670000e-02 1.178240000e-03 3.396437879e-04 -1.949670000e-02 4.521740000e-02 1.079620000e-03 3.430219654e-04 -1.969000000e-02 4.245560000e-02 1.022250000e-03 3.464349651e-04 -1.988520000e-02 4.126130000e-02 9.947810000e-04 3.498819376e-04 -2.008240000e-02 3.523330000e-02 8.777900000e-04 3.533637323e-04 -2.028150000e-02 3.352710000e-02 8.367470000e-04 3.568803492e-04 -2.048270000e-02 3.326840000e-02 8.339590000e-04 3.604326376e-04 -2.068590000e-02 3.166440000e-02 7.898860000e-04 3.640210222e-04 -2.089120000e-02 2.916000000e-02 7.496130000e-04 3.676450784e-04 -2.109850000e-02 2.652010000e-02 7.063830000e-04 3.713060800e-04 -2.130800000e-02 2.518290000e-02 6.713890000e-04 3.750036024e-04 -2.151950000e-02 2.387570000e-02 6.426930000e-04 3.787384951e-04 -2.173310000e-02 2.289290000e-02 6.330680000e-04 3.825111825e-04 -2.194900000e-02 2.086460000e-02 5.931280000e-04 3.863216648e-04 -2.216690000e-02 2.087710000e-02 5.920100000e-04 3.901703665e-04 -2.238710000e-02 1.822280000e-02 5.292050000e-04 3.940581370e-04 -2.260950000e-02 1.773460000e-02 5.251520000e-04 3.979849764e-04 -2.283410000e-02 1.587140000e-02 4.818770000e-04 4.019513092e-04 -2.306100000e-02 1.432550000e-02 4.516190000e-04 4.059575601e-04 -2.329020000e-02 1.427760000e-02 4.522490000e-04 4.100041538e-04 -2.352170000e-02 1.266240000e-02 4.198450000e-04 4.140919397e-04 -2.375550000e-02 1.221280000e-02 4.137860000e-04 4.182204929e-04 -2.399170000e-02 1.046080000e-02 3.745320000e-04 4.223906630e-04 -2.423020000e-02 1.061330000e-02 3.810520000e-04 4.266016005e-04 -2.447120000e-02 9.879030000e-03 3.641280000e-04 4.308567027e-04 -2.471450000e-02 8.372030000e-03 3.313930000e-04 4.351542710e-04 -2.496030000e-02 7.670480000e-03 3.077050000e-04 4.394943054e-04 -2.520860000e-02 7.344890000e-03 3.049780000e-04 4.438810525e-04 -2.545940000e-02 6.798650000e-03 2.898680000e-04 4.483102657e-04 -2.571270000e-02 5.916300000e-03 2.671780000e-04 4.527819450e-04 -2.596850000e-02 5.344980000e-03 2.515360000e-04 4.573003369e-04 -2.622690000e-02 5.122650000e-03 2.474120000e-04 4.618611950e-04 -2.648800000e-02 4.750310000e-03 2.379530000e-04 4.664730124e-04 -2.675160000e-02 4.307150000e-03 2.238500000e-04 4.711272958e-04 -2.701790000e-02 4.018170000e-03 2.200510000e-04 4.758325386e-04 -2.728680000e-02 3.539150000e-03 2.046530000e-04 4.805802475e-04 -2.755850000e-02 3.818190000e-03 2.079440000e-04 4.853789156e-04 -2.783290000e-02 2.864750000e-03 1.819210000e-04 4.902285431e-04 -2.811000000e-02 2.795800000e-03 1.766910000e-04 4.951206367e-04 -2.839000000e-02 2.621500000e-03 1.750020000e-04 5.000679362e-04 -2.867270000e-02 2.484770000e-03 1.694560000e-04 5.050619484e-04 -2.895830000e-02 2.420090000e-03 1.709250000e-04 5.101069199e-04 -2.924670000e-02 2.359260000e-03 1.700600000e-04 5.151986041e-04 -2.953810000e-02 1.978560000e-03 1.600210000e-04 5.203454942e-04 -2.983230000e-02 1.947200000e-03 1.582820000e-04 5.255475902e-04 -3.012960000e-02 1.735930000e-03 1.527310000e-04 5.307963989e-04 -3.042980000e-02 1.894590000e-03 1.603050000e-04 5.361004136e-04 -3.073300000e-02 1.696680000e-03 1.525740000e-04 5.414596341e-04 -3.103920000e-02 1.793690000e-03 1.592450000e-04 5.468698140e-04 -3.134860000e-02 1.786860000e-03 1.552020000e-04 5.523351998e-04 -3.166100000e-02 1.872010000e-03 1.583950000e-04 5.578557915e-04 -3.197660000e-02 1.688180000e-03 1.499840000e-04 5.634315891e-04 -3.229530000e-02 1.732370000e-03 1.543250000e-04 5.690625926e-04 -3.261730000e-02 1.537600000e-03 1.453960000e-04 5.747530487e-04 -3.294240000e-02 1.541340000e-03 1.497560000e-04 5.804987107e-04 -3.327080000e-02 1.700330000e-03 1.572080000e-04 5.863038252e-04 -3.360260000e-02 2.142240000e-03 1.728870000e-04 5.921683922e-04 -3.393760000e-02 1.944020000e-03 1.686890000e-04 5.980924118e-04 -3.422050000e-02 1.914000000e-03 1.077140000e-04 6.019483327e-04 -3.466450000e-02 1.986410000e-03 1.438280000e-04 6.103523719e-04 -3.502190000e-02 1.849740000e-03 1.279740000e-04 6.165099550e-04 -3.538500000e-02 1.932640000e-03 1.227080000e-04 6.227354838e-04 -3.571620000e-02 2.188990000e-03 1.495980000e-04 6.288506008e-04 -3.607730000e-02 2.314320000e-03 1.503450000e-04 6.351568151e-04 -3.646770000e-02 1.831770000e-03 1.041590000e-04 6.416328939e-04 -3.682630000e-02 1.797150000e-03 1.012920000e-04 6.480155472e-04 -3.716160000e-02 2.278740000e-03 1.417330000e-04 6.543599810e-04 -3.754210000e-02 2.183390000e-03 1.294600000e-04 6.609379784e-04 -3.794040000e-02 1.799000000e-03 9.624980000e-05 6.676221409e-04 -3.831280000e-02 1.828080000e-03 9.734640000e-05 6.742680840e-04 -3.868400000e-02 1.949850000e-03 1.082600000e-04 6.809692330e-04 -3.906920000e-02 2.042140000e-03 1.122450000e-04 6.877680540e-04 -3.947340000e-02 1.659460000e-03 8.395540000e-05 6.946772869e-04 -3.986330000e-02 1.620310000e-03 8.073380000e-05 7.016035062e-04 -4.026580000e-02 1.588620000e-03 7.617690000e-05 7.086189042e-04 -4.066080000e-02 1.582510000e-03 7.829120000e-05 7.156810150e-04 -4.107010000e-02 1.429900000e-03 7.034450000e-05 7.228323046e-04 -4.147810000e-02 1.336120000e-03 6.498820000e-05 7.300472933e-04 -4.187440000e-02 1.721900000e-03 9.200060000e-05 7.373174879e-04 -4.230490000e-02 1.404890000e-03 6.788170000e-05 7.446980943e-04 -4.273170000e-02 1.146480000e-03 5.315150000e-05 7.521381533e-04 -4.315460000e-02 1.030130000e-03 4.872800000e-05 7.596504046e-04 -4.358350000e-02 1.018640000e-03 4.851630000e-05 7.672348483e-04 -4.401550000e-02 1.060250000e-03 5.148880000e-05 7.748999775e-04 -4.445490000e-02 9.115610000e-04 4.517670000e-05 7.826415457e-04 -4.489830000e-02 7.795360000e-04 3.746760000e-05 7.904553063e-04 -4.534700000e-02 5.957570000e-04 2.867780000e-05 7.983539991e-04 -4.579470000e-02 6.476450000e-04 3.286690000e-05 8.063333774e-04 -4.625120000e-02 5.268390000e-04 2.666210000e-05 8.143891946e-04 -4.671240000e-02 4.365460000e-04 2.240950000e-05 8.225299441e-04 -4.717830000e-02 3.696050000e-04 1.929650000e-05 8.307471325e-04 -4.764690000e-02 3.315640000e-04 1.808550000e-05 8.390577463e-04 -4.812080000e-02 2.545840000e-04 1.440620000e-05 8.474405525e-04 -4.859940000e-02 2.090800000e-04 1.312430000e-05 8.559167841e-04 -4.908280000e-02 1.929370000e-04 1.261590000e-05 8.644779478e-04 -4.957100000e-02 1.309220000e-04 9.992220000e-06 8.731240437e-04 -5.006400000e-02 1.098230000e-04 9.080510000e-06 8.818635651e-04 -5.056190000e-02 9.565480000e-05 8.370090000e-06 8.906880186e-04 -5.106550000e-02 7.925720000e-05 7.699130000e-06 8.995974043e-04 -5.157340000e-02 7.956050000e-05 6.898560000e-06 9.086044619e-04 -5.208640000e-02 7.224950000e-05 6.102730000e-06 9.177006984e-04 -5.260450000e-02 7.825130000e-05 6.391360000e-06 9.268903603e-04 -5.312790000e-02 8.637120000e-05 6.561860000e-06 9.361734476e-04 -5.365650000e-02 1.331620000e-04 8.789840000e-06 9.455499603e-04 -5.419050000e-02 1.436320000e-04 8.178840000e-06 9.550241449e-04 -5.472980000e-02 1.586820000e-04 8.565340000e-06 9.645917550e-04 -5.527450000e-02 1.900070000e-04 9.755240000e-06 9.742612837e-04 -5.582470000e-02 2.354200000e-04 1.141720000e-05 9.840284844e-04 -5.638040000e-02 2.339320000e-04 1.034520000e-05 9.938933571e-04 -5.694170000e-02 2.433900000e-04 1.059550000e-05 1.003855902e-03 -5.750870000e-02 2.708580000e-04 1.125130000e-05 1.013924612e-03 -5.808130000e-02 3.109660000e-04 1.233550000e-05 1.024095240e-03 -5.865970000e-02 3.483270000e-04 1.357860000e-05 1.034367788e-03 -5.924390000e-02 3.329570000e-04 1.275590000e-05 1.044750747e-03 -5.983400000e-02 3.546590000e-04 1.322360000e-05 1.055235624e-03 -6.043000000e-02 3.544670000e-04 1.306610000e-05 1.065826667e-03 -6.103190000e-02 3.855150000e-04 1.408930000e-05 1.076523875e-03 -6.164000000e-02 3.325090000e-04 1.201760000e-05 1.087335742e-03 -6.225410000e-02 3.330870000e-04 1.197150000e-05 1.098253773e-03 -6.287440000e-02 3.303070000e-04 1.197230000e-05 1.109286464e-03 -6.350090000e-02 3.224690000e-04 1.162910000e-05 1.120433812e-03 -6.413370000e-02 2.744620000e-04 9.948790000e-06 1.131691573e-03 -6.477280000e-02 2.792430000e-04 1.025370000e-05 1.143063992e-03 -6.541840000e-02 2.389380000e-04 8.960850000e-06 1.154555315e-03 -6.607040000e-02 2.330280000e-04 8.726650000e-06 1.166165544e-03 -6.672900000e-02 1.863430000e-04 7.236140000e-06 1.177890432e-03 -6.739420000e-02 1.649490000e-04 6.619500000e-06 1.189738471e-03 -6.806600000e-02 1.332440000e-04 5.541570000e-06 1.201709662e-03 -6.874460000e-02 1.153760000e-04 4.971620000e-06 1.213799758e-03 -6.943000000e-02 8.250330000e-05 3.951150000e-06 1.226017252e-03 -7.012230000e-02 6.719230000e-05 3.570180000e-06 1.238357898e-03 -7.082150000e-02 5.033220000e-05 2.926230000e-06 1.250825942e-03 -7.152780000e-02 3.336120000e-05 2.509700000e-06 1.263421384e-03 -7.224110000e-02 2.290520000e-05 2.116380000e-06 1.276148471e-03 -7.293380000e-02 1.879440000e-05 1.860530000e-06 1.288068702e-03 -7.366830000e-02 1.553440000e-05 1.798210000e-06 1.301292643e-03 -7.440150000e-02 1.824520000e-05 1.951840000e-06 1.314359459e-03 -7.514050000e-02 2.235620000e-05 1.801000000e-06 1.327506960e-03 -7.587210000e-02 2.773620000e-05 1.890140000e-06 1.340293500e-03 -7.664020000e-02 3.931010000e-05 2.300950000e-06 1.354192651e-03 -7.739640000e-02 4.596690000e-05 2.332400000e-06 1.367582209e-03 -7.810610000e-02 5.541310000e-05 2.459260000e-06 1.379328330e-03 -7.886820000e-02 6.726050000e-05 2.729570000e-06 1.392692408e-03 -7.979530000e-02 7.650640000e-05 2.773090000e-06 1.409143772e-03 -8.072890000e-02 7.794720000e-05 2.857410000e-06 1.425518696e-03 -8.153340000e-02 8.505420000e-05 2.990400000e-06 1.439910454e-03 -8.233180000e-02 9.685690000e-05 3.222700000e-06 1.454408377e-03 -8.316050000e-02 9.462400000e-05 3.166210000e-06 1.469122877e-03 -8.402520000e-02 8.585640000e-05 2.824460000e-06 1.484058201e-03 -8.486480000e-02 8.101440000e-05 2.662540000e-06 1.499057224e-03 -8.573420000e-02 7.655920000e-05 2.549780000e-06 1.514268578e-03 -8.655530000e-02 7.385430000e-05 2.485840000e-06 1.529496917e-03 -8.743720000e-02 6.249710000e-05 2.190920000e-06 1.545009780e-03 -8.831630000e-02 5.495430000e-05 2.001270000e-06 1.560658534e-03 -8.911080000e-02 5.694540000e-05 2.081580000e-06 1.576247836e-03 -9.004160000e-02 4.229200000e-05 1.729710000e-06 1.592304265e-03 -9.095460000e-02 3.367540000e-05 1.458910000e-06 1.608462612e-03 -9.185040000e-02 2.545640000e-05 1.249630000e-06 1.624727124e-03 -9.274350000e-02 2.092210000e-05 1.159760000e-06 1.641140268e-03 -9.362890000e-02 1.531120000e-05 1.060140000e-06 1.657689303e-03 -9.455270000e-02 1.265650000e-05 9.577530000e-07 1.674480395e-03 -9.551780000e-02 9.331240000e-06 8.621390000e-07 1.691509297e-03 -9.647710000e-02 8.901520000e-06 7.828970000e-07 1.708691077e-03 -9.741890000e-02 1.079150000e-05 8.938470000e-07 1.725991763e-03 -9.842590000e-02 1.213680000e-05 9.113080000e-07 1.743589710e-03 -9.942010000e-02 1.540800000e-05 9.077770000e-07 1.761323549e-03 -1.004250000e-01 1.938030000e-05 9.602530000e-07 1.779248486e-03 -1.014340000e-01 2.091160000e-05 1.040890000e-06 1.797347534e-03 -1.024990000e-01 2.519250000e-05 1.078370000e-06 1.815722611e-03 -1.034980000e-01 2.818800000e-05 1.132170000e-06 1.834144401e-03 -1.045460000e-01 2.998100000e-05 1.178530000e-06 1.852837973e-03 -1.056080000e-01 3.056280000e-05 1.150490000e-06 1.871735383e-03 -1.066820000e-01 2.819030000e-05 1.099070000e-06 1.890832384e-03 -1.077580000e-01 2.692630000e-05 1.037400000e-06 1.910111989e-03 -1.088310000e-01 2.541920000e-05 1.012560000e-06 1.929574198e-03 -1.099160000e-01 2.282700000e-05 9.367100000e-07 1.949244491e-03 -1.110400000e-01 1.769170000e-05 7.904500000e-07 1.969169580e-03 -1.121170000e-01 1.547510000e-05 7.625400000e-07 1.989209328e-03 -1.132440000e-01 1.172580000e-05 6.688050000e-07 2.009529352e-03 -1.143190000e-01 1.050270000e-05 6.598050000e-07 2.029959788e-03 -1.154640000e-01 8.121530000e-06 5.847030000e-07 2.050708720e-03 -1.166450000e-01 4.988840000e-06 5.267190000e-07 2.071720941e-03 -1.177620000e-01 4.909990000e-06 5.360960000e-07 2.092830835e-03 -1.189250000e-01 5.304340000e-06 5.240840000e-07 2.114229497e-03 -1.201290000e-01 5.689050000e-06 4.912340000e-07 2.135895696e-03 -1.213460000e-01 6.444710000e-06 5.061070000e-07 2.157799706e-03 -1.225860000e-01 7.456190000e-06 5.058030000e-07 2.179954265e-03 -1.238230000e-01 8.248420000e-06 5.025600000e-07 2.202325401e-03 -1.250910000e-01 1.067490000e-05 5.845390000e-07 2.224964074e-03 -1.263610000e-01 1.131620000e-05 5.722280000e-07 2.247827816e-03 -1.276430000e-01 1.104400000e-05 5.762550000e-07 2.270937863e-03 -1.289320000e-01 1.062580000e-05 5.655340000e-07 2.294294212e-03 -1.302100000e-01 9.337660000e-06 5.254720000e-07 2.317858646e-03 -1.315470000e-01 8.922800000e-06 5.000680000e-07 2.341754314e-03 -1.328540000e-01 6.436110000e-06 4.446880000e-07 2.365841081e-03 -1.341620000e-01 6.181130000e-06 4.556250000e-07 2.390178397e-03 -1.355150000e-01 4.685680000e-06 4.034780000e-07 2.414821469e-03 -1.368240000e-01 4.667540000e-06 3.809290000e-07 2.439659885e-03 -1.382040000e-01 4.262500000e-06 3.763010000e-07 2.464842276e-03 -1.395890000e-01 3.884300000e-06 3.571580000e-07 2.490292204e-03 -1.409890000e-01 3.699480000e-06 3.796620000e-07 2.516022408e-03 -1.424300000e-01 3.659930000e-06 3.543380000e-07 2.542066861e-03 -1.438510000e-01 4.559390000e-06 3.665690000e-07 2.568357617e-03 -1.452630000e-01 4.580880000e-06 3.470620000e-07 2.594911663e-03 -1.467500000e-01 4.829290000e-06 3.503920000e-07 2.621826671e-03 -1.482250000e-01 4.930920000e-06 3.533300000e-07 2.649009215e-03 -1.497100000e-01 4.781000000e-06 3.612560000e-07 2.676489022e-03 -1.512250000e-01 4.644840000e-06 3.397110000e-07 2.704291571e-03 -1.527360000e-01 4.365080000e-06 3.324590000e-07 2.732374397e-03 -1.542610000e-01 3.807060000e-06 3.253780000e-07 2.760775718e-03 -1.558250000e-01 3.544150000e-06 3.053880000e-07 2.789516767e-03 -1.573660000e-01 2.632690000e-06 2.773550000e-07 2.818538093e-03 -1.589410000e-01 2.129680000e-06 2.570610000e-07 2.847903395e-03 -1.605110000e-01 2.509080000e-06 2.684170000e-07 2.877574452e-03 -1.621370000e-01 2.606260000e-06 2.683540000e-07 2.907627704e-03 -1.637550000e-01 2.467960000e-06 2.618110000e-07 2.937986711e-03 -1.654040000e-01 2.437690000e-06 2.457080000e-07 2.968710928e-03 -1.670350000e-01 2.818460000e-06 2.626350000e-07 2.999740899e-03 -1.687360000e-01 3.201910000e-06 2.677220000e-07 3.031182793e-03 -1.704100000e-01 3.020960000e-06 2.548660000e-07 3.062934688e-03 -1.721190000e-01 2.398550000e-06 2.324640000e-07 3.095064532e-03 -1.738390000e-01 2.458240000e-06 2.385460000e-07 3.127555337e-03 -1.755740000e-01 2.033660000e-06 2.231960000e-07 3.160419844e-03 -1.773360000e-01 1.684040000e-06 2.048280000e-07 3.193666546e-03 -1.790870000e-01 1.293830000e-06 2.007650000e-07 3.227265717e-03 -1.809070000e-01 1.439670000e-06 1.882070000e-07 3.261306534e-03 -1.827150000e-01 1.540200000e-06 1.999980000e-07 3.295708314e-03 -1.845350000e-01 1.338490000e-06 1.864820000e-07 3.330500781e-03 -1.863830000e-01 1.615990000e-06 1.863690000e-07 3.365705170e-03 -1.882470000e-01 1.493470000e-06 1.883860000e-07 3.401317233e-03 -1.901260000e-01 2.059680000e-06 1.920700000e-07 3.437345464e-03 -1.920380000e-01 1.690150000e-06 1.767700000e-07 3.473806849e-03 -1.939540000e-01 1.488370000e-06 1.792740000e-07 3.510675908e-03 -1.959000000e-01 1.403820000e-06 1.747280000e-07 3.547990861e-03 -1.978600000e-01 1.346890000e-06 1.705690000e-07 3.585734722e-03 -1.998450000e-01 9.477100000e-07 1.617790000e-07 3.623937217e-03 -2.018330000e-01 1.089550000e-06 1.735020000e-07 3.662568619e-03 -2.038630000e-01 1.246350000e-06 1.654660000e-07 3.701684134e-03 -2.059040000e-01 1.227370000e-06 1.644900000e-07 3.741254037e-03 -2.079590000e-01 1.168960000e-06 1.584560000e-07 3.781286820e-03 -2.100420000e-01 1.161720000e-06 1.521980000e-07 3.821807963e-03 -2.121400000e-01 1.216890000e-06 1.588540000e-07 3.862808973e-03 -2.142570000e-01 1.317570000e-06 1.546070000e-07 3.904298343e-03 -2.163990000e-01 1.032790000e-06 1.472920000e-07 3.946293059e-03 -2.185700000e-01 1.030660000e-06 1.514730000e-07 3.988805862e-03 -2.207810000e-01 5.945350000e-07 1.511720000e-07 4.031853738e-03 -2.229880000e-01 7.279960000e-07 1.429280000e-07 4.075394220e-03 -2.252160000e-01 7.806310000e-07 1.485250000e-07 4.119465528e-03 -2.274670000e-01 1.063390000e-06 1.499300000e-07 4.164076155e-03 -2.297410000e-01 6.526970000e-07 1.296680000e-07 4.209234596e-03 -2.320370000e-01 1.074380000e-06 1.484530000e-07 4.254932355e-03 -2.343560000e-01 8.687630000e-07 1.468620000e-07 4.301220393e-03 -2.366980000e-01 9.218680000e-07 1.450820000e-07 4.348060490e-03 -2.390640000e-01 6.471490000e-07 1.368090000e-07 4.395495113e-03 -2.414540000e-01 5.363930000e-07 1.356880000e-07 4.443524261e-03 -2.438670000e-01 6.337170000e-07 1.304030000e-07 4.492147934e-03 -2.463040000e-01 6.292700000e-07 1.365570000e-07 4.541408598e-03 -2.487660000e-01 6.332920000e-07 1.224530000e-07 4.591263788e-03 -2.512530000e-01 1.037050000e-06 1.354090000e-07 4.641713503e-03 -2.537640000e-01 8.252860000e-07 1.431610000e-07 4.692842675e-03 -2.563010000e-01 6.268250000e-07 1.192570000e-07 4.744608839e-03 -2.588630000e-01 5.252590000e-07 1.227410000e-07 4.797054460e-03 -2.614500000e-01 5.218320000e-07 1.309180000e-07 4.850137073e-03 -2.640640000e-01 3.916590000e-07 1.213700000e-07 4.903941609e-03 -2.667030000e-01 4.724390000e-07 1.399730000e-07 4.958425602e-03 -2.693690000e-01 5.595360000e-07 1.391510000e-07 5.013589053e-03 -2.720620000e-01 6.640700000e-07 1.448670000e-07 5.069474428e-03 -2.747820000e-01 4.593780000e-07 1.514660000e-07 5.126081726e-03 -2.775290000e-01 3.669610000e-07 1.435460000e-07 5.183453413e-03 -2.803030000e-01 5.315310000e-07 1.483080000e-07 5.241589490e-03 -2.831050000e-01 4.289140000e-07 1.361450000e-07 5.300447491e-03 -2.859350000e-01 5.520060000e-07 1.419830000e-07 5.360112348e-03 -2.887930000e-01 5.702640000e-07 1.529000000e-07 5.420541594e-03 -2.916800000e-01 5.047310000e-07 1.364290000e-07 5.481777696e-03 -2.945960000e-01 6.619230000e-07 1.407930000e-07 5.543863119e-03 -2.975410000e-01 7.601320000e-07 1.607210000e-07 5.606755399e-03 -3.005160000e-01 4.685270000e-07 1.348870000e-07 5.670454534e-03 -3.035200000e-01 4.428600000e-07 1.471340000e-07 5.735087923e-03 -3.065540000e-01 4.899790000e-07 1.404300000e-07 5.800528167e-03 -3.096190000e-01 3.601630000e-07 1.337280000e-07 5.866902666e-03 -3.127140000e-01 2.905630000e-07 1.480620000e-07 5.934168953e-03 -3.158410000e-01 5.199630000e-07 1.515620000e-07 6.002327027e-03 -3.189980000e-01 4.666650000e-07 1.562560000e-07 6.071461822e-03 -3.221870000e-01 3.888830000e-07 1.570110000e-07 6.141530870e-03 -3.254080000e-01 4.467780000e-07 1.555120000e-07 6.212576639e-03 -3.286620000e-01 3.238850000e-07 1.525860000e-07 6.284599127e-03 -3.319470000e-01 3.787360000e-07 1.581800000e-07 6.357640802e-03 -3.352660000e-01 3.881990000e-07 1.539110000e-07 6.431701663e-03 -3.386180000e-01 4.428770000e-07 1.490980000e-07 6.506824176e-03 -3.420030000e-01 2.477870000e-07 1.463210000e-07 6.582965876e-03 -3.454230000e-01 2.857690000e-07 1.351350000e-07 6.660211693e-03 -3.488760000e-01 4.759640000e-07 1.536640000e-07 6.738561630e-03 -3.523640000e-01 4.496390000e-07 1.539560000e-07 6.818015684e-03 -3.558870000e-01 2.471470000e-07 1.443220000e-07 6.898573857e-03 -3.594450000e-01 2.167640000e-07 1.491810000e-07 6.980363546e-03 -3.630390000e-01 5.205850000e-07 1.573690000e-07 7.063257354e-03 -3.666690000e-01 5.246970000e-07 1.545410000e-07 7.147425144e-03 -3.703350000e-01 3.644030000e-07 1.550240000e-07 7.232781985e-03 -3.740370000e-01 4.597520000e-07 1.653080000e-07 7.319370343e-03 -3.777770000e-01 5.359220000e-07 1.820430000e-07 7.407232683e-03 -3.815540000e-01 4.268070000e-07 1.770030000e-07 7.496411472e-03 -3.853690000e-01 4.036660000e-07 1.811100000e-07 7.586906710e-03 -3.892220000e-01 2.554540000e-07 1.597290000e-07 7.678718396e-03 -3.931130000e-01 9.269720000e-08 1.654020000e-07 7.771931464e-03 -3.970440000e-01 1.106720000e-07 1.808880000e-07 7.866503446e-03 -4.010140000e-01 4.521630000e-07 1.731910000e-07 7.962519276e-03 -4.050230000e-01 3.780660000e-07 1.515970000e-07 8.059978953e-03 -4.090730000e-01 3.091360000e-07 1.571950000e-07 8.158924942e-03 -4.131630000e-01 3.417740000e-07 1.448180000e-07 8.259357245e-03 -4.172940000e-01 3.449240000e-07 1.595810000e-07 8.361318327e-03 -4.214660000e-01 2.518400000e-07 1.597570000e-07 8.464850655e-03 -4.256800000e-01 4.017370000e-07 1.538140000e-07 8.569954228e-03 -4.299360000e-01 3.172790000e-07 1.691300000e-07 8.676713978e-03 -4.342350000e-01 5.506310000e-07 1.611420000e-07 8.785087440e-03 -4.385770000e-01 5.085100000e-07 1.649900000e-07 8.895159545e-03 -4.429620000e-01 6.025930000e-07 1.738350000e-07 9.006930294e-03 -4.473910000e-01 4.384540000e-07 1.653500000e-07 9.120484618e-03 -4.518650000e-01 3.387570000e-07 1.876390000e-07 9.235822519e-03 -4.563830000e-01 4.358460000e-07 1.978260000e-07 9.352943995e-03 -4.609460000e-01 3.855790000e-07 1.761430000e-07 9.471933979e-03 -4.655550000e-01 3.834150000e-07 1.884540000e-07 9.592834938e-03 diff --git a/docs/src/tutorials/sample/material_solvated.ipynb b/docs/src/tutorials/sample/material_solvated.ipynb index 2cad6851..39197d94 100644 --- a/docs/src/tutorials/sample/material_solvated.ipynb +++ b/docs/src/tutorials/sample/material_solvated.ipynb @@ -34,8 +34,11 @@ "\n", "import numpy as np\n", "import scipp as sc\n", - "import easyreflectometry\n", + "import pooch\n", "import refnx\n", + "\n", + "import easyreflectometry\n", + "\n", "from easyreflectometry.data import load\n", "from easyreflectometry.sample import Layer\n", "from easyreflectometry.sample import Sample\n", @@ -77,7 +80,7 @@ "source": [ "For information about the data being read in and the details of the model see [the previous tutorial](../fitting/simple_fitting.rst). \n", "We will gloss over these details here.\n", - "Use link to [download](example.ort) the ort data." + "We use `pooch` to fetch the file from the repository." ] }, { @@ -87,7 +90,12 @@ "metadata": {}, "outputs": [], "source": [ - "data = load('example.ort')" + "file_path = pooch.retrieve(\n", + " # URL to one of Pooch's test files\n", + " url=\"https://raw.githubusercontent.com/EasyScience/EasyReflectometryLib/master/docs/src/tutorials/fitting/example.ort\",\n", + " known_hash=\"82d0c95c069092279a799a8131ad3710335f601d9f1080754b387f42e407dfab\",\n", + ")\n", + "data = load(file_path)" ] }, { diff --git a/docs/src/tutorials/sample/monolayer.ipynb b/docs/src/tutorials/sample/monolayer.ipynb index 837117e1..514673f3 100644 --- a/docs/src/tutorials/sample/monolayer.ipynb +++ b/docs/src/tutorials/sample/monolayer.ipynb @@ -30,8 +30,10 @@ "%matplotlib inline\n", "\n", "import refnx\n", + "import pooch\n", "\n", "import easyreflectometry\n", + "\n", "from easyreflectometry.calculators import CalculatorFactory\n", "from easyreflectometry.data import load\n", "from easyreflectometry.plot import plot\n", @@ -44,7 +46,7 @@ "from easyreflectometry.experiment import PercentageFhwm\n", "from easyreflectometry.fitting import Fitter\n", "from easyreflectometry.plot import plot\n", - "\n" + "from easyscience.fitting import AvailableMinimizers\n" ] }, { @@ -75,7 +77,7 @@ "\n", "As has been [shown previously](../fitting/simple_fitting.rst), we use the `load` function to read in our experimental data. \n", "For this tutorial we will be looking at [DSPC](https://en.wikipedia.org/wiki/Distearoylphosphatidylcholine), a phospholipid molecule that will self-assemble into a monolayer at the air-water interface. \n", - "The data being used has kindly been shared by the authors of [previous work on the system](#hollinshead2009). " + "The data being used has kindly been shared by the authors of [previous work on the system](#hollinshead2009). We use `pooch` to fetch the file from our repository." ] }, { @@ -85,7 +87,12 @@ "metadata": {}, "outputs": [], "source": [ - "data = load('d70d2o.ort')\n", + "file_path = pooch.retrieve(\n", + " # URL to one of Pooch's test files\n", + " url=\"https://raw.githubusercontent.com/EasyScience/EasyReflectometryLib/master/docs/src/tutorials/sample/d70d2o.ort\",\n", + " known_hash=\"d877793b3c415834791b2c114dceb12f4564c083b2e7bf51b7a9f289a58165a2\",\n", + ")\n", + "data = load(file_path)\n", "plot(data)" ] }, @@ -371,7 +378,8 @@ "calculator = CalculatorFactory()\n", "model.interface = calculator\n", "fitter = Fitter(model)\n", - "analysed = fitter.fit(data, method='differential_evolution')" + "fitter.switch_minimizer(AvailableMinimizers.LMFit_differential_evolution)\n", + "analysed = fitter.fit(data)" ] }, { @@ -464,8 +472,22 @@ } ], "metadata": { + "kernelspec": { + "display_name": "erl_1_311", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } }, "nbformat": 4, diff --git a/docs/src/tutorials/sample/multi_contrast.ipynb b/docs/src/tutorials/sample/multi_contrast.ipynb index 72035055..fab42c39 100644 --- a/docs/src/tutorials/sample/multi_contrast.ipynb +++ b/docs/src/tutorials/sample/multi_contrast.ipynb @@ -29,8 +29,11 @@ "source": [ "%matplotlib inline\n", "\n", - "import easyreflectometry\n", "import refnx\n", + "import pooch\n", + "\n", + "import easyreflectometry\n", + "\n", "from easyreflectometry.data import load\n", "from easyreflectometry.plot import plot\n", "from easyreflectometry.sample import Material\n", @@ -42,6 +45,8 @@ "from easyreflectometry.experiment import PercentageFhwm\n", "from easyreflectometry.calculators import CalculatorFactory\n", "from easyreflectometry.fitting import Fitter\n", + "from easyscience.fitting import AvailableMinimizers\n", + "\n", "print(f'easyreflectometry: {easyreflectometry.__version__}')\n", "print(f'refnx: {refnx.__version__}')" ] @@ -55,7 +60,7 @@ "\n", "We load in three different isotopic contrast that are stored in a single `.ort` file. \n", "This `.ort` file uses the [mutliple data set syntax](https://github.com/reflectivity/file_format/blob/master/specification.md#multiple-data-sets) to indicate that different measurements are present in a single file.\n", - "Use link to [download](multiple.ort) the ort data." + "We use `pooch` to fetch the file from the repository." ] }, { @@ -65,7 +70,12 @@ "metadata": {}, "outputs": [], "source": [ - "data = load('multiple.ort')" + "file_path = pooch.retrieve(\n", + " # URL to one of Pooch's test files\n", + " url=\"https://raw.githubusercontent.com/EasyScience/EasyReflectometryLib/master/docs/src/tutorials/sample/multiple.ort\",\n", + " known_hash=\"241bcb819cdae47fbbb310a99c2456c7332312719496b936a153dc7dee83e62c\",\n", + ")\n", + "data = load(file_path)" ] }, { @@ -366,7 +376,7 @@ "metadata": {}, "outputs": [], "source": [ - "d13d2o.head_layer.thickness.raw_value, d70d2o.head_layer.thickness.raw_value, d83acmw.head_layer.thickness.raw_value" + "d13d2o.head_layer.thickness.value, d70d2o.head_layer.thickness.value, d83acmw.head_layer.thickness.value" ] }, { @@ -386,7 +396,7 @@ "metadata": {}, "outputs": [], "source": [ - "d13d2o.head_layer.thickness.raw_value, d70d2o.head_layer.thickness.raw_value, d83acmw.head_layer.thickness.raw_value" + "d13d2o.head_layer.thickness.value, d70d2o.head_layer.thickness.value, d83acmw.head_layer.thickness.value" ] }, { @@ -501,7 +511,8 @@ "metadata": {}, "outputs": [], "source": [ - "f = Fitter(d13d2o_model, d70d2o_model, d83acmw_model)" + "fitter = Fitter(d13d2o_model, d70d2o_model, d83acmw_model)\n", + "fitter.switch_minimizer(AvailableMinimizers.LMFit_scipy_least_squares)" ] }, { @@ -511,7 +522,7 @@ "metadata": {}, "outputs": [], "source": [ - "analysed = f.fit(data)" + "analysed = fitter.fit(data)" ] }, { @@ -544,8 +555,22 @@ } ], "metadata": { + "kernelspec": { + "display_name": "erl_1_311", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } }, "nbformat": 4, diff --git a/docs/src/tutorials/sample/resolution_functions.ipynb b/docs/src/tutorials/sample/resolution_functions.ipynb index 555f2aff..63f3d7c7 100644 --- a/docs/src/tutorials/sample/resolution_functions.ipynb +++ b/docs/src/tutorials/sample/resolution_functions.ipynb @@ -36,10 +36,10 @@ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import scipp as sc\n", - "\n", + "import refnx\n", + "import pooch\n", "\n", "import easyreflectometry\n", - "import refnx\n", "\n", "from easyreflectometry.calculators import CalculatorFactory\n", "from easyreflectometry.data import load\n", @@ -71,7 +71,7 @@ "print(f'numpy: {np.__version__}')\n", "print(f'scipp: {sc.__version__}')\n", "print(f'easyreflectometry: {easyreflectometry.__version__}')\n", - "print(f'Refl1D: {refnx.__version__}')" + "print(f'refnx: {refnx.__version__}')" ] }, { @@ -84,7 +84,7 @@ "The data that we will investigate in this tutorial was generated with `Refnx` and are stored in `.ort` [format file](https://github.com/reflectivity/file_format/blob/master/specification.md) files. In this tutorial we are investigation how we can include resolution effects when simulating and reproducing data measured in an experiment. For an `.ort` file the resoultion data for reflectivity is stored in the fourth column.\n", "\n", "IMPORTANT when using `easyreflectometry` functionality for loading an `.ort` file we store the resolution data as a variance (squared value). As a consequence one needs to take the squareroot of the loaded data to recover the raw values (fourth column).\n", - "Use links to [download 0.0](mod_pointwise_two_layer_sample_dq-0.0.ort), [download 1.0](mod_pointwise_two_layer_sample_dq-1.0.ort), and [download 10.0](mod_pointwise_two_layer_sample_dq-10.0.ort) ort data." + "We use `pooch` to fetch the file from the repository." ] }, { @@ -94,10 +94,25 @@ "metadata": {}, "outputs": [], "source": [ + "file_path_0 = pooch.retrieve(\n", + " # URL to one of Pooch's test files\n", + " url=\"https://raw.githubusercontent.com/EasyScience/EasyReflectometryLib/master/docs/src/tutorials/sample/mod_pointwise_two_layer_sample_dq-0.0.ort\",\n", + " known_hash=\"f8a3e7007b83f0de4e2c761134e7d1c55027f0099528bd56f746b50349369f50\",\n", + ")\n", + "file_path_1 = pooch.retrieve(\n", + " # URL to one of Pooch's test files\n", + " url=\"https://raw.githubusercontent.com/EasyScience/EasyReflectometryLib/master/docs/src/tutorials/sample/mod_pointwise_two_layer_sample_dq-1.0.ort\",\n", + " known_hash=\"9d81a512cbe45f923806ad307e476b27535614b2e08a2bf0f4559ab608a34f7a\",\n", + ")\n", + "file_path_10 = pooch.retrieve(\n", + " # URL to one of Pooch's test files\n", + " url=\"https://raw.githubusercontent.com/EasyScience/EasyReflectometryLib/master/docs/src/tutorials/sample/mod_pointwise_two_layer_sample_dq-10.0.ort\",\n", + " known_hash=\"991395c0b6a91bf60c12d234c645143dcac1cab929944fc4e452020d44b787ad\",\n", + ")\n", "dict_reference = {}\n", - "dict_reference['0'] = load(\"mod_pointwise_two_layer_sample_dq-0.0.ort\")\n", - "dict_reference['1'] = load(\"mod_pointwise_two_layer_sample_dq-1.0.ort\")\n", - "dict_reference['10'] = load('mod_pointwise_two_layer_sample_dq-10.0.ort')" + "dict_reference['0'] = load(file_path_0)\n", + "dict_reference['1'] = load(file_path_1)\n", + "dict_reference['10'] = load(file_path_10)" ] }, { @@ -293,7 +308,7 @@ " model.resolution_function = resolution_function_dict[key]\n", " model_data = model.interface().fit_func(\n", " model_coords,\n", - " model.uid,\n", + " model.unique_name,\n", " )\n", " plt.plot(model_coords, model_data, 'k-', label=f'Resolution: {key}%')\n", " plt.plot(reference_coords, reference_data, 'rx', label=f'Reference')\n", @@ -343,14 +358,14 @@ "model.resolution_function = resolution_function_dict[key]\n", "model_data = model.interface().fit_func(\n", " model_coords,\n", - " model.uid,\n", + " model.unique_name,\n", ")\n", "plt.plot(model_coords, model_data, 'k-', label=f'Variable', linewidth=5)\n", "\n", "model.resolution_function = PercentageFhwm(1.0)\n", "model_data = model.interface().fit_func(\n", " model_coords,\n", - " model.uid,\n", + " model.unique_name,\n", ")\n", "plt.plot(model_coords, model_data, 'r-', label=f'Percentage')\n", "\n", diff --git a/pyproject.toml b/pyproject.toml index 45ca59eb..1bb784ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,11 +29,12 @@ classifiers = [ ] requires-python = ">=3.9,<3.12" dependencies = [ - "easyscience>=0.6.0", + "easyscience>=1.0.1", "scipp>=23.12.0", "refnx>=0.1.15", "refl1d>=0.8.14", - "orsopy>=0.0.4" + "orsopy>=0.0.4", + "pint==0.23" # Only to ensure that unit is reported as dimensionless rather than empty string ] [project.optional-dependencies] @@ -47,6 +48,7 @@ dev = [ "jupyter>=1.0.0", "jupyterlab", "plopp", + "pooch", "pytest>=5.2", "pytest-cov>=3.0.0", "ruff", @@ -94,7 +96,7 @@ exclude = [ [tool.ruff.format] quote-style = "single" -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] # allow asserts in test files "*test_*.py" = ["S101"] diff --git a/src/easyreflectometry/__version__.py b/src/easyreflectometry/__version__.py index a6221b3d..1a72d32e 100644 --- a/src/easyreflectometry/__version__.py +++ b/src/easyreflectometry/__version__.py @@ -1 +1 @@ -__version__ = '1.0.2' +__version__ = '1.1.0' diff --git a/src/easyreflectometry/calculators/bornagain/calculator.py b/src/easyreflectometry/calculators/bornagain/calculator.py index b5ab44d5..3a17caef 100644 --- a/src/easyreflectometry/calculators/bornagain/calculator.py +++ b/src/easyreflectometry/calculators/bornagain/calculator.py @@ -1,12 +1,13 @@ __author__ = 'github.com/arm61' import numpy as np +from easyscience.Objects.Inferface import ItemContainer + from easyreflectometry.experiment import Model from easyreflectometry.sample import Layer from easyreflectometry.sample import Material from easyreflectometry.sample import MaterialMixture from easyreflectometry.sample import Multilayer -from easyscience.Objects.Inferface import ItemContainer from ..calculator_base import CalculatorBase from .wrapper import BornAgainWrapper @@ -66,7 +67,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta r_list = [] t_ = type(model) if issubclass(t_, Material): - key = model.uid + key = model.unique_name if key not in self._wrapper.storage['material'].keys(): self._wrapper.create_material(key) r_list.append( @@ -78,7 +79,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta ) ) elif issubclass(t_, MaterialMixture): - key = model.uid + key = model.unique_name if key not in self._wrapper.storage['material'].keys(): self._wrapper.create_material(key) r_list.append( @@ -90,7 +91,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta ) ) elif issubclass(t_, Layer): - key = model.uid + key = model.unique_name if key not in self._wrapper.storage['layer'].keys(): self._wrapper.create_layer(key) r_list.append( @@ -101,9 +102,9 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta self._wrapper.update_layer, ) ) - self.assign_material_to_layer(model.material.uid, key) + self.assign_material_to_layer(model.material.unique_name, key) elif issubclass(t_, Multilayer): - key = model.uid + key = model.unique_name self._wrapper.create_item(key) r_list.append( ItemContainer( @@ -114,7 +115,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta ) ) for i in model.layers: - self.add_layer_to_item(i.uid, model.uid) + self.add_layer_to_item(i.unique_name, model.unique_name) elif issubclass(t_, Model): self._wrapper.create_model() r_list.append( @@ -126,7 +127,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta ) ) for i in model.structure: - self.add_item_to_model(i.uid) + self.add_item_to_model(i.unique_name) return r_list def assign_material_to_layer(self, material_id: int, layer_id: int) -> None: diff --git a/src/easyreflectometry/calculators/calculator_base.py b/src/easyreflectometry/calculators/calculator_base.py index 36fb4da9..122e3cbc 100644 --- a/src/easyreflectometry/calculators/calculator_base.py +++ b/src/easyreflectometry/calculators/calculator_base.py @@ -54,7 +54,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta r_list = [] t_ = type(model) if issubclass(t_, Material): - key = model.uid + key = model.unique_name if key not in self._wrapper.storage['material'].keys(): self._wrapper.create_material(key) r_list.append( @@ -66,7 +66,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta ) ) elif issubclass(t_, MaterialMixture): - key = model.uid + key = model.unique_name if key not in self._wrapper.storage['material'].keys(): self._wrapper.create_material(key) r_list.append( @@ -78,7 +78,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta ) ) elif issubclass(t_, Layer): - key = model.uid + key = model.unique_name if key not in self._wrapper.storage['layer'].keys(): self._wrapper.create_layer(key) r_list.append( @@ -89,9 +89,9 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta self._wrapper.update_layer, ) ) - self.assign_material_to_layer(model.material.uid, key) + self.assign_material_to_layer(model.material.unique_name, key) elif issubclass(t_, BaseAssembly): - key = model.uid + key = model.unique_name self._wrapper.create_item(key) r_list.append( ItemContainer( @@ -102,9 +102,9 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta ) ) for i in model.layers: - self.add_layer_to_item(i.uid, model.uid) + self.add_layer_to_item(i.unique_name, model.unique_name) elif issubclass(t_, Model): - key = model.uid + key = model.unique_name self._wrapper.create_model(key) r_list.append( ItemContainer( @@ -115,7 +115,7 @@ def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemConta ) ) for i in model.sample: - self.add_item_to_model(i.uid, key) + self.add_item_to_model(i.unique_name, key) return r_list def assign_material_to_layer(self, material_id: str, layer_id: str) -> None: @@ -177,3 +177,16 @@ def sld_profile(self, model_id: str) -> tuple[np.ndarray, np.ndarray]: def set_resolution_function(self, resolution_function: Callable[[np.array], np.array]) -> None: return self._wrapper.set_resolution_function(resolution_function) + + @property + def include_magnetism(self): + return self._wrapper.magnetism + + @include_magnetism.setter + def include_magnetism(self, magnetism: bool): + """ + Set the magnetism flag for the calculator + + :param magnetism: True if the calculator should include magnetism + """ + self._wrapper.magnetism = magnetism diff --git a/src/easyreflectometry/calculators/refl1d/wrapper.py b/src/easyreflectometry/calculators/refl1d/wrapper.py index bed77817..020801b7 100644 --- a/src/easyreflectometry/calculators/refl1d/wrapper.py +++ b/src/easyreflectometry/calculators/refl1d/wrapper.py @@ -3,15 +3,15 @@ from typing import Tuple import numpy as np -from easyreflectometry.experiment.resolution_functions import PercentageFhwm from refl1d import model from refl1d import names +from easyreflectometry.experiment.resolution_functions import PercentageFhwm + from ..wrapper_base import WrapperBase RESOLUTION_PADDING = 3.5 OVERSAMPLING_FACTOR = 21 -MAGNETISM = False ALL_POLARIZATIONS = False @@ -30,8 +30,8 @@ def create_layer(self, name: str): :param name: The name of the layer """ - if MAGNETISM: # A test with hardcoded magnetism in all layers - magnetism = names.Magnetism(rhoM=0.2, thetaM=270) + if self._magnetism: + magnetism = names.Magnetism(rhoM=0.0, thetaM=0.0) else: magnetism = None self.storage['layer'][name] = model.Slab(name=str(name), magnetism=magnetism) @@ -47,17 +47,27 @@ def create_item(self, name: str): ) del self.storage['item'][name].stack[0] - def get_item_value(self, name: str, key: str) -> float: + def update_layer(self, name: str, **kwargs): + """Update a layer in a given item. + + :param name: The layer name. + :param kwargs: """ - A function to get a given item value + kwargs_no_magnetism = {k: v for k, v in kwargs.items() if k != 'magnetism_rhoM' and k != 'magnetism_thetaM'} + super().update_layer(name, **kwargs_no_magnetism) + if any(item.startswith('magnetism') for item in kwargs.keys()): + magnetism = names.Magnetism(rhoM=kwargs['magnetism_rhoM'], thetaM=kwargs['magnetism_thetaM']) + self.storage['layer'][name].magnetism = magnetism - :param name: The item name + def get_layer_value(self, name: str, key: str) -> float: + """A function to get a given layer value + + :param name: The layer name :param key: The given value keys - :return: The desired value """ - item = self.storage['item'][name] - item = getattr(item, key) - return getattr(item, 'value') + if key in ['magnetism_rhoM', 'magnetism_thetaM']: + return getattr(self.storage['layer'][name].magnetism, key.split('_')[-1]) + return super().get_layer_value(name, key) def create_model(self, name: str): """ @@ -151,7 +161,7 @@ def calculate(self, q_array: np.ndarray, model_name: str) -> np.ndarray: # Get percentage of Q and change from sigma to FWHM dq_array = dq_array * q_array / 100 / (2 * np.sqrt(2 * np.log(2))) - if not MAGNETISM: + if not self._magnetism: probe = _get_probe( q_array=q_array, dq_array=dq_array, @@ -159,6 +169,7 @@ def calculate(self, q_array: np.ndarray, model_name: str) -> np.ndarray: storage=self.storage, oversampling_factor=OVERSAMPLING_FACTOR, ) + # returns q, reflectivity _, reflectivity = names.Experiment(probe=probe, sample=sample).reflectivity() else: polarized_probe = _get_polarized_probe( @@ -173,11 +184,14 @@ def calculate(self, q_array: np.ndarray, model_name: str) -> np.ndarray: if ALL_POLARIZATIONS: raise NotImplementedError('Polarized reflectivity not yet implemented') + # returns q, reflectivity # _, reflectivity_pp = polarized_reflectivity[0] # _, reflectivity_pm = polarized_reflectivity[1] # _, reflectivity_mp = polarized_reflectivity[2] # _, reflectivity_mm = polarized_reflectivity[3] else: + # Only pick the pp reflectivity + # returns q, reflectivity _, reflectivity = polarized_reflectivity[0] return reflectivity @@ -236,7 +250,7 @@ def _get_polarized_probe( storage: dict, oversampling_factor: int = 1, all_polarizations: bool = False, -) -> names.QProbe: +) -> names.PolarizedQProbe: four_probes = [] for i in range(4): if i == 0 or all_polarizations: diff --git a/src/easyreflectometry/calculators/refnx/wrapper.py b/src/easyreflectometry/calculators/refnx/wrapper.py index 8d29b82b..3ab62a9b 100644 --- a/src/easyreflectometry/calculators/refnx/wrapper.py +++ b/src/easyreflectometry/calculators/refnx/wrapper.py @@ -3,13 +3,26 @@ from typing import Tuple import numpy as np -from easyreflectometry.experiment.resolution_functions import PercentageFhwm from refnx import reflect +from easyreflectometry.experiment.resolution_functions import PercentageFhwm + from ..wrapper_base import WrapperBase class RefnxWrapper(WrapperBase): + @property + def include_magnetism(self) -> bool: + return self._magnetism + + @include_magnetism.setter + def include_magnetism(self, magnetism: bool) -> None: + """Set the magnetism flag. + + :param magnetism: The magnetism flag + """ + raise NotImplementedError('Magnetism is not supported by refnx') + def create_material(self, name: str): """ Create a material using SLD. diff --git a/src/easyreflectometry/calculators/wrapper_base.py b/src/easyreflectometry/calculators/wrapper_base.py index 37cbadd4..8f9440ee 100644 --- a/src/easyreflectometry/calculators/wrapper_base.py +++ b/src/easyreflectometry/calculators/wrapper_base.py @@ -9,6 +9,7 @@ class WrapperBase: def __init__(self): """Constructor.""" + self._magnetism = False self.storage = { 'material': {}, 'layer': {}, @@ -210,3 +211,15 @@ def set_resolution_function(self, resolution_function: ResolutionFunction) -> No :param resolution_function: The resolution function """ self._resolution_function = resolution_function + + @property + def magnetism(self) -> bool: + return self._magnetism + + @magnetism.setter + def magnetism(self, magnetism: bool) -> None: + """Set the magnetism flag. + + :param magnetism: The magnetism flag + """ + self._magnetism = magnetism diff --git a/src/easyreflectometry/experiment/model.py b/src/easyreflectometry/experiment/model.py index 22d9d0db..681ad2bd 100644 --- a/src/easyreflectometry/experiment/model.py +++ b/src/easyreflectometry/experiment/model.py @@ -2,15 +2,16 @@ __author__ = 'github.com/arm61' +import copy from numbers import Number from typing import Union import numpy as np -import yaml +from easyscience.Objects.new_variable import Parameter from easyscience.Objects.ObjectClasses import BaseObj -from easyscience.Objects.ObjectClasses import Parameter from easyreflectometry.parameter_utils import get_as_parameter +from easyreflectometry.parameter_utils import yaml_dump from easyreflectometry.sample import BaseAssembly from easyreflectometry.sample import Layer from easyreflectometry.sample import LayerCollection @@ -100,7 +101,7 @@ def add_item(self, *assemblies: list[BaseAssembly]) -> None: if issubclass(arg.__class__, BaseAssembly): self.sample.append(arg) if self.interface is not None: - self.interface().add_item_to_model(arg.uid, self.uid) + self.interface().add_item_to_model(arg.unique_name, self.unique_name) else: raise ValueError(f'Object {arg} is not a valid type, must be a child of BaseAssembly.') @@ -115,8 +116,8 @@ def duplicate_item(self, idx: int) -> None: duplicate_layers.append( Layer( material=i.material, - thickness=i.thickness.raw_value, - roughness=i.roughness.raw_value, + thickness=i.thickness.value, + roughness=i.roughness.value, name=i.name + ' duplicate', interface=i.interface, ) @@ -132,9 +133,10 @@ def remove_item(self, idx: int) -> None: :param idx: Index of the item to remove. """ - if self.interface is not None: - self.interface().remove_item_from_model(self.sample[idx].uid, self.uid) + item_unique_name = self.sample[idx].unique_name del self.sample[idx] + if self.interface is not None: + self.interface().remove_item_from_model(item_unique_name, self.unique_name) @property def resolution_function(self) -> ResolutionFunction: @@ -164,11 +166,6 @@ def interface(self, new_interface) -> None: self.generate_bindings() self._interface().set_resolution_function(self._resolution_function) - @property - def uid(self) -> int: - """Return a UID from the borg map.""" - return self._borg.map.convert_id_to_key(self) - # Representation @property def _dict_repr(self) -> dict[str, dict[str, str]]: @@ -181,8 +178,8 @@ def _dict_repr(self) -> dict[str, dict[str, str]]: return { self.name: { - 'scale': self.scale.raw_value, - 'background': self.background.raw_value, + 'scale': float(self.scale.value), + 'background': float(self.background.value), 'resolution': resolution, 'sample': self.sample._dict_repr, } @@ -190,7 +187,7 @@ def _dict_repr(self) -> dict[str, dict[str, str]]: def __repr__(self) -> str: """String representation of the layer.""" - return yaml.dump(self._dict_repr, sort_keys=False) + return yaml_dump(self._dict_repr) def as_dict(self, skip: list = None) -> dict: """Produces a cleaned dict using a custom as_dict method to skip necessary things. @@ -200,26 +197,40 @@ def as_dict(self, skip: list = None) -> dict: """ if skip is None: skip = [] + skip.extend(['sample', 'resolution_function', 'interface']) this_dict = super().as_dict(skip=skip) this_dict['sample'] = self.sample.as_dict(skip=skip) this_dict['resolution_function'] = self.resolution_function.as_dict() + if self.interface is None: + this_dict['interface'] = None + else: + this_dict['interface'] = self.interface().name return this_dict @classmethod - def from_dict(cls, this_dict: dict) -> Model: + def from_dict(cls, passed_dict: dict) -> Model: """ Create a Model from a dictionary. :param this_dict: dictionary of the Model :return: Model """ + # Causes circular import if imported at the top + from easyreflectometry.calculators import CalculatorFactory + + this_dict = copy.deepcopy(passed_dict) resolution_function = ResolutionFunction.from_dict(this_dict['resolution_function']) del this_dict['resolution_function'] - sample = Sample.from_dict(this_dict['sample']) - del this_dict['sample'] + interface_name = this_dict['interface'] + del this_dict['interface'] + if interface_name is not None: + interface = CalculatorFactory() + interface.switch(interface_name) + else: + interface = None model = super().from_dict(this_dict) - model.sample = sample model.resolution_function = resolution_function + model.interface = interface return model diff --git a/src/easyreflectometry/experiment/model_collection.py b/src/easyreflectometry/experiment/model_collection.py index 373bb90e..7f8def28 100644 --- a/src/easyreflectometry/experiment/model_collection.py +++ b/src/easyreflectometry/experiment/model_collection.py @@ -2,6 +2,7 @@ __author__ = 'github.com/arm61' +from typing import List from typing import Optional from easyreflectometry.sample.base_element_collection import SIZE_DEFAULT_COLLECTION @@ -19,10 +20,18 @@ def __init__( *models: Optional[tuple[Model]], name: str = 'EasyModels', interface=None, + populate_if_none: bool = True, **kwargs, ): if not models: - models = [Model(interface=interface) for _ in range(SIZE_DEFAULT_COLLECTION)] + if populate_if_none: + models = [Model(interface=interface) for _ in range(SIZE_DEFAULT_COLLECTION)] + else: + models = [] + # Needed to ensure an empty list is created when saving and instatiating the object as_dict -> from_dict + # Else collisions might occur in global_object.map + self.populate_if_none = False + super().__init__(name, interface, *models, **kwargs) self.interface = interface @@ -42,6 +51,11 @@ def remove_model(self, idx: int): """ del self[idx] + def as_dict(self, skip: List[str] | None = None) -> dict: + this_dict = super().as_dict(skip) + this_dict['populate_if_none'] = self.populate_if_none + return this_dict + @classmethod def from_dict(cls, this_dict: dict) -> ModelCollection: """ @@ -50,11 +64,17 @@ def from_dict(cls, this_dict: dict) -> ModelCollection: :param data: The dictionary for the collection :return: An instance of the collection """ - collection = super().from_dict(this_dict) # type: ModelCollection + collection_dict = this_dict.copy() + # We neeed to call from_dict on the base class to get the models + dict_data = collection_dict['data'] + del collection_dict['data'] + + collection = super().from_dict(collection_dict) # type: ModelCollection + + for model_data in dict_data: + collection.add_model(Model.from_dict(model_data)) if len(collection) != len(this_dict['data']): raise ValueError(f"Expected {len(collection)} models, got {len(this_dict['data'])}") - for i, model_data in enumerate(this_dict['data']): - collection[i] = Model.from_dict(model_data) return collection diff --git a/src/easyreflectometry/fitting.py b/src/easyreflectometry/fitting.py index 13ca1452..92f7794a 100644 --- a/src/easyreflectometry/fitting.py +++ b/src/easyreflectometry/fitting.py @@ -2,7 +2,8 @@ import numpy as np import scipp as sc -from easyscience.Fitting.Fitting import MultiFitter as easyFitter +from easyscience.fitting import AvailableMinimizers +from easyscience.fitting.multi_fitter import MultiFitter as easyFitter from easyreflectometry.experiment import Model @@ -16,18 +17,18 @@ def __init__(self, *args: Model): :param args: Reflectometry model """ - # This lets the uid be passed with the fit_func. - def func_wrapper(func, uid): + # This lets the unique_name be passed with the fit_func. + def func_wrapper(func, unique_name): def wrapped(*args, **kwargs): - return func(*args, uid, **kwargs) + return func(*args, unique_name, **kwargs) return wrapped - self._fit_func = [func_wrapper(m.interface.fit_func, m.uid) for m in args] + self._fit_func = [func_wrapper(m.interface.fit_func, m.unique_name) for m in args] self._models = args self.easy_f = easyFitter(args, self._fit_func) - def fit(self, data: sc.DataGroup, method: str = 'least_squares', id: int = 0) -> sc.DataGroup: + def fit(self, data: sc.DataGroup, id: int = 0) -> sc.DataGroup: """ Perform the fitting and populate the DataGroups with the result. @@ -38,14 +39,14 @@ def fit(self, data: sc.DataGroup, method: str = 'least_squares', id: int = 0) -> x = [data['coords'][f'Qz_{i}'].values for i in refl_nums] y = [data['data'][f'R_{i}'].values for i in refl_nums] dy = [1 / np.sqrt(data['data'][f'R_{i}'].variances) for i in refl_nums] - result = self.easy_f.fit(x, y, weights=dy, method=method) + result = self.easy_f.fit(x, y, weights=dy) new_data = data.copy() for i, _ in enumerate(result): id = refl_nums[i] new_data[f'R_{id}_model'] = sc.array( dims=[f'Qz_{id}'], values=self._fit_func[i](data['coords'][f'Qz_{id}'].values) ) - sld_profile = self.easy_f._fit_objects[i].interface.sld_profile(self._models[i].uid) + sld_profile = self.easy_f._fit_objects[i].interface.sld_profile(self._models[i].unique_name) new_data[f'SLD_{id}'] = sc.array(dims=[f'z_{id}'], values=sld_profile[1] * 1e-6, unit=sc.Unit('1/angstrom') ** 2) new_data['attrs'][f'R_{id}_model'] = {'model': sc.scalar(self._models[i].as_dict())} new_data['coords'][f'z_{id}'] = sc.array( @@ -53,6 +54,14 @@ def fit(self, data: sc.DataGroup, method: str = 'least_squares', id: int = 0) -> ) return new_data + def switch_minimizer(self, minimizer: AvailableMinimizers) -> None: + """ + Switch the minimizer for the fitting. + + :param minimizer: Minimizer to be switched to + """ + self.easy_f.switch_minimizer(minimizer) + def _flatten_list(this_list: list) -> list: """ diff --git a/src/easyreflectometry/parameter_utils.py b/src/easyreflectometry/parameter_utils.py index 3dceea99..e4e1a821 100644 --- a/src/easyreflectometry/parameter_utils.py +++ b/src/easyreflectometry/parameter_utils.py @@ -1,11 +1,19 @@ from copy import deepcopy from numbers import Number +from typing import Optional from typing import Union -from easyscience.Objects.ObjectClasses import Parameter +import yaml +from easyscience import global_object +from easyscience.Objects.new_variable import Parameter -def get_as_parameter(name: str, value: Union[Parameter, Number, None], default_dict: dict) -> Parameter: +def get_as_parameter( + name: str, + value: Union[Parameter, Number, None], + default_dict: dict, + unique_name_prefix: Optional[str] = None, +) -> Parameter: """ This function creates a parameter for the variable `name`. A parameter has a value and metadata. If the value already is a parameter, it is returned. @@ -16,6 +24,10 @@ def get_as_parameter(name: str, value: Union[Parameter, Number, None], default_d param name: The name of the parameter param default_dict: Dictionary with entry for `name` containing the default value and metadata for the parameter """ + # This is a parameter, return it + if isinstance(value, Parameter): + return value + # Ensure we got the dictionary for the parameter with the given name # Should leave the passed dictionary unchanged if name not in default_dict: @@ -23,6 +35,10 @@ def get_as_parameter(name: str, value: Union[Parameter, Number, None], default_d else: parameter_dict = deepcopy(default_dict[name]) + # Add specific unique name prefix if requested + if unique_name_prefix is not None: + parameter_dict['unique_name'] = global_object.generate_unique_name(unique_name_prefix + 'Parameter') + if value is None: # Create a default parameter using both value and metadata from dictionary return Parameter(name, **parameter_dict) @@ -30,6 +46,9 @@ def get_as_parameter(name: str, value: Union[Parameter, Number, None], default_d # Create a parameter using provided value and metadata from dictionary del parameter_dict['value'] return Parameter(name, value, **parameter_dict) - elif not isinstance(value, Parameter): - raise ValueError(f'{name} must be a Parameter, a number, or None.') - return value + + raise ValueError(f'{name} must be a Parameter, a number, or None.') + + +def yaml_dump(dict_repr: dict) -> str: + return yaml.dump(dict_repr, sort_keys=False, allow_unicode=True) diff --git a/src/easyreflectometry/sample/assemblies/base_assembly.py b/src/easyreflectometry/sample/assemblies/base_assembly.py index 1982dc0a..bf408c50 100644 --- a/src/easyreflectometry/sample/assemblies/base_assembly.py +++ b/src/easyreflectometry/sample/assemblies/base_assembly.py @@ -1,7 +1,7 @@ from typing import Any from typing import Optional -from easyscience.Fitting.Constraints import ObjConstraint +from easyscience.fitting.Constraints import ObjConstraint from ..base_core import BaseCore from ..elements.layers.layer import Layer @@ -109,7 +109,10 @@ def _enable_thickness_constraints(self): # Make sure that the thickness parameter is enabled for i in range(len(self.layers)): self.layers[i].thickness.enabled = True - self.front_layer.thickness.value = self.front_layer.thickness.raw_value + # Make sure that the thickness constraint is applied + for i in range(1, len(self.layers)): + self.front_layer.thickness.user_constraints[f'thickness_{i}']() + else: raise Exception('Roughness constraints not setup') @@ -148,7 +151,9 @@ def _enable_roughness_constraints(self): # Make sure that the roughness parameter is enabled for i in range(len(self.layers)): self.layers[i].roughness.enabled = True - self.front_layer.roughness.value = self.front_layer.roughness.raw_value + # Make sure that the roughness constraint is applied + for i in range(1, len(self.layers)): + self.front_layer.roughness.user_constraints[f'roughness_{i}']() else: raise Exception('Roughness constraints not setup') diff --git a/src/easyreflectometry/sample/assemblies/gradient_layer.py b/src/easyreflectometry/sample/assemblies/gradient_layer.py index 61829198..05800a8a 100644 --- a/src/easyreflectometry/sample/assemblies/gradient_layer.py +++ b/src/easyreflectometry/sample/assemblies/gradient_layer.py @@ -1,5 +1,6 @@ from typing import Optional +from easyscience import global_object from numpy import arange from ..elements.layers.layer import Layer @@ -73,7 +74,7 @@ def __init__( @property def thickness(self) -> float: """Get the thickness of the gradient layer in Angstrom.""" - return self.front_layer.thickness.raw_value * self._discretisation_elements + return self.front_layer.thickness.value * self._discretisation_elements @thickness.setter def thickness(self, thickness: float) -> None: @@ -86,7 +87,7 @@ def thickness(self, thickness: float) -> None: @property def roughness(self) -> float: """Get the Roughness of the gradient layer in Angstrom.""" - return self.front_layer.roughness.raw_value + return self.front_layer.roughness.value @roughness.setter def roughness(self, roughness: float) -> None: @@ -100,8 +101,8 @@ def roughness(self, roughness: float) -> None: def _dict_repr(self) -> dict[str, str]: """A simplified dict representation.""" return { - 'thickness': self.thickness, - 'discretisation_elements': self._discretisation_elements, + 'thickness': float(self.thickness), # Conversion to float is necessary to prevent property reference in dict + 'discretisation_elements': int(self._discretisation_elements), # Same as above 'back_layer': self.back_layer._dict_repr, 'front_layer': self.front_layer._dict_repr, } @@ -139,13 +140,13 @@ def _prepare_gradient_layers( interface=None, ) -> LayerCollection: gradient_sld = _linear_gradient( - front_value=front_material.sld.raw_value, - back_value=back_material.sld.raw_value, + front_value=front_material.sld.value, + back_value=back_material.sld.value, discretisation_elements=discretisation_elements, ) gradient_isld = _linear_gradient( - front_value=front_material.isld.raw_value, - back_value=back_material.isld.raw_value, + front_value=front_material.isld.value, + back_value=back_material.isld.value, discretisation_elements=discretisation_elements, ) gradient_layers = [] @@ -157,6 +158,7 @@ def _prepare_gradient_layers( roughness=0.0, name=str(i), interface=interface, + unique_name=global_object.generate_unique_name('GradientLayer'), ) gradient_layers.append(layer) return LayerCollection(gradient_layers) diff --git a/src/easyreflectometry/sample/assemblies/multilayer.py b/src/easyreflectometry/sample/assemblies/multilayer.py index 12accaf2..d57d78b3 100644 --- a/src/easyreflectometry/sample/assemblies/multilayer.py +++ b/src/easyreflectometry/sample/assemblies/multilayer.py @@ -1,9 +1,11 @@ from __future__ import annotations +from typing import Optional from typing import Union +from easyreflectometry.sample.base_element_collection import SIZE_DEFAULT_COLLECTION + from ..elements.layers.layer import Layer -from ..elements.layers.layer_collection import SIZE_DEFAULT_COLLECTION from ..elements.layers.layer_collection import LayerCollection from .base_assembly import BaseAssembly @@ -25,6 +27,7 @@ def __init__( name: str = 'EasyMultilayer', interface=None, type: str = 'Multi-layer', + populate_if_none: Optional[bool] = True, ): """Constructor. @@ -34,11 +37,18 @@ def __init__( :param type: Type of the constructed instance, defaults to 'Multi-layer' """ if layers is None: - layers = LayerCollection() + if populate_if_none: + layers = LayerCollection([Layer(interface=interface) for _ in range(SIZE_DEFAULT_COLLECTION)]) + else: + layers = LayerCollection() elif isinstance(layers, Layer): layers = LayerCollection(layers, name=layers.name) elif isinstance(layers, list): layers = LayerCollection(*layers, name='/'.join([layer.name for layer in layers])) + # Needed to ensure an empty list is created when saving and instatiating the object as_dict -> from_dict + # Else collisions might occur in global_object.map + self.populate_if_none = False + super().__init__(name, layers=layers, type=type, interface=interface) def add_layer(self, *layers: tuple[Layer]) -> None: @@ -50,7 +60,7 @@ def add_layer(self, *layers: tuple[Layer]) -> None: if issubclass(arg.__class__, Layer): self.layers.append(arg) if self.interface is not None: - self.interface().add_layer_to_item(arg.uid, self.uid) + self.interface().add_layer_to_item(arg.unique_name, self.unique_name) def duplicate_layer(self, idx: int) -> None: """Duplicate a given layer. @@ -61,8 +71,8 @@ def duplicate_layer(self, idx: int) -> None: to_duplicate = self.layers[idx] duplicate_layer = Layer( material=to_duplicate.material, - thickness=to_duplicate.thickness.raw_value, - roughness=to_duplicate.roughness.raw_value, + thickness=to_duplicate.thickness.value, + roughness=to_duplicate.roughness.value, name=to_duplicate.name + ' duplicate', ) self.add_layer(duplicate_layer) @@ -73,7 +83,7 @@ def remove_layer(self, idx: int) -> None: :param idx: index of layer to remove """ if self.interface is not None: - self.interface().remove_layer_from_item(self.layers[idx].uid, self.uid) + self.interface().remove_layer_from_item(self.layers[idx].unique_name, self.unique_name) del self.layers[idx] # Representation diff --git a/src/easyreflectometry/sample/assemblies/repeating_multilayer.py b/src/easyreflectometry/sample/assemblies/repeating_multilayer.py index c2965c40..ba26fef6 100644 --- a/src/easyreflectometry/sample/assemblies/repeating_multilayer.py +++ b/src/easyreflectometry/sample/assemblies/repeating_multilayer.py @@ -1,7 +1,11 @@ +from typing import Optional from typing import Union +from easyscience import global_object +from easyscience.Objects.new_variable import Parameter + from easyreflectometry.parameter_utils import get_as_parameter -from easyscience.Objects.ObjectClasses import Parameter +from easyreflectometry.sample.base_element_collection import SIZE_DEFAULT_COLLECTION from ..elements.layers.layer import Layer from ..elements.layers.layer_collection import LayerCollection @@ -37,7 +41,9 @@ def __init__( layers: Union[LayerCollection, Layer, list[Layer], None] = None, repetitions: Union[Parameter, int, None] = None, name: str = 'EasyRepeatingMultilayer', + unique_name: Optional[str] = None, interface=None, + populate_if_none: bool = True, ): """Constructor. @@ -46,15 +52,28 @@ def __init__( :param name: Name for the repeating multi layer, defaults to 'EasyRepeatingMultilayer'. :param interface: Calculator interface, defaults to `None`. """ + if unique_name is None: + unique_name = global_object.generate_unique_name(self.__class__.__name__) if layers is None: - layers = LayerCollection() + if populate_if_none: + layers = LayerCollection([Layer(interface=interface) for _ in range(SIZE_DEFAULT_COLLECTION)]) + else: + layers = LayerCollection() elif isinstance(layers, Layer): layers = LayerCollection(layers, name=layers.name) elif isinstance(layers, list): layers = LayerCollection(*layers, name='/'.join([layer.name for layer in layers])) + # Needed to ensure an empty list is created when saving and instatiating the object as_dict -> from_dict + # Else collisions might occur in global_object.map + self.populate_if_none = False - repetitions = get_as_parameter('repetitions', repetitions, DEFAULTS) + repetitions = get_as_parameter( + name='repetitions', + value=repetitions, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Repetitions', + ) super().__init__( layers=layers, @@ -70,5 +89,5 @@ def __init__( def _dict_repr(self) -> dict: """A simplified dict representation.""" d_dict = {self.name: self.layers._dict_repr} - d_dict[self.name]['repetitions'] = self.repetitions.raw_value + d_dict[self.name]['repetitions'] = float(self.repetitions.value) return d_dict diff --git a/src/easyreflectometry/sample/assemblies/surfactant_layer.py b/src/easyreflectometry/sample/assemblies/surfactant_layer.py index e675f6fb..56a7cb0c 100644 --- a/src/easyreflectometry/sample/assemblies/surfactant_layer.py +++ b/src/easyreflectometry/sample/assemblies/surfactant_layer.py @@ -2,8 +2,8 @@ from typing import Optional -from easyscience.Fitting.Constraints import ObjConstraint -from easyscience.Objects.ObjectClasses import Parameter +from easyscience.fitting.Constraints import ObjConstraint +from easyscience.Objects.new_variable import Parameter from ..elements.layers.layer_area_per_molecule import LayerAreaPerMolecule from ..elements.layers.layer_collection import LayerCollection @@ -117,10 +117,12 @@ def constrain_area_per_molecule(self, status: bool): """Set the status for the area per molecule constraint such that the head and tail layers have the same area per molecule. - :param x: Boolean description the wanted of the constraint. + :param status: Boolean description the wanted of the constraint. """ self.tail_layer._area_per_molecule.user_constraints['area_per_molecule'].enabled = status - self.tail_layer._area_per_molecule.value = self.tail_layer._area_per_molecule.raw_value + if status: + # Apply the constraint by running it + self.tail_layer._area_per_molecule.user_constraints['area_per_molecule']() @property def conformal_roughness(self) -> bool: @@ -129,10 +131,12 @@ def conformal_roughness(self) -> bool: @conformal_roughness.setter def conformal_roughness(self, status: bool): - """Set the status for the roughness to be the same for both layers.""" + """Set the status for the roughness to be the same for both layers. + + :param status: Boolean description the wanted of the constraint. + """ if status: self._enable_roughness_constraints() - self.tail_layer.roughness.value = self.tail_layer.roughness.raw_value else: self._disable_roughness_constraints() diff --git a/src/easyreflectometry/sample/base_core.py b/src/easyreflectometry/sample/base_core.py index 4779c51f..c17c3722 100644 --- a/src/easyreflectometry/sample/base_core.py +++ b/src/easyreflectometry/sample/base_core.py @@ -1,8 +1,9 @@ from abc import abstractmethod -import yaml from easyscience.Objects.ObjectClasses import BaseObj +from easyreflectometry.parameter_utils import yaml_dump + class BaseCore(BaseObj): def __init__( @@ -19,13 +20,6 @@ def __init__( @abstractmethod def _dict_repr(self) -> dict[str, str]: ... - @property - def uid(self) -> int: - """ - :return: UID from the borg map - """ - return self._borg.map.convert_id_to_key(self) - def __repr__(self) -> str: """ String representation of the layer. @@ -33,7 +27,7 @@ def __repr__(self) -> str: :return: a string representation of the layer :rtype: str """ - return yaml.dump(self._dict_repr, sort_keys=False) + return yaml_dump(self._dict_repr) # For classes with special serialization needs one must adopt the dict produced by super # def as_dict(self, skip: list = None) -> dict: diff --git a/src/easyreflectometry/sample/base_element_collection.py b/src/easyreflectometry/sample/base_element_collection.py index d59cc9cc..ab839920 100644 --- a/src/easyreflectometry/sample/base_element_collection.py +++ b/src/easyreflectometry/sample/base_element_collection.py @@ -1,10 +1,10 @@ -from typing import Any from typing import List from typing import Optional -import yaml from easyscience.Objects.Groups import BaseCollection +from easyreflectometry.parameter_utils import yaml_dump + SIZE_DEFAULT_COLLECTION = 2 @@ -19,13 +19,6 @@ def __init__( super().__init__(name, *args, **kwargs) self.interface = interface - @property - def uid(self) -> int: - """ - :return: UID from the borg map - """ - return self._borg.map.convert_id_to_key(self) - @property def names(self) -> list: """ @@ -39,7 +32,7 @@ def __repr__(self) -> str: :return: a string representation of the collection """ - return yaml.dump(self._dict_repr, sort_keys=False) + return yaml_dump(self._dict_repr) @property def _dict_repr(self) -> dict: @@ -61,17 +54,3 @@ def as_dict(self, skip: Optional[List[str]] = None) -> dict: for collection_element in self: this_dict['data'].append(collection_element.as_dict(skip=skip)) return this_dict - - @classmethod - def from_dict(cls, data: dict) -> Any: - """ - Create an instance of a collection from a dictionary. - - :param data: The dictionary for the collection - :return: An instance of the collection - """ - collection = super().from_dict(data) - # Remove the default elements - for i in range(SIZE_DEFAULT_COLLECTION): - del collection[0] - return collection diff --git a/src/easyreflectometry/sample/elements/layers/layer.py b/src/easyreflectometry/sample/elements/layers/layer.py index 9d837dd8..08a75c54 100644 --- a/src/easyreflectometry/sample/elements/layers/layer.py +++ b/src/easyreflectometry/sample/elements/layers/layer.py @@ -1,9 +1,12 @@ __author__ = 'github.com/arm61' +from typing import Optional from typing import Union import numpy as np +from easyscience import global_object +from easyscience.Objects.new_variable import Parameter + from easyreflectometry.parameter_utils import get_as_parameter -from easyscience.Objects.ObjectClasses import Parameter from ...base_core import BaseCore from ..materials.material import Material @@ -13,7 +16,7 @@ 'description': 'The thickness of the layer in angstroms', 'url': 'https://github.com/reflectivity/edu_outreach/blob/master/refl_maths/paper.tex', 'value': 10.0, - 'units': 'angstrom', + 'unit': 'angstrom', 'min': 0.0, 'max': np.Inf, 'fixed': True, @@ -22,7 +25,7 @@ 'description': 'The interfacial roughness, Nevot-Croce, for the layer in angstroms.', 'url': 'https://doi.org/10.1051/rphysap:01980001503076100', 'value': 3.3, - 'units': 'angstrom', + 'unit': 'angstrom', 'min': 0.0, 'max': np.Inf, 'fixed': True, @@ -45,6 +48,7 @@ def __init__( thickness: Union[Parameter, float, None] = None, roughness: Union[Parameter, float, None] = None, name: str = 'EasyLayer', + unique_name: Optional[str] = None, interface=None, ): """Constructor. @@ -58,8 +62,21 @@ def __init__( if material is None: material = Material(interface=interface) - thickness = get_as_parameter('thickness', thickness, DEFAULTS) - roughness = get_as_parameter('roughness', roughness, DEFAULTS) + if unique_name is None: + unique_name = global_object.generate_unique_name(self.__class__.__name__) + + thickness = get_as_parameter( + name='thickness', + value=thickness, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Thickness', + ) + roughness = get_as_parameter( + name='roughness', + value=roughness, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Roughness', + ) super().__init__( name=name, @@ -67,6 +84,7 @@ def __init__( material=material, thickness=thickness, roughness=roughness, + unique_name=unique_name, ) def assign_material(self, material: Material) -> None: @@ -76,7 +94,7 @@ def assign_material(self, material: Material) -> None: """ self.material = material if self.interface is not None: - self.interface().assign_material_to_layer(self.material.uid, self.uid) + self.interface().assign_material_to_layer(self.material.unique_name, self.unique_name) # Representation @property @@ -85,7 +103,7 @@ def _dict_repr(self) -> dict[str, str]: return { self.name: { 'material': self.material._dict_repr, - 'thickness': f'{self.thickness.raw_value:.3f} {self.thickness.unit}', - 'roughness': f'{self.roughness.raw_value:.3f} {self.roughness.unit}', + 'thickness': f'{self.thickness.value:.3f} {self.thickness.unit}', + 'roughness': f'{self.roughness.value:.3f} {self.roughness.unit}', } } diff --git a/src/easyreflectometry/sample/elements/layers/layer_area_per_molecule.py b/src/easyreflectometry/sample/elements/layers/layer_area_per_molecule.py index f9e5a0bd..230c4709 100644 --- a/src/easyreflectometry/sample/elements/layers/layer_area_per_molecule.py +++ b/src/easyreflectometry/sample/elements/layers/layer_area_per_molecule.py @@ -1,11 +1,14 @@ +from typing import Optional from typing import Union import numpy as np +from easyscience import global_object +from easyscience.fitting.Constraints import FunctionalConstraint +from easyscience.Objects.new_variable import Parameter + from easyreflectometry.parameter_utils import get_as_parameter from easyreflectometry.special.calculations import area_per_molecule_to_scattering_length_density from easyreflectometry.special.calculations import neutron_scattering_length -from easyscience.Fitting.Constraints import FunctionalConstraint -from easyscience.Objects.ObjectClasses import Parameter from ..materials.material import Material from ..materials.material_solvated import DEFAULTS as MATERIAL_SOLVATED_DEFAULTS @@ -18,7 +21,7 @@ 'area_per_molecule': { 'description': 'Surface coverage', 'value': 48.2, - 'units': 'angstrom ** 2', + 'unit': 'angstrom^2', 'min': 0, 'max': np.inf, 'fixed': True, @@ -27,7 +30,7 @@ 'description': 'The real scattering length for a molecule formula in angstrom.', 'url': 'https://www.ncnr.nist.gov/resources/activation/', 'value': 4.186, - 'units': 'angstrom', + 'unit': 'angstrom', 'min': -np.Inf, 'max': np.Inf, 'fixed': True, @@ -36,7 +39,7 @@ 'description': 'The real scattering length for a molecule formula in angstrom.', 'url': 'https://www.ncnr.nist.gov/resources/activation/', 'value': 0.0, - 'units': 'angstrom', + 'unit': 'angstrom', 'min': -np.Inf, 'max': np.Inf, 'fixed': True, @@ -72,6 +75,7 @@ def __init__( area_per_molecule: Union[Parameter, float, None] = None, roughness: Union[Parameter, float, None] = None, name: str = 'EasyLayerAreaPerMolecule', + unique_name: Optional[str] = None, interface=None, ): """Constructor. @@ -88,19 +92,48 @@ def __init__( if solvent is None: solvent = Material(6.36, 0, 'D2O', interface=interface) + if unique_name is None: + unique_name = global_object.generate_unique_name(self.__class__.__name__) + # Create the solvated molecule and corresponding constraints if molecular_formula is None: molecular_formula = DEFAULTS['molecular_formula'] - molecule = Material(sld=0.0, isld=0.0, name=molecular_formula, interface=interface) + molecule_material = Material( + sld=0.0, + isld=0.0, + name=molecular_formula, + interface=interface, + unique_name=unique_name + 'Material', + ) - thickness = get_as_parameter('thickness', thickness, DEFAULTS) - _area_per_molecule = get_as_parameter('area_per_molecule', area_per_molecule, DEFAULTS) - _scattering_length_real = get_as_parameter('scattering_length_real', 0.0, DEFAULTS['sl']) - _scattering_length_imag = get_as_parameter('scattering_length_imag', 0.0, DEFAULTS['isl']) + thickness = get_as_parameter( + name='thickness', + value=thickness, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Thickness', + ) + _area_per_molecule = get_as_parameter( + name='area_per_molecule', + value=area_per_molecule, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_AreaPerMolecule', + ) + _scattering_length_real = get_as_parameter( + name='scattering_length_real', + value=0.0, + default_dict=DEFAULTS['sl'], + unique_name_prefix=f'{unique_name}_Sl', + ) + _scattering_length_imag = get_as_parameter( + name='scattering_length_imag', + value=0.0, + default_dict=DEFAULTS['isl'], + unique_name_prefix=f'{unique_name}_Isl', + ) # Constrain the real part of the sld value for the molecule constraint_sld_real = FunctionalConstraint( - dependent_obj=molecule.sld, + dependent_obj=molecule_material.sld, func=area_per_molecule_to_scattering_length_density, independent_objs=[_scattering_length_real, thickness, _area_per_molecule], ) @@ -110,7 +143,7 @@ def __init__( # Constrain the imaginary part of the sld value for the molecule constraint_sld_imag = FunctionalConstraint( - dependent_obj=molecule.isld, + dependent_obj=molecule_material.isld, func=area_per_molecule_to_scattering_length_density, independent_objs=[_scattering_length_imag, thickness, _area_per_molecule], ) @@ -118,14 +151,15 @@ def __init__( _area_per_molecule.user_constraints['iarea_per_molecule'] = constraint_sld_imag _scattering_length_imag.user_constraints['iarea_per_molecule'] = constraint_sld_imag - solvated_molecule = MaterialSolvated( - material=molecule, + solvated_molecule_material = MaterialSolvated( + material=molecule_material, solvent=solvent, solvent_fraction=solvent_fraction, interface=interface, + unique_name=unique_name + 'MaterialSolvated', ) super().__init__( - material=solvated_molecule, + material=solvated_molecule_material, thickness=thickness, roughness=roughness, name=name, @@ -149,7 +183,7 @@ def area_per_molecule_parameter(self) -> Parameter: @property def area_per_molecule(self) -> float: """Get the area per molecule.""" - return self._area_per_molecule.raw_value + return self._area_per_molecule.value @area_per_molecule.setter def area_per_molecule(self, new_area_per_molecule: float) -> None: @@ -236,6 +270,8 @@ def as_dict(self, skip: list = None) -> dict[str, str]: """ this_dict = super().as_dict(skip=skip) this_dict['solvent_fraction'] = self.material._fraction.as_dict() + this_dict['area_per_molecule'] = self._area_per_molecule.as_dict() + this_dict['solvent'] = self.solvent.as_dict() del this_dict['material'] del this_dict['_scattering_length_real'] del this_dict['_scattering_length_imag'] diff --git a/src/easyreflectometry/sample/elements/layers/layer_collection.py b/src/easyreflectometry/sample/elements/layers/layer_collection.py index 69490d24..69c481fb 100644 --- a/src/easyreflectometry/sample/elements/layers/layer_collection.py +++ b/src/easyreflectometry/sample/elements/layers/layer_collection.py @@ -2,7 +2,6 @@ from typing import Optional -from ...base_element_collection import SIZE_DEFAULT_COLLECTION from ...base_element_collection import BaseElementCollection from .layer import Layer @@ -14,11 +13,11 @@ class LayerCollection(BaseElementCollection): def __init__( self, *layers: Optional[list[Layer]], - name: str = 'EasyLayers', + name: str = 'EasyLayerCollection', interface=None, **kwargs, ): if not layers: - layers = [Layer(interface=interface) for _ in range(SIZE_DEFAULT_COLLECTION)] + layers = [] super().__init__(name, interface, *layers, **kwargs) diff --git a/src/easyreflectometry/sample/elements/materials/material.py b/src/easyreflectometry/sample/elements/materials/material.py index 219a06bc..1485abaa 100644 --- a/src/easyreflectometry/sample/elements/materials/material.py +++ b/src/easyreflectometry/sample/elements/materials/material.py @@ -1,10 +1,13 @@ __author__ = 'github.com/arm61' +from typing import Optional from typing import Union import numpy as np +from easyscience import global_object +from easyscience.Objects.new_variable import Parameter + from easyreflectometry.parameter_utils import get_as_parameter -from easyscience.Objects.ObjectClasses import Parameter from ...base_core import BaseCore @@ -13,7 +16,7 @@ 'description': 'The real scattering length density for a material in e-6 per squared angstrom.', 'url': 'https://www.ncnr.nist.gov/resources/activation/', 'value': 4.186, - 'units': '1 / angstrom ** 2', + 'unit': '1 / angstrom^2', 'min': -np.Inf, 'max': np.Inf, 'fixed': True, @@ -22,7 +25,7 @@ 'description': 'The imaginary scattering length density for a material in e-6 per squared angstrom.', 'url': 'https://www.ncnr.nist.gov/resources/activation/', 'value': 0.0, - 'units': '1 / angstrom ** 2', + 'unit': '1 / angstrom^2', 'min': -np.Inf, 'max': np.Inf, 'fixed': True, @@ -40,6 +43,7 @@ def __init__( sld: Union[Parameter, float, None] = None, isld: Union[Parameter, float, None] = None, name: str = 'EasyMaterial', + unique_name: Optional[str] = None, interface=None, ): """Constructor. @@ -49,8 +53,21 @@ def __init__( :param name: Name of the material, defaults to 'EasyMaterial'. :param interface: Calculator interface, defaults to `None`. """ - sld = get_as_parameter('sld', sld, DEFAULTS) - isld = get_as_parameter('isld', isld, DEFAULTS) + if unique_name is None: + unique_name = global_object.generate_unique_name(self.__class__.__name__) + + sld = get_as_parameter( + name='sld', + value=sld, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Sld', + ) + isld = get_as_parameter( + name='isld', + value=isld, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Isld', + ) super().__init__(name=name, sld=sld, isld=isld, interface=interface) @@ -60,7 +77,7 @@ def _dict_repr(self) -> dict[str, str]: """A simplified dict representation.""" return { self.name: { - 'sld': f'{self.sld.raw_value:.3f}e-6 {self.sld.unit}', - 'isld': f'{self.isld.raw_value:.3f}e-6 {self.isld.unit}', + 'sld': f'{self.sld.value:.3f}e-6 {self.sld.unit}', + 'isld': f'{self.isld.value:.3f}e-6 {self.isld.unit}', } } diff --git a/src/easyreflectometry/sample/elements/materials/material_density.py b/src/easyreflectometry/sample/elements/materials/material_density.py index bba2ec3b..fee6168a 100644 --- a/src/easyreflectometry/sample/elements/materials/material_density.py +++ b/src/easyreflectometry/sample/elements/materials/material_density.py @@ -1,41 +1,26 @@ +from typing import Optional from typing import Union import numpy as np +from easyscience import global_object +from easyscience.fitting.Constraints import FunctionalConstraint +from easyscience.Objects.new_variable import Parameter + from easyreflectometry.parameter_utils import get_as_parameter from easyreflectometry.special.calculations import density_to_sld from easyreflectometry.special.calculations import molecular_weight from easyreflectometry.special.calculations import neutron_scattering_length -from easyscience.Fitting.Constraints import FunctionalConstraint -from easyscience.Objects.ObjectClasses import Parameter from .material import DEFAULTS as MATERIAL_DEFAULTS from .material import Material DEFAULTS = { 'chemical_structure': 'Si', - 'sl': { - 'description': 'The real scattering length for a chemical formula in angstrom.', - 'url': 'https://www.ncnr.nist.gov/resources/activation/', - 'value': 4.1491, - 'units': 'angstrom', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True, - }, - 'isl': { - 'description': 'The real scattering length for a chemical formula in angstrom.', - 'url': 'https://www.ncnr.nist.gov/resources/activation/', - 'value': 0.0, - 'units': 'angstrom', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True, - }, 'density': { 'description': 'The mass density of the material.', 'url': 'https://en.wikipedia.org/wiki/Density', 'value': 2.33, - 'units': 'gram / centimeter ** 3', + 'unit': 'gram / centimeter ** 3', 'min': 0, 'max': np.Inf, 'fixed': True, @@ -44,7 +29,7 @@ 'description': 'The molecular weight of a material.', 'url': 'https://en.wikipedia.org/wiki/Molecular_mass', 'value': 28.02, - 'units': 'g / mole', + 'unit': 'g / mole', 'min': -np.Inf, 'max': np.Inf, 'fixed': True, @@ -65,6 +50,7 @@ def __init__( chemical_structure: Union[str, None] = None, density: Union[Parameter, float, None] = None, name: str = 'EasyMaterialDensity', + unique_name: Optional[str] = None, interface=None, ): """Constructor. @@ -74,26 +60,50 @@ def __init__( :param name: Identifier, defaults to `EasyMaterialDensity`. :param interface: Interface object, defaults to `None`. """ + if unique_name is None: + unique_name = global_object.generate_unique_name(self.__class__.__name__) + if chemical_structure is None: chemical_structure = DEFAULTS['chemical_structure'] - density = get_as_parameter('density', density, DEFAULTS) + density = get_as_parameter( + name='density', + value=density, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Density', + ) scattering_length = neutron_scattering_length(chemical_structure) - mw = get_as_parameter('molecular_weight', molecular_weight(chemical_structure), DEFAULTS) + mw = get_as_parameter( + name='molecular_weight', + value=molecular_weight(chemical_structure), + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Mw', + ) scattering_length_real = get_as_parameter( name='scattering_length_real', value=scattering_length.real, default_dict=DEFAULTS['sld'], + unique_name_prefix=f'{unique_name}_ScatteringLengthReal', + ) + scattering_length_imag = get_as_parameter( + name='scattering_length_imag', + value=scattering_length.imag, + default_dict=DEFAULTS['isld'], + unique_name_prefix=f'{unique_name}_ScatteringLengthImag', ) - scattering_length_imag = get_as_parameter('scattering_length_imag', scattering_length.imag, DEFAULTS['isld']) - sld = get_as_parameter( - 'sld', density_to_sld(scattering_length_real.raw_value, mw.raw_value, density.raw_value), DEFAULTS + name='sld', + value=density_to_sld(scattering_length_real.value, mw.value, density.value), + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Sld', ) isld = get_as_parameter( - 'isld', density_to_sld(scattering_length_imag.raw_value, mw.raw_value, density.raw_value), DEFAULTS + name='isld', + value=density_to_sld(scattering_length_imag.value, mw.value, density.value), + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Isld', ) constraint = FunctionalConstraint(sld, density_to_sld, [scattering_length_real, mw, density]) @@ -135,7 +145,7 @@ def _dict_repr(self) -> dict[str, str]: """Dictionary representation of the instance.""" mat_dict = super()._dict_repr mat_dict['chemical_structure'] = self._chemical_structure - mat_dict['density'] = f'{self.density.raw_value:.2e} {self.density.unit}' + mat_dict['density'] = f'{self.density.value:.2e} {self.density.unit}' return mat_dict def as_dict(self, skip: list = []) -> dict[str, str]: diff --git a/src/easyreflectometry/sample/elements/materials/material_mixture.py b/src/easyreflectometry/sample/elements/materials/material_mixture.py index 2a3e0beb..487b64e3 100644 --- a/src/easyreflectometry/sample/elements/materials/material_mixture.py +++ b/src/easyreflectometry/sample/elements/materials/material_mixture.py @@ -1,9 +1,12 @@ +from typing import Optional from typing import Union +from easyscience import global_object +from easyscience.fitting.Constraints import FunctionalConstraint +from easyscience.Objects.new_variable import Parameter + from easyreflectometry.parameter_utils import get_as_parameter from easyreflectometry.special.calculations import weighted_average -from easyscience.Fitting.Constraints import FunctionalConstraint -from easyscience.Objects.ObjectClasses import Parameter from ...base_core import BaseCore from .material import DEFAULTS as MATERIAL_DEFAULTS @@ -13,7 +16,7 @@ 'fraction': { 'description': 'The fraction of material b in material a', 'value': 0.5, - 'units': 'dimensionless', + 'unit': 'dimensionless', 'min': 0, 'max': 1, 'fixed': True, @@ -34,6 +37,7 @@ def __init__( material_b: Union[Material, None] = None, fraction: Union[Parameter, float, None] = None, name: Union[str, None] = None, + unique_name: Optional[str] = None, interface=None, ): """Constructor. @@ -44,26 +48,44 @@ def __init__( :param name: Name of the material, defaults to None that causes the name to be constructed. :param interface: Calculator interface, defaults to `None`. """ + if unique_name is None: + unique_name = global_object.generate_unique_name(self.__class__.__name__) + if material_a is None: material_a = Material(interface=interface) if material_b is None: material_b = Material(interface=interface) - fraction = get_as_parameter('fraction', fraction, DEFAULTS) + fraction = get_as_parameter( + name='fraction', + value=fraction, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Fraction', + ) sld = weighted_average( - a=material_a.sld.raw_value, - b=material_b.sld.raw_value, - p=fraction.raw_value, + a=material_a.sld.value, + b=material_b.sld.value, + p=fraction.value, ) isld = weighted_average( - a=material_a.isld.raw_value, - b=material_b.isld.raw_value, - p=fraction.raw_value, + a=material_a.isld.value, + b=material_b.isld.value, + p=fraction.value, ) - self._sld = get_as_parameter('sld', sld, DEFAULTS) - self._isld = get_as_parameter('isld', isld, DEFAULTS) + self._sld = get_as_parameter( + name='sld', + value=sld, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Sld', + ) + self._isld = get_as_parameter( + name='isld', + value=isld, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Isld', + ) # To avoid problems when setting the interface # self._sld and self._isld need to be declared before calling the super constructor @@ -85,11 +107,11 @@ def _get_linkable_attributes(self): @property def sld(self) -> float: - return self._sld.raw_value + return self._sld.value @property def isld(self) -> float: - return self._isld.raw_value + return self._isld.value def _materials_constraints(self): self._sld.enabled = True @@ -116,7 +138,7 @@ def _materials_constraints(self): @property def fraction(self) -> float: """Get the fraction of material_b.""" - return self._fraction.raw_value + return self._fraction.value @fraction.setter def fraction(self, fraction: float) -> None: @@ -126,7 +148,7 @@ def fraction(self, fraction: float) -> None: """ if not isinstance(fraction, float): raise ValueError('fraction must be a float') - self._fraction.raw_value = fraction + self._fraction.value = fraction @property def material_a(self) -> Material: @@ -171,9 +193,9 @@ def _dict_repr(self) -> dict[str, str]: """A simplified dict representation.""" return { self.name: { - 'fraction': f'{self._fraction.raw_value:.3f} {self._fraction.unit}', - 'sld': f'{self._sld.raw_value:.3f}e-6 {self._sld.unit}', - 'isld': f'{self._isld.raw_value:.3f}e-6 {self._isld.unit}', + 'fraction': f'{self._fraction.value:.3f} {self._fraction.unit}', + 'sld': f'{self._sld.value:.3f}e-6 {self._sld.unit}', + 'isld': f'{self._isld.value:.3f}e-6 {self._isld.unit}', 'material_a': self._material_a._dict_repr, 'material_b': self._material_b._dict_repr, } diff --git a/src/easyreflectometry/sample/elements/materials/material_solvated.py b/src/easyreflectometry/sample/elements/materials/material_solvated.py index 05ff7780..bef267ed 100644 --- a/src/easyreflectometry/sample/elements/materials/material_solvated.py +++ b/src/easyreflectometry/sample/elements/materials/material_solvated.py @@ -1,7 +1,10 @@ +from typing import Optional from typing import Union +from easyscience import global_object +from easyscience.Objects.new_variable import Parameter + from easyreflectometry.parameter_utils import get_as_parameter -from easyscience.Objects.ObjectClasses import Parameter from .material import Material from .material_mixture import MaterialMixture @@ -10,7 +13,7 @@ 'solvent_fraction': { 'description': 'Fraction of solvent in layer.', 'value': 0.2, - 'units': 'dimensionless', + 'unit': 'dimensionless', 'min': 0, 'max': 1, 'fixed': True, @@ -25,6 +28,7 @@ def __init__( solvent: Union[Material, None] = None, solvent_fraction: Union[Parameter, float, None] = None, name=None, + unique_name: Optional[str] = None, interface=None, ): """Constructor. @@ -35,12 +39,20 @@ def __init__( :param name: Name of the material, defaults to None that causes the name to be constructed. :param interface: Calculator interface, defaults to `None`. """ + if unique_name is None: + unique_name = global_object.generate_unique_name(self.__class__.__name__) + if material is None: material = Material(sld=6.36, isld=0, name='D2O', interface=interface) if solvent is None: solvent = Material(sld=-0.561, isld=0, name='H2O', interface=interface) - solvent_fraction = get_as_parameter('solvent_fraction', solvent_fraction, DEFAULTS) + solvent_fraction = get_as_parameter( + name='solvent_fraction', + value=solvent_fraction, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_Fraction', + ) # In super class, the fraction is the fraction of material b in material a super().__init__( @@ -118,9 +130,9 @@ def _dict_repr(self) -> dict[str, str]: """A simplified dict representation.""" return { self.name: { - 'solvent_fraction': f'{self._fraction.raw_value:.3f} {self._fraction.unit}', - 'sld': f'{self._sld.raw_value:.3f}e-6 {self._sld.unit}', - 'isld': f'{self._isld.raw_value:.3f}e-6 {self._isld.unit}', + 'solvent_fraction': f'{self._fraction.value:.3f} {self._fraction.unit}', + 'sld': f'{self._sld.value:.3f}e-6 {self._sld.unit}', + 'isld': f'{self._isld.value:.3f}e-6 {self._isld.unit}', 'material': self.material._dict_repr, 'solvent': self.solvent._dict_repr, } diff --git a/src/easyreflectometry/sample/sample.py b/src/easyreflectometry/sample/sample.py index 7dec0c94..a613b7d1 100644 --- a/src/easyreflectometry/sample/sample.py +++ b/src/easyreflectometry/sample/sample.py @@ -4,9 +4,10 @@ from typing import Union -import yaml from easyscience.Objects.Groups import BaseCollection +from easyreflectometry.parameter_utils import yaml_dump + from .assemblies.base_assembly import BaseAssembly from .assemblies.multilayer import Multilayer from .elements.layers.layer import Layer @@ -22,6 +23,7 @@ def __init__( *list_layer_like: list[Union[Layer, BaseAssembly]], name: str = 'EasySample', interface=None, + populate_if_none: bool = True, **kwargs, ): """Constructor. @@ -32,7 +34,13 @@ def __init__( """ new_items = [] if not list_layer_like: - list_layer_like = [Multilayer(interface=interface) for _ in range(NR_DEFAULT_LAYERS)] + if populate_if_none: + list_layer_like = [Multilayer(interface=interface) for _ in range(NR_DEFAULT_LAYERS)] + else: + list_layer_like = [] + # Needed to ensure an empty list is created when saving and instatiating the object as_dict -> from_dict + # Else collisions might occur in global_object.map + self.populate_if_none = False for layer_like in list_layer_like: if issubclass(type(layer_like), Layer): @@ -44,11 +52,6 @@ def __init__( super().__init__(name, *new_items, **kwargs) self.interface = interface - @property - def uid(self) -> int: - """The UID from the borg map.""" - return self._borg.map.convert_id_to_key(self) - # Representation @property def _dict_repr(self) -> dict: @@ -57,7 +60,7 @@ def _dict_repr(self) -> dict: def __repr__(self) -> str: """String representation of the sample.""" - return yaml.dump(self._dict_repr, sort_keys=False) + return yaml_dump(self._dict_repr) def as_dict(self, skip: list = None) -> dict: """Produces a cleaned dict using a custom as_dict method to skip necessary things. @@ -70,25 +73,5 @@ def as_dict(self, skip: list = None) -> dict: this_dict = super().as_dict(skip=skip) for i, layer in enumerate(self.data): this_dict['data'][i] = layer.as_dict(skip=skip) + this_dict['populate_if_none'] = self.populate_if_none return this_dict - - @classmethod - def from_dict(cls, data: dict) -> Sample: - """ - Create a Sample from a dictionary. - - :param data: dictionary of the Sample - :return: Sample - """ - sample = super().from_dict(data) - - # Remove the default multilayers - for i in range(NR_DEFAULT_LAYERS): - sample.__delitem__(0) - - # Ensure that the data is also converted - # TODO Should probably be handled in easyscience - for i in range(len(sample.data)): - sample[i] = sample[i].__class__.from_dict(data['data'][i]) - - return sample diff --git a/src/easyreflectometry/special/calculations.py b/src/easyreflectometry/special/calculations.py index 18ad2119..f7ea9068 100644 --- a/src/easyreflectometry/special/calculations.py +++ b/src/easyreflectometry/special/calculations.py @@ -1,6 +1,7 @@ __author__ = 'github.com/arm61' import periodictable as pt + from easyreflectometry.special.parsing import parse_formula @@ -59,8 +60,8 @@ def area_per_molecule_to_scattering_length_density( :param scattering_length: Scattering length of component, in angstrom. :param thickness: Thickness of component, in angstrom. - :param area_per_molecule: Area per molecule, in angstrom ** 2. - :return: Scattering length density of layer in e-6 1/angstrom ** 2. + :param area_per_molecule: Area per molecule, in angstrom^2. + :return: Scattering length density of layer in e-6 1/angstrom^2. """ return scattering_length / (thickness * area_per_molecule) * 1e6 @@ -72,7 +73,7 @@ def density_to_sld(scattering_length: float, molecular_weight: float, density: f :param scattering_length: Scattering length of component, in angstrom. :param molecular_weight: Molecular weight of component, in u. :param density: Mass density of the component, in gram centimeter^-3. - :return: Scattering length density of layer in e-6 1/angstrom ** 2. + :return: Scattering length density of layer in e-6 1/angstrom^2. """ # 0.602214076 is avogadros constant times 1e-24 return 0.602214076e6 * density * scattering_length / molecular_weight diff --git a/tests/calculators/refl1d/test_refl1d_calculator.py b/tests/calculators/refl1d/test_refl1d_calculator.py index df16c3b7..ba6e39a8 100644 --- a/tests/calculators/refl1d/test_refl1d_calculator.py +++ b/tests/calculators/refl1d/test_refl1d_calculator.py @@ -8,10 +8,11 @@ import unittest import numpy as np -from easyreflectometry.calculators.refl1d.calculator import Refl1d from numpy.testing import assert_almost_equal from numpy.testing import assert_equal +from easyreflectometry.calculators.refl1d.calculator import Refl1d + class TestRefl1d(unittest.TestCase): def test_init(self): @@ -108,6 +109,50 @@ def test_calculate2(self): ] assert_almost_equal(p.fit_func(q, 'MyModel'), expected) + def test_calculate_magnetic(self): + p = Refl1d() + p.include_magnetism = True + p._wrapper.create_material('Material1') + p._wrapper.update_material('Material1', rho=0.000, irho=0.000) + p._wrapper.create_material('Material2') + p._wrapper.update_material('Material2', rho=2.000, irho=0.000) + p._wrapper.create_material('Material3') + p._wrapper.update_material('Material3', rho=4.000, irho=0.000) + p._wrapper.create_model('MyModel') + p._wrapper.update_model('MyModel', bkg=1e-7) + p._wrapper.create_layer('Layer1') + p._wrapper.assign_material_to_layer('Material1', 'Layer1') + p._wrapper.create_layer('Layer2') + p._wrapper.assign_material_to_layer('Material2', 'Layer2') + p._wrapper.update_layer('Layer2', thickness=10, interface=1.0) + p._wrapper.create_layer('Layer3') + p._wrapper.assign_material_to_layer('Material3', 'Layer3') + p._wrapper.update_layer('Layer3', interface=1.0) + p._wrapper.create_item('Item1') + p._wrapper.add_layer_to_item('Layer1', 'Item1') + p._wrapper.create_item('Item2') + p._wrapper.add_layer_to_item('Layer2', 'Item2') + p._wrapper.add_layer_to_item('Layer1', 'Item2') + p._wrapper.create_item('Item3') + p._wrapper.add_layer_to_item('Layer3', 'Item3') + p._wrapper.add_item('Item1', 'MyModel') + p._wrapper.add_item('Item2', 'MyModel') + p._wrapper.add_item('Item3', 'MyModel') + q = np.linspace(0.001, 0.3, 10) + expected = [ + 9.99491251e-01, + 1.08413641e-02, + 1.46824402e-04, + 2.11783999e-05, + 5.24616472e-06, + 1.61422945e-06, + 5.66961121e-07, + 2.34269519e-07, + 1.30026616e-07, + 1.05139655e-07, + ] + assert_almost_equal(p.fit_func(q, 'MyModel'), expected) + def test_sld_profile(self): p = Refl1d() p._wrapper.create_material('Material1') diff --git a/tests/calculators/refl1d/test_refl1d_wrapper.py b/tests/calculators/refl1d/test_refl1d_wrapper.py index 08f8a618..6753a439 100644 --- a/tests/calculators/refl1d/test_refl1d_wrapper.py +++ b/tests/calculators/refl1d/test_refl1d_wrapper.py @@ -10,13 +10,14 @@ from unittest.mock import patch import numpy as np +from numpy.testing import assert_almost_equal +from numpy.testing import assert_equal + from easyreflectometry.calculators.refl1d.wrapper import Refl1dWrapper from easyreflectometry.calculators.refl1d.wrapper import _build_sample from easyreflectometry.calculators.refl1d.wrapper import _get_oversampling_q from easyreflectometry.calculators.refl1d.wrapper import _get_polarized_probe from easyreflectometry.calculators.refl1d.wrapper import _get_probe -from numpy.testing import assert_almost_equal -from numpy.testing import assert_equal class TestRefl1d(unittest.TestCase): @@ -24,6 +25,12 @@ def test_init(self): p = Refl1dWrapper() assert_equal(list(p.storage.keys()), ['material', 'layer', 'item', 'model']) assert_equal(issubclass(p.storage['material'].__class__, dict), True) + assert p._magnetism is False + + def test_set_magnetism(self): + p = Refl1dWrapper() + p.magnetism = True + assert p._magnetism is True def test_reset_storage(self): p = Refl1dWrapper() @@ -70,6 +77,14 @@ def test_update_layer(self): assert_almost_equal(p.storage['layer']['Si'].thickness.value, 10) assert_almost_equal(p.storage['layer']['Si'].interface.value, 5) + def test_update_magnetic_layer(self): + p = Refl1dWrapper() + p.magnetism = True + p.create_layer('Si') + p.update_layer('Si', magnetism_rhoM=5, magnetism_thetaM=10) + assert_almost_equal(p.storage['layer']['Si'].magnetism.thetaM.value, 10) + assert_almost_equal(p.storage['layer']['Si'].magnetism.rhoM.value, 5) + def test_get_layer_value(self): p = Refl1dWrapper() p.create_layer('Si') @@ -77,6 +92,14 @@ def test_get_layer_value(self): assert_almost_equal(p.get_layer_value('Si', 'thickness'), 10) assert_almost_equal(p.get_layer_value('Si', 'interface'), 5) + def test_magnetic_get_layer_value(self): + p = Refl1dWrapper() + p.magnetism = True + p.create_layer('Si') + p.update_layer('Si', magnetism_rhoM=5, magnetism_thetaM=10) + assert_almost_equal(p.get_layer_value('Si', 'magnetism_thetaM'), 10) + assert_almost_equal(p.get_layer_value('Si', 'magnetism_rhoM'), 5) + def test_create_item(self): p = Refl1dWrapper() p.create_item('SiNi') diff --git a/tests/calculators/refnx/test_refnx_calculator.py b/tests/calculators/refnx/test_refnx_calculator.py index fa0fa923..782df2c0 100644 --- a/tests/calculators/refnx/test_refnx_calculator.py +++ b/tests/calculators/refnx/test_refnx_calculator.py @@ -8,10 +8,11 @@ import unittest import numpy as np -from easyreflectometry.calculators.refnx.calculator import Refnx from numpy.testing import assert_almost_equal from numpy.testing import assert_equal +from easyreflectometry.calculators.refnx.calculator import Refnx + class TestRefnx(unittest.TestCase): def test_init(self): diff --git a/tests/calculators/refnx/test_refnx_wrapper.py b/tests/calculators/refnx/test_refnx_wrapper.py index 3a4bbd04..de2a0027 100644 --- a/tests/calculators/refnx/test_refnx_wrapper.py +++ b/tests/calculators/refnx/test_refnx_wrapper.py @@ -9,14 +9,16 @@ import unittest import numpy as np -from easyreflectometry.calculators.refnx.wrapper import RefnxWrapper -from easyreflectometry.experiment import LinearSpline -from easyreflectometry.experiment import PercentageFhwm +import pytest from numpy.testing import assert_allclose from numpy.testing import assert_almost_equal from numpy.testing import assert_equal from refnx import reflect +from easyreflectometry.calculators.refnx.wrapper import RefnxWrapper +from easyreflectometry.experiment import LinearSpline +from easyreflectometry.experiment import PercentageFhwm + class TestRefnx(unittest.TestCase): def test_init(self): @@ -24,6 +26,11 @@ def test_init(self): assert_equal(list(p.storage.keys()), ['material', 'layer', 'item', 'model']) assert_equal(issubclass(p.storage['material'].__class__, dict), True) + def test_set_magnetism(self): + p = RefnxWrapper() + with pytest.raises(NotImplementedError): + p.include_magnetism = True + def test_reset_storage(self): p = RefnxWrapper() p.storage['material']['a'] = 1 diff --git a/tests/experiment/test_model.py b/tests/experiment/test_model.py index ea99a69a..c1386f8a 100644 --- a/tests/experiment/test_model.py +++ b/tests/experiment/test_model.py @@ -5,12 +5,15 @@ __author__ = 'github.com/arm61' __version__ = '0.0.1' - import unittest from unittest.mock import MagicMock import numpy as np import pytest +from easyscience import global_object +from numpy.testing import assert_almost_equal +from numpy.testing import assert_equal + from easyreflectometry.calculators import CalculatorFactory from easyreflectometry.experiment import LinearSpline from easyreflectometry.experiment import Model @@ -22,7 +25,6 @@ from easyreflectometry.sample import RepeatingMultilayer from easyreflectometry.sample import Sample from easyreflectometry.sample import SurfactantLayer -from numpy.testing import assert_equal class TestModel(unittest.TestCase): @@ -33,13 +35,13 @@ def test_default(self): assert_equal(p.sample.name, 'EasySample') assert_equal(p.scale.display_name, 'scale') assert_equal(str(p.scale.unit), 'dimensionless') - assert_equal(p.scale.value.value.magnitude, 1.0) + assert_equal(p.scale.value, 1.0) assert_equal(p.scale.min, 0.0) assert_equal(p.scale.max, np.Inf) assert_equal(p.scale.fixed, True) assert_equal(p.background.display_name, 'background') assert_equal(str(p.background.unit), 'dimensionless') - assert_equal(p.background.value.value.magnitude, 1.0e-8) + assert_equal(p.background.value, 1.0e-8) assert_equal(p.background.min, 0.0) assert_equal(p.background.max, np.Inf) assert_equal(p.background.fixed, True) @@ -69,13 +71,13 @@ def test_from_pars(self): assert_equal(mod.sample.name, 'myModel') assert_equal(mod.scale.display_name, 'scale') assert_equal(str(mod.scale.unit), 'dimensionless') - assert_equal(mod.scale.value.value.magnitude, 2.0) + assert_equal(mod.scale.value, 2.0) assert_equal(mod.scale.min, 0.0) assert_equal(mod.scale.max, np.Inf) assert_equal(mod.scale.fixed, True) assert_equal(mod.background.display_name, 'background') assert_equal(str(mod.background.unit), 'dimensionless') - assert_equal(mod.background.value.value.magnitude, 1.0e-5) + assert_equal(mod.background.value, 1.0e-5) assert_equal(mod.background.min, 0.0) assert_equal(mod.background.max, np.Inf) assert_equal(mod.background.fixed, True) @@ -335,10 +337,6 @@ def test_remove_item_with_interface_refl1d(self): # assert_equal(len(mod.interface()._wrapper.storage['item']), 1) # assert_equal(len(mod.interface()._wrapper.storage['layer']), 2) - def test_uid(self): - p = Model() - assert_equal(p.uid, p._borg.map.convert_id_to_key(p)) - def test_resolution_function(self): mock_resolution_function = MagicMock() interface = CalculatorFactory() @@ -380,7 +378,7 @@ def test_repr(self): assert ( model.__repr__() - == 'EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: 5.0 %\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' # noqa: E501 + == 'EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: 5.0 %\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501 ) def test_repr_resolution_function(self): @@ -389,26 +387,35 @@ def test_repr_resolution_function(self): model.resolution_function = resolution_function assert ( model.__repr__() - == 'EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: function of Q\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' # noqa: E501 + == 'EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: function of Q\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501 ) - def test_dict_round_trip(self): - # When - resolution_function = LinearSpline([0, 10], [0, 10]) - interface = CalculatorFactory() - model = Model(interface=interface) - model.resolution_function = resolution_function - surfactant = SurfactantLayer() - model.add_item(surfactant) - multilayer = Multilayer() - model.add_item(multilayer) - repeating = RepeatingMultilayer() - model.add_item(repeating) - src_dict = model.as_dict() - # Then - model_from_dict = Model.from_dict(src_dict) - - # Expect - assert model.as_data_dict(skip=['resolution_function']) == model_from_dict.as_data_dict(skip=['resolution_function']) - assert model._resolution_function.smearing(5.5) == model_from_dict._resolution_function.smearing(5.5) +@pytest.mark.parametrize( + 'interface', + [None, CalculatorFactory()], +) +def test_dict_round_trip(interface): # , additional_layer): + # When + resolution_function = LinearSpline([0, 10], [0, 10]) + model = Model(interface=interface) + model.resolution_function = resolution_function + for additional_layer in [SurfactantLayer(), Multilayer(), RepeatingMultilayer()]: + model.add_item(additional_layer) + src_dict = model.as_dict() + global_object.map._clear() + + # Then + model_from_dict = Model.from_dict(src_dict) + + # Expect + assert sorted(model.as_data_dict(skip=['resolution_function', 'interface'])) == sorted( + model_from_dict.as_data_dict(skip=['resolution_function', 'interface']) + ) + assert model._resolution_function.smearing(5.5) == model_from_dict._resolution_function.smearing(5.5) + if interface is not None: + assert model.interface().name == model_from_dict.interface().name + assert_almost_equal( + model.interface().fit_func([0.3], model.unique_name), + model_from_dict.interface().fit_func([0.3], model_from_dict.unique_name), + ) diff --git a/tests/experiment/test_model_collection.py b/tests/experiment/test_model_collection.py index 11c3ba71..02f0ec4b 100644 --- a/tests/experiment/test_model_collection.py +++ b/tests/experiment/test_model_collection.py @@ -1,10 +1,10 @@ -import unittest +from easyscience import global_object from easyreflectometry.experiment.model import Model from easyreflectometry.experiment.model_collection import ModelCollection -class TestModelCollection(unittest.TestCase): +class TestModelCollection: def test_default(self): # When Then collection = ModelCollection() @@ -76,17 +76,16 @@ def test_dict_round_trip(self): model_1 = Model(name='Model1') model_2 = Model(name='Model2') model_3 = Model(name='Model3') + p = ModelCollection(model_1, model_2, model_3) + p_dict = p.as_dict() + global_object.map._clear() # Then - collection = ModelCollection(model_1, model_2, model_3) - - src_dict = collection.as_dict() - - # Then - collection_from_dict = ModelCollection.from_dict(src_dict) + q = ModelCollection.from_dict(p_dict) # Expect - assert collection.as_data_dict(skip=['resolution_function', 'interface']) == collection_from_dict.as_data_dict( - skip=['resolution_function', 'interface'] + # We have to skip the resolution_function and interface + assert sorted(p.as_data_dict(skip=['resolution_function', 'interface'])) == sorted( + q.as_data_dict(skip=['resolution_function', 'interface']) ) - assert collection[0]._resolution_function.smearing(5.5) == collection_from_dict[0]._resolution_function.smearing(5.5) + assert p[0]._resolution_function.smearing(5.5) == q[0]._resolution_function.smearing(5.5) diff --git a/tests/experiment/test_resolution_functions.py b/tests/experiment/test_resolution_functions.py index a34f95b4..ab8eb614 100644 --- a/tests/experiment/test_resolution_functions.py +++ b/tests/experiment/test_resolution_functions.py @@ -1,6 +1,7 @@ import unittest import numpy as np + from easyreflectometry.experiment.resolution_functions import DEFAULT_RESOLUTION_FWHM_PERCENTAGE from easyreflectometry.experiment.resolution_functions import LinearSpline from easyreflectometry.experiment.resolution_functions import PercentageFhwm diff --git a/tests/sample/assemblies/test_base_assembly.py b/tests/sample/assemblies/test_base_assembly.py index e5c09ebb..acbba9ea 100644 --- a/tests/sample/assemblies/test_base_assembly.py +++ b/tests/sample/assemblies/test_base_assembly.py @@ -5,14 +5,18 @@ from typing import Any from unittest.mock import MagicMock -import easyreflectometry.sample.assemblies.base_assembly import pytest +from easyscience import global_object + +import easyreflectometry.sample.assemblies.base_assembly from easyreflectometry.sample.assemblies.base_assembly import BaseAssembly class TestBaseAssembly: @pytest.fixture def base_assembly(self) -> BaseAssembly: + global_object.map._clear() + self.mock_layer_0 = MagicMock() self.mock_layer_1 = MagicMock() self.mock_layers = [self.mock_layer_0, self.mock_layer_1] @@ -65,7 +69,7 @@ def test_enable_thickness_constraints(self, base_assembly: BaseAssembly) -> None # Expect assert self.mock_layer_0.thickness.user_constraints['thickness_1'].enabled is True - assert self.mock_layer_0.thickness.value == self.mock_layer_0.thickness.raw_value + assert self.mock_layer_0.thickness.value == self.mock_layer_0.thickness.value assert self.mock_layer_0.thickness.enabled is True assert self.mock_layer_1.thickness.enabled is True @@ -116,7 +120,7 @@ def test_enable_roughness_constraints(self, base_assembly): # Expect assert self.mock_layer_0.roughness.user_constraints['roughness_1'].enabled is True - assert self.mock_layer_0.roughness.value == self.mock_layer_0.roughness.raw_value + assert self.mock_layer_0.roughness.value == self.mock_layer_0.roughness.value assert self.mock_layer_0.roughness.enabled is True assert self.mock_layer_1.roughness.enabled is True diff --git a/tests/sample/assemblies/test_gradient_layer.py b/tests/sample/assemblies/test_gradient_layer.py index c506d930..0663e54a 100644 --- a/tests/sample/assemblies/test_gradient_layer.py +++ b/tests/sample/assemblies/test_gradient_layer.py @@ -4,18 +4,22 @@ from unittest.mock import MagicMock -import easyreflectometry.sample.assemblies.gradient_layer import pytest +from easyscience import global_object +from numpy.testing import assert_almost_equal + +import easyreflectometry.sample.assemblies.gradient_layer from easyreflectometry.sample.assemblies.gradient_layer import GradientLayer from easyreflectometry.sample.assemblies.gradient_layer import _linear_gradient from easyreflectometry.sample.assemblies.gradient_layer import _prepare_gradient_layers from easyreflectometry.sample.elements.materials.material import Material -from numpy.testing import assert_almost_equal class TestGradientLayer: @pytest.fixture def gradient_layer(self) -> GradientLayer: + global_object.map._clear() + self.front = Material(10.0, -10.0, 'Material_1') self.back = Material(0.0, 0.0, 'Material_2') @@ -36,14 +40,14 @@ def test_init(self, gradient_layer: GradientLayer) -> None: assert gradient_layer._type, 'Gradient-layer' assert gradient_layer.interface is None assert gradient_layer.thickness == 1.0 - assert gradient_layer.back_layer.thickness.raw_value == 0.1 + assert gradient_layer.back_layer.thickness.value == 0.1 - assert gradient_layer.front_layer.material.sld.raw_value == 10.0 - assert gradient_layer.layers[5].material.sld.raw_value == 5.0 - assert gradient_layer.back_layer.material.sld.raw_value == 1.0 - assert gradient_layer.front_layer.material.isld.raw_value == -10.0 - assert gradient_layer.layers[5].material.isld.raw_value == -5.0 - assert gradient_layer.back_layer.material.isld.raw_value == -1.0 + assert gradient_layer.front_layer.material.sld.value == 10.0 + assert gradient_layer.layers[5].material.sld.value == 5.0 + assert gradient_layer.back_layer.material.sld.value == 1.0 + assert gradient_layer.front_layer.material.isld.value == -10.0 + assert gradient_layer.layers[5].material.isld.value == -5.0 + assert gradient_layer.back_layer.material.isld.value == -1.0 def test_default(self) -> None: # When Then @@ -78,32 +82,37 @@ def test_from_pars(self) -> None: def test_repr(self, gradient_layer: GradientLayer) -> None: # When Then Expect - expected_str = "thickness: 1.0\ndiscretisation_elements: 10\nback_layer:\n '9':\n material:\n EasyMaterial:\n sld: 1.000e-6 1 / angstrom ** 2\n isld: -1.000e-6 1 / angstrom ** 2\n thickness: 0.100 angstrom\n roughness: 2.000 angstrom\nfront_layer:\n '0':\n material:\n EasyMaterial:\n sld: 10.000e-6 1 / angstrom ** 2\n isld: -10.000e-6 1 / angstrom ** 2\n thickness: 0.100 angstrom\n roughness: 2.000 angstrom\n" # noqa: E501 + expected_str = "thickness: 1.0\ndiscretisation_elements: 10\nback_layer:\n '9':\n material:\n EasyMaterial:\n sld: 1.000e-6 1/Å^2\n isld: -1.000e-6 1/Å^2\n thickness: 0.100 Å\n roughness: 2.000 Å\nfront_layer:\n '0':\n material:\n EasyMaterial:\n sld: 10.000e-6 1/Å^2\n isld: -10.000e-6 1/Å^2\n thickness: 0.100 Å\n roughness: 2.000 Å\n" # noqa: E501 assert gradient_layer.__repr__() == expected_str - def test_dict_round_trip(self, gradient_layer: GradientLayer) -> None: - # When Then - result = GradientLayer.from_dict(gradient_layer.as_dict()) + def test_dict_round_trip(self) -> None: + # When + p = GradientLayer() + p_dict = p.as_dict() + global_object.map._clear() - # Expect - assert result.as_data_dict() == gradient_layer.as_data_dict() - assert len(gradient_layer.layers) == len(result.layers) + # Then + q = GradientLayer.from_dict(p_dict) + + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) + assert len(p.layers) == len(q.layers) # Just one layer of the generated layers is checked - assert gradient_layer.layers[5].__repr__() == result.layers[5].__repr__() + assert p.layers[5].__repr__() == q.layers[5].__repr__() - def test_thickness_setter(self, gradient_layer: GradientLayer) -> None: + def test_thickness_setter(self) -> None: # When + gradient_layer = GradientLayer() gradient_layer.thickness = 10.0 # Then assert gradient_layer.thickness == 10.0 - assert gradient_layer.front_layer.thickness.raw_value == 1.0 - assert gradient_layer.back_layer.thickness.raw_value == 1.0 + assert gradient_layer.front_layer.thickness.value == 1.0 + assert gradient_layer.back_layer.thickness.value == 1.0 def test_thickness_getter(self, gradient_layer: GradientLayer) -> None: # When gradient_layer.layers = [MagicMock(), MagicMock()] - gradient_layer.front_layer.thickness.raw_value = 10.0 + gradient_layer.front_layer.thickness.value = 10.0 # Then # discretisation_elements * discrete_layer_thickness @@ -115,13 +124,13 @@ def test_roughness_setter(self, gradient_layer: GradientLayer) -> None: # Then assert gradient_layer.roughness == 10.0 - assert gradient_layer.front_layer.roughness.raw_value == 10.0 - assert gradient_layer.back_layer.roughness.raw_value == 10.0 + assert gradient_layer.front_layer.roughness.value == 10.0 + assert gradient_layer.back_layer.roughness.value == 10.0 def test_roughness_getter(self, gradient_layer: GradientLayer) -> None: # When gradient_layer.layers = [MagicMock(), MagicMock()] - gradient_layer.front_layer.roughness.raw_value = 10.0 + gradient_layer.front_layer.roughness.value = 10.0 # Then assert gradient_layer.roughness == 10.0 @@ -177,9 +186,3 @@ def test_prepare_gradient_layers(monkeypatch): assert mock_Layer.call_args_list[0][1]['thickness'] == 0.0 assert mock_Layer.call_args_list[0][1]['name'] == '0' assert mock_Layer.call_args_list[0][1]['interface'] is None - - -def test_dict_round_trip(): - p = GradientLayer() - q = GradientLayer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() diff --git a/tests/sample/assemblies/test_multilayer.py b/tests/sample/assemblies/test_multilayer.py index 5d4bd264..c1ee3fd0 100644 --- a/tests/sample/assemblies/test_multilayer.py +++ b/tests/sample/assemblies/test_multilayer.py @@ -7,13 +7,15 @@ import unittest +from easyscience import global_object +from numpy.testing import assert_equal +from numpy.testing import assert_raises + from easyreflectometry.calculators.factory import CalculatorFactory from easyreflectometry.sample.assemblies.multilayer import Multilayer from easyreflectometry.sample.elements.layers.layer import Layer from easyreflectometry.sample.elements.layers.layer_collection import LayerCollection from easyreflectometry.sample.elements.materials.material import Material -from numpy.testing import assert_equal -from numpy.testing import assert_raises class TestMultilayer(unittest.TestCase): @@ -23,7 +25,14 @@ def test_default(self): assert_equal(p._type, 'Multi-layer') assert_equal(p.interface, None) assert_equal(len(p.layers), 2) - assert_equal(p.layers.name, 'EasyLayers') + assert_equal(p.layers.name, 'EasyLayerCollection') + + def test_default_empty(self): + p = Multilayer(populate_if_none=False) + assert_equal(p.name, 'EasyMultilayer') + assert_equal(p._type, 'Multi-layer') + assert_equal(p.interface, None) + assert_equal(len(p.layers), 0) def test_from_pars(self): m = Material(6.908, -0.278, 'Boron') @@ -74,10 +83,10 @@ def test_add_layer_with_interface_refnx(self): p = Layer(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer(k, 50.0, 1.0, 'thickPotassium', interface=interface) o = Multilayer(p, 'twoLayerItem', interface=interface) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 1) o.add_layer(q) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) - assert_equal(o.interface()._wrapper.storage['item'][o.uid].components[1].thick.value, 50.0) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 2) + assert_equal(o.interface()._wrapper.storage['item'][o.unique_name].components[1].thick.value, 50.0) def test_duplicate_layer(self): m = Material(6.908, -0.278, 'Boron') @@ -101,18 +110,18 @@ def test_duplicate_layer_with_interface_refnx(self): p = Layer(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer(k, 50.0, 1.0, 'thickPotassium', interface=interface) o = Multilayer(p, 'twoLayerItem', interface=interface) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 1) o.add_layer(q) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) - assert_equal(o.interface()._wrapper.storage['item'][o.uid].components[1].thick.value, 50.0) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 2) + assert_equal(o.interface()._wrapper.storage['item'][o.unique_name].components[1].thick.value, 50.0) o.duplicate_layer(1) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 3) - assert_equal(o.interface()._wrapper.storage['item'][o.uid].components[2].thick.value, 50.0) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 3) + assert_equal(o.interface()._wrapper.storage['item'][o.unique_name].components[2].thick.value, 50.0) assert_raises( AssertionError, assert_equal, - o.interface()._wrapper.storage['item'][o.uid].components[1].name, - o.interface()._wrapper.storage['item'][o.uid].components[2].name, + o.interface()._wrapper.storage['item'][o.unique_name].components[1].name, + o.interface()._wrapper.storage['item'][o.unique_name].components[2].name, ) def test_remove_layer(self): @@ -137,22 +146,25 @@ def test_remove_layer_with_interface_refnx(self): p = Layer(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer(k, 50.0, 1.0, 'thickPotassium', interface=interface) o = Multilayer(p, name='twoLayerItem', interface=interface) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 1) o.add_layer(q) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 2) assert_equal(o.layers[1].name, 'thickPotassium') o.remove_layer(1) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 1) assert_equal(o.layers[0].name, 'thinBoron') def test_repr(self): p = Multilayer() assert ( p.__repr__() - == 'EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' # noqa: E501 + == 'EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501 ) def test_dict_round_trip(self): p = Multilayer() - q = Multilayer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + p_dict = p.as_dict() + global_object.map._clear() + + q = Multilayer.from_dict(p_dict) + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/sample/assemblies/test_repeating_multilayer.py b/tests/sample/assemblies/test_repeating_multilayer.py index 89057926..ce585992 100644 --- a/tests/sample/assemblies/test_repeating_multilayer.py +++ b/tests/sample/assemblies/test_repeating_multilayer.py @@ -8,13 +8,15 @@ import unittest +from easyscience import global_object +from numpy.testing import assert_equal +from numpy.testing import assert_raises + from easyreflectometry.calculators import CalculatorFactory from easyreflectometry.sample.assemblies.repeating_multilayer import RepeatingMultilayer from easyreflectometry.sample.elements.layers.layer import Layer from easyreflectometry.sample.elements.layers.layer_collection import LayerCollection from easyreflectometry.sample.elements.materials.material import Material -from numpy.testing import assert_equal -from numpy.testing import assert_raises class TestRepeatingMultilayer(unittest.TestCase): @@ -26,11 +28,24 @@ def test_default(self): assert_equal(len(p.layers), 2) assert_equal(p.repetitions.display_name, 'repetitions') assert_equal(str(p.repetitions.unit), 'dimensionless') - assert_equal(p.repetitions.value.value.magnitude, 1.0) + assert_equal(p.repetitions.value, 1.0) assert_equal(p.repetitions.min, 1) assert_equal(p.repetitions.max, 9999) assert_equal(p.repetitions.fixed, True) - assert_equal(p.layers.name, 'EasyLayers') + assert_equal(p.layers.name, 'EasyLayerCollection') + + def test_default_empty(self): + p = RepeatingMultilayer(populate_if_none=False) + assert_equal(p.name, 'EasyRepeatingMultilayer') + assert_equal(p._type, 'Repeating Multi-layer') + assert_equal(p.interface, None) + assert_equal(p.repetitions.display_name, 'repetitions') + assert_equal(str(p.repetitions.unit), 'dimensionless') + assert_equal(p.repetitions.value, 1.0) + assert_equal(p.repetitions.min, 1) + assert_equal(p.repetitions.max, 9999) + assert_equal(p.repetitions.fixed, True) + assert_equal(p.layers.name, 'EasyLayerCollection') def test_from_pars(self): m = Material(6.908, -0.278, 'Boron') @@ -44,7 +59,7 @@ def test_from_pars(self): assert_equal(o.interface, None) assert_equal(o.repetitions.display_name, 'repetitions') assert_equal(str(o.repetitions.unit), 'dimensionless') - assert_equal(o.repetitions.value.value.magnitude, 2.0) + assert_equal(o.repetitions.value, 2.0) assert_equal(o.repetitions.min, 1) assert_equal(o.repetitions.max, 9999) assert_equal(o.repetitions.fixed, True) @@ -58,7 +73,7 @@ def test_from_pars_layer(self): assert_equal(o.interface, None) assert_equal(o.repetitions.display_name, 'repetitions') assert_equal(str(o.repetitions.unit), 'dimensionless') - assert_equal(o.repetitions.value.value.magnitude, 2.0) + assert_equal(o.repetitions.value, 2.0) assert_equal(o.repetitions.min, 1) assert_equal(o.repetitions.max, 9999) assert_equal(o.repetitions.fixed, True) @@ -73,7 +88,7 @@ def test_from_pars_layer_list(self): assert_equal(o.name, 'twoLayerItem') assert_equal(o.interface, None) assert_equal(o.layers.name, 'thinBoron/layerPotassium') - assert_equal(o.repetitions.value.value.magnitude, 10.0) + assert_equal(o.repetitions.value, 10.0) assert_equal(o.repetitions.min, 1) assert_equal(o.repetitions.max, 9999) @@ -96,10 +111,10 @@ def test_add_layer_with_interface_refnx(self): p = Layer(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer(k, 50.0, 1.0, 'thickPotassium', interface=interface) o = RepeatingMultilayer(p, 2.0, 'twoLayerItem', interface=interface) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 1) o.add_layer(q) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) - assert_equal(o.interface()._wrapper.storage['item'][o.uid].components[1].thick.value, 50.0) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 2) + assert_equal(o.interface()._wrapper.storage['item'][o.unique_name].components[1].thick.value, 50.0) def test_duplicate_layer(self): m = Material(6.908, -0.278, 'Boron') @@ -123,18 +138,18 @@ def test_duplicate_layer_with_interface_refnx(self): p = Layer(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer(k, 50.0, 1.0, 'thickPotassium', interface=interface) o = RepeatingMultilayer(p, 2.0, 'twoLayerItem', interface=interface) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 1) o.add_layer(q) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) - assert_equal(o.interface()._wrapper.storage['item'][o.uid].components[1].thick.value, 50.0) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 2) + assert_equal(o.interface()._wrapper.storage['item'][o.unique_name].components[1].thick.value, 50.0) o.duplicate_layer(1) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 3) - assert_equal(o.interface()._wrapper.storage['item'][o.uid].components[2].thick.value, 50.0) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 3) + assert_equal(o.interface()._wrapper.storage['item'][o.unique_name].components[2].thick.value, 50.0) assert_raises( AssertionError, assert_equal, - o.interface()._wrapper.storage['item'][o.uid].components[1].name, - o.interface()._wrapper.storage['item'][o.uid].components[2].name, + o.interface()._wrapper.storage['item'][o.unique_name].components[1].name, + o.interface()._wrapper.storage['item'][o.unique_name].components[2].name, ) def test_remove_layer(self): @@ -159,22 +174,25 @@ def test_remove_layer_with_interface_refnx(self): p = Layer(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer(k, 50.0, 1.0, 'thickPotassium', interface=interface) o = RepeatingMultilayer(p, repetitions=2.0, name='twoLayerItem', interface=interface) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 1) o.add_layer(q) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 2) assert_equal(o.layers[1].name, 'thickPotassium') o.remove_layer(1) - assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) + assert_equal(len(o.interface()._wrapper.storage['item'][o.unique_name].components), 1) assert_equal(o.layers[0].name, 'thinBoron') def test_repr(self): - p = RepeatingMultilayer() + p = RepeatingMultilayer(populate_if_none=True) assert ( p.__repr__() - == 'EasyRepeatingMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n repetitions: 1.0\n' # noqa: E501 + == 'EasyRepeatingMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n repetitions: 1.0\n' # noqa: E501 ) def test_dict_round_trip(self): - p = RepeatingMultilayer() - q = RepeatingMultilayer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + p = RepeatingMultilayer(populate_if_none=True) + p_dict = p.as_dict() + global_object.map._clear() + + q = RepeatingMultilayer.from_dict(p_dict) + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/sample/assemblies/test_surfactant_layer.py b/tests/sample/assemblies/test_surfactant_layer.py index c432df32..85672708 100644 --- a/tests/sample/assemblies/test_surfactant_layer.py +++ b/tests/sample/assemblies/test_surfactant_layer.py @@ -8,12 +8,15 @@ import unittest +from easyscience import global_object + from easyreflectometry.sample.assemblies.surfactant_layer import SurfactantLayer from easyreflectometry.sample.elements.layers.layer import Layer +from easyreflectometry.sample.elements.layers.layer_area_per_molecule import LayerAreaPerMolecule from easyreflectometry.sample.elements.materials.material import Material -class TestSurfactantLayer(unittest.TestCase): +class TestSurfactantLayer: def test_default(self): p = SurfactantLayer() assert p.name == 'EasySurfactantLayer' @@ -37,15 +40,15 @@ def test_from_pars(self): assert p.layers[0].name == 'A Test Tail Layer' assert p.tail_layer.name == 'A Test Tail Layer' assert p.tail_layer.molecular_formula == 'C8O10H12P' - assert p.tail_layer.thickness.raw_value == 12 + assert p.tail_layer.thickness.value == 12 assert p.tail_layer.solvent.as_data_dict() == h2o.as_data_dict() assert p.tail_layer.solvent_fraction == 0.5 assert p.tail_layer.area_per_molecule == 50 - assert p.tail_layer.roughness.raw_value == 2 + assert p.tail_layer.roughness.value == 2 assert p.layers[1].name == 'A Test Head Layer' assert p.head_layer.name == 'A Test Head Layer' assert p.head_layer.molecular_formula == 'C10H24' - assert p.head_layer.thickness.raw_value == 10 + assert p.head_layer.thickness.value == 10 assert p.head_layer.solvent.as_data_dict() == noth2o.as_data_dict() assert p.head_layer.solvent_fraction == 0.2 assert p.head_layer.area_per_molecule == 40 @@ -68,33 +71,33 @@ def test_constraint_area_per_molecule(self): def test_conformal_roughness(self): p = SurfactantLayer() p.tail_layer.roughness.value = 2 - assert p.tail_layer.roughness.raw_value == 2 - assert p.head_layer.roughness.raw_value == 3 + assert p.tail_layer.roughness.value == 2 + assert p.head_layer.roughness.value == 3 p.conformal_roughness = True - assert p.tail_layer.roughness.raw_value == 2 - assert p.head_layer.roughness.raw_value == 2 + assert p.tail_layer.roughness.value == 2 + assert p.head_layer.roughness.value == 2 assert p.conformal_roughness is True p.tail_layer.roughness.value = 4 - assert p.tail_layer.roughness.raw_value == 4 - assert p.head_layer.roughness.raw_value == 4 + assert p.tail_layer.roughness.value == 4 + assert p.head_layer.roughness.value == 4 def test_constain_solvent_roughness(self): p = SurfactantLayer() layer = Layer() p.tail_layer.roughness.value = 2 - assert p.tail_layer.roughness.raw_value == 2 - assert p.head_layer.roughness.raw_value == 3 - assert layer.roughness.raw_value == 3.3 + assert p.tail_layer.roughness.value == 2 + assert p.head_layer.roughness.value == 3 + assert layer.roughness.value == 3.3 p.conformal_roughness = True p.constrain_solvent_roughness(layer.roughness) - assert p.tail_layer.roughness.raw_value == 2 - assert p.head_layer.roughness.raw_value == 2 - assert layer.roughness.raw_value == 2 + assert p.tail_layer.roughness.value == 2 + assert p.head_layer.roughness.value == 2 + assert layer.roughness.value == 2 assert p.conformal_roughness is True p.tail_layer.roughness.value = 4 - assert p.tail_layer.roughness.raw_value == 4 - assert p.head_layer.roughness.raw_value == 4 - assert layer.roughness.raw_value == 4 + assert p.tail_layer.roughness.value == 4 + assert p.head_layer.roughness.value == 4 + assert layer.roughness.value == 4 def test_dict_repr(self): p = SurfactantLayer() @@ -105,42 +108,34 @@ def test_dict_repr(self): 'material': { 'C10H18NO8P in D2O': { 'solvent_fraction': '0.200 dimensionless', - 'sld': '2.269e-6 1 / angstrom ** 2', - 'isld': '0.000e-6 1 / angstrom ** 2', - 'material': { - 'C10H18NO8P': {'sld': '1.246e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'} - }, - 'solvent': { - 'D2O': {'sld': '6.360e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'} - }, + 'sld': '2.269e-6 1/Å^2', + 'isld': '0.000e-6 1/Å^2', + 'material': {'C10H18NO8P': {'sld': '1.246e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, + 'solvent': {'D2O': {'sld': '6.360e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, } }, - 'thickness': '10.000 angstrom', - 'roughness': '3.000 angstrom', + 'thickness': '10.000 Å', + 'roughness': '3.000 Å', }, 'molecular_formula': 'C10H18NO8P', - 'area_per_molecule': '48.20 angstrom ** 2', + 'area_per_molecule': '48.20 Å^2', }, 'tail_layer': { 'DPPC Tail': { 'material': { 'C32D64 in Air': { 'solvent_fraction': '0.000 dimensionless', - 'sld': '8.297e-6 1 / angstrom ** 2', - 'isld': '0.000e-6 1 / angstrom ** 2', - 'material': { - 'C32D64': {'sld': '8.297e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'} - }, - 'solvent': { - 'Air': {'sld': '0.000e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'} - }, + 'sld': '8.297e-6 1/Å^2', + 'isld': '0.000e-6 1/Å^2', + 'material': {'C32D64': {'sld': '8.297e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, + 'solvent': {'Air': {'sld': '0.000e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, } }, - 'thickness': '16.000 angstrom', - 'roughness': '3.000 angstrom', + 'thickness': '16.000 Å', + 'roughness': '3.000 Å', }, 'molecular_formula': 'C32D64', - 'area_per_molecule': '48.20 angstrom ** 2', + 'area_per_molecule': '48.20 Å^2', }, 'area per molecule constrained': False, 'conformal roughness': False, @@ -189,33 +184,89 @@ def test_set_tail_layer(self): assert p.front_layer == new_layer assert p.layers[0] == new_layer - def test_dict_round_trip(self): - p = SurfactantLayer() - q = SurfactantLayer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() - def test_dict_round_trip_area_per_molecule_constraint_enabled(self): - p = SurfactantLayer() - p.constrain_area_per_molecule = True - q = SurfactantLayer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() +def test_dict_round_trip(): + # When + solvent = Material(-0.561, 0, 'H2O') + tail_layer = LayerAreaPerMolecule( + molecular_formula='CO2', + solvent=solvent, + solvent_fraction=0.25, + area_per_molecule=50, + thickness=10, + roughness=4, + ) + head_layer = LayerAreaPerMolecule( + molecular_formula='CH2', + solvent_fraction=0.75, + area_per_molecule=50, + thickness=5, + roughness=2, + ) + p = SurfactantLayer(head_layer=head_layer, tail_layer=tail_layer) + p_dict = p.as_dict() + global_object.map._clear() - def test_dict_round_trip_area_per_molecule_constraint_disabled(self): - p = SurfactantLayer() - p.constrain_area_per_molecule = True - p.constrain_area_per_molecule = False - q = SurfactantLayer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + # Then + q = SurfactantLayer.from_dict(p_dict) - def test_dict_round_trip_roughness_constraint_enabled(self): - p = SurfactantLayer() - p.conformal_roughness = True - q = SurfactantLayer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + # Expect + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) - def test_dict_round_trip_roughness_constraint_disabled(self): - p = SurfactantLayer() - p.conformal_roughness = True - p.conformal_roughness = False - q = SurfactantLayer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + +def test_dict_round_trip_area_per_molecule_constraint_enabled(): + # When + p = SurfactantLayer() + p.constrain_area_per_molecule = True + p_dict = p.as_dict() + global_object.map._clear() + + # Then + q = SurfactantLayer.from_dict(p_dict) + + # Expect + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) + + +def test_dict_round_trip_area_per_molecule_constraint_disabled(): + # When + p = SurfactantLayer() + p.constrain_area_per_molecule = True + p.constrain_area_per_molecule = False + p_dict = p.as_dict() + global_object.map._clear() + + # Then + q = SurfactantLayer.from_dict(p_dict) + + # Expect + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) + + +def test_dict_round_trip_roughness_constraint_enabled(): + # When + p = SurfactantLayer() + p.conformal_roughness = True + p_dict = p.as_dict() + global_object.map._clear() + + # Then + q = SurfactantLayer.from_dict(p_dict) + + # Expect + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) + + +def test_dict_round_trip_roughness_constraint_disabled(): + # When + p = SurfactantLayer() + p.conformal_roughness = True + p.conformal_roughness = False + p_dict = p.as_dict() + global_object.map._clear() + + # Then + q = SurfactantLayer.from_dict(p_dict) + + # Expect + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/sample/elements/layers/test_layer.py b/tests/sample/elements/layers/test_layer.py index 93b6a111..2b75e717 100644 --- a/tests/sample/elements/layers/test_layer.py +++ b/tests/sample/elements/layers/test_layer.py @@ -8,13 +8,15 @@ import unittest import numpy as np +from easyscience import global_object +from numpy.testing import assert_almost_equal +from numpy.testing import assert_equal + from easyreflectometry.calculators.factory import CalculatorFactory from easyreflectometry.parameter_utils import get_as_parameter from easyreflectometry.sample.elements.layers.layer import DEFAULTS from easyreflectometry.sample.elements.layers.layer import Layer from easyreflectometry.sample.elements.materials.material import Material -from numpy.testing import assert_almost_equal -from numpy.testing import assert_equal class TestLayer(unittest.TestCase): @@ -24,14 +26,14 @@ def test_no_arguments(self): assert_equal(p.interface, None) assert_equal(p.material.name, 'EasyMaterial') assert_equal(p.thickness.display_name, 'thickness') - assert_equal(str(p.thickness.unit), 'angstrom') - assert_equal(p.thickness.value.value.magnitude, 10.0) + assert_equal(str(p.thickness.unit), 'Å') + assert_equal(p.thickness.value, 10.0) assert_equal(p.thickness.min, 0.0) assert_equal(p.thickness.max, np.Inf) assert_equal(p.thickness.fixed, True) assert_equal(p.roughness.display_name, 'roughness') - assert_equal(str(p.roughness.unit), 'angstrom') - assert_equal(p.roughness.value.value.magnitude, 3.3) + assert_equal(str(p.roughness.unit), 'Å') + assert_equal(p.roughness.value, 3.3) assert_equal(p.roughness.min, 0.0) assert_equal(p.roughness.max, np.Inf) assert_equal(p.roughness.fixed, True) @@ -43,14 +45,14 @@ def test_shuffled_arguments(self): assert_equal(p.interface, None) assert_equal(p.material.name, 'Boron') assert_equal(p.thickness.display_name, 'thickness') - assert_equal(str(p.thickness.unit), 'angstrom') - assert_equal(p.thickness.value.value.magnitude, 5.0) + assert_equal(str(p.thickness.unit), 'Å') + assert_equal(p.thickness.value, 5.0) assert_equal(p.thickness.min, 0.0) assert_equal(p.thickness.max, np.Inf) assert_equal(p.thickness.fixed, True) assert_equal(p.roughness.display_name, 'roughness') - assert_equal(str(p.roughness.unit), 'angstrom') - assert_equal(p.roughness.value.value.magnitude, 2.0) + assert_equal(str(p.roughness.unit), 'Å') + assert_equal(p.roughness.value, 2.0) assert_equal(p.roughness.min, 0.0) assert_equal(p.roughness.max, np.Inf) assert_equal(p.roughness.fixed, True) @@ -58,8 +60,8 @@ def test_shuffled_arguments(self): def test_only_roughness_key(self): p = Layer(roughness=10.0) assert_equal(p.roughness.display_name, 'roughness') - assert_equal(str(p.roughness.unit), 'angstrom') - assert_equal(p.roughness.value.value.magnitude, 10.0) + assert_equal(str(p.roughness.unit), 'Å') + assert_equal(p.roughness.value, 10.0) assert_equal(p.roughness.min, 0.0) assert_equal(p.roughness.max, np.Inf) assert_equal(p.roughness.fixed, True) @@ -68,14 +70,14 @@ def test_only_roughness_key_paramter(self): roughness = get_as_parameter('roughness', 10, DEFAULTS) roughness.min = -10.0 p = Layer(roughness=roughness) - assert_equal(p.roughness.value.value.magnitude, 10.0) + assert_equal(p.roughness.value, 10.0) assert_equal(p.roughness.min, -10.0) def test_only_thickness_key(self): p = Layer(thickness=10.0) assert_equal(p.thickness.display_name, 'thickness') - assert_equal(str(p.thickness.unit), 'angstrom') - assert_equal(p.thickness.value.value.magnitude, 10.0) + assert_equal(str(p.thickness.unit), 'Å') + assert_equal(p.thickness.value, 10.0) assert_equal(p.thickness.min, 0.0) assert_equal(p.thickness.max, np.Inf) assert_equal(p.thickness.fixed, True) @@ -84,37 +86,37 @@ def test_only_thickness_key_paramter(self): thickness = get_as_parameter('thickness', 10, DEFAULTS) thickness.min = -10.0 p = Layer(thickness=thickness) - assert_equal(p.thickness.value.value.magnitude, 10.0) + assert_equal(p.thickness.value, 10.0) assert_equal(p.thickness.min, -10.0) def test_assign_material(self): m = Material(6.908, -0.278, 'Boron') p = Layer(m, 5.0, 2.0, 'thinBoron') k = Material(2.074, 0.0, 'Silicon') - assert_almost_equal(p.material.sld.raw_value, 6.908) - assert_almost_equal(p.material.isld.raw_value, -0.278) + assert_almost_equal(p.material.sld.value, 6.908) + assert_almost_equal(p.material.isld.value, -0.278) p.assign_material(k) - assert_almost_equal(p.material.sld.raw_value, 2.074) - assert_almost_equal(p.material.isld.raw_value, 0.0) + assert_almost_equal(p.material.sld.value, 2.074) + assert_almost_equal(p.material.isld.value, 0.0) def test_assign_material_with_interface_refnx(self): interface = CalculatorFactory() m = Material(6.908, -0.278, 'Boron', interface=interface) p = Layer(m, 5.0, 2.0, 'thinBoron', interface=interface) k = Material(2.074, 0.0, 'Silicon', interface=interface) - assert_almost_equal(p.interface()._wrapper.storage['layer'][p.uid].sld.real.value, 6.908) - assert_almost_equal(p.interface()._wrapper.storage['layer'][p.uid].sld.imag.value, -0.278) + assert_almost_equal(p.interface()._wrapper.storage['layer'][p.unique_name].sld.real.value, 6.908) + assert_almost_equal(p.interface()._wrapper.storage['layer'][p.unique_name].sld.imag.value, -0.278) p.assign_material(k) - assert_almost_equal(p.interface()._wrapper.storage['layer'][p.uid].sld.real.value, 2.074) - assert_almost_equal(p.interface()._wrapper.storage['layer'][p.uid].sld.imag.value, 0.0) + assert_almost_equal(p.interface()._wrapper.storage['layer'][p.unique_name].sld.real.value, 2.074) + assert_almost_equal(p.interface()._wrapper.storage['layer'][p.unique_name].sld.imag.value, 0.0) def test_dict_repr(self): p = Layer() assert p._dict_repr == { 'EasyLayer': { - 'material': {'EasyMaterial': {'isld': '0.000e-6 1 / angstrom ** 2', 'sld': '4.186e-6 1 / angstrom ** 2'}}, - 'roughness': '3.300 angstrom', - 'thickness': '10.000 angstrom', + 'material': {'EasyMaterial': {'isld': '0.000e-6 1/Å^2', 'sld': '4.186e-6 1/Å^2'}}, + 'roughness': '3.300 Å', + 'thickness': '10.000 Å', } } @@ -122,10 +124,13 @@ def test_repr(self): p = Layer() assert ( p.__repr__() - == 'EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' # noqa: E501 + == 'EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501 ) # noqa: E501 def test_dict_round_trip(self): p = Layer() - q = Layer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + p_dict = p.as_dict() + global_object.map._clear() + + q = Layer.from_dict(p_dict) + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/sample/elements/layers/test_layer_area_per_molecule.py b/tests/sample/elements/layers/test_layer_area_per_molecule.py index e9455f7f..acfabd9f 100644 --- a/tests/sample/elements/layers/test_layer_area_per_molecule.py +++ b/tests/sample/elements/layers/test_layer_area_per_molecule.py @@ -4,9 +4,11 @@ import unittest +from easyscience import global_object +from numpy.testing import assert_almost_equal + from easyreflectometry.sample.elements.layers.layer_area_per_molecule import LayerAreaPerMolecule from easyreflectometry.sample.elements.materials.material import Material -from numpy.testing import assert_almost_equal class TestLayerAreaPerMolecule(unittest.TestCase): @@ -14,19 +16,19 @@ def test_default(self): p = LayerAreaPerMolecule() assert p.molecular_formula == 'C10H18NO8P' assert p.area_per_molecule == 48.2 - assert str(p._area_per_molecule.unit) == 'angstrom ** 2' + assert str(p._area_per_molecule.unit) == 'Å^2' assert p._area_per_molecule.fixed is True - assert p.thickness.raw_value == 10.0 - assert str(p.thickness.unit) == 'angstrom' + assert p.thickness.value == 10.0 + assert str(p.thickness.unit) == 'Å' assert p.thickness.fixed is True - assert p.roughness.raw_value == 3.3 - assert str(p.roughness.unit) == 'angstrom' + assert p.roughness.value == 3.3 + assert str(p.roughness.unit) == 'Å' assert p.roughness.fixed is True assert_almost_equal(p.material.sld, 2.2691419) assert_almost_equal(p.material.isld, 0) assert p.material.name == 'C10H18NO8P in D2O' - assert p.solvent.sld.raw_value == 6.36 - assert p.solvent.isld.raw_value == 0 + assert p.solvent.sld.value == 6.36 + assert p.solvent.isld.value == 0 assert p.solvent.name == 'D2O' assert p.solvent_fraction == 0.2 assert str(p.material._fraction.unit) == 'dimensionless' @@ -45,10 +47,10 @@ def test_from_pars(self): ) assert p.molecular_formula == 'C8O10H12P' assert p.area_per_molecule == 50 - assert p.thickness.raw_value == 12 - assert p.roughness.raw_value == 2 - assert p.solvent.sld.raw_value == -0.561 - assert p.solvent.isld.raw_value == 0 + assert p.thickness.value == 12 + assert p.roughness.value == 2 + assert p.solvent.sld.value == -0.561 + assert p.solvent.isld.value == 0 assert p.solvent_fraction == 0.5 def test_from_pars_constraint(self): @@ -65,16 +67,16 @@ def test_from_pars_constraint(self): assert p.molecular_formula == 'C8O10H12P' assert p.area_per_molecule == 50 assert_almost_equal(p.material.sld, 0.31513666667) - assert p.thickness.raw_value == 12 - assert p.roughness.raw_value == 2 - assert p.solvent.sld.raw_value == -0.561 - assert p.solvent.isld.raw_value == 0 + assert p.thickness.value == 12 + assert p.roughness.value == 2 + assert p.solvent.sld.value == -0.561 + assert p.solvent.isld.value == 0 assert p.solvent_fraction == 0.5 p.area_per_molecule = 30 assert p.area_per_molecule == 30 assert_almost_equal(p.material.sld, 0.712227778) p.thickness.value = 10 - assert p.thickness.raw_value == 10 + assert p.thickness.value == 10 assert_almost_equal(p.material.sld, 0.910773333) def test_solvent_change(self): @@ -92,20 +94,20 @@ def test_solvent_change(self): assert p.area_per_molecule == 50 print(p.material) assert_almost_equal(p.material.sld, 0.31513666667) - assert p.thickness.raw_value == 12 - assert p.roughness.raw_value == 2 - assert p.solvent.sld.raw_value == -0.561 - assert p.solvent.isld.raw_value == 0 + assert p.thickness.value == 12 + assert p.roughness.value == 2 + assert p.solvent.sld.value == -0.561 + assert p.solvent.isld.value == 0 assert p.solvent_fraction == 0.5 d2o = Material(6.335, 0, 'D2O') p.solvent = d2o assert p.molecular_formula == 'C8O10H12P' assert p.area_per_molecule == 50 assert_almost_equal(p.material.sld, 3.7631366667) - assert p.thickness.raw_value == 12 - assert p.roughness.raw_value == 2 - assert p.solvent.sld.raw_value == 6.335 - assert p.solvent.isld.raw_value == 0 + assert p.thickness.value == 12 + assert p.roughness.value == 2 + assert p.solvent.sld.value == 6.335 + assert p.solvent.isld.value == 0 assert p.solvent_fraction == 0.5 def test_molecular_formula_change(self): @@ -122,21 +124,21 @@ def test_molecular_formula_change(self): assert p.molecular_formula == 'C8O10H12P' assert p.area_per_molecule == 50 assert_almost_equal(p.material.sld, 0.31513666667) - assert p.thickness.raw_value == 12 - assert p.roughness.raw_value == 2 + assert p.thickness.value == 12 + assert p.roughness.value == 2 - assert p.solvent.sld.raw_value == -0.561 - assert p.solvent.isld.raw_value == 0 + assert p.solvent.sld.value == -0.561 + assert p.solvent.isld.value == 0 assert p.solvent_fraction == 0.5 assert p.material.name == 'C8O10H12P in H2O' p.molecular_formula = 'C8O10D12P' assert p.molecular_formula == 'C8O10D12P' assert p.area_per_molecule == 50 assert_almost_equal(p.material.sld, 1.3566266666666666) - assert p.thickness.raw_value == 12 - assert p.roughness.raw_value == 2 - assert p.solvent.sld.raw_value == -0.561 - assert p.solvent.isld.raw_value == 0 + assert p.thickness.value == 12 + assert p.roughness.value == 2 + assert p.solvent.sld.value == -0.561 + assert p.solvent.isld.value == 0 assert p.solvent_fraction == 0.5 assert p.material.name == 'C8O10D12P in H2O' @@ -147,22 +149,35 @@ def test_dict_repr(self): 'material': { 'C10H18NO8P in D2O': { 'solvent_fraction': '0.200 dimensionless', - 'sld': '2.269e-6 1 / angstrom ** 2', - 'isld': '0.000e-6 1 / angstrom ** 2', - 'material': { - 'C10H18NO8P': {'sld': '1.246e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'} - }, - 'solvent': {'D2O': {'sld': '6.360e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'}}, + 'sld': '2.269e-6 1/Å^2', + 'isld': '0.000e-6 1/Å^2', + 'material': {'C10H18NO8P': {'sld': '1.246e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, + 'solvent': {'D2O': {'sld': '6.360e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, } }, - 'thickness': '10.000 angstrom', - 'roughness': '3.300 angstrom', + 'thickness': '10.000 Å', + 'roughness': '3.300 Å', }, 'molecular_formula': 'C10H18NO8P', - 'area_per_molecule': '48.20 angstrom ** 2', + 'area_per_molecule': '48.20 Å^2', } def test_dict_round_trip(self): - p = LayerAreaPerMolecule() - q = LayerAreaPerMolecule.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + # When + solvent = Material(-0.561, 0, 'H2O') + p = LayerAreaPerMolecule( + molecular_formula='CO2', + solvent=solvent, + solvent_fraction=0.5, + area_per_molecule=50, + thickness=10, + roughness=3, + ) + p_dict = p.as_dict() + global_object.map._clear() + + # Then + q = LayerAreaPerMolecule.from_dict(p_dict) + + # Expect + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/sample/elements/layers/test_layer_collection.py b/tests/sample/elements/layers/test_layer_collection.py index 9c91c570..c2c0bec2 100644 --- a/tests/sample/elements/layers/test_layer_collection.py +++ b/tests/sample/elements/layers/test_layer_collection.py @@ -7,21 +7,21 @@ import unittest +from easyscience import global_object +from numpy.testing import assert_equal + from easyreflectometry.sample.assemblies.repeating_multilayer import RepeatingMultilayer from easyreflectometry.sample.elements.layers.layer import Layer from easyreflectometry.sample.elements.layers.layer_collection import LayerCollection from easyreflectometry.sample.elements.materials.material import Material -from numpy.testing import assert_equal class TestLayerCollection(unittest.TestCase): def test_default(self): p = LayerCollection() - assert_equal(p.name, 'EasyLayers') + assert_equal(p.name, 'EasyLayerCollection') assert_equal(p.interface, None) - assert_equal(len(p), 2) - assert_equal(p[0].name, 'EasyLayer') - assert_equal(p[1].name, 'EasyLayer') + assert_equal(len(p), 0) def test_from_pars(self): m = Material(6.908, -0.278, 'Boron') @@ -44,35 +44,31 @@ def test_from_pars_item(self): assert_equal(layers.interface, None) def test_dict_repr(self): - p = LayerCollection() + p = LayerCollection(layers=[Layer(), Layer()]) assert p._dict_repr == { - 'EasyLayers': [ + 'EasyLayerCollection': [ { 'EasyLayer': { - 'material': { - 'EasyMaterial': {'sld': '4.186e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'} - }, - 'thickness': '10.000 angstrom', - 'roughness': '3.300 angstrom', + 'material': {'EasyMaterial': {'sld': '4.186e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, + 'thickness': '10.000 Å', + 'roughness': '3.300 Å', } }, { 'EasyLayer': { - 'material': { - 'EasyMaterial': {'sld': '4.186e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'} - }, - 'thickness': '10.000 angstrom', - 'roughness': '3.300 angstrom', + 'material': {'EasyMaterial': {'sld': '4.186e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, + 'thickness': '10.000 Å', + 'roughness': '3.300 Å', } }, ] } def test_repr(self): - p = LayerCollection() + p = LayerCollection([Layer(), Layer()]) assert ( p.__repr__() - == 'EasyLayers:\n- EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n- EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' # noqa: E501 + == 'EasyLayerCollection:\n- EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n- EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501 ) def test_dict_round_trip(self): @@ -84,9 +80,11 @@ def test_dict_round_trip(self): r = LayerCollection() r.insert(0, p) r.append(q) + r_dict = r.as_dict() + global_object.map._clear() # Then - s = LayerCollection.from_dict(r.as_dict()) + s = LayerCollection.from_dict(r_dict) # Expect - assert s.as_data_dict() == r.as_data_dict() + assert sorted(r.as_data_dict()) == sorted(s.as_data_dict()) diff --git a/tests/sample/elements/materials/test_material.py b/tests/sample/elements/materials/test_material.py index f5c39967..0d836037 100644 --- a/tests/sample/elements/materials/test_material.py +++ b/tests/sample/elements/materials/test_material.py @@ -9,6 +9,8 @@ import unittest import numpy as np +from easyscience import global_object + from easyreflectometry.parameter_utils import get_as_parameter from easyreflectometry.sample.elements.materials.material import DEFAULTS from easyreflectometry.sample.elements.materials.material import Material @@ -20,14 +22,14 @@ def test_no_arguments(self): assert p.name == 'EasyMaterial' assert p.interface is None assert p.sld.display_name == 'sld' - assert str(p.sld.unit) == '1 / angstrom ** 2' - assert p.sld.value.value.magnitude == 4.186 + assert str(p.sld.unit) == '1/Å^2' + assert p.sld.value == 4.186 assert p.sld.min == -np.Inf assert p.sld.max == np.Inf assert p.sld.fixed is True assert p.isld.display_name == 'isld' - assert str(p.isld.unit) == '1 / angstrom ** 2' - assert p.isld.value.value.magnitude == 0.0 + assert str(p.isld.unit) == '1/Å^2' + assert p.isld.value == 0.0 assert p.isld.min == -np.Inf assert p.isld.max == np.Inf assert p.isld.fixed is True @@ -37,14 +39,14 @@ def test_shuffled_arguments(self): assert p.name == 'Boron' assert p.interface is None assert p.sld.display_name == 'sld' - assert str(p.sld.unit) == '1 / angstrom ** 2' - assert p.sld.value.value.magnitude == 6.908 + assert str(p.sld.unit) == '1/Å^2' + assert p.sld.value == 6.908 assert p.sld.min == -np.Inf assert p.sld.max == np.Inf assert p.sld.fixed is True assert p.isld.display_name == 'isld' - assert str(p.isld.unit) == '1 / angstrom ** 2' - assert p.isld.value.value.magnitude == -0.278 + assert str(p.isld.unit) == '1/Å^2' + assert p.isld.value == -0.278 assert p.isld.min == -np.Inf assert p.isld.max == np.Inf assert p.isld.fixed is True @@ -52,8 +54,8 @@ def test_shuffled_arguments(self): def test_only_sld_key(self): p = Material(sld=10) assert p.sld.display_name == 'sld' - assert str(p.sld.unit) == '1 / angstrom ** 2' - assert p.sld.value.value.magnitude == 10 + assert str(p.sld.unit) == '1/Å^2' + assert p.sld.value == 10 assert p.sld.min == -np.Inf assert p.sld.max == np.Inf assert p.sld.fixed is True @@ -62,14 +64,14 @@ def test_only_sld_key_parameter(self): sld = get_as_parameter('sld', 10, DEFAULTS) sld.min = -10.0 p = Material(sld=sld) - assert p.sld.value.value.magnitude == 10 + assert p.sld.value == 10 assert p.sld.min == -10 def test_only_isld_key(self): p = Material(isld=10) assert p.isld.display_name == 'isld' - assert str(p.isld.unit) == '1 / angstrom ** 2' - assert p.isld.value.value.magnitude == 10 + assert str(p.isld.unit) == '1/Å^2' + assert p.isld.value == 10 assert p.isld.min == -np.Inf assert p.isld.max == np.Inf assert p.isld.fixed is True @@ -78,18 +80,22 @@ def test_only_isld_key_parameter(self): isld = get_as_parameter('isld', 10, DEFAULTS) isld.min = -10.0 p = Material(isld=isld) - assert p.isld.value.value.magnitude == 10 + assert p.isld.value == 10 assert p.isld.min == -10 def test_dict_repr(self): p = Material() - assert p._dict_repr == {'EasyMaterial': {'sld': '4.186e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'}} + assert p._dict_repr == {'EasyMaterial': {'sld': '4.186e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}} def test_repr(self): p = Material() - assert p.__repr__() == 'EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n' + assert p.__repr__() == 'EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n' def test_dict_round_trip(self): p = Material() - q = Material.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + p_dict = p.as_dict() + global_object.map._clear() + + q = Material.from_dict(p_dict) + + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/sample/elements/materials/test_material_collection.py b/tests/sample/elements/materials/test_material_collection.py index 1374dd6e..cdfc47b0 100644 --- a/tests/sample/elements/materials/test_material_collection.py +++ b/tests/sample/elements/materials/test_material_collection.py @@ -7,11 +7,13 @@ import unittest +from easyscience import global_object + from easyreflectometry.sample.elements.materials.material import Material from easyreflectometry.sample.elements.materials.material_collection import MaterialCollection -class TestLayerCollection(unittest.TestCase): +class TestMaterialCollection(unittest.TestCase): def test_default(self): p = MaterialCollection() assert p.name == 'EasyMaterials' @@ -40,16 +42,17 @@ def test_dict_repr(self): p = MaterialCollection() assert p._dict_repr == { 'EasyMaterials': [ - {'EasyMaterial': {'isld': '0.000e-6 1 / angstrom ** 2', 'sld': '4.186e-6 1 / angstrom ** 2'}}, - {'EasyMaterial': {'isld': '0.000e-6 1 / angstrom ** 2', 'sld': '4.186e-6 1 / angstrom ** 2'}}, + {'EasyMaterial': {'isld': '0.000e-6 1/Å^2', 'sld': '4.186e-6 1/Å^2'}}, + {'EasyMaterial': {'isld': '0.000e-6 1/Å^2', 'sld': '4.186e-6 1/Å^2'}}, ] } def test_repr(self): p = MaterialCollection() + p.__repr__() assert ( p.__repr__() - == 'EasyMaterials:\n- EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n- EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n' # noqa: E501 + == 'EasyMaterials:\n- EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n- EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n' # noqa: E501 ) def test_dict_round_trip(self): @@ -59,9 +62,11 @@ def test_dict_round_trip(self): p = MaterialCollection() p.insert(0, m) p.append(k) + p_dict = p.as_dict() + global_object.map._clear() # Then - q = MaterialCollection.from_dict(p.as_dict()) + q = MaterialCollection.from_dict(p_dict) # Expect - assert p.as_data_dict() == q.as_data_dict() + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/sample/elements/materials/test_material_density.py b/tests/sample/elements/materials/test_material_density.py index 864d094a..cde67369 100644 --- a/tests/sample/elements/materials/test_material_density.py +++ b/tests/sample/elements/materials/test_material_density.py @@ -1,9 +1,11 @@ import unittest import numpy as np -from easyreflectometry.sample.elements.materials.material_density import MaterialDensity +from easyscience import global_object from numpy.testing import assert_almost_equal +from easyreflectometry.sample.elements.materials.material_density import MaterialDensity + class TestMaterialDensity(unittest.TestCase): def test_default(self): @@ -11,47 +13,51 @@ def test_default(self): assert p.name == 'EasyMaterialDensity' assert p.interface is None assert p.density.display_name == 'density' - assert str(p.density.unit) == 'gram / centimeter ** 3' - assert p.density.value.value.magnitude == 2.33 + assert str(p.density.unit) == 'kg/L' + assert p.density.value == 2.33 assert p.density.min == 0 assert p.density.max == np.Inf assert p.density.fixed is True def test_default_constraint(self): p = MaterialDensity() - assert p.density.value.value.magnitude == 2.33 - assert_almost_equal(p.sld.value.value.magnitude, 2.073705382) + assert p.density.value == 2.33 + assert_almost_equal(p.sld.value, 2.073705382) p.density.value = 2 - assert_almost_equal(p.sld.value.value.magnitude, 1.780004619) + assert_almost_equal(p.sld.value, 1.780004619) def test_from_pars(self): p = MaterialDensity('Co', 8.9, 'Cobalt') - assert p.density.value.value.magnitude == 8.9 - assert_almost_equal(p.sld.value.value.magnitude, 2.2645412328256) + assert p.density.value == 8.9 + assert_almost_equal(p.sld.value, 2.2645412328256) assert p.chemical_structure == 'Co' def test_chemical_structure_change(self): p = MaterialDensity('Co', 8.9, 'Cobolt') - assert p.density.value.value.magnitude == 8.9 - assert_almost_equal(p.sld.value.value.magnitude, 2.2645412328256) - assert_almost_equal(p.isld.value.value.magnitude, 0.0) + assert p.density.value == 8.9 + assert_almost_equal(p.sld.value, 2.2645412328256) + assert_almost_equal(p.isld.value, 0.0) assert p.chemical_structure == 'Co' p.chemical_structure = 'B' - assert p.density.value.value.magnitude == 8.9 - assert_almost_equal(p.sld.value.value.magnitude, 4.820107844970) - assert_almost_equal(p.isld.value.value.magnitude, -0.19098540517806603) + assert p.density.value == 8.9 + assert_almost_equal(p.sld.value, 4.820107844970) + assert_almost_equal(p.isld.value, -0.19098540517806603) assert p.chemical_structure == 'B' def test_dict_repr(self): p = MaterialDensity() print(p._dict_repr) assert p._dict_repr == { - 'EasyMaterialDensity': {'sld': '2.074e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'}, + 'EasyMaterialDensity': {'sld': '2.074e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}, 'chemical_structure': 'Si', - 'density': '2.33e+00 gram / centimeter ** 3', + 'density': '2.33e+00 kg/L', } def test_dict_round_trip(self): p = MaterialDensity() - q = MaterialDensity.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + p_dict = p.as_dict() + global_object.map._clear() + + q = MaterialDensity.from_dict(p_dict) + + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/sample/elements/materials/test_material_mixture.py b/tests/sample/elements/materials/test_material_mixture.py index 18f8bb7f..9a8db8e2 100644 --- a/tests/sample/elements/materials/test_material_mixture.py +++ b/tests/sample/elements/materials/test_material_mixture.py @@ -1,133 +1,141 @@ -import unittest from unittest.mock import MagicMock +from easyscience import global_object +from numpy.testing import assert_almost_equal + from easyreflectometry.sample.elements.materials.material import Material from easyreflectometry.sample.elements.materials.material_mixture import MaterialMixture -from numpy.testing import assert_almost_equal -class TestMaterialMixture(unittest.TestCase): - def test_default(self): - p = MaterialMixture() - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert p.sld == Material().sld.raw_value - assert p.isld == Material().isld.raw_value - assert str(p._sld.unit) == '1 / angstrom ** 2' - assert str(p._isld.unit) == '1 / angstrom ** 2' - - def test_default_constraint(self): - p = MaterialMixture() - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert p.sld == Material().sld.raw_value - assert p.isld == Material().isld.raw_value - p.material_a.sld.value = 0 - p.material_b.isld.value = -1 - assert_almost_equal(p.sld, 2.093) - assert_almost_equal(p.isld, -0.5) - assert str(p._sld.unit) == '1 / angstrom ** 2' - assert str(p._isld.unit) == '1 / angstrom ** 2' +class TestMaterialMixture: + def test_default(self) -> None: + material_mixture = MaterialMixture() + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 4.186) + assert_almost_equal(material_mixture.isld, 0) + assert str(material_mixture._sld.unit) == '1/Å^2' + assert str(material_mixture._isld.unit) == '1/Å^2' + + def test_default_constraint(self) -> None: + material_mixture = MaterialMixture() + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 4.186) + assert_almost_equal(material_mixture.isld, 0) + material_mixture.material_a.sld.value = 0 + material_mixture.material_b.isld.value = -1 + assert_almost_equal(material_mixture.sld, 2.093) + assert_almost_equal(material_mixture.isld, -0.5) + assert str(material_mixture._sld.unit) == '1/Å^2' + assert str(material_mixture._isld.unit) == '1/Å^2' def test_fraction_constraint(self): p = Material() q = Material(6.908, -0.278, 'Boron') - r = MaterialMixture(p, q, 0.2) - assert r.fraction == 0.2 - assert_almost_equal(r.sld, 4.7304) - assert_almost_equal(r.isld, -0.0556) - r._fraction.value = 0.5 - assert r.fraction == 0.5 - assert_almost_equal(r.sld, 5.54700) - assert_almost_equal(r.isld, -0.1390) - - def test_material_a_change(self): - p = MaterialMixture() - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert p.sld == Material().sld.raw_value - assert p.isld == Material().isld.raw_value + material_mixture = MaterialMixture(p, q, 0.2) + assert material_mixture.fraction == 0.2 + assert_almost_equal(material_mixture.sld, 4.7304) + assert_almost_equal(material_mixture.isld, -0.0556) + material_mixture._fraction.value = 0.5 + assert material_mixture.fraction == 0.5 + assert_almost_equal(material_mixture.sld, 5.54700) + assert_almost_equal(material_mixture.isld, -0.1390) + + def test_material_a_change(self) -> None: + material_mixture = MaterialMixture() + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 4.186) + assert_almost_equal(material_mixture.isld, 0) q = Material(6.908, -0.278, 'Boron') - p.material_a = q - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert_almost_equal(p.sld, 5.54700) - assert_almost_equal(p.isld, -0.1390) + material_mixture.material_a = q + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 5.54700) + assert_almost_equal(material_mixture.isld, -0.1390) - def test_material_b_change(self): - p = MaterialMixture() - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert p.sld == Material().sld.raw_value - assert p.isld == Material().isld.raw_value + def test_material_b_change(self) -> None: + material_mixture = MaterialMixture() + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 4.186) + assert_almost_equal(material_mixture.isld, 0) q = Material(6.908, -0.278, 'Boron') - p.material_b = q - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert_almost_equal(p.sld, 5.54700) - assert_almost_equal(p.isld, -0.1390) + material_mixture.material_b = q + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 5.54700) + assert_almost_equal(material_mixture.isld, -0.1390) - def test_material_b_change_double(self): - p = MaterialMixture() - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert p.sld == Material().sld.raw_value - assert p.isld == Material().isld.raw_value + def test_material_b_change_double(self) -> None: + material_mixture = MaterialMixture() + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 4.186) + assert_almost_equal(material_mixture.isld, 0) q = Material(6.908, -0.278, 'Boron') - p.material_b = q - assert p.name == 'EasyMaterial/Boron' - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert_almost_equal(p.sld, 5.54700) - assert_almost_equal(p.isld, -0.1390) + material_mixture.material_b = q + assert material_mixture.name == 'EasyMaterial/Boron' + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 5.54700) + assert_almost_equal(material_mixture.isld, -0.1390) r = Material(0.00, 0.00, 'ACMW') - p.material_b = r - assert p.name == 'EasyMaterial/ACMW' - assert p.fraction == 0.5 - assert str(p._fraction.unit) == 'dimensionless' - assert_almost_equal(p.sld, 2.0930) - assert_almost_equal(p.isld, 0.0000) + material_mixture.material_b = r + assert material_mixture.name == 'EasyMaterial/ACMW' + assert material_mixture.fraction == 0.5 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 2.0930) + assert_almost_equal(material_mixture.isld, 0.0000) def test_from_pars(self): p = Material() q = Material(6.908, -0.278, 'Boron') - r = MaterialMixture(p, q, 0.2) - assert r.fraction == 0.2 - assert str(r._fraction.unit) == 'dimensionless' - assert_almost_equal(r.sld, 4.7304) - assert_almost_equal(r.isld, -0.0556) - assert str(r._sld.unit) == '1 / angstrom ** 2' - assert str(r._isld.unit) == '1 / angstrom ** 2' - - def test_dict_repr(self): - p = MaterialMixture() - assert p._dict_repr == { + material_mixture = MaterialMixture(p, q, 0.2) + assert material_mixture.fraction == 0.2 + assert str(material_mixture._fraction.unit) == 'dimensionless' + assert_almost_equal(material_mixture.sld, 4.7304) + assert_almost_equal(material_mixture.isld, -0.0556) + assert str(material_mixture._sld.unit) == '1/Å^2' + assert str(material_mixture._isld.unit) == '1/Å^2' + + def test_dict_repr(self) -> None: + material_mixture = MaterialMixture() + assert material_mixture._dict_repr == { 'EasyMaterial/EasyMaterial': { 'fraction': '0.500 dimensionless', - 'sld': '4.186e-6 1 / angstrom ** 2', - 'isld': '0.000e-6 1 / angstrom ** 2', - 'material_a': {'EasyMaterial': {'sld': '4.186e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'}}, - 'material_b': {'EasyMaterial': {'sld': '4.186e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'}}, + 'sld': '4.186e-6 1/Å^2', + 'isld': '0.000e-6 1/Å^2', + 'material_a': {'EasyMaterial': {'sld': '4.186e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, + 'material_b': {'EasyMaterial': {'sld': '4.186e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, } } - def test_dict_round_trip(self): + def test_dict_round_trip(self) -> None: + # When p = MaterialMixture() - q = MaterialMixture.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + p_dict = p.as_dict() + global_object.map._clear() - def test_update_name(self): + # Then + q = MaterialMixture.from_dict(p_dict) + + # Expect + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) + + def test_update_name(self) -> None: # When - p = MaterialMixture() + material_mixture = MaterialMixture() mock_material_a = MagicMock() mock_material_a.name = 'name_a' - p._material_a = mock_material_a + material_mixture._material_a = mock_material_a mock_material_b = MagicMock() mock_material_b.name = 'name_b' - p._material_b = mock_material_b + material_mixture._material_b = mock_material_b # Then - p._update_name() + material_mixture._update_name() # Expect - assert p.name == 'name_a/name_b' + assert material_mixture.name == 'name_a/name_b' diff --git a/tests/sample/elements/materials/test_material_solvated.py b/tests/sample/elements/materials/test_material_solvated.py index 15795660..4fe90cd7 100644 --- a/tests/sample/elements/materials/test_material_solvated.py +++ b/tests/sample/elements/materials/test_material_solvated.py @@ -1,11 +1,13 @@ from unittest.mock import MagicMock +import pytest +from easyscience import global_object +from easyscience.Objects.new_variable import Parameter + import easyreflectometry.sample.elements.materials.material_mixture import easyreflectometry.sample.elements.materials.material_solvated -import pytest from easyreflectometry.sample.elements.materials.material import Material from easyreflectometry.sample.elements.materials.material_solvated import MaterialSolvated -from easyscience.Objects.ObjectClasses import Parameter class TestMaterialSolvated: @@ -14,7 +16,7 @@ def material_solvated(self, monkeypatch) -> MaterialSolvated: self.material = Material(sld=1.0, isld=0, name='material') self.solvent = Material(sld=2.0, isld=0, name='solvent') self.mock_solvent_fraction = MagicMock(spec=Parameter) - self.mock_solvent_fraction.raw_value = 0.1 + self.mock_solvent_fraction.value = 0.1 self.mock_interface = MagicMock() self.mock_Parameter = MagicMock() self.mock_FunctionalConstraint = MagicMock() @@ -109,17 +111,21 @@ def test_dict_repr(self) -> None: assert p._dict_repr == { 'D2O in H2O': { 'solvent_fraction': '0.200 dimensionless', - 'sld': '4.976e-6 1 / angstrom ** 2', - 'isld': '0.000e-6 1 / angstrom ** 2', - 'material': {'D2O': {'sld': '6.360e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'}}, - 'solvent': {'H2O': {'sld': '-0.561e-6 1 / angstrom ** 2', 'isld': '0.000e-6 1 / angstrom ** 2'}}, + 'sld': '4.976e-6 1/Å^2', + 'isld': '0.000e-6 1/Å^2', + 'material': {'D2O': {'sld': '6.360e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, + 'solvent': {'H2O': {'sld': '-0.561e-6 1/Å^2', 'isld': '0.000e-6 1/Å^2'}}, } } def test_dict_round_trip(self): p = MaterialSolvated() - q = MaterialSolvated.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() + p_dict = p.as_dict() + global_object.map._clear() + + q = MaterialSolvated.from_dict(p_dict) + + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) def test_update_name(self, material_solvated: MaterialSolvated) -> None: # When diff --git a/tests/sample/test_sample.py b/tests/sample/test_sample.py index a2c83213..0a916173 100644 --- a/tests/sample/test_sample.py +++ b/tests/sample/test_sample.py @@ -7,6 +7,9 @@ import unittest +from easyscience import global_object +from numpy.testing import assert_equal + from easyreflectometry.sample import Layer from easyreflectometry.sample import LayerCollection from easyreflectometry.sample import Material @@ -14,7 +17,6 @@ from easyreflectometry.sample import RepeatingMultilayer from easyreflectometry.sample import Sample from easyreflectometry.sample import SurfactantLayer -from numpy.testing import assert_equal class TestSample(unittest.TestCase): @@ -60,7 +62,7 @@ def test_repr(self): p = Sample() assert ( p.__repr__() - == 'EasySample:\n- EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n- EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' # noqa: E501 + == 'EasySample:\n- EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n- EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501 ) def test_dict_round_trip(self): @@ -72,9 +74,10 @@ def test_dict_round_trip(self): p.append(multilayer) repeating = RepeatingMultilayer() p.append(repeating) + p_dict = p.as_dict() + global_object.map._clear() # Then - q = Sample.from_dict(p.as_dict()) + q = Sample.from_dict(p_dict) - # Expect - assert p.as_data_dict() == q.as_data_dict() + assert sorted(p.as_data_dict()) == sorted(q.as_data_dict()) diff --git a/tests/special/test_calculations.py b/tests/special/test_calculations.py index 45f9d0bf..3e3211a2 100644 --- a/tests/special/test_calculations.py +++ b/tests/special/test_calculations.py @@ -2,11 +2,12 @@ import unittest +from numpy.testing import assert_almost_equal + from easyreflectometry.special.calculations import area_per_molecule_to_scattering_length_density from easyreflectometry.special.calculations import molecular_weight from easyreflectometry.special.calculations import neutron_scattering_length from easyreflectometry.special.calculations import weighted_average -from numpy.testing import assert_almost_equal class TestMaterialMixture(unittest.TestCase): diff --git a/tests/test_data.py b/tests/test_data.py index eda39985..ded2107e 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -4,15 +4,16 @@ import os import unittest -import easyreflectometry import numpy as np -from easyreflectometry.data import _load_orso -from easyreflectometry.data import _load_txt -from easyreflectometry.data import load from numpy.testing import assert_almost_equal from orsopy.fileio import Header from orsopy.fileio import load_orso +import easyreflectometry +from easyreflectometry.data import _load_orso +from easyreflectometry.data import _load_txt +from easyreflectometry.data import load + PATH_STATIC = os.path.join(os.path.dirname(easyreflectometry.__file__), '..', '..', 'tests' , '_static') diff --git a/tests/test_fitting.py b/tests/test_fitting.py index d92de5fd..42948feb 100644 --- a/tests/test_fitting.py +++ b/tests/test_fitting.py @@ -1,7 +1,9 @@ __author__ = 'github.com/arm61' import os -import unittest + +import pytest +from easyscience.fitting.minimizers.factory import AvailableMinimizers import easyreflectometry from easyreflectometry.calculators import CalculatorFactory @@ -16,43 +18,44 @@ PATH_STATIC = os.path.join(os.path.dirname(easyreflectometry.__file__), '..', '..', 'tests', '_static') -class TestFitting(unittest.TestCase): - def test_fitting(self): - fpath = os.path.join(PATH_STATIC, 'example.ort') - data = load(fpath) - si = Material(2.07, 0, 'Si') - sio2 = Material(3.47, 0, 'SiO2') - film = Material(2.0, 0, 'Film') - d2o = Material(6.36, 0, 'D2O') - si_layer = Layer(si, 0, 0, 'Si layer') - sio2_layer = Layer(sio2, 30, 3, 'SiO2 layer') - film_layer = Layer(film, 250, 3, 'Film Layer') - superphase = Layer(d2o, 0, 3, 'D2O Subphase') - sample = Sample( - si_layer, - sio2_layer, - film_layer, - superphase, - name='Film Structure', - ) - resolution_function = PercentageFhwm(0.02) - model = Model(sample, 1, 1e-6, resolution_function, 'Film Model') - # Thicknesses - sio2_layer.thickness.bounds = (15, 50) - film_layer.thickness.bounds = (200, 300) - # Roughnesses - sio2_layer.roughness.bounds = (1, 15) - film_layer.roughness.bounds = (1, 15) - superphase.roughness.bounds = (1, 15) - # Scattering length density - film.sld.bounds = (0.1, 3) - # Background - model.background.bounds = (1e-7, 1e-5) - # Scale - model.scale.bounds = (0.5, 1.5) - interface = CalculatorFactory() - model.interface = interface - fitter = Fitter(model) - analysed = fitter.fit(data) - assert 'R_0_model' in analysed.keys() - assert 'SLD_0' in analysed.keys() +@pytest.mark.parametrize('minimizer', [AvailableMinimizers.Bumps, AvailableMinimizers.DFO, AvailableMinimizers.LMFit]) +def test_fitting(minimizer): + fpath = os.path.join(PATH_STATIC, 'example.ort') + data = load(fpath) + si = Material(2.07, 0, 'Si') + sio2 = Material(3.47, 0, 'SiO2') + film = Material(2.0, 0, 'Film') + d2o = Material(6.36, 0, 'D2O') + si_layer = Layer(si, 0, 0, 'Si layer') + sio2_layer = Layer(sio2, 30, 3, 'SiO2 layer') + film_layer = Layer(film, 250, 3, 'Film Layer') + superphase = Layer(d2o, 0, 3, 'D2O Subphase') + sample = Sample( + si_layer, + sio2_layer, + film_layer, + superphase, + name='Film Structure', + ) + resolution_function = PercentageFhwm(0.02) + model = Model(sample, 1, 1e-6, resolution_function, 'Film Model') + # Thicknesses + sio2_layer.thickness.bounds = (15, 50) + film_layer.thickness.bounds = (200, 300) + # Roughnesses + sio2_layer.roughness.bounds = (1, 15) + film_layer.roughness.bounds = (1, 15) + superphase.roughness.bounds = (1, 15) + # Scattering length density + film.sld.bounds = (0.1, 3) + # Background + model.background.bounds = (1e-7, 1e-5) + # Scale + model.scale.bounds = (0.5, 1.5) + interface = CalculatorFactory() + model.interface = interface + fitter = Fitter(model) + fitter.easy_f.switch_minimizer(minimizer) + analysed = fitter.fit(data) + assert 'R_0_model' in analysed.keys() + assert 'SLD_0' in analysed.keys() diff --git a/tests/test_parameter_utils.py b/tests/test_parameter_utils.py index 174a350a..910f4b4b 100644 --- a/tests/test_parameter_utils.py +++ b/tests/test_parameter_utils.py @@ -1,8 +1,9 @@ import numpy as np import pytest -from easyreflectometry.parameter_utils import get_as_parameter from numpy.testing import assert_equal +from easyreflectometry.parameter_utils import get_as_parameter + PARAMETER_DETAILS = { 'test_parameter': { 'description': 'Test parameter', @@ -33,7 +34,7 @@ def test_get_as_parameter(): # Expected test_parameter.name == 'test_parameter' assert_equal(str(test_parameter.unit), 'dimensionless') - assert_equal(test_parameter.raw_value, 1.0) + assert_equal(test_parameter.value, 1.0) assert_equal(test_parameter.min, 0.0) assert_equal(test_parameter.max, np.Inf) assert_equal(test_parameter.fixed, True) @@ -48,7 +49,7 @@ def test_get_as_parameter_from_float(): test_parameter = get_as_parameter('test_parameter', float(test_parameter), PARAMETER_DETAILS) # Expected - assert_equal(test_parameter.raw_value, 2.0) + assert_equal(test_parameter.value, 2.0) def test_get_as_parameter_from_int(): @@ -59,7 +60,7 @@ def test_get_as_parameter_from_int(): test_parameter = get_as_parameter('test_parameter', int(test_parameter), PARAMETER_DETAILS) # Expected - assert_equal(test_parameter.raw_value, 2.0) + assert_equal(test_parameter.value, 2.0) def test_get_as_parameter_from_parameter(): @@ -72,7 +73,7 @@ def test_get_as_parameter_from_parameter(): # Expected test_parameter.name == 'test_parameter' assert_equal(str(test_parameter.unit), 'dimensionless') - assert_equal(test_parameter.raw_value, 10.0) + assert_equal(test_parameter.value, 10.0) assert_equal(test_parameter.min, -10.0) assert_equal(test_parameter.max, 10.0) assert_equal(test_parameter.fixed, False)