diff --git a/README.md b/README.md index 993a111..cd6dcc3 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,9 @@ refl1d models ============= Curated repository of refl1d models. + +``` +conda env create -f environment.yml +source activate refl1d_models +pip install -e . +``` diff --git a/environment.yml b/environment.yml index 6720ebd..95b71b3 100644 --- a/environment.yml +++ b/environment.yml @@ -1,4 +1,4 @@ -name: mypythonapp +name: refl1d_models channels: - conda-forge dependencies: @@ -14,6 +14,7 @@ dependencies: # jupyter: list all jupyter dependencies here, if applicable - jupyterlab - ipympl + - notebook # -- Development dependencies # utils: - pre-commit diff --git a/pyproject.toml b/pyproject.toml index d21b5fd..ce32462 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,6 +57,6 @@ norecursedirs = [".git", "tmp*", "_tmp*", "__pycache__", "*dataset*", "*data_set [tool.ruff] line-length = 120 -select = ["A", "ARG","ASYNC","BLE","C90", "E", "F", "I", "N", "UP032", "W"] +lint.select = ["A", "ARG","ASYNC","BLE","C90", "E", "F", "I", "N", "UP032", "W"] # Add additional 3rd party tool configuration here as needed diff --git a/src/refl1d_models/mixed_model.py b/src/refl1d_models/mixed_model.py new file mode 100644 index 0000000..883e196 --- /dev/null +++ b/src/refl1d_models/mixed_model.py @@ -0,0 +1,87 @@ +""" +This module provides an example of a mixed model. +In this example, we incoherently add two cross-sections +from a non-polarized neutron reflectometry experiment on +a nickel film. +""" + +from refl1d.names import SLD, FitProblem, MixedExperiment + +from .probes import create_qprobe + + +def create_mixed_model(datafile: str = None): + """ + Create a mixed model for the given datafile + :param datafile: The datafile to use for the model + """ + + # Define a probe + probe = create_qprobe(datafile) + + # Define the materials for the structure + # The SLD class defines the material properties. + # The rho parameter is the scattering length density of the material. + thf = SLD("THF", rho=6.2) + silicon = SLD("Si", rho=2.07) + titanium = SLD("Ti", rho=-2.0) + + # In this exammple, we are using the same material for the up and down spins + # In this case, nickel has a nominal SLD of 9.4 +- 1.46 for the two spin states + nickel_up = SLD("Ni_up", rho=8.3) + nickel_down = SLD("Ni_down", rho=10.4) + + # Here we will add an oxide layer on top of the nickel + material = SLD(name="material", rho=5.6) + + # Create the sample structure + # In this case the incoming beam is coming through the Si substrate + sample_up = thf(0, 5) | material(34, 20) | nickel_up(555, 10) | titanium(50, 3) | silicon + sample_down = thf(0, 5) | material(34, 20) | nickel_down(555, 10) | titanium(50, 3) | silicon + + # Create a MixedExperiment. We provide the two samples, the probe, the ratio of the contribution + # of the two samples, and whether the two samples are coherently added or not. + experiment = MixedExperiment(samples=[sample_up, sample_down], probe=probe, ratio=[1, 0.75], coherent=False) + + # Define the range of the parameters + sample_down["THF"].material.rho.range(5.5, 7) + sample_down["THF"].interface.range(1, 25) + + sample_up["Ni_up"].material.rho.range(5, 12) + sample_down["Ni_down"].material.rho.range(5, 12) + sample_down["Ni_down"].interface.range(1, 25) + sample_down["Ni_down"].thickness.range(400, 600) + + sample_down["Ti"].thickness.range(10.0, 80.0) + sample_down["Ti"].material.rho.range(-3.0, 0) + sample_down["Ti"].interface.range(1.0, 23.0) + + sample_up["Ti"].thickness = sample_down["Ti"].thickness + sample_up["Ti"].interface = sample_down["Ti"].interface + sample_up["Ti"].material.rho = sample_down["Ti"].material.rho + + sample_down["material"].thickness.range(10.0, 50.0) + sample_down["material"].material.rho.range(1.0, 6) + sample_down["material"].interface.range(1.0, 33.0) + + sample_up["material"].thickness = sample_down["material"].thickness + sample_up["material"].interface = sample_down["material"].interface + sample_up["material"].material.rho = sample_down["material"].material.rho + + probe.intensity.range(0.8, 1.5) + + sample_up["Ni_up"].thickness = sample_down["Ni_down"].thickness + sample_up["Ni_up"].interface = sample_down["Ni_down"].interface + + # Choose a range on the ratio of the two samples + experiment.ratio[1].range(0, 5) + + # Create a FitProblem + problem = FitProblem(experiment) + + return problem, experiment + + +# The following is only necessary so this python file can be +# used as a model with the refl1d cli. +problem, _ = create_mixed_model() diff --git a/src/refl1d_models/probes.py b/src/refl1d_models/probes.py new file mode 100644 index 0000000..5be48f3 --- /dev/null +++ b/src/refl1d_models/probes.py @@ -0,0 +1,31 @@ +""" +This module contains example definitions of probes used in refl1d models. +""" + +import numpy as np +from refl1d.probe import QProbe + + +def create_qprobe(data_file_path: str, fwhm: bool = True) -> QProbe: + """ + Create a QProbe object from a data file. + + :param data_file_path: path to the data file. + :type data_file_path: str + :return: QProbe object. + :rtype: QProbe + """ + if data_file_path is None: + q = np.logspace(np.log10(0.005), np.log10(0.2), num=250) + dq = 0.025 * q + data = errors = None + else: + q, data, errors, dq = np.loadtxt(data_file_path, unpack=True) + + # If the Q resolution is given as FWHM, convert it to sigma + if fwhm: + dq /= 2.355 + + probe = QProbe(q, dq, data=(data, errors)) + + return probe diff --git a/tests/test_mixed_model.py b/tests/test_mixed_model.py new file mode 100644 index 0000000..8666812 --- /dev/null +++ b/tests/test_mixed_model.py @@ -0,0 +1,20 @@ +import os +from pathlib import Path + +from bumps.fitters import fit + +from refl1d_models.mixed_model import create_mixed_model + + +def test_mixed_model(): + """ + Example use and test of the mixed model + """ + data_dir = str(Path(__file__).parent / "data") + data_file = os.path.join(data_dir, "REFL_211832_combined_data_auto.txt") + + problem, experiment = create_mixed_model(data_file) + results = fit(problem, method="amoeba", samples=2000, burn=2000, pop=20, verbose=1) + + assert results.success + assert results.fun / len(experiment.probe.Q) < 5 diff --git a/tests/test_version.py b/tests/test_version.py deleted file mode 100644 index 5b09eca..0000000 --- a/tests/test_version.py +++ /dev/null @@ -1,5 +0,0 @@ -from packagenamepy import __version__ - - -def test_version(): - assert __version__ == "unknown"