Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion EasyReflectometryApp/Backends/Py/logic/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@ def flatten(project_lib: ProjectLib) -> tuple[list[dict], list[dict], float]:
"""Flatten the current model's sample into drawable boxes for the Structure view.

Returns (structure, legend, total_thickness):
- structure: one dict per drawn box, top to bottom (contract in MD/VISUAL_LAYERS_PLAN.md §3.2)
- structure: one dict per drawn box, top to bottom, with the keys
label layer name, or assembly name for a gradient
material material name, or 'front -> back' for a gradient
color box color; color_end is the second color of a gradient, else ''
sld, isld real/imaginary SLD of the material, preformatted to 2 decimals
thickness layer thickness in Angstrom, as a float
roughness upper roughness in Angstrom, preformatted to 1 decimal
assembly assembly name, and assembly_index/layer_index to address the layer
kind 'layer' | 'gradient' | 'superphase' | 'subphase'
repetitions n for a collapsed repeating multilayer, else 1
- legend: distinct {label, color} pairs in stack order
- total_thickness: physical total in Angstrom (collapsed repeats counted n times, caps excluded)
"""
Expand Down
2 changes: 1 addition & 1 deletion INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ To make the installation of EasyReflectometry as easy as possible, we prepare pa
- [Windows](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_windows-2022.exe)
- [MacOS](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_macos-14-AppleSilicon.zip) (ARM)
- [Linux](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_ubuntu-22.04) (built on Ubuntu-22.04)
- [Linux](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_ubuntu-24.04) (built on Ubuntu-22.04)
- [Linux](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_ubuntu-24.04) (built on Ubuntu-24.04)

If the relevant EasyReflectometry installation does not work on your system, then please try installation from source.

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ Current main features of **EasyReflectometry**:
- Growing support for flexible _item_ types, including chemically consistent models.
- Ability to corefine multiple contrasts of reflectometry data.
- Multiple minimization engines: [lmfit](https://lmfit.github.io/lmfit-py), [bumps](https://github.com/bumps/bumps) and [DFO-LS](https://github.com/numericalalgorithmsgroup/dfols) (including the differential evolution method).
- Support for magnetic structures: a magnetic scattering length density and in-plane moment angle per layer, refined with [refl1d](https://refl1d.readthedocs.io/en/latest/).
- Support for polarised reflectometry measurements: import of one file per spin channel, spin-asymmetry views, and simultaneous refinement of all measured spin channels against a single model.
- Interactive HTML and standard PDF report generation.
- Undo/redo for both parameter changes and fitting.
- Saving and loading of projects.

Planned improvements / new functionality for **EasyReflectometry**:

- Support for magnetic structures and polarised reflectometry measurements.
- Support for mixed model reflectometry datasets.
- Reading of q-dependent resolution from a file.

Expand Down
13 changes: 10 additions & 3 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ gantt
ESS Start of User Operation :milestone, m3, 2026-11, 2min

section Hard Condensed Matter
Magnetism support :c1, after m1, 40w
Spin asymmetry and polarisation analysis :c2, after m2, 30w
Magnetism support :done, c1, after m1, 40w
Spin asymmetry and polarisation analysis :done, c2, after m2, 30w

section Item Library
# Bilayer item :d1, 2024-01, 8w
Expand Down Expand Up @@ -121,7 +121,10 @@ This would involve significant EasyCore work, in addition to the development of

### Magnetism support

This will begin with enabling the magnetism functionality available in `refl1d`, concurrent to which the graphical user interface will be designed with input from the relevant user communities.
**Delivered.** Magnetic layers can be defined on the Model page and refined with `refl1d`;
see the [magnetism tutorial](./docs/src/tutorials/magnetism.md).

This began with enabling the magnetism functionality available in `refl1d`, concurrent to which the graphical user interface will be designed with input from the relevant user communities.

- [Discuss with user community and document requirements for magnetism](https://github.com/easyScience/EasyReflectometryApp/issues/113)
- [Expose magnetisation in EasyReflectometryLib](https://github.com/easyScience/EasyReflectometryLib/issues/21)
Expand All @@ -130,6 +133,10 @@ This will begin with enabling the magnetism functionality available in `refl1d`,

### Spin asymmetry and polarisation analysis

**Delivered.** Polarised experiments are imported one file per spin channel, displayed per channel,
fitted together and shown as spin asymmetry; see the
[polarised data tutorial](./docs/src/tutorials/polarized_data.md).

Enabling the analysis of difference between different spin flipper state

- [Discuss with user community and document requirements for polarisation analysis](https://github.com/easyScience/EasyReflectometryApp/issues/115)
Expand Down
4 changes: 4 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ tutorial.md
tutorials/model_def.md
tutorials/load_data.md
tutorials/simple_fitting.md
tutorials/magnetism.md
tutorials/polarized_data.md
tutorials/bayesian.md
tutorials/summary.md
tutorials/datasets.md
tutorials/settings.md
```
8 changes: 4 additions & 4 deletions docs/src/installation.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Installation

To make the installation of EasyReflectometry as easy as possible, we prepare packaged releases for three major operating systems:
- [Windows](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.1.1/EasyReflectometryApp_v1.1.1_windows-2022.exe)
- [MacOS (Silicon)](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.1.1/EasyReflectometryApp_v1.1.1_macos-14-AppleSilicon.zip)
- [Linux (built on Ubuntu-24.04)](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.1.1/EasyReflectometryApp_v1.1.1_ubuntu-22.04)
- [Linux (built on Ubuntu-22.04)](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.1.1/EasyReflectometryApp_v1.1.1_ubuntu-24.04)
- [Windows](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_windows-2022.exe)
- [MacOS (Silicon)](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_macos-14-AppleSilicon.zip)
- [Linux (built on Ubuntu-22.04)](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_ubuntu-22.04)
- [Linux (built on Ubuntu-24.04)](https://github.com/EasyScience/EasyReflectometryApp/releases/download/v1.4.0/EasyReflectometryApp_v1.4.0_ubuntu-24.04)

If the relevant EasyReflectometry installation does not work on your system, then please try installation from source.

Expand Down
94 changes: 94 additions & 0 deletions docs/src/tutorials/bayesian.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Bayesian analysis

Next to the classical minimisers the app can sample the posterior distribution of the fitted
parameters with the BUMPS DREAM sampler. Instead of a single best value per parameter you
get a distribution: a median, a credible interval, and the correlations between parameters.

## Starting a sampling run

Bayesian sampling is selected like a minimiser. In `Analysis` › `Advanced` ›
**Minimization method**, pick **BUMPS-DREAM (Bayesian)** - the first entry of the
**Minimizer** drop-down.

<!-- TODO: screenshot of the Minimization method group in Bayesian mode -> _images/anal_bayesian_settings.png -->

The settings below the drop-down change with the choice. Instead of the classical
**Tolerance** and **Max evaluations**, the sampler shows:

| Setting | Meaning |
|---|---|
| **Samples** | Total number of samples to draw. |
| **Burn-in steps** | Initial steps discarded before the chains are recorded. |
| **Population** | Number of chains walking the parameter space. |
| **Thinning** | Keep every n-th draw, to reduce autocorrelation. |
| **Initializer** | How the starting population is spread over the parameter ranges. |

The parameters that are sampled, and the ranges they are sampled in, are the ones ticked
for fitting in the `Basic controls`, exactly as for a classical fit.

With the Bayesian minimiser selected, the fit button in `Basic controls` reads
**Start sampling** instead of **Start fitting**. It becomes **Cancel fitting** while a run
is in progress; cancelling keeps the interface locked until the worker has actually
stopped, so a superseded run can never write into the parameters of the next one.

## Reading the results

### On the reflectivity chart

When a run finishes, the `Reflectivity` tab of the `Analysis` page gains two extra items,
with their own legend entries:

- **Posterior median** - the median calculated curve over the retained draws.
- **95% credible interval** - the band containing 95 % of the posterior predictive curves.

### The Bayesian Posterior tab

The `Analysis` page has a second main tab, **Bayesian Posterior**, holding five views.
Until a run has finished, it shows *"No Bayesian results available. Run a BUMPS-DREAM
sampling to see posterior distributions."*

<!-- TODO: screenshot of the Bayesian Posterior tab -> _images/anal_bayesian_posterior.png -->

| View | Shows |
|---|---|
| **Marginals** | Marginal posterior distribution of each sampled parameter. |
| **Corner Plot** | Pairwise parameter correlations together with the marginals. |
| **Traces** | The MCMC chain traces, for eyeballing mixing and burn-in. |
| **2D Heatmap** | Joint posterior density of any two chosen parameters - pick them with the **X-axis** and **Y-axis** selectors. |
| **Diagnostics** | Convergence diagnostics, see below. |

Each view has a **Save** button that writes the plot to disk.

```{note}
The **Marginals**, **Corner Plot** and **Traces** views are rendered with `plotly`. If it is
not installed the view says so and gives the install command; the rest of the app is
unaffected.
```

### Diagnostics

The **Diagnostics** view reports whether the run can be trusted:

- **Sampling Configuration** - requested samples, burn-in steps, thinning, population
(chains), retained draws and number of parameters, as actually used by the run.
- **Acceptance Rate** of the sampler.
- **Gelman-Rubin R̂** per parameter. Values close to 1 indicate that the chains have
converged on the same distribution.

## When results are discarded

Posterior results describe one specific run of one specific model, so the app clears them -
the overlays, the plots and the results dialog - as soon as they would become stale:

- when a project is created, loaded or reset,
- when a classical fit is started,
- when a new sampling run is started.

## Limitations

- **Polarised experiments cannot be sampled yet.** Fitting them classically is supported,
see [polarised data](./polarized_data.md), but a Bayesian run over a polarised experiment
is not available.
- Data files without an uncertainty column are sampled with zero variances, which the
sampler reports with a message. The fit is still performed, but the resulting credible
intervals should not be read as measurement uncertainties.
33 changes: 33 additions & 0 deletions docs/src/tutorials/datasets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Demo datasets

The repository ships four simulated neutron reflectometry datasets for trying out the
constraint features described in [material and model setup](./model_def.md). They live in
[`examples/datasets`](https://github.com/easyScience/EasyReflectometryApp/tree/master/examples/datasets),
together with a
[full write-up](https://github.com/easyScience/EasyReflectometryApp/blob/master/examples/datasets/README.md)
of each demo and the script that regenerates them.

All four are ORSO `.ort` files simulated from a **known structure** with 4 % noise and 5 %
`dQ/Q` resolution, so every demo has a right answer to compare the fit against. The ground
truth is recorded in each file's header, and each header also carries the sample structure
in the ORSO model language, so the files can be opened in two ways:

- **Model** › `Load a sample` › **Load sample from file** builds the layer stack for you,
with the true thicknesses, roughnesses and SLDs as starting values. Change those starting
values before fitting, so there is something to find.
- **Experiment** › **Load experiment(s) from file(s)** loads the reflectivity curve, which
also works with a hand-built sample.

| Dataset | Demonstrates |
|---|---|
| `two_layer_film.ort` | A two-layer film whose total thickness is known (exactly 90 Å) - the thickness budget and the derived total film thickness. |
| `swapped_layers.ort` | A layer-ordering inequality: started from the swapped guess, the fit only recovers the truth with a `≤` constraint between the two thicknesses. |
| `ni_ti_multilayer.ort` | A `[Ti / Ni] × 8` repeating multilayer with a Bragg peak that pins the period - the **Constant period Λ** and **Conformal roughness** recipes. |
| `dppc_monolayer.ort` | A DPPC monolayer at the air/D2O interface - the surfactant recipes (equal head/tail area per molecule, solvent roughness). |

```{note}
For `ni_ti_multilayer.ort` the loaded stack arrives flattened (8 × [Ti | Ni] becomes 16
layers in one assembly); rebuild it as a `Repeating Multi-layer` by hand for the
constant-period demo. For `dppc_monolayer.ort` the loaded stack is the slab equivalent of
the surfactant; replace it with a `Surfactant layer` assembly for the surfactant recipes.
```
5 changes: 5 additions & 0 deletions docs/src/tutorials/load_data.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ When data is loaded, it is possible to change instrumental parameters that affec
- **A**: Scale the data by the given value.
- **B**: Set the level where data merges into the experimental background.
- **C**: Instrumental resolution that percentage varies as a function of Q.

## Polarised data
A measurement that recorded several spin channels is loaded with the second button,
`Load polarized experiment (file per channel)`, which takes one file per channel and asks
how to assign them. See [polarised data](./polarized_data.md).
88 changes: 88 additions & 0 deletions docs/src/tutorials/magnetism.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Magnetic layers

A layer can be given a magnetisation, so that the two neutron spin states see different
scattering length densities. This page covers the **Model** page controls; loading and
fitting measured spin channels is described in
[polarised data](./polarized_data.md).

```{note}
Magnetic layers can only be calculated by `refl1d`. `refnx` has no magnetism, so the app
asks to switch the project's calculation engine the first time a layer is made magnetic.
```

## The Magnetism group

The **Magnetism** group is in the basic controls of the **Model** page, below the layer
editor. It is titled after the assembly currently selected in the `Layer editor`, for
example `Magnetism: Multi-layer`, and shows one row per layer of that assembly.

<!-- TODO: screenshot of the Magnetism group -> _images/sample_magnetism.png -->

| Column | Meaning |
|---|---|
| **No.** | Position of the layer in the assembly. |
| **Layer** | Layer name, as in the `Layer editor`. |
| **ρM/10⁻⁶Å⁻²** | Magnetic scattering length density of the layer. |
| **θM/°** | In-plane angle of the magnetic moment. |
| **Magn.** | Makes the layer magnetic. Unticking it removes the magnetisation. |

`ρM` and `θM` are only editable once **Magn.** is ticked.

```{note}
`θM = 270°` aligns the moment with the guide field, which produces no spin-flip
scattering. This is the value to start from for a simple saturated film.
```

### Switching the calculation engine

Ticking **Magn.** while the project uses an engine that cannot model magnetism opens the
**Switch calculation engine?** dialog. Accepting it makes the layer magnetic *and* switches
the project to `refl1d` in one step - the page does not change under you.

Switching recalculates the reflectivity and makes any existing fit result stale; the sample
and the loaded data are untouched. The engine can be switched back once no layer is
magnetic any more.

The engine itself lives in the **Calculation engine** group of the advanced controls on the
**Model** page (and in `Analysis` › `Advanced`, see
[simple fitting](./simple_fitting.md)). Selecting an engine that cannot model magnetism
while the sample still has magnetic layers is refused, with a message on both pages.

## Fitting ρM and θM

`ρM` and `θM` appear in the `Analysis` parameter table like any other layer parameter,
named after their assembly and model - for example `Model Fe rho_m`. They come with default
limits, a fit checkbox and can be used in constraints. The parameter name filter accepts
`magnetic` as a keyword to show only the magnetic parameters.

## Magnetic depth profiles

Once at least one layer is magnetic, the **Magnetic profile** group appears in the basic
controls, below **Magnetism**. The same switches are repeated in `Analysis` ›
`Advanced` › `Plot control`, and the two share one selection.

<!-- TODO: screenshot of the Magnetic profile group -> _images/sample_magnetic_profile.png -->

- **Show ρ↑ and ρ↓** - adds the spin-up and spin-down potentials
`ρ ± ρM·cos(θM − A)` for each magnetic model to the SLD chart, dashed in the model's
colour. For non-magnetic layers the two curves collapse onto the nuclear SLD.
- **Show ρM** - the magnetic SLD profile on its own.
- **Show θM** - the in-plane moment angle, on its own right-hand axis. `θM` is only defined
where there is a moment, so the curve is drawn in pieces rather than joined across the
gaps.
- **Show R↑↑ and R↓↓** - splits each magnetic model's reflectivity into its two
non-spin-flip cross-sections on the **Model** page reflectivity chart, dashed in the
model's colour with their own legend rows. Off by default.

The y-range of the SLD chart covers every visible curve and grows when a curve is switched
on, so `ρ + ρM` is never clipped. If no model is magnetic, the chart, its legend and the
sidebar are unchanged.

```{note}
For a magnetic sample the plain model curve is **not** an unpolarised average - the
calculator returns the ↑↑ cross-section - so `R↑↑` is drawn on top of it. The sidebar says
so as well.
```

The `Analysis` reflectivity chart is unaffected by this switch: it already draws one
calculated curve per measured spin channel when the experiment is polarised.
Loading
Loading