From ac5b4e2d1b0f1226e6529e91958526e65869d8ff Mon Sep 17 00:00:00 2001 From: rozyczko Date: Tue, 11 Aug 2026 14:02:26 +0200 Subject: [PATCH 01/22] initial version --- CHANGELOG.md | 35 ++ docs/docs/api-reference/calculators.md | 26 + .../docs/tutorials/simulation/magnetism.ipynb | 509 ++++++++++++++---- src/easyreflectometry/calculators/__init__.py | 3 +- .../calculators/calculator_base.py | 39 ++ src/easyreflectometry/calculators/factory.py | 4 + .../calculators/polarization.py | 29 + .../calculators/refl1d/wrapper.py | 128 +++-- .../calculators/refnx/wrapper.py | 16 - .../calculators/wrapper_base.py | 56 ++ .../refl1d/test_refl1d_calculator.py | 37 ++ .../calculators/refl1d/test_refl1d_wrapper.py | 253 ++++++++- tests/calculators/refnx/test_refnx_wrapper.py | 13 +- .../test_polarization_interface.py | 83 +++ 14 files changed, 1031 insertions(+), 200 deletions(-) create mode 100644 docs/docs/api-reference/calculators.md create mode 100644 src/easyreflectometry/calculators/polarization.py create mode 100644 tests/calculators/test_polarization_interface.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 938f27b5..93bebd49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +# Unreleased + +All four polarization channels (pp, pm, mp, mm) are now available from +the refl1d calculator; previously only the non-spin-flip pp channel was +returned. + +- New `polarized_reflectivity_profiles(x_array, model_id)` on the + calculator (and on `CalculatorFactory`) returns the reflectivity of + all four spin channels in one calculation as a dictionary keyed + `'pp'`, `'pm'`, `'mp'`, `'mm'` (in that order). Requires + `include_magnetism = True`. +- New `polarization_channel` property (accepts + `'pp'`/`'pm'`/`'mp'`/`'mm'` or the new `PolarizationChannel` enum) + selects which channel `reflectity_profile` — and hence fitting — + returns, enabling fits against spin-flip or mm data. Default `'pp'`; + disabling magnetism resets it to `'pp'`. Note: the channel belongs to + the currently active calculator instance, not to a model or dataset — + it affects every subsequent calculation with that calculator, and + `interface.switch(...)` constructs a fresh calculator, resetting it + (along with `include_magnetism`). +- Magnetic calculations now always build all four refl1d cross-sections, + so they may take somewhat longer than before; pp results are + unchanged. +- Bug fix: `include_magnetism = True` on a refnx-backed calculator now + raises `NotImplementedError`. Previously it was silently accepted (the + guard sat on a property the calculator never called) even though refnx + magnetism is not supported. +- Bug fix (pre-existing): disabling magnetism after layers were created + with it enabled used to leave refl1d `Magnetism` objects on the slabs, + making a subsequent unpolarized calculation raise `AttributeError` + inside refl1d. Disabling magnetism now strips the magnetic state from + existing layers, so the unpolarized path works again. Consequently, + magnetic parameters (`rhoM`/`thetaM`) do not survive a + disable/re-enable cycle and must be set again. + # Version 1.7.0 (1 Aug 2026) Restored the measured per-point resolution on data load (issue #368). diff --git a/docs/docs/api-reference/calculators.md b/docs/docs/api-reference/calculators.md new file mode 100644 index 00000000..870355f2 --- /dev/null +++ b/docs/docs/api-reference/calculators.md @@ -0,0 +1,26 @@ +# Calculators + +The calculator translates an EasyReflectometry model into a backend +engine (refl1d or refnx) and computes reflectivity. + +## Polarized reflectivity + +With the refl1d calculator and `include_magnetism` enabled, all four +spin channels are available: + +- `polarized_reflectivity_profiles(x_array, model_id)` returns the + reflectivity of all four channels as a dictionary keyed `'pp'`, + `'pm'`, `'mp'`, `'mm'` (in that order). +- `polarization_channel` selects which channel `reflectity_profile` — + and hence fitting — uses (default `'pp'`). + +Note that `polarization_channel` is state of the currently active +calculator instance, not of a model or dataset: it affects every +subsequent calculation using that calculator, and switching calculators +via the factory constructs a fresh instance, which resets the channel +(along with `include_magnetism`). Calculators without magnetism support +(refnx) raise `NotImplementedError` when magnetism is enabled. + +::: easyreflectometry.calculators.polarization + +::: easyreflectometry.calculators.calculator_base diff --git a/docs/docs/tutorials/simulation/magnetism.ipynb b/docs/docs/tutorials/simulation/magnetism.ipynb index 8efdb9e4..c1486825 100644 --- a/docs/docs/tutorials/simulation/magnetism.ipynb +++ b/docs/docs/tutorials/simulation/magnetism.ipynb @@ -7,8 +7,9 @@ "source": [ "# Magnetism\n", "\n", - "Magntism is only available in Refl1d and it does not support RepeatingMultilayer\n", - "\n" + "Magnetism is only available in Refl1d and it does not support RepeatingMultilayer.\n", + "\n", + "When magnetism is enabled (`include_magnetism = True`) all four polarization channels are available: the non-spin-flip channels (`pp`, `mm`) and the spin-flip channels (`pm`, `mp`).\n" ] }, { @@ -16,15 +17,21 @@ "id": "f5d0bd58", "metadata": {}, "source": [ - "## Setup\n", - "First configure matplotlib to place figures in notebook and import needed modules" + "## Setup" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "644e53e3", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:44.947675Z", + "iopub.status.busy": "2026-08-11T11:33:44.947675Z", + "iopub.status.idle": "2026-08-11T11:33:45.377398Z", + "shell.execute_reply": "2026-08-11T11:33:45.377398Z" + } + }, "outputs": [], "source": [ "%matplotlib inline" @@ -32,9 +39,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "29d5d62d-af4a-416d-bbe2-1338d32b30f5", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:45.380421Z", + "iopub.status.busy": "2026-08-11T11:33:45.380421Z", + "iopub.status.idle": "2026-08-11T11:33:48.205821Z", + "shell.execute_reply": "2026-08-11T11:33:48.205821Z" + } + }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", @@ -64,10 +78,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "549734c1-bbd9-41f3-8a20-d7a8ded37802", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.209574Z", + "iopub.status.busy": "2026-08-11T11:33:48.207956Z", + "iopub.status.idle": "2026-08-11T11:33:48.214078Z", + "shell.execute_reply": "2026-08-11T11:33:48.214078Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "numpy: 2.3.5\n", + "scipp: 25.11.0\n", + "easyreflectometry: 1.5.0\n", + "refl1d: 1.0.0\n" + ] + } + ], "source": [ "print(f'numpy: {np.__version__}')\n", "print(f'scipp: {sc.__version__}')\n", @@ -99,9 +131,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "0f95d620-35b7-4b47-a3b4-9e33d5525b50", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.218231Z", + "iopub.status.busy": "2026-08-11T11:33:48.217199Z", + "iopub.status.idle": "2026-08-11T11:33:48.231252Z", + "shell.execute_reply": "2026-08-11T11:33:48.230138Z" + } + }, "outputs": [], "source": [ "sld_4 = Material(sld=4.0, isld=0, name='Sld 4')\n", @@ -127,9 +166,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "2af8c30b", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.233176Z", + "iopub.status.busy": "2026-08-11T11:33:48.233176Z", + "iopub.status.idle": "2026-08-11T11:33:48.239570Z", + "shell.execute_reply": "2026-08-11T11:33:48.238822Z" + } + }, "outputs": [], "source": [ "two_layers = Multilayer([sld_4_layer, sld_8_layer], name='SLD 4/8 Layer')\n", @@ -152,9 +198,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "b0259cd0", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.241312Z", + "iopub.status.busy": "2026-08-11T11:33:48.241312Z", + "iopub.status.idle": "2026-08-11T11:33:48.245759Z", + "shell.execute_reply": "2026-08-11T11:33:48.245759Z" + } + }, "outputs": [], "source": [ "refl1d_sld_4 = refl1d.names.SLD(name='Sld 4', rho=4.0, irho=0)\n", @@ -178,9 +231,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "f1500603-d85d-4e16-b697-e1bf16502991", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.247908Z", + "iopub.status.busy": "2026-08-11T11:33:48.247908Z", + "iopub.status.idle": "2026-08-11T11:33:48.251851Z", + "shell.execute_reply": "2026-08-11T11:33:48.251851Z" + } + }, "outputs": [], "source": [ "interface = CalculatorFactory()" @@ -197,9 +257,16 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "18010202", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.255153Z", + "iopub.status.busy": "2026-08-11T11:33:48.254020Z", + "iopub.status.idle": "2026-08-11T11:33:48.259502Z", + "shell.execute_reply": "2026-08-11T11:33:48.258989Z" + } + }, "outputs": [], "source": [ "model_coords = np.linspace(\n", @@ -229,10 +296,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "cdf959c8", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.262018Z", + "iopub.status.busy": "2026-08-11T11:33:48.261013Z", + "iopub.status.idle": "2026-08-11T11:33:48.731414Z", + "shell.execute_reply": "2026-08-11T11:33:48.731414Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Refl1d\n", "probe = refl1d.names.QProbe(\n", @@ -251,7 +336,7 @@ "model.interface = interface\n", "model.resolution_function = PercentageFwhm(0)\n", "model_interface = model.interface()\n", - "model_interface.magnetism = False\n", + "model_interface.include_magnetism = False\n", "model_data_no_magnetism_ref1d_easy = model.interface().reflectity_profile(\n", " model_coords,\n", " model.unique_name,\n", @@ -274,10 +359,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "bf311973", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.734190Z", + "iopub.status.busy": "2026-08-11T11:33:48.733539Z", + "iopub.status.idle": "2026-08-11T11:33:48.972922Z", + "shell.execute_reply": "2026-08-11T11:33:48.972922Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Without magnetic layers\n", "interface.switch('refl1d')\n", @@ -295,12 +398,8 @@ "model.interface = interface\n", "model_interface = model.interface()\n", "model_interface.include_magnetism = True\n", - "model_interface._wrapper.update_layer(\n", - " list(model_interface._wrapper.storage['layer'].keys())[1], magnetism_rhoM=10, magnetism_thetaM=70\n", - ")\n", - "model_interface._wrapper.update_layer(\n", - " list(model_interface._wrapper.storage['layer'].keys())[2], magnetism_rhoM=5, magnetism_thetaM=175\n", - ")\n", + "model_interface._wrapper.update_layer(sld_4_layer.unique_name, magnetism_rhoM=10, magnetism_thetaM=70)\n", + "model_interface._wrapper.update_layer(sld_8_layer.unique_name, magnetism_rhoM=5, magnetism_thetaM=175)\n", "model_data_magnetism_layer_1 = model.interface().reflectity_profile(\n", " model_coords,\n", " model.unique_name,\n", @@ -329,10 +428,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "18cb7037", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:48.975220Z", + "iopub.status.busy": "2026-08-11T11:33:48.975220Z", + "iopub.status.idle": "2026-08-11T11:33:49.187981Z", + "shell.execute_reply": "2026-08-11T11:33:49.187981Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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v//+69cxGjRogLZt26JWrVpGNDVv3hwdO3ZMcn+KLKbWHGJjY3Hw4EEUKFDAeJCSozQplrZt24bcuXP7fTsvo2uia6LXif529F6i99e0ghkb+nudjE9EVXW98sorZvGH6Ohos7jJmzdviu+bokfCR9dErxP97QQCvZ/oeug1knwY9AgmAT16wYIFERUVhT179sTbzvWiRYsG8q6EEEIIIdJX+GTJkgW1a9fGzJkz46WeuN6wYcNA3pUQQgghRPBTXceOHcP69evj1jdt2mSqtJiTY26Ofptu3bqhTp06qFevHoYPH25K4J0qr1CF6TKW4PumzSIZXRNdE71O9Lej9xK9v3rtMyeDlcy6MVZZNW3aNMF2ip1x48aZnzly4s0338Tu3btNz5533nnHNDIUQgghhEhPki18hBBCCCHClZAbUiqEEEIIESwkfIQQQggRMUj4CCGEECJi8LTwSe6UeM4Ju+KKK8z+1atXx9SpU+P9nnaoF154AcWKFUO2bNnMKI5169YhUq9H9+7dTXds99K6dWuEE8m5JitXrsTNN99s9udjZcViao8ZCddk0KBBCV4nfF159ZqMHj0a1113HfLly2cWvk/47h/u7yXBuCaR9n7y7bffmupnNtzNkSOHKQT69NNPI/p18q0f1yQgrxPLo3z55ZdWlixZrI8//thauXKl1aNHDytv3rzWnj17Et3/jz/+sKKioqw33njDWrVqlfX8889bmTNntlasWBG3z2uvvWblyZPHmjx5srV8+XKrY8eOVtmyZa2TJ09akXg9unXrZrVu3dratWtX3HLw4EErXEjuNVm0aJH15JNPWl988YVVtGhR6+233071MSPhmgwcONCqWrVqvNfJvn37rHAhudfkzjvvtP7v//7PWrZsmbV69Wqre/fu5n1j+/btnngvCdY1ibT3k1mzZlnffvuteX9dv369NXz4cPOeO23atIh9nczy45oE4nXiWeFTr149q1evXnHr58+ft4oXL24NGTIk0f1vu+02q127dvG21a9f33rwwQfNz7GxseaN/c0334z7/eHDh63o6Gjzph9p18N5AXbq1MkKV5J7TdxcdtlliX7Ip+aYXr0mFD41atSwwpXUPqfnzp2zcuXKZX3yySeeeC8JxjWJ9PcTh1q1apkvmUSvk4TXJFCvE0+mupwp8QwL+jslntvd+5NWrVrF7c9GjexL5N6H02MZuvN38ryXroe7r1PhwoVRqVIl9OzZ00zVDQdSck3S45hpSTDPn+H54sWL4/LLL0eXLl2wdetWhAOBuCYnTpzA2bNn4wYvhvN7SbCuSaS/nzAIwQkHa9asQaNGjcy2SH+dWIlck0C9TjwpfC42JZ4vpMTg9ovt7/yfnGN6+XoQ5lXHjx9vXpyvv/465syZgzZt2pj7CnVSck3S45hpSbDOn2/UbG46bdo0vPfee+YNnX6Po0ePIhKuyTPPPGNEn/MBEM7vJcG6JpH6fsJp5Dlz5jTjntq1a4eRI0eiZcuWEf06ibnINQnU6yQkp7OL8OD222+P+5nm5yuvvBLlypUzarx58+bpem4idOCbkgNfIxRCl112Gb766ivcd9998DKvvfYavvzyS/M3QXOnSPqaROL7Sa5cuczIJ46C4gc5Rz4xKtqkSRNEKrkucU0C8TrxZMQnJVPiuf1i+zv/h+Pk+WBcj8Tgi5P35Z7l5qVrkh7HTEvS6vxZsVGxYkXPv06GDh1qPuSnT59u3pwdwvm9JFjXJFLfT5j6KV++vKle6tu3L2655RYMGTIkol8nGS9yTQL1OvGk8EnJlHhud+9Pfvnll7j9y5Yta54s9z5HjhzBwoULQ37yfDCuR2Js377d5FpZehnqpOSapMcx05K0On9+k9uwYYOnXydvvPEGXnrpJZPeY3mum3B+LwnWNUmMSHw/4W1Onz4d0a+Ti12TgL1OLI/CMjpWSYwbN86Uxj3wwAOmjG737t3m9127drX69esXr3w7U6ZM1tChQ025JStREitn5zG+++476++//zbO8nApLQz09Th69KgpY54/f761adMma8aMGdZVV11lVahQwTp16pQVDiT3mpw+fdqU43IpVqyYefz8ed26dX4fMxKvSd++fa3Zs2eb1wlfVy1atLAKFixo7d271/LiNeH7BEt4v/7663glt/yb8cJ7STCuSSS+n7z66qvW9OnTrQ0bNpj9+V7L99zRo0dH7Ovk1Utck0C9TjwrfMjIkSOt0qVLmz84lhouWLAg7neNGzc2ZXFuvvrqK6tixYpmf/YdmTJlSrzfs7xwwIABVpEiRcyT2bx5c2vNmjVWJF6PEydOWNdff71VqFAhI4hYysweDeHyAZ+Sa8I/NH5X8F24n7/HjMRr0rlzZyOKeLwSJUqYdfbo8Oo14d9CYteEXx688l4S6GsSie8nzz33nFW+fHkra9asVr58+ayGDRsaoeAm0l4nz13imgTqdaLp7EIIIYSIGDzp8RFCCCGESAwJHyGEEEJEDBI+QgghhIgYJHyEEEIIETFI+AghhBAiYpDwEUIIIUTEIOEjhBBCiIhBwkcIIYQQEYOEjxBCCCEiBgkfIYQQQkQMEj5CCCGEiBgkfIQQQgiBSOH/AdvsJNlBYJIFAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Refl1d model is inverted as compared to EasyReflectometry, so the order of the layers is reversed\n", "refl1d_sample = (\n", @@ -343,8 +460,9 @@ ")\n", "model_name = model.unique_name\n", "storage = {'model': {model_name: {}}}\n", - "storage['model'][model_name]['scale'] = 10.0\n", - "storage['model'][model_name]['bkg'] = 20.0\n", + "# Match the EasyReflectometry model: scale=1, background=0\n", + "storage['model'][model_name]['scale'] = 1.0\n", + "storage['model'][model_name]['bkg'] = 0.0\n", "\n", "polarized_probe = _get_polarized_probe(\n", " q_array=model_coords, dq_array=np.zeros(len(model_coords)), model_name=model_name, storage=storage\n", @@ -359,12 +477,8 @@ "model.interface = interface\n", "model_interface = model.interface()\n", "model_interface.include_magnetism = True\n", - "model_interface._wrapper.update_layer(\n", - " list(model_interface._wrapper.storage['layer'].keys())[1], magnetism_rhoM=10, magnetism_thetaM=70\n", - ")\n", - "model_interface._wrapper.update_layer(\n", - " list(model_interface._wrapper.storage['layer'].keys())[2], magnetism_rhoM=5, magnetism_thetaM=175\n", - ")\n", + "model_interface._wrapper.update_layer(sld_4_layer.unique_name, magnetism_rhoM=10, magnetism_thetaM=70)\n", + "model_interface._wrapper.update_layer(sld_8_layer.unique_name, magnetism_rhoM=5, magnetism_thetaM=175)\n", "model_data_magnetism_easy = model.interface().reflectity_profile(\n", " model_coords,\n", " model.unique_name,\n", @@ -384,10 +498,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "7033f755", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:49.190329Z", + "iopub.status.busy": "2026-08-11T11:33:49.190329Z", + "iopub.status.idle": "2026-08-11T11:33:49.200414Z", + "shell.execute_reply": "2026-08-11T11:33:49.200414Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0\n" + ] + } + ], "source": [ "print(max(abs(model_data_magnetism_easy - model_data_magnetism_ref1d)))" ] @@ -406,22 +535,40 @@ "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." + "## All polarization channels for a single layer sample\n", + "\n", + "This study is done with magnetism to show the reflectivity of all four spin cross-sections. First we compute the reference directly in Refl1d for a single magnetic layer on a silicon subphase." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "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,\n", - "# and the spin-flipping is not maximized.\n", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:49.202700Z", + "iopub.status.busy": "2026-08-11T11:33:49.202700Z", + "iopub.status.idle": "2026-08-11T11:33:49.429422Z", + "shell.execute_reply": "2026-08-11T11:33:49.429422Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# The magnetic SLD is set to 8 and its angle to 45 degrees.\n", + "# An angle of 90 would double (pp) and cancel out (mm) the magnitude of the reflectivity oscillations,\n", + "# while an angle of 0 would give the strongest spin-flipping (pm and mp).\n", + "# At 45 degrees neither effect is maximized, so all four channels are distinct.\n", "refl1d_sample = (\n", " refl1d_si(0, 0) | refl1d_sld_8(150, 0, magnetism=refl1d.names.Magnetism(rhoM=8, thetaM=45)) | refl1d_vacuum(0, 0)\n", ")\n", @@ -432,69 +579,180 @@ "storage['model'][model_name]['bkg'] = 0.0\n", "\n", "polarized_probe = _get_polarized_probe(\n", - " q_array=model_coords, dq_array=np.zeros(len(model_coords)), model_name=model_name, storage=storage, all_polarizations=True\n", + " q_array=model_coords, dq_array=np.zeros(len(model_coords)), model_name=model_name, storage=storage\n", ")\n", "\n", - "experiment = refl1d.names.Experiment(probe=polarized_probe, sample=refl1d_sample)" + "experiment = refl1d.names.Experiment(probe=polarized_probe, sample=refl1d_sample)\n", + "# One reflectivity() call returns all four cross-sections, in the order pp, pm, mp, mm\n", + "raw_reflectivities = experiment.reflectivity()\n", + "raw_channels = {key: reflectivity for key, (_, reflectivity) in zip(('pp', 'pm', 'mp', 'mm'), raw_reflectivities)}\n", + "\n", + "plt.plot(model_coords, raw_channels['pp'], '-k', label='Refl1d pp', linewidth=4)\n", + "plt.plot(model_coords, raw_channels['mm'], '-r', label='Refl1d mm', linewidth=2)\n", + "plt.plot(model_coords, raw_channels['pm'], ':k', label='Refl1d pm', linewidth=4)\n", + "plt.plot(model_coords, raw_channels['mp'], ':r', label='Refl1d mp', linewidth=2)\n", + "\n", + "plot_apply_makeup()" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "239e2a04", + "cell_type": "markdown", + "id": "0b4b4d3f", "metadata": {}, - "outputs": [], "source": [ - "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", + "### All polarization channels in EasyReflectometry\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", + "The same four channels are available through the EasyReflectometry API via `polarized_reflectivity_profiles`, which returns a dictionary keyed `pp`, `pm`, `mp`, `mm`. We build the equivalent single layer model and enable magnetism.\n", + "\n", + "Note that setting the magnetic layer parameters through `_wrapper.update_layer(...)` is a *temporary workaround*: magnetic layer parameters are not yet part of the public `Layer` API." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "239e2a04", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:49.431597Z", + "iopub.status.busy": "2026-08-11T11:33:49.431597Z", + "iopub.status.idle": "2026-08-11T11:33:49.751931Z", + "shell.execute_reply": "2026-08-11T11:33:49.750821Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# The single layer model matching the raw Refl1d sample above\n", + "vacuum_single = Material(sld=0, isld=0, name='Vacuum')\n", + "sld_8_single = Material(sld=8.0, isld=0, name='Sld 8')\n", + "si_single = Material(sld=2.047, isld=0, name='Si')\n", + "superphase_single = Layer(material=vacuum_single, thickness=0, roughness=0, name='Vacuum Superphase')\n", + "magnetic_layer = Layer(material=sld_8_single, thickness=150, roughness=0, name='Magnetic Layer')\n", + "subphase_single = Layer(material=si_single, thickness=0, roughness=0, name='Si Subphase')\n", + "single_layer_model = Model(\n", + " sample=Sample(\n", + " Multilayer(superphase_single),\n", + " Multilayer(magnetic_layer),\n", + " Multilayer(subphase_single),\n", + " name='Single Layer Sample',\n", + " ),\n", + " scale=1,\n", + " background=0,\n", + " name='Single Layer Model',\n", + ")\n", + "\n", + "interface.switch('refl1d')\n", + "single_layer_model.interface = interface\n", + "single_layer_model.resolution_function = PercentageFwhm(0)\n", + "model_interface = single_layer_model.interface()\n", + "model_interface.include_magnetism = True\n", + "\n", + "# Temporary workaround: set the magnetic layer parameters directly on the wrapper\n", + "model_interface._wrapper.update_layer(magnetic_layer.unique_name, magnetism_rhoM=8, magnetism_thetaM=45)\n", + "\n", + "channels = single_layer_model.interface.polarized_reflectivity_profiles(\n", + " model_coords,\n", + " single_layer_model.unique_name,\n", + ")\n", + "\n", + "plt.plot(model_coords, channels['pp'], '-k', label='EasyReflectometry pp', linewidth=4)\n", + "plt.plot(model_coords, channels['mm'], '-r', label='EasyReflectometry mm', linewidth=2)\n", + "plt.plot(model_coords, channels['pm'], ':k', label='EasyReflectometry pm', linewidth=4)\n", + "plt.plot(model_coords, channels['mp'], ':r', label='EasyReflectometry mp', linewidth=2)\n", "\n", "plot_apply_makeup()" ] }, { "cell_type": "markdown", - "id": "ac52936c", + "id": "48fca800", "metadata": {}, "source": [ - "## Refl1 and Refnx in EasyReflectometry.\n", - "This study is done without magnetism as Refnx does not support this yet." + "The two models agree for every polarization channel." ] }, { "cell_type": "code", - "execution_count": null, - "id": "e59d3153-f0da-4fce-a4f0-a424010acbec", + "execution_count": 15, + "id": "f70f3a34", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:49.754827Z", + "iopub.status.busy": "2026-08-11T11:33:49.754827Z", + "iopub.status.idle": "2026-08-11T11:33:49.761491Z", + "shell.execute_reply": "2026-08-11T11:33:49.759728Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pp: 0.0\n", + "pm: 0.0\n", + "mp: 0.0\n", + "mm: 0.0\n" + ] + } + ], + "source": [ + "for key in channels:\n", + " print(f'{key}: {max(abs(channels[key] - raw_channels[key]))}')" + ] + }, + { + "cell_type": "markdown", + "id": "30a07896", "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().reflectity_profile(\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", + "### Selecting a single channel\n", "\n", - "# Refl1d\n", - "interface.switch('refl1d')\n", - "model.interface = interface\n", - "model_interface = model.interface()\n", - "model_data_no_magnetism_ref1d = model.interface().reflectity_profile(\n", + "`reflectity_profile` — the function used when fitting — returns the channel selected by `polarization_channel` (default `pp`). To fit against, say, `mm` data, select the `mm` channel.\n", + "\n", + "Note that the selected channel is state of the currently active calculator: it affects every model and fit using that calculator until it is changed, and `interface.switch(...)` constructs a fresh calculator, which resets the channel (along with `include_magnetism`)." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "23beb5a6", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:49.763863Z", + "iopub.status.busy": "2026-08-11T11:33:49.763374Z", + "iopub.status.idle": "2026-08-11T11:33:49.773854Z", + "shell.execute_reply": "2026-08-11T11:33:49.773347Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mm channel reproduced: 0.0\n" + ] + } + ], + "source": [ + "model_interface.polarization_channel = 'mm'\n", + "reflectivity_mm = single_layer_model.interface().reflectity_profile(\n", " model_coords,\n", - " model.unique_name,\n", + " single_layer_model.unique_name,\n", ")\n", - "plt.plot(model_coords, model_data_no_magnetism_ref1d, 'r-', label=f'EasyReflectometry ({model_interface.name})', linewidth=2)\n", + "print(f'mm channel reproduced: {max(abs(reflectivity_mm - channels[\"mm\"]))}')\n", "\n", - "plot_apply_makeup()" + "# Reset to the default channel so later cells are unaffected\n", + "model_interface.polarization_channel = 'pp'" ] }, { @@ -508,27 +766,45 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "id": "b087e848", - "metadata": {}, - "outputs": [], - "source": [ - "# With Magnitism\n", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:49.990332Z", + "iopub.status.busy": "2026-08-11T11:33:49.990332Z", + "iopub.status.idle": "2026-08-11T11:33:50.201479Z", + "shell.execute_reply": "2026-08-11T11:33:50.201479Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj4AAAGdCAYAAAASUnlxAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAimhJREFUeJztnQeYE+X2xg+9995BkCbSpSgISpMmWLGC6EVF9GIHrgpeG4oNy1UURRAVUf8UlaKIKEV6URSkCdI7LH13Yef/vB/ft34bkiW7mzKZvL/nCbszZJPJJJl555z3nJPNcRxHCCGEEELigOzR3gBCCCGEkEhB4UMIIYSQuIHChxBCCCFxA4UPIYQQQuIGCh9CCCGExA0UPoQQQgiJGyh8CCGEEBI3UPgQQgghJG7IKR4jJSVFdu7cKYUKFZJs2bJFe3MIIYQQEgTop3z06FEpX768ZM8evriM54QPRE+lSpWivRmEEEIIyQTbtm2TihUrSrjwnPBBpMfsuMKFC0d7cwghhBASBEeOHFGBC3MeDxeeEz4mvQXRQ+FDCCGExBbZwmxTobmZEEIIIXGDZ4TP//73P6lbt65ccskl0d4UQgghhLiUbA5s1B7LERYpUkQSEhKY6iKEEEJihCMROn97zuNDCPEuuE47ffq0nDlzJtqbQgjJIDly5JCcOXNGvdUMhQ8hJCZISkqSXbt2yYkTJ6K9KYSQTJI/f34pV66c5M6dW6IFhQ8hJCYak27evFldMaK5GQ6a0b5qJIRkLFqLi5d9+/ap7/KFF14Y1iaF6UHhQwhxPThgQvygxweuGAkhsUe+fPkkV65c8vfff6vvdN68eaOyHZ6p6iKEeJ9oXSESQrzzHY7+Fvjh22+/lVq1aqlQ2AcffBDtzSGEEEKIR3Cd8EHFxsMPPyw//vijrFy5Ul5++WU5cOBAtDeLEEIixk8//aQ8TIcPH073flWrVpWRI0dGbLvcDPbXlClTIv68H374oXTs2DEkj/X0009LmTJlUl/LHXfcIT179szy49qfk6SkJLW8bNkyiVdcJ3yWLFkiF110kVSoUEEKFiwonTt3lu+//z7am0UIIRlm1KhRau4QLugMx44dUz6Htm3b+hU7mzZtkksvvVRVsKGnCRg7dqwULVqU74AWBw0bNjxnX2B/4XwRSU6dOiVPPfWUDBs2LMuPtXbtWvnvf/8r7733XsDXMnfuXOnevbsy+GdW6OXOnVseffRRGTRokMQrITc3441BlGb58uXqzZs8efI5ihVdlnGf3bt3S4MGDeStt96SZs2apU5Xh+gx4PcdO3ZIOJk3b55MmDAhU3+LD1/NmjXlrrvuUkKNEEIMV1xxhRI6uLpu0aJF6vGmbNmysnjxYnXiNAbPOXPmSOXKlaV69epqGfchwRON/fXVV1+pRnuXXXZZwPsgwhJM6TYEL+jRo0fAisXjx4+rc+add94p1157baa3+9Zbb5VHHnlE/vjjDxVoiDdCLnzO98ZMnDhRpbJwJdS8eXMVfuvUqZOsW7dOSpcuLdFgzZo18u6770rjTPwt2qi9JyJvvvmmTJ8+XXmTCCHhpV+/fvL7779HdTfXq1dPRo8ene59cDxAzxJEc4zwwe84uSGdv2jRotTID9ZDKNm/Hzp0SFatWiV9+/ZV680JEREGRD4A+hrhePvll19KsWLF5Mknn5S777474Dbh+S6++GLVGmDcuHHqpPzcc8/JLbfcIvfff786mSPdggtSE3VAw0g8JrYZF6wQaPfdd58MHDjwHJvCxx9/rB77X//6l7ovuvCayASeu379+krswb+J57733ntTXwtAeg8RialTp0piYqI0bdpUXn/9dXVeQeQLURF7X3z00UcqJYRlc6ENsYFt+b//+z+1D/F68DxDhgxJ/Vucg7755hv1mqpUqSJjxoyRUqVKqe1eunSper7x48enClF/fP755yoCY4NtwWvA+CRc5OfJk0eVb2/btk2JDWQwYPBt3bq1vPHGGyrthNdvXpcx//obqoD343xRrb1796oL8R9++EGJQby3vhQrVkyJNWz/s88+K3GHE0bw8JMnT06zrlmzZs6AAQNSl8+cOeOUL1/eGT58uFpesGCB07Nnz9T/HzhwoPPpp58GfI5Tp045CQkJqbdt27ap58XvwTJq1CgnB3ZFJm+bRJw6Ik6BAgWcDz74wElJScngniKEpMfJkyedNWvWqJ+gRYsW6nsezRu2IRhuueUWp2PHjqnLl1xyifPll1869957rzN06FC17sSJE06ePHmcsWPHquU5c+ao5zh06JCTmJjojBw50ilcuLCza9cudTt69Ki6X5UqVZzixYs7//vf/5wNGzao42j27NmdP//8M+D2tGnTxilUqJDz7LPPOuvXr1c/c+TI4XTu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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# With Magnetism\n", "interface.switch('refl1d')\n", "model.interface = interface\n", "model_interface = model.interface()\n", - "model_interface.magnetism = True\n", + "model_interface.include_magnetism = True\n", "model_data_magnetism = model.interface().reflectity_profile(\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", + "# Without Magnetism\n", "interface.switch('refl1d')\n", "model.interface = interface\n", "model_interface = model.interface()\n", - "model_interface.magnetism = False\n", + "model_interface.include_magnetism = False\n", "model_data_no_magnetism = model.interface().reflectity_profile(\n", " model_coords,\n", " model.unique_name,\n", @@ -543,15 +819,30 @@ "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)." + "We don't see any change in the determined reflectivity when enabling the ability to account for magnetism for a sample without any magnetic layers, even though magnetic samples are handled with a `PolarizedQProbe` (Refl1d) whereas non-magnetic samples are handled with a `QProbe` (Refl1d)." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "00c25554", - "metadata": {}, - "outputs": [], + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T11:33:50.204484Z", + "iopub.status.busy": "2026-08-11T11:33:50.203485Z", + "iopub.status.idle": "2026-08-11T11:33:50.208336Z", + "shell.execute_reply": "2026-08-11T11:33:50.208336Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0\n" + ] + } + ], "source": [ "print(max(abs(model_data_no_magnetism - model_data_magnetism)))" ] @@ -573,7 +864,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.12" + "version": "3.12.12" } }, "nbformat": 4, diff --git a/src/easyreflectometry/calculators/__init__.py b/src/easyreflectometry/calculators/__init__.py index 9f7bde45..1282928d 100644 --- a/src/easyreflectometry/calculators/__init__.py +++ b/src/easyreflectometry/calculators/__init__.py @@ -5,6 +5,7 @@ from .calculator_base import CalculatorBase from .factory import CalculatorFactory +from .polarization import PolarizationChannel imported_calculators = [] @@ -31,4 +32,4 @@ traceback.print_exc() print('Warning: refl1d is not installed') -__all__ = ['CalculatorBase', 'CalculatorFactory'] + [c.__name__ for c in imported_calculators] +__all__ = ['CalculatorBase', 'CalculatorFactory', 'PolarizationChannel'] + [c.__name__ for c in imported_calculators] diff --git a/src/easyreflectometry/calculators/calculator_base.py b/src/easyreflectometry/calculators/calculator_base.py index e2a92804..10fdb222 100644 --- a/src/easyreflectometry/calculators/calculator_base.py +++ b/src/easyreflectometry/calculators/calculator_base.py @@ -201,6 +201,25 @@ def reflectity_profile(self, x_array: np.ndarray, model_id: str) -> np.ndarray: """ return self._wrapper.calculate(x_array, model_id) + def polarized_reflectivity_profiles(self, x_array: np.ndarray, model_id: str) -> dict[str, np.ndarray]: + """Determines the reflectivity profiles of all four spin channels for the given range and model. + + Requires `include_magnetism` to be enabled and a calculator that supports it (refl1d). + + Parameters + ---------- + x_array : np.ndarray + Points to be calculated at. + model_id : str + The model id. + + Returns + ------- + dict[str, np.ndarray] + Reflectivity per spin channel, keyed 'pp', 'pm', 'mp', 'mm' (in that order). + """ + return self._wrapper.calculate_polarized(x_array, model_id) + def sld_profile(self, model_id: str) -> tuple[np.ndarray, np.ndarray]: """Return the scattering length density profile. @@ -235,3 +254,23 @@ def include_magnetism(self, magnetism: bool): True if the calculator should include magnetism. """ self._wrapper.magnetism = magnetism + + @property + def polarization_channel(self): + """The spin channel ('pp', 'pm', 'mp' or 'mm') used by `reflectity_profile` when magnetism is enabled. + + Note: this state belongs to the currently-active calculator instance; switching + calculators via the factory constructs a fresh instance and resets it. + """ + return self._wrapper.polarization_channel + + @polarization_channel.setter + def polarization_channel(self, channel) -> None: + """Set the spin channel for reflectivity calculations. + + Parameters + ---------- + channel : PolarizationChannel | str + One of 'pp', 'pm', 'mp', 'mm' (or the corresponding enum member). + """ + self._wrapper.polarization_channel = channel diff --git a/src/easyreflectometry/calculators/factory.py b/src/easyreflectometry/calculators/factory.py index c3e1479c..53fba178 100644 --- a/src/easyreflectometry/calculators/factory.py +++ b/src/easyreflectometry/calculators/factory.py @@ -22,6 +22,10 @@ def sld_profile(self, model_id: str) -> tuple: """Sld profile.""" return self().sld_profile(model_id) + def polarized_reflectivity_profiles(self, x_array, model_id: str) -> dict: + """Reflectivity profiles of all four spin channels ('pp', 'pm', 'mp', 'mm').""" + return self().polarized_reflectivity_profiles(x_array, model_id) + @property def fit_func(self) -> Callable: """Fit func.""" diff --git a/src/easyreflectometry/calculators/polarization.py b/src/easyreflectometry/calculators/polarization.py new file mode 100644 index 00000000..f70d04e1 --- /dev/null +++ b/src/easyreflectometry/calculators/polarization.py @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + +from enum import Enum + + +class PolarizationChannel(str, Enum): + """Spin cross-section channels for polarized neutron reflectometry. + + The accepted spellings are exactly the enum values ('pp', 'pm', 'mp', 'mm'); + uppercase strings are rejected. + """ + + PP = 'pp' # non-spin-flip, up-up + PM = 'pm' # spin-flip, up-down + MP = 'mp' # spin-flip, down-up + MM = 'mm' # non-spin-flip, down-down + + +# Mapping to refl1d cross-section indices: PolarizedNeutronProbe.xs is documented as +# "a sequence pp, pm, mp and mm". The pp/mm assignment is additionally pinned by +# physics tests; pm vs mp rests on the refl1d docstring alone (they are identical by +# symmetry for non-chiral, non-absorptive samples). +POLARIZATION_CHANNEL_TO_INDEX = { + PolarizationChannel.PP: 0, + PolarizationChannel.PM: 1, + PolarizationChannel.MP: 2, + PolarizationChannel.MM: 3, +} diff --git a/src/easyreflectometry/calculators/refl1d/wrapper.py b/src/easyreflectometry/calculators/refl1d/wrapper.py index 6985d47c..43829f78 100644 --- a/src/easyreflectometry/calculators/refl1d/wrapper.py +++ b/src/easyreflectometry/calculators/refl1d/wrapper.py @@ -8,14 +8,16 @@ from refl1d import names from refl1d.sample.layers import Repeat +from ..polarization import POLARIZATION_CHANNEL_TO_INDEX from ..wrapper_base import WrapperBase RESOLUTION_PADDING = 3.5 OVERSAMPLING_FACTOR = 21 -ALL_POLARIZATIONS = False class Refl1dWrapper(WrapperBase): + supports_magnetism = True + def create_material(self, name: str): """Create a material using SLD. @@ -82,6 +84,16 @@ def get_layer_value(self, name: str, key: str) -> float: ).value # TODO: check if we want to return the raw value or the full Parameter # noqa: E501 return super().get_layer_value(name, key) + def _remove_magnetism_from_layers(self) -> None: + """Detach Magnetism objects from all slabs. + + Called when magnetism is disabled: slabs carrying Magnetism objects would + crash refl1d's plain (unpolarized) QProbe path. Magnetic parameters must be + set again (via `update_layer`) after re-enabling magnetism. + """ + for layer in self.storage['layer'].values(): + layer.magnetism = None + def create_model(self, name: str): """Create a model for analysis. @@ -202,44 +214,68 @@ def calculate(self, q_array: np.ndarray, model_name: str) -> np.ndarray: np.ndarray Reflectivity calculated at q. """ + if self._magnetism: + reflectivities = self._polarized_reflectivities(q_array, model_name) + return reflectivities[POLARIZATION_CHANNEL_TO_INDEX[self._polarization_channel]] + sample = _build_sample(self.storage, model_name) # smearing() returns sigma, which is exactly what refl1d's probe.dQ expects. dq_array = self._resolution_function.smearing(q_array) + probe = _get_probe( + q_array=q_array, + dq_array=dq_array, + model_name=model_name, + storage=self.storage, + oversampling_factor=OVERSAMPLING_FACTOR, + ) + # returns q, reflectivity + _, reflectivity = names.Experiment(probe=probe, sample=sample).reflectivity() + return reflectivity + + def calculate_polarized(self, q_array: np.ndarray, model_name: str) -> dict[str, np.ndarray]: + """For a given q array calculate the reflectivity of all four spin channels. + Parameters + ---------- + q_array : np.ndarray + Array of data points to be calculated. + model_name : str + The model name. + + Returns + ------- + dict[str, np.ndarray] + Reflectivity per spin channel, keyed 'pp', 'pm', 'mp', 'mm' (in that order). + """ if not self._magnetism: - probe = _get_probe( - q_array=q_array, - dq_array=dq_array, - model_name=model_name, - storage=self.storage, - oversampling_factor=OVERSAMPLING_FACTOR, + raise ValueError( + 'Polarized reflectivity requires magnetism: enable it on this calculator first ' + '(`include_magnetism = True` on the calculator / `magnetism = True` on the wrapper).' ) - # returns q, reflectivity - _, reflectivity = names.Experiment(probe=probe, sample=sample).reflectivity() - else: - polarized_probe = _get_polarized_probe( - q_array=q_array, - dq_array=dq_array, - model_name=model_name, - storage=self.storage, - oversampling_factor=OVERSAMPLING_FACTOR, - all_polarizations=ALL_POLARIZATIONS, - ) - polarized_reflectivity = names.Experiment(probe=polarized_probe, sample=sample).reflectivity() - - 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] + reflectivities = self._polarized_reflectivities(q_array, model_name) + return {channel.value: reflectivities[index] for channel, index in POLARIZATION_CHANNEL_TO_INDEX.items()} - return reflectivity + def _polarized_reflectivities(self, q_array: np.ndarray, model_name: str) -> list: + """Reflectivity of the four spin cross-sections, in refl1d order (pp, pm, mp, mm).""" + sample = _build_sample(self.storage, model_name) + dq_array = self._resolution_function.smearing(q_array) + polarized_probe = _get_polarized_probe( + q_array=q_array, + dq_array=dq_array, + model_name=model_name, + storage=self.storage, + oversampling_factor=OVERSAMPLING_FACTOR, + ) + polarized_reflectivity = names.Experiment(probe=polarized_probe, sample=sample).reflectivity() + + # returns (q, reflectivity) per cross-section + reflectivities = [reflectivity for _, reflectivity in polarized_reflectivity] + if len(reflectivities) != 4: + raise RuntimeError(f'refl1d returned {len(reflectivities)} polarized cross-sections; expected 4.') + for channel, index in POLARIZATION_CHANNEL_TO_INDEX.items(): + if len(reflectivities[index]) != len(q_array) or not np.all(np.isfinite(reflectivities[index])): + raise RuntimeError(f'refl1d returned a malformed {channel.value} cross-section.') + return reflectivities def sld_profile(self, model_name: str) -> Tuple[np.ndarray, np.ndarray]: """Return the scattering length density profile. @@ -309,23 +345,19 @@ def _get_polarized_probe( model_name: str, storage: dict, oversampling_factor: int = 1, - all_polarizations: bool = False, ) -> names.PolarizedNeutronQProbe: - """Get polarized probe.""" - four_probes = [] - for i in range(4): - if i == 0 or all_polarizations: - probe = _get_probe( - q_array=q_array, - dq_array=dq_array, - model_name=model_name, - storage=storage, - oversampling_factor=oversampling_factor, - magnetism=True, # Enable magnetism for polarized probes - ) - else: - probe = None - four_probes.append(probe) + """Get polarized probe with all four cross-sections (pp, pm, mp, mm).""" + four_probes = [ + _get_probe( + q_array=q_array, + dq_array=dq_array, + model_name=model_name, + storage=storage, + oversampling_factor=oversampling_factor, + magnetism=True, # Enable magnetism for polarized probes + ) + for _ in range(4) + ] # Create polarized probe and work around initialization bug polarized_probe = names.PolarizedNeutronQProbe.__new__(names.PolarizedNeutronQProbe) diff --git a/src/easyreflectometry/calculators/refnx/wrapper.py b/src/easyreflectometry/calculators/refnx/wrapper.py index 65dc8662..3ebb0bc4 100644 --- a/src/easyreflectometry/calculators/refnx/wrapper.py +++ b/src/easyreflectometry/calculators/refnx/wrapper.py @@ -14,22 +14,6 @@ class RefnxWrapper(WrapperBase): - @property - def include_magnetism(self) -> bool: - """Include magnetism.""" - return self._magnetism - - @include_magnetism.setter - def include_magnetism(self, magnetism: bool) -> None: - """Set the magnetism flag. - - Parameters - ---------- - magnetism : bool - 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 dc53ceca..23a0329d 100644 --- a/src/easyreflectometry/calculators/wrapper_base.py +++ b/src/easyreflectometry/calculators/wrapper_base.py @@ -8,11 +8,17 @@ from easyreflectometry.model import PercentageFwhm from easyreflectometry.model import ResolutionFunction +from .polarization import PolarizationChannel + class WrapperBase: + #: Whether this calculator backend can model magnetic samples. + supports_magnetism = False + def __init__(self): """Constructor.""" self._magnetism = False + self._polarization_channel = PolarizationChannel.PP self.storage = { 'material': {}, 'layer': {}, @@ -317,4 +323,54 @@ def magnetism(self, magnetism: bool) -> None: magnetism : bool The magnetism flag. """ + if magnetism and not self.supports_magnetism: + raise NotImplementedError(f'Magnetism is not supported by {self.__class__.__name__}') self._magnetism = magnetism + if not magnetism: + # A non-pp channel is only meaningful on the polarized probe path. + self._polarization_channel = PolarizationChannel.PP + # Leave no magnetic residue behind: the unpolarized calculation path + # must work on the layers that already exist. + self._remove_magnetism_from_layers() + + def _remove_magnetism_from_layers(self) -> None: + """Strip backend magnetism state from existing layers when magnetism is disabled. + + No-op by default; overridden by backends that attach magnetic objects to layers. + """ + + @property + def polarization_channel(self) -> PolarizationChannel: + """The spin channel returned by `calculate` when magnetism is enabled.""" + return self._polarization_channel + + @polarization_channel.setter + def polarization_channel(self, channel: PolarizationChannel | str) -> None: + """Set the spin channel returned by `calculate`. + + Parameters + ---------- + channel : PolarizationChannel | str + One of 'pp', 'pm', 'mp', 'mm' (or the corresponding enum member). + """ + channel = PolarizationChannel(channel) + if channel is not PolarizationChannel.PP and not self._magnetism: + raise ValueError(f"Selecting the '{channel.value}' channel requires magnetism to be enabled.") + self._polarization_channel = channel + + def calculate_polarized(self, q_array: np.ndarray, model_name: str) -> dict[str, np.ndarray]: + """For a given q array calculate the reflectivity of all four spin channels. + + Parameters + ---------- + q_array : np.ndarray + Array of data points to be calculated. + model_name : str + The model name. + + Returns + ------- + dict[str, np.ndarray] + Reflectivity per spin channel, keyed 'pp', 'pm', 'mp', 'mm' (in that order). + """ + raise NotImplementedError(f'{self.__class__.__name__} does not support polarized reflectivity.') diff --git a/tests/calculators/refl1d/test_refl1d_calculator.py b/tests/calculators/refl1d/test_refl1d_calculator.py index 50de2db4..2bdfa410 100644 --- a/tests/calculators/refl1d/test_refl1d_calculator.py +++ b/tests/calculators/refl1d/test_refl1d_calculator.py @@ -155,6 +155,43 @@ def test_calculate_magnetic(self): ] assert_almost_equal(actual, expected, decimal=4) + def test_polarized_reflectivity_profiles(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=4.000, irho=0.000) + p._wrapper.create_material('Material3') + p._wrapper.update_material('Material3', rho=2.047, irho=0.000) + p._wrapper.create_model('MyModel') + 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=100, interface=0) + p._wrapper.update_layer('Layer2', magnetism_rhoM=2, magnetism_thetaM=45) + p._wrapper.create_layer('Layer3') + p._wrapper.assign_material_to_layer('Material3', 'Layer3') + p._wrapper.create_item('Item') + p._wrapper.add_layer_to_item('Layer1', 'Item') + p._wrapper.add_layer_to_item('Layer2', 'Item') + p._wrapper.add_layer_to_item('Layer3', 'Item') + p._wrapper.add_item('Item', 'MyModel') + q = np.linspace(0.005, 0.3, 50) + + channels = p.polarized_reflectivity_profiles(q, 'MyModel') + + assert_equal(list(channels.keys()), ['pp', 'pm', 'mp', 'mm']) + for reflectivity in channels.values(): + assert_equal(len(reflectivity), len(q)) + + # reflectity_profile follows the selected channel + for key in ['pp', 'pm', 'mp', 'mm']: + p.polarization_channel = key + assert_equal(p.polarization_channel.value, key) + assert_almost_equal(p.reflectity_profile(q, 'MyModel'), channels[key]) + 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 57c43ee8..be971293 100644 --- a/tests/calculators/refl1d/test_refl1d_wrapper.py +++ b/tests/calculators/refl1d/test_refl1d_wrapper.py @@ -10,9 +10,12 @@ from unittest.mock import patch import numpy as np +import pytest +from numpy.testing import assert_allclose from numpy.testing import assert_almost_equal from numpy.testing import assert_equal +from easyreflectometry.calculators.polarization import PolarizationChannel from easyreflectometry.calculators.refl1d.wrapper import Refl1dWrapper from easyreflectometry.calculators.refl1d.wrapper import _build_sample from easyreflectometry.calculators.refl1d.wrapper import _get_oversampling_q @@ -377,9 +380,11 @@ def test_get_polarized_probe(): assert all(probe.dQ == dq) assert len(probe.calc_Q) == len(q) assert len(probe.xs) == 4 - assert probe.xs[1:4] == [None, None, None] - assert probe.xs[0].intensity.value == 10 - assert probe.xs[0].background.value == 20 + for cross_section in probe.xs: + assert cross_section is not None + assert cross_section.intensity.value == 10 + assert cross_section.background.value == 20 + assert len(cross_section.calc_Q) == len(q) def test_get_polarized_probe_oversampling(): @@ -396,33 +401,231 @@ def test_get_polarized_probe_oversampling(): probe = _get_polarized_probe(q_array=q, dq_array=dq, model_name=model_name, storage=storage, oversampling_factor=2) # Then - assert len(probe.xs[0].calc_Qo) == 2 * len(q) + for cross_section in probe.xs: + assert len(cross_section.calc_Qo) == 2 * len(q) -def test_get_polarized_probe_polarization(): - # When - q = np.linspace(1, 10, 10) - dq = np.linspace(0.01, 0.1, 10) - model_name = 'model_name' +Q_POLARIZED = np.linspace(0.005, 0.3, 100) - storage = {'model': {model_name: {}}} - storage['model'][model_name]['scale'] = 10.0 - storage['model'][model_name]['bkg'] = 20.0 - # Then - probe = _get_polarized_probe( - q_array=q, - dq_array=dq, - model_name=model_name, - storage=storage, - all_polarizations=True, - ) +def _sample_wrapper(rho: float, magnetic: bool, rhoM: float = 0.0, thetaM: float = 270.0) -> Refl1dWrapper: + """Vacuum | 100 A layer of `rho` (optionally magnetic) | Si substrate. - # Expect - assert len(probe.xs[0].calc_Q) == len(q) - assert len(probe.xs[1].calc_Q) == len(q) - assert len(probe.xs[2].calc_Q) == len(q) - assert len(probe.xs[3].calc_Q) == len(q) + Magnetism must be enabled before `create_layer` — only then does the wrapper + attach a `Magnetism` object to the slab. `update_layer` requires BOTH magnetism + kwargs; partial updates raise KeyError and are not supported. + """ + p = Refl1dWrapper() + if magnetic: + p.magnetism = True + p.create_material('Vacuum') + p.update_material('Vacuum', rho=0.0, irho=0.0) + p.create_material('MaterialMag') + p.update_material('MaterialMag', rho=rho, irho=0.0) + p.create_material('Si') + p.update_material('Si', rho=2.047, irho=0.0) + p.create_model('MyModel') + p.create_layer('Superphase') + p.assign_material_to_layer('Vacuum', 'Superphase') + p.create_layer('LayerMag') + p.assign_material_to_layer('MaterialMag', 'LayerMag') + p.update_layer('LayerMag', thickness=100, interface=0) + if magnetic: + p.update_layer('LayerMag', magnetism_rhoM=rhoM, magnetism_thetaM=thetaM) + p.create_layer('Subphase') + p.assign_material_to_layer('Si', 'Subphase') + p.create_item('Item') + p.add_layer_to_item('Superphase', 'Item') + p.add_layer_to_item('LayerMag', 'Item') + p.add_layer_to_item('Subphase', 'Item') + p.add_item('Item', 'MyModel') + return p + + +def test_calculate_polarized_shape(): + p = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=45) + + channels = p.calculate_polarized(Q_POLARIZED, 'MyModel') + + assert list(channels.keys()) == ['pp', 'pm', 'mp', 'mm'] + for reflectivity in channels.values(): + assert isinstance(reflectivity, np.ndarray) + assert len(reflectivity) == len(Q_POLARIZED) + assert np.all(np.isfinite(reflectivity)) + + +def test_calculate_follows_selected_channel(): + p = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=45) + channels = p.calculate_polarized(Q_POLARIZED, 'MyModel') + + for channel in PolarizationChannel: + p.polarization_channel = channel + assert_allclose(p.calculate(Q_POLARIZED, 'MyModel'), channels[channel.value], rtol=1e-10) + + +def test_calculate_polarized_zero_magnetic_sld(): + # Polarized calculation with zero magnetic SLD (magnetism enabled, rhoM=0): + # the non-spin-flip channels degenerate to the unpolarized result and the + # spin-flip channels vanish. + p = _sample_wrapper(rho=4.0, magnetic=True, rhoM=0.0, thetaM=270) + unpolarized = _sample_wrapper(rho=4.0, magnetic=False) + + channels = p.calculate_polarized(Q_POLARIZED, 'MyModel') + reference = unpolarized.calculate(Q_POLARIZED, 'MyModel') + + assert_allclose(channels['pp'], reference, rtol=1e-5) + assert_allclose(channels['mm'], reference, rtol=1e-5) + # Tolerance pinned from the observed numerics of refl1d 1.0.0 (machine noise). + assert np.max(channels['pm']) < 1e-16 + assert np.max(channels['mp']) < 1e-16 + + +def test_calculate_polarized_channel_ordering(): + # Pins the pp/mm halves of POLARIZATION_CHANNEL_TO_INDEX with physics, guarding + # against a pp/mm swap: with the moment collinear with the neutron polarization + # axis there is no spin flip and the non-spin-flip channels see rho +/- rhoM. + # Empirically verified sign convention of refl1d 1.0.0 (QProbe path, default + # Aguide=270): thetaM=90 is the orientation where pp sees rho + rhoM; + # thetaM=270 swaps pp and mm; both are spin-flip-free. (What matters is + # refl1d's eigenstate assignment, not the geometric angle relative to Aguide.) + # If this test ever fails while the index map matches the refl1d docstring, + # the sign convention changed - adjust the expectation, not the code. + rho, rhoM = 4.0, 2.0 + p = _sample_wrapper(rho=rho, magnetic=True, rhoM=rhoM, thetaM=90) + plus = _sample_wrapper(rho=rho + rhoM, magnetic=False) + minus = _sample_wrapper(rho=rho - rhoM, magnetic=False) + + channels = p.calculate_polarized(Q_POLARIZED, 'MyModel') + + assert_allclose(channels['pp'], plus.calculate(Q_POLARIZED, 'MyModel'), rtol=1e-4, atol=1e-9) + assert_allclose(channels['mm'], minus.calculate(Q_POLARIZED, 'MyModel'), rtol=1e-4, atol=1e-9) + # Spin-flip tolerance pinned from observed refl1d 1.0.0 numerics (~1e-31). + assert np.max(channels['pm']) < 1e-30 + assert np.max(channels['mp']) < 1e-30 + + +def test_calculate_polarized_spin_flip(): + # Moment perpendicular to the neutron polarization (thetaM=0) produces spin flip; + # a collinear moment (thetaM=90, see test_calculate_polarized_channel_ordering) + # produces essentially none. + aligned = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=90) + perpendicular = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=0) + + channels_aligned = aligned.calculate_polarized(Q_POLARIZED, 'MyModel') + channels_perpendicular = perpendicular.calculate_polarized(Q_POLARIZED, 'MyModel') + + assert np.max(channels_perpendicular['pm']) > 1e3 * np.max(channels_aligned['pm']) + assert np.max(channels_perpendicular['pm']) > 1e-6 # absolute sanity floor + # pm and mp are identical by symmetry for a non-chiral, non-absorptive sample, + # so this cannot distinguish them: the pm=1 / mp=2 indices rest on the refl1d + # docstring alone ("a sequence pp, pm, mp and mm"). + assert_allclose(channels_perpendicular['pm'], channels_perpendicular['mp'], rtol=1e-10) + + +def test_calculate_polarized_scale_and_background(): + # Intensity and background must reach every cross-section: + # R_out = scale * R + bkg, channel by channel. + scale, bkg = 2.0, 1e-6 + plain = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=45) + scaled = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=45) + scaled.update_model('MyModel', scale=scale, bkg=bkg) + + channels_plain = plain.calculate_polarized(Q_POLARIZED, 'MyModel') + channels_scaled = scaled.calculate_polarized(Q_POLARIZED, 'MyModel') + + for key in ['pp', 'pm', 'mp', 'mm']: + assert_allclose(channels_scaled[key], scale * channels_plain[key] + bkg, rtol=1e-8) + + +def test_calculate_polarized_requires_magnetism(): + p = _sample_wrapper(rho=4.0, magnetic=False) + with pytest.raises(ValueError): + p.calculate_polarized(Q_POLARIZED, 'MyModel') + + +def test_polarization_channel_normalization(): + p = Refl1dWrapper() + p.magnetism = True + + p.polarization_channel = PolarizationChannel.MM + assert p.polarization_channel is PolarizationChannel.MM + p.polarization_channel = 'pm' + assert p.polarization_channel is PolarizationChannel.PM + + for bad in ['MM', 'xx', None]: + with pytest.raises(ValueError): + p.polarization_channel = bad + + +def test_polarization_channel_requires_magnetism(): + p = Refl1dWrapper() + with pytest.raises(ValueError): + p.polarization_channel = 'mm' + # pp is always allowed + p.polarization_channel = 'pp' + assert p.polarization_channel is PolarizationChannel.PP + + +def test_disabling_magnetism_resets_channel(): + p = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=45) + unpolarized = _sample_wrapper(rho=4.0, magnetic=False) + p.polarization_channel = 'mm' + + p.magnetism = False + + # The transition is complete: channel back to pp, slab Magnetism objects + # stripped, and the plain (unpolarized) calculation path works. + assert p.polarization_channel is PolarizationChannel.PP + assert all(layer.magnetism is None for layer in p.storage['layer'].values()) + assert_allclose(p.calculate(Q_POLARIZED, 'MyModel'), unpolarized.calculate(Q_POLARIZED, 'MyModel'), rtol=1e-10) + + # Re-enabling gives a clean magnetic state (no stale rhoM/thetaM); magnetic + # parameters must be set again via update_layer. + p.magnetism = True + assert p.polarization_channel is PolarizationChannel.PP + channels = p.calculate_polarized(Q_POLARIZED, 'MyModel') + assert_allclose(channels['pp'], unpolarized.calculate(Q_POLARIZED, 'MyModel'), rtol=1e-5) + + +def test_polarized_reflectivities_guards_malformed_output(): + p = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=45) + q = Q_POLARIZED + + # Fewer than four cross-sections + with patch('easyreflectometry.calculators.refl1d.wrapper.names.Experiment') as mock_experiment: + mock_experiment.return_value.reflectivity.return_value = [(q, np.ones(len(q)))] * 3 + with pytest.raises(RuntimeError, match='expected 4'): + p.calculate_polarized(q, 'MyModel') + + # Wrong-length cross-section + with patch('easyreflectometry.calculators.refl1d.wrapper.names.Experiment') as mock_experiment: + mock_experiment.return_value.reflectivity.return_value = [ + (q, np.ones(len(q))), + (q, np.ones(len(q) - 1)), + (q, np.ones(len(q))), + (q, np.ones(len(q))), + ] + with pytest.raises(RuntimeError, match='malformed pm'): + p.calculate_polarized(q, 'MyModel') + + # Non-finite values + with patch('easyreflectometry.calculators.refl1d.wrapper.names.Experiment') as mock_experiment: + bad = np.ones(len(q)) + bad[0] = np.nan + mock_experiment.return_value.reflectivity.return_value = [(q, np.ones(len(q)))] * 3 + [(q, bad)] + with pytest.raises(RuntimeError, match='malformed mm'): + p.calculate_polarized(q, 'MyModel') + + +def test_polarization_channel_survives_reset_storage(): + # reset_storage leaves _magnetism and the resolution function alone; + # the selected channel behaves consistently. + p = Refl1dWrapper() + p.magnetism = True + p.polarization_channel = 'mm' + p.reset_storage() + assert p.polarization_channel is PolarizationChannel.MM + assert p._magnetism is True @patch('easyreflectometry.calculators.refl1d.wrapper.names.Stack') diff --git a/tests/calculators/refnx/test_refnx_wrapper.py b/tests/calculators/refnx/test_refnx_wrapper.py index bb99d633..4ea18d0a 100644 --- a/tests/calculators/refnx/test_refnx_wrapper.py +++ b/tests/calculators/refnx/test_refnx_wrapper.py @@ -28,7 +28,18 @@ def test_init(self): def test_set_magnetism(self): p = RefnxWrapper() with pytest.raises(NotImplementedError): - p.include_magnetism = True + p.magnetism = True + assert p._magnetism is False + + def test_calculate_polarized_not_supported(self): + p = RefnxWrapper() + with pytest.raises(NotImplementedError): + p.calculate_polarized(np.linspace(0.01, 0.3, 10), 'MyModel') + + def test_polarization_channel_requires_magnetism(self): + p = RefnxWrapper() + with pytest.raises(ValueError): + p.polarization_channel = 'mm' def test_reset_storage(self): p = RefnxWrapper() diff --git a/tests/calculators/test_polarization_interface.py b/tests/calculators/test_polarization_interface.py new file mode 100644 index 00000000..0bfd4ea2 --- /dev/null +++ b/tests/calculators/test_polarization_interface.py @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + +""" +Model/factory-level tests for polarization channel selection. +""" + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from easyreflectometry.calculators import CalculatorFactory +from easyreflectometry.calculators import PolarizationChannel +from easyreflectometry.model import Model +from easyreflectometry.model import PercentageFwhm +from easyreflectometry.sample import Layer +from easyreflectometry.sample import Material +from easyreflectometry.sample import Multilayer +from easyreflectometry.sample import Sample + + +def _magnetic_model() -> Model: + vacuum = Material(sld=0, isld=0, name='Vacuum') + material = Material(sld=4.0, isld=0, name='Sld 4') + si = Material(sld=2.047, isld=0, name='Si') + superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase') + layer = Layer(material=material, thickness=100, roughness=0, name='Sld 4 Layer') + subphase = Layer(material=si, thickness=0, roughness=0, name='Si Subphase') + sample = Sample(Multilayer(superphase), Multilayer(layer), Multilayer(subphase), name='Sample') + model = Model(sample=sample, scale=1, background=0, name='Magnetic Model') + model.resolution_function = PercentageFwhm(0) + return model + + +Q = np.linspace(0.005, 0.3, 50) + + +def test_polarized_reflectivity_profiles_through_factory(): + model = _magnetic_model() + interface = CalculatorFactory() + interface.switch('refl1d') + model.interface = interface + calculator = model.interface() + calculator.include_magnetism = True + layer_name = list(calculator._wrapper.storage['layer'].keys())[1] + calculator._wrapper.update_layer(layer_name, magnetism_rhoM=2, magnetism_thetaM=45) + + # Through the factory (model.interface), not only the calculator (model.interface()) + channels = model.interface.polarized_reflectivity_profiles(Q, model.unique_name) + + assert list(channels.keys()) == ['pp', 'pm', 'mp', 'mm'] + for reflectivity in channels.values(): + assert len(reflectivity) == len(Q) + + # reflectity_profile (the fitting path) follows the selected channel + calculator.polarization_channel = 'mm' + assert_allclose(model.interface().reflectity_profile(Q, model.unique_name), channels['mm'], rtol=1e-10) + + +def test_switch_resets_polarization_state(): + model = _magnetic_model() + interface = CalculatorFactory() + interface.switch('refl1d') + model.interface = interface + calculator = model.interface() + calculator.include_magnetism = True + calculator.polarization_channel = 'mm' + + # switch() constructs a fresh calculator instance: channel and magnetism reset + interface.switch('refl1d') + + calculator = model.interface() + assert calculator.polarization_channel is PolarizationChannel.PP + assert calculator.include_magnetism is False + + +def test_refnx_include_magnetism_raises(): + interface = CalculatorFactory() + interface.switch('refnx') + + with pytest.raises(NotImplementedError): + interface().include_magnetism = True + assert interface().include_magnetism is False From 530f681aa1f37166d474cf88868be40c3c9951fe Mon Sep 17 00:00:00 2001 From: rozyczko Date: Tue, 11 Aug 2026 14:50:13 +0200 Subject: [PATCH 02/22] added magnetic SLD profile --- CHANGELOG.md | 5 + docs/docs/api-reference/calculators.md | 3 + .../docs/tutorials/simulation/magnetism.ipynb | 384 ++++++++---------- .../calculators/calculator_base.py | 17 + src/easyreflectometry/calculators/factory.py | 4 + .../calculators/refl1d/wrapper.py | 29 ++ .../calculators/wrapper_base.py | 14 + .../test_polarization_interface.py | 40 ++ 8 files changed, 283 insertions(+), 213 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93bebd49..44fe8d62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ returned. it affects every subsequent calculation with that calculator, and `interface.switch(...)` constructs a fresh calculator, resetting it (along with `include_magnetism`). +- New `magnetic_sld_profile(model_id)` on the calculator (and on + `CalculatorFactory`) returns the nuclear and magnetic scattering + length density profiles as a tuple `z`, `sld(z)`, `rhoM(z)` (magnetic + SLD) and `thetaM(z)` (magnetic angle). Requires + `include_magnetism = True`; refl1d only. - Magnetic calculations now always build all four refl1d cross-sections, so they may take somewhat longer than before; pp results are unchanged. diff --git a/docs/docs/api-reference/calculators.md b/docs/docs/api-reference/calculators.md index 870355f2..d90afbbc 100644 --- a/docs/docs/api-reference/calculators.md +++ b/docs/docs/api-reference/calculators.md @@ -13,6 +13,9 @@ spin channels are available: `'pm'`, `'mp'`, `'mm'` (in that order). - `polarization_channel` selects which channel `reflectity_profile` — and hence fitting — uses (default `'pp'`). +- `magnetic_sld_profile(model_id)` returns the nuclear and magnetic + scattering length density profiles as a tuple `z`, `sld(z)`, + `rhoM(z)` (magnetic SLD) and `thetaM(z)` (magnetic angle). Note that `polarization_channel` is state of the currently active calculator instance, not of a model or dataset: it affects every diff --git a/docs/docs/tutorials/simulation/magnetism.ipynb b/docs/docs/tutorials/simulation/magnetism.ipynb index c1486825..2c8f6977 100644 --- a/docs/docs/tutorials/simulation/magnetism.ipynb +++ b/docs/docs/tutorials/simulation/magnetism.ipynb @@ -22,14 +22,14 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "644e53e3", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:44.947675Z", - "iopub.status.busy": "2026-08-11T11:33:44.947675Z", - "iopub.status.idle": "2026-08-11T11:33:45.377398Z", - "shell.execute_reply": "2026-08-11T11:33:45.377398Z" + "iopub.execute_input": "2026-08-11T12:33:34.890141Z", + "iopub.status.busy": "2026-08-11T12:33:34.890141Z", + "iopub.status.idle": "2026-08-11T12:33:35.320450Z", + "shell.execute_reply": "2026-08-11T12:33:35.320450Z" } }, "outputs": [], @@ -39,14 +39,14 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "29d5d62d-af4a-416d-bbe2-1338d32b30f5", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:45.380421Z", - "iopub.status.busy": "2026-08-11T11:33:45.380421Z", - "iopub.status.idle": "2026-08-11T11:33:48.205821Z", - "shell.execute_reply": "2026-08-11T11:33:48.205821Z" + "iopub.execute_input": "2026-08-11T12:33:35.320450Z", + "iopub.status.busy": "2026-08-11T12:33:35.320450Z", + "iopub.status.idle": "2026-08-11T12:33:38.194648Z", + "shell.execute_reply": "2026-08-11T12:33:38.194648Z" } }, "outputs": [], @@ -78,28 +78,17 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "549734c1-bbd9-41f3-8a20-d7a8ded37802", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.209574Z", - "iopub.status.busy": "2026-08-11T11:33:48.207956Z", - "iopub.status.idle": "2026-08-11T11:33:48.214078Z", - "shell.execute_reply": "2026-08-11T11:33:48.214078Z" + "iopub.execute_input": "2026-08-11T12:33:38.194648Z", + "iopub.status.busy": "2026-08-11T12:33:38.194648Z", + "iopub.status.idle": "2026-08-11T12:33:38.202401Z", + "shell.execute_reply": "2026-08-11T12:33:38.202401Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "numpy: 2.3.5\n", - "scipp: 25.11.0\n", - "easyreflectometry: 1.5.0\n", - "refl1d: 1.0.0\n" - ] - } - ], + "outputs": [], "source": [ "print(f'numpy: {np.__version__}')\n", "print(f'scipp: {sc.__version__}')\n", @@ -131,14 +120,14 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "0f95d620-35b7-4b47-a3b4-9e33d5525b50", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.218231Z", - "iopub.status.busy": "2026-08-11T11:33:48.217199Z", - "iopub.status.idle": "2026-08-11T11:33:48.231252Z", - "shell.execute_reply": "2026-08-11T11:33:48.230138Z" + "iopub.execute_input": "2026-08-11T12:33:38.202401Z", + "iopub.status.busy": "2026-08-11T12:33:38.202401Z", + "iopub.status.idle": "2026-08-11T12:33:38.215900Z", + "shell.execute_reply": "2026-08-11T12:33:38.214943Z" } }, "outputs": [], @@ -166,14 +155,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "2af8c30b", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.233176Z", - "iopub.status.busy": "2026-08-11T11:33:48.233176Z", - "iopub.status.idle": "2026-08-11T11:33:48.239570Z", - "shell.execute_reply": "2026-08-11T11:33:48.238822Z" + "iopub.execute_input": "2026-08-11T12:33:38.215900Z", + "iopub.status.busy": "2026-08-11T12:33:38.215900Z", + "iopub.status.idle": "2026-08-11T12:33:38.223629Z", + "shell.execute_reply": "2026-08-11T12:33:38.223629Z" } }, "outputs": [], @@ -198,14 +187,14 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "b0259cd0", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.241312Z", - "iopub.status.busy": "2026-08-11T11:33:48.241312Z", - "iopub.status.idle": "2026-08-11T11:33:48.245759Z", - "shell.execute_reply": "2026-08-11T11:33:48.245759Z" + "iopub.execute_input": "2026-08-11T12:33:38.223629Z", + "iopub.status.busy": "2026-08-11T12:33:38.223629Z", + "iopub.status.idle": "2026-08-11T12:33:38.229743Z", + "shell.execute_reply": "2026-08-11T12:33:38.229743Z" } }, "outputs": [], @@ -231,14 +220,14 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "f1500603-d85d-4e16-b697-e1bf16502991", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.247908Z", - "iopub.status.busy": "2026-08-11T11:33:48.247908Z", - "iopub.status.idle": "2026-08-11T11:33:48.251851Z", - "shell.execute_reply": "2026-08-11T11:33:48.251851Z" + "iopub.execute_input": "2026-08-11T12:33:38.232600Z", + "iopub.status.busy": "2026-08-11T12:33:38.232600Z", + "iopub.status.idle": "2026-08-11T12:33:38.237388Z", + "shell.execute_reply": "2026-08-11T12:33:38.237388Z" } }, "outputs": [], @@ -257,14 +246,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "18010202", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.255153Z", - "iopub.status.busy": "2026-08-11T11:33:48.254020Z", - "iopub.status.idle": "2026-08-11T11:33:48.259502Z", - "shell.execute_reply": "2026-08-11T11:33:48.258989Z" + "iopub.execute_input": "2026-08-11T12:33:38.237388Z", + "iopub.status.busy": "2026-08-11T12:33:38.237388Z", + "iopub.status.idle": "2026-08-11T12:33:38.244064Z", + "shell.execute_reply": "2026-08-11T12:33:38.244064Z" } }, "outputs": [], @@ -296,28 +285,17 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "cdf959c8", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.262018Z", - "iopub.status.busy": "2026-08-11T11:33:48.261013Z", - "iopub.status.idle": "2026-08-11T11:33:48.731414Z", - "shell.execute_reply": "2026-08-11T11:33:48.731414Z" + "iopub.execute_input": "2026-08-11T12:33:38.244064Z", + "iopub.status.busy": "2026-08-11T12:33:38.244064Z", + "iopub.status.idle": "2026-08-11T12:33:38.717693Z", + "shell.execute_reply": "2026-08-11T12:33:38.717693Z" } }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Refl1d\n", "probe = refl1d.names.QProbe(\n", @@ -359,28 +337,17 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "bf311973", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.734190Z", - "iopub.status.busy": "2026-08-11T11:33:48.733539Z", - "iopub.status.idle": "2026-08-11T11:33:48.972922Z", - "shell.execute_reply": "2026-08-11T11:33:48.972922Z" + "iopub.execute_input": "2026-08-11T12:33:38.717693Z", + "iopub.status.busy": "2026-08-11T12:33:38.717693Z", + "iopub.status.idle": "2026-08-11T12:33:38.962554Z", + "shell.execute_reply": "2026-08-11T12:33:38.962554Z" } }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Without magnetic layers\n", "interface.switch('refl1d')\n", @@ -428,28 +395,17 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "18cb7037", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:48.975220Z", - "iopub.status.busy": "2026-08-11T11:33:48.975220Z", - "iopub.status.idle": "2026-08-11T11:33:49.187981Z", - "shell.execute_reply": "2026-08-11T11:33:49.187981Z" + "iopub.execute_input": "2026-08-11T12:33:38.965319Z", + "iopub.status.busy": "2026-08-11T12:33:38.965319Z", + "iopub.status.idle": "2026-08-11T12:33:39.181473Z", + "shell.execute_reply": "2026-08-11T12:33:39.181473Z" } }, - "outputs": [ - { - "data": { - "image/png": "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v//+69cxGjRogLZt26JWrVpGNDVv3hwdO3ZMcn+KLKbWHGJjY3Hw4EEUKFDAeJCSozQplrZt24bcuXP7fTsvo2uia6LXif529F6i99e0ghkb+nudjE9EVXW98sorZvGH6Ohos7jJmzdviu+bokfCR9dErxP97QQCvZ/oeug1knwY9AgmAT16wYIFERUVhT179sTbzvWiRYsG8q6EEEIIIdJX+GTJkgW1a9fGzJkz46WeuN6wYcNA3pUQQgghRPBTXceOHcP69evj1jdt2mSqtJiTY26Ofptu3bqhTp06qFevHoYPH25K4J0qr1CF6TKW4PumzSIZXRNdE71O9Lej9xK9v3rtMyeDlcy6MVZZNW3aNMF2ip1x48aZnzly4s0338Tu3btNz5533nnHNDIUQgghhEhPki18hBBCCCHClZAbUiqEEEIIESwkfIQQQggRMUj4CCGEECJi8LTwSe6UeM4Ju+KKK8z+1atXx9SpU+P9nnaoF154AcWKFUO2bNnMKI5169YhUq9H9+7dTXds99K6dWuEE8m5JitXrsTNN99s9udjZcViao8ZCddk0KBBCV4nfF159ZqMHj0a1113HfLly2cWvk/47h/u7yXBuCaR9n7y7bffmupnNtzNkSOHKQT69NNPI/p18q0f1yQgrxPLo3z55ZdWlixZrI8//thauXKl1aNHDytv3rzWnj17Et3/jz/+sKKioqw33njDWrVqlfX8889bmTNntlasWBG3z2uvvWblyZPHmjx5srV8+XKrY8eOVtmyZa2TJ09akXg9unXrZrVu3dratWtX3HLw4EErXEjuNVm0aJH15JNPWl988YVVtGhR6+233071MSPhmgwcONCqWrVqvNfJvn37rHAhudfkzjvvtP7v//7PWrZsmbV69Wqre/fu5n1j+/btnngvCdY1ibT3k1mzZlnffvuteX9dv369NXz4cPOeO23atIh9nczy45oE4nXiWeFTr149q1evXnHr58+ft4oXL24NGTIk0f1vu+02q127dvG21a9f33rwwQfNz7GxseaN/c0334z7/eHDh63o6Gjzph9p18N5AXbq1MkKV5J7TdxcdtlliX7Ip+aYXr0mFD41atSwwpXUPqfnzp2zcuXKZX3yySeeeC8JxjWJ9PcTh1q1apkvmUSvk4TXJFCvE0+mupwp8QwL+jslntvd+5NWrVrF7c9GjexL5N6H02MZuvN38ryXroe7r1PhwoVRqVIl9OzZ00zVDQdSck3S45hpSTDPn+H54sWL4/LLL0eXLl2wdetWhAOBuCYnTpzA2bNn4wYvhvN7SbCuSaS/nzAIwQkHa9asQaNGjcy2SH+dWIlck0C9TjwpfC42JZ4vpMTg9ovt7/yfnGN6+XoQ5lXHjx9vXpyvv/465syZgzZt2pj7CnVSck3S45hpSbDOn2/UbG46bdo0vPfee+YNnX6Po0ePIhKuyTPPPGNEn/MBEM7vJcG6JpH6fsJp5Dlz5jTjntq1a4eRI0eiZcuWEf06ibnINQnU6yQkp7OL8OD222+P+5nm5yuvvBLlypUzarx58+bpem4idOCbkgNfIxRCl112Gb766ivcd9998DKvvfYavvzyS/M3QXOnSPqaROL7Sa5cuczIJ46C4gc5Rz4xKtqkSRNEKrkucU0C8TrxZMQnJVPiuf1i+zv/h+Pk+WBcj8Tgi5P35Z7l5qVrkh7HTEvS6vxZsVGxYkXPv06GDh1qPuSnT59u3pwdwvm9JFjXJFLfT5j6KV++vKle6tu3L2655RYMGTIkol8nGS9yTQL1OvGk8EnJlHhud+9Pfvnll7j9y5Yta54s9z5HjhzBwoULQ37yfDCuR2Js377d5FpZehnqpOSapMcx05K0On9+k9uwYYOnXydvvPEGXnrpJZPeY3mum3B+LwnWNUmMSHw/4W1Onz4d0a+Ti12TgL1OLI/CMjpWSYwbN86Uxj3wwAOmjG737t3m9127drX69esXr3w7U6ZM1tChQ025JStREitn5zG+++476++//zbO8nApLQz09Th69KgpY54/f761adMma8aMGdZVV11lVahQwTp16pQVDiT3mpw+fdqU43IpVqyYefz8ed26dX4fMxKvSd++fa3Zs2eb1wlfVy1atLAKFixo7d271/LiNeH7BEt4v/7663glt/yb8cJ7STCuSSS+n7z66qvW9OnTrQ0bNpj9+V7L99zRo0dH7Ovk1Utck0C9TjwrfMjIkSOt0qVLmz84lhouWLAg7neNGzc2ZXFuvvrqK6tixYpmf/YdmTJlSrzfs7xwwIABVpEiRcyT2bx5c2vNmjVWJF6PEydOWNdff71VqFAhI4hYysweDeHyAZ+Sa8I/NH5X8F24n7/HjMRr0rlzZyOKeLwSJUqYdfbo8Oo14d9CYteEXx688l4S6GsSie8nzz33nFW+fHkra9asVr58+ayGDRsaoeAm0l4nz13imgTqdaLp7EIIIYSIGDzp8RFCCCGESAwJHyGEEEJEDBI+QgghhIgYJHyEEEIIETFI+AghhBAiYpDwEUIIIUTEIOEjhBBCiIhBwkcIIYQQEYOEjxBCCCEiBgkfIYQQQkQMEj5CCCGEiBgkfIQQQgiBSOH/AdvsJNlBYJIFAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Refl1d model is inverted as compared to EasyReflectometry, so the order of the layers is reversed\n", "refl1d_sample = (\n", @@ -498,25 +454,17 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "7033f755", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:49.190329Z", - "iopub.status.busy": "2026-08-11T11:33:49.190329Z", - "iopub.status.idle": "2026-08-11T11:33:49.200414Z", - "shell.execute_reply": "2026-08-11T11:33:49.200414Z" + "iopub.execute_input": "2026-08-11T12:33:39.183451Z", + "iopub.status.busy": "2026-08-11T12:33:39.183451Z", + "iopub.status.idle": "2026-08-11T12:33:39.188618Z", + "shell.execute_reply": "2026-08-11T12:33:39.188618Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0\n" - ] - } - ], + "outputs": [], "source": [ "print(max(abs(model_data_magnetism_easy - model_data_magnetism_ref1d)))" ] @@ -542,28 +490,17 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "352c35e9", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:49.202700Z", - "iopub.status.busy": "2026-08-11T11:33:49.202700Z", - "iopub.status.idle": "2026-08-11T11:33:49.429422Z", - "shell.execute_reply": "2026-08-11T11:33:49.429422Z" + "iopub.execute_input": "2026-08-11T12:33:39.188618Z", + "iopub.status.busy": "2026-08-11T12:33:39.188618Z", + "iopub.status.idle": "2026-08-11T12:33:39.424010Z", + "shell.execute_reply": "2026-08-11T12:33:39.424010Z" } }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# The magnetic SLD is set to 8 and its angle to 45 degrees.\n", "# An angle of 90 would double (pp) and cancel out (mm) the magnitude of the reflectivity oscillations,\n", @@ -609,28 +546,17 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "239e2a04", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:49.431597Z", - "iopub.status.busy": "2026-08-11T11:33:49.431597Z", - "iopub.status.idle": "2026-08-11T11:33:49.751931Z", - "shell.execute_reply": "2026-08-11T11:33:49.750821Z" + "iopub.execute_input": "2026-08-11T12:33:39.424010Z", + "iopub.status.busy": "2026-08-11T12:33:39.424010Z", + "iopub.status.idle": "2026-08-11T12:33:39.723594Z", + "shell.execute_reply": "2026-08-11T12:33:39.723594Z" } }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# The single layer model matching the raw Refl1d sample above\n", "vacuum_single = Material(sld=0, isld=0, name='Vacuum')\n", @@ -683,28 +609,17 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "f70f3a34", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:49.754827Z", - "iopub.status.busy": "2026-08-11T11:33:49.754827Z", - "iopub.status.idle": "2026-08-11T11:33:49.761491Z", - "shell.execute_reply": "2026-08-11T11:33:49.759728Z" + "iopub.execute_input": "2026-08-11T12:33:39.723594Z", + "iopub.status.busy": "2026-08-11T12:33:39.723594Z", + "iopub.status.idle": "2026-08-11T12:33:39.729761Z", + "shell.execute_reply": "2026-08-11T12:33:39.729761Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "pp: 0.0\n", - "pm: 0.0\n", - "mp: 0.0\n", - "mm: 0.0\n" - ] - } - ], + "outputs": [], "source": [ "for key in channels:\n", " print(f'{key}: {max(abs(channels[key] - raw_channels[key]))}')" @@ -724,25 +639,17 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "23beb5a6", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:49.763863Z", - "iopub.status.busy": "2026-08-11T11:33:49.763374Z", - "iopub.status.idle": "2026-08-11T11:33:49.773854Z", - "shell.execute_reply": "2026-08-11T11:33:49.773347Z" + "iopub.execute_input": "2026-08-11T12:33:39.729761Z", + "iopub.status.busy": "2026-08-11T12:33:39.729761Z", + "iopub.status.idle": "2026-08-11T12:33:39.739748Z", + "shell.execute_reply": "2026-08-11T12:33:39.739748Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "mm channel reproduced: 0.0\n" - ] - } - ], + "outputs": [], "source": [ "model_interface.polarization_channel = 'mm'\n", "reflectivity_mm = single_layer_model.interface().reflectity_profile(\n", @@ -755,6 +662,76 @@ "model_interface.polarization_channel = 'pp'" ] }, + { + "cell_type": "markdown", + "id": "a2d2d869", + "metadata": {}, + "source": [ + "### Magnetic SLD profile\n", + "\n", + "Alongside the nuclear SLD profile (`sld_profile`), the magnetic components are available through `magnetic_sld_profile`, which returns `z`, the nuclear SLD, the magnetic SLD (`rhoM`) and the magnetic angle (`thetaM`). Like the polarized reflectivities it requires `include_magnetism` to be enabled and is only available for the Refl1d calculator.\n", + "\n", + "Below we display the profiles for the single magnetic layer sample used above. The nuclear and magnetic SLD share the same unit ($10^{-6}$ Å$^{-2}$) so they are shown on one axis; the magnetic angle is constant (45 degrees) inside the layer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6975f54", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T12:33:39.739748Z", + "iopub.status.busy": "2026-08-11T12:33:39.739748Z", + "iopub.status.idle": "2026-08-11T12:33:39.834787Z", + "shell.execute_reply": "2026-08-11T12:33:39.834787Z" + } + }, + "outputs": [], + "source": [ + "z, sld, sld_magnetic, theta_magnetic = single_layer_model.interface.magnetic_sld_profile(single_layer_model.unique_name)\n", + "\n", + "# In this sample the nuclear and magnetic SLD are both 8 inside the layer, so the curves overlap there\n", + "plt.plot(z, sld, '-k', label='Nuclear SLD', linewidth=4)\n", + "plt.plot(z, sld_magnetic, '-r', label='Magnetic SLD', linewidth=2)\n", + "plt.xlabel(r'$z$ / Å')\n", + "plt.ylabel(r'SLD / $10^{-6}$ Å$^{-2}$')\n", + "plt.legend()\n", + "plt.show()\n", + "\n", + "inside_layer = (z > 25) & (z < 125)\n", + "print(f'Magnetic angle inside the layer: {theta_magnetic[inside_layer].mean():.1f} degrees')" + ] + }, + { + "cell_type": "markdown", + "id": "f0223d02", + "metadata": {}, + "source": [ + "The profiles reproduce the ones determined directly by Refl1d for the equivalent sample (`magnetic_smooth_profile`). Since the Refl1d sample is built in the reverse order, its profiles are flipped before comparing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6327bf82", + "metadata": { + "execution": { + "iopub.execute_input": "2026-08-11T12:33:39.834787Z", + "iopub.status.busy": "2026-08-11T12:33:39.834787Z", + "iopub.status.idle": "2026-08-11T12:33:39.843123Z", + "shell.execute_reply": "2026-08-11T12:33:39.843123Z" + } + }, + "outputs": [], + "source": [ + "# `experiment` is the raw Refl1d experiment for the single magnetic layer sample defined above\n", + "raw_z, raw_sld, _, raw_sld_magnetic, raw_theta_magnetic = experiment.magnetic_smooth_profile()\n", + "\n", + "print(f'sld: {max(abs(sld - raw_sld[::-1]))}')\n", + "print(f'magnetic sld: {max(abs(sld_magnetic - raw_sld_magnetic[::-1]))}')\n", + "print(f'magnetic angle: {max(abs(theta_magnetic - raw_theta_magnetic[::-1]))}')" + ] + }, { "cell_type": "markdown", "id": "97e3094a", @@ -766,28 +743,17 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "b087e848", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:49.990332Z", - "iopub.status.busy": "2026-08-11T11:33:49.990332Z", - "iopub.status.idle": "2026-08-11T11:33:50.201479Z", - "shell.execute_reply": "2026-08-11T11:33:50.201479Z" + "iopub.execute_input": "2026-08-11T12:33:39.843123Z", + "iopub.status.busy": "2026-08-11T12:33:39.843123Z", + "iopub.status.idle": "2026-08-11T12:33:40.044974Z", + "shell.execute_reply": "2026-08-11T12:33:40.044974Z" } }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj4AAAGdCAYAAAASUnlxAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAimhJREFUeJztnQeYE+X2xg+9995BkCbSpSgISpMmWLGC6EVF9GIHrgpeG4oNy1UURRAVUf8UlaKIKEV6URSkCdI7LH13Yef/vB/ft34bkiW7mzKZvL/nCbszZJPJJJl555z3nJPNcRxHCCGEEELigOzR3gBCCCGEkEhB4UMIIYSQuIHChxBCCCFxA4UPIYQQQuIGCh9CCCGExA0UPoQQQgiJGyh8CCGEEBI3UPgQQgghJG7IKR4jJSVFdu7cKYUKFZJs2bJFe3MIIYQQEgTop3z06FEpX768ZM8evriM54QPRE+lSpWivRmEEEIIyQTbtm2TihUrSrjwnPBBpMfsuMKFC0d7cwghhBASBEeOHFGBC3MeDxeeEz4mvQXRQ+FDCCGExBbZwmxTobmZEEIIIXGDZ4TP//73P6lbt65ccskl0d4UQgghhLiUbA5s1B7LERYpUkQSEhKY6iKEEEJihCMROn97zuNDCPEuuE47ffq0nDlzJtqbQgjJIDly5JCcOXNGvdUMhQ8hJCZISkqSXbt2yYkTJ6K9KYSQTJI/f34pV66c5M6dW6IFhQ8hJCYak27evFldMaK5GQ6a0b5qJIRkLFqLi5d9+/ap7/KFF14Y1iaF6UHhQwhxPThgQvygxweuGAkhsUe+fPkkV65c8vfff6vvdN68eaOyHZ6p6iKEeJ9oXSESQrzzHY7+Fvjh22+/lVq1aqlQ2AcffBDtzSGEEEKIR3Cd8EHFxsMPPyw//vijrFy5Ul5++WU5cOBAtDeLEEIixk8//aQ8TIcPH073flWrVpWRI0dGbLvcDPbXlClTIv68H374oXTs2DEkj/X0009LmTJlUl/LHXfcIT179szy49qfk6SkJLW8bNkyiVdcJ3yWLFkiF110kVSoUEEKFiwonTt3lu+//z7am0UIIRlm1KhRau4QLugMx44dUz6Htm3b+hU7mzZtkksvvVRVsKGnCRg7dqwULVqU74AWBw0bNjxnX2B/4XwRSU6dOiVPPfWUDBs2LMuPtXbtWvnvf/8r7733XsDXMnfuXOnevbsy+GdW6OXOnVseffRRGTRokMQrITc3441BlGb58uXqzZs8efI5ihVdlnGf3bt3S4MGDeStt96SZs2apU5Xh+gx4PcdO3ZIOJk3b55MmDAhU3+LD1/NmjXlrrvuUkKNEEIMV1xxhRI6uLpu0aJF6vGmbNmysnjxYnXiNAbPOXPmSOXKlaV69epqGfchwRON/fXVV1+pRnuXXXZZwPsgwhJM6TYEL+jRo0fAisXjx4+rc+add94p1157baa3+9Zbb5VHHnlE/vjjDxVoiDdCLnzO98ZMnDhRpbJwJdS8eXMVfuvUqZOsW7dOSpcuLdFgzZo18u6770rjTPwt2qi9JyJvvvmmTJ8+XXmTCCHhpV+/fvL7779HdTfXq1dPRo8ene59cDxAzxJEc4zwwe84uSGdv2jRotTID9ZDKNm/Hzp0SFatWiV9+/ZV680JEREGRD4A+hrhePvll19KsWLF5Mknn5S777474Dbh+S6++GLVGmDcuHHqpPzcc8/JLbfcIvfff786mSPdggtSE3VAw0g8JrYZF6wQaPfdd58MHDjwHJvCxx9/rB77X//6l7ovuvCayASeu379+krswb+J57733ntTXwtAeg8RialTp0piYqI0bdpUXn/9dXVeQeQLURF7X3z00UcqJYRlc6ENsYFt+b//+z+1D/F68DxDhgxJ/Vucg7755hv1mqpUqSJjxoyRUqVKqe1eunSper7x48enClF/fP755yoCY4NtwWvA+CRc5OfJk0eVb2/btk2JDWQwYPBt3bq1vPHGGyrthNdvXpcx//obqoD343xRrb1796oL8R9++EGJQby3vhQrVkyJNWz/s88+K3GHE0bw8JMnT06zrlmzZs6AAQNSl8+cOeOUL1/eGT58uFpesGCB07Nnz9T/HzhwoPPpp58GfI5Tp045CQkJqbdt27ap58XvwTJq1CgnB3ZFJm+bRJw6Ik6BAgWcDz74wElJScngniKEpMfJkyedNWvWqJ+gRYsW6nsezRu2IRhuueUWp2PHjqnLl1xyifPll1869957rzN06FC17sSJE06ePHmcsWPHquU5c+ao5zh06JCTmJjojBw50ilcuLCza9cudTt69Ki6X5UqVZzixYs7//vf/5wNGzao42j27NmdP//8M+D2tGnTxilUqJDz7LPPOuvXr1c/c+TI4XTu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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# With Magnetism\n", "interface.switch('refl1d')\n", @@ -824,25 +790,17 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "00c25554", "metadata": { "execution": { - "iopub.execute_input": "2026-08-11T11:33:50.204484Z", - "iopub.status.busy": "2026-08-11T11:33:50.203485Z", - "iopub.status.idle": "2026-08-11T11:33:50.208336Z", - "shell.execute_reply": "2026-08-11T11:33:50.208336Z" + "iopub.execute_input": "2026-08-11T12:33:40.044974Z", + "iopub.status.busy": "2026-08-11T12:33:40.044974Z", + "iopub.status.idle": "2026-08-11T12:33:40.052467Z", + "shell.execute_reply": "2026-08-11T12:33:40.051582Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.0\n" - ] - } - ], + "outputs": [], "source": [ "print(max(abs(model_data_no_magnetism - model_data_magnetism)))" ] diff --git a/src/easyreflectometry/calculators/calculator_base.py b/src/easyreflectometry/calculators/calculator_base.py index 10fdb222..3761bfed 100644 --- a/src/easyreflectometry/calculators/calculator_base.py +++ b/src/easyreflectometry/calculators/calculator_base.py @@ -235,6 +235,23 @@ def sld_profile(self, model_id: str) -> tuple[np.ndarray, np.ndarray]: """ return self._wrapper.sld_profile(model_id) + def magnetic_sld_profile(self, model_id: str) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + """Return the nuclear and magnetic scattering length density profiles. + + Requires `include_magnetism` to be enabled and a calculator that supports it (refl1d). + + Parameters + ---------- + model_id : str + The model id. + + Returns + ------- + tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray] + z, sld(z), magnetic sld rhoM(z) and magnetic angle thetaM(z). + """ + return self._wrapper.magnetic_sld_profile(model_id) + def set_resolution_function(self, resolution_function: Callable[[np.array], np.array]) -> None: """Set resolution function.""" return self._wrapper.set_resolution_function(resolution_function) diff --git a/src/easyreflectometry/calculators/factory.py b/src/easyreflectometry/calculators/factory.py index 53fba178..c05618d1 100644 --- a/src/easyreflectometry/calculators/factory.py +++ b/src/easyreflectometry/calculators/factory.py @@ -26,6 +26,10 @@ def polarized_reflectivity_profiles(self, x_array, model_id: str) -> dict: """Reflectivity profiles of all four spin channels ('pp', 'pm', 'mp', 'mm').""" return self().polarized_reflectivity_profiles(x_array, model_id) + def magnetic_sld_profile(self, model_id: str) -> tuple: + """Nuclear and magnetic sld profiles: z, sld(z), rhoM(z) and thetaM(z).""" + return self().magnetic_sld_profile(model_id) + @property def fit_func(self) -> Callable: """Fit func.""" diff --git a/src/easyreflectometry/calculators/refl1d/wrapper.py b/src/easyreflectometry/calculators/refl1d/wrapper.py index 43829f78..0f9b341d 100644 --- a/src/easyreflectometry/calculators/refl1d/wrapper.py +++ b/src/easyreflectometry/calculators/refl1d/wrapper.py @@ -301,6 +301,35 @@ def sld_profile(self, model_name: str) -> Tuple[np.ndarray, np.ndarray]: # -1 to reverse the order return z, sld[::-1] + def magnetic_sld_profile(self, model_name: str) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + """Return the nuclear and magnetic scattering length density profiles. + + Parameters + ---------- + model_name : str + The model name. + + Returns + ------- + + z, sld(z), magnetic sld rhoM(z) and magnetic angle thetaM(z). + """ + if not self._magnetism: + raise ValueError( + 'The magnetic sld profile requires magnetism: enable it on this calculator first ' + '(`include_magnetism = True` on the calculator / `magnetism = True` on the wrapper).' + ) + sample = _build_sample(self.storage, model_name) + probe = _get_probe( + q_array=np.array([1]), # dummy value + dq_array=np.array([1]), # dummy value + model_name=model_name, + storage=self.storage, + ) + z, sld, _, sld_magnetic, theta_magnetic = names.Experiment(probe=probe, sample=sample).magnetic_smooth_profile() + # -1 to reverse the order + return z, sld[::-1], sld_magnetic[::-1], theta_magnetic[::-1] + def _get_oversampling_q(q_array: np.ndarray, dq_array: np.ndarray, oversampling_factor: int) -> np.ndarray: """Get oversampling q.""" diff --git a/src/easyreflectometry/calculators/wrapper_base.py b/src/easyreflectometry/calculators/wrapper_base.py index 23a0329d..35d19fb5 100644 --- a/src/easyreflectometry/calculators/wrapper_base.py +++ b/src/easyreflectometry/calculators/wrapper_base.py @@ -374,3 +374,17 @@ def calculate_polarized(self, q_array: np.ndarray, model_name: str) -> dict[str, Reflectivity per spin channel, keyed 'pp', 'pm', 'mp', 'mm' (in that order). """ raise NotImplementedError(f'{self.__class__.__name__} does not support polarized reflectivity.') + + def magnetic_sld_profile(self, model_name: str) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + """Return the nuclear and magnetic scattering length density profiles. + + Parameters + ---------- + model_name : str + The model name. + + Returns + ------- + z, sld(z), magnetic sld rhoM(z) and magnetic angle thetaM(z). + """ + raise NotImplementedError(f'{self.__class__.__name__} does not support magnetic sld profiles.') diff --git a/tests/calculators/test_polarization_interface.py b/tests/calculators/test_polarization_interface.py index 0bfd4ea2..55b72f8c 100644 --- a/tests/calculators/test_polarization_interface.py +++ b/tests/calculators/test_polarization_interface.py @@ -74,6 +74,46 @@ def test_switch_resets_polarization_state(): assert calculator.include_magnetism is False +def test_magnetic_sld_profile_through_factory(): + model = _magnetic_model() + interface = CalculatorFactory() + interface.switch('refl1d') + model.interface = interface + calculator = model.interface() + calculator.include_magnetism = True + layer_name = list(calculator._wrapper.storage['layer'].keys())[1] + calculator._wrapper.update_layer(layer_name, magnetism_rhoM=2, magnetism_thetaM=45) + + z, sld, sld_magnetic, theta_magnetic = interface.magnetic_sld_profile(model.unique_name) + + assert len(z) == len(sld) == len(sld_magnetic) == len(theta_magnetic) + # inside the 100 angstrom magnetic layer (zero roughness, so plateaus are exact) + inside = (z > 25) & (z < 75) + assert_allclose(sld[inside], 4.0) + assert_allclose(sld_magnetic[inside], 2.0) + assert_allclose(theta_magnetic[inside], 45.0) + + +def test_magnetic_sld_profile_requires_magnetism(): + model = _magnetic_model() + interface = CalculatorFactory() + interface.switch('refl1d') + model.interface = interface + + with pytest.raises(ValueError): + interface.magnetic_sld_profile(model.unique_name) + + +def test_refnx_magnetic_sld_profile_raises(): + model = _magnetic_model() + interface = CalculatorFactory() + interface.switch('refnx') + model.interface = interface + + with pytest.raises(NotImplementedError): + interface.magnetic_sld_profile(model.unique_name) + + def test_refnx_include_magnetism_raises(): interface = CalculatorFactory() interface.switch('refnx') From 6bd74831a7fd6d2b2089cb127b2c5b49e50da154 Mon Sep 17 00:00:00 2001 From: rozyczko Date: Thu, 13 Aug 2026 11:48:28 +0200 Subject: [PATCH 03/22] added magnetic parameters --- .github/workflows/pypi-test.yml | 8 - docs/docs/api-reference/calculators.md | 4 +- pixi.toml | 10 +- .../calculators/calculator_base.py | 5 + .../calculators/refl1d/calculator.py | 2 + .../calculators/refl1d/wrapper.py | 61 +++++- .../calculators/wrapper_base.py | 11 +- src/easyreflectometry/model/model.py | 5 + src/easyreflectometry/project.py | 9 + src/easyreflectometry/sample/__init__.py | 2 + .../sample/elements/layers/layer.py | 50 ++++- .../sample/elements/layers/layer_magnetism.py | 120 +++++++++++ .../calculators/refl1d/test_refl1d_wrapper.py | 15 +- .../elements/layers/test_layer_magnetism.py | 203 ++++++++++++++++++ 14 files changed, 469 insertions(+), 36 deletions(-) create mode 100644 src/easyreflectometry/sample/elements/layers/layer_magnetism.py create mode 100644 tests/sample/elements/layers/test_layer_magnetism.py diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml index 8bcb4b5e..123c24c6 100644 --- a/.github/workflows/pypi-test.yml +++ b/.github/workflows/pypi-test.yml @@ -45,18 +45,10 @@ jobs: - name: Init pixi project run: pixi init easyreflectometry - - name: Set the minimum system requirements - working-directory: easyreflectometry - run: pixi project system-requirements add macos 14.0 - - name: Add Python 3.13 from Conda working-directory: easyreflectometry run: pixi add "python=3.13" - - name: Add other Conda dependencies - working-directory: easyreflectometry - run: pixi add gsl - - name: Add easyreflectometry (with dev dependencies) from PyPI working-directory: easyreflectometry run: pixi add --pypi "easyreflectometry[dev]" diff --git a/docs/docs/api-reference/calculators.md b/docs/docs/api-reference/calculators.md index d90afbbc..7564c19a 100644 --- a/docs/docs/api-reference/calculators.md +++ b/docs/docs/api-reference/calculators.md @@ -14,8 +14,8 @@ spin channels are available: - `polarization_channel` selects which channel `reflectity_profile` — and hence fitting — uses (default `'pp'`). - `magnetic_sld_profile(model_id)` returns the nuclear and magnetic - scattering length density profiles as a tuple `z`, `sld(z)`, - `rhoM(z)` (magnetic SLD) and `thetaM(z)` (magnetic angle). + scattering length density profiles as a tuple `z`, `sld(z)`, `rhoM(z)` + (magnetic SLD) and `thetaM(z)` (magnetic angle). Note that `polarization_channel` is state of the currently active calculator instance, not of a model or dataset: it affects every diff --git a/pixi.toml b/pixi.toml index f0023fba..ee0c756f 100644 --- a/pixi.toml +++ b/pixi.toml @@ -5,7 +5,8 @@ [workspace] # Supported platforms for the lock file (pixi.lock) -platforms = ['win-64', 'linux-64', 'osx-arm64'] +platforms = ['win-64', 'linux-64', +{platform = 'osx-arm64', macos = '14.0'}] # Channels for fetching packages channels = ['nodefaults', 'conda-forge'] @@ -19,13 +20,6 @@ channels = ['nodefaults', 'conda-forge'] [activation.env] PYTHONIOENCODING = 'utf-8' -[system-requirements] -# Set minimum supported version for macOS to be 14.0 to ensure packages -# like `scipp` that only have wheels for macOS 14.0+ (macosx_14_0_arm64) -# are used instead of building from source. This is a workaround for -# Pixi, see https://github.com/prefix-dev/pixi/issues/5667 -macos = '14.0' - # Non-default features: # Set specific Python versions to be used in CI testing. diff --git a/src/easyreflectometry/calculators/calculator_base.py b/src/easyreflectometry/calculators/calculator_base.py index 3761bfed..cca5c631 100644 --- a/src/easyreflectometry/calculators/calculator_base.py +++ b/src/easyreflectometry/calculators/calculator_base.py @@ -256,6 +256,11 @@ def set_resolution_function(self, resolution_function: Callable[[np.array], np.a """Set resolution function.""" return self._wrapper.set_resolution_function(resolution_function) + @property + def supports_magnetism(self) -> bool: + """Whether this calculator backend can model magnetic samples.""" + return self._wrapper.supports_magnetism + @property def include_magnetism(self): """Include magnetism.""" diff --git a/src/easyreflectometry/calculators/refl1d/calculator.py b/src/easyreflectometry/calculators/refl1d/calculator.py index 2f5068de..ac7214e2 100644 --- a/src/easyreflectometry/calculators/refl1d/calculator.py +++ b/src/easyreflectometry/calculators/refl1d/calculator.py @@ -19,6 +19,8 @@ class Refl1d(CalculatorBase): _layer_link = { 'thickness': 'thickness', 'roughness': 'interface', + 'rho_m': 'magnetism_rhoM', + 'theta_m': 'magnetism_thetaM', } _item_link = { diff --git a/src/easyreflectometry/calculators/refl1d/wrapper.py b/src/easyreflectometry/calculators/refl1d/wrapper.py index 0f9b341d..79f94df2 100644 --- a/src/easyreflectometry/calculators/refl1d/wrapper.py +++ b/src/easyreflectometry/calculators/refl1d/wrapper.py @@ -14,10 +14,26 @@ RESOLUTION_PADDING = 3.5 OVERSAMPLING_FACTOR = 21 +# refl1d convention: with the default guide field (Aguide = 270 deg) a moment at +# thetaM = 270 deg is aligned with the field, i.e. produces no spin-flip. +DEFAULT_THETA_M = 270.0 + class Refl1dWrapper(WrapperBase): supports_magnetism = True + def __init__(self): + """Constructor.""" + super().__init__() + # Magnetic values per layer name, kept outside the slabs so they survive + # magnetism being toggled off/on and can be set before it is enabled. + self._layer_magnetism: dict[str, dict[str, float]] = {} + + def reset_storage(self): + """Reset the storage area (including stored magnetic values) to blank.""" + super().reset_storage() + self._layer_magnetism = {} + def create_material(self, name: str): """Create a material using SLD. @@ -37,7 +53,11 @@ def create_layer(self, name: str): The name of the layer. """ if self._magnetism: - magnetism = names.Magnetism(rhoM=0.0, thetaM=0.0) + values = self._layer_magnetism.get(name, {}) + magnetism = names.Magnetism( + rhoM=values.get('rhoM', 0.0), + thetaM=values.get('thetaM', DEFAULT_THETA_M), + ) else: magnetism = None self.storage['layer'][name] = names.Slab(name=str(name), magnetism=magnetism) @@ -56,17 +76,24 @@ def create_item(self, name: str): def update_layer(self, name: str, **kwargs): """Update a layer in a given item. + Magnetic keys (`magnetism_rhoM`, `magnetism_thetaM`) may be passed alone or + together; values are stored per layer and attached to the slab when + magnetism is enabled. + Parameters ---------- name : str The layer name. **kwargs : """ - kwargs_no_magnetism = {k: v for k, v in kwargs.items() if k != 'magnetism_rhoM' and k != 'magnetism_thetaM'} + magnetic_values = {k.removeprefix('magnetism_'): v for k, v in kwargs.items() if k.startswith('magnetism_')} + kwargs_no_magnetism = {k: v for k, v in kwargs.items() if not k.startswith('magnetism_')} 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 + if magnetic_values: + stored = self._layer_magnetism.setdefault(name, {'rhoM': 0.0, 'thetaM': DEFAULT_THETA_M}) + stored.update(magnetic_values) + if self._magnetism: + self._apply_magnetism_to_layer(name) def get_layer_value(self, name: str, key: str) -> float: """A function to get a given layer value. @@ -79,21 +106,35 @@ def get_layer_value(self, name: str, key: str) -> float: The given value keys. """ if key in ['magnetism_rhoM', 'magnetism_thetaM']: - return getattr( - self.storage['layer'][name].magnetism, key.split('_')[-1] - ).value # TODO: check if we want to return the raw value or the full Parameter # noqa: E501 + defaults = {'rhoM': 0.0, 'thetaM': DEFAULT_THETA_M} + magnetic_key = key.removeprefix('magnetism_') + return self._layer_magnetism.get(name, defaults).get(magnetic_key, defaults[magnetic_key]) return super().get_layer_value(name, key) def _remove_magnetism_from_layers(self) -> None: """Detach Magnetism objects from all slabs. Called when magnetism is disabled: slabs carrying Magnetism objects would - crash refl1d's plain (unpolarized) QProbe path. Magnetic parameters must be - set again (via `update_layer`) after re-enabling magnetism. + crash refl1d's plain (unpolarized) QProbe path. The magnetic values remain + stored and are re-attached when magnetism is re-enabled. """ for layer in self.storage['layer'].values(): layer.magnetism = None + def _apply_magnetism_to_layers(self) -> None: + """Attach stored magnetic values to all slabs (called when magnetism is enabled).""" + for name in self.storage['layer']: + self._apply_magnetism_to_layer(name) + + def _apply_magnetism_to_layer(self, name: str) -> None: + """Attach the stored magnetic values (or defaults) of one layer to its slab.""" + values = self._layer_magnetism.get(name, {}) + slab = self.storage['layer'][name] + slab.magnetism = names.Magnetism( + rhoM=values.get('rhoM', 0.0), + thetaM=values.get('thetaM', DEFAULT_THETA_M), + ) + def create_model(self, name: str): """Create a model for analysis. diff --git a/src/easyreflectometry/calculators/wrapper_base.py b/src/easyreflectometry/calculators/wrapper_base.py index 35d19fb5..8e010956 100644 --- a/src/easyreflectometry/calculators/wrapper_base.py +++ b/src/easyreflectometry/calculators/wrapper_base.py @@ -326,7 +326,10 @@ def magnetism(self, magnetism: bool) -> None: if magnetism and not self.supports_magnetism: raise NotImplementedError(f'Magnetism is not supported by {self.__class__.__name__}') self._magnetism = magnetism - if not magnetism: + if magnetism: + # Attach any magnetic values set (or restored) while magnetism was off. + self._apply_magnetism_to_layers() + else: # A non-pp channel is only meaningful on the polarized probe path. self._polarization_channel = PolarizationChannel.PP # Leave no magnetic residue behind: the unpolarized calculation path @@ -339,6 +342,12 @@ def _remove_magnetism_from_layers(self) -> None: No-op by default; overridden by backends that attach magnetic objects to layers. """ + def _apply_magnetism_to_layers(self) -> None: + """Attach stored magnetic values to existing layers when magnetism is enabled. + + No-op by default; overridden by backends that attach magnetic objects to layers. + """ + @property def polarization_channel(self) -> PolarizationChannel: """The spin channel returned by `calculate` when magnetism is enabled.""" diff --git a/src/easyreflectometry/model/model.py b/src/easyreflectometry/model/model.py index da1396d0..993dc3e8 100644 --- a/src/easyreflectometry/model/model.py +++ b/src/easyreflectometry/model/model.py @@ -196,6 +196,11 @@ def remove_assembly(self, index: int) -> None: if self.interface is not None: self.interface().remove_item_from_model(assembly_unique_name, self.unique_name) + @property + def has_magnetism(self) -> bool: + """Whether any layer in the sample carries magnetic properties.""" + return any(getattr(layer, 'magnetism', None) is not None for assembly in self.sample for layer in assembly.layers) + @property def is_default(self) -> bool: """Whether this model was created as a default placeholder.""" diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index aa0eacf6..b5068739 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -307,6 +307,15 @@ def calculator(self, calculator: str) -> None: for model in self._models: model.generate_bindings() + @property + def calculator_supports_magnetism(self) -> bool: + """Whether the active calculator can model magnetic samples. + + The GUI uses this to gate magnetism-related controls (e.g. when the + refnx or bornagain backend is selected). + """ + return self._calculator().supports_magnetism + self._fitter = None self._fitter_model_index = None diff --git a/src/easyreflectometry/sample/__init__.py b/src/easyreflectometry/sample/__init__.py index e6f347ad..e40b90ad 100644 --- a/src/easyreflectometry/sample/__init__.py +++ b/src/easyreflectometry/sample/__init__.py @@ -12,6 +12,7 @@ from .collections.sample import Sample from .elements.layers.layer import Layer from .elements.layers.layer_area_per_molecule import LayerAreaPerMolecule +from .elements.layers.layer_magnetism import LayerMagnetism from .elements.materials.material import Material from .elements.materials.material_density import MaterialDensity from .elements.materials.material_mixture import MaterialMixture @@ -24,6 +25,7 @@ 'Layer', 'LayerAreaPerMolecule', 'LayerCollection', + 'LayerMagnetism', 'Material', 'MaterialCollection', 'MaterialDensity', diff --git a/src/easyreflectometry/sample/elements/layers/layer.py b/src/easyreflectometry/sample/elements/layers/layer.py index 7eea9872..5265a766 100644 --- a/src/easyreflectometry/sample/elements/layers/layer.py +++ b/src/easyreflectometry/sample/elements/layers/layer.py @@ -13,6 +13,7 @@ from ...base_core import BaseCore from ..materials.material import Material +from .layer_magnetism import LayerMagnetism DEFAULTS = { 'thickness': { @@ -45,6 +46,7 @@ def __init__( name: str = 'EasyLayer', unique_name: Optional[str] = None, interface=None, + magnetism: Union[LayerMagnetism, None] = None, ): """Constructor. @@ -58,6 +60,9 @@ def __init__( Layer thickness in Angstrom. By default, None. roughness : Union[Parameter, float, None], optional Upper roughness on the layer in Angstrom. By default, None. + magnetism : Union[LayerMagnetism, None], optional + Magnetic properties of the layer; None for a non-magnetic layer. + By default, None. name : str, optional Name of the layer. By default, 'EasyLayer'. interface : @@ -91,10 +96,30 @@ def __init__( self._material = material self._thickness = thickness self._roughness = roughness + self._magnetism = magnetism if interface is not None: self.interface = interface + # ----- interface (override BaseCore's to switch on calculator magnetism) ----- + + @BaseCore.interface.setter + def interface(self, new_interface) -> None: + """Set the interface; runs `generate_bindings` and, for a magnetic layer, + enables magnetism on the calculator (raising if it does not support it). + """ + BaseCore.interface.fset(self, new_interface) + if new_interface is not None and self._magnetism is not None: + self._enable_calculator_magnetism() + + def _enable_calculator_magnetism(self) -> None: + """Turn on magnetism on the attached calculator. + + Raises `NotImplementedError` when the active calculator cannot model + magnetic samples (e.g. refnx or bornagain). + """ + self.interface().include_magnetism = True + @property def material(self) -> Material: return self._material @@ -119,6 +144,26 @@ def roughness(self) -> Parameter: def roughness(self, value: float) -> None: self._roughness.value = value + @property + def magnetism(self) -> Optional[LayerMagnetism]: + return self._magnetism + + @magnetism.setter + def magnetism(self, value: Optional[LayerMagnetism]) -> None: + """Attach or remove the magnetic properties of this layer. + + Attaching regenerates the calculator bindings so `rho_m`/`theta_m` become + live on the backend; removing zeroes the magnetic SLD on the backend so no + stale magnetism is left in subsequent calculations. + """ + if value is None and self._magnetism is not None and self.interface is not None: + # Zero the backend moment through the still-bound parameter before detaching. + self._magnetism.rho_m = 0.0 + self._magnetism = value + if value is not None and self.interface is not None: + self._enable_calculator_magnetism() + self.generate_bindings() + def assign_material(self, material: Material) -> None: """Assign a material to the layer interface. @@ -135,10 +180,13 @@ def assign_material(self, material: Material) -> None: @property def _dict_repr(self) -> dict[str, str]: """A simplified dict representation.""" - return { + this_dict = { self.name: { 'material': self.material._dict_repr, 'thickness': f'{self.thickness.value:.3f} {self.thickness.unit}', 'roughness': f'{self.roughness.value:.3f} {self.roughness.unit}', } } + if self._magnetism is not None: + this_dict[self.name]['magnetism'] = self._magnetism._dict_repr + return this_dict diff --git a/src/easyreflectometry/sample/elements/layers/layer_magnetism.py b/src/easyreflectometry/sample/elements/layers/layer_magnetism.py new file mode 100644 index 00000000..7b5c85bf --- /dev/null +++ b/src/easyreflectometry/sample/elements/layers/layer_magnetism.py @@ -0,0 +1,120 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + + +from typing import Optional +from typing import Union + +import numpy as np +from easyscience import global_object +from easyscience.variable import Parameter + +from easyreflectometry.utils import get_as_parameter + +from ...base_core import BaseCore + +DEFAULTS = { + 'rho_m': { + 'description': 'The magnetic scattering length density of the layer in e-6 per squared angstrom.', + 'url': 'https://refl1d.readthedocs.io/en/latest/guide/magnetism.html', + 'value': 0.0, + 'unit': '1 / angstrom^2', + 'min': -np.inf, + 'max': np.inf, + 'fixed': True, + }, + 'theta_m': { + 'description': 'The angle of the in-plane magnetic moment with respect to the beam direction in degrees. ' + 'The default of 270 degrees aligns the moment with the default guide field (Aguide), giving no spin-flip.', + 'url': 'https://refl1d.readthedocs.io/en/latest/guide/magnetism.html', + 'value': 270.0, + 'unit': 'degree', + 'min': 0.0, + 'max': 360.0, + 'fixed': True, + }, +} + + +class LayerMagnetism(BaseCore): + """Magnetic properties of a layer: magnetic SLD amplitude and in-plane moment angle. + + Attach to a `Layer` via its `magnetism` argument/property to make the layer magnetic. + Both attributes are `Parameter` objects, so they can be fitted and serialized like any + other sample parameter. A calculator that supports magnetism (refl1d) binds them as + `magnetism_rhoM` / `magnetism_thetaM` on the corresponding slab. + """ + + def __init__( + self, + rho_m: Union[Parameter, float, None] = None, + theta_m: Union[Parameter, float, None] = None, + name: str = 'EasyLayerMagnetism', + unique_name: Optional[str] = None, + interface=None, + ): + """Constructor. + + Parameters + ---------- + rho_m : Union[Parameter, float, None], optional + Magnetic scattering length density in e-6 per squared angstrom. By default, 0. + theta_m : Union[Parameter, float, None], optional + In-plane moment angle in degrees. By default, 270 (aligned with the default + guide field, i.e. no spin-flip). + name : str, optional + Name of the magnetism element. By default, 'EasyLayerMagnetism'. + unique_name : Optional[str], optional + By default, None. + interface : + Calculator interface. By default, None. + """ + if unique_name is None: + unique_name = global_object.generate_unique_name(self.__class__.__name__) + + rho_m = get_as_parameter( + name='rho_m', + value=rho_m, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_RhoM', + ) + theta_m = get_as_parameter( + name='theta_m', + value=theta_m, + default_dict=DEFAULTS, + unique_name_prefix=f'{unique_name}_ThetaM', + ) + + super().__init__(name=name, unique_name=unique_name) + self._rho_m = rho_m + self._theta_m = theta_m + + if interface is not None: + self.interface = interface + + @property + def rho_m(self) -> Parameter: + return self._rho_m + + @rho_m.setter + def rho_m(self, value: float) -> None: + self._rho_m.value = value + + @property + def theta_m(self) -> Parameter: + return self._theta_m + + @theta_m.setter + def theta_m(self, value: float) -> None: + self._theta_m.value = value + + # Representation + @property + def _dict_repr(self) -> dict[str, str]: + """A simplified dict representation.""" + return { + self.name: { + 'rho_m': f'{self._rho_m.value:.3f}e-6 {self._rho_m.unit}', + 'theta_m': f'{self._theta_m.value:.3f} {self._theta_m.unit}', + } + } diff --git a/tests/calculators/refl1d/test_refl1d_wrapper.py b/tests/calculators/refl1d/test_refl1d_wrapper.py index be971293..b238b541 100644 --- a/tests/calculators/refl1d/test_refl1d_wrapper.py +++ b/tests/calculators/refl1d/test_refl1d_wrapper.py @@ -411,9 +411,9 @@ def test_get_polarized_probe_oversampling(): def _sample_wrapper(rho: float, magnetic: bool, rhoM: float = 0.0, thetaM: float = 270.0) -> Refl1dWrapper: """Vacuum | 100 A layer of `rho` (optionally magnetic) | Si substrate. - Magnetism must be enabled before `create_layer` — only then does the wrapper - attach a `Magnetism` object to the slab. `update_layer` requires BOTH magnetism - kwargs; partial updates raise KeyError and are not supported. + Magnetic values may be set via `update_layer` at any time (also one key at a + time); they are stored per layer and attached to the slabs whenever magnetism + is enabled. """ p = Refl1dWrapper() if magnetic: @@ -579,12 +579,15 @@ def test_disabling_magnetism_resets_channel(): assert all(layer.magnetism is None for layer in p.storage['layer'].values()) assert_allclose(p.calculate(Q_POLARIZED, 'MyModel'), unpolarized.calculate(Q_POLARIZED, 'MyModel'), rtol=1e-10) - # Re-enabling gives a clean magnetic state (no stale rhoM/thetaM); magnetic - # parameters must be set again via update_layer. + # Re-enabling restores the stored magnetic values (rhoM/thetaM survive the + # toggle so the wrapper stays in sync with model parameters that still hold them). p.magnetism = True assert p.polarization_channel is PolarizationChannel.PP + restored = _sample_wrapper(rho=4.0, magnetic=True, rhoM=2.0, thetaM=45) channels = p.calculate_polarized(Q_POLARIZED, 'MyModel') - assert_allclose(channels['pp'], unpolarized.calculate(Q_POLARIZED, 'MyModel'), rtol=1e-5) + reference = restored.calculate_polarized(Q_POLARIZED, 'MyModel') + for channel in ('pp', 'pm', 'mp', 'mm'): + assert_allclose(channels[channel], reference[channel], rtol=1e-10) def test_polarized_reflectivities_guards_malformed_output(): diff --git a/tests/sample/elements/layers/test_layer_magnetism.py b/tests/sample/elements/layers/test_layer_magnetism.py new file mode 100644 index 00000000..5cdf8515 --- /dev/null +++ b/tests/sample/elements/layers/test_layer_magnetism.py @@ -0,0 +1,203 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + +""" +Tests for LayerMagnetism and magnetic-parameter plumbing through the calculators. +""" + +import numpy as np +import pytest +from numpy.testing import assert_allclose +from numpy.testing import assert_equal + +from easyreflectometry.calculators.factory import CalculatorFactory +from easyreflectometry.model import Model +from easyreflectometry.model import PercentageFwhm +from easyreflectometry.sample import Layer +from easyreflectometry.sample import LayerMagnetism +from easyreflectometry.sample import Material +from easyreflectometry.sample import Multilayer +from easyreflectometry.sample import Sample + +Q = np.linspace(0.005, 0.3, 50) + + +def _magnetic_model(magnetism: LayerMagnetism | None) -> Model: + vacuum = Material(sld=0, isld=0, name='Vacuum') + material = Material(sld=4.0, isld=0, name='Sld 4') + si = Material(sld=2.047, isld=0, name='Si') + superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase') + layer = Layer(material=material, thickness=100, roughness=0, magnetism=magnetism, name='Sld 4 Layer') + subphase = Layer(material=si, thickness=0, roughness=0, name='Si Subphase') + sample = Sample(Multilayer(superphase), Multilayer(layer), Multilayer(subphase), name='Sample') + model = Model(sample=sample, scale=1, background=0, name='Magnetic Model') + model.resolution_function = PercentageFwhm(0) + return model + + +class TestLayerMagnetism: + def test_default_construction(self): + magnetism = LayerMagnetism() + assert_equal(magnetism.name, 'EasyLayerMagnetism') + assert_equal(magnetism.rho_m.value, 0.0) + assert_equal(magnetism.rho_m.fixed, True) + assert_equal(magnetism.theta_m.value, 270.0) + assert_equal(magnetism.theta_m.min, 0.0) + assert_equal(magnetism.theta_m.max, 360.0) + assert_equal(magnetism.theta_m.fixed, True) + + def test_construction_with_values(self): + magnetism = LayerMagnetism(rho_m=2.5, theta_m=45.0, name='FeMoment') + assert_equal(magnetism.name, 'FeMoment') + assert_equal(magnetism.rho_m.value, 2.5) + assert_equal(magnetism.theta_m.value, 45.0) + + def test_dict_round_trip(self): + magnetism = LayerMagnetism(rho_m=1.5, theta_m=90.0) + magnetism_dict = magnetism.as_dict() + reloaded = LayerMagnetism.from_dict(magnetism_dict) + assert_equal(reloaded.rho_m.value, 1.5) + assert_equal(reloaded.theta_m.value, 90.0) + assert sorted(magnetism.as_dict()) == sorted(reloaded.as_dict()) + + +class TestLayerWithMagnetism: + def test_layer_default_is_non_magnetic(self): + layer = Layer() + assert layer.magnetism is None + + def test_layer_dict_round_trip_with_magnetism(self): + layer = Layer(magnetism=LayerMagnetism(rho_m=2.0, theta_m=45.0), name='MagneticLayer') + layer_dict = layer.as_dict() + reloaded = Layer.from_dict(layer_dict) + assert reloaded.magnetism is not None + assert_equal(reloaded.magnetism.rho_m.value, 2.0) + assert_equal(reloaded.magnetism.theta_m.value, 45.0) + assert sorted(layer.as_dict()) == sorted(reloaded.as_dict()) + + def test_magnetic_parameters_are_fittable_variables(self): + magnetism = LayerMagnetism(rho_m=2.0, theta_m=45.0) + layer = Layer(magnetism=magnetism) + variable_names = [variable.name for variable in layer.get_all_variables()] + assert 'rho_m' in variable_names + assert 'theta_m' in variable_names + + +class TestMagnetismThroughCalculator: + def _interface(self, name: str) -> CalculatorFactory: + interface = CalculatorFactory() + interface.switch(name) + return interface + + def test_model_interface_enables_magnetism_and_binds_parameters(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + interface = self._interface('refl1d') + model.interface = interface + calculator = interface() + + assert model.has_magnetism is True + assert calculator.include_magnetism is True + layer = model.sample[1].layers[0] + wrapper = calculator._wrapper + assert wrapper.get_layer_value(layer.unique_name, 'magnetism_rhoM') == 2.0 + assert wrapper.get_layer_value(layer.unique_name, 'magnetism_thetaM') == 45.0 + + def test_magnetic_parameter_change_changes_reflectivity(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = self._interface('refl1d') + + before = model.interface.polarized_reflectivity_profiles(Q, model.unique_name) + model.sample[1].layers[0].magnetism.rho_m = 4.0 + after = model.interface.polarized_reflectivity_profiles(Q, model.unique_name) + + assert not np.allclose(before['pp'], after['pp']) + assert not np.allclose(before['mm'], after['mm']) + + def test_model_parameters_match_direct_wrapper_values(self): + # Setting rho_m/theta_m through the model must reproduce the reflectivity + # obtained by setting magnetism_rhoM/thetaM directly on the wrapper. + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = self._interface('refl1d') + via_model = model.interface.polarized_reflectivity_profiles(Q, model.unique_name) + + reference_model = _magnetic_model(None) + interface = self._interface('refl1d') + reference_model.interface = interface + calculator = interface() + calculator.include_magnetism = True + layer_name = reference_model.sample[1].layers[0].unique_name + calculator._wrapper.update_layer(layer_name, magnetism_rhoM=2.0, magnetism_thetaM=45.0) + via_wrapper = interface.polarized_reflectivity_profiles(Q, reference_model.unique_name) + + for channel in ('pp', 'pm', 'mp', 'mm'): + assert_allclose(via_model[channel], via_wrapper[channel], rtol=1e-10) + + def test_magnetism_added_after_interface_is_bound(self): + model = _magnetic_model(None) + model.interface = self._interface('refl1d') + calculator = model.interface() + assert calculator.include_magnetism is False + + layer = model.sample[1].layers[0] + layer.magnetism = LayerMagnetism(rho_m=2.0, theta_m=45.0) + + assert calculator.include_magnetism is True + wrapper = calculator._wrapper + assert wrapper.get_layer_value(layer.unique_name, 'magnetism_rhoM') == 2.0 + # The parameter is live: a change propagates to the backend. + layer.magnetism.rho_m = 3.0 + assert wrapper.get_layer_value(layer.unique_name, 'magnetism_rhoM') == 3.0 + + def test_removing_magnetism_zeroes_backend(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = self._interface('refl1d') + layer = model.sample[1].layers[0] + + layer.magnetism = None + + assert layer.magnetism is None + assert model.has_magnetism is False + wrapper = model.interface()._wrapper + assert wrapper.get_layer_value(layer.unique_name, 'magnetism_rhoM') == 0.0 + + def test_magnetic_layer_with_refnx_raises(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + with pytest.raises(NotImplementedError): + model.interface = self._interface('refnx') + + def test_supports_magnetism_capability_flag(self): + assert self._interface('refl1d')().supports_magnetism is True + assert self._interface('refnx')().supports_magnetism is False + + def test_project_exposes_calculator_capability(self): + from easyreflectometry.project import Project + + project = Project() + project.calculator = 'refl1d' + assert project.calculator_supports_magnetism is True + project.calculator = 'refnx' + assert project.calculator_supports_magnetism is False + + def test_fit_recovers_rho_m_from_synthetic_data(self): + from easyreflectometry.data import DataSet1D + from easyreflectometry.fitting import MultiFitter + + # Synthesize noiseless pp data from a model with a known moment. + truth = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=270.0)) + truth.interface = self._interface('refl1d') + reflectivity = truth.interface.polarized_reflectivity_profiles(Q, truth.unique_name)['pp'] + # `ye` holds variances. + data = DataSet1D(name='synthetic_pp', x=Q, y=reflectivity, ye=(0.01 * reflectivity) ** 2) + + # Fit a model starting from the wrong moment; only rho_m is free. + model = _magnetic_model(LayerMagnetism(rho_m=1.0, theta_m=270.0)) + model.interface = self._interface('refl1d') + rho_m = model.sample[1].layers[0].magnetism.rho_m + rho_m.fixed = False + rho_m.bounds = (0.0, 5.0) + + fitter = MultiFitter(model) + result = fitter.fit_single_data_set_1d(data) + + assert result.success + assert_allclose(rho_m.value, 2.5, atol=0.01) From 2bd39bbcd2d72e077fb82df14e551efedaf6d992 Mon Sep 17 00:00:00 2001 From: rozyczko Date: Thu, 13 Aug 2026 12:00:14 +0200 Subject: [PATCH 04/22] ruff --- pixi.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pixi.toml b/pixi.toml index ee0c756f..70a2a773 100644 --- a/pixi.toml +++ b/pixi.toml @@ -5,8 +5,7 @@ [workspace] # Supported platforms for the lock file (pixi.lock) -platforms = ['win-64', 'linux-64', -{platform = 'osx-arm64', macos = '14.0'}] +platforms = ['win-64', 'linux-64', { platform = 'osx-arm64', macos = '14.0' }] # Channels for fetching packages channels = ['nodefaults', 'conda-forge'] From 0fb3e7af0be28228be97ec82b1c1864dc81282e9 Mon Sep 17 00:00:00 2001 From: rozyczko Date: Thu, 13 Aug 2026 14:59:13 +0200 Subject: [PATCH 05/22] code review comments addressed --- CHANGELOG.md | 19 ++++++++++-- .../calculators/calculator_base.py | 11 +++++++ .../calculators/refl1d/wrapper.py | 20 +++++++++++++ .../calculators/wrapper_base.py | 11 +++++++ src/easyreflectometry/project.py | 7 +++-- .../sample/elements/layers/layer.py | 15 ++++++---- .../elements/layers/test_layer_magnetism.py | 29 +++++++++++++++++-- 7 files changed, 98 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44fe8d62..cc275b86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All four polarization channels (pp, pm, mp, mm) are now available from the refl1d calculator; previously only the non-spin-flip pp channel was returned. +- New `LayerMagnetism` sample element makes magnetism part of the model: + `Layer` accepts an optional `magnetism` (with `rho_m`, the magnetic + SLD, and `theta_m`, the in-plane moment angle, as fittable, serialized + `Parameter`s). Attaching a magnetic layer automatically enables + `include_magnetism` on the calculator (raising `NotImplementedError` + on backends without magnetism support); removing the last magnetic + layer disables it again. `Model.has_magnetism`, + `CalculatorBase.supports_magnetism` and + `Project.calculator_supports_magnetism` expose the state to + applications. + - New `polarized_reflectivity_profiles(x_array, model_id)` on the calculator (and on `CalculatorFactory`) returns the reflectivity of all four spin channels in one calculation as a dictionary keyed @@ -34,9 +45,11 @@ returned. with it enabled used to leave refl1d `Magnetism` objects on the slabs, making a subsequent unpolarized calculation raise `AttributeError` inside refl1d. Disabling magnetism now strips the magnetic state from - existing layers, so the unpolarized path works again. Consequently, - magnetic parameters (`rhoM`/`thetaM`) do not survive a - disable/re-enable cycle and must be set again. + existing layers, so the unpolarized path works again. Magnetic + parameters (`rhoM`/`thetaM`) are kept in a per-layer store inside the + wrapper, so they survive a disable/re-enable cycle and are re-attached + when magnetism is enabled again; `update_layer` also accepts the + magnetism keys one at a time. # Version 1.7.0 (1 Aug 2026) diff --git a/src/easyreflectometry/calculators/calculator_base.py b/src/easyreflectometry/calculators/calculator_base.py index cca5c631..18fbd1a6 100644 --- a/src/easyreflectometry/calculators/calculator_base.py +++ b/src/easyreflectometry/calculators/calculator_base.py @@ -261,6 +261,17 @@ def supports_magnetism(self) -> bool: """Whether this calculator backend can model magnetic samples.""" return self._wrapper.supports_magnetism + def remove_layer_magnetism(self, layer_id: str) -> None: + """Remove the magnetic state of one layer; disables `include_magnetism` + when no magnetic layer is left. + + Parameters + ---------- + layer_id : str + The layer id. + """ + self._wrapper.remove_layer_magnetism(layer_id) + @property def include_magnetism(self): """Include magnetism.""" diff --git a/src/easyreflectometry/calculators/refl1d/wrapper.py b/src/easyreflectometry/calculators/refl1d/wrapper.py index 79f94df2..883a56e4 100644 --- a/src/easyreflectometry/calculators/refl1d/wrapper.py +++ b/src/easyreflectometry/calculators/refl1d/wrapper.py @@ -135,6 +135,26 @@ def _apply_magnetism_to_layer(self, name: str) -> None: thetaM=values.get('thetaM', DEFAULT_THETA_M), ) + def remove_layer_magnetism(self, name: str) -> None: + """Remove the magnetic state of one layer; disable magnetism when none is left. + + Keeps `magnetism` (the calculator flag) in sync with the model: once no + layer holds magnetic values any more, the polarized calculation path is + switched off entirely. + + Parameters + ---------- + name : str + The layer name. + """ + self._layer_magnetism.pop(name, None) + slab = self.storage['layer'].get(name) + if slab is not None: + # A non-magnetic slab is fine inside a polarized calculation. + slab.magnetism = None + if self._magnetism and not self._layer_magnetism: + self.magnetism = False + def create_model(self, name: str): """Create a model for analysis. diff --git a/src/easyreflectometry/calculators/wrapper_base.py b/src/easyreflectometry/calculators/wrapper_base.py index 8e010956..4e2a4aab 100644 --- a/src/easyreflectometry/calculators/wrapper_base.py +++ b/src/easyreflectometry/calculators/wrapper_base.py @@ -348,6 +348,17 @@ def _apply_magnetism_to_layers(self) -> None: No-op by default; overridden by backends that attach magnetic objects to layers. """ + def remove_layer_magnetism(self, name: str) -> None: + """Remove the magnetic state of one layer; disable magnetism when none is left. + + No-op by default; overridden by backends that support magnetism. + + Parameters + ---------- + name : str + The layer name. + """ + @property def polarization_channel(self) -> PolarizationChannel: """The spin channel returned by `calculate` when magnetism is enabled.""" diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index b5068739..456e9803 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -307,6 +307,10 @@ def calculator(self, calculator: str) -> None: for model in self._models: model.generate_bindings() + # The cached fitter holds fit functions bound to the previous backend. + self._fitter = None + self._fitter_model_index = None + @property def calculator_supports_magnetism(self) -> bool: """Whether the active calculator can model magnetic samples. @@ -316,9 +320,6 @@ def calculator_supports_magnetism(self) -> bool: """ return self._calculator().supports_magnetism - self._fitter = None - self._fitter_model_index = None - @property def minimizer(self) -> AvailableMinimizers: """Minimizer function.""" diff --git a/src/easyreflectometry/sample/elements/layers/layer.py b/src/easyreflectometry/sample/elements/layers/layer.py index 5265a766..fe580cb4 100644 --- a/src/easyreflectometry/sample/elements/layers/layer.py +++ b/src/easyreflectometry/sample/elements/layers/layer.py @@ -153,12 +153,17 @@ def magnetism(self, value: Optional[LayerMagnetism]) -> None: """Attach or remove the magnetic properties of this layer. Attaching regenerates the calculator bindings so `rho_m`/`theta_m` become - live on the backend; removing zeroes the magnetic SLD on the backend so no - stale magnetism is left in subsequent calculations. + live on the backend; removing drops the layer's magnetic state from the + backend (and switches calculator magnetism off entirely when this was the + last magnetic layer). """ - if value is None and self._magnetism is not None and self.interface is not None: - # Zero the backend moment through the still-bound parameter before detaching. - self._magnetism.rho_m = 0.0 + if value is None and self._magnetism is not None: + if self.interface is not None: + self.interface().remove_layer_magnetism(self.unique_name) + # Detach the calculator callbacks of the removed parameters so later + # value changes on the detached object no longer reach the backend. + self._magnetism.rho_m._callback = property() + self._magnetism.theta_m._callback = property() self._magnetism = value if value is not None and self.interface is not None: self._enable_calculator_magnetism() diff --git a/tests/sample/elements/layers/test_layer_magnetism.py b/tests/sample/elements/layers/test_layer_magnetism.py index 5cdf8515..a2932dec 100644 --- a/tests/sample/elements/layers/test_layer_magnetism.py +++ b/tests/sample/elements/layers/test_layer_magnetism.py @@ -148,17 +148,40 @@ def test_magnetism_added_after_interface_is_bound(self): layer.magnetism.rho_m = 3.0 assert wrapper.get_layer_value(layer.unique_name, 'magnetism_rhoM') == 3.0 - def test_removing_magnetism_zeroes_backend(self): + def test_removing_last_magnetism_disables_calculator_magnetism(self): model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) model.interface = self._interface('refl1d') + calculator = model.interface() layer = model.sample[1].layers[0] + detached = layer.magnetism layer.magnetism = None + # Model and calculator agree again: no magnetism anywhere. assert layer.magnetism is None assert model.has_magnetism is False - wrapper = model.interface()._wrapper - assert wrapper.get_layer_value(layer.unique_name, 'magnetism_rhoM') == 0.0 + assert calculator.include_magnetism is False + assert calculator._wrapper.get_layer_value(layer.unique_name, 'magnetism_rhoM') == 0.0 + # The plain (unpolarized) calculation path works. + reflectivity = calculator.reflectity_profile(Q, model.unique_name) + assert len(reflectivity) == len(Q) + # The detached parameters no longer reach the backend. + detached.rho_m = 5.0 + assert calculator._wrapper.get_layer_value(layer.unique_name, 'magnetism_rhoM') == 0.0 + + def test_removing_one_of_two_magnetic_layers_keeps_magnetism(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = self._interface('refl1d') + calculator = model.interface() + subphase = model.sample[2].layers[0] + subphase.magnetism = LayerMagnetism(rho_m=1.0, theta_m=270.0) + + model.sample[1].layers[0].magnetism = None + + # One magnetic layer remains: the polarized path stays on, its values intact. + assert model.has_magnetism is True + assert calculator.include_magnetism is True + assert calculator._wrapper.get_layer_value(subphase.unique_name, 'magnetism_rhoM') == 1.0 def test_magnetic_layer_with_refnx_raises(self): model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) From c9e2ca110cfd00161244f557f3dcf6fa97b4c2bb Mon Sep 17 00:00:00 2001 From: rozyczko Date: Thu, 13 Aug 2026 19:36:04 +0200 Subject: [PATCH 06/22] code review fixes for Phase 2, added notebook --- CHANGELOG.md | 25 + notebooks/polarized_demo_data/fe_on_si_dd.dat | 161 ++++++ notebooks/polarized_demo_data/fe_on_si_du.dat | 161 ++++++ notebooks/polarized_demo_data/fe_on_si_ud.dat | 161 ++++++ notebooks/polarized_demo_data/fe_on_si_uu.dat | 161 ++++++ notebooks/polarized_fitting.ipynb | 505 ++++++++++++++++++ .../calculators/calculator_base.py | 23 + src/easyreflectometry/calculators/factory.py | 31 ++ .../calculators/polarization.py | 20 +- .../calculators/refl1d/wrapper.py | 80 ++- .../calculators/wrapper_base.py | 29 + src/easyreflectometry/data/__init__.py | 4 + src/easyreflectometry/data/polarized.py | 284 ++++++++++ src/easyreflectometry/fitting.py | 118 ++++ src/easyreflectometry/project.py | 86 ++- .../calculators/refl1d/test_refl1d_wrapper.py | 53 +- tests/data/test_polarized.py | 191 +++++++ tests/test_polarized_fitting.py | 314 +++++++++++ 18 files changed, 2372 insertions(+), 35 deletions(-) create mode 100644 notebooks/polarized_demo_data/fe_on_si_dd.dat create mode 100644 notebooks/polarized_demo_data/fe_on_si_du.dat create mode 100644 notebooks/polarized_demo_data/fe_on_si_ud.dat create mode 100644 notebooks/polarized_demo_data/fe_on_si_uu.dat create mode 100644 notebooks/polarized_fitting.ipynb create mode 100644 src/easyreflectometry/data/polarized.py create mode 100644 tests/data/test_polarized.py create mode 100644 tests/test_polarized_fitting.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cc275b86..1af5c483 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,31 @@ returned. `CalculatorBase.supports_magnetism` and `Project.calculator_supports_magnetism` expose the state to applications. +- New `PolarizedDataSet` groups per-spin-channel `DataSet1D` objects + (one file per channel; 'pp'/'mm' only for NSF experiments, spin-flip + channels optional) into one experiment sharing a single model. + `Project.load_polarized_experiment(paths)` loads it from an explicit + channel → file mapping, and + `Project.suggest_polarized_channel_assignment(paths)` pre-fills that + mapping from the ORSO header polarization (`pp`/`mm`/`pm`/`mp` only — + partially-analysed observables such as `po`/`mo`, which measure channel + sums, and `op`/`om`/`unpolarized` are left for the user to decide) or, + for plain text files, from filename tokens (`_uu`/`_up`/`_pp` → pp, + `_dd`/`_down`/`_mm` → mm, `_ud`/`_pm` → pm, `_du`/`_mp` → mp). +- New `calculate_channel(q, model, channel)` on the wrapper (and + `reflectivity_profile_channel` on the calculator, + `fit_func_for_channel` on `CalculatorFactory`) evaluates one explicit + spin channel without touching the global `polarization_channel` + state. +- New `MultiFitter.fit_polarized(data)` fits all measured channels of a + `PolarizedDataSet` simultaneously against the shared model: one fit + function per channel, common structural parameters, magnetic + parameters constrained by all channels at once. Returns per-channel + `FitResults`. +- The refl1d wrapper now caches the four polarized cross-sections per + model state and (q, dq) grid — they come from a single kernel + evaluation, so a simultaneous N-channel fit costs about one + evaluation per iteration instead of N. - New `polarized_reflectivity_profiles(x_array, model_id)` on the calculator (and on `CalculatorFactory`) returns the reflectivity of diff --git a/notebooks/polarized_demo_data/fe_on_si_dd.dat b/notebooks/polarized_demo_data/fe_on_si_dd.dat new file mode 100644 index 00000000..94cb89ab --- /dev/null +++ b/notebooks/polarized_demo_data/fe_on_si_dd.dat @@ -0,0 +1,161 @@ +# Qz (1/angstrom) R sR +8.000000000000000167e-03 9.075912642881344139e-01 2.704673059762681755e-02 +9.333333333333334106e-03 8.757161125297344695e-01 2.518846900075938366e-02 +1.066666666666666631e-02 6.977199840304085798e-01 2.087428696703839612e-02 +1.200000000000000025e-02 6.872509831174377082e-01 2.026445554463356807e-02 +1.333333333333333245e-02 6.631355988679124014e-01 1.992801525696327389e-02 +1.466666666666666639e-02 6.501014136246322517e-01 1.960325872581101386e-02 +1.600000000000000033e-02 6.261877501539732105e-01 1.923544318530388472e-02 +1.733333333333333254e-02 6.343907439684939309e-01 1.878917213001779843e-02 +1.866666666666666474e-02 5.922151008251805848e-01 1.823221530598403625e-02 +2.000000000000000042e-02 5.960631236889448736e-01 1.753182597463828069e-02 +2.133333333333333262e-02 5.732409575569683557e-01 1.665496617619377159e-02 +2.266666666666666829e-02 5.247578820550162781e-01 1.557151300442079396e-02 +2.400000000000000050e-02 4.712753742667411538e-01 1.426072465920071378e-02 +2.533333333333333270e-02 4.298037665936796370e-01 1.272086791267182382e-02 +2.666666666666666491e-02 3.626175654261608594e-01 1.098020577277717021e-02 +2.800000000000000058e-02 3.120130601042385710e-01 9.104851279847682097e-03 +2.933333333333333279e-02 2.267220914996334880e-01 7.197086378452600963e-03 +3.066666666666666499e-02 1.775178148014185664e-01 5.379698437054512838e-03 +3.200000000000000067e-02 1.181310173097377125e-01 3.769033640753254900e-03 +3.333333333333333287e-02 7.792836377261480596e-02 2.447631659821120943e-03 +3.466666666666666508e-02 5.027360606248993530e-02 1.448924192475278426e-03 +3.600000000000000422e-02 2.598520932519453666e-02 7.591683953662536856e-04 +3.733333333333332948e-02 1.082782187122047213e-02 3.320006942449972034e-04 +3.866666666666666863e-02 3.390612178886910372e-03 1.065197543374674642e-04 +4.000000000000000083e-02 6.634734704890265719e-04 2.184720411327092837e-05 +4.133333333333333304e-02 8.402822722116700099e-04 2.562630155983095458e-05 +4.266666666666666524e-02 2.761189837369125693e-03 7.722798210359995330e-05 +4.399999999999999745e-02 4.984147983628604318e-03 1.475987877702071994e-04 +4.533333333333332965e-02 7.130190399400800419e-03 2.175578930081850085e-04 +4.666666666666666879e-02 9.315547059324427726e-03 2.756208378691675140e-04 +4.800000000000000100e-02 1.003920070040243209e-02 3.159726242251264801e-04 +4.933333333333333320e-02 1.112594162420929046e-02 3.367822456192096385e-04 +5.066666666666666541e-02 1.132996142293247153e-02 3.388874925461203276e-04 +5.199999999999999761e-02 1.079869901323585180e-02 3.247999349804731625e-04 +5.333333333333332982e-02 1.016770659568656983e-02 2.979622858794108331e-04 +5.466666666666666202e-02 8.830283975467438998e-03 2.621975650898551008e-04 +5.600000000000000117e-02 7.524710540696799392e-03 2.213042152714181170e-04 +5.733333333333333337e-02 5.835794375082871931e-03 1.787655487150832450e-04 +5.866666666666666557e-02 4.708583433713307148e-03 1.375525977593421471e-04 +5.999999999999999778e-02 3.496438325611750386e-03 1.000060435868242991e-04 +6.133333333333332998e-02 2.193770931717122993e-03 6.778600474993069151e-05 +6.266666666666666219e-02 1.358795672471345200e-03 4.187914831486427934e-05 +6.400000000000000133e-02 7.853249876843387258e-04 2.265200387178431808e-05 +6.533333333333332660e-02 3.293824670891950385e-04 9.938524318625599184e-06 +6.666666666666666574e-02 1.094068554305717308e-04 3.149562947669204988e-06 +6.800000000000000488e-02 4.579681042216983595e-05 1.392389778723863959e-06 +6.933333333333333015e-02 1.248796610637395508e-04 3.589694748014370890e-06 +7.066666666666665542e-02 2.874470694658368109e-04 8.589530042765663588e-06 +7.200000000000000844e-02 5.126077964958570014e-04 1.526001661087636688e-05 +7.333333333333333370e-02 7.874764261774640402e-04 2.256505436841721351e-05 +7.466666666666665897e-02 9.766001106681053163e-04 2.961946677919368234e-05 +7.599999999999998423e-02 1.157173229345322303e-03 3.572381091442601147e-05 +7.733333333333333726e-02 1.327900995494995596e-03 4.038042628291313278e-05 +7.866666666666666252e-02 1.462689364936814581e-03 4.329319153718547912e-05 +7.999999999999998779e-02 1.409018005772228778e-03 4.435396905627936038e-05 +8.133333333333334081e-02 1.481769978748375717e-03 4.361892548833955793e-05 +8.266666666666666607e-02 1.353690733781721641e-03 4.127790251118508715e-05 +8.399999999999999134e-02 1.297175308564354644e-03 3.761984328443019373e-05 +8.533333333333334436e-02 1.020887830362373315e-03 3.299671678670470522e-05 +8.666666666666666963e-02 9.044813985473879655e-04 2.779020672111869435e-05 +8.799999999999999489e-02 7.081969582030287706e-04 2.237810772498295098e-05 +8.933333333333332016e-02 5.561639284949144524e-04 1.710899656797894980e-05 +9.066666666666667318e-02 4.124016110169756878e-04 1.228080225182807722e-05 +9.199999999999999845e-02 2.694131913742567504e-04 8.126088081576757726e-06 +9.333333333333332371e-02 1.589095847446595053e-04 4.803802818737303555e-06 +9.466666666666667673e-02 7.952535288150432656e-05 2.397208566565326355e-06 +9.600000000000000200e-02 3.076423342320826502e-05 9.173297799956004235e-07 +9.733333333333332726e-02 1.005251023256088463e-05 3.109844786323962683e-07 +9.866666666666665253e-02 1.607610631621909667e-05 4.722685047958857686e-07 +1.000000000000000056e-01 4.270820470822345427e-05 1.256271490261844000e-06 +1.013333333333333308e-01 8.408706070466543899e-05 2.493805529005849394e-06 +1.026666666666666561e-01 1.357632565588043198e-04 4.006013285794612694e-06 +1.040000000000000091e-01 1.889277283032915380e-04 5.617874673711491538e-06 +1.053333333333333344e-01 2.535854776939441531e-04 7.169830416826538728e-06 +1.066666666666666596e-01 2.834895567475302354e-04 8.526966173941271989e-06 +1.079999999999999849e-01 3.165709134255915796e-04 9.585433172802807031e-06 +1.093333333333333379e-01 3.347902142037292557e-04 1.027600399898480948e-05 +1.106666666666666632e-01 3.412563648594390620e-04 1.056486233606250258e-05 +1.119999999999999885e-01 3.353893732424267221e-04 1.045189380634267694e-05 +1.133333333333333415e-01 3.233706619271815397e-04 9.966875831629191658e-06 +1.146666666666666667e-01 3.048207765620222862e-04 9.164054863718112327e-06 +1.159999999999999920e-01 2.732346614974670118e-04 8.115649185306674919e-06 +1.173333333333333173e-01 2.305140128431734333e-04 6.904826573947082877e-06 +1.186666666666666703e-01 1.829880995447076856e-04 5.618681964024802284e-06 +1.199999999999999956e-01 1.486311764967542036e-04 4.341685740344124731e-06 +1.213333333333333208e-01 1.073289623471351128e-04 3.149994494995711424e-06 +1.226666666666666738e-01 6.989429649780126480e-05 2.106919880718955435e-06 +1.239999999999999991e-01 4.116899314943323328e-05 1.259745030541038548e-06 +1.253333333333333244e-01 2.161552831842888031e-05 6.379694419153882662e-07 +1.266666666666666496e-01 8.479532071820358884e-06 2.529594711760443070e-07 +1.280000000000000027e-01 3.160320019205485887e-06 9.911555885444282819e-08 +1.293333333333333279e-01 5.156191795148754930e-06 1.550018658500189904e-07 +1.306666666666666532e-01 1.302114699187069443e-05 3.875875551631081219e-07 +1.320000000000000062e-01 2.448191181775688001e-05 7.548308766651755472e-07 +1.333333333333333315e-01 4.055071110271158921e-05 1.209632113147993333e-06 +1.346666666666666567e-01 5.430723368147500208e-05 1.703568712487504400e-06 +1.360000000000000098e-01 7.593907561589344907e-05 2.190370009653334060e-06 +1.373333333333333350e-01 9.091137562687272972e-05 2.628918504551843803e-06 +1.386666666666666603e-01 9.876665112112765978e-05 2.985617136047146022e-06 +1.400000000000000133e-01 1.090434830349768235e-04 3.236020124501763433e-06 +1.413333333333333386e-01 1.040784406922520204e-04 3.365684317789212350e-06 +1.426666666666666639e-01 1.084446320665948850e-04 3.370254540572536249e-06 +1.439999999999999891e-01 1.075386877360002690e-04 3.254846793366482747e-06 +1.453333333333333421e-01 9.784288464121572589e-05 3.032834603029489718e-06 +1.466666666666666674e-01 9.275195865909300664e-05 2.724174538521047095e-06 +1.479999999999999927e-01 8.314801822003993546e-05 2.353425867014385280e-06 +1.493333333333333457e-01 6.262927242161476637e-05 1.947626347500982468e-06 +1.506666666666666710e-01 4.985457261801826958e-05 1.534181812229199650e-06 +1.519999999999999962e-01 3.823191202299757656e-05 1.138912706537873486e-06 +1.533333333333333492e-01 2.740965478464539993e-05 7.843779252809585293e-07 +1.546666666666666745e-01 1.568836390366752764e-05 4.885672798968271187e-07 +1.559999999999999998e-01 8.866456616526149662e-06 2.640211742127980506e-07 +1.573333333333333250e-01 4.127225828926081602e-06 1.174020483931366809e-07 +1.586666666666666781e-01 1.568531262286780794e-06 4.950925901345495624e-08 +1.600000000000000033e-01 1.785293006053637639e-06 5.569943511660690089e-08 +1.613333333333333286e-01 4.168008252489919221e-06 1.266497375405589417e-07 +1.626666666666666816e-01 8.182944871264921272e-06 2.493831250965557545e-07 +1.640000000000000069e-01 1.394736579666988838e-05 4.084634364659220295e-07 +1.653333333333333321e-01 1.971738563115660661e-05 5.872640271270070076e-07 +1.666666666666666574e-01 2.427522111972123093e-05 7.692165387495262632e-07 +1.680000000000000104e-01 3.178245932529219920e-05 9.389553589627007831e-07 +1.693333333333333357e-01 3.548210427269819290e-05 1.083231375481271260e-06 +1.706666666666666610e-01 4.124756671870089794e-05 1.191858190928503467e-06 +1.720000000000000140e-01 4.114124414311627793e-05 1.257920990354555467e-06 +1.733333333333333393e-01 4.179113020179383182e-05 1.278144495652170089e-06 +1.746666666666666645e-01 4.243796202477311701e-05 1.252800954782821035e-06 +1.759999999999999898e-01 4.041810378176311592e-05 1.185411655127856387e-06 +1.773333333333333428e-01 3.655984369932937427e-05 1.082246689385805984e-06 +1.786666666666666681e-01 3.011829789651725348e-05 9.516730637963951386e-07 +1.799999999999999933e-01 2.721251947756813565e-05 8.034077531126447617e-07 +1.813333333333333464e-01 2.092119218494401328e-05 6.477344750345810442e-07 +1.826666666666666716e-01 1.660776611688476398e-05 4.947409616157709699e-07 +1.839999999999999969e-01 1.128412102902365682e-05 3.536277928505755650e-07 +1.853333333333333222e-01 7.841308266496928731e-06 2.321310905890118204e-07 +1.866666666666666752e-01 4.426575290143259573e-06 1.360903689794665034e-07 +1.880000000000000004e-01 2.217283777652154650e-06 6.918051302336152221e-08 +1.893333333333333257e-01 1.117228285514961176e-06 3.281414660715856888e-08 +1.906666666666666787e-01 8.799482043854008228e-07 2.620845226422373283e-08 +1.920000000000000040e-01 1.524709408323732203e-06 4.659961472149735373e-08 +1.933333333333333293e-01 3.115966333801529127e-06 8.957912985717266193e-08 +1.946666666666666545e-01 4.918104415699437457e-06 1.495197192619391863e-07 +1.960000000000000075e-01 7.342224987678191212e-06 2.200547862104041078e-07 +1.973333333333333328e-01 9.898358472380025657e-06 2.945743044174480836e-07 +1.986666666666666581e-01 1.199615466869351067e-05 3.667016150254416437e-07 +2.000000000000000111e-01 1.456024485260493697e-05 4.307195176666933625e-07 +2.013333333333333364e-01 1.580691336393115994e-05 4.819198389956592358e-07 +2.026666666666666616e-01 1.701583522311326881e-05 5.168578111597016410e-07 +2.039999999999999869e-01 1.851032070732363719e-05 5.335005051206870393e-07 +2.053333333333333399e-01 1.715605943587205967e-05 5.312666428372068491e-07 +2.066666666666666652e-01 1.579925214859981974e-05 5.109627938210401376e-07 +2.079999999999999905e-01 1.658552037802851100e-05 4.746277493330595449e-07 +2.093333333333333435e-01 1.526098059118070652e-05 4.253023617610078228e-07 +2.106666666666666687e-01 1.207623756549564588e-05 3.667460540832117272e-07 +2.119999999999999940e-01 9.516911054610977533e-06 3.031201720409783612e-07 +2.133333333333333470e-01 7.883129891417216319e-06 2.387174348937093517e-07 +2.146666666666666723e-01 5.874845517182401667e-06 1.777718369021740767e-07 +2.159999999999999976e-01 4.134533427932239587e-06 1.247467744346401067e-07 +2.173333333333333228e-01 2.702962831977833632e-06 8.389098142566779552e-08 +2.186666666666666758e-01 1.919475662810552952e-06 5.660017207259385942e-08 +2.200000000000000011e-01 1.362130841063948462e-06 4.023088338049808278e-08 diff --git a/notebooks/polarized_demo_data/fe_on_si_du.dat b/notebooks/polarized_demo_data/fe_on_si_du.dat new file mode 100644 index 00000000..310d49b0 --- /dev/null +++ b/notebooks/polarized_demo_data/fe_on_si_du.dat @@ -0,0 +1,161 @@ +# Qz (1/angstrom) R sR +8.000000000000000167e-03 1.007528046687608952e-01 2.953269400266327581e-03 +9.333333333333334106e-03 1.572583517379290974e-01 4.744889095853950176e-03 +1.066666666666666631e-02 2.190450883612067301e-01 6.348290158922448474e-03 +1.200000000000000025e-02 1.817726548209979698e-01 5.333047055155122271e-03 +1.333333333333333245e-02 1.700993783671900794e-01 4.838694817206525695e-03 +1.466666666666666639e-02 1.539094991606667073e-01 4.518230715159094954e-03 +1.600000000000000033e-02 1.354415289972342729e-01 4.264358150216949334e-03 +1.733333333333333254e-02 1.342962108592117287e-01 4.036995014532265835e-03 +1.866666666666666474e-02 1.227038410099842297e-01 3.815260616836199312e-03 +2.000000000000000042e-02 1.176659451520821187e-01 3.585730713023881338e-03 +2.133333333333333262e-02 1.163308261281947587e-01 3.338564763918748599e-03 +2.266666666666666829e-02 1.041684027537248536e-01 3.066403995854867417e-03 +2.400000000000000050e-02 9.022380436698591855e-02 2.764683779577930840e-03 +2.533333333333333270e-02 7.862819405015539220e-02 2.432831941156086541e-03 +2.666666666666666491e-02 6.926198949118184123e-02 2.075818194789780333e-03 +2.800000000000000058e-02 5.476494393947105993e-02 1.705182012707162978e-03 +2.933333333333333279e-02 4.371604781302679960e-02 1.338429655907041925e-03 +3.066666666666666499e-02 3.351430919901606659e-02 9.961054459564167105e-04 +3.200000000000000067e-02 2.404199231537985579e-02 6.970987714316889813e-04 +3.333333333333333287e-02 1.541282094899675166e-02 4.540916239051047713e-04 +3.466666666666666508e-02 8.419121830050425859e-03 2.712167368097655575e-04 +3.600000000000000422e-02 4.866438123017920343e-03 1.446721420904415584e-04 +3.733333333333332948e-02 2.184334737089637605e-03 6.538873658233344843e-05 +3.866666666666666863e-02 7.480584982595504936e-04 2.216651973245298166e-05 +4.000000000000000083e-02 1.437357498433520478e-04 4.112664642903217370e-06 +4.133333333333333304e-02 6.269007326684950790e-05 2.004793178707047923e-06 +4.266666666666666524e-02 2.865889630817411481e-04 8.752884696178654547e-06 +4.399999999999999745e-02 6.550303833826847787e-04 1.930862386371530147e-05 +4.533333333333332965e-02 9.631658244066027561e-04 3.033427008241416770e-05 +4.666666666666666879e-02 1.385728562476485660e-03 3.980916777581890430e-05 +4.800000000000000100e-02 1.573018053011269015e-03 4.667475297322238204e-05 +4.933333333333333320e-02 1.727633637372023986e-03 5.054528427436336515e-05 +5.066666666666666541e-02 1.686791702169595239e-03 5.148561259012480502e-05 +5.199999999999999761e-02 1.702067580113456003e-03 4.984516191675345741e-05 +5.333333333333332982e-02 1.587135771138159556e-03 4.613524715474374467e-05 +5.466666666666666202e-02 1.374157060934677209e-03 4.093870012507680326e-05 +5.600000000000000117e-02 1.169622308953436338e-03 3.484364780409830557e-05 +5.733333333333333337e-02 9.542071375089862875e-04 2.839591481744609854e-05 +5.866666666666666557e-02 7.164976311395233151e-04 2.206645809742515836e-05 +5.999999999999999778e-02 5.386531942509326619e-04 1.623143388812568893e-05 +6.133333333333332998e-02 3.703997764791592249e-04 1.116307189423245952e-05 +6.266666666666666219e-02 2.370178599298105031e-04 7.029681693010564393e-06 +6.400000000000000133e-02 1.340037966314343190e-04 3.903043182621346832e-06 +6.533333333333332660e-02 5.716856891883270394e-05 1.771306833428139153e-06 +6.666666666666666574e-02 1.844625167704147395e-05 5.554707159785059965e-07 +6.800000000000000488e-02 4.441357711528022514e-06 1.275710080193205838e-07 +6.933333333333333015e-02 1.071971812064922420e-05 3.289291796014142583e-07 +7.066666666666665542e-02 3.209209197047745474e-05 9.871123536089981223e-07 +7.200000000000000844e-02 6.474504632760995197e-05 1.930634013376320455e-06 +7.333333333333333370e-02 1.025604519999324173e-04 3.000798661515613774e-06 +7.466666666666665897e-02 1.353942129095416121e-04 4.060434544764981782e-06 +7.599999999999998423e-02 1.732954268357779178e-04 4.999537169031312049e-06 +7.733333333333333726e-02 1.961847617845852187e-04 5.738052975700706681e-06 +7.866666666666666252e-02 2.127803051638275087e-04 6.226169716693550195e-06 +7.999999999999998779e-02 2.183218588359995497e-04 6.442557935201045315e-06 +8.133333333333334081e-02 2.279107827402499539e-04 6.391039775753467422e-06 +8.266666666666666607e-02 2.019546328883078804e-04 6.096159940720307629e-06 +8.399999999999999134e-02 1.753877097453329192e-04 5.598109401755303868e-06 +8.533333333333334436e-02 1.728490010012067517e-04 4.947365070275968397e-06 +8.666666666666666963e-02 1.380682774156448791e-04 4.199714530999939949e-06 +8.799999999999999489e-02 1.140730814567600827e-04 3.411152544137300506e-06 +8.933333333333332016e-02 9.124993726152226591e-05 2.634016764123891206e-06 +9.066666666666667318e-02 6.072257135519556961e-05 1.913662668736402492e-06 +9.199999999999999845e-02 4.126117502474644250e-05 1.286128630253563915e-06 +9.333333333333332371e-02 2.464822992743742941e-05 7.767612157382617825e-07 +9.466666666666667673e-02 1.300805868990023634e-05 3.997658169802781140e-07 +9.600000000000000200e-02 5.356127149208341696e-06 1.585921273381317201e-07 +9.733333333333332726e-02 1.591982395813902045e-06 4.702005169047973194e-08 +9.866666666666665253e-02 1.706746190254171456e-06 5.078149721359232735e-08 +1.000000000000000056e-01 4.773339088185987279e-06 1.495380398780969620e-07 +1.013333333333333308e-01 9.897442493022866688e-06 3.190332704228019704e-07 +1.026666666666666561e-01 1.780398721376930520e-05 5.332500947762536672e-07 +1.040000000000000091e-01 2.518591650492778427e-05 7.664249224476511593e-07 +1.053333333333333344e-01 3.361698178729349720e-05 9.947995508378553946e-07 +1.066666666666666596e-01 4.077510951086607538e-05 1.198024554907400912e-06 +1.079999999999999849e-01 4.552677179032260055e-05 1.360162230472498603e-06 +1.093333333333333379e-01 5.012660576403459684e-05 1.470270143505473408e-06 +1.106666666666666632e-01 5.110153064570546782e-05 1.522576166114266844e-06 +1.119999999999999885e-01 5.134643196822854741e-05 1.516281145477262508e-06 +1.133333333333333415e-01 4.747617465830521113e-05 1.455045182847769484e-06 +1.146666666666666667e-01 4.463245108405789895e-05 1.346227467082781511e-06 +1.159999999999999920e-01 4.023471123264161690e-05 1.199957647518279284e-06 +1.173333333333333173e-01 3.511423690670630504e-05 1.028119077817123801e-06 +1.186666666666666703e-01 2.777866275678767680e-05 8.433213933226984112e-07 +1.199999999999999956e-01 2.227397560266068137e-05 6.579324820953016552e-07 +1.213333333333333208e-01 1.597916664254661170e-05 4.832288283145328242e-07 +1.226666666666666738e-01 1.091834395770022714e-05 3.287094353144487512e-07 +1.239999999999999991e-01 6.887338736991865434e-06 2.016031524920293201e-07 +1.253333333333333244e-01 3.340351128440268230e-06 1.065833450927718424e-07 +1.266666666666666496e-01 1.463717126803408988e-06 4.568853130034033272e-08 +1.280000000000000027e-01 5.881997961615234001e-07 1.846714590461363399e-08 +1.293333333333333279e-01 6.857057984571196680e-07 2.211974366523686281e-08 +1.306666666666666532e-01 1.701367205018803992e-06 5.210116722106288193e-08 +1.320000000000000062e-01 3.492999864241714603e-06 1.024858058137199515e-07 +1.333333333333333315e-01 5.503821082625335375e-06 1.665379527543910645e-07 +1.346666666666666567e-01 7.955450596612713394e-06 2.372557130903818383e-07 +1.360000000000000098e-01 1.059807236400316402e-05 3.078816679312774017e-07 +1.373333333333333350e-01 1.290597782263722626e-05 3.723484773852549928e-07 +1.386666666666666603e-01 1.415840904620301583e-05 4.256350973333881385e-07 +1.400000000000000133e-01 1.609534352654533548e-05 4.640176725731832458e-07 +1.413333333333333386e-01 1.621829397454471562e-05 4.852078020329178800e-07 +1.426666666666666639e-01 1.587033743462086622e-05 4.883791582282670671e-07 +1.439999999999999891e-01 1.552122944192228855e-05 4.740908767816654892e-07 +1.453333333333333421e-01 1.414653781401564062e-05 4.441223134538681079e-07 +1.466666666666666674e-01 1.301826040420639851e-05 4.012384147119141744e-07 +1.479999999999999927e-01 1.150534861508465464e-05 3.489079040197437945e-07 +1.493333333333333457e-01 9.933765042361007673e-06 2.909977092754138739e-07 +1.506666666666666710e-01 8.113854683736707309e-06 2.314666173503457221e-07 +1.519999999999999962e-01 5.562031195432281614e-06 1.740792077582149680e-07 +1.533333333333333492e-01 4.119918280027462889e-06 1.221579385885165474e-07 +1.546666666666666745e-01 2.531339545498620342e-06 7.838714440552982691e-08 +1.559999999999999998e-01 1.510475390185554071e-06 4.467800612282128617e-08 +1.573333333333333250e-01 7.337241119796921424e-07 2.209862841490182638e-08 +1.586666666666666781e-01 3.423860559455051005e-07 1.086856102959510645e-08 +1.600000000000000033e-01 3.567575149171467956e-07 1.041274193708904071e-08 +1.613333333333333286e-01 6.370957478418122976e-07 1.946618409280882712e-08 +1.626666666666666816e-01 1.187928772811707295e-06 3.621796661461974208e-08 +1.640000000000000069e-01 1.886817482226232735e-06 5.848134113771266685e-08 +1.653333333333333321e-01 2.740990010100866872e-06 8.387606143231342599e-08 +1.666666666666666574e-01 3.714050285203545527e-06 1.100093500775490443e-07 +1.680000000000000104e-01 4.462623715355420957e-06 1.346431249439114636e-07 +1.693333333333333357e-01 5.245524653001244682e-06 1.558292846488293914e-07 +1.706666666666666610e-01 5.797230945146113261e-06 1.720488808566032506e-07 +1.720000000000000140e-01 6.315639180347299995e-06 1.822485165816315026e-07 +1.733333333333333393e-01 6.132828576201715253e-06 1.858965400689284567e-07 +1.746666666666666645e-01 5.828917138659435816e-06 1.829743260720297752e-07 +1.759999999999999898e-01 5.983528676165092459e-06 1.739369770554233248e-07 +1.773333333333333428e-01 5.268554844138802376e-06 1.596442507501743892e-07 +1.786666666666666681e-01 4.866416974192439597e-06 1.412687952699017713e-07 +1.799999999999999933e-01 4.052403967000443886e-06 1.201897786982718147e-07 +1.813333333333333464e-01 3.249843487696617775e-06 9.788037836311576685e-08 +1.826666666666666716e-01 2.553015230252052794e-06 7.579736806318378599e-08 +1.839999999999999969e-01 1.950528170525877562e-06 5.528027007827940618e-08 +1.853333333333333222e-01 1.326693994938500607e-06 3.746631560777318907e-08 +1.866666666666666752e-01 7.758080648631726058e-07 2.322589876926246457e-08 +1.880000000000000004e-01 4.394834729298315398e-07 1.312144622023900073e-08 +1.893333333333333257e-01 2.543140075460072141e-07 7.390792205372861249e-09 +1.906666666666666787e-01 1.934438757758805036e-07 5.954377832495878448e-09 +1.920000000000000040e-01 2.842791322772257605e-07 8.444000575771938985e-09 +1.933333333333333293e-01 4.746155226669125089e-07 1.424952167638332013e-08 +1.946666666666666545e-01 7.597199115755520296e-07 2.257896566540885911e-08 +1.960000000000000075e-01 1.057121768979611590e-06 3.252685916284303909e-08 +1.973333333333333328e-01 1.369599608430002247e-06 4.314546721810592838e-08 +1.986666666666666581e-01 1.672859432810189042e-06 5.351377826236124305e-08 +2.000000000000000111e-01 2.023149424947413852e-06 6.279962173735473534e-08 +2.013333333333333364e-01 2.311453826628649454e-06 7.031111390500286278e-08 +2.026666666666666616e-01 2.495675246732672360e-06 7.553464103958034328e-08 +2.039999999999999869e-01 2.756666934061640622e-06 7.815772084942974035e-08 +2.053333333333333399e-01 2.688893360609558629e-06 7.807624648935259530e-08 +2.066666666666666652e-01 2.440362129845528739e-06 7.538673102154169124e-08 +2.079999999999999905e-01 2.369971933634048958e-06 7.036516117011379262e-08 +2.093333333333333435e-01 2.088673000388599980e-06 6.343487876882732770e-08 +2.106666666666666687e-01 1.821873819654762058e-06 5.512649403226413222e-08 +2.119999999999999940e-01 1.542950070958765300e-06 4.603257164174993808e-08 +2.133333333333333470e-01 1.248412959336401762e-06 3.676939240707617536e-08 +2.146666666666666723e-01 9.221627784440944102e-07 2.794934492486567134e-08 +2.159999999999999976e-01 6.957246227612103214e-07 2.022620820483283792e-08 +2.173333333333333228e-01 4.582678415428967050e-07 1.423572479592624386e-08 +2.186666666666666758e-01 3.403128237926930089e-07 1.020744354514502879e-08 +2.200000000000000011e-01 2.793881066986614425e-07 7.775682608413154104e-09 diff --git a/notebooks/polarized_demo_data/fe_on_si_ud.dat b/notebooks/polarized_demo_data/fe_on_si_ud.dat new file mode 100644 index 00000000..83bbb25e --- /dev/null +++ b/notebooks/polarized_demo_data/fe_on_si_ud.dat @@ -0,0 +1,161 @@ +# Qz (1/angstrom) R sR +8.000000000000000167e-03 9.559206354062578515e-02 2.953269400266322810e-03 +9.333333333333334106e-03 1.547218526161534269e-01 4.744889095853945839e-03 +1.066666666666666631e-02 2.251218154200637367e-01 6.348290158922438933e-03 +1.200000000000000025e-02 1.733877413610561136e-01 5.333047055155121403e-03 +1.333333333333333245e-02 1.653470206046200042e-01 4.838694817206520490e-03 +1.466666666666666639e-02 1.465280571957182643e-01 4.518230715159088882e-03 +1.600000000000000033e-02 1.461178326438707653e-01 4.264358150216949334e-03 +1.733333333333333254e-02 1.361205456154175597e-01 4.036995014532264968e-03 +1.866666666666666474e-02 1.265777394925009791e-01 3.815260616836197143e-03 +2.000000000000000042e-02 1.193781936588917819e-01 3.585730713023881338e-03 +2.133333333333333262e-02 1.090994410028168771e-01 3.338564763918749900e-03 +2.266666666666666829e-02 1.035813041095600406e-01 3.066403995854868284e-03 +2.400000000000000050e-02 9.089824053785679203e-02 2.764683779577925635e-03 +2.533333333333333270e-02 7.811270518917826089e-02 2.432831941156086541e-03 +2.666666666666666491e-02 6.654117375774777632e-02 2.075818194789778599e-03 +2.800000000000000058e-02 5.713369423648240186e-02 1.705182012707161677e-03 +2.933333333333333279e-02 4.672782443053131313e-02 1.338429655907042142e-03 +3.066666666666666499e-02 3.336288338280176541e-02 9.961054459564173610e-04 +3.200000000000000067e-02 2.315392308301371729e-02 6.970987714316887645e-04 +3.333333333333333287e-02 1.526617871939014472e-02 4.540916239051049339e-04 +3.466666666666666508e-02 9.394767424312444604e-03 2.712167368097654491e-04 +3.600000000000000422e-02 4.854143272757511021e-03 1.446721420904412874e-04 +3.733333333333332948e-02 2.152754540292944711e-03 6.538873658233317738e-05 +3.866666666666666863e-02 7.634065616031009398e-04 2.216651973245304942e-05 +4.000000000000000083e-02 1.388521528749857737e-04 4.112664642903227534e-06 +4.133333333333333304e-02 6.990531242714679162e-05 2.004793178707042841e-06 +4.266666666666666524e-02 2.933666531073274254e-04 8.752884696178646077e-06 +4.399999999999999745e-02 6.199779834909185505e-04 1.930862386371532180e-05 +4.533333333333332965e-02 9.696402254148140049e-04 3.033427008241414060e-05 +4.666666666666666879e-02 1.392694326233942948e-03 3.980916777581890430e-05 +4.800000000000000100e-02 1.636276770833382027e-03 4.667475297322231428e-05 +4.933333333333333320e-02 1.675768959477367138e-03 5.054528427436338548e-05 +5.066666666666666541e-02 1.696458452372038997e-03 5.148561259012481858e-05 +5.199999999999999761e-02 1.734351324604243086e-03 4.984516191675345741e-05 +5.333333333333332982e-02 1.486767745672122306e-03 4.613524715474371079e-05 +5.466666666666666202e-02 1.327994376379723054e-03 4.093870012507678293e-05 +5.600000000000000117e-02 1.183870779060362073e-03 3.484364780409832590e-05 +5.733333333333333337e-02 9.353253204596205170e-04 2.839591481744607821e-05 +5.866666666666666557e-02 7.354355817901460021e-04 2.206645809742515158e-05 +5.999999999999999778e-02 5.383948836689723960e-04 1.623143388812569909e-05 +6.133333333333332998e-02 3.758707654323206531e-04 1.116307189423245105e-05 +6.266666666666666219e-02 2.442168725740734964e-04 7.029681693010561005e-06 +6.400000000000000133e-02 1.304549962240428025e-04 3.903043182621345985e-06 +6.533333333333332660e-02 6.018417429911210201e-05 1.771306833428140000e-06 +6.666666666666666574e-02 1.737687996777799413e-05 5.554707159785055730e-07 +6.800000000000000488e-02 4.246151878334042932e-06 1.275710080193207426e-07 +6.933333333333333015e-02 1.068694294569129868e-05 3.289291796014143113e-07 +7.066666666666665542e-02 3.170063969161577715e-05 9.871123536089979105e-07 +7.200000000000000844e-02 6.265907628402426100e-05 1.930634013376314526e-06 +7.333333333333333370e-02 9.902398487688450469e-05 3.000798661515614621e-06 +7.466666666666665897e-02 1.390667804814529310e-04 4.060434544764975005e-06 +7.599999999999998423e-02 1.600198892746388583e-04 4.999537169031307814e-06 +7.733333333333333726e-02 1.914441976505883657e-04 5.738052975700704140e-06 +7.866666666666666252e-02 2.045244694928812943e-04 6.226169716693544266e-06 +7.999999999999998779e-02 2.126408782791344753e-04 6.442557935201040233e-06 +8.133333333333334081e-02 2.194433243387519748e-04 6.391039775753465728e-06 +8.266666666666666607e-02 2.064857691278901329e-04 6.096159940720307629e-06 +8.399999999999999134e-02 1.940905476443417473e-04 5.598109401755303021e-06 +8.533333333333334436e-02 1.641477730085187799e-04 4.947365070275969244e-06 +8.666666666666666963e-02 1.370677240676891823e-04 4.199714530999940796e-06 +8.799999999999999489e-02 1.129414682318471226e-04 3.411152544137297965e-06 +8.933333333333332016e-02 8.843929941758425740e-05 2.634016764123887818e-06 +9.066666666666667318e-02 6.412665746891291934e-05 1.913662668736399103e-06 +9.199999999999999845e-02 4.147629179578066075e-05 1.286128630253563915e-06 +9.333333333333332371e-02 2.596232947739787901e-05 7.767612157382616766e-07 +9.466666666666667673e-02 1.341676511753195322e-05 3.997658169802778493e-07 +9.600000000000000200e-02 5.685655807735410875e-06 1.585921273381317201e-07 +9.733333333333332726e-02 1.655584386986792729e-06 4.702005169047989737e-08 +9.866666666666665253e-02 1.649387598957539986e-06 5.078149721359380304e-08 +1.000000000000000056e-01 4.941626584690286240e-06 1.495380398780978620e-07 +1.013333333333333308e-01 1.016755565950663672e-05 3.190332704228036644e-07 +1.026666666666666561e-01 1.746000858802738620e-05 5.332500947762522907e-07 +1.040000000000000091e-01 2.578938495532255237e-05 7.664249224476520064e-07 +1.053333333333333344e-01 3.435956766836428144e-05 9.947995508378539123e-07 +1.066666666666666596e-01 3.906069149004365998e-05 1.198024554907398794e-06 +1.079999999999999849e-01 4.444899573477161902e-05 1.360162230472496274e-06 +1.093333333333333379e-01 4.585190983361416169e-05 1.470270143505472138e-06 +1.106666666666666632e-01 5.050486761681483483e-05 1.522576166114266420e-06 +1.119999999999999885e-01 4.893178590785611683e-05 1.516281145477262508e-06 +1.133333333333333415e-01 4.773114780652696064e-05 1.455045182847769273e-06 +1.146666666666666667e-01 4.369379483838258795e-05 1.346227467082781299e-06 +1.159999999999999920e-01 3.988547717775933039e-05 1.199957647518278861e-06 +1.173333333333333173e-01 3.246348183446290359e-05 1.028119077817123801e-06 +1.186666666666666703e-01 2.687352247034604499e-05 8.433213933226984112e-07 +1.199999999999999956e-01 2.333198357392108141e-05 6.579324820953014434e-07 +1.213333333333333208e-01 1.548550790498974200e-05 4.832288283145338830e-07 +1.226666666666666738e-01 1.059645740899342626e-05 3.287094353144484335e-07 +1.239999999999999991e-01 7.090432641232453841e-06 2.016031524920286319e-07 +1.253333333333333244e-01 3.862409946375891984e-06 1.065833450927718027e-07 +1.266666666666666496e-01 1.469423884753160196e-06 4.568853130034043198e-08 +1.280000000000000027e-01 6.087710229395623162e-07 1.846714590461365715e-08 +1.293333333333333279e-01 7.448799100754689694e-07 2.211974366523678340e-08 +1.306666666666666532e-01 1.826772739081254099e-06 5.210116722106263709e-08 +1.320000000000000062e-01 3.315054684218319539e-06 1.024858058137203220e-07 +1.333333333333333315e-01 5.510417021493823192e-06 1.665379527543908528e-07 +1.346666666666666567e-01 8.092951550599856943e-06 2.372557130903813883e-07 +1.360000000000000098e-01 1.039657876854264040e-05 3.078816679312777193e-07 +1.373333333333333350e-01 1.227155477246007717e-05 3.723484773852547281e-07 +1.386666666666666603e-01 1.413087682725235949e-05 4.256350973333876091e-07 +1.400000000000000133e-01 1.482927979940897518e-05 4.640176725731831399e-07 +1.413333333333333386e-01 1.605802964228135723e-05 4.852078020329175624e-07 +1.426666666666666639e-01 1.614920717614244647e-05 4.883791582282668553e-07 +1.439999999999999891e-01 1.591309885259072651e-05 4.740908767816653833e-07 +1.453333333333333421e-01 1.455744390598313682e-05 4.441223134538681608e-07 +1.466666666666666674e-01 1.356381319299208061e-05 4.012384147119141214e-07 +1.479999999999999927e-01 1.198360802068795317e-05 3.489079040197438474e-07 +1.493333333333333457e-01 9.745153220819537010e-06 2.909977092754138739e-07 +1.506666666666666710e-01 7.796973777661658211e-06 2.314666173503456162e-07 +1.519999999999999962e-01 5.811893499401644837e-06 1.740792077582153121e-07 +1.533333333333333492e-01 4.071941595569973699e-06 1.221579385885168385e-07 +1.546666666666666745e-01 2.556343939745368904e-06 7.838714440552985338e-08 +1.559999999999999998e-01 1.503407203321582893e-06 4.467800612282114720e-08 +1.573333333333333250e-01 7.344710467753042064e-07 2.209862841490180983e-08 +1.586666666666666781e-01 3.850350951639976028e-07 1.086856102959512961e-08 +1.600000000000000033e-01 3.634743364778997648e-07 1.041274193708913005e-08 +1.613333333333333286e-01 6.563837631172683734e-07 1.946618409280883705e-08 +1.626666666666666816e-01 1.179629173274889455e-06 3.621796661461978179e-08 +1.640000000000000069e-01 1.884322723145190400e-06 5.848134113771273303e-08 +1.653333333333333321e-01 2.895777093918394305e-06 8.387606143231335982e-08 +1.666666666666666574e-01 3.695883207410113677e-06 1.100093500775489120e-07 +1.680000000000000104e-01 4.552752173050893864e-06 1.346431249439112783e-07 +1.693333333333333357e-01 4.922451901935751810e-06 1.558292846488293385e-07 +1.706666666666666610e-01 5.894527448035536099e-06 1.720488808566031183e-07 +1.720000000000000140e-01 6.157767985265139900e-06 1.822485165816314762e-07 +1.733333333333333393e-01 5.990126113411925469e-06 1.858965400689284567e-07 +1.746666666666666645e-01 6.012867268531592707e-06 1.829743260720296958e-07 +1.759999999999999898e-01 5.843769408504052207e-06 1.739369770554231395e-07 +1.773333333333333428e-01 5.329851047718660423e-06 1.596442507501745216e-07 +1.786666666666666681e-01 4.667685170899017475e-06 1.412687952699019301e-07 +1.799999999999999933e-01 3.993887724572344332e-06 1.201897786982717883e-07 +1.813333333333333464e-01 3.238015637652685508e-06 9.788037836311563450e-08 +1.826666666666666716e-01 2.538142772321931427e-06 7.579736806318370658e-08 +1.839999999999999969e-01 1.924020142961066166e-06 5.528027007827952529e-08 +1.853333333333333222e-01 1.152713951720483779e-06 3.746631560777311627e-08 +1.866666666666666752e-01 7.686955653751071116e-07 2.322589876926246126e-08 +1.880000000000000004e-01 4.396976389188759605e-07 1.312144622023899743e-08 +1.893333333333333257e-01 2.485473702509119916e-07 7.390792205372861249e-09 +1.906666666666666787e-01 1.962647411445939018e-07 5.954377832495861077e-09 +1.920000000000000040e-01 2.666329261163428065e-07 8.444000575771889354e-09 +1.933333333333333293e-01 4.796578346343829695e-07 1.424952167638328705e-08 +1.946666666666666545e-01 7.916339700247115016e-07 2.257896566540885249e-08 +1.960000000000000075e-01 1.034336944868405880e-06 3.252685916284300600e-08 +1.973333333333333328e-01 1.475452503850628252e-06 4.314546721810594161e-08 +1.986666666666666581e-01 1.766211982801583463e-06 5.351377826236124305e-08 +2.000000000000000111e-01 2.089469578857149948e-06 6.279962173735476181e-08 +2.013333333333333364e-01 2.269673329716914896e-06 7.031111390500284955e-08 +2.026666666666666616e-01 2.492558341063638476e-06 7.553464103958034328e-08 +2.039999999999999869e-01 2.706865884171947466e-06 7.815772084942968741e-08 +2.053333333333333399e-01 2.648033083936830869e-06 7.807624648935255560e-08 +2.066666666666666652e-01 2.643484343093922649e-06 7.538673102154163830e-08 +2.079999999999999905e-01 2.428354149668647728e-06 7.036516117011372644e-08 +2.093333333333333435e-01 2.142349369207815779e-06 6.343487876882734094e-08 +2.106666666666666687e-01 1.933686797324920648e-06 5.512649403226417192e-08 +2.119999999999999940e-01 1.554627038754858763e-06 4.603257164174999102e-08 +2.133333333333333470e-01 1.256091035750277766e-06 3.676939240707620845e-08 +2.146666666666666723e-01 9.233558669538192546e-07 2.794934492486571105e-08 +2.159999999999999976e-01 6.755529096878243186e-07 2.022620820483286770e-08 +2.173333333333333228e-01 4.645958262495849456e-07 1.423572479592631334e-08 +2.186666666666666758e-01 3.503492403170606910e-07 1.020744354514503706e-08 +2.200000000000000011e-01 2.500273052907539463e-07 7.775682608413152450e-09 diff --git a/notebooks/polarized_demo_data/fe_on_si_uu.dat b/notebooks/polarized_demo_data/fe_on_si_uu.dat new file mode 100644 index 00000000..3694bfb7 --- /dev/null +++ b/notebooks/polarized_demo_data/fe_on_si_uu.dat @@ -0,0 +1,161 @@ +# Qz (1/angstrom) R sR +8.000000000000000167e-03 9.097992872869360426e-01 2.704673059568740018e-02 +9.333333333333334106e-03 8.063090430971723288e-01 2.496826939277983076e-02 +1.066666666666666631e-02 3.993410666786681995e-01 1.171645321068302131e-02 +1.200000000000000025e-02 2.208085349552587062e-01 6.442469270198266686e-03 +1.333333333333333245e-02 1.378701987041681676e-01 4.393247367307369713e-03 +1.466666666666666639e-02 1.054462848413706411e-01 3.291991459709087391e-03 +1.600000000000000033e-02 8.744568904510718887e-02 2.613347927772132363e-03 +1.733333333333333254e-02 7.146167917255358271e-02 2.164384492054474089e-03 +1.866666666666666474e-02 6.167976374855279470e-02 1.851326045070834578e-03 +2.000000000000000042e-02 5.265218748124480402e-02 1.621050441499697877e-03 +2.133333333333333262e-02 4.927121530985689724e-02 1.440142702025466563e-03 +2.266666666666666829e-02 4.386922365138993390e-02 1.286067912029972274e-03 +2.400000000000000050e-02 3.818426600138049437e-02 1.143263265913084135e-03 +2.533333333333333270e-02 3.451548358949688838e-02 1.001593387513934411e-03 +2.666666666666666491e-02 2.893314831493903547e-02 8.559889643176760762e-04 +2.800000000000000058e-02 2.294049681346108441e-02 7.064256926010059525e-04 +2.933333333333333279e-02 1.878720553741572083e-02 5.574493693613232197e-04 +3.066666666666666499e-02 1.348984789683120925e-02 4.166819078504779708e-04 +3.200000000000000067e-02 1.000274871208850849e-02 2.923772937100064802e-04 +3.333333333333333287e-02 6.352269739014006905e-03 1.908539488883274199e-04 +3.466666666666666508e-02 3.807788233433779135e-03 1.148707051044449136e-04 +3.600000000000000422e-02 2.072462443473301660e-03 6.347044024293114052e-05 +3.733333333333332948e-02 1.139804540655666408e-03 3.298439271106011055e-05 +3.866666666666666863e-02 6.122598816790504953e-04 1.845334402119248518e-05 +4.000000000000000083e-02 4.883437676771125490e-04 1.484101765341856499e-05 +4.133333333333333304e-02 5.863780344992955938e-04 1.777916019914761169e-05 +4.266666666666666524e-02 8.085021003458838429e-04 2.387381547227360713e-05 +4.399999999999999745e-02 1.035202554519308550e-03 3.071929115713385912e-05 +4.533333333333332965e-02 1.240744044437433122e-03 3.676707224072342880e-05 +4.666666666666666879e-02 1.389209491691400242e-03 4.114450721474634341e-05 +4.800000000000000100e-02 1.542409592178596968e-03 4.347879975014890382e-05 +4.933333333333333320e-02 1.440437539146308685e-03 4.374650324943592949e-05 +5.066666666666666541e-02 1.383520769361035446e-03 4.215340635820877485e-05 +5.199999999999999761e-02 1.269632015624745126e-03 3.904210241469862578e-05 +5.333333333333332982e-02 1.182193676818206300e-03 3.482231541233326162e-05 +5.466666666666666202e-02 1.031084451500815938e-03 2.991919527059426961e-05 +5.600000000000000117e-02 8.217067419050427812e-04 2.473575956461779602e-05 +5.733333333333333337e-02 6.377339302697241285e-04 1.962670295639886505e-05 +5.866666666666666557e-02 4.837841769535204824e-04 1.488161363565887598e-05 +5.999999999999999778e-02 3.641768060699149896e-04 1.071614871017455281e-05 +6.133333333333332998e-02 2.477354188367224784e-04 7.269959728324917905e-06 +6.266666666666666219e-02 1.561776233511654095e-04 4.610207090761423770e-06 +6.400000000000000133e-02 8.949197401578911281e-05 2.739453201419914626e-06 +6.533333333333332660e-02 5.392847547534247797e-05 1.606664106320856537e-06 +6.666666666666666574e-02 3.746532653419778743e-05 1.120039016259490813e-06 +6.800000000000000488e-02 3.893568137509112090e-05 1.160457079345345069e-06 +6.933333333333333015e-02 5.453891258501781982e-05 1.594483025713826729e-06 +7.066666666666665542e-02 7.671411705147658197e-05 2.286088733596215601e-06 +7.200000000000000844e-02 1.056589203629337842e-04 3.106496336003633004e-06 +7.333333333333333370e-02 1.316596805769652617e-04 3.941798924206651489e-06 +7.466666666666665897e-02 1.579664076472516462e-04 4.698241645434700220e-06 +7.599999999999998423e-02 1.801902185616797894e-04 5.305232630010590067e-06 +7.733333333333333726e-02 1.822135625501344306e-04 5.716292754145070896e-06 +7.866666666666666252e-02 1.950528509916157188e-04 5.908246419330654096e-06 +7.999999999999998779e-02 1.932016051436307032e-04 5.879007890693535743e-06 +8.133333333333334081e-02 1.845385281838792435e-04 5.644337103825948520e-06 +8.266666666666666607e-02 1.730243643665857608e-04 5.233933216032450874e-06 +8.399999999999999134e-02 1.632474535192117589e-04 4.687210468655069082e-06 +8.533333333333334436e-02 1.314622827741316344e-04 4.049042081922340294e-06 +8.666666666666666963e-02 1.154554884671698140e-04 3.365891070005132427e-06 +8.799999999999999489e-02 8.488985829504271097e-05 2.682104738513855658e-06 +8.933333333333332016e-02 6.722305804620891541e-05 2.037158154475688395e-06 +9.066666666666667318e-02 4.901979405596596531e-05 1.463448400295844109e-06 +9.199999999999999845e-02 3.340711008125167076e-05 9.848923261699929418e-07 +9.333333333333332371e-02 2.098114612862189604e-05 6.162848391097110831e-07 +9.466666666666667673e-02 1.240039510890430205e-05 3.633636469272716737e-07 +9.600000000000000200e-02 7.371654825392529470e-06 2.234877178786924464e-07 +9.733333333333332726e-02 6.140559884840828304e-06 1.868079259283322616e-07 +9.866666666666665253e-02 8.130369333425144199e-06 2.377905239719707620e-07 +1.000000000000000056e-01 1.182996255883061676e-05 3.569474444613453580e-07 +1.013333333333333308e-01 1.675432993250500145e-05 5.226313062615320515e-07 +1.026666666666666561e-01 2.295109736382246668e-05 7.127659779979260574e-07 +1.040000000000000091e-01 2.938005921282587575e-05 9.064036228776255802e-07 +1.053333333333333344e-01 3.670689922491348158e-05 1.085024036035259375e-06 +1.066666666666666596e-01 4.129299961414677103e-05 1.233519440994174777e-06 +1.079999999999999849e-01 4.562034593524948124e-05 1.340835558594794967e-06 +1.093333333333333379e-01 4.607726060783903278e-05 1.400265836765147427e-06 +1.106666666666666632e-01 4.720407447129864551e-05 1.409418769648823861e-06 +1.119999999999999885e-01 4.652023459077186561e-05 1.369897201817758128e-06 +1.133333333333333415e-01 4.249337645689370844e-05 1.286742776469133279e-06 +1.146666666666666667e-01 3.945697934704982322e-05 1.167707977743549753e-06 +1.159999999999999920e-01 3.340398457858895855e-05 1.022422578174446118e-06 +1.173333333333333173e-01 2.840458916012842668e-05 8.615210305247138080e-07 +1.186666666666666703e-01 2.292748762504616899e-05 6.957929447530528394e-07 +1.199999999999999956e-01 1.720676560194368978e-05 5.354109368057387761e-07 +1.213333333333333208e-01 1.316555588048205136e-05 3.892796225096771491e-07 +1.226666666666666738e-01 8.693733480536175017e-06 2.645372363504957658e-07 +1.239999999999999991e-01 5.543016807589842190e-06 1.662281979824507759e-07 +1.253333333333333244e-01 3.285099840968646500e-06 9.715183266031837250e-08 +1.266666666666666496e-01 1.955185778289120519e-06 5.788021375121773621e-08 +1.280000000000000027e-01 1.596394802918315076e-06 4.695455817334131952e-08 +1.293333333333333279e-01 2.029656599321841208e-06 6.107013363064531437e-08 +1.306666666666666532e-01 3.147121394412769539e-06 9.562801718007675268e-08 +1.320000000000000062e-01 4.824223022243863525e-06 1.450736410006232978e-07 +1.333333333333333315e-01 6.436506611609275528e-06 2.033908498851756146e-07 +1.346666666666666567e-01 8.435753307531895936e-06 2.645579526131113161e-07 +1.360000000000000098e-01 1.033821735151448561e-05 3.229619707509598506e-07 +1.373333333333333350e-01 1.208550851253621602e-05 3.737467893770618719e-07 +1.386666666666666603e-01 1.393439253998442856e-05 4.130776423481388130e-07 +1.400000000000000133e-01 1.421350564839384544e-05 4.383116294576547520e-07 +1.413333333333333386e-01 1.476626395892329756e-05 4.480712316077902185e-07 +1.426666666666666639e-01 1.531537018309152517e-05 4.422245846616717551e-07 +1.439999999999999891e-01 1.390914065522243100e-05 4.217821936334787589e-07 +1.453333333333333421e-01 1.324417411875621121e-05 3.887245121754610337e-07 +1.466666666666666674e-01 1.120311462801779919e-05 3.457781774349806621e-07 +1.479999999999999927e-01 9.811176893421107628e-06 2.961605820037383467e-07 +1.493333333333333457e-01 7.879277007894731297e-06 2.433128885477734744e-07 +1.506666666666666710e-01 6.290054724570724669e-06 1.906406462781018071e-07 +1.519999999999999962e-01 4.828018842308375617e-06 1.412790277686688167e-07 +1.533333333333333492e-01 3.094121452750714761e-06 9.789660788748038203e-08 +1.546666666666666745e-01 2.112100091210456856e-06 6.254783498520832551e-08 +1.559999999999999998e-01 1.228036474086856133e-06 3.658020173382685675e-08 +1.573333333333333250e-01 6.743588996379143178e-07 2.059791448856219756e-08 +1.586666666666666781e-01 4.617236887443765433e-07 1.447987267050306567e-08 +1.600000000000000033e-01 5.828001903164824309e-07 1.744625401808121305e-08 +1.613333333333333286e-01 9.239253352866243629e-07 2.816515742376424879e-08 +1.626666666666666816e-01 1.506735250009409562e-06 4.488872137164296450e-08 +1.640000000000000069e-01 2.188333174891102094e-06 6.560698564480311472e-08 +1.653333333333333321e-01 3.081539047557577154e-06 8.820750389297125160e-08 +1.666666666666666574e-01 3.661163273165275461e-06 1.106292904922382127e-07 +1.680000000000000104e-01 4.232620483364079970e-06 1.310009900462210656e-07 +1.693333333333333357e-01 4.951403813595259923e-06 1.477474886625623296e-07 +1.706666666666666610e-01 5.358535328271501864e-06 1.597020422565462717e-07 +1.720000000000000140e-01 5.763097368670237389e-06 1.661193023089299000e-07 +1.733333333333333393e-01 5.696373066655557468e-06 1.667144389158185545e-07 +1.746666666666666645e-01 5.445754992892647173e-06 1.616420882892517069e-07 +1.759999999999999898e-01 5.269992715381009275e-06 1.514512115875856919e-07 +1.773333333333333428e-01 4.404327904642388854e-06 1.370162321789783880e-07 +1.786666666666666681e-01 3.905281055242455666e-06 1.194510004765750695e-07 +1.799999999999999933e-01 3.241090765491239341e-06 1.000128067826545866e-07 +1.813333333333333464e-01 2.635606843178464082e-06 8.000379318150786771e-08 +1.826666666666666716e-01 1.939025085671843059e-06 6.067673728096377354e-08 +1.839999999999999969e-01 1.465786188839188880e-06 4.315135690925841093e-08 +1.853333333333333222e-01 9.385709943918117380e-07 2.834610426400807751e-08 +1.866666666666666752e-01 5.394002925749825609e-07 1.692898657779461566e-08 +1.880000000000000004e-01 3.002120132500959102e-07 9.289383504435447816e-09 +1.893333333333333257e-01 1.861089710946391928e-07 5.531245471371540921e-09 +1.906666666666666787e-01 1.874838176711437345e-07 5.486561540334066774e-09 +1.920000000000000040e-01 3.090206001193436630e-07 8.746675300421817014e-09 +1.933333333333333293e-01 5.331229924230900979e-07 1.470797893923666382e-08 +1.946666666666666545e-01 7.636365505684721927e-07 2.262778153340758893e-08 +1.960000000000000075e-01 1.024837574750060220e-06 3.168575511907128065e-08 +1.973333333333333328e-01 1.280697801903912735e-06 4.104631324165549513e-08 +1.986666666666666581e-01 1.677281826504772928e-06 4.991754978763511508e-08 +2.000000000000000111e-01 1.873269516152690271e-06 5.760291888369212628e-08 +2.013333333333333364e-01 2.091694161306209310e-06 6.354261138831364574e-08 +2.026666666666666616e-01 2.203530231180347108e-06 6.734251099808780579e-08 +2.039999999999999869e-01 2.283302344461021660e-06 6.878961887636610838e-08 +2.053333333333333399e-01 2.334125714454877571e-06 6.785384569096191238e-08 +2.066666666666666652e-01 2.166057926540760182e-06 6.467701482049679655e-08 +2.079999999999999905e-01 1.975577859553661420e-06 5.955074044679917747e-08 +2.093333333333333435e-01 1.708078682226702826e-06 5.288549222147748138e-08 +2.106666666666666687e-01 1.430135275616249324e-06 4.517360209801992675e-08 +2.119999999999999940e-01 1.213667744507251196e-06 3.694909139659465115e-08 +2.133333333333333470e-01 9.568654434433844830e-07 2.875235455239735603e-08 +2.146666666666666723e-01 7.408982845384479317e-07 2.110745357871421160e-08 +2.159999999999999976e-01 4.869702574935956964e-07 1.455223416454345105e-08 +2.173333333333333228e-01 3.285896769109876361e-07 9.575386973950528065e-09 +2.186666666666666758e-01 2.068206441979765097e-07 6.298970777474893842e-09 +2.200000000000000011e-01 1.401986602342771183e-07 4.360985483968211197e-09 diff --git a/notebooks/polarized_fitting.ipynb b/notebooks/polarized_fitting.ipynb new file mode 100644 index 00000000..ebf6f53b --- /dev/null +++ b/notebooks/polarized_fitting.ipynb @@ -0,0 +1,505 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "intro-title", + "metadata": {}, + "source": [ + "# Polarized Neutron Reflectometry — Magnetic Models and Simultaneous Multi-Channel Fitting\n", + "\n", + "This notebook demonstrates the polarized (PNR) functionality of `easyreflectometry`:\n", + "\n", + "| Feature | API |\n", + "|---|---|\n", + "| Magnetic layers as first-class model parameters | `LayerMagnetism(rho_m, theta_m)` on `Layer` |\n", + "| All four spin channels in one calculation | `interface.polarized_reflectivity_profiles(q, model_id)` |\n", + "| Nuclear + magnetic SLD profile | `interface.magnetic_sld_profile(model_id)` |\n", + "| Per-file spin-channel detection | `detect_polarization_channel(path)` |\n", + "| One experiment = one dataset per channel | `PolarizedDataSet` |\n", + "| Simultaneous fit of all measured channels | `MultiFitter.fit_polarized(data)` |\n", + "\n", + "The magnetic parameters `rho_m` (magnetic SLD, in 10⁻⁶ Å⁻²) and `theta_m` (in-plane moment\n", + "angle, degrees) are ordinary `easyscience` `Parameter`s: they can be fixed or freed, bounded,\n", + "serialized, and fitted: together with the structural parameters, against **all measured spin\n", + "channels at once**. The refl1d backend computes the four spin cross-sections in a single kernel\n", + "evaluation and caches them per iteration, so an N-channel fit costs about as much as a\n", + "single-channel one.\n", + "\n", + "**Convention** (refl1d): with the default guide field (`Aguide = 270°`), a moment at\n", + "`theta_m = 270°` is *aligned* with the field: the non-spin-flip channels see\n", + "ρ ± ρ_M and the spin-flip channels vanish. Any other angle cants the moment and\n", + "produces spin-flip scattering. Polarized calculations require the **refl1d** calculator." + ] + }, + { + "cell_type": "markdown", + "id": "imports-md", + "metadata": {}, + "source": [ + "## 1. Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "imports-code", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "from easyreflectometry.calculators import CalculatorFactory\n", + "from easyreflectometry.data import PolarizedDataSet\n", + "from easyreflectometry.data import detect_polarization_channel\n", + "from easyreflectometry.data import load_as_dataset\n", + "from easyreflectometry.fitting import MultiFitter\n", + "from easyreflectometry.model import Model\n", + "from easyreflectometry.model import PercentageFwhm\n", + "from easyreflectometry.sample import Layer\n", + "from easyreflectometry.sample import LayerMagnetism\n", + "from easyreflectometry.sample import Material\n", + "from easyreflectometry.sample import Multilayer\n", + "from easyreflectometry.sample import Sample\n", + "\n", + "%matplotlib inline\n", + "\n", + "rng = np.random.default_rng(42)\n", + "\n", + "CHANNEL_LABELS = {'pp': 'R++ (up-up)', 'pm': 'R+- (up-down)', 'mp': 'R-+ (down-up)', 'mm': 'R-- (down-down)'}\n", + "CHANNEL_COLORS = {'pp': 'C0', 'pm': 'C2', 'mp': 'C3', 'mm': 'C1'}" + ] + }, + { + "cell_type": "markdown", + "id": "sample-md", + "metadata": {}, + "source": [ + "## 2. Build a Magnetic Sample\n", + "\n", + "A single ferromagnetic iron film on silicon, measured in vacuum:\n", + "\n", + "- **Vacuum** superphase\n", + "- **Fe film**, 200 Å: nuclear SLD 8.02·10⁻⁶ Å⁻², magnetic SLD `rho_m = 5.0`·10⁻⁶ Å⁻²\n", + " (bulk Fe), moment canted at `theta_m = 40°` so that all four channels are non-trivial\n", + "- **Si** substrate\n", + "\n", + "Attaching a `LayerMagnetism` to a layer is all that is needed: when the model is given a\n", + "calculator interface, magnetism is switched on automatically (and removing the last magnetic\n", + "layer switches it off again)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sample-code", + "metadata": {}, + "outputs": [], + "source": [ + "TRUTH = {'thickness': 200.0, 'rho_m': 5.0, 'theta_m': 40.0}\n", + "\n", + "def build_model(thickness=TRUTH['thickness'], rho_m=TRUTH['rho_m'], theta_m=TRUTH['theta_m'], name='PNR Model'):\n", + " \"\"\"Vacuum | Fe film (magnetic) | Si substrate, with a fresh refl1d calculator.\"\"\"\n", + " vacuum = Material(0.0, 0.0, 'Vacuum')\n", + " iron = Material(8.02, 0.0, 'Fe')\n", + " silicon = Material(2.07, 0.0, 'Si')\n", + "\n", + " superphase = Layer(vacuum, 0, 0, 'Vacuum superphase')\n", + " film = Layer(\n", + " iron, thickness, 5, 'Fe film',\n", + " magnetism=LayerMagnetism(rho_m=rho_m, theta_m=theta_m, name='Fe moment'),\n", + " )\n", + " substrate = Layer(silicon, 0, 3, 'Si substrate')\n", + "\n", + " sample = Sample(Multilayer(superphase), Multilayer(film), Multilayer(substrate), name='Fe on Si')\n", + " model = Model(sample, 1.0, 0.0, PercentageFwhm(2.0), name)\n", + "\n", + " interface = CalculatorFactory()\n", + " interface.switch('refl1d') # magnetism requires the refl1d backend\n", + " model.interface = interface\n", + " return model\n", + "\n", + "truth_model = build_model()\n", + "print(truth_model)\n", + "print(f'has_magnetism : {truth_model.has_magnetism}')\n", + "print(f'calculator magnetism flag: {truth_model.interface().include_magnetism} (enabled automatically)')" + ] + }, + { + "cell_type": "markdown", + "id": "simulate-md", + "metadata": {}, + "source": [ + "## 3. Simulate All Four Spin Channels\n", + "\n", + "`polarized_reflectivity_profiles` returns a dictionary keyed `'pp'`, `'pm'`, `'mp'`, `'mm'`.\n", + "All four cross-sections come from one refl1d kernel evaluation.\n", + "\n", + "With the moment canted at 40° the non-spin-flip channels split (they see the moment's\n", + "projection on the field) and the spin-flip channels pick up the perpendicular component.\n", + "For a non-chiral, non-absorptive sample `pm` and `mp` coincide by symmetry." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "simulate-code", + "metadata": {}, + "outputs": [], + "source": [ + "q = np.linspace(0.008, 0.22, 160)\n", + "channels_truth = truth_model.interface.polarized_reflectivity_profiles(q, truth_model.unique_name)\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 6))\n", + "for channel, reflectivity in channels_truth.items():\n", + " ax.plot(q, reflectivity, color=CHANNEL_COLORS[channel], lw=1.5, label=CHANNEL_LABELS[channel])\n", + "ax.set_yscale('log')\n", + "ax.set_xlabel('Q (Å⁻¹)')\n", + "ax.set_ylabel('Reflectivity')\n", + "ax.set_title('Simulated spin channels — Fe film, moment canted 40°')\n", + "ax.legend()\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "sld-md", + "metadata": {}, + "source": [ + "## 4. Nuclear and Magnetic SLD Profile\n", + "\n", + "`magnetic_sld_profile` returns `z`, nuclear ρ(z), magnetic ρ_M(z) and the moment angle θ_M(z).\n", + "The most intuitive PNR view adds the **spin-dependent potentials**: what each neutron spin\n", + "state actually \"sees\":\n", + "\n", + "$$\\rho_\\pm(z) = \\rho(z) \\pm \\rho_M(z)\\,\\cos\\bigl(\\theta_M(z) - A_\\text{guide}\\bigr)$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sld-code", + "metadata": {}, + "outputs": [], + "source": [ + "z, sld, rho_m_profile, theta_m_profile = truth_model.interface.magnetic_sld_profile(truth_model.unique_name)\n", + "\n", + "AGUIDE = 270.0 # refl1d default guide-field angle\n", + "projection = rho_m_profile * np.cos(np.radians(theta_m_profile - AGUIDE))\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 6))\n", + "ax.plot(z, sld, 'k-', lw=2, label='nuclear ρ(z)')\n", + "ax.plot(z, rho_m_profile, 'C4-', lw=2, label='magnetic ρ$_M$(z)')\n", + "ax.plot(z, sld + projection, 'C0--', lw=1.5, label='spin-up potential ρ + ρ$_M$cos(θ$_M$−A)')\n", + "ax.plot(z, sld - projection, 'C1--', lw=1.5, label='spin-down potential ρ − ρ$_M$cos(θ$_M$−A)')\n", + "ax.set_xlabel('z (Å)')\n", + "ax.set_ylabel('SLD (10⁻⁶ Å⁻²)')\n", + "ax.set_title('Nuclear and magnetic SLD profile')\n", + "ax.legend(loc='upper right', fontsize=9)\n", + "\n", + "ax2 = ax.twinx()\n", + "ax2.plot(z, theta_m_profile, 'C7:', lw=1.5)\n", + "ax2.set_ylabel('θ$_M$ (deg)', color='C7')\n", + "ax2.tick_params(axis='y', colors='C7')\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "sa-md", + "metadata": {}, + "source": [ + "## 5. Spin Asymmetry\n", + "\n", + "The spin asymmetry\n", + "\n", + "$$SA = \\frac{R^{++} - R^{--}}{R^{++} + R^{--}}$$\n", + "\n", + "removes most of the structural (nuclear) contribution and is visually far more sensitive to\n", + "weak magnetism than the raw reflectivities: the standard first look at any PNR measurement." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sa-code", + "metadata": {}, + "outputs": [], + "source": [ + "spin_asymmetry = (channels_truth['pp'] - channels_truth['mm']) / (channels_truth['pp'] + channels_truth['mm'])\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 4))\n", + "ax.plot(q, spin_asymmetry, 'C5-', lw=1.5)\n", + "ax.axhline(0, color='k', lw=0.5)\n", + "ax.set_xlabel('Q (Å⁻¹)')\n", + "ax.set_ylabel('(R⁺⁺ − R⁻⁻) / (R⁺⁺ + R⁻⁻)')\n", + "ax.set_title('Spin asymmetry')\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "files-md", + "metadata": {}, + "source": [ + "## 6. A Synthetic Experiment: One File per Channel\n", + "\n", + "Polarized measurements typically arrive as **one file per spin channel**. We simulate that:\n", + "3% relative noise on each channel, written to four separate files whose names carry the\n", + "conventional channel suffixes (`_uu`, `_dd`, `_ud`, `_du`).\n", + "\n", + "`detect_polarization_channel` identifies the channel of each file: from the ORSO header\n", + "(`instrument_settings.polarization`) when present, otherwise from filename tokens. Only the\n", + "four fully-analysed cross-sections `pp`/`pm`/`mp`/`mm` are ever assigned; partially-analysed\n", + "observables (`po`, `mo`, …) measure *channel sums* and are left for the user to decide.\n", + "(For GUI workflows, `Project.suggest_polarized_channel_assignment(paths)` wraps this per-file\n", + "and `Project.load_polarized_experiment({channel: path})` performs the load.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "files-code", + "metadata": {}, + "outputs": [], + "source": [ + "DATA_DIR = 'polarized_demo_data'\n", + "os.makedirs(DATA_DIR, exist_ok=True)\n", + "\n", + "FILE_SUFFIX = {'pp': 'uu', 'pm': 'ud', 'mp': 'du', 'mm': 'dd'}\n", + "NOISE = 0.03\n", + "\n", + "file_paths = []\n", + "for channel, reflectivity in channels_truth.items():\n", + " sigma = NOISE * reflectivity\n", + " noisy = np.clip(reflectivity + sigma * rng.standard_normal(len(q)), 1e-12, None)\n", + " path = os.path.join(DATA_DIR, f'fe_on_si_{FILE_SUFFIX[channel]}.dat')\n", + " np.savetxt(path, np.column_stack([q, noisy, sigma]), header='Qz (1/angstrom) R sR')\n", + " file_paths.append(path)\n", + "\n", + "print('Automatic channel detection:')\n", + "for path in file_paths:\n", + " detected = detect_polarization_channel(path)\n", + " print(f' {os.path.basename(path):24s} -> {detected.value if detected else \"(user must assign)\"}')" + ] + }, + { + "cell_type": "markdown", + "id": "dataset-md", + "metadata": {}, + "source": [ + "## 7. Group the Channels into a `PolarizedDataSet`\n", + "\n", + "A `PolarizedDataSet` holds one `DataSet1D` per measured channel (any subset of the four -\n", + "an NSF-only experiment would just have `pp` and `mm`) and one **shared model**. Channels are\n", + "kept in canonical order (pp, pm, mp, mm); the `channels` mapping is read-only, with validated\n", + "`set_channel` / `remove_channel` methods for editing.\n", + "\n", + "> **Note** — `DataSet1D.ye` stores **variances** (σ²), following the scipp convention. The\n", + "> text-file loader squares the error column for you.\n", + "\n", + "We start the fit model from deliberately wrong values: thickness 180 Å (truth 200),\n", + "`rho_m` 3.0 (truth 5.0), `theta_m` 60° (truth 40°)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dataset-code", + "metadata": {}, + "outputs": [], + "source": [ + "fit_model = build_model(thickness=180.0, rho_m=3.0, theta_m=60.0, name='PNR Fit Model')\n", + "\n", + "data = PolarizedDataSet(\n", + " name='Fe on Si (synthetic PNR)',\n", + " channels={detect_polarization_channel(path): load_as_dataset(path) for path in file_paths},\n", + " model=fit_model,\n", + ")\n", + "print(data)\n", + "print(f'channels: {[channel.value for channel in data.available_channels]}')\n", + "print(f\"points per channel: {len(data['pp'].x)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "fit-md", + "metadata": {}, + "source": [ + "## 8. Simultaneous Multi-Channel Fit\n", + "\n", + "`MultiFitter.fit_polarized(data)` fits **all measured channels at once** against the one\n", + "shared model:\n", + "\n", + "- structural parameters (thickness, roughness, nuclear SLD, scale, background) are common to\n", + " every channel automatically;\n", + "- the magnetic parameters shape the channels through the spin-dependent kernel: the\n", + " non-spin-flip splitting pins `rho_m·cos θ_m` while the spin-flip channels pin the\n", + " perpendicular component, so `rho_m` and `theta_m` are individually well-determined;\n", + "- each iteration costs a single refl1d kernel evaluation thanks to the four-channel cache.\n", + "\n", + "It returns one `FitResults` per channel." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fit-code", + "metadata": {}, + "outputs": [], + "source": [ + "film_layer = fit_model.sample[1].layers[0]\n", + "\n", + "film_layer.thickness.fixed = False\n", + "film_layer.thickness.bounds = (150, 250)\n", + "film_layer.magnetism.rho_m.fixed = False\n", + "film_layer.magnetism.rho_m.bounds = (0, 8)\n", + "film_layer.magnetism.theta_m.fixed = False\n", + "film_layer.magnetism.theta_m.bounds = (0, 90)\n", + "\n", + "print('Free parameters (start values):')\n", + "for parameter in fit_model.get_fit_parameters():\n", + " print(f' {parameter.name:12s} = {float(parameter.value):8.3f} bounds={parameter.bounds}')\n", + "\n", + "fitter = MultiFitter(fit_model)\n", + "results = fitter.fit_polarized(data)\n", + "\n", + "print(f'\\nsuccess: {all(result.success for result in results.values())}')\n", + "print(f'reduced chi² (all channels): {fitter.reduced_chi:.3f}')\n", + "\n", + "print(f'\\n{\"Parameter\":12s} {\"truth\":>10s} {\"start\":>10s} {\"fitted\":>10s}')\n", + "print('-' * 46)\n", + "starts = {'thickness': 180.0, 'rho_m': 3.0, 'theta_m': 60.0}\n", + "fitted = {\n", + " 'thickness': float(film_layer.thickness.value),\n", + " 'rho_m': float(film_layer.magnetism.rho_m.value),\n", + " 'theta_m': float(film_layer.magnetism.theta_m.value),\n", + "}\n", + "for key in TRUTH:\n", + " print(f'{key:12s} {TRUTH[key]:>10.3f} {starts[key]:>10.3f} {fitted[key]:>10.3f}')" + ] + }, + { + "cell_type": "markdown", + "id": "plots-md", + "metadata": {}, + "source": [ + "## 9. Fitted Curves per Channel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "plots-code", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axes = plt.subplots(2, 2, figsize=(12, 8), sharex=True)\n", + "\n", + "for ax, channel in zip(axes.flat, data.available_channels):\n", + " dataset = data[channel]\n", + " fitted_curve = fit_model.interface.reflectivity_profile_channel(dataset.x, fit_model.unique_name, channel)\n", + " ax.errorbar(\n", + " dataset.x, dataset.y, yerr=np.sqrt(dataset.ye), # ye holds variances\n", + " fmt='o', ms=2.5, alpha=0.45, color=CHANNEL_COLORS[channel.value], label='synthetic data',\n", + " )\n", + " ax.plot(dataset.x, fitted_curve, 'k-', lw=1.5, label='fit')\n", + " ax.set_yscale('log')\n", + " ax.set_title(CHANNEL_LABELS[channel.value])\n", + " ax.legend(fontsize=9)\n", + "\n", + "for ax in axes[1]:\n", + " ax.set_xlabel('Q (Å⁻¹)')\n", + "for ax in axes[:, 0]:\n", + " ax.set_ylabel('Reflectivity')\n", + "\n", + "fig.suptitle('Simultaneous four-channel fit — all channels share one model', y=1.0)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "sa-fit-md", + "metadata": {}, + "source": [ + "### Spin asymmetry: data vs fit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sa-fit-code", + "metadata": {}, + "outputs": [], + "source": [ + "fitted_channels = fit_model.interface.polarized_reflectivity_profiles(q, fit_model.unique_name)\n", + "\n", + "sa_data = (data['pp'].y - data['mm'].y) / (data['pp'].y + data['mm'].y)\n", + "sa_fit = (fitted_channels['pp'] - fitted_channels['mm']) / (fitted_channels['pp'] + fitted_channels['mm'])\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 4))\n", + "ax.plot(q, sa_data, 'o', ms=3, alpha=0.5, color='C5', label='synthetic data')\n", + "ax.plot(q, sa_fit, 'k-', lw=1.5, label='fit')\n", + "ax.axhline(0, color='k', lw=0.5)\n", + "ax.set_xlabel('Q (Å⁻¹)')\n", + "ax.set_ylabel('(R⁺⁺ − R⁻⁻) / (R⁺⁺ + R⁻⁻)')\n", + "ax.set_title('Spin asymmetry — data vs simultaneous fit')\n", + "ax.legend()\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "summary-md", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "- **`LayerMagnetism(rho_m, theta_m)`** makes a layer magnetic; both are fittable, bounded,\n", + " serializable `Parameter`s. Magnetism is enabled on the calculator automatically (refl1d\n", + " only).\n", + "- **`polarized_reflectivity_profiles`** / **`magnetic_sld_profile`** give the four spin\n", + " channels and the nuclear + magnetic depth profile in one call each.\n", + "- **`detect_polarization_channel`** assigns spin channels from ORSO headers or filename\n", + " tokens; partially-analysed observables (`po`/`mo` = channel sums) are never auto-assigned.\n", + "- **`PolarizedDataSet`** groups per-channel datasets (2-channel NSF-only works the same way:\n", + " provide just `pp` and `mm`) under one shared model; `ye` holds variances.\n", + "- **`MultiFitter.fit_polarized`** fits every measured channel simultaneously and recovered\n", + " thickness, `rho_m` and `theta_m` here to within a fraction of a percent, at roughly the\n", + " cost of a single-channel fit (four-channel cache: one kernel evaluation per iteration).\n", + "\n", + "For file-based / GUI workflows the same functionality is reachable through\n", + "`Project.suggest_polarized_channel_assignment(paths)` and\n", + "`Project.load_polarized_experiment({channel: path})`." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/easyreflectometry/calculators/calculator_base.py b/src/easyreflectometry/calculators/calculator_base.py index 18fbd1a6..dda986b1 100644 --- a/src/easyreflectometry/calculators/calculator_base.py +++ b/src/easyreflectometry/calculators/calculator_base.py @@ -201,6 +201,29 @@ def reflectity_profile(self, x_array: np.ndarray, model_id: str) -> np.ndarray: """ return self._wrapper.calculate(x_array, model_id) + def reflectivity_profile_channel(self, x_array: np.ndarray, model_id: str, channel) -> np.ndarray: + """Determine the reflectivity profile of one explicit spin channel. + + Unlike `polarization_channel` (global calculator state), the channel is an + argument, so several channels can be evaluated against the same model — + one per dataset in a simultaneous multi-channel fit. + + Parameters + ---------- + x_array : np.ndarray + Points to be calculated at. + model_id : str + The model id. + channel : PolarizationChannel | str + One of 'pp', 'pm', 'mp', 'mm' (or the corresponding enum member). + + Returns + ------- + np.ndarray + Reflectivity of the requested channel at q. + """ + return self._wrapper.calculate_channel(x_array, model_id, channel) + def polarized_reflectivity_profiles(self, x_array: np.ndarray, model_id: str) -> dict[str, np.ndarray]: """Determines the reflectivity profiles of all four spin channels for the given range and model. diff --git a/src/easyreflectometry/calculators/factory.py b/src/easyreflectometry/calculators/factory.py index c05618d1..b39f1daa 100644 --- a/src/easyreflectometry/calculators/factory.py +++ b/src/easyreflectometry/calculators/factory.py @@ -8,6 +8,8 @@ from easyreflectometry.calculators import CalculatorBase +from .polarization import PolarizationChannel + class CalculatorFactory(InterfaceFactoryTemplate): def __init__(self): @@ -26,6 +28,10 @@ def polarized_reflectivity_profiles(self, x_array, model_id: str) -> dict: """Reflectivity profiles of all four spin channels ('pp', 'pm', 'mp', 'mm').""" return self().polarized_reflectivity_profiles(x_array, model_id) + def reflectivity_profile_channel(self, x_array, model_id: str, channel: PolarizationChannel | str): + """Reflectivity profile of one explicit spin channel ('pp', 'pm', 'mp' or 'mm').""" + return self().reflectivity_profile_channel(x_array, model_id, channel) + def magnetic_sld_profile(self, model_id: str) -> tuple: """Nuclear and magnetic sld profiles: z, sld(z), rhoM(z) and thetaM(z).""" return self().magnetic_sld_profile(model_id) @@ -51,3 +57,28 @@ def __fit_func(*args, **kwargs): return self().reflectity_profile(*args, **kwargs) return __fit_func + + def fit_func_for_channel(self, channel: PolarizationChannel | str) -> Callable: + """A fit function evaluating one explicit spin channel. + + Used for simultaneous multi-channel fitting: each channel dataset gets its + own fit function while all of them share the same model (and hence the + same parameters). + + Parameters + ---------- + channel : PolarizationChannel | str + One of 'pp', 'pm', 'mp', 'mm' (or the corresponding enum member). + + Returns + ------- + Callable + Function of (x_array, model_id) returning the channel reflectivity. + """ + channel = PolarizationChannel(channel) + + def __fit_func(x_array, model_id): + """Fit func for one spin channel.""" + return self().reflectivity_profile_channel(x_array, model_id, channel) + + return __fit_func diff --git a/src/easyreflectometry/calculators/polarization.py b/src/easyreflectometry/calculators/polarization.py index f70d04e1..16717bd2 100644 --- a/src/easyreflectometry/calculators/polarization.py +++ b/src/easyreflectometry/calculators/polarization.py @@ -17,13 +17,17 @@ class PolarizationChannel(str, Enum): MM = 'mm' # non-spin-flip, down-down -# Mapping to refl1d cross-section indices: PolarizedNeutronProbe.xs is documented as -# "a sequence pp, pm, mp and mm". The pp/mm assignment is additionally pinned by -# physics tests; pm vs mp rests on the refl1d docstring alone (they are identical by -# symmetry for non-chiral, non-absorptive samples). +# Mapping to refl1d cross-section indices. refl1d returns the polarized +# cross-sections in the order of `PolarizedNeutronProbe._xs_names`, which is +# ['mm', 'mp', 'pm', 'pp'] (refl1d/probe/probe.py); `magnetic_amplitude` returns +# (--, -+, +-, ++) accordingly (refl1d/sample/reflectivity.py). The pp/mm +# assignment is pinned by a physics test (moment aligned with the guide field: +# pp must see rho + rhoM); pm vs mp rests on the refl1d source alone (they are +# identical by symmetry for non-chiral, non-absorptive samples). +# The key order (pp, pm, mp, mm) is the canonical channel order used throughout. POLARIZATION_CHANNEL_TO_INDEX = { - PolarizationChannel.PP: 0, - PolarizationChannel.PM: 1, - PolarizationChannel.MP: 2, - PolarizationChannel.MM: 3, + PolarizationChannel.PP: 3, + PolarizationChannel.PM: 2, + PolarizationChannel.MP: 1, + PolarizationChannel.MM: 0, } diff --git a/src/easyreflectometry/calculators/refl1d/wrapper.py b/src/easyreflectometry/calculators/refl1d/wrapper.py index 883a56e4..ec27a3ef 100644 --- a/src/easyreflectometry/calculators/refl1d/wrapper.py +++ b/src/easyreflectometry/calculators/refl1d/wrapper.py @@ -28,11 +28,18 @@ def __init__(self): # Magnetic values per layer name, kept outside the slabs so they survive # magnetism being toggled off/on and can be set before it is enabled. self._layer_magnetism: dict[str, dict[str, float]] = {} + # Per-model cache of polarized reflectivities: all four cross-sections come + # from a single kernel evaluation, so a simultaneous multi-channel fit costs + # one evaluation per iteration instead of one per channel. Keyed on a token + # of every model input; entries per (q, dq) grid, so channels measured on + # different grids coexist within one iteration. + self._polarized_cache: dict[str, dict] = {} def reset_storage(self): """Reset the storage area (including stored magnetic values) to blank.""" super().reset_storage() self._layer_magnetism = {} + self._polarized_cache = {} def create_material(self, name: str): """Create a material using SLD. @@ -277,7 +284,8 @@ def calculate(self, q_array: np.ndarray, model_name: str) -> np.ndarray: """ if self._magnetism: reflectivities = self._polarized_reflectivities(q_array, model_name) - return reflectivities[POLARIZATION_CHANNEL_TO_INDEX[self._polarization_channel]] + # Copy: the arrays live in the polarized cache and must not be mutated. + return reflectivities[POLARIZATION_CHANNEL_TO_INDEX[self._polarization_channel]].copy() sample = _build_sample(self.storage, model_name) # smearing() returns sigma, which is exactly what refl1d's probe.dQ expects. @@ -314,12 +322,60 @@ def calculate_polarized(self, q_array: np.ndarray, model_name: str) -> dict[str, '(`include_magnetism = True` on the calculator / `magnetism = True` on the wrapper).' ) reflectivities = self._polarized_reflectivities(q_array, model_name) - return {channel.value: reflectivities[index] for channel, index in POLARIZATION_CHANNEL_TO_INDEX.items()} + # Copies: the arrays live in the polarized cache and must not be mutated. + return {channel.value: reflectivities[index].copy() for channel, index in POLARIZATION_CHANNEL_TO_INDEX.items()} + + def _model_state_token(self, model_name: str) -> tuple: + """A token of every model input that affects the reflectivity. + + Two calls with equal tokens (and equal q/dq grids) are guaranteed to + produce the same reflectivity, so cached cross-sections can be reused. + The resolution function needs no entry here — it enters through the dq + part of the per-grid cache key. Must be extended whenever a new slab or + material attribute starts reaching the kernel. + """ + model = self.storage['model'][model_name] + values: list = [model['scale'], model['bkg']] + for item in model['items']: + values.append(item.repeat.value) + for slab in item.stack: + values.extend(( + slab.thickness.value, + slab.interface.value, + slab.material.rho.value, + slab.material.irho.value, + )) + if slab.magnetism is None: + values.append(None) + else: + values.extend((slab.magnetism.rhoM.value, slab.magnetism.thetaM.value)) + return tuple(values) def _polarized_reflectivities(self, q_array: np.ndarray, model_name: str) -> list: - """Reflectivity of the four spin cross-sections, in refl1d order (pp, pm, mp, mm).""" + """Reflectivity of the four spin cross-sections, in refl1d order (mm, mp, pm, pp). + + The list follows `PolarizedNeutronProbe._xs_names`; use + `POLARIZATION_CHANNEL_TO_INDEX` to pick a channel out of it. + Results are cached per model state and (q, dq) grid; see `_polarized_cache`. + """ + # Normalized dtype plus explicit shape in the key: raw bytes alone do not + # uniquely identify an ndarray (equal bytes can encode different + # dtype/shape combinations), which could return a wrong-length hit. + q_array = np.asarray(q_array, dtype=np.float64) + dq_array = np.asarray(self._resolution_function.smearing(q_array), dtype=np.float64) + + token = self._model_state_token(model_name) + grid_key = (q_array.shape, q_array.tobytes(), dq_array.shape, dq_array.tobytes()) + cache = self._polarized_cache.get(model_name) + if cache is not None and cache['token'] == token: + cached = cache['entries'].get(grid_key) + if cached is not None: + return cached + else: + cache = {'token': token, 'entries': {}} + self._polarized_cache[model_name] = cache + sample = _build_sample(self.storage, model_name) - dq_array = self._resolution_function.smearing(q_array) polarized_probe = _get_polarized_probe( q_array=q_array, dq_array=dq_array, @@ -336,6 +392,7 @@ def _polarized_reflectivities(self, q_array: np.ndarray, model_name: str) -> lis for channel, index in POLARIZATION_CHANNEL_TO_INDEX.items(): if len(reflectivities[index]) != len(q_array) or not np.all(np.isfinite(reflectivities[index])): raise RuntimeError(f'refl1d returned a malformed {channel.value} cross-section.') + cache['entries'][grid_key] = reflectivities return reflectivities def sld_profile(self, model_name: str) -> Tuple[np.ndarray, np.ndarray]: @@ -449,10 +506,17 @@ def _get_polarized_probe( for _ in range(4) ] - # Create polarized probe and work around initialization bug - polarized_probe = names.PolarizedNeutronQProbe.__new__(names.PolarizedNeutronQProbe) - polarized_probe._union_cache_key = None # Initialize missing attribute - polarized_probe.__init__(xs=four_probes, name='polarized') + try: + polarized_probe = names.PolarizedNeutronQProbe(xs=four_probes, name='polarized') + except AttributeError: + # refl1d 1.0.0 bug: PolarizedQProbe.__init__ calls _calculate_union(), which + # reads self._union_cache_key before the attribute is ever assigned (the + # non-Q PolarizedNeutronProbe assigns it in __init__; the Q variant does + # not). Pre-seed the attribute and re-run __init__. The try/except makes + # the workaround self-removing once refl1d fixes the initialization. + polarized_probe = names.PolarizedNeutronQProbe.__new__(names.PolarizedNeutronQProbe) + polarized_probe._union_cache_key = None + polarized_probe.__init__(xs=four_probes, name='polarized') return polarized_probe diff --git a/src/easyreflectometry/calculators/wrapper_base.py b/src/easyreflectometry/calculators/wrapper_base.py index 4e2a4aab..feaab335 100644 --- a/src/easyreflectometry/calculators/wrapper_base.py +++ b/src/easyreflectometry/calculators/wrapper_base.py @@ -378,6 +378,35 @@ def polarization_channel(self, channel: PolarizationChannel | str) -> None: raise ValueError(f"Selecting the '{channel.value}' channel requires magnetism to be enabled.") self._polarization_channel = channel + def calculate_channel(self, q_array: np.ndarray, model_name: str, channel: PolarizationChannel | str) -> np.ndarray: + """For a given q array calculate the reflectivity of one explicit spin channel. + + Unlike the `polarization_channel` property (global calculator state used by + `calculate`), the channel is passed explicitly, so several channels can be + evaluated against the same model, e.g. one per dataset in a simultaneous + multi-channel fit. + + Parameters + ---------- + q_array : np.ndarray + Array of data points to be calculated. + model_name : str + The model name. + channel : PolarizationChannel | str + One of 'pp', 'pm', 'mp', 'mm' (or the corresponding enum member). + + Returns + ------- + np.ndarray + Reflectivity of the requested channel at q. + """ + channel = PolarizationChannel(channel) + if not self._magnetism: + if channel is PolarizationChannel.PP: + return self.calculate(q_array, model_name) + raise ValueError(f"Calculating the '{channel.value}' channel requires magnetism to be enabled.") + return self.calculate_polarized(q_array, model_name)[channel.value] + def calculate_polarized(self, q_array: np.ndarray, model_name: str) -> dict[str, np.ndarray]: """For a given q array calculate the reflectivity of all four spin channels. diff --git a/src/easyreflectometry/data/__init__.py b/src/easyreflectometry/data/__init__.py index 0d058120..e63470eb 100644 --- a/src/easyreflectometry/data/__init__.py +++ b/src/easyreflectometry/data/__init__.py @@ -6,6 +6,8 @@ from .measurement import load from .measurement import load_as_dataset from .measurement import merge_datagroups +from .polarized import PolarizedDataSet +from .polarized import detect_polarization_channel __all__ = [ 'load', @@ -13,4 +15,6 @@ 'merge_datagroups', 'ProjectData', 'DataSet1D', + 'PolarizedDataSet', + 'detect_polarization_channel', ] diff --git a/src/easyreflectometry/data/polarized.py b/src/easyreflectometry/data/polarized.py new file mode 100644 index 00000000..ae8fcac8 --- /dev/null +++ b/src/easyreflectometry/data/polarized.py @@ -0,0 +1,284 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + +"""Polarized experiment data: per-spin-channel datasets and channel detection. + +A polarized neutron reflectometry experiment measures up to four spin +cross-sections (non-spin-flip 'pp'/'mm', spin-flip 'pm'/'mp'), typically stored +as one file per channel. `PolarizedDataSet` groups those per-channel +`DataSet1D` objects into a single experiment sharing one model. +""" + +from __future__ import annotations + +import os +import re +from types import MappingProxyType +from typing import Mapping +from typing import Optional +from typing import Union + +from easyreflectometry.calculators.polarization import POLARIZATION_CHANNEL_TO_INDEX +from easyreflectometry.calculators.polarization import PolarizationChannel + +from .data_store import DataSet1D + +# ORSO instrument_settings.polarization values → spin channel. +# Only the four fully-analysed cross-sections are mapped. Partially-analysed +# observables are deliberately NOT: 'po' (incident plus, no outgoing analysis) +# measures the sum pp + pm — a physically different observable from the pp +# cross-section — and likewise 'mo' = mp + mm; 'op'/'om' analyse only the +# outgoing spin; 'unpolarized' is no channel at all. A header declaring any of +# those suppresses the filename fallback and the user must assign (or the data +# must be modelled as a channel sum, which this formalism does not do yet). +_ORSO_POLARIZATION_TO_CHANNEL = { + 'pp': PolarizationChannel.PP, + 'pm': PolarizationChannel.PM, + 'mp': PolarizationChannel.MP, + 'mm': PolarizationChannel.MM, +} + +# Filename tokens → spin channel (used when there is no ORSO header). +_TOKEN_TO_CHANNEL = { + 'pp': PolarizationChannel.PP, + 'uu': PolarizationChannel.PP, + 'upup': PolarizationChannel.PP, + 'plusplus': PolarizationChannel.PP, + 'mm': PolarizationChannel.MM, + 'dd': PolarizationChannel.MM, + 'downdown': PolarizationChannel.MM, + 'minusminus': PolarizationChannel.MM, + 'pm': PolarizationChannel.PM, + 'ud': PolarizationChannel.PM, + 'updown': PolarizationChannel.PM, + 'mp': PolarizationChannel.MP, + 'du': PolarizationChannel.MP, + 'downup': PolarizationChannel.MP, +} + +# Single tokens for 2-channel (NSF-only) naming like 'sample_up.dat' / 'sample_down.dat'. +_SINGLE_TOKEN_TO_CHANNEL = { + 'u': PolarizationChannel.PP, + 'up': PolarizationChannel.PP, + 'p': PolarizationChannel.PP, + 'plus': PolarizationChannel.PP, + 'd': PolarizationChannel.MM, + 'down': PolarizationChannel.MM, + 'm': PolarizationChannel.MM, + 'minus': PolarizationChannel.MM, +} + +# Adjacent token pairs like 'up_down' → channel. +_TOKEN_PAIR_TO_CHANNEL = { + ('up', 'up'): PolarizationChannel.PP, + ('up', 'down'): PolarizationChannel.PM, + ('down', 'up'): PolarizationChannel.MP, + ('down', 'down'): PolarizationChannel.MM, + ('u', 'u'): PolarizationChannel.PP, + ('u', 'd'): PolarizationChannel.PM, + ('d', 'u'): PolarizationChannel.MP, + ('d', 'd'): PolarizationChannel.MM, + ('plus', 'plus'): PolarizationChannel.PP, + ('plus', 'minus'): PolarizationChannel.PM, + ('minus', 'plus'): PolarizationChannel.MP, + ('minus', 'minus'): PolarizationChannel.MM, +} + + +class PolarizedDataSet: + """A polarized experiment: one dataset per measured spin channel, one shared model. + + Parameters + ---------- + name : str, optional + Name of the experiment. By default, 'PolarizedSeries'. + channels : dict[PolarizationChannel | str, DataSet1D] + The measured channels; at least one. Keys are 'pp', 'pm', 'mp', 'mm' + (or the corresponding enum members). Two-channel NSF-only experiments + simply provide 'pp' and 'mm'. + model : Model, optional + The model shared by all channels. By default, None. + """ + + def __init__( + self, + name: str = 'PolarizedSeries', + channels: Optional[dict[Union[PolarizationChannel, str], DataSet1D]] = None, + model=None, + ): + if not channels: + raise ValueError('A PolarizedDataSet requires at least one channel dataset.') + normalized: dict[PolarizationChannel, DataSet1D] = {} + for channel, dataset in channels.items(): + channel = PolarizationChannel(channel) + if channel in normalized: + raise ValueError(f"Duplicate channel '{channel.value}'.") + normalized[channel] = self._validated_dataset(channel, dataset) + self._channels = self._in_canonical_order(normalized) + self.name = name + self.model = model + + @staticmethod + def _validated_dataset(channel: PolarizationChannel, dataset: DataSet1D) -> DataSet1D: + if not isinstance(dataset, DataSet1D): + raise ValueError(f"Channel '{channel.value}' must be a DataSet1D, got {type(dataset).__name__}.") + return dataset + + @staticmethod + def _in_canonical_order(channels: dict[PolarizationChannel, DataSet1D]) -> dict[PolarizationChannel, DataSet1D]: + # Canonical channel order: pp, pm, mp, mm. + return {channel: channels[channel] for channel in POLARIZATION_CHANNEL_TO_INDEX if channel in channels} + + @property + def model(self): + """The model shared by all channels.""" + return self._model + + @model.setter + def model(self, new_model) -> None: + self._model = new_model + for dataset in self._channels.values(): + dataset.model = new_model + + @property + def channels(self) -> Mapping[PolarizationChannel, DataSet1D]: + """The measured channels, in canonical order (pp, pm, mp, mm). + + Read-only view: use :meth:`set_channel` / :meth:`remove_channel` to + modify the channel set, so validation and model propagation apply. + """ + return MappingProxyType(self._channels) + + @property + def available_channels(self) -> list[PolarizationChannel]: + """The measured channels, in canonical order (pp, pm, mp, mm).""" + return list(self._channels.keys()) + + def set_channel(self, channel: Union[PolarizationChannel, str], dataset: DataSet1D) -> None: + """Add or replace one channel dataset. + + The dataset is validated, adopts the shared model, and the canonical + channel order is preserved. + + Parameters + ---------- + channel : Union[PolarizationChannel, str] + One of 'pp', 'pm', 'mp', 'mm' (or the corresponding enum member). + dataset : DataSet1D + The channel data. + """ + channel = PolarizationChannel(channel) + dataset = self._validated_dataset(channel, dataset) + dataset.model = self._model + merged = dict(self._channels) + merged[channel] = dataset + self._channels = self._in_canonical_order(merged) + + def remove_channel(self, channel: Union[PolarizationChannel, str]) -> None: + """Remove one channel dataset; the last remaining channel cannot be removed. + + Parameters + ---------- + channel : Union[PolarizationChannel, str] + One of 'pp', 'pm', 'mp', 'mm' (or the corresponding enum member). + """ + channel = PolarizationChannel(channel) + if channel not in self._channels: + raise ValueError(f"No '{channel.value}' channel in this dataset.") + if len(self._channels) == 1: + raise ValueError('A PolarizedDataSet requires at least one channel dataset.') + del self._channels[channel] + + def __getitem__(self, channel: Union[PolarizationChannel, str]) -> DataSet1D: + return self._channels[PolarizationChannel(channel)] + + def __contains__(self, channel: Union[PolarizationChannel, str]) -> bool: + try: + return PolarizationChannel(channel) in self._channels + except ValueError: + return False + + def __len__(self) -> int: + return len(self._channels) + + @property + def is_experiment(self) -> bool: + """Is experiment.""" + return self._model is not None + + @property + def is_simulation(self) -> bool: + """Is simulation.""" + return self._model is None + + def __repr__(self) -> str: + channel_names = ', '.join(channel.value for channel in self._channels) + return f"Polarized dataset '{self.name}' with channels: {channel_names}" + + +def detect_polarization_channel(path: str) -> Optional[PolarizationChannel]: + """Detect the spin channel of a data file. + + Tries the ORSO header (`data_source.measurement.instrument_settings.polarization`) + first, then filename heuristics ('_uu'/'_pp'/'_up' → pp, '_dd'/'_mm'/'_down' → mm, + '_ud'/'_pm' → pm, '_du'/'_mp' → mp, ...). Returns None when neither yields a + channel; a GUI should then ask the user to assign one. + + Parameters + ---------- + path : str + Path to the data file. + + Returns + ------- + Optional[PolarizationChannel] + The detected channel, or None. + """ + header_declared, channel = _channel_from_orso_header(path) + if channel is not None: + return channel + if header_declared: + # The header explicitly declares a non-channel polarization (e.g. + # 'unpolarized'): trust it over any channel-looking filename tokens. + return None + return _channel_from_filename(path) + + +def _channel_from_orso_header(path: str) -> tuple[bool, Optional[PolarizationChannel]]: + """Read the polarization of the first dataset in an ORSO file. + + Returns + ------- + tuple[bool, Optional[PolarizationChannel]] + (header declares a polarization, mapped channel or None). The flag is + False when the file is unreadable or carries no polarization field. + """ + try: + from orsopy.fileio import orso + + orso_data = orso.load_orso(str(path)) + polarization = orso_data[0].info.data_source.measurement.instrument_settings.polarization + except Exception: + return False, None + if polarization is None: + return False, None + value = getattr(polarization, 'value', polarization) + return True, _ORSO_POLARIZATION_TO_CHANNEL.get(str(value).lower()) + + +def _channel_from_filename(path: str) -> Optional[PolarizationChannel]: + """Guess the spin channel from separator-delimited tokens in the file name.""" + basename = os.path.splitext(os.path.basename(str(path)))[0].lower() + tokens = [token for token in re.split(r'[^a-z0-9]+', basename) if token] + + # Prefer the most specific match, scanning from the end (suffix convention). + for token in reversed(tokens): + if token in _TOKEN_TO_CHANNEL: + return _TOKEN_TO_CHANNEL[token] + for first, second in reversed(list(zip(tokens, tokens[1:]))): + if (first, second) in _TOKEN_PAIR_TO_CHANNEL: + return _TOKEN_PAIR_TO_CHANNEL[(first, second)] + for token in reversed(tokens): + if token in _SINGLE_TOKEN_TO_CHANNEL: + return _SINGLE_TOKEN_TO_CHANNEL[token] + return None diff --git a/src/easyreflectometry/fitting.py b/src/easyreflectometry/fitting.py index 0efaaaa5..a1958d8b 100644 --- a/src/easyreflectometry/fitting.py +++ b/src/easyreflectometry/fitting.py @@ -14,6 +14,7 @@ from easyscience.fitting.multi_fitter import MultiFitter as EasyScienceMultiFitter from easyreflectometry.data import DataSet1D +from easyreflectometry.data import PolarizedDataSet from easyreflectometry.model import Model _VALID_OBJECTIVES = ('legacy_mask', 'mighell', 'hybrid', 'auto') @@ -357,6 +358,123 @@ def fit_single_data_set_1d(self, data: DataSet1D, objective: str | None = None) ] return result + def fit_polarized(self, data: PolarizedDataSet, objective: str | None = None) -> dict[str, FitResults]: + """Fit all measured spin channels of a polarized experiment simultaneously. + + Each channel dataset gets its own fit function evaluating the + corresponding spin cross-section, while every channel shares the single + model — so structural parameters (thickness, roughness, nuclear SLD, + scale, background) are common, and the magnetic parameters + (`rho_m`/`theta_m`) are constrained by all channels at once. The refl1d + backend computes all four cross-sections in one kernel evaluation and + caches them per iteration, so fitting N channels costs about as much as + fitting one. + + Parameters + ---------- + data : PolarizedDataSet + The polarized experiment (its model must be the model this fitter + was constructed with). Note that per-channel ``ye`` stores + variances (σ²), not standard deviations. + objective : str | None, optional + Per-call override for the zero-variance objective. + If ``None``, uses the instance default set at construction. By default, None. + + Returns + ------- + dict[str, FitResults] + Fit results per channel, keyed 'pp', 'pm', 'mp', 'mm' (measured + channels only, in that order). + """ + obj = _validate_objective(objective) if objective is not None else self._objective + if len(self._models) != 1: + raise ValueError('Polarized fitting requires a MultiFitter constructed with exactly one model.') + model = self._models[0] + if data.model is not model: + raise ValueError('PolarizedDataSet.model must be the model this fitter was constructed with.') + channels = data.available_channels + for channel in channels: + if data[channel].model is not model: + raise ValueError(f"The '{channel.value}' channel dataset is bound to a different model than the fitter's.") + + def func_wrapper(func, unique_name): + """Func wrapper.""" + + def wrapped(*args, **kwargs): + """Wrapped function.""" + return func(*args, unique_name, **kwargs) + + return wrapped + + channel_fit_funcs = [ + func_wrapper(model.interface.fit_func_for_channel(channel), model.unique_name) for channel in channels + ] + # One fit function per channel, all bound to the single model. Constructed + # per call because the channel set comes from the data; the minimizer + # selection and its generic settings (tolerance, max_evaluations) are + # carried over. Engine-specific settings applied directly to the minimizer + # instance of this fitter would not be. + polarized_fitter = EasyScienceMultiFitter([model], channel_fit_funcs) + polarized_fitter.switch_minimizer(self.easy_science_multi_fitter.minimizer.enum) + polarized_fitter.tolerance = self.easy_science_multi_fitter.tolerance + polarized_fitter.max_evaluations = self.easy_science_multi_fitter.max_evaluations + + x = [] + y = [] + dy = [] + original_arrays = [] + for channel in channels: + dataset = data[channel] + x_vals = np.asarray(dataset.x) + y_vals = np.asarray(dataset.y) + variances = np.asarray(dataset.ye) + + x_out, y_eff, weights, stats = _prepare_fit_arrays(x_vals, y_vals, variances, obj) + + if stats['masked'] > 0: + warnings.warn( + f'Masked {stats["masked"]} data point(s) in channel {channel.value} due to zero variance during fitting.', + UserWarning, + ) + if stats.get('transformed_all_points'): + warnings.warn( + f'Applied Mighell transform to all {len(y_vals)} point(s) in channel {channel.value} during fitting.', + UserWarning, + ) + elif stats['mighell_substituted'] > 0: + warnings.warn( + f'Applied Mighell substitution to {stats["mighell_substituted"]} ' + f'zero-variance point(s) in channel {channel.value} during fitting.', + UserWarning, + ) + if obj == 'legacy_mask' and len(x_out) == 0: + raise ValueError(f'Cannot fit channel {channel.value}: all points have zero variance.') + + x.append(x_out) + y.append(y_eff) + dy.append(weights) + original_arrays.append({'x': x_vals, 'y': y_vals, 'variances': variances}) + + results = polarized_fitter.fit(x, y, weights=dy) + self._fit_results = list(results) + + self._classical_fit_metrics = [] + for index, (channel, result) in enumerate(zip(channels, results)): + original = original_arrays[index] + model_curve = channel_fit_funcs[index](original['x']) + sigma_classical = np.sqrt(np.clip(original['variances'], 0.0, None)) + n_classical_points = int(np.sum(original['variances'] > 0.0)) + classical_chi2 = _compute_weighted_chi2(original['y'], model_curve, sigma_classical) + self._classical_fit_metrics.append({ + 'classical_chi2': classical_chi2, + 'classical_reduced_chi': _compute_reduced_chi2(classical_chi2, n_classical_points, result.n_pars), + 'objective_chi2': float(result.chi2), + 'objective_reduced_chi': _fit_result_reduced_chi(result, np.size(result.x)), + 'n_classical_points': n_classical_points, + }) + + return {channel.value: result for channel, result in zip(channels, results)} + def mcmc_sample( self, data: sc.DataGroup, diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index 456e9803..0bfa9fd5 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -19,7 +19,10 @@ from scipp import DataGroup from easyreflectometry.calculators import CalculatorFactory +from easyreflectometry.calculators import PolarizationChannel from easyreflectometry.data import DataSet1D +from easyreflectometry.data import PolarizedDataSet +from easyreflectometry.data import detect_polarization_channel from easyreflectometry.data import load_as_dataset from easyreflectometry.data.measurement import extract_orso_title from easyreflectometry.data.measurement import load_data_from_orso_file @@ -343,12 +346,12 @@ def minimizer(self, minimizer: AvailableMinimizers) -> None: self._fitter.easy_science_multi_fitter.switch_minimizer(minimizer) @property - def experiments(self) -> Dict[int, DataSet1D]: + def experiments(self) -> Dict[int, Union[DataSet1D, PolarizedDataSet]]: """Experiments function.""" return self._experiments @experiments.setter - def experiments(self, experiments: Dict[int, DataSet1D]) -> None: + def experiments(self, experiments: Dict[int, Union[DataSet1D, PolarizedDataSet]]) -> None: """Experiments function.""" self._experiments = experiments @@ -648,6 +651,85 @@ def load_all_experiments_from_file(self, path: Union[Path, str]) -> int: self._with_experiments = True return len(data_keys) + def suggest_polarized_channel_assignment(self, paths: List[Union[Path, str]]) -> Dict[str, Optional[PolarizationChannel]]: + """Suggest a spin-channel assignment for a set of data files. + + For each file, the ORSO header polarization is used when present, falling + back to filename heuristics ('_uu'/'_up' → pp, '_dd'/'_down' → mm, ...). + Files that cannot be identified map to None; a GUI should present the + result as an editable file → channel table. + + Parameters + ---------- + paths : List[Union[Path, str]] + Paths of the per-channel data files. + + Returns + ------- + Dict[str, Optional[PolarizationChannel]] + Detected channel (or None) per path. + """ + return {str(path): detect_polarization_channel(str(path)) for path in paths} + + def load_polarized_experiment( + self, + paths: Dict[Union[PolarizationChannel, str], Union[Path, str]], + model_index: Optional[int] = None, + ) -> None: + """Load a polarized experiment from one data file per spin channel. + + The channel datasets form a single :class:`PolarizedDataSet` experiment + sharing one model. Two-channel NSF-only experiments simply pass 'pp' and + 'mm' entries; spin-flip channels are optional. + + Parameters + ---------- + paths : Dict[Union[PolarizationChannel, str], Union[Path, str]] + Explicit channel → file assignment (e.g. from the GUI import dialog, + pre-filled via :meth:`suggest_polarized_channel_assignment`). + model_index : Optional[int], optional + Index of the model the experiment belongs to. By default, the + current model. + """ + paths = {PolarizationChannel(channel): path for channel, path in paths.items()} + channels = {} + for channel, path in paths.items(): + # One file per channel means one dataset per file; a multi-dataset ORSO + # file has no defined channel-to-dataset assignment here. + if self.count_datasets_in_file(path) > 1: + raise ValueError( + f"File '{path}' contains multiple datasets; polarized loading requires one dataset " + f'per channel file. Export the {channel.value} channel to its own file.' + ) + dataset = load_as_dataset(str(path)) + # Keep the source file visible per channel (file → channel provenance). + dataset.name = f'{channel.value}: {Path(path).name}' + channels[channel] = dataset + + new_index = len(self._experiments) + if model_index is None: + model_index = self._current_model_index + model = self.models[model_index] + + experiment = PolarizedDataSet( + name=f'Polarized experiment {new_index}', + channels=channels, + model=model, + ) + # Name from the ORSO title of the first file, when available. + first_channel = experiment.available_channels[0] + first_path = paths[first_channel] + self._apply_experiment_metadata(first_path, experiment, f'Polarized experiment {new_index}') + + # Background and resolution follow the first (in canonical order) channel; + # per-channel resolution functions are not supported (one per experiment). + first_dataset = experiment[first_channel] + self._auto_set_background(first_dataset) + self._apply_resolution_function(first_dataset, model) + + self._experiments[new_index] = experiment + self._with_experiments = True + def load_experiment_for_model_at_index(self, path: Union[Path, str], index: Optional[int] = 0) -> None: """Load experiment for model at index.""" experiment = load_as_dataset(str(path)) diff --git a/tests/calculators/refl1d/test_refl1d_wrapper.py b/tests/calculators/refl1d/test_refl1d_wrapper.py index b238b541..49ef2966 100644 --- a/tests/calculators/refl1d/test_refl1d_wrapper.py +++ b/tests/calculators/refl1d/test_refl1d_wrapper.py @@ -484,24 +484,43 @@ def test_calculate_polarized_channel_ordering(): # Pins the pp/mm halves of POLARIZATION_CHANNEL_TO_INDEX with physics, guarding # against a pp/mm swap: with the moment collinear with the neutron polarization # axis there is no spin flip and the non-spin-flip channels see rho +/- rhoM. - # Empirically verified sign convention of refl1d 1.0.0 (QProbe path, default - # Aguide=270): thetaM=90 is the orientation where pp sees rho + rhoM; - # thetaM=270 swaps pp and mm; both are spin-flip-free. (What matters is - # refl1d's eigenstate assignment, not the geometric angle relative to Aguide.) - # If this test ever fails while the index map matches the refl1d docstring, - # the sign convention changed - adjust the expectation, not the code. + # refl1d returns cross-sections in probe._xs_names order ['mm','mp','pm','pp'] + # (refl1d/probe/probe.py; magnetic_amplitude returns (--,-+,+-,++)). With the + # default guide field (Aguide=270), thetaM=270 is the moment-parallel-to-field + # orientation: spin-up ('pp') sees rho + rhoM, spin-down ('mm') sees rho - rhoM. + # thetaM=90 (anti-parallel) swaps the two; both are spin-flip-free. rho, rhoM = 4.0, 2.0 - p = _sample_wrapper(rho=rho, magnetic=True, rhoM=rhoM, thetaM=90) plus = _sample_wrapper(rho=rho + rhoM, magnetic=False) minus = _sample_wrapper(rho=rho - rhoM, magnetic=False) + reflectivity_plus = plus.calculate(Q_POLARIZED, 'MyModel') + reflectivity_minus = minus.calculate(Q_POLARIZED, 'MyModel') - channels = p.calculate_polarized(Q_POLARIZED, 'MyModel') + aligned = _sample_wrapper(rho=rho, magnetic=True, rhoM=rhoM, thetaM=270) + channels = aligned.calculate_polarized(Q_POLARIZED, 'MyModel') + assert_allclose(channels['pp'], reflectivity_plus, rtol=1e-4, atol=1e-9) + assert_allclose(channels['mm'], reflectivity_minus, rtol=1e-4, atol=1e-9) + assert np.max(channels['pm']) < 1e-16 + assert np.max(channels['mp']) < 1e-16 + + anti_aligned = _sample_wrapper(rho=rho, magnetic=True, rhoM=rhoM, thetaM=90) + channels = anti_aligned.calculate_polarized(Q_POLARIZED, 'MyModel') + assert_allclose(channels['pp'], reflectivity_minus, rtol=1e-4, atol=1e-9) + assert_allclose(channels['mm'], reflectivity_plus, rtol=1e-4, atol=1e-9) + + +def test_channel_index_map_matches_refl1d_xs_names(): + # The index map must agree with refl1d's own cross-section labels: the wrapper + # extracts Experiment.reflectivity() results in probe.xs order, which is + # PolarizedNeutronProbe._xs_names. This pins the convention at the source so a + # refl1d-side reordering (or a wrapper-side swap) fails loudly. + from refl1d import names as refl1d_names + + from easyreflectometry.calculators.polarization import POLARIZATION_CHANNEL_TO_INDEX - assert_allclose(channels['pp'], plus.calculate(Q_POLARIZED, 'MyModel'), rtol=1e-4, atol=1e-9) - assert_allclose(channels['mm'], minus.calculate(Q_POLARIZED, 'MyModel'), rtol=1e-4, atol=1e-9) - # Spin-flip tolerance pinned from observed refl1d 1.0.0 numerics (~1e-31). - assert np.max(channels['pm']) < 1e-30 - assert np.max(channels['mp']) < 1e-30 + xs_names = refl1d_names.PolarizedNeutronQProbe._xs_names + assert len(xs_names) == 4 + for channel, index in POLARIZATION_CHANNEL_TO_INDEX.items(): + assert xs_names[index] == channel.value def test_calculate_polarized_spin_flip(): @@ -600,7 +619,7 @@ def test_polarized_reflectivities_guards_malformed_output(): with pytest.raises(RuntimeError, match='expected 4'): p.calculate_polarized(q, 'MyModel') - # Wrong-length cross-section + # Wrong-length cross-section: index 1 is 'mp' in refl1d's (mm, mp, pm, pp) order. with patch('easyreflectometry.calculators.refl1d.wrapper.names.Experiment') as mock_experiment: mock_experiment.return_value.reflectivity.return_value = [ (q, np.ones(len(q))), @@ -608,15 +627,15 @@ def test_polarized_reflectivities_guards_malformed_output(): (q, np.ones(len(q))), (q, np.ones(len(q))), ] - with pytest.raises(RuntimeError, match='malformed pm'): + with pytest.raises(RuntimeError, match='malformed mp'): p.calculate_polarized(q, 'MyModel') - # Non-finite values + # Non-finite values: index 3 is 'pp' in refl1d's (mm, mp, pm, pp) order. with patch('easyreflectometry.calculators.refl1d.wrapper.names.Experiment') as mock_experiment: bad = np.ones(len(q)) bad[0] = np.nan mock_experiment.return_value.reflectivity.return_value = [(q, np.ones(len(q)))] * 3 + [(q, bad)] - with pytest.raises(RuntimeError, match='malformed mm'): + with pytest.raises(RuntimeError, match='malformed pp'): p.calculate_polarized(q, 'MyModel') diff --git a/tests/data/test_polarized.py b/tests/data/test_polarized.py new file mode 100644 index 00000000..7838c2c3 --- /dev/null +++ b/tests/data/test_polarized.py @@ -0,0 +1,191 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + +""" +Tests for PolarizedDataSet and spin-channel detection. +""" + +from types import SimpleNamespace +from unittest.mock import patch + +import numpy as np +import pytest + +from easyreflectometry.calculators import PolarizationChannel +from easyreflectometry.data import DataSet1D +from easyreflectometry.data import PolarizedDataSet +from easyreflectometry.data import detect_polarization_channel +from easyreflectometry.data.polarized import _channel_from_filename + + +def _dataset(name: str = 'series') -> DataSet1D: + return DataSet1D(name=name, x=np.array([0.01, 0.02]), y=np.array([1.0, 0.5])) + + +class TestPolarizedDataSet: + def test_requires_at_least_one_channel(self): + with pytest.raises(ValueError): + PolarizedDataSet(channels={}) + + def test_rejects_invalid_channel_key(self): + with pytest.raises(ValueError): + PolarizedDataSet(channels={'xx': _dataset()}) + + def test_rejects_non_dataset_values(self): + with pytest.raises(ValueError): + PolarizedDataSet(channels={'pp': np.array([1.0])}) + + def test_channels_in_canonical_order(self): + p = PolarizedDataSet(channels={'mm': _dataset('a'), 'pm': _dataset('b'), 'pp': _dataset('c')}) + assert p.available_channels == [ + PolarizationChannel.PP, + PolarizationChannel.PM, + PolarizationChannel.MM, + ] + + def test_getitem_and_contains_accept_strings_and_enums(self): + pp = _dataset('up-up') + p = PolarizedDataSet(channels={'pp': pp, 'mm': _dataset('down-down')}) + assert p['pp'] is pp + assert p[PolarizationChannel.PP] is pp + assert 'pp' in p + assert PolarizationChannel.MM in p + assert 'pm' not in p + assert 'xx' not in p + assert len(p) == 2 + + def test_model_propagates_to_channel_datasets(self): + pp = _dataset() + mm = _dataset() + p = PolarizedDataSet(channels={'pp': pp, 'mm': mm}) + assert p.is_simulation + marker = object() + p.model = marker + assert p.is_experiment + assert pp.model is marker + assert mm.model is marker + + def test_channels_view_is_read_only(self): + p = PolarizedDataSet(channels={'pp': _dataset()}) + with pytest.raises(TypeError): + p.channels[PolarizationChannel.MM] = _dataset() + with pytest.raises(TypeError): + del p.channels[PolarizationChannel.PP] + + def test_set_channel_validates_propagates_and_reorders(self): + p = PolarizedDataSet(channels={'mm': _dataset()}) + marker = object() + p.model = marker + + with pytest.raises(ValueError): + p.set_channel('xx', _dataset()) + with pytest.raises(ValueError): + p.set_channel('pp', np.array([1.0])) + + pp = _dataset('up-up') + p.set_channel('pp', pp) + # Canonical order restored, model propagated to the new dataset. + assert p.available_channels == [PolarizationChannel.PP, PolarizationChannel.MM] + assert p['pp'] is pp + assert pp.model is marker + + replacement = _dataset('up-up-2') + p.set_channel(PolarizationChannel.PP, replacement) + assert p['pp'] is replacement + + def test_remove_channel_guards(self): + p = PolarizedDataSet(channels={'pp': _dataset(), 'mm': _dataset()}) + with pytest.raises(ValueError): + p.remove_channel('pm') # not present + p.remove_channel('pp') + assert p.available_channels == [PolarizationChannel.MM] + with pytest.raises(ValueError): + p.remove_channel('mm') # last channel cannot be removed + assert p.available_channels == [PolarizationChannel.MM] + + +class TestChannelDetection: + @pytest.mark.parametrize( + 'filename,expected', + [ + ('sample_uu.dat', PolarizationChannel.PP), + ('sample_pp.ort', PolarizationChannel.PP), + ('sample-up-up.txt', PolarizationChannel.PP), + ('sample_up.dat', PolarizationChannel.PP), + ('run12_plus.txt', PolarizationChannel.PP), + ('sample_dd.dat', PolarizationChannel.MM), + ('sample_down.dat', PolarizationChannel.MM), + ('sample-down-down.txt', PolarizationChannel.MM), + ('sample_ud.dat', PolarizationChannel.PM), + ('sample_up_down.dat', PolarizationChannel.PM), + ('sample_pm.ort', PolarizationChannel.PM), + ('sample_du.dat', PolarizationChannel.MP), + ('sample_down_up.dat', PolarizationChannel.MP), + ('sample_mp.ort', PolarizationChannel.MP), + ('nothing_here.dat', None), + ('d2o_layer.dat', None), + ], + ) + def test_filename_heuristics(self, filename, expected): + assert _channel_from_filename(filename) == expected + + @pytest.mark.parametrize( + 'polarization,expected', + [ + ('pp', PolarizationChannel.PP), + ('mm', PolarizationChannel.MM), + ('pm', PolarizationChannel.PM), + ('mp', PolarizationChannel.MP), + # Partially-analysed observables are not spin channels: 'po' measures + # pp + pm (incident plus, no outgoing analysis), 'mo' measures mp + mm. + ('po', None), + ('mo', None), + ('op', None), + ('om', None), + ('unpolarized', None), + ], + ) + def test_orso_header_detection(self, polarization, expected): + orso_dataset = SimpleNamespace( + info=SimpleNamespace( + data_source=SimpleNamespace( + measurement=SimpleNamespace( + instrument_settings=SimpleNamespace(polarization=polarization), + ) + ) + ) + ) + with patch('orsopy.fileio.orso.load_orso', return_value=[orso_dataset]): + assert detect_polarization_channel('whatever.ort') == expected + + def test_header_takes_precedence_over_filename(self): + orso_dataset = SimpleNamespace( + info=SimpleNamespace( + data_source=SimpleNamespace( + measurement=SimpleNamespace( + instrument_settings=SimpleNamespace(polarization='mm'), + ) + ) + ) + ) + with patch('orsopy.fileio.orso.load_orso', return_value=[orso_dataset]): + assert detect_polarization_channel('sample_uu.ort') == PolarizationChannel.MM + + def test_unreadable_file_falls_back_to_filename(self): + # No such file: the ORSO branch raises internally and the name decides. + assert detect_polarization_channel('no_such_file_dd.ort') == PolarizationChannel.MM + + def test_unpolarized_header_suppresses_filename_fallback(self): + # A header that explicitly declares a non-channel polarization wins over + # channel-looking filename tokens. + orso_dataset = SimpleNamespace( + info=SimpleNamespace( + data_source=SimpleNamespace( + measurement=SimpleNamespace( + instrument_settings=SimpleNamespace(polarization='unpolarized'), + ) + ) + ) + ) + with patch('orsopy.fileio.orso.load_orso', return_value=[orso_dataset]): + assert detect_polarization_channel('sample_uu.ort') is None diff --git a/tests/test_polarized_fitting.py b/tests/test_polarized_fitting.py new file mode 100644 index 00000000..71eaef4f --- /dev/null +++ b/tests/test_polarized_fitting.py @@ -0,0 +1,314 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + +""" +Tests for explicit-channel calculation, the polarized reflectivity cache, +per-channel experiment loading, and simultaneous multi-channel fitting. +""" + +from unittest.mock import patch + +import numpy as np +import pytest +from numpy.testing import assert_allclose + +from easyreflectometry.calculators import CalculatorFactory +from easyreflectometry.calculators import PolarizationChannel +from easyreflectometry.calculators.refl1d import wrapper as refl1d_wrapper +from easyreflectometry.data import DataSet1D +from easyreflectometry.data import PolarizedDataSet +from easyreflectometry.fitting import MultiFitter +from easyreflectometry.model import Model +from easyreflectometry.model import PercentageFwhm +from easyreflectometry.project import Project +from easyreflectometry.sample import Layer +from easyreflectometry.sample import LayerMagnetism +from easyreflectometry.sample import Material +from easyreflectometry.sample import Multilayer +from easyreflectometry.sample import Sample + +Q = np.linspace(0.005, 0.3, 50) + + +def _magnetic_model(magnetism: LayerMagnetism | None) -> Model: + vacuum = Material(sld=0, isld=0, name='Vacuum') + material = Material(sld=4.0, isld=0, name='Sld 4') + si = Material(sld=2.047, isld=0, name='Si') + superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase') + layer = Layer(material=material, thickness=100, roughness=0, magnetism=magnetism, name='Sld 4 Layer') + subphase = Layer(material=si, thickness=0, roughness=0, name='Si Subphase') + sample = Sample(Multilayer(superphase), Multilayer(layer), Multilayer(subphase), name='Sample') + model = Model(sample=sample, scale=1, background=0, name='Magnetic Model') + model.resolution_function = PercentageFwhm(0) + return model + + +def _refl1d_interface() -> CalculatorFactory: + interface = CalculatorFactory() + interface.switch('refl1d') + return interface + + +def _polarized_data(channels: dict[str, np.ndarray], model=None) -> PolarizedDataSet: + datasets = { + channel: DataSet1D(name=channel, x=Q, y=reflectivity, ye=(0.01 * reflectivity) ** 2) + for channel, reflectivity in channels.items() + } + return PolarizedDataSet(name='synthetic', channels=datasets, model=model) + + +class TestCalculateChannel: + def test_channels_match_calculate_polarized(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = _refl1d_interface() + calculator = model.interface() + + reference = calculator.polarized_reflectivity_profiles(Q, model.unique_name) + for channel in ('pp', 'pm', 'mp', 'mm'): + assert_allclose( + calculator.reflectivity_profile_channel(Q, model.unique_name, channel), + reference[channel], + rtol=1e-12, + ) + + def test_pp_without_magnetism_falls_back_to_unpolarized(self): + model = _magnetic_model(None) + model.interface = _refl1d_interface() + calculator = model.interface() + + assert_allclose( + calculator.reflectivity_profile_channel(Q, model.unique_name, 'pp'), + calculator.reflectity_profile(Q, model.unique_name), + rtol=1e-12, + ) + + def test_spin_flip_without_magnetism_raises(self): + model = _magnetic_model(None) + model.interface = _refl1d_interface() + with pytest.raises(ValueError): + model.interface().reflectivity_profile_channel(Q, model.unique_name, 'pm') + + def test_fit_func_for_channel(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = _refl1d_interface() + + reference = model.interface.polarized_reflectivity_profiles(Q, model.unique_name) + fit_func = model.interface.fit_func_for_channel('mm') + assert_allclose(fit_func(Q, model.unique_name), reference['mm'], rtol=1e-12) + + +class TestPolarizedCache: + def test_repeated_calculation_hits_cache(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = _refl1d_interface() + calculator = model.interface() + + with patch.object(refl1d_wrapper.names, 'Experiment', wraps=refl1d_wrapper.names.Experiment) as experiment: + first = calculator.polarized_reflectivity_profiles(Q, model.unique_name) + second = calculator.polarized_reflectivity_profiles(Q, model.unique_name) + assert experiment.call_count == 1 + # All four channels through calculate_channel: still no new evaluation. + for channel in ('pp', 'pm', 'mp', 'mm'): + calculator.reflectivity_profile_channel(Q, model.unique_name, channel) + assert experiment.call_count == 1 + for channel in ('pp', 'pm', 'mp', 'mm'): + assert_allclose(first[channel], second[channel], rtol=1e-15) + + def test_parameter_change_invalidates_cache(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = _refl1d_interface() + calculator = model.interface() + + with patch.object(refl1d_wrapper.names, 'Experiment', wraps=refl1d_wrapper.names.Experiment) as experiment: + before = calculator.polarized_reflectivity_profiles(Q, model.unique_name) + model.sample[1].layers[0].magnetism.rho_m = 3.0 + after = calculator.polarized_reflectivity_profiles(Q, model.unique_name) + assert experiment.call_count == 2 + assert not np.allclose(before['mm'], after['mm']) + + def test_q_dtype_is_normalized_before_keying(self): + # Keying happens after normalization to float64 plus explicit shape, so + # byte-identical arrays of different dtype/shape can never collide. A + # float32 grid whose values are exactly representable normalizes to the + # same key as its float64 twin and shares the cache entry. + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = _refl1d_interface() + calculator = model.interface() + q_pow2 = np.array([0.03125, 0.0625, 0.125, 0.25]) # exact in float32 + + with patch.object(refl1d_wrapper.names, 'Experiment', wraps=refl1d_wrapper.names.Experiment) as experiment: + first = calculator.polarized_reflectivity_profiles(q_pow2, model.unique_name) + second = calculator.polarized_reflectivity_profiles(q_pow2.astype(np.float32), model.unique_name) + assert experiment.call_count == 1 + for channel in ('pp', 'pm', 'mp', 'mm'): + assert len(first[channel]) == len(q_pow2) + assert_allclose(second[channel], first[channel], rtol=1e-15) + + def test_different_q_grids_coexist_within_one_state(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = _refl1d_interface() + calculator = model.interface() + q_other = np.linspace(0.01, 0.2, 30) + + with patch.object(refl1d_wrapper.names, 'Experiment', wraps=refl1d_wrapper.names.Experiment) as experiment: + calculator.polarized_reflectivity_profiles(Q, model.unique_name) + calculator.polarized_reflectivity_profiles(q_other, model.unique_name) + assert experiment.call_count == 2 + # Both grids now cached for the same model state. + calculator.polarized_reflectivity_profiles(Q, model.unique_name) + calculator.polarized_reflectivity_profiles(q_other, model.unique_name) + assert experiment.call_count == 2 + + +class TestLoadPolarizedExperiment: + @staticmethod + def _write_channel_file(directory, name: str) -> str: + path = directory / name + q = np.linspace(0.01, 0.2, 20) + reflectivity = np.exp(-q * 30) + error = 0.01 * reflectivity + np.savetxt(path, np.column_stack([q, reflectivity, error])) + return str(path) + + def test_load_polarized_experiment(self, tmp_path): + pp_path = self._write_channel_file(tmp_path, 'sample_uu.txt') + mm_path = self._write_channel_file(tmp_path, 'sample_dd.txt') + + project = Project() + project.calculator = 'refl1d' + project.default_model() + + project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) + + experiment = project.experiments[0] + assert isinstance(experiment, PolarizedDataSet) + assert experiment.available_channels == [PolarizationChannel.PP, PolarizationChannel.MM] + assert experiment.name == 'Polarized experiment 0' + assert experiment.model is project.models[0] + assert experiment['pp'].model is project.models[0] + assert len(experiment['pp'].x) == 20 + + def test_multi_dataset_file_is_rejected(self, tmp_path): + import os + + multi_path = os.path.join(os.path.dirname(__file__), '_static', 'test_example2.ort') + mm_path = self._write_channel_file(tmp_path, 'sample_dd.txt') + + project = Project() + project.calculator = 'refl1d' + project.default_model() + + with pytest.raises(ValueError, match='multiple datasets'): + project.load_polarized_experiment({'pp': multi_path, 'mm': mm_path}) + assert len(project.experiments) == 0 + + def test_suggest_polarized_channel_assignment(self, tmp_path): + pp_path = self._write_channel_file(tmp_path, 'sample_uu.txt') + mm_path = self._write_channel_file(tmp_path, 'sample_dd.txt') + unknown_path = self._write_channel_file(tmp_path, 'sample_other.txt') + + project = Project() + suggestion = project.suggest_polarized_channel_assignment([pp_path, mm_path, unknown_path]) + + assert suggestion[str(pp_path)] == PolarizationChannel.PP + assert suggestion[str(mm_path)] == PolarizationChannel.MM + assert suggestion[str(unknown_path)] is None + + +class TestFitPolarized: + def test_two_channel_nsf_fit_recovers_rho_m(self): + truth = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=270.0)) + truth.interface = _refl1d_interface() + reference = truth.interface.polarized_reflectivity_profiles(Q, truth.unique_name) + + model = _magnetic_model(LayerMagnetism(rho_m=1.0, theta_m=270.0)) + model.interface = _refl1d_interface() + rho_m = model.sample[1].layers[0].magnetism.rho_m + rho_m.fixed = False + rho_m.bounds = (0.0, 5.0) + + data = _polarized_data({'pp': reference['pp'], 'mm': reference['mm']}, model=model) + fitter = MultiFitter(model) + results = fitter.fit_polarized(data) + + assert list(results.keys()) == ['pp', 'mm'] + assert all(result.success for result in results.values()) + assert_allclose(rho_m.value, 2.5, atol=0.01) + + def test_four_channel_fit_recovers_rho_m_and_theta_m(self): + truth = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=45.0)) + truth.interface = _refl1d_interface() + reference = truth.interface.polarized_reflectivity_profiles(Q, truth.unique_name) + + model = _magnetic_model(LayerMagnetism(rho_m=1.5, theta_m=60.0)) + model.interface = _refl1d_interface() + magnetism = model.sample[1].layers[0].magnetism + magnetism.rho_m.fixed = False + magnetism.rho_m.bounds = (0.0, 5.0) + magnetism.theta_m.fixed = False + magnetism.theta_m.bounds = (0.0, 90.0) + + data = _polarized_data(dict(reference), model=model) + fitter = MultiFitter(model) + results = fitter.fit_polarized(data) + + assert list(results.keys()) == ['pp', 'pm', 'mp', 'mm'] + assert all(result.success for result in results.values()) + assert_allclose(magnetism.rho_m.value, 2.5, atol=0.02) + assert_allclose(magnetism.theta_m.value, 45.0, atol=0.5) + + def test_shared_structural_parameter_fitted_across_channels(self): + truth = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=270.0)) + truth.interface = _refl1d_interface() + reference = truth.interface.polarized_reflectivity_profiles(Q, truth.unique_name) + + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=270.0)) + model.interface = _refl1d_interface() + thickness = model.sample[1].layers[0].thickness + thickness.value = 90.0 + thickness.fixed = False + thickness.bounds = (50.0, 150.0) + + data = _polarized_data({'pp': reference['pp'], 'mm': reference['mm']}, model=model) + fitter = MultiFitter(model) + results = fitter.fit_polarized(data) + + assert all(result.success for result in results.values()) + assert_allclose(thickness.value, 100.0, atol=0.1) + + def test_fit_polarized_requires_matching_model(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=270.0)) + model.interface = _refl1d_interface() + other = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=270.0)) + other.interface = _refl1d_interface() + + data = _polarized_data({'pp': np.ones_like(Q)}, model=other) + fitter = MultiFitter(model) + with pytest.raises(ValueError, match='must be the model'): + fitter.fit_polarized(data) + + def test_fit_polarized_requires_matching_channel_models(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=270.0)) + model.interface = _refl1d_interface() + other = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=270.0)) + other.interface = _refl1d_interface() + + data = _polarized_data({'pp': np.ones_like(Q), 'mm': np.ones_like(Q)}, model=model) + # Rebind one channel dataset behind the experiment's back. + data['mm'].model = other + + fitter = MultiFitter(model) + with pytest.raises(ValueError, match="'mm' channel dataset"): + fitter.fit_polarized(data) + + def test_fit_polarized_requires_single_model(self): + model_a = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=270.0)) + model_b = _magnetic_model(None) + interface = _refl1d_interface() + model_a.interface = interface + model_b.interface = interface + + data = _polarized_data({'pp': np.ones_like(Q)}, model=model_a) + fitter = MultiFitter(model_a, model_b) + with pytest.raises(ValueError): + fitter.fit_polarized(data) From 1767f8150212a7ad3179c42fa20af652c942e1af Mon Sep 17 00:00:00 2001 From: rozyczko Date: Thu, 13 Aug 2026 21:18:12 +0200 Subject: [PATCH 07/22] ruff --- CHANGELOG.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1af5c483..b3392180 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,15 +21,15 @@ returned. channel → file mapping, and `Project.suggest_polarized_channel_assignment(paths)` pre-fills that mapping from the ORSO header polarization (`pp`/`mm`/`pm`/`mp` only — - partially-analysed observables such as `po`/`mo`, which measure channel - sums, and `op`/`om`/`unpolarized` are left for the user to decide) or, - for plain text files, from filename tokens (`_uu`/`_up`/`_pp` → pp, - `_dd`/`_down`/`_mm` → mm, `_ud`/`_pm` → pm, `_du`/`_mp` → mp). + partially-analysed observables such as `po`/`mo`, which measure + channel sums, and `op`/`om`/`unpolarized` are left for the user to + decide) or, for plain text files, from filename tokens + (`_uu`/`_up`/`_pp` → pp, `_dd`/`_down`/`_mm` → mm, `_ud`/`_pm` → pm, + `_du`/`_mp` → mp). - New `calculate_channel(q, model, channel)` on the wrapper (and `reflectivity_profile_channel` on the calculator, `fit_func_for_channel` on `CalculatorFactory`) evaluates one explicit - spin channel without touching the global `polarization_channel` - state. + spin channel without touching the global `polarization_channel` state. - New `MultiFitter.fit_polarized(data)` fits all measured channels of a `PolarizedDataSet` simultaneously against the shared model: one fit function per channel, common structural parameters, magnetic @@ -37,8 +37,8 @@ returned. `FitResults`. - The refl1d wrapper now caches the four polarized cross-sections per model state and (q, dq) grid — they come from a single kernel - evaluation, so a simultaneous N-channel fit costs about one - evaluation per iteration instead of N. + evaluation, so a simultaneous N-channel fit costs about one evaluation + per iteration instead of N. - New `polarized_reflectivity_profiles(x_array, model_id)` on the calculator (and on `CalculatorFactory`) returns the reflectivity of From 74981b8799a68bf47d177abd198509d83f85632e Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Fri, 14 Aug 2026 13:35:49 +0200 Subject: [PATCH 08/22] PR code review comments --- CHANGELOG.md | 18 ++++ src/easyreflectometry/project.py | 116 +++++++++++++++++++++-- src/easyreflectometry/summary/summary.py | 103 ++++++++++++++------ tests/summary/test_summary.py | 81 ++++++++++++++++ tests/test_polarized_fitting.py | 97 ++++++++++++++++++- 5 files changed, 377 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3392180..a5a12f60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,24 @@ returned. decide) or, for plain text files, from filename tokens (`_uu`/`_up`/`_pp` → pp, `_dd`/`_down`/`_mm` → mm, `_ud`/`_pm` → pm, `_du`/`_mp` → mp). +- Experiment and model accessors are channel aware: + `Project.experimental_data_for_model_at_index(index, channel=...)` + returns the `DataSet1D` of one spin channel (`None`, the default, + keeps the previous behavior and returns the stored experiment), + `Project.model_data_for_model_at_index(index, q_range, channel=...)` + calculates one spin cross-section, and + `Project.experiment_is_polarized_at_index(index)` / + `Project.experiment_channels_at_index(index)` report the polarization + state. Asking for a channel that was not measured raises `KeyError`; + an unknown channel, or any channel on an unpolarized experiment, + raises `ValueError`. +- The summary/report figures now show one measured series per spin + channel of a polarized experiment, each in its channel color, with the + matching calculated cross-section. Channels whose cross-section cannot + be calculated (e.g. spin-flip on a non-magnetic model) are shown + without a calculated overlay rather than with the channel-agnostic + curve. Previously a polarized experiment made the report figures fail + on `PolarizedDataSet.x`. - New `calculate_channel(q, model, channel)` on the wrapper (and `reflectivity_profile_channel` on the calculator, `fit_func_for_channel` on `CalculatorFactory`) evaluates one explicit diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index 0bfa9fd5..6ad7c646 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -675,7 +675,7 @@ def load_polarized_experiment( self, paths: Dict[Union[PolarizationChannel, str], Union[Path, str]], model_index: Optional[int] = None, - ) -> None: + ) -> int: """Load a polarized experiment from one data file per spin channel. The channel datasets form a single :class:`PolarizedDataSet` experiment @@ -690,6 +690,12 @@ def load_polarized_experiment( model_index : Optional[int], optional Index of the model the experiment belongs to. By default, the current model. + + Returns + ------- + int + Index of the newly loaded experiment, so a caller can make it + current. """ paths = {PolarizationChannel(channel): path for channel, path in paths.items()} channels = {} @@ -729,6 +735,7 @@ def load_polarized_experiment( self._experiments[new_index] = experiment self._with_experiments = True + return new_index def load_experiment_for_model_at_index(self, path: Union[Path, str], index: Optional[int] = 0) -> None: """Load experiment for model at index.""" @@ -760,25 +767,114 @@ def sample_data_for_model_at_index(self, index: int = 0, q_range: Optional[np.ar return reflectivity_data - def model_data_for_model_at_index(self, index: int = 0, q_range: Optional[np.array] = None) -> DataSet1D: - """Model data for model at index.""" + def model_data_for_model_at_index( + self, + index: int = 0, + q_range: Optional[np.array] = None, + channel: Optional[Union[PolarizationChannel, str]] = None, + ) -> DataSet1D: + """Model data for model at index. + + Parameters + ---------- + index : int + Index of the model. + q_range : Optional[np.array] + Points to calculate at; the project q range by default. + channel : Optional[Union[PolarizationChannel, str]] + Spin cross-section to calculate. `None` (the default) gives the + ordinary, channel-agnostic reflectivity. A channel requires a + magnetic model (except 'pp', which falls back to the unpolarized + calculation) and a calculator supporting magnetism, otherwise the + calculator raises. + """ if q_range is None: q_range = np.linspace(self.q_min, self.q_max, self.q_resolution) self.models[index].interface = self._calculator - reflectivity = self.models[index].interface().reflectity_profile(q_range, self._models[index].unique_name) + if channel is None: + reflectivity = self.models[index].interface().reflectity_profile(q_range, self._models[index].unique_name) + name = f'Reflectivity for Model {index}' + else: + channel = PolarizationChannel(channel) + reflectivity = ( + self.models[index].interface().reflectivity_profile_channel(q_range, self._models[index].unique_name, channel) + ) + name = f'Reflectivity ({channel.value}) for Model {index}' return DataSet1D( - name=f'Reflectivity for Model {index}', + name=name, x=q_range, y=reflectivity, ) - def experimental_data_for_model_at_index(self, index: int = 0) -> DataSet1D: - """Experimental data for model at index.""" - if index in self._experiments.keys(): - return self._experiments[index] - else: + def experimental_data_for_model_at_index( + self, + index: int = 0, + channel: Optional[Union[PolarizationChannel, str]] = None, + ) -> Union[DataSet1D, PolarizedDataSet]: + """Experimental data for model at index. + + Parameters + ---------- + index : int + Index of the experiment. + channel : Optional[Union[PolarizationChannel, str]] + Spin channel of a polarized experiment. `None` (the default) keeps + the historical behavior and returns the stored experiment as is: a + `DataSet1D` for an unpolarized experiment, the whole + `PolarizedDataSet` for a polarized one. + + Returns + ------- + Union[DataSet1D, PolarizedDataSet] + The experiment, or the `DataSet1D` of the requested channel. + + Raises + ------ + IndexError + No experiment is loaded at `index`. + KeyError + `channel` is a valid spin channel but was not measured. + ValueError + `channel` was given for an unpolarized experiment, or is not one of + 'pp', 'pm', 'mp', 'mm'. + """ + if index not in self._experiments.keys(): raise IndexError(f'No experiment data for model at index {index}') + experiment = self._experiments[index] + if channel is None: + return experiment + + if not isinstance(experiment, PolarizedDataSet): + raise ValueError( + f"Experiment at index {index} is not polarized; it has no '{channel}' channel. " + 'Call without a channel argument to get its data.' + ) + try: + channel = PolarizationChannel(channel) + except ValueError as exception: + known = ', '.join(member.value for member in PolarizationChannel) + raise ValueError(f"Unknown spin channel '{channel}'; expected one of {known}.") from exception + if channel not in experiment: + measured = ', '.join(member.value for member in experiment.available_channels) + raise KeyError(f"Channel '{channel.value}' was not measured in experiment {index} (measured: {measured}).") + return experiment[channel] + + def experiment_is_polarized_at_index(self, index: int = 0) -> bool: + """Whether the experiment at index holds per-channel (polarized) data. + + Returns False when no experiment is loaded at `index`, so consumers can + use it as a plain predicate. + """ + return isinstance(self._experiments.get(index), PolarizedDataSet) + + def experiment_channels_at_index(self, index: int = 0) -> List[PolarizationChannel]: + """Measured spin channels of the experiment at index ([] when unpolarized).""" + experiment = self._experiments.get(index) + if not isinstance(experiment, PolarizedDataSet): + return [] + return experiment.available_channels + def default_model(self): """Default model.""" self._replace_collection(MaterialCollection(interface=self._calculator), self._materials) diff --git a/src/easyreflectometry/summary/summary.py b/src/easyreflectometry/summary/summary.py index f40f23ad..d3ba8601 100644 --- a/src/easyreflectometry/summary/summary.py +++ b/src/easyreflectometry/summary/summary.py @@ -48,6 +48,9 @@ def _silence_pdf_converter(): _NAME_MAX_LEN = 20 +# Fixed per-channel colors for polarized experiments, shared with the app so a +# channel keeps the same color in the GUI and in the report. +_CHANNEL_COLORS = {'pp': '#0173B2', 'pm': '#029E73', 'mp': '#CC78BC', 'mm': '#DE8F05'} # Custom href scheme used to pass the full name to QML via TextEdit.hoveredLink. _TOOLTIP_SCHEME = 'nametooltip' @@ -184,22 +187,63 @@ def save_sld_plot(self, filename: str) -> None: fig.savefig(filename, dpi=600) plt.close() + def _measured_series(self) -> list: + """Measured data of experiment 0 as ``(label, dataset, color, channel)`` entries. + + One entry per measured spin channel for a polarized experiment (each + with its own channel color and channel key), a single channel-less + entry for an ordinary experiment, and an empty list when no experiment + is loaded. + """ + try: + experiment = self._project.experimental_data_for_model_at_index(0) + except IndexError: + return [] + + channels = self._project.experiment_channels_at_index(0) + if not channels: + return [('Experiment', experiment, 'red', None)] + return [ + (f'Experiment ({channel.value})', experiment[channel], _CHANNEL_COLORS[channel.value], channel) + for channel in channels + ] + + def _model_curve(self, channel=None): + """Calculated curve for experiment 0, per spin channel when asked. + + Returns None when the requested channel cannot be calculated (e.g. a + spin-flip channel on a non-magnetic model) — an incorrect overlay is + worse than none. + """ + try: + return self._project.model_data_for_model_at_index(0, channel=channel) + except (ValueError, NotImplementedError) as exception: + logging.getLogger(__name__).warning( + 'No calculated curve for channel %s: %s', getattr(channel, 'value', channel), exception + ) + return None + def save_fit_experiment_plot(self, filename: str) -> None: """Save fit experiment plot.""" fig = plt.figure() ax = fig.add_subplot(1, 1, 1) legends = [] - model = self._project.model_data_for_model_at_index(0) - ax.plot(model.x, np.log10(model.y), color='blue') - legends.append('Model') - - try: - experiment = self._project.experimental_data_for_model_at_index(0) - ax.plot(experiment.x, np.log10(experiment.y), color='red') - legends.append('Experiment') - except IndexError: - pass + measured_series = self._measured_series() + # One calculated curve per measured channel; the ordinary single curve + # when the experiment is unpolarized or nothing is loaded. + model_channels = [entry[3] for entry in measured_series] or [None] + for channel in model_channels: + model = self._model_curve(channel) + if model is None: + continue + color = 'blue' if channel is None else _CHANNEL_COLORS[channel.value] + ax.plot(model.x, np.log10(model.y), color=color) + legends.append('Model' if channel is None else f'Model ({channel.value})') + + for label, dataset, color, _channel in measured_series: + ax.plot(dataset.x, np.log10(dataset.y), color=color) + legends.append(label) ax.set_xlabel('Q (Å⁻¹)') ax.set_ylabel('Reflectivity') @@ -411,30 +455,35 @@ def _fit_experiment_plotly_figure(self): fig = go.Figure() - model = self._project.model_data_for_model_at_index(0) - fig.add_trace( - go.Scatter( - x=np.asarray(model.x), - y=np.asarray(model.y), - mode='lines', - name='Model', - line={'color': 'blue'}, + measured_series = self._measured_series() + # One calculated curve per measured channel; the ordinary single curve + # when the experiment is unpolarized or nothing is loaded. + model_channels = [entry[3] for entry in measured_series] or [None] + for channel in model_channels: + model = self._model_curve(channel) + if model is None: + continue + color = 'blue' if channel is None else _CHANNEL_COLORS[channel.value] + fig.add_trace( + go.Scatter( + x=np.asarray(model.x), + y=np.asarray(model.y), + mode='lines', + name='Model' if channel is None else f'Model ({channel.value})', + line={'color': color}, + ) ) - ) - try: - experiment = self._project.experimental_data_for_model_at_index(0) + for label, dataset, color, _channel in measured_series: fig.add_trace( go.Scatter( - x=np.asarray(experiment.x), - y=np.asarray(experiment.y), + x=np.asarray(dataset.x), + y=np.asarray(dataset.y), mode='markers', - name='Experiment', - marker={'color': 'red', 'size': 4}, + name=label, + marker={'color': color, 'size': 4}, ) ) - except IndexError: - pass fig.update_layout( xaxis_title='Q (Å⁻¹)', diff --git a/tests/summary/test_summary.py b/tests/summary/test_summary.py index a636b504..49527316 100644 --- a/tests/summary/test_summary.py +++ b/tests/summary/test_summary.py @@ -226,3 +226,84 @@ def test_figures_section_interactive(self, project: Project) -> None: # Two interactive plotly charts with the library embedded inline once. assert html.count('class="plotly-graph-div"') == 2 assert 'Plotly.newPlot' in html + + +class TestSummaryPolarized: + """Report figures of a polarized (per-channel) experiment.""" + + @staticmethod + def _write_channel_file(directory, name: str) -> str: + import numpy as np + + path = directory / name + q = np.linspace(0.01, 0.2, 20) + reflectivity = np.exp(-q * 30) + np.savetxt(path, np.column_stack([q, reflectivity, 0.01 * reflectivity])) + return str(path) + + @pytest.fixture + def polarized_project(self, tmp_path) -> Project: + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.default_model() + project.load_polarized_experiment({ + 'pp': self._write_channel_file(tmp_path, 'sample_uu.txt'), + 'mm': self._write_channel_file(tmp_path, 'sample_dd.txt'), + }) + return project + + def test_measured_series_one_entry_per_channel(self, polarized_project: Project) -> None: + # When + summary = Summary(polarized_project) + + # Then + series = summary._measured_series() + + # Expect + assert [entry[0] for entry in series] == ['Experiment (pp)', 'Experiment (mm)'] + assert series[0][2] != series[1][2] # distinct channel colors + assert [entry[3].value for entry in series] == ['pp', 'mm'] + + @pytest.fixture + def project(self) -> Project: + global_object.map._clear() + project = Project() + project.default_model() + return project + + def test_measured_series_unpolarized_and_empty(self, project: Project) -> None: + # When + summary = Summary(project) + + # Then Expect: nothing loaded + assert summary._measured_series() == [] + + # When an ordinary experiment is loaded + project.load_experiment_for_model_at_index(os.path.join(PATH_STATIC, 'example.ort')) + + # Expect one channel-less entry + series = summary._measured_series() + assert len(series) == 1 + assert series[0][0] == 'Experiment' and series[0][3] is None + + def test_model_curve_skips_uncalculable_channel(self, polarized_project: Project) -> None: + # When: a non-magnetic model cannot produce a spin-flip cross-section + summary = Summary(polarized_project) + + # Then Expect: no misleading overlay is generated for it + assert summary._model_curve('pm') is None + assert summary._model_curve('pp') is not None + + def test_fit_experiment_figure_has_a_trace_per_channel(self, polarized_project: Project) -> None: + # When + summary = Summary(polarized_project) + + # Then + figure = summary._fit_experiment_plotly_figure() + + # Expect: one measured trace per channel plus the calculable model curves + names = [trace.name for trace in figure.data] + assert 'Experiment (pp)' in names + assert 'Experiment (mm)' in names + assert 'Model (pp)' in names diff --git a/tests/test_polarized_fitting.py b/tests/test_polarized_fitting.py index 71eaef4f..f0164343 100644 --- a/tests/test_polarized_fitting.py +++ b/tests/test_polarized_fitting.py @@ -10,6 +10,7 @@ import numpy as np import pytest +from easyscience import global_object from numpy.testing import assert_allclose from easyreflectometry.calculators import CalculatorFactory @@ -19,6 +20,7 @@ from easyreflectometry.data import PolarizedDataSet from easyreflectometry.fitting import MultiFitter from easyreflectometry.model import Model +from easyreflectometry.model import ModelCollection from easyreflectometry.model import PercentageFwhm from easyreflectometry.project import Project from easyreflectometry.sample import Layer @@ -178,8 +180,10 @@ def test_load_polarized_experiment(self, tmp_path): project.calculator = 'refl1d' project.default_model() - project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) + new_index = project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) + # The index is returned so a GUI can make the new experiment current. + assert new_index == 0 experiment = project.experiments[0] assert isinstance(experiment, PolarizedDataSet) assert experiment.available_channels == [PolarizationChannel.PP, PolarizationChannel.MM] @@ -215,6 +219,97 @@ def test_suggest_polarized_channel_assignment(self, tmp_path): assert suggestion[str(unknown_path)] is None +class TestChannelAwareExperimentAccessors: + """`experimental_data_for_model_at_index(index, channel=…)` and friends.""" + + @staticmethod + def _polarized_project(tmp_path) -> Project: + pp_path = TestLoadPolarizedExperiment._write_channel_file(tmp_path, 'sample_uu.txt') + mm_path = TestLoadPolarizedExperiment._write_channel_file(tmp_path, 'sample_dd.txt') + project = Project() + project.calculator = 'refl1d' + project.default_model() + project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) + return project + + def test_without_channel_returns_the_whole_experiment(self, tmp_path): + project = self._polarized_project(tmp_path) + + experiment = project.experimental_data_for_model_at_index(0) + + assert isinstance(experiment, PolarizedDataSet) + assert project.experiment_is_polarized_at_index(0) is True + assert project.experiment_channels_at_index(0) == [PolarizationChannel.PP, PolarizationChannel.MM] + + def test_channel_returns_that_channel_dataset(self, tmp_path): + project = self._polarized_project(tmp_path) + experiment = project.experiments[0] + + for channel in ('pp', PolarizationChannel.MM): + data = project.experimental_data_for_model_at_index(0, channel=channel) + assert isinstance(data, DataSet1D) + assert data is experiment[channel] + + def test_unmeasured_channel_raises_key_error(self, tmp_path): + project = self._polarized_project(tmp_path) + + with pytest.raises(KeyError, match='was not measured'): + project.experimental_data_for_model_at_index(0, channel='pm') + + def test_unknown_channel_raises_value_error(self, tmp_path): + project = self._polarized_project(tmp_path) + + with pytest.raises(ValueError, match='Unknown spin channel'): + project.experimental_data_for_model_at_index(0, channel='xx') + + def test_channel_on_unpolarized_experiment_raises_value_error(self, tmp_path): + path = TestLoadPolarizedExperiment._write_channel_file(tmp_path, 'sample.txt') + project = Project() + project.calculator = 'refl1d' + project.default_model() + project.load_experiment_for_model_at_index(path, 0) + + assert project.experiment_is_polarized_at_index(0) is False + assert project.experiment_channels_at_index(0) == [] + with pytest.raises(ValueError, match='not polarized'): + project.experimental_data_for_model_at_index(0, channel='pp') + + def test_missing_experiment_raises_index_error(self): + project = Project() + project.default_model() + + assert project.experiment_is_polarized_at_index(0) is False + with pytest.raises(IndexError): + project.experimental_data_for_model_at_index(0, channel='pp') + + def test_model_data_per_channel_differs_for_magnetic_model(self): + global_object.map._clear() + model = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=40.0)) + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(model) + q_range = np.linspace(0.01, 0.2, 25) + + pp = project.model_data_for_model_at_index(0, q_range=q_range, channel='pp') + mm = project.model_data_for_model_at_index(0, q_range=q_range, channel='mm') + pm = project.model_data_for_model_at_index(0, q_range=q_range, channel='pm') + + assert pp.name.endswith('(pp) for Model 0') + # Each cross-section is genuinely different — this is what a per-channel + # display/report must show instead of one curve repeated four times. + assert not np.allclose(pp.y, mm.y) + assert not np.allclose(pp.y, pm.y) + + def test_spin_flip_channel_of_non_magnetic_model_raises(self): + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.default_model() + + with pytest.raises(ValueError, match='requires magnetism'): + project.model_data_for_model_at_index(0, channel='pm') + + class TestFitPolarized: def test_two_channel_nsf_fit_recovers_rho_m(self): truth = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=270.0)) From a60e096c16ffa6aa48c03832395bb462c49da54d Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Fri, 14 Aug 2026 15:48:45 +0200 Subject: [PATCH 09/22] fixed polarized file load issue --- CHANGELOG.md | 6 ++++ src/easyreflectometry/summary/summary.py | 45 +++++++++++++++--------- tests/summary/test_summary.py | 42 ++++++++++++++++++++++ tests/test_polarized_fitting.py | 14 ++++++++ 4 files changed, 90 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5a12f60..9140b4ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,12 @@ returned. without a calculated overlay rather than with the channel-agnostic curve. Previously a polarized experiment made the report figures fail on `PolarizedDataSet.x`. +- The summary's experiments table lists one row per spin channel of a + polarized experiment, named ` ()`. It previously + raised + `AttributeError: 'PolarizedDataSet' object has no attribute 'x'`, + which crashed applications that read the summary while a polarized + experiment was loaded. - New `calculate_channel(q, model, channel)` on the wrapper (and `reflectivity_profile_channel` on the calculator, `fit_func_for_channel` on `CalculatorFactory`) evaluates one explicit diff --git a/src/easyreflectometry/summary/summary.py b/src/easyreflectometry/summary/summary.py index d3ba8601..89b82f48 100644 --- a/src/easyreflectometry/summary/summary.py +++ b/src/easyreflectometry/summary/summary.py @@ -304,28 +304,39 @@ def _experiments_section(self) -> str: html_experiments = [] for idx, experiment in self._project.experiments.items(): - experiment_name = experiment.name - num_data_points = len(experiment.x) - resolution_function = experiment.model.resolution_function.as_dict()['smearing'] - if resolution_function == 'PercentageFwhm': - precentage = experiment.model.resolution_function.as_dict()['constant'] - resolution_function = f'{resolution_function} {precentage}%' - range_min = min(experiment.y) - range_max = max(experiment.y) - range_units = 'Å⁻¹' - html_experiment = HTML_DATA_COLLECTION_TEMPLATE - html_experiment = html_experiment.replace('experiment_name', _truncate_name(experiment_name)) - html_experiment = html_experiment.replace('range_min', _format_value(range_min, 2)) - html_experiment = html_experiment.replace('range_max', _format_value(range_max, 2)) - html_experiment = html_experiment.replace('range_units', f'{range_units}') - html_experiment = html_experiment.replace('num_data_points', f'{num_data_points}') - html_experiment = html_experiment.replace('resolution_function', f'{resolution_function}') - html_experiments.append(html_experiment) + # A polarized experiment holds one dataset per spin channel; report + # one row per channel, as the plots already draw one curve each. + channels = getattr(experiment, 'available_channels', None) + if channels is None: + rows = [(experiment.name, experiment)] + else: + rows = [(f'{experiment.name} ({channel.value})', experiment[channel]) for channel in channels] + for row_name, dataset in rows: + html_experiments.append(self._experiment_row(row_name, dataset, experiment.model)) html_experiments_str = '\n'.join(html_experiments) return html_experiments_str + def _experiment_row(self, experiment_name, dataset, model) -> str: + """One row of the experiments table for a single measured dataset.""" + num_data_points = len(dataset.x) + resolution_function = model.resolution_function.as_dict()['smearing'] + if resolution_function == 'PercentageFwhm': + precentage = model.resolution_function.as_dict()['constant'] + resolution_function = f'{resolution_function} {precentage}%' + range_min = min(dataset.y) + range_max = max(dataset.y) + range_units = 'Å⁻¹' + html_experiment = HTML_DATA_COLLECTION_TEMPLATE + html_experiment = html_experiment.replace('experiment_name', _truncate_name(experiment_name)) + html_experiment = html_experiment.replace('range_min', _format_value(range_min, 2)) + html_experiment = html_experiment.replace('range_max', _format_value(range_max, 2)) + html_experiment = html_experiment.replace('range_units', f'{range_units}') + html_experiment = html_experiment.replace('num_data_points', f'{num_data_points}') + html_experiment = html_experiment.replace('resolution_function', f'{resolution_function}') + return html_experiment + def _refinement_section(self) -> str: """Refinement section.""" html_refinement = HTML_REFINEMENT_TEMPLATE diff --git a/tests/summary/test_summary.py b/tests/summary/test_summary.py index 49527316..df1a7ac9 100644 --- a/tests/summary/test_summary.py +++ b/tests/summary/test_summary.py @@ -4,6 +4,7 @@ import os from unittest.mock import MagicMock +import numpy as np import pytest from easyscience import global_object @@ -151,6 +152,47 @@ def test_experiments_section_percentage_fhwm(self, project: Project) -> None: # Expect assert 'PercentageFwhm 5%' in html + def test_experiments_section_polarized(self, project: Project, tmp_path) -> None: + # When + # A polarized experiment holds one DataSet1D per spin channel, not the + # x/y arrays an ordinary experiment has — the section used to raise + # AttributeError, which killed the app when QML read the summary. + channel_paths = {} + for channel, suffix in (('pp', 'uu'), ('mm', 'dd')): + path = tmp_path / f'sample_{suffix}.txt' + q = np.linspace(0.01, 0.2, 20) + reflectivity = np.exp(-q * 30) + np.savetxt(path, np.column_stack([q, reflectivity, 0.01 * reflectivity])) + channel_paths[channel] = str(path) + project.calculator = 'refl1d' + project.load_polarized_experiment(channel_paths) + summary = Summary(project) + + # Then + html = summary._experiments_section() + + # Expect: one row per measured channel + assert 'Polarized experiment 0 (pp)' in html + assert 'Polarized experiment 0 (mm)' in html + assert html.count('No. of data points') == 2 + assert '20' in html + + def test_compile_html_summary_polarized(self, project: Project, tmp_path) -> None: + # When + channel_paths = {} + for channel, suffix in (('pp', 'uu'), ('mm', 'dd')): + path = tmp_path / f'sample_{suffix}.txt' + q = np.linspace(0.01, 0.2, 20) + reflectivity = np.exp(-q * 30) + np.savetxt(path, np.column_stack([q, reflectivity, 0.01 * reflectivity])) + channel_paths[channel] = str(path) + project.calculator = 'refl1d' + project.load_polarized_experiment(channel_paths) + summary = Summary(project) + + # Then Expect: the whole report compiles for a polarized experiment + assert 'Polarized experiment 0 (pp)' in summary.compile_html_summary() + def test_refinement_section(self, project: Project) -> None: # When summary = Summary(project) diff --git a/tests/test_polarized_fitting.py b/tests/test_polarized_fitting.py index f0164343..a3b3dc15 100644 --- a/tests/test_polarized_fitting.py +++ b/tests/test_polarized_fitting.py @@ -192,6 +192,20 @@ def test_load_polarized_experiment(self, tmp_path): assert experiment['pp'].model is project.models[0] assert len(experiment['pp'].x) == 20 + def test_second_polarized_experiment_gets_the_next_index(self, tmp_path): + pp_path = self._write_channel_file(tmp_path, 'sample_uu.txt') + mm_path = self._write_channel_file(tmp_path, 'sample_dd.txt') + + project = Project() + project.calculator = 'refl1d' + project.default_model() + + first = project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) + second = project.load_polarized_experiment({'pp': pp_path}) + + assert (first, second) == (0, 1) + assert len(project.experiments) == 2 + def test_multi_dataset_file_is_rejected(self, tmp_path): import os From 0fe7b33c6a1e125ebeead487bf96ce031e9892b1 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Fri, 14 Aug 2026 22:16:32 +0200 Subject: [PATCH 10/22] enable magnetic layers --- CHANGELOG.md | 17 ++ src/easyreflectometry/fitting.py | 106 +++++++++++++ src/easyreflectometry/limits.py | 5 +- src/easyreflectometry/project.py | 6 + .../sample/elements/layers/layer_magnetism.py | 5 + tests/test_limits.py | 51 ++++++ tests/test_polarized_fitting.py | 146 ++++++++++++++++++ 7 files changed, 334 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9140b4ef..d3a4c873 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,23 @@ returned. `reflectivity_profile_channel` on the calculator, `fit_func_for_channel` on `CalculatorFactory`) evaluates one explicit spin channel without touching the global `polarization_channel` state. +- New `MultiFitter.for_experiments(experiments)` builds a fitter with + one fit function per dataset — one per measured spin channel for a + polarized experiment, one for an ordinary one — across any number of + experiments and models, and returns without running the fit. + `fit_datasets` and `fit_channels` give the flat dataset list in + fit-function order, so an application can prepare the data arrays and + drive `easy_science_multi_fitter.fit(...)` from a worker thread. +- New `MultiFitter.record_fit_results(results)` adopts results from such + a caller-driven fit, so `chi2` and `reduced_chi` describe it instead + of reporting that no fit was performed. The classical metrics need the + original data arrays and stay None. +- `rho_m` now takes part in the project's default-limit policy: it is + created with `default_limits_pending`, and + `Project._sync_parameter_states` gives it the shared SLD window (-1 + to 10) unless an explicit `Parameter` with its own bounds was passed. + `theta_m` keeps its explicit 0-360 bounds. Previously both stayed + unbounded, which made them awkward to fit and to display. - New `MultiFitter.fit_polarized(data)` fits all measured channels of a `PolarizedDataSet` simultaneously against the shared model: one fit function per channel, common structural parameters, magnetic diff --git a/src/easyreflectometry/fitting.py b/src/easyreflectometry/fitting.py index a1958d8b..21663613 100644 --- a/src/easyreflectometry/fitting.py +++ b/src/easyreflectometry/fitting.py @@ -197,6 +197,91 @@ def wrapped(*args, **kwargs): self._classical_fit_metrics: list[dict] | None = None self._objective = _validate_objective(objective) self._sampler: Sampler | None = None + # Set by `for_experiments`: the datasets the fit functions correspond + # to, and the spin channel each one is evaluated on (None = unpolarized). + self.fit_datasets: list[DataSet1D] = [] + self.fit_channels: list[Any] = [] + + @classmethod + def for_experiments( + cls, + experiments: list[DataSet1D | PolarizedDataSet], + objective: str = 'hybrid', + ) -> 'MultiFitter': + """Build a fitter for a mixed list of unpolarized and polarized experiments. + + Every experiment contributes one fit function per dataset: an ordinary + experiment one, a polarized experiment one per measured spin channel, + each evaluating that channel's spin cross-section against the single + model the channels share. Structural parameters are therefore common to + all channels and the magnetic ones are constrained by all of them at + once, exactly as in :meth:`fit_polarized` — but here several experiments + (and several models) can be fitted together, which is what an + application's "fit everything that is loaded" action needs. + + The resulting fitter is *not* run: the caller supplies the data arrays + to ``easy_science_multi_fitter.fit(...)`` in the order given by + :attr:`fit_datasets`, which lets a GUI drive it from a worker thread. + + Parameters + ---------- + experiments : list[DataSet1D | PolarizedDataSet] + The loaded experiments, in the order they should be fitted. + objective : str, optional + Zero-variance handling strategy, see :meth:`__init__`. By default, 'hybrid'. + + Returns + ------- + MultiFitter + Fitter whose ``easy_science_multi_fitter`` has one fit function per + entry of ``fit_datasets``, with ``fit_channels`` holding the + matching :class:`PolarizationChannel` (``None`` when unpolarized). + """ + if not experiments: + raise ValueError('At least one experiment is required to build a fitter.') + + models: list[Model] = [] + datasets: list[DataSet1D] = [] + channels: list[Any] = [] + for experiment in experiments: + model = experiment.model + if model is None: + raise ValueError(f"Experiment '{getattr(experiment, 'name', experiment)}' has no model to fit.") + # `in` would compare models by value; identity is what matters here. + if not any(model is known for known in models): + models.append(model) + experiment_channels = getattr(experiment, 'available_channels', None) + if experiment_channels is None: + datasets.append(experiment) + channels.append(None) + continue + for channel in experiment_channels: + datasets.append(experiment[channel]) + channels.append(channel) + + fitter = cls(*models, objective=objective) + + def func_wrapper(func, unique_name): + """Func wrapper.""" + + def wrapped(*args, **kwargs): + """Wrapped function.""" + return func(*args, unique_name, **kwargs) + + return wrapped + + fit_funcs = [] + for dataset, channel in zip(datasets, channels): + model = dataset.model + interface = model.interface + func = interface.fit_func if channel is None else interface.fit_func_for_channel(channel) + fit_funcs.append(func_wrapper(func, model.unique_name)) + + fitter._fit_func = fit_funcs + fitter.easy_science_multi_fitter = EasyScienceMultiFitter(models, fit_funcs) + fitter.fit_datasets = datasets + fitter.fit_channels = channels + return fitter def fit(self, data: sc.DataGroup, id: int = 0, objective: str | None = None) -> sc.DataGroup: """Perform the fitting and populate the DataGroups with the result. @@ -660,6 +745,27 @@ def objective_reduced_chi(self) -> float | None: """Objective-space reduced chi-squared returned by the minimizer.""" return self.reduced_chi + def record_fit_results(self, results: list[FitResults] | None) -> None: + """Adopt fit results produced elsewhere, so this fitter reports on them. + + An application that drives ``easy_science_multi_fitter.fit(...)`` itself + — to run it in a worker thread, for instance — leaves the ``MultiFitter`` + that owns the goodness-of-fit properties none the wiser. Handing the + results back here makes :attr:`chi2` / :attr:`reduced_chi` describe that + fit instead of reporting "no fit performed". + + Only the minimizer-reported metrics are restored: the classical ones + need the original data arrays, which are not part of ``FitResults``, so + :attr:`classical_chi2` and :attr:`classical_reduced_chi` stay None. + + Parameters + ---------- + results : list[FitResults] | None + Results of the fit, one per fitted dataset. None clears them. + """ + self._fit_results = list(results) if results else None + self._classical_fit_metrics = None + def switch_minimizer(self, minimizer: AvailableMinimizers) -> None: """Switch the minimizer for the fitting. diff --git a/src/easyreflectometry/limits.py b/src/easyreflectometry/limits.py index 001bba64..b41a37a1 100644 --- a/src/easyreflectometry/limits.py +++ b/src/easyreflectometry/limits.py @@ -17,14 +17,15 @@ def apply_default_limits(parameter: Parameter, kind: str) -> None: parameter : Parameter The parameter to adjust. kind : str - One of 'thickness', 'roughness', 'sld', 'isld', 'scale'. + One of 'thickness', 'roughness', 'sld', 'isld', 'rho_m', 'scale'. """ if not parameter.independent: return if kind in ('thickness', 'roughness'): _apply_percentage_limits(parameter) - elif kind in ('sld', 'isld'): + elif kind in ('sld', 'isld', 'rho_m'): + # A magnetic SLD is an SLD: same physical scale, same default window. _apply_fixed_limits(parameter, *SLD_LIMITS) elif kind == 'scale': _apply_fixed_limits(parameter, *SCALE_LIMITS) diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index 6ad7c646..0559ac4f 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -133,6 +133,12 @@ def _sync_parameter_states(self) -> None: for layer in assembly.layers: self._sync_layer_parameter_state(layer.thickness, 'thickness', disabled_ids) self._sync_layer_parameter_state(layer.roughness, 'roughness', disabled_ids) + magnetism = getattr(layer, 'magnetism', None) + if magnetism is not None: + # Magnetic parameters exist only on magnetic layers, so + # they are never in `disabled_ids`; theta_m carries + # explicit 0-360 bounds and needs no default window. + self._sync_layer_parameter_state(magnetism.rho_m, 'rho_m', disabled_ids) def _sync_layer_parameter_state(self, parameter: Parameter, kind: str, disabled_ids: set[int]) -> None: """Update a layer parameter's enabled state and pending default limits.""" diff --git a/src/easyreflectometry/sample/elements/layers/layer_magnetism.py b/src/easyreflectometry/sample/elements/layers/layer_magnetism.py index 7b5c85bf..dcf16132 100644 --- a/src/easyreflectometry/sample/elements/layers/layer_magnetism.py +++ b/src/easyreflectometry/sample/elements/layers/layer_magnetism.py @@ -72,12 +72,17 @@ def __init__( if unique_name is None: unique_name = global_object.generate_unique_name(self.__class__.__name__) + rho_m_value = rho_m rho_m = get_as_parameter( name='rho_m', value=rho_m, default_dict=DEFAULTS, unique_name_prefix=f'{unique_name}_RhoM', ) + # The default bounds are infinite; `Project._sync_parameter_states` + # narrows them to the shared SLD window unless the caller passed an + # explicit Parameter with its own bounds (same contract as `Layer`). + rho_m.default_limits_pending = not isinstance(rho_m_value, Parameter) theta_m = get_as_parameter( name='theta_m', value=theta_m, diff --git a/tests/test_limits.py b/tests/test_limits.py index 2fd1cc74..190b0d32 100644 --- a/tests/test_limits.py +++ b/tests/test_limits.py @@ -151,3 +151,54 @@ def test_existing_parameter_bounds_preserved(self): mat = Material(sld=custom_sld) assert mat.sld.min == -0.5 assert mat.sld.max == 7.0 + + +class TestMagneticParameterLimits: + def setup_method(self): + global_object.map._clear() + + def test_rho_m_uses_the_sld_window(self): + param = Parameter('rho_m', 5.0, min=-np.inf, max=np.inf) + apply_default_limits(param, 'rho_m') + assert param.min == SLD_LIMITS[0] + assert param.max == SLD_LIMITS[1] + + def test_magnetism_constructor_keeps_default_bounds_until_project_sync(self): + from easyreflectometry.sample import LayerMagnetism + + magnetism = LayerMagnetism(rho_m=5.0) + assert np.isinf(magnetism.rho_m.min) + assert np.isinf(magnetism.rho_m.max) + + def test_project_sync_narrows_rho_m_and_leaves_theta_m(self): + from easyreflectometry.project import Project + from easyreflectometry.sample import LayerMagnetism + + project = Project() + project.calculator = 'refl1d' + project.default_model() + layer = project.models[0].sample[1].layers[0] + layer.magnetism = LayerMagnetism(rho_m=5.0, theta_m=40.0) + + project._sync_parameter_states() + + assert layer.magnetism.rho_m.min == SLD_LIMITS[0] + assert layer.magnetism.rho_m.max == SLD_LIMITS[1] + # theta_m ships with explicit physical bounds; the sync must not touch them. + assert layer.magnetism.theta_m.min == 0.0 + assert layer.magnetism.theta_m.max == 360.0 + + def test_project_sync_keeps_explicit_rho_m_bounds(self): + from easyreflectometry.project import Project + from easyreflectometry.sample import LayerMagnetism + + project = Project() + project.calculator = 'refl1d' + project.default_model() + layer = project.models[0].sample[1].layers[0] + layer.magnetism = LayerMagnetism(rho_m=Parameter('rho_m', 5.0, min=1.0, max=8.0)) + + project._sync_parameter_states() + + assert layer.magnetism.rho_m.min == 1.0 + assert layer.magnetism.rho_m.max == 8.0 diff --git a/tests/test_polarized_fitting.py b/tests/test_polarized_fitting.py index a3b3dc15..81fef9e2 100644 --- a/tests/test_polarized_fitting.py +++ b/tests/test_polarized_fitting.py @@ -421,3 +421,149 @@ def test_fit_polarized_requires_single_model(self): fitter = MultiFitter(model_a, model_b) with pytest.raises(ValueError): fitter.fit_polarized(data) + + +class TestMultiFitterForExperiments: + """`MultiFitter.for_experiments` — one fit function per dataset, channels expanded.""" + + @staticmethod + def _unpolarized_data(model, name='plain') -> DataSet1D: + reflectivity = np.exp(-Q * 30) + dataset = DataSet1D(name=name, x=Q, y=reflectivity, ye=(0.01 * reflectivity) ** 2) + dataset.model = model + return dataset + + def test_polarized_experiment_expands_to_one_function_per_channel(self): + model = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=45.0)) + model.interface = _refl1d_interface() + reference = model.interface.polarized_reflectivity_profiles(Q, model.unique_name) + data = _polarized_data(dict(reference), model=model) + + fitter = MultiFitter.for_experiments([data]) + + assert fitter.fit_channels == [ + PolarizationChannel.PP, + PolarizationChannel.PM, + PolarizationChannel.MP, + PolarizationChannel.MM, + ] + assert fitter.fit_datasets == [data[channel] for channel in data.available_channels] + assert len(fitter._fit_func) == 4 + # Each function evaluates its own cross-section, not four copies of one. + curves = [func(Q) for func in fitter._fit_func] + for index, channel in enumerate(data.available_channels): + assert_allclose(curves[index], reference[channel.value], rtol=1e-9) + + def test_unpolarized_experiment_keeps_one_function(self): + model = _magnetic_model(None) + model.interface = _refl1d_interface() + data = self._unpolarized_data(model) + + fitter = MultiFitter.for_experiments([data]) + + assert fitter.fit_channels == [None] + assert fitter.fit_datasets == [data] + assert_allclose(fitter._fit_func[0](Q), model.interface.fit_func(Q, model.unique_name), rtol=1e-9) + + def test_mixed_experiments_share_one_fitter(self): + """A polarized and an ordinary experiment fitted together, two models.""" + magnetic = _magnetic_model(LayerMagnetism(rho_m=2.0, theta_m=270.0)) + plain = _magnetic_model(None) + interface = _refl1d_interface() + magnetic.interface = interface + plain.interface = interface + reference = magnetic.interface.polarized_reflectivity_profiles(Q, magnetic.unique_name) + polarized = _polarized_data({'pp': reference['pp'], 'mm': reference['mm']}, model=magnetic) + unpolarized = self._unpolarized_data(plain) + + fitter = MultiFitter.for_experiments([polarized, unpolarized]) + + assert fitter.fit_channels == [PolarizationChannel.PP, PolarizationChannel.MM, None] + # Both models' parameters are enumerated, so they are fitted together. + assert len(fitter._models) == 2 + assert len(fitter.easy_science_multi_fitter._fit_functions) == 3 + + def test_repeated_model_is_registered_once(self): + model = _magnetic_model(None) + model.interface = _refl1d_interface() + first = self._unpolarized_data(model, name='a') + second = self._unpolarized_data(model, name='b') + + fitter = MultiFitter.for_experiments([first, second]) + + assert len(fitter._models) == 1 + assert len(fitter.fit_datasets) == 2 + + def test_experiment_without_model_is_rejected(self): + dataset = DataSet1D(name='orphan', x=Q, y=np.ones_like(Q), ye=np.ones_like(Q)) + dataset.model = None + + with pytest.raises(ValueError, match='no model'): + MultiFitter.for_experiments([dataset]) + + def test_empty_experiment_list_is_rejected(self): + with pytest.raises(ValueError, match='At least one experiment'): + MultiFitter.for_experiments([]) + + def test_prepared_fitter_recovers_rho_m_when_run(self): + """The fitter is usable exactly like `fit_polarized`, but caller-driven.""" + truth = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=270.0)) + truth.interface = _refl1d_interface() + reference = truth.interface.polarized_reflectivity_profiles(Q, truth.unique_name) + + model = _magnetic_model(LayerMagnetism(rho_m=1.0, theta_m=270.0)) + model.interface = _refl1d_interface() + rho_m = model.sample[1].layers[0].magnetism.rho_m + rho_m.fixed = False + rho_m.bounds = (0.0, 5.0) + data = _polarized_data({'pp': reference['pp'], 'mm': reference['mm']}, model=model) + + fitter = MultiFitter.for_experiments([data]) + x = [np.asarray(dataset.x) for dataset in fitter.fit_datasets] + y = [np.asarray(dataset.y) for dataset in fitter.fit_datasets] + weights = [1.0 / np.sqrt(np.asarray(dataset.ye)) for dataset in fitter.fit_datasets] + results = fitter.easy_science_multi_fitter.fit(x, y, weights=weights) + + assert all(result.success for result in results) + assert_allclose(rho_m.value, 2.5, atol=0.01) + + +class TestRecordFitResults: + """Results produced by a caller-driven fit can be handed back to the fitter.""" + + def _fitter_and_results(self): + model = _magnetic_model(None) + model.interface = _refl1d_interface() + reflectivity = np.exp(-Q * 30) + dataset = DataSet1D(name='plain', x=Q, y=reflectivity, ye=(0.01 * reflectivity) ** 2) + dataset.model = model + fitter = MultiFitter.for_experiments([dataset]) + results = fitter.easy_science_multi_fitter.fit( + [np.asarray(dataset.x)], [np.asarray(dataset.y)], weights=[1.0 / np.sqrt(np.asarray(dataset.ye))] + ) + return fitter, list(results) + + def test_metrics_are_none_before_recording(self): + fitter, _results = self._fitter_and_results() + + # `easy_science_multi_fitter.fit` bypasses MultiFitter entirely. + assert fitter.chi2 is None + assert fitter.reduced_chi is None + + def test_recording_makes_the_metrics_available(self): + fitter, results = self._fitter_and_results() + + fitter.record_fit_results(results) + + assert fitter.chi2 == pytest.approx(sum(r.chi2 for r in results)) + assert fitter.reduced_chi is not None + # The classical metrics need the original arrays, which FitResults lacks. + assert fitter.classical_chi2 is None + + def test_recording_none_clears_the_metrics(self): + fitter, results = self._fitter_and_results() + fitter.record_fit_results(results) + + fitter.record_fit_results(None) + + assert fitter.chi2 is None From 46dfca691e69b86326cb8dfe8d51b245463a0057 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Sun, 16 Aug 2026 11:21:21 +0200 Subject: [PATCH 11/22] new LayerMagnetism component --- CHANGELOG.md | 231 +++++--- .../calculators/refl1d/wrapper.py | 60 +- src/easyreflectometry/project.py | 454 ++++++++++++++ tests/test_polarized_fitting.py | 557 ++++++++++++++++++ 4 files changed, 1215 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3a4c873..e1de1079 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,85 +1,144 @@ # Unreleased All four polarization channels (pp, pm, mp, mm) are now available from -the refl1d calculator; previously only the non-spin-flip pp channel was +the refl1d calculator. Previously only the non-spin-flip pp channel was returned. -- New `LayerMagnetism` sample element makes magnetism part of the model: - `Layer` accepts an optional `magnetism` (with `rho_m`, the magnetic - SLD, and `theta_m`, the in-plane moment angle, as fittable, serialized - `Parameter`s). Attaching a magnetic layer automatically enables - `include_magnetism` on the calculator (raising `NotImplementedError` - on backends without magnetism support); removing the last magnetic - layer disables it again. `Model.has_magnetism`, +- New `LayerMagnetism` sample element. `Layer` takes an optional + `magnetism` with fittable, serialized `Parameter`s `rho_m` (magnetic + SLD) and `theta_m` (in-plane moment angle). Adding a magnetic layer + turns on `include_magnetism` on the calculator, or raises + `NotImplementedError` if the backend cannot do magnetism. Removing + the last magnetic layer turns it off again. `Model.has_magnetism`, `CalculatorBase.supports_magnetism` and - `Project.calculator_supports_magnetism` expose the state to - applications. -- New `PolarizedDataSet` groups per-spin-channel `DataSet1D` objects - (one file per channel; 'pp'/'mm' only for NSF experiments, spin-flip - channels optional) into one experiment sharing a single model. - `Project.load_polarized_experiment(paths)` loads it from an explicit - channel → file mapping, and - `Project.suggest_polarized_channel_assignment(paths)` pre-fills that - mapping from the ORSO header polarization (`pp`/`mm`/`pm`/`mp` only — - partially-analysed observables such as `po`/`mo`, which measure - channel sums, and `op`/`om`/`unpolarized` are left for the user to - decide) or, for plain text files, from filename tokens - (`_uu`/`_up`/`_pp` → pp, `_dd`/`_down`/`_mm` → mm, `_ud`/`_pm` → pm, - `_du`/`_mp` → mp). -- Experiment and model accessors are channel aware: + `Project.calculator_supports_magnetism` report the current state. +- New `PolarizedDataSet` groups per-channel `DataSet1D` objects (one + file per channel; NSF experiments use 'pp'/'mm' only, spin-flip + channels are optional) into one experiment that shares a single + model. `Project.load_polarized_experiment(paths)` loads from an + explicit channel-to-file mapping. + `Project.suggest_polarized_channel_assignment(paths)` fills that + mapping from the ORSO header polarization (`pp`/`mm`/`pm`/`mp` + only). Partially analysed observables such as `po`/`mo` (channel + sums) and `op`/`om`/`unpolarized` are left for the user. For plain + text files the mapping comes from filename tokens (`_uu`/`_up`/`_pp` + → pp, `_dd`/`_down`/`_mm` → mm, `_ud`/`_pm` → pm, `_du`/`_mp` → + mp). +- Experiment and model accessors are channel-aware. `Project.experimental_data_for_model_at_index(index, channel=...)` - returns the `DataSet1D` of one spin channel (`None`, the default, - keeps the previous behavior and returns the stored experiment), + returns the `DataSet1D` of one spin channel. `channel=None` (the + default) still returns the stored experiment. `Project.model_data_for_model_at_index(index, q_range, channel=...)` - calculates one spin cross-section, and - `Project.experiment_is_polarized_at_index(index)` / - `Project.experiment_channels_at_index(index)` report the polarization - state. Asking for a channel that was not measured raises `KeyError`; - an unknown channel, or any channel on an unpolarized experiment, - raises `ValueError`. -- The summary/report figures now show one measured series per spin - channel of a polarized experiment, each in its channel color, with the - matching calculated cross-section. Channels whose cross-section cannot - be calculated (e.g. spin-flip on a non-magnetic model) are shown - without a calculated overlay rather than with the channel-agnostic - curve. Previously a polarized experiment made the report figures fail - on `PolarizedDataSet.x`. -- The summary's experiments table lists one row per spin channel of a + calculates one spin cross-section. + `Project.experiment_is_polarized_at_index(index)` and + `Project.experiment_channels_at_index(index)` report the + polarization state. A channel that was not measured raises + `KeyError`. An unknown channel, or any channel on an unpolarized + experiment, raises `ValueError`. +- Summary/report figures now plot one measured series per spin channel + of a polarized experiment, each in its channel colour, plus the + matching calculated cross-section. Channels that cannot be + calculated (for example spin-flip on a non-magnetic model) are shown + without a calculated overlay. Previously a polarized experiment made + the report figures fail on `PolarizedDataSet.x`. +- The summary experiments table lists one row per spin channel of a polarized experiment, named ` ()`. It previously - raised - `AttributeError: 'PolarizedDataSet' object has no attribute 'x'`, - which crashed applications that read the summary while a polarized + raised `AttributeError: 'PolarizedDataSet' object has no attribute + 'x'` and crashed anything that read the summary while a polarized experiment was loaded. +- New `Project.calculators_supporting_magnetism` lists the available + calculators that can model magnetic samples, without switching the + active one. `Project.models_have_magnetism` reports whether any + model has a magnetic layer. Use these to pick a suitable engine, or + to refuse one that cannot carry the sample's magnetism, instead of + hitting an error inside the binding. +- New `Project.magnetic_sld_data_for_model_at_index(index)` returns + the depth profiles of a magnetic model as `DataSet1D`s keyed + `'sld'`, `'rho_m'`, `'theta_m'`, `'spin_up'` and `'spin_down'`. The + last two are the potentials each spin state sees, + rho +/- rho_m*cos(theta_m - A). The guide-field angle A is the new + module constant `GUIDE_FIELD_ANGLE` (270 degrees, refl1d's default + and the only value the library can currently model). A non-magnetic + model raises `ValueError`. + `Project.model_has_magnetism_at_index(index)` reports whether the + model is magnetic. +- The magnetic depth profile is now built by smoothing the two + in-plane components of the moment and converting back, rather than + smoothing magnitude and angle separately as refl1d does channel by + channel. At an interface where moments differ by a few degrees + across 0/360, the smoothed *angle* used to take the long way around + the circle, pass through the guide-field direction, and report the + full moment as longitudinal. That produced a spurious + spin-up/spin-down splitting exactly at the interface (a 2-degree + difference gave the full 2*rho_m splitting; it is now the correct + ~0.02*rho_m). Collinear samples are unaffected. The reported + `theta_m` profile is restricted to depths that carry a moment (the + angle of a zero-length vector is arbitrary) and is made continuous + within each magnetic region. A profile turning from 359 to 1 degree + is a 2 degree turn; the wrapped values would plot as a full sweep. + If the installed refl1d does not expose the microslab data the + component-safe profile needs, the calculator now raises + `NotImplementedError` instead of falling back to the angle-smoothed + profile. +- New `Project.spin_asymmetry_for_experiment_at_index(index)` returns + the measured spin asymmetry (R++ - R--)/(R++ + R--) of a polarized + experiment, the matching model curve when the model is magnetic, and + the number of points dropped. `ye` holds the SA **variance**, as + everywhere else in the library. Channels measured on different q + grids are interpolated onto the pp grid (values with the linear + weights, variances with their squares) only inside the q range both + channels cover. Outside that range `np.interp` would clamp to the + edge value. Dropped points are reported as `out_of_overlap_points`. + Points where R++ + R-- is not above `SPIN_ASYMMETRY_SIGNIFICANCE` + (3) times its own uncertainty are also dropped. A second, + uncertainty-independent guard drops points whose denominator is + non-positive or smaller than + `SPIN_ASYMMETRY_CANCELLATION_FRACTION` (1e-3) of |R++| + |R--|. + Without it, a file with no uncertainties (two columns, or a + malformed uncertainty array) had no guard, and background-subtracted + data could put values of +/-1e3 on the axis. Points with a + non-finite reflectivity or a negative/non-finite variance are + dropped rather than treated as having no uncertainty. Dropped + points are reported by reason (`low_significance_points`, + `small_denominator_points`, `invalid_points`). +- Both channels of a spin asymmetry are validated before use. Empty, + length-mismatched, non-finite or duplicated q grids are rejected, + and `experiment_supports_spin_asymmetry_at_index` reports False for + them. A descending grid is sorted before pairing; `np.interp` + silently returns nonsense for one. + `Project.experiment_supports_spin_asymmetry_at_index(index)` reports + whether both non-spin-flip channels were measured. - New `calculate_channel(q, model, channel)` on the wrapper (and `reflectivity_profile_channel` on the calculator, - `fit_func_for_channel` on `CalculatorFactory`) evaluates one explicit - spin channel without touching the global `polarization_channel` state. + `fit_func_for_channel` on `CalculatorFactory`) evaluates one + explicit spin channel without touching the global + `polarization_channel` state. - New `MultiFitter.for_experiments(experiments)` builds a fitter with - one fit function per dataset — one per measured spin channel for a - polarized experiment, one for an ordinary one — across any number of + one fit function per dataset (one per measured spin channel for a + polarized experiment, one for an ordinary one) across any number of experiments and models, and returns without running the fit. `fit_datasets` and `fit_channels` give the flat dataset list in - fit-function order, so an application can prepare the data arrays and - drive `easy_science_multi_fitter.fit(...)` from a worker thread. -- New `MultiFitter.record_fit_results(results)` adopts results from such - a caller-driven fit, so `chi2` and `reduced_chi` describe it instead - of reporting that no fit was performed. The classical metrics need the - original data arrays and stay None. -- `rho_m` now takes part in the project's default-limit policy: it is + fit-function order, so an application can prepare the data arrays + and drive `easy_science_multi_fitter.fit(...)` from a worker thread. +- New `MultiFitter.record_fit_results(results)` adopts results from + such a caller-driven fit, so `chi2` and `reduced_chi` describe it + instead of reporting that no fit was performed. The classical + metrics need the original data arrays and stay None. +- `rho_m` now takes part in the project's default-limit policy. It is created with `default_limits_pending`, and - `Project._sync_parameter_states` gives it the shared SLD window (-1 - to 10) unless an explicit `Parameter` with its own bounds was passed. - `theta_m` keeps its explicit 0-360 bounds. Previously both stayed - unbounded, which made them awkward to fit and to display. -- New `MultiFitter.fit_polarized(data)` fits all measured channels of a - `PolarizedDataSet` simultaneously against the shared model: one fit - function per channel, common structural parameters, magnetic + `Project._sync_parameter_states` gives it the shared SLD window + (-1 to 10) unless an explicit `Parameter` with its own bounds was + passed. `theta_m` keeps its explicit 0-360 bounds. Previously both + stayed unbounded. +- New `MultiFitter.fit_polarized(data)` fits all measured channels of + a `PolarizedDataSet` simultaneously against the shared model: one + fit function per channel, common structural parameters, magnetic parameters constrained by all channels at once. Returns per-channel `FitResults`. - The refl1d wrapper now caches the four polarized cross-sections per - model state and (q, dq) grid — they come from a single kernel - evaluation, so a simultaneous N-channel fit costs about one evaluation - per iteration instead of N. + model state and (q, dq) grid. They come from a single kernel + evaluation, so a simultaneous N-channel fit costs about one + evaluation per iteration instead of N. - New `polarized_reflectivity_profiles(x_array, model_id)` on the calculator (and on `CalculatorFactory`) returns the reflectivity of @@ -88,33 +147,33 @@ returned. `include_magnetism = True`. - New `polarization_channel` property (accepts `'pp'`/`'pm'`/`'mp'`/`'mm'` or the new `PolarizationChannel` enum) - selects which channel `reflectity_profile` — and hence fitting — - returns, enabling fits against spin-flip or mm data. Default `'pp'`; - disabling magnetism resets it to `'pp'`. Note: the channel belongs to - the currently active calculator instance, not to a model or dataset — - it affects every subsequent calculation with that calculator, and - `interface.switch(...)` constructs a fresh calculator, resetting it - (along with `include_magnetism`). + selects which channel `reflectity_profile` (and therefore fitting) + returns, so fits can target spin-flip or mm data. Default `'pp'`; + disabling magnetism resets it to `'pp'`. The channel belongs to the + currently active calculator instance, not to a model or dataset. It + affects every subsequent calculation with that calculator. + `interface.switch(...)` constructs a fresh calculator and resets + both this and `include_magnetism`. - New `magnetic_sld_profile(model_id)` on the calculator (and on `CalculatorFactory`) returns the nuclear and magnetic scattering - length density profiles as a tuple `z`, `sld(z)`, `rhoM(z)` (magnetic - SLD) and `thetaM(z)` (magnetic angle). Requires + length density profiles as a tuple `z`, `sld(z)`, `rhoM(z)` + (magnetic SLD) and `thetaM(z)` (magnetic angle). Requires `include_magnetism = True`; refl1d only. -- Magnetic calculations now always build all four refl1d cross-sections, - so they may take somewhat longer than before; pp results are - unchanged. +- Magnetic calculations now always build all four refl1d + cross-sections, so they may take somewhat longer than before. pp + results are unchanged. - Bug fix: `include_magnetism = True` on a refnx-backed calculator now - raises `NotImplementedError`. Previously it was silently accepted (the - guard sat on a property the calculator never called) even though refnx - magnetism is not supported. -- Bug fix (pre-existing): disabling magnetism after layers were created - with it enabled used to leave refl1d `Magnetism` objects on the slabs, - making a subsequent unpolarized calculation raise `AttributeError` - inside refl1d. Disabling magnetism now strips the magnetic state from - existing layers, so the unpolarized path works again. Magnetic - parameters (`rhoM`/`thetaM`) are kept in a per-layer store inside the - wrapper, so they survive a disable/re-enable cycle and are re-attached - when magnetism is enabled again; `update_layer` also accepts the + raises `NotImplementedError`. Previously it was silently accepted + (the guard sat on a property the calculator never called) even + though refnx magnetism is not supported. +- Bug fix (pre-existing): disabling magnetism after layers were + created with it enabled used to leave refl1d `Magnetism` objects on + the slabs, and a later unpolarized calculation raised + `AttributeError` inside refl1d. Disabling magnetism now strips the + magnetic state from existing layers. Magnetic parameters + (`rhoM`/`thetaM`) are kept in a per-layer store inside the wrapper, + so they survive a disable/re-enable cycle and are re-attached when + magnetism is enabled again. `update_layer` also accepts the magnetism keys one at a time. # Version 1.7.0 (1 Aug 2026) diff --git a/src/easyreflectometry/calculators/refl1d/wrapper.py b/src/easyreflectometry/calculators/refl1d/wrapper.py index ec27a3ef..169eddd0 100644 --- a/src/easyreflectometry/calculators/refl1d/wrapper.py +++ b/src/easyreflectometry/calculators/refl1d/wrapper.py @@ -2,15 +2,19 @@ # SPDX-License-Identifier: BSD-3-Clause +import logging from typing import Tuple import numpy as np from refl1d import names +from refl1d.profile import build_profile from refl1d.sample.layers import Repeat from ..polarization import POLARIZATION_CHANNEL_TO_INDEX from ..wrapper_base import WrapperBase +logger = logging.getLogger(__name__) + RESOLUTION_PADDING = 3.5 OVERSAMPLING_FACTOR = 21 @@ -422,6 +426,10 @@ def sld_profile(self, model_name: str) -> Tuple[np.ndarray, np.ndarray]: def magnetic_sld_profile(self, model_name: str) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """Return the nuclear and magnetic scattering length density profiles. + The magnetic profile is built by smoothing the two in-plane components + of the moment and converting back, not by smoothing its magnitude and + angle separately — see :meth:`_smoothed_magnetic_vector`. + Parameters ---------- model_name : str @@ -444,10 +452,60 @@ def magnetic_sld_profile(self, model_name: str) -> Tuple[np.ndarray, np.ndarray, model_name=model_name, storage=self.storage, ) - z, sld, _, sld_magnetic, theta_magnetic = names.Experiment(probe=probe, sample=sample).magnetic_smooth_profile() + experiment = names.Experiment(probe=probe, sample=sample) + z, sld, _, _, _ = experiment.magnetic_smooth_profile() + sld_magnetic, theta_magnetic = self._smoothed_magnetic_vector(experiment, z) # -1 to reverse the order return z, sld[::-1], sld_magnetic[::-1], theta_magnetic[::-1] + @staticmethod + def _smoothed_magnetic_vector(experiment, z: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: + """Magnitude and angle of the smoothed in-plane moment. + + refl1d smooths the magnetic microslabs channel by channel, so the + magnitude |rhoM| and the angle thetaM are interpolated independently + across an interface. For two layers whose moments differ by a couple of + degrees around 0/360 (e.g. 359 and 1) the angle then takes the long way + round the circle, passing through the guide-field direction: the profile + reports the *full* moment as longitudinal exactly where it is almost + entirely transverse, which shows up as a spurious spin-up/spin-down + splitting at the interface. + + Smoothing the Cartesian components instead and converting back is + interpolation of the moment as a vector, which is what the physics does. + The reference angle used for the decomposition cancels out. + + Raises + ------ + NotImplementedError + The installed refl1d does not expose the microslab data this needs. + Falling back to its angle-smoothed profile is deliberately *not* + done: that output is wrong in a way a user cannot see, and a silent + change of results after a dependency update is worse than no + profile at all. + """ + try: + slabs = experiment._render_slabs() + offsets = np.cumsum(slabs.w[:-1]) + slabs._z_offset + roughness = slabs.sigma + rho_m = np.asarray(slabs.rhoM, dtype=float) + theta_m = np.asarray(slabs.thetaM, dtype=float) + except (AttributeError, IndexError, TypeError) as exception: # pragma: no cover - refl1d internals + raise NotImplementedError( + 'The installed refl1d does not provide the microslab data needed for a ' + f'component-safe magnetic profile ({exception}). The magnetic depth profile is ' + "unavailable; refl1d's own profile smooths the moment angle separately, which " + 'misreports the spin-up/spin-down splitting at interfaces between differently ' + 'oriented moments.' + ) from exception + + relative_angle = np.radians(theta_m - DEFAULT_THETA_M) + parallel = build_profile(z, offsets, roughness, rho_m * np.cos(relative_angle)) + perpendicular = build_profile(z, offsets, roughness, rho_m * np.sin(relative_angle)) + magnitude = np.hypot(parallel, perpendicular) + angle = (DEFAULT_THETA_M + np.degrees(np.arctan2(perpendicular, parallel))) % 360.0 + return magnitude, angle + def _get_oversampling_q(q_array: np.ndarray, dq_array: np.ndarray, oversampling_factor: int) -> np.ndarray: """Get oversampling q.""" diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index 0559ac4f..3444e45c 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -20,6 +20,7 @@ from easyreflectometry.calculators import CalculatorFactory from easyreflectometry.calculators import PolarizationChannel +from easyreflectometry.calculators.calculator_base import CalculatorBase from easyreflectometry.data import DataSet1D from easyreflectometry.data import PolarizedDataSet from easyreflectometry.data import detect_polarization_channel @@ -45,6 +46,33 @@ Q_MAX = 0.3 Q_RESOLUTION = 500 +# Guide-field angle A in degrees, used to project the moment onto the neutron +# quantisation axis for the spin-up/spin-down potentials and nothing else. +# refl1d's default (`Aguide = 270`), which is what every model the library can +# currently build uses — `Aguide` is not exposed as a parameter yet. Keep this +# the single source of the value: exposing it later is a change here only. +GUIDE_FIELD_ANGLE = 270.0 + +# Points whose spin-asymmetry denominator (R⁺⁺ + R⁻⁻) is smaller than this +# multiple of its own uncertainty are dropped: there SA is noise divided by +# noise and would swamp the axis with ±values of no physical meaning. +SPIN_ASYMMETRY_SIGNIFICANCE = 3.0 + +# Points whose spin-asymmetry denominator (R⁺⁺ + R⁻⁻) is smaller than this +# fraction of |R⁺⁺| + |R⁻⁻| are dropped too: there the sum is what is left after +# cancellation between the two channels, so SA is a ratio of rounding noise. +# Unlike the significance rule above this needs no uncertainties, which is what +# keeps a two-column file from putting ±10³ values on the axis. +SPIN_ASYMMETRY_CANCELLATION_FRACTION = 1e-3 + +# A depth whose magnetic SLD is below this fraction of the largest one in the +# profile carries no moment worth speaking of, and its moment *angle* is +# meaningless — the direction of a (nearly) zero-length vector. Used to restrict +# the reported theta_m profile; a relative floor because it has to work both for +# a weak 0.1 and a strong 5 (1e-6 A^-2) moment, and because the interface +# roughness leaves a small erf tail everywhere. +MAGNETIC_MOMENT_FLOOR_FRACTION = 0.01 + DEFAULT_MINIMIZER = AvailableMinimizers.LMFit_leastsq @@ -329,6 +357,33 @@ def calculator_supports_magnetism(self) -> bool: """ return self._calculator().supports_magnetism + @property + def calculators_supporting_magnetism(self) -> List[str]: + """Names of the available calculators that can model magnetic samples. + + Lets an application offer the switch a magnetic sample needs ("this + requires refl1d — change to it?") instead of only reporting that the + current calculator cannot do it. The active calculator is not touched. + """ + supporting = [] + for name in self._calculator.available_interfaces: + calculator = next( + (candidate for candidate in CalculatorBase._calculators if candidate.name == name), + None, + ) + if calculator is not None and calculator().supports_magnetism: + supporting.append(name) + return supporting + + @property + def models_have_magnetism(self) -> bool: + """Whether any model in the project carries a magnetic layer. + + A calculator without magnetism cannot be selected while this holds — the + binding it would have to build does not exist. + """ + return any(model.has_magnetism for model in self._models) + @property def minimizer(self) -> AvailableMinimizers: """Minimizer function.""" @@ -764,6 +819,74 @@ def sld_data_for_model_at_index(self, index: int = 0) -> DataSet1D: y=sld[1], ) + def model_has_magnetism_at_index(self, index: int = 0) -> bool: + """Whether the model at index carries magnetism (False when there is no such model).""" + try: + return bool(self.models[index].has_magnetism) + except (IndexError, AttributeError): + return False + + def magnetic_sld_data_for_model_at_index(self, index: int = 0) -> Dict[str, DataSet1D]: + """Nuclear and magnetic depth profiles of a magnetic model. + + Parameters + ---------- + index : int + Index of the model. + + Returns + ------- + Dict[str, DataSet1D] + Profiles versus depth z, keyed: + + - ``'sld'``: nuclear ρ(z), the same curve as + :meth:`sld_data_for_model_at_index`; + - ``'rho_m'``: magnetic SLD ρM(z); + - ``'theta_m'``: in-plane moment angle θM(z) in degrees, restricted + to the depths that carry a moment — the angle of a zero-length + vector is meaningless, so points below + :data:`MAGNETIC_MOMENT_FLOOR_FRACTION` of the largest ρM are left + out instead of drawing an arbitrary angle through vacuum; + - ``'spin_up'`` / ``'spin_down'``: the potentials each spin state + sees, ρ(z) ± ρM(z)·cos(θM(z) − A), where A is the guide-field + angle :data:`GUIDE_FIELD_ANGLE`. + + Raises + ------ + ValueError + The model has no magnetic layer, so there is no magnetic profile. + NotImplementedError + The active calculator cannot model magnetism. + """ + model = self.models[index] + if not model.has_magnetism: + raise ValueError( + f'Model {index} has no magnetic layer; there is no magnetic SLD profile to show. ' + 'Attach magnetism to a layer first.' + ) + model.interface = self._calculator + z, sld, rho_m, theta_m = model.interface().magnetic_sld_profile(model.unique_name) + z = np.asarray(z, dtype=float) + sld = np.asarray(sld, dtype=float) + rho_m = np.asarray(rho_m, dtype=float) + theta_m = np.asarray(theta_m, dtype=float) + # Component of the moment along the guide field: what the neutron spin + # states add to / subtract from the nuclear potential. + projection = rho_m * np.cos(np.radians(theta_m - GUIDE_FIELD_ANGLE)) + # Only report the angle where there is a moment to have an angle, and + # make it continuous along z: 359° followed by 1° is a 2° turn, but a + # plotted line through the wrapped values sweeps the whole circle. + magnitude = np.abs(rho_m) + has_moment = magnitude > MAGNETIC_MOMENT_FLOOR_FRACTION * magnitude.max(initial=0.0) + theta_display = _unwrapped_angle(theta_m, has_moment) + return { + 'sld': DataSet1D(name=f'SLD for Model {index}', x=z, y=sld), + 'rho_m': DataSet1D(name=f'Magnetic SLD for Model {index}', x=z, y=rho_m), + 'theta_m': DataSet1D(name=f'Moment angle for Model {index}', x=z[has_moment], y=theta_display[has_moment]), + 'spin_up': DataSet1D(name=f'Spin-up potential for Model {index}', x=z, y=sld + projection), + 'spin_down': DataSet1D(name=f'Spin-down potential for Model {index}', x=z, y=sld - projection), + } + def sample_data_for_model_at_index(self, index: int = 0, q_range: Optional[np.array] = None) -> DataSet1D: """Sample data for model at index.""" original_resolution_function = self.models[index].resolution_function @@ -881,6 +1004,161 @@ def experiment_channels_at_index(self, index: int = 0) -> List[PolarizationChann return [] return experiment.available_channels + def experiment_supports_spin_asymmetry_at_index(self, index: int = 0) -> bool: + """Whether SA can be formed for the experiment at index. + + Spin asymmetry needs both non-spin-flip channels; an NSF-incomplete or + spin-flip-only experiment has no SA. The two channel datasets must also + be structurally usable (non-empty, matching lengths, a strictly ordered + q grid) — an application asks this before offering a spin-asymmetry + view, so a dataset that cannot be turned into one must not be advertised + and then fail. + """ + channels = self.experiment_channels_at_index(index) + if PolarizationChannel.PP not in channels or PolarizationChannel.MM not in channels: + return False + experiment = self._experiments[index] + try: + for channel in (PolarizationChannel.PP, PolarizationChannel.MM): + _ordered_channel_arrays(experiment[channel]) + except ValueError as exception: + logger.warning('Experiment %s cannot produce a spin asymmetry: %s', index, exception) + return False + return True + + def spin_asymmetry_for_experiment_at_index(self, index: int = 0) -> Dict[str, object]: + """Spin asymmetry SA = (R⁺⁺ − R⁻⁻) / (R⁺⁺ + R⁻⁻) of a polarized experiment. + + The measured SA is formed on the q grid of the pp channel. A mm channel + measured on a different grid is linearly interpolated onto it — values + with the usual weights, variances with the *squared* weights, which is + the propagation rule for independent endpoints (the covariance this + introduces between neighbouring SA points is not representable in + `DataSet1D` and is discarded). Interpolation happens **only inside the q + range both channels cover**: `np.interp` would otherwise clamp to the + edge value and turn extrapolated points into fabricated measurements. + + Points are dropped when the denominator cannot carry a meaningful + asymmetry: + + - it is not positive, or is lost to cancellation between the two + channels (see :data:`SPIN_ASYMMETRY_CANCELLATION_FRACTION`) — this + guard needs no uncertainties and is what keeps a background-subtracted + tail from throwing ±10³ values onto the axis; + - it is not above :data:`SPIN_ASYMMETRY_SIGNIFICANCE` times its own + uncertainty, when the channels carry usable uncertainties; + - the point itself is not usable (non-finite reflectivity, or a negative + or non-finite variance). + + Parameters + ---------- + index : int + Index of the experiment. + + Returns + ------- + Dict[str, object] + - ``'measured'``: `DataSet1D` of SA versus q. As everywhere in this + library, ``ye`` holds **variances**, not standard deviations. + - ``'calculated'``: `DataSet1D` of the model SA on the same q + points, or None when the model is not magnetic (an unpolarized + model has SA ≡ 0, which is not worth drawing). + - ``'masked_points'``: how many measured points were dropped for any + of the reasons above. + - ``'low_significance_points'``: of those, how many had a + denominator below the significance threshold. + - ``'small_denominator_points'``: of those, how many had a + denominator that was non-positive or lost to cancellation. + - ``'invalid_points'``: of those, how many carried a non-finite + reflectivity or an unusable variance. + - ``'out_of_overlap_points'``: number of pp points dropped because + the mm channel does not cover their q. + + Raises + ------ + IndexError + No experiment is loaded at `index`. + ValueError + The experiment does not carry both pp and mm channels, or their + datasets are not structurally usable. + """ + if index not in self._experiments.keys(): + raise IndexError(f'No experiment data for model at index {index}') + channels = self.experiment_channels_at_index(index) + if PolarizationChannel.PP not in channels or PolarizationChannel.MM not in channels: + raise ValueError(f'Experiment {index} does not have both non-spin-flip channels; spin asymmetry needs pp and mm.') + + experiment = self._experiments[index] + q, r_pp, var_pp = _ordered_channel_arrays(experiment[PolarizationChannel.PP]) + q_mm, r_mm_source, var_mm_source = _ordered_channel_arrays(experiment[PolarizationChannel.MM]) + + out_of_overlap = 0 + if q.shape == q_mm.shape and np.allclose(q, q_mm, rtol=1e-9, atol=0.0): + # The usual case: both channels come from one instrument scan. + # A tolerance keeps grids that only differ by float round-trips + # (text files) on this path. + r_mm, var_mm = r_mm_source, var_mm_source + else: + # Different grids: put mm on the pp grid, but only where mm has data + # — np.interp clamps outside its range, which would silently invent + # measurements at the edges. + inside = (q >= q_mm.min()) & (q <= q_mm.max()) + out_of_overlap = int(np.count_nonzero(~inside)) + if out_of_overlap: + logger.warning( + 'Spin asymmetry of experiment %s: %s of %s pp points lie outside the mm q range ' + '[%.5g, %.5g] and are dropped.', + index, + out_of_overlap, + q.size, + q_mm.min(), + q_mm.max(), + ) + q, r_pp, var_pp = q[inside], r_pp[inside], var_pp[inside] + r_mm, var_mm = _interpolate_with_variance(q, q_mm, r_mm_source, var_mm_source) + + asymmetry, variance, keep, reasons = _spin_asymmetry(r_pp, r_mm, var_pp, var_mm) + measured = DataSet1D( + name=f'Spin asymmetry for Experiment {index}', + x=q[keep], + y=asymmetry[keep], + ye=variance[keep], + x_label='q (1/angstrom)', + y_label='Spin asymmetry', + ) + + calculated = None + model_index = self._model_index_for_experiment(experiment) + if model_index is not None and self.models[model_index].has_magnetism and measured.x.size > 0: + calculated_pp = self.model_data_for_model_at_index(model_index, q_range=measured.x, channel='pp').y + calculated_mm = self.model_data_for_model_at_index(model_index, q_range=measured.x, channel='mm').y + calculated_asymmetry, _, _, _ = _spin_asymmetry(calculated_pp, calculated_mm) + calculated = DataSet1D( + name=f'Calculated spin asymmetry for Experiment {index}', + x=measured.x, + y=calculated_asymmetry, + x_label='q (1/angstrom)', + y_label='Spin asymmetry', + ) + + return { + 'measured': measured, + 'calculated': calculated, + 'masked_points': int(np.count_nonzero(~keep)), + 'out_of_overlap_points': out_of_overlap, + **reasons, + } + + def _model_index_for_experiment(self, experiment) -> Optional[int]: + """Index of the model an experiment is bound to, or None.""" + model = getattr(experiment, 'model', None) + if model is None: + return None + for model_index, candidate in enumerate(self._models): + if candidate is model: + return model_index + return None + def default_model(self): """Default model.""" self._replace_collection(MaterialCollection(interface=self._calculator), self._materials) @@ -1185,3 +1463,179 @@ def _replace_collection(self, src_collection: BaseCollection, dst_collection: Ba def _timestamp_modification(self): """Timestamp modification.""" self._info['modified'] = datetime.datetime.now().strftime('%d.%m.%Y %H:%M') + + +def _unwrapped_angle(angle: np.ndarray, mask: np.ndarray) -> np.ndarray: + """Make an angle profile continuous along z within each magnetic region. + + The moment angle is periodic, so a profile that turns smoothly from 359° to + 1° comes back from `atan2` as a jump of nearly 360°. Drawn as a line that is + a full sweep across the chart where the moment barely moves. Each contiguous + run of `mask` (a magnetic region) is therefore unwrapped on its own and then + shifted by whole turns so it sits as close to the conventional [0, 360) + range as possible — neighbouring regions stay independent, since the angle + between them is not defined. + + Parameters + ---------- + angle : np.ndarray + Wrapped angles in degrees. + mask : np.ndarray + Which points carry a moment. + + Returns + ------- + np.ndarray + Angles in degrees, continuous within each masked region. Points outside + the mask are returned unchanged (callers drop them). + """ + unwrapped = np.array(angle, dtype=float, copy=True) + if mask.size == 0 or not np.any(mask): + return unwrapped + + # Each contiguous run of masked points is one magnetic region. + masked_indices = np.flatnonzero(mask) + region_breaks = np.flatnonzero(np.diff(masked_indices) > 1) + 1 + for region in np.split(masked_indices, region_breaks): + segment = np.unwrap(unwrapped[region], period=360.0) + # Keep the drawn values near the usual range rather than at 720 deg. + turns = np.round(np.median(segment) / 360.0 - 0.5) + unwrapped[region] = segment - turns * 360.0 + return unwrapped + + +def _ordered_channel_arrays(dataset: DataSet1D) -> tuple: + """A channel's (q, reflectivity, variance) arrays, ordered and checked. + + Spin asymmetry pairs two channels point by point and interpolates one onto + the other, both of which assume well-formed, strictly increasing q. Rather + than trusting that, the arrays are checked here and sorted when needed — + `np.interp` silently returns nonsense for a descending grid. + + Raises + ------ + ValueError + The dataset is empty, its arrays disagree in length or shape, its q + values are not finite, or it visits the same q twice. + """ + name = getattr(dataset, 'name', '?') + q = np.asarray(getattr(dataset, 'x', np.empty(0)), dtype=float).ravel() + y = np.asarray(getattr(dataset, 'y', np.empty(0)), dtype=float).ravel() + if q.size == 0: + raise ValueError(f"Channel '{name}' has no data points.") + if q.size != y.size: + raise ValueError(f"Channel '{name}' has {q.size} q values for {y.size} reflectivities.") + if not np.all(np.isfinite(q)): + raise ValueError(f"Channel '{name}' has non-finite q values.") + + variance = np.asarray(getattr(dataset, 'ye', None) if getattr(dataset, 'ye', None) is not None else [], dtype=float) + variance = variance.ravel() + if variance.size == 0: + variance = np.zeros_like(y) + elif variance.size != y.size: + logger.warning( + "Channel '%s' has %s uncertainties for %s points; treating it as having none.", + name, + variance.size, + y.size, + ) + variance = np.zeros_like(y) + + order = np.argsort(q, kind='stable') + if not np.array_equal(order, np.arange(q.size)): + logger.warning("Channel '%s' is not ordered in q; sorting it before pairing.", name) + q, y, variance = q[order], y[order], variance[order] + if np.any(np.diff(q) <= 0): + raise ValueError(f"Channel '{name}' visits the same q more than once; the pairing would be ambiguous.") + return q, y, variance + + +def _interpolate_with_variance(q: np.ndarray, q_source: np.ndarray, values: np.ndarray, variances: np.ndarray) -> tuple: + """Linear interpolation of values and their variances onto `q`. + + Values use the linear weights (1−t, t); variances use their **squares**, + which is the propagation rule for independent endpoints — interpolating a + variance linearly (as `np.interp` would) overestimates it, by a factor 2 at + the midpoint of two equal variances. + + `q` must lie inside `q_source`; the caller restricts it to the overlap. + """ + upper = np.clip(np.searchsorted(q_source, q, side='left'), 1, q_source.size - 1) + lower = upper - 1 + span = q_source[upper] - q_source[lower] + # span is > 0 for a strictly increasing source grid; guard anyway. + weight = np.where(span > 0, (q - q_source[lower]) / np.where(span > 0, span, 1.0), 0.0) + interpolated = (1.0 - weight) * values[lower] + weight * values[upper] + interpolated_variance = (1.0 - weight) ** 2 * variances[lower] + weight**2 * variances[upper] + return interpolated, interpolated_variance + + +def _spin_asymmetry( + r_pp: np.ndarray, + r_mm: np.ndarray, + var_pp: Optional[np.ndarray] = None, + var_mm: Optional[np.ndarray] = None, +) -> tuple: + """Spin asymmetry, its variance, which points to keep, and why not. + + Parameters + ---------- + r_pp, r_mm : np.ndarray + Non-spin-flip reflectivities on a common q grid. + var_pp, var_mm : Optional[np.ndarray] + Their variances (`DataSet1D.ye`), or None for a calculated curve. + + Returns + ------- + tuple + SA, its variance (zeros without input variances), a boolean mask of the + points to keep, and a dict counting the dropped ones by reason. + """ + r_pp = np.asarray(r_pp, dtype=float) + r_mm = np.asarray(r_mm, dtype=float) + denominator = r_pp + r_mm + var_pp = np.zeros_like(r_pp) if var_pp is None else np.asarray(var_pp, dtype=float) + var_mm = np.zeros_like(r_mm) if var_mm is None else np.asarray(var_mm, dtype=float) + + # A denominator of exactly zero would divide by zero; those points are + # dropped by the masks below anyway. + safe_denominator = np.where(denominator == 0, np.nan, denominator) + with np.errstate(invalid='ignore', divide='ignore'): + asymmetry = (r_pp - r_mm) / safe_denominator + # sigma_SA = 2 sqrt(R--^2 sigma_++^2 + R++^2 sigma_--^2) / (R++ + R--)^2, + # so the variance is its square. ye holds variances, hence no squaring + # of var_pp / var_mm here. + variance = 4.0 * (r_mm**2 * var_pp + r_pp**2 * var_mm) / safe_denominator**4 + + asymmetry = np.nan_to_num(asymmetry, nan=0.0, posinf=0.0, neginf=0.0) + variance = np.nan_to_num(variance, nan=0.0, posinf=0.0, neginf=0.0) + + # A point with a non-finite reflectivity, or an uncertainty that is not a + # usable variance, cannot produce a meaningful SA — and must not be silently + # demoted to "no uncertainty", which would also skip the significance test. + invalid = ~np.isfinite(r_pp) | ~np.isfinite(r_mm) + invalid |= ~np.isfinite(var_pp) | ~np.isfinite(var_mm) | (var_pp < 0) | (var_mm < 0) + + # Uncertainty-independent guard: the denominator must be positive and must + # not be the small remainder of two much larger numbers. Reflectivities are + # positive, so this only bites on background-subtracted data — which is + # exactly where SA otherwise explodes to +/-1e3 and destroys the axis. + magnitude = np.abs(r_pp) + np.abs(r_mm) + with np.errstate(invalid='ignore'): + degenerate = ~np.isfinite(denominator) | (denominator <= 0) + degenerate |= np.abs(denominator) <= SPIN_ASYMMETRY_CANCELLATION_FRACTION * magnitude + degenerate &= ~invalid + + # Uncertainty-based guard: is the denominator above the noise? + denominator_sigma = np.sqrt(np.clip(var_pp + var_mm, 0.0, None)) + with_uncertainty = denominator_sigma > 0 + insignificant = with_uncertainty & (denominator <= SPIN_ASYMMETRY_SIGNIFICANCE * denominator_sigma) + insignificant &= ~invalid & ~degenerate + + keep = ~invalid & ~degenerate & ~insignificant + reasons = { + 'invalid_points': int(np.count_nonzero(invalid)), + 'small_denominator_points': int(np.count_nonzero(degenerate)), + 'low_significance_points': int(np.count_nonzero(insignificant)), + } + return asymmetry, variance, keep, reasons diff --git a/tests/test_polarized_fitting.py b/tests/test_polarized_fitting.py index 81fef9e2..073b5904 100644 --- a/tests/test_polarized_fitting.py +++ b/tests/test_polarized_fitting.py @@ -32,6 +32,19 @@ Q = np.linspace(0.005, 0.3, 50) +@pytest.fixture(autouse=True) +def _isolated_global_object(): + """Leave the easyscience object map clean for the next test file. + + Several tests here build a `Project` (and therefore a 'project_models' + collection); a leftover registration makes the *next* module's first + `Project()` fail with 'Object name project_models already exists'. + """ + global_object.map._clear() + yield + global_object.map._clear() + + def _magnetic_model(magnetism: LayerMagnetism | None) -> Model: vacuum = Material(sld=0, isld=0, name='Vacuum') material = Material(sld=4.0, isld=0, name='Sld 4') @@ -567,3 +580,547 @@ def test_recording_none_clears_the_metrics(self): fitter.record_fit_results(None) assert fitter.chi2 is None + + +class TestMagneticSldData: + """`Project.magnetic_sld_data_for_model_at_index` (Phase 5a).""" + + @staticmethod + def _magnetic_project(theta_m: float = 270.0) -> Project: + global_object.map._clear() + model = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=theta_m)) + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(model) + return project + + def test_profiles_and_spin_potentials(self): + project = self._magnetic_project() + + profiles = project.magnetic_sld_data_for_model_at_index(0) + + assert set(profiles) == {'sld', 'rho_m', 'theta_m', 'spin_up', 'spin_down'} + sld, rho_m = profiles['sld'], profiles['rho_m'] + assert sld.x.size == rho_m.x.size and sld.x.size > 0 + assert np.allclose(profiles['spin_up'].x, sld.x) + # theta_m = 270 == the guide field, so the projection is the full rho_m: + # spin-up sees rho + rho_m, spin-down rho - rho_m. + assert_allclose(profiles['spin_up'].y, sld.y + rho_m.y, atol=1e-10) + assert_allclose(profiles['spin_down'].y, sld.y - rho_m.y, atol=1e-10) + # The magnetic layer really is magnetic somewhere along z. + assert np.abs(rho_m.y).max() > 0 + + def test_canted_moment_reduces_the_split(self): + along_field = self._magnetic_project(theta_m=270.0).magnetic_sld_data_for_model_at_index(0) + canted = self._magnetic_project(theta_m=270.0 - 60.0).magnetic_sld_data_for_model_at_index(0) + + split_along = np.abs(along_field['spin_up'].y - along_field['spin_down'].y).max() + split_canted = np.abs(canted['spin_up'].y - canted['spin_down'].y).max() + + # cos(60 deg) = 0.5 of the moment is seen by the spin states. + assert_allclose(split_canted, split_along * 0.5, rtol=1e-6) + + def test_nuclear_profile_matches_the_ordinary_sld_curve(self): + project = self._magnetic_project() + + profiles = project.magnetic_sld_data_for_model_at_index(0) + nuclear = project.sld_data_for_model_at_index(0) + + assert_allclose(profiles['sld'].x, nuclear.x) + assert_allclose(profiles['sld'].y, nuclear.y) + + def test_non_magnetic_model_raises_and_reports_no_magnetism(self): + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.default_model() + + assert project.model_has_magnetism_at_index(0) is False + assert project.model_has_magnetism_at_index(7) is False + with pytest.raises(ValueError, match='no magnetic layer'): + project.magnetic_sld_data_for_model_at_index(0) + + +class TestSpinAsymmetry: + """`Project.spin_asymmetry_for_experiment_at_index` (Phase 5b).""" + + @staticmethod + def _project_with_channels(channels: dict, model=None) -> Project: + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(model if model is not None else _magnetic_model(None)) + data = _polarized_data(channels, model=project.models[0]) + project._experiments[0] = data + return project + + def test_asymmetry_and_error_propagation(self): + global_object.map._clear() + r_pp = np.full_like(Q, 0.6) + r_mm = np.full_like(Q, 0.2) + project = self._project_with_channels({'pp': r_pp, 'mm': r_mm}) + + result = project.spin_asymmetry_for_experiment_at_index(0) + measured = result['measured'] + + # (0.6 - 0.2) / 0.8 + assert_allclose(measured.y, 0.5) + # ye holds variances: sigma_SA = 2 sqrt(R--^2 s++^2 + R++^2 s--^2)/(R+++R--)^2 + var_pp = (0.01 * r_pp) ** 2 + var_mm = (0.01 * r_mm) ** 2 + expected = 4.0 * (r_mm**2 * var_pp + r_pp**2 * var_mm) / (r_pp + r_mm) ** 4 + assert_allclose(measured.ye, expected, rtol=1e-12) + assert result['masked_points'] == 0 + + def test_insignificant_points_are_dropped_and_counted(self): + global_object.map._clear() + r_pp = np.full_like(Q, 0.6) + r_mm = np.full_like(Q, 0.2) + # Make the last five points pure noise: huge uncertainty, tiny signal. + r_pp[-5:] = 1e-9 + r_mm[-5:] = 1e-9 + noisy = np.where(np.arange(Q.size) >= Q.size - 5, 1.0, 1e-12) + datasets = { + 'pp': DataSet1D(name='pp', x=Q, y=r_pp, ye=noisy), + 'mm': DataSet1D(name='mm', x=Q, y=r_mm, ye=noisy), + } + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(_magnetic_model(None)) + project._experiments[0] = PolarizedDataSet(name='noisy', channels=datasets, model=project.models[0]) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['masked_points'] == 5 + assert result['measured'].x.size == Q.size - 5 + assert np.all(np.isfinite(result['measured'].y)) + + def test_channels_on_different_q_grids_are_interpolated(self): + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(_magnetic_model(None)) + q_mm = Q + 0.001 + datasets = { + 'pp': DataSet1D(name='pp', x=Q, y=np.full_like(Q, 0.6), ye=np.full_like(Q, 1e-12)), + 'mm': DataSet1D(name='mm', x=q_mm, y=np.full_like(q_mm, 0.2), ye=np.full_like(q_mm, 1e-12)), + } + project._experiments[0] = PolarizedDataSet(name='shifted', channels=datasets, model=project.models[0]) + + result = project.spin_asymmetry_for_experiment_at_index(0) + measured = result['measured'] + + # SA lives on the pp grid, restricted to where mm has data; the constant + # mm channel interpolates to 0.2 there. + assert_allclose(measured.x, Q[Q >= q_mm.min()]) + assert result['out_of_overlap_points'] == int(np.count_nonzero(Q < q_mm.min())) + assert_allclose(measured.y, 0.5, atol=1e-9) + + def test_calculated_asymmetry_only_for_a_magnetic_model(self): + global_object.map._clear() + magnetic = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=270.0)) + magnetic.interface = _refl1d_interface() + reference = magnetic.interface.polarized_reflectivity_profiles(Q, magnetic.unique_name) + project = self._project_with_channels({'pp': reference['pp'], 'mm': reference['mm']}, model=magnetic) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + calculated = result['calculated'] + assert calculated is not None + # Data and model are the same sample, so the two SA curves agree. + assert_allclose(calculated.y, result['measured'].y, atol=1e-6) + assert np.abs(calculated.y).max() > 0.01 # a real magnetic signal + + def test_no_calculated_asymmetry_without_magnetism(self): + global_object.map._clear() + project = self._project_with_channels({'pp': np.full_like(Q, 0.6), 'mm': np.full_like(Q, 0.2)}) + + assert project.spin_asymmetry_for_experiment_at_index(0)['calculated'] is None + + def test_availability_and_errors(self): + global_object.map._clear() + project = self._project_with_channels({'pp': np.full_like(Q, 0.6), 'mm': np.full_like(Q, 0.2)}) + + assert project.experiment_supports_spin_asymmetry_at_index(0) is True + assert project.experiment_supports_spin_asymmetry_at_index(1) is False + + # pp only: no asymmetry to form. + global_object.map._clear() + nsf_incomplete = self._project_with_channels({'pp': np.full_like(Q, 0.6)}) + assert nsf_incomplete.experiment_supports_spin_asymmetry_at_index(0) is False + with pytest.raises(ValueError, match='both non-spin-flip channels'): + nsf_incomplete.spin_asymmetry_for_experiment_at_index(0) + with pytest.raises(IndexError): + project.spin_asymmetry_for_experiment_at_index(3) + + +class TestMagneticProfileSmoothing: + """CR1 M1: the profile must interpolate the moment as a vector.""" + + @staticmethod + def _two_layer_model(theta_top: float, theta_bottom: float) -> Model: + vacuum = Material(sld=0, isld=0, name='Vacuum') + iron = Material(sld=8.0, isld=0, name='Fe') + si = Material(sld=2.047, isld=0, name='Si') + superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase') + top = Layer( + material=iron, + thickness=100, + roughness=5, + magnetism=LayerMagnetism(rho_m=5.0, theta_m=theta_top), + name='Fe top', + ) + bottom = Layer( + material=iron, + thickness=100, + roughness=5, + magnetism=LayerMagnetism(rho_m=5.0, theta_m=theta_bottom), + name='Fe bottom', + ) + subphase = Layer(material=si, thickness=0, roughness=5, name='Si Subphase') + sample = Sample(Multilayer(superphase), Multilayer(top), Multilayer(bottom), Multilayer(subphase), name='Sample') + model = Model(sample=sample, scale=1, background=0, name='Two-layer magnetic') + model.resolution_function = PercentageFwhm(0) + return model + + def _profiles(self, theta_top: float, theta_bottom: float) -> dict: + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(self._two_layer_model(theta_top, theta_bottom)) + return project.magnetic_sld_data_for_model_at_index(0) + + def test_interface_between_almost_antiparallel_angles_does_not_invent_splitting(self): + # 359 deg and 1 deg are 2 deg apart, but smoothing the *angle* takes the + # long way round through 180 deg — and through the guide-field direction + # at 270 deg, where the full moment would look longitudinal. + profiles = self._profiles(359.0, 1.0) + + splitting = np.abs(profiles['spin_up'].y - profiles['spin_down'].y) + + # 2 * 5.0 * |cos(89 deg)| ~ 0.18, not 2 * 5.0. + assert splitting.max() < 0.5 + + def test_collinear_layers_keep_the_full_splitting(self): + # The same geometry with both moments along the guide field must still + # show the whole moment: the fix must not damp real magnetism. + profiles = self._profiles(270.0, 270.0) + + splitting = np.abs(profiles['spin_up'].y - profiles['spin_down'].y) + + assert splitting.max() == pytest.approx(2 * 5.0, rel=1e-3) + + def test_magnitude_and_angle_round_trip(self): + profiles = self._profiles(210.0, 210.0) + + rho_m = profiles['rho_m'] + theta_m = profiles['theta_m'] + + assert rho_m.y.max() == pytest.approx(5.0, rel=1e-3) + # theta_m is reported only where there is a moment. + assert theta_m.x.size < rho_m.x.size + assert np.allclose(theta_m.y, 210.0, atol=1e-6) + + def test_angle_is_not_reported_through_non_magnetic_regions(self): + profiles = self._profiles(270.0, 270.0) + + theta_m = profiles['theta_m'] + rho_m = profiles['rho_m'] + + # Every reported angle sits at a depth that carries a moment. + carried = np.interp(theta_m.x, rho_m.x, rho_m.y) + assert np.all(np.abs(carried) > 0) + assert theta_m.x.size > 0 + + +class TestSpinAsymmetryGridPairing: + """CR1 M2: pair channels only where both were measured.""" + + @staticmethod + def _project_with(pp: DataSet1D, mm: DataSet1D) -> Project: + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(_magnetic_model(None)) + project._experiments[0] = PolarizedDataSet(name='pairing', channels={'pp': pp, 'mm': mm}, model=project.models[0]) + return project + + def test_points_outside_the_mm_range_are_dropped_not_extrapolated(self): + # pp reaches further in q than mm; np.interp would clamp to the mm edge + # value and present the result as measured data. + q_pp = np.linspace(0.01, 0.30, 30) + q_mm = np.linspace(0.01, 0.20, 20) + pp = DataSet1D(name='pp', x=q_pp, y=np.full_like(q_pp, 0.6), ye=np.full_like(q_pp, 1e-12)) + mm = DataSet1D(name='mm', x=q_mm, y=np.full_like(q_mm, 0.2), ye=np.full_like(q_mm, 1e-12)) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['out_of_overlap_points'] == int(np.count_nonzero(q_pp > 0.20)) + assert result['measured'].x.max() <= 0.20 + assert np.allclose(result['measured'].y, 0.5) + + def test_disjoint_grids_give_no_asymmetry(self): + q_pp = np.linspace(0.30, 0.40, 10) + q_mm = np.linspace(0.01, 0.20, 10) + pp = DataSet1D(name='pp', x=q_pp, y=np.full_like(q_pp, 0.6), ye=np.full_like(q_pp, 1e-12)) + mm = DataSet1D(name='mm', x=q_mm, y=np.full_like(q_mm, 0.2), ye=np.full_like(q_mm, 1e-12)) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['out_of_overlap_points'] == q_pp.size + assert result['measured'].x.size == 0 + assert result['calculated'] is None + + def test_a_float_round_trip_difference_still_counts_as_the_same_grid(self): + q = np.linspace(0.01, 0.2, 20) + # The kind of difference a text round-trip introduces. + q_mm = np.array([float(f'{value:.12g}') for value in q * (1 + 1e-12)]) + pp = DataSet1D(name='pp', x=q, y=np.full_like(q, 0.6), ye=np.full_like(q, 1e-12)) + mm = DataSet1D(name='mm', x=q_mm, y=np.full_like(q_mm, 0.2), ye=np.full_like(q_mm, 1e-12)) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['out_of_overlap_points'] == 0 + assert result['measured'].x.size == q.size + + def test_equal_grids_report_no_dropped_points(self): + q = np.linspace(0.01, 0.2, 20) + pp = DataSet1D(name='pp', x=q, y=np.full_like(q, 0.6), ye=np.full_like(q, 1e-12)) + mm = DataSet1D(name='mm', x=q, y=np.full_like(q, 0.2), ye=np.full_like(q, 1e-12)) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['out_of_overlap_points'] == 0 + assert result['masked_points'] == 0 + + +class TestSpinAsymmetryDenominatorGuards: + """CR2: the guard must not depend on the data carrying uncertainties.""" + + @staticmethod + def _project_with(pp: DataSet1D, mm: DataSet1D) -> Project: + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(_magnetic_model(None)) + project._experiments[0] = PolarizedDataSet(name='guards', channels={'pp': pp, 'mm': mm}, model=project.models[0]) + return project + + def test_cancellation_without_uncertainties_is_dropped(self): + # Background-subtracted tail: the two channels nearly cancel, so SA is + # a ratio of rounding noise (here it would be 1999). + q = np.linspace(0.01, 0.2, 5) + pp = DataSet1D(name='pp', x=q, y=np.full_like(q, 1e-300)) + mm = DataSet1D(name='mm', x=q, y=np.full_like(q, -0.999e-300)) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['measured'].x.size == 0 + assert result['small_denominator_points'] == q.size + assert result['masked_points'] == q.size + + def test_ordinary_data_without_uncertainties_is_kept(self): + # A two-column file must still produce its asymmetry. + q = np.linspace(0.01, 0.2, 5) + pp = DataSet1D(name='pp', x=q, y=np.full_like(q, 0.6)) + mm = DataSet1D(name='mm', x=q, y=np.full_like(q, 0.2)) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['measured'].x.size == q.size + assert_allclose(result['measured'].y, 0.5) + assert result['masked_points'] == 0 + + def test_exact_cancellation_is_dropped(self): + q = np.linspace(0.01, 0.2, 4) + pp = DataSet1D(name='pp', x=q, y=np.full_like(q, 0.5)) + mm = DataSet1D(name='mm', x=q, y=np.full_like(q, -0.5)) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['measured'].x.size == 0 + assert result['small_denominator_points'] == q.size + + def test_unusable_variances_do_not_silently_become_no_uncertainty(self): + q = np.linspace(0.01, 0.2, 4) + variance = np.array([1e-12, -1.0, np.nan, 1e-12]) + pp = DataSet1D(name='pp', x=q, y=np.full_like(q, 0.6), ye=variance) + mm = DataSet1D(name='mm', x=q, y=np.full_like(q, 0.2), ye=np.full_like(q, 1e-12)) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + # The two malformed points are dropped and counted; the rest survive. + assert result['invalid_points'] == 2 + assert result['measured'].x.size == 2 + assert np.all(result['measured'].ye >= 0) + + def test_low_significance_and_cancellation_are_counted_separately(self): + q = np.linspace(0.01, 0.2, 6) + # First three: small but clean signal drowned in uncertainty. + # Last three: clean cancellation with negligible uncertainty. + r_pp = np.array([1e-6, 1e-6, 1e-6, 1.0, 1.0, 1.0]) + r_mm = np.array([1e-6, 1e-6, 1e-6, -0.9999, -0.9999, -0.9999]) + variance = np.array([1.0, 1.0, 1.0, 1e-24, 1e-24, 1e-24]) + pp = DataSet1D(name='pp', x=q, y=r_pp, ye=variance) + mm = DataSet1D(name='mm', x=q, y=r_mm, ye=variance) + project = self._project_with(pp, mm) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + assert result['low_significance_points'] == 3 + assert result['small_denominator_points'] == 3 + assert result['masked_points'] == 6 + + +class TestSpinAsymmetryChannelValidation: + """CR2: do not advertise a spin asymmetry that cannot be computed.""" + + @staticmethod + def _project_with(pp: DataSet1D, mm: DataSet1D) -> Project: + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(_magnetic_model(None)) + project._experiments[0] = PolarizedDataSet(name='validation', channels={'pp': pp, 'mm': mm}, model=project.models[0]) + return project + + def test_empty_channel_is_not_advertised(self): + q = np.linspace(0.01, 0.2, 5) + project = self._project_with( + DataSet1D(name='pp', x=np.array([]), y=np.array([])), + DataSet1D(name='mm', x=q, y=np.full_like(q, 0.2)), + ) + + assert project.experiment_supports_spin_asymmetry_at_index(0) is False + + def test_duplicate_q_is_not_advertised(self): + q = np.array([0.01, 0.02, 0.02, 0.03]) + project = self._project_with( + DataSet1D(name='pp', x=q, y=np.full_like(q, 0.6)), + DataSet1D(name='mm', x=q, y=np.full_like(q, 0.2)), + ) + + assert project.experiment_supports_spin_asymmetry_at_index(0) is False + + def test_non_finite_q_is_not_advertised(self): + q = np.array([0.01, 0.02, np.nan, 0.03]) + project = self._project_with( + DataSet1D(name='pp', x=q, y=np.full_like(q, 0.6)), + DataSet1D(name='mm', x=q, y=np.full_like(q, 0.2)), + ) + + assert project.experiment_supports_spin_asymmetry_at_index(0) is False + + def test_descending_grid_is_sorted_before_pairing(self): + # np.interp needs an increasing grid and returns nonsense otherwise. + q = np.linspace(0.01, 0.2, 6) + descending = q[::-1] + project = self._project_with( + DataSet1D(name='pp', x=descending, y=np.full_like(q, 0.6)), + DataSet1D(name='mm', x=q + 0.0005, y=np.linspace(0.2, 0.3, 6)), + ) + + assert project.experiment_supports_spin_asymmetry_at_index(0) is True + measured = project.spin_asymmetry_for_experiment_at_index(0)['measured'] + + assert np.all(np.diff(measured.x) > 0) + assert np.all(np.isfinite(measured.y)) + + +class TestSpinAsymmetryInterpolatedVariance: + """CR2: variances interpolate with squared weights.""" + + def test_midpoint_variance_uses_squared_weights(self): + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(_magnetic_model(None)) + # mm sampled at 0.10 and 0.20 with variances 1 and 9; pp asks for 0.15. + pp = DataSet1D(name='pp', x=np.array([0.15]), y=np.array([1.0]), ye=np.array([0.0])) + mm = DataSet1D(name='mm', x=np.array([0.10, 0.20]), y=np.array([0.5, 0.5]), ye=np.array([0.01, 0.09])) + project._experiments[0] = PolarizedDataSet(name='interp', channels={'pp': pp, 'mm': mm}, model=project.models[0]) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + # var_mm at the midpoint: 0.25*0.01 + 0.25*0.09 = 0.025 (the linear rule + # would give 0.05). + r_pp, r_mm = 1.0, 0.5 + expected = 4.0 * (r_pp**2 * 0.025) / (r_pp + r_mm) ** 4 + assert_allclose(result['measured'].ye, [expected], rtol=1e-12) + + def test_endpoints_take_the_source_variance_unchanged(self): + global_object.map._clear() + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(_magnetic_model(None)) + pp = DataSet1D(name='pp', x=np.array([0.10, 0.20]), y=np.array([1.0, 1.0]), ye=np.array([0.0, 0.0])) + mm = DataSet1D(name='mm', x=np.array([0.10, 0.15, 0.20]), y=np.array([0.5, 0.5, 0.5]), ye=np.array([0.01, 0.04, 0.09])) + project._experiments[0] = PolarizedDataSet(name='endpoints', channels={'pp': pp, 'mm': mm}, model=project.models[0]) + + result = project.spin_asymmetry_for_experiment_at_index(0) + + r_pp, r_mm = 1.0, 0.5 + expected = 4.0 * (r_pp**2 * np.array([0.01, 0.09])) / (r_pp + r_mm) ** 4 + assert_allclose(result['measured'].ye, expected, rtol=1e-12) + + +class TestMagneticProfileDisplayContinuity: + """CR2: a periodic angle must not be drawn as a full sweep.""" + + def test_angle_is_continuous_across_the_zero_boundary(self): + profiles = TestMagneticProfileSmoothing()._profiles(359.0, 1.0) + + theta = profiles['theta_m'].y + + # 359 -> 361 rather than 359 -> 1: no 358-degree jump anywhere. + assert np.abs(np.diff(theta)).max() < 10.0 + assert theta.max() - theta.min() < 10.0 + + def test_component_smoothing_failure_is_not_papered_over(self): + # The wrapper must refuse rather than serve refl1d's angle-smoothed + # profile, which misreports the splitting at such an interface. + global_object.map._clear() + model = _magnetic_model(LayerMagnetism(rho_m=2.5, theta_m=270.0)) + project = Project() + project.calculator = 'refl1d' + project.models = ModelCollection(model) + calculator = project.models[0].interface() + + with patch.object( + type(calculator._wrapper), '_smoothed_magnetic_vector', side_effect=NotImplementedError('no microslabs') + ): + with pytest.raises(NotImplementedError): + project.magnetic_sld_data_for_model_at_index(0) + + +class TestCalculatorCapabilities: + """Helpers an application needs to offer the engine magnetism requires.""" + + def test_reports_which_calculators_support_magnetism(self): + project = Project() + + supporting = project.calculators_supporting_magnetism + + assert supporting == ['refl1d'] + # Asking must not change the active calculator. + assert project.calculator == 'refnx' + + def test_models_have_magnetism_follows_the_sample(self): + project = Project() + project.calculator = 'refl1d' + project.default_model() + + assert project.models_have_magnetism is False + + project.models[0].sample[1].layers[0].magnetism = LayerMagnetism(rho_m=2.0) + assert project.models_have_magnetism is True + + project.models[0].sample[1].layers[0].magnetism = None + assert project.models_have_magnetism is False From fe49fac119c45398c5eb37e121201f436f98c256 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Sun, 16 Aug 2026 11:27:25 +0200 Subject: [PATCH 12/22] ruff --- CHANGELOG.md | 211 +++++++++++++++++++++++++-------------------------- 1 file changed, 103 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1de1079..1d9e5c6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,22 +8,21 @@ returned. `magnetism` with fittable, serialized `Parameter`s `rho_m` (magnetic SLD) and `theta_m` (in-plane moment angle). Adding a magnetic layer turns on `include_magnetism` on the calculator, or raises - `NotImplementedError` if the backend cannot do magnetism. Removing - the last magnetic layer turns it off again. `Model.has_magnetism`, + `NotImplementedError` if the backend cannot do magnetism. Removing the + last magnetic layer turns it off again. `Model.has_magnetism`, `CalculatorBase.supports_magnetism` and `Project.calculator_supports_magnetism` report the current state. - New `PolarizedDataSet` groups per-channel `DataSet1D` objects (one file per channel; NSF experiments use 'pp'/'mm' only, spin-flip - channels are optional) into one experiment that shares a single - model. `Project.load_polarized_experiment(paths)` loads from an - explicit channel-to-file mapping. + channels are optional) into one experiment that shares a single model. + `Project.load_polarized_experiment(paths)` loads from an explicit + channel-to-file mapping. `Project.suggest_polarized_channel_assignment(paths)` fills that - mapping from the ORSO header polarization (`pp`/`mm`/`pm`/`mp` - only). Partially analysed observables such as `po`/`mo` (channel - sums) and `op`/`om`/`unpolarized` are left for the user. For plain - text files the mapping comes from filename tokens (`_uu`/`_up`/`_pp` - → pp, `_dd`/`_down`/`_mm` → mm, `_ud`/`_pm` → pm, `_du`/`_mp` → - mp). + mapping from the ORSO header polarization (`pp`/`mm`/`pm`/`mp` only). + Partially analysed observables such as `po`/`mo` (channel sums) and + `op`/`om`/`unpolarized` are left for the user. For plain text files + the mapping comes from filename tokens (`_uu`/`_up`/`_pp` → pp, + `_dd`/`_down`/`_mm` → mm, `_ud`/`_pm` → pm, `_du`/`_mp` → mp). - Experiment and model accessors are channel-aware. `Project.experimental_data_for_model_at_index(index, channel=...)` returns the `DataSet1D` of one spin channel. `channel=None` (the @@ -31,114 +30,111 @@ returned. `Project.model_data_for_model_at_index(index, q_range, channel=...)` calculates one spin cross-section. `Project.experiment_is_polarized_at_index(index)` and - `Project.experiment_channels_at_index(index)` report the - polarization state. A channel that was not measured raises - `KeyError`. An unknown channel, or any channel on an unpolarized - experiment, raises `ValueError`. + `Project.experiment_channels_at_index(index)` report the polarization + state. A channel that was not measured raises `KeyError`. An unknown + channel, or any channel on an unpolarized experiment, raises + `ValueError`. - Summary/report figures now plot one measured series per spin channel of a polarized experiment, each in its channel colour, plus the - matching calculated cross-section. Channels that cannot be - calculated (for example spin-flip on a non-magnetic model) are shown - without a calculated overlay. Previously a polarized experiment made - the report figures fail on `PolarizedDataSet.x`. + matching calculated cross-section. Channels that cannot be calculated + (for example spin-flip on a non-magnetic model) are shown without a + calculated overlay. Previously a polarized experiment made the report + figures fail on `PolarizedDataSet.x`. - The summary experiments table lists one row per spin channel of a polarized experiment, named ` ()`. It previously - raised `AttributeError: 'PolarizedDataSet' object has no attribute - 'x'` and crashed anything that read the summary while a polarized - experiment was loaded. + raised + `AttributeError: 'PolarizedDataSet' object has no attribute 'x'` and + crashed anything that read the summary while a polarized experiment + was loaded. - New `Project.calculators_supporting_magnetism` lists the available calculators that can model magnetic samples, without switching the - active one. `Project.models_have_magnetism` reports whether any - model has a magnetic layer. Use these to pick a suitable engine, or - to refuse one that cannot carry the sample's magnetism, instead of + active one. `Project.models_have_magnetism` reports whether any model + has a magnetic layer. Use these to pick a suitable engine, or to + refuse one that cannot carry the sample's magnetism, instead of hitting an error inside the binding. -- New `Project.magnetic_sld_data_for_model_at_index(index)` returns - the depth profiles of a magnetic model as `DataSet1D`s keyed - `'sld'`, `'rho_m'`, `'theta_m'`, `'spin_up'` and `'spin_down'`. The - last two are the potentials each spin state sees, - rho +/- rho_m*cos(theta_m - A). The guide-field angle A is the new - module constant `GUIDE_FIELD_ANGLE` (270 degrees, refl1d's default - and the only value the library can currently model). A non-magnetic - model raises `ValueError`. - `Project.model_has_magnetism_at_index(index)` reports whether the - model is magnetic. -- The magnetic depth profile is now built by smoothing the two - in-plane components of the moment and converting back, rather than - smoothing magnitude and angle separately as refl1d does channel by - channel. At an interface where moments differ by a few degrees - across 0/360, the smoothed *angle* used to take the long way around - the circle, pass through the guide-field direction, and report the - full moment as longitudinal. That produced a spurious - spin-up/spin-down splitting exactly at the interface (a 2-degree - difference gave the full 2*rho_m splitting; it is now the correct - ~0.02*rho_m). Collinear samples are unaffected. The reported - `theta_m` profile is restricted to depths that carry a moment (the - angle of a zero-length vector is arbitrary) and is made continuous - within each magnetic region. A profile turning from 359 to 1 degree - is a 2 degree turn; the wrapped values would plot as a full sweep. - If the installed refl1d does not expose the microslab data the - component-safe profile needs, the calculator now raises +- New `Project.magnetic_sld_data_for_model_at_index(index)` returns the + depth profiles of a magnetic model as `DataSet1D`s keyed `'sld'`, + `'rho_m'`, `'theta_m'`, `'spin_up'` and `'spin_down'`. The last two + are the potentials each spin state sees, rho +/- rho_m\*cos(theta_m - + A). The guide-field angle A is the new module constant + `GUIDE_FIELD_ANGLE` (270 degrees, refl1d's default and the only value + the library can currently model). A non-magnetic model raises + `ValueError`. `Project.model_has_magnetism_at_index(index)` reports + whether the model is magnetic. +- The magnetic depth profile is now built by smoothing the two in-plane + components of the moment and converting back, rather than smoothing + magnitude and angle separately as refl1d does channel by channel. At + an interface where moments differ by a few degrees across 0/360, the + smoothed _angle_ used to take the long way around the circle, pass + through the guide-field direction, and report the full moment as + longitudinal. That produced a spurious spin-up/spin-down splitting + exactly at the interface (a 2-degree difference gave the full 2*rho_m + splitting; it is now the correct ~0.02*rho_m). Collinear samples are + unaffected. The reported `theta_m` profile is restricted to depths + that carry a moment (the angle of a zero-length vector is arbitrary) + and is made continuous within each magnetic region. A profile turning + from 359 to 1 degree is a 2 degree turn; the wrapped values would plot + as a full sweep. If the installed refl1d does not expose the microslab + data the component-safe profile needs, the calculator now raises `NotImplementedError` instead of falling back to the angle-smoothed profile. - New `Project.spin_asymmetry_for_experiment_at_index(index)` returns the measured spin asymmetry (R++ - R--)/(R++ + R--) of a polarized experiment, the matching model curve when the model is magnetic, and the number of points dropped. `ye` holds the SA **variance**, as - everywhere else in the library. Channels measured on different q - grids are interpolated onto the pp grid (values with the linear - weights, variances with their squares) only inside the q range both - channels cover. Outside that range `np.interp` would clamp to the - edge value. Dropped points are reported as `out_of_overlap_points`. - Points where R++ + R-- is not above `SPIN_ASYMMETRY_SIGNIFICANCE` - (3) times its own uncertainty are also dropped. A second, - uncertainty-independent guard drops points whose denominator is - non-positive or smaller than + everywhere else in the library. Channels measured on different q grids + are interpolated onto the pp grid (values with the linear weights, + variances with their squares) only inside the q range both channels + cover. Outside that range `np.interp` would clamp to the edge value. + Dropped points are reported as `out_of_overlap_points`. Points where + R++ + R-- is not above `SPIN_ASYMMETRY_SIGNIFICANCE` (3) times its own + uncertainty are also dropped. A second, uncertainty-independent guard + drops points whose denominator is non-positive or smaller than `SPIN_ASYMMETRY_CANCELLATION_FRACTION` (1e-3) of |R++| + |R--|. - Without it, a file with no uncertainties (two columns, or a - malformed uncertainty array) had no guard, and background-subtracted - data could put values of +/-1e3 on the axis. Points with a - non-finite reflectivity or a negative/non-finite variance are - dropped rather than treated as having no uncertainty. Dropped - points are reported by reason (`low_significance_points`, - `small_denominator_points`, `invalid_points`). + Without it, a file with no uncertainties (two columns, or a malformed + uncertainty array) had no guard, and background-subtracted data could + put values of +/-1e3 on the axis. Points with a non-finite + reflectivity or a negative/non-finite variance are dropped rather than + treated as having no uncertainty. Dropped points are reported by + reason (`low_significance_points`, `small_denominator_points`, + `invalid_points`). - Both channels of a spin asymmetry are validated before use. Empty, - length-mismatched, non-finite or duplicated q grids are rejected, - and `experiment_supports_spin_asymmetry_at_index` reports False for - them. A descending grid is sorted before pairing; `np.interp` - silently returns nonsense for one. + length-mismatched, non-finite or duplicated q grids are rejected, and + `experiment_supports_spin_asymmetry_at_index` reports False for them. + A descending grid is sorted before pairing; `np.interp` silently + returns nonsense for one. `Project.experiment_supports_spin_asymmetry_at_index(index)` reports whether both non-spin-flip channels were measured. - New `calculate_channel(q, model, channel)` on the wrapper (and `reflectivity_profile_channel` on the calculator, - `fit_func_for_channel` on `CalculatorFactory`) evaluates one - explicit spin channel without touching the global - `polarization_channel` state. + `fit_func_for_channel` on `CalculatorFactory`) evaluates one explicit + spin channel without touching the global `polarization_channel` state. - New `MultiFitter.for_experiments(experiments)` builds a fitter with one fit function per dataset (one per measured spin channel for a polarized experiment, one for an ordinary one) across any number of experiments and models, and returns without running the fit. `fit_datasets` and `fit_channels` give the flat dataset list in - fit-function order, so an application can prepare the data arrays - and drive `easy_science_multi_fitter.fit(...)` from a worker thread. -- New `MultiFitter.record_fit_results(results)` adopts results from - such a caller-driven fit, so `chi2` and `reduced_chi` describe it - instead of reporting that no fit was performed. The classical - metrics need the original data arrays and stay None. + fit-function order, so an application can prepare the data arrays and + drive `easy_science_multi_fitter.fit(...)` from a worker thread. +- New `MultiFitter.record_fit_results(results)` adopts results from such + a caller-driven fit, so `chi2` and `reduced_chi` describe it instead + of reporting that no fit was performed. The classical metrics need the + original data arrays and stay None. - `rho_m` now takes part in the project's default-limit policy. It is created with `default_limits_pending`, and - `Project._sync_parameter_states` gives it the shared SLD window - (-1 to 10) unless an explicit `Parameter` with its own bounds was - passed. `theta_m` keeps its explicit 0-360 bounds. Previously both - stayed unbounded. -- New `MultiFitter.fit_polarized(data)` fits all measured channels of - a `PolarizedDataSet` simultaneously against the shared model: one - fit function per channel, common structural parameters, magnetic + `Project._sync_parameter_states` gives it the shared SLD window (-1 + to 10) unless an explicit `Parameter` with its own bounds was passed. + `theta_m` keeps its explicit 0-360 bounds. Previously both stayed + unbounded. +- New `MultiFitter.fit_polarized(data)` fits all measured channels of a + `PolarizedDataSet` simultaneously against the shared model: one fit + function per channel, common structural parameters, magnetic parameters constrained by all channels at once. Returns per-channel `FitResults`. - The refl1d wrapper now caches the four polarized cross-sections per model state and (q, dq) grid. They come from a single kernel - evaluation, so a simultaneous N-channel fit costs about one - evaluation per iteration instead of N. + evaluation, so a simultaneous N-channel fit costs about one evaluation + per iteration instead of N. - New `polarized_reflectivity_profiles(x_array, model_id)` on the calculator (and on `CalculatorFactory`) returns the reflectivity of @@ -152,29 +148,28 @@ returned. disabling magnetism resets it to `'pp'`. The channel belongs to the currently active calculator instance, not to a model or dataset. It affects every subsequent calculation with that calculator. - `interface.switch(...)` constructs a fresh calculator and resets - both this and `include_magnetism`. + `interface.switch(...)` constructs a fresh calculator and resets both + this and `include_magnetism`. - New `magnetic_sld_profile(model_id)` on the calculator (and on `CalculatorFactory`) returns the nuclear and magnetic scattering - length density profiles as a tuple `z`, `sld(z)`, `rhoM(z)` - (magnetic SLD) and `thetaM(z)` (magnetic angle). Requires + length density profiles as a tuple `z`, `sld(z)`, `rhoM(z)` (magnetic + SLD) and `thetaM(z)` (magnetic angle). Requires `include_magnetism = True`; refl1d only. -- Magnetic calculations now always build all four refl1d - cross-sections, so they may take somewhat longer than before. pp - results are unchanged. +- Magnetic calculations now always build all four refl1d cross-sections, + so they may take somewhat longer than before. pp results are + unchanged. - Bug fix: `include_magnetism = True` on a refnx-backed calculator now - raises `NotImplementedError`. Previously it was silently accepted - (the guard sat on a property the calculator never called) even - though refnx magnetism is not supported. -- Bug fix (pre-existing): disabling magnetism after layers were - created with it enabled used to leave refl1d `Magnetism` objects on - the slabs, and a later unpolarized calculation raised - `AttributeError` inside refl1d. Disabling magnetism now strips the - magnetic state from existing layers. Magnetic parameters - (`rhoM`/`thetaM`) are kept in a per-layer store inside the wrapper, - so they survive a disable/re-enable cycle and are re-attached when - magnetism is enabled again. `update_layer` also accepts the - magnetism keys one at a time. + raises `NotImplementedError`. Previously it was silently accepted (the + guard sat on a property the calculator never called) even though refnx + magnetism is not supported. +- Bug fix (pre-existing): disabling magnetism after layers were created + with it enabled used to leave refl1d `Magnetism` objects on the slabs, + and a later unpolarized calculation raised `AttributeError` inside + refl1d. Disabling magnetism now strips the magnetic state from + existing layers. Magnetic parameters (`rhoM`/`thetaM`) are kept in a + per-layer store inside the wrapper, so they survive a + disable/re-enable cycle and are re-attached when magnetism is enabled + again. `update_layer` also accepts the magnetism keys one at a time. # Version 1.7.0 (1 Aug 2026) From e198637c6333353350cdd00748c271456b76f770 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Sun, 16 Aug 2026 11:52:47 +0200 Subject: [PATCH 13/22] ruff on notebooks --- notebooks/polarized_fitting.ipynb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/notebooks/polarized_fitting.ipynb b/notebooks/polarized_fitting.ipynb index ebf6f53b..83c4cedc 100644 --- a/notebooks/polarized_fitting.ipynb +++ b/notebooks/polarized_fitting.ipynb @@ -100,6 +100,7 @@ "source": [ "TRUTH = {'thickness': 200.0, 'rho_m': 5.0, 'theta_m': 40.0}\n", "\n", + "\n", "def build_model(thickness=TRUTH['thickness'], rho_m=TRUTH['rho_m'], theta_m=TRUTH['theta_m'], name='PNR Model'):\n", " \"\"\"Vacuum | Fe film (magnetic) | Si substrate, with a fresh refl1d calculator.\"\"\"\n", " vacuum = Material(0.0, 0.0, 'Vacuum')\n", @@ -121,6 +122,7 @@ " model.interface = interface\n", " return model\n", "\n", + "\n", "truth_model = build_model()\n", "print(truth_model)\n", "print(f'has_magnetism : {truth_model.has_magnetism}')\n", From 48d596c7ee6aa20008a2f74862dcd4688774d161 Mon Sep 17 00:00:00 2001 From: Piotr Rozyczko Date: Mon, 17 Aug 2026 22:22:36 +0200 Subject: [PATCH 14/22] bind calculator to model for performance --- src/easyreflectometry/project.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index 3444e45c..1564fee2 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -809,9 +809,21 @@ def load_experiment_for_model_at_index(self, path: Union[Path, str], index: Opti self._with_experiments = True self._apply_resolution_function(experiment, self._models[index]) + def _bind_calculator(self, model) -> None: + """Bind the project's calculator to a model unless it already is. + + Reassigning ``model.interface`` re-propagates the interface over the + whole sample tree — an expensive rebuild. The plot getters run on every + chart refresh, so an already-bound model must be left alone; engine + switches go through the ``calculator`` setter, which regenerates the + bindings itself. + """ + if model.interface is not self._calculator: + model.interface = self._calculator + def sld_data_for_model_at_index(self, index: int = 0) -> DataSet1D: """Sld data for model at index.""" - self.models[index].interface = self._calculator + self._bind_calculator(self.models[index]) sld = self.models[index].interface().sld_profile(self._models[index].unique_name) return DataSet1D( name=f'SLD for Model {index}', @@ -864,7 +876,7 @@ def magnetic_sld_data_for_model_at_index(self, index: int = 0) -> Dict[str, Data f'Model {index} has no magnetic layer; there is no magnetic SLD profile to show. ' 'Attach magnetism to a layer first.' ) - model.interface = self._calculator + self._bind_calculator(model) z, sld, rho_m, theta_m = model.interface().magnetic_sld_profile(model.unique_name) z = np.asarray(z, dtype=float) sld = np.asarray(sld, dtype=float) @@ -919,7 +931,7 @@ def model_data_for_model_at_index( """ if q_range is None: q_range = np.linspace(self.q_min, self.q_max, self.q_resolution) - self.models[index].interface = self._calculator + self._bind_calculator(self.models[index]) if channel is None: reflectivity = self.models[index].interface().reflectity_profile(q_range, self._models[index].unique_name) name = f'Reflectivity for Model {index}' From d0b591506d26bbcbfee930c93d46ad39f8368cfc Mon Sep 17 00:00:00 2001 From: rozyczko Date: Tue, 18 Aug 2026 08:39:25 +0200 Subject: [PATCH 15/22] attempt at fixing package testing --- .github/workflows/test.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 530c154f..a4e004d2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -217,9 +217,6 @@ jobs: echo "Adding Python $py_ver" pixi add "python=$py_ver" - echo "Setting macOS 14.0 as minimum required" - pixi project system-requirements add macos 14.0 - echo "Looking for wheel in ../dist/py$py_ver/" ls -l "../dist/py$py_ver/" From c98fca9ecfe544b6704a96747fb04615d5c05d6d Mon Sep 17 00:00:00 2001 From: rozyczko Date: Tue, 18 Aug 2026 09:16:44 +0200 Subject: [PATCH 16/22] package tests only on master --- .github/workflows/pypi-test.yml | 2 ++ .github/workflows/test.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml index 123c24c6..f0c8f88a 100644 --- a/.github/workflows/pypi-test.yml +++ b/.github/workflows/pypi-test.yml @@ -24,6 +24,8 @@ env: jobs: # Job 1: Test installation from PyPI on multiple OS pypi-package-tests: + # Only run the package tests on the master branch + if: github.ref == 'refs/heads/master' strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a4e004d2..51de8d5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -174,6 +174,8 @@ jobs: # Job 3: Test the package package-test: needs: source-test # depend on previous job + # Only run the package tests on the master branch + if: github.ref == 'refs/heads/master' strategy: fail-fast: false From 86b4d503a33263a34316a73fb5c2f15c82c3435d Mon Sep 17 00:00:00 2001 From: rozyczko Date: Tue, 18 Aug 2026 09:48:37 +0200 Subject: [PATCH 17/22] don't run ruff twice --- .github/workflows/python-ci.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index ea40ee70..f8bfa049 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -13,17 +13,6 @@ name: CI using pip on: [push, pull_request] jobs: - Code_Consistency: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: chartboost/ruff-action@v1 - - name: Suggestion to fix issues - if: ${{ failure() }} - run: | - echo "::notice::In project root run 'python.exe -m ruff . --fix' and commit changes to fix issues." - exit 1 - Code_Testing: strategy: max-parallel: 4 From 9906775d941b36bbd61fbaafb483e0243f779b34 Mon Sep 17 00:00:00 2001 From: rozyczko Date: Tue, 18 Aug 2026 13:46:05 +0200 Subject: [PATCH 18/22] code review fixes --- .../advancedfitting/bayesian_bumps.ipynb | 8 +- .../docs/tutorials/simulation/magnetism.ipynb | 38 ++- .../calculators/refl1d/wrapper.py | 17 +- .../calculators/wrapper_base.py | 3 + src/easyreflectometry/fitting.py | 278 +++++++++--------- src/easyreflectometry/project.py | 79 ++++- .../sample/elements/layers/layer.py | 3 + tests/test_polarized_fitting.py | 84 ++++++ 8 files changed, 333 insertions(+), 177 deletions(-) diff --git a/docs/docs/tutorials/advancedfitting/bayesian_bumps.ipynb b/docs/docs/tutorials/advancedfitting/bayesian_bumps.ipynb index dcaaa025..bf136533 100644 --- a/docs/docs/tutorials/advancedfitting/bayesian_bumps.ipynb +++ b/docs/docs/tutorials/advancedfitting/bayesian_bumps.ipynb @@ -217,12 +217,18 @@ "# We deliberately start with a *very* short run. It finishes in seconds but is\n", "# far too short to trust — which is exactly the situation the \"extend the chain\"\n", "# section below exists to fix. In production you would ask for 20 k+ samples.\n", + "#\n", + "# ``thin=1`` here (rather than the ``10`` used later in ``extend()``) works\n", + "# around a BUMPS DREAM bug: its outlier-chain replacement indexes the thinned\n", + "# sample buffer using the un-thinned generation counter, which overruns the\n", + "# buffer whenever outlier removal fires before enough thinned generations\n", + "# have accumulated — reliably the case for a run this short with thin > 1.\n", "\n", "posterior_dict = fitter.mcmc_sample(\n", " data,\n", " samples=500, # Deliberately too short — extended later in this notebook\n", " burn=100,\n", - " thin=10,\n", + " thin=1,\n", ")\n", "\n", "print('DREAM sampling complete.')\n", diff --git a/docs/docs/tutorials/simulation/magnetism.ipynb b/docs/docs/tutorials/simulation/magnetism.ipynb index 2c8f6977..63e49538 100644 --- a/docs/docs/tutorials/simulation/magnetism.ipynb +++ b/docs/docs/tutorials/simulation/magnetism.ipynb @@ -4,13 +4,7 @@ "cell_type": "markdown", "id": "a60117e3-d089-4375-ac7c-12a52ed47271", "metadata": {}, - "source": [ - "# Magnetism\n", - "\n", - "Magnetism is only available in Refl1d and it does not support RepeatingMultilayer.\n", - "\n", - "When magnetism is enabled (`include_magnetism = True`) all four polarization channels are available: the non-spin-flip channels (`pp`, `mm`) and the spin-flip channels (`pm`, `mp`).\n" - ] + "source": "# Magnetism\n\nMagnetism is only available in Refl1d, and refl1d itself does not support magnetic layers inside a `RepeatingMultilayer` (it raises `NotImplementedError` rather than silently producing a wrong profile).\n\nWhen magnetism is enabled (`include_magnetism = True`) all four polarization channels are available: the non-spin-flip channels (`pp`, `mm`) and the spin-flip channels (`pm`, `mp`)." }, { "cell_type": "markdown", @@ -63,6 +57,7 @@ "from easyreflectometry.model import Model\n", "from easyreflectometry.model import PercentageFwhm\n", "from easyreflectometry.sample import Layer\n", + "from easyreflectometry.sample import LayerMagnetism\n", "from easyreflectometry.sample import Material\n", "from easyreflectometry.sample import Multilayer\n", "from easyreflectometry.sample import Sample" @@ -363,10 +358,9 @@ "# With magnetic layers\n", "interface.switch('refl1d')\n", "model.interface = interface\n", + "sld_4_layer.magnetism = LayerMagnetism(rho_m=10, theta_m=70, name='Sld 4 moment')\n", + "sld_8_layer.magnetism = LayerMagnetism(rho_m=5, theta_m=175, name='Sld 8 moment')\n", "model_interface = model.interface()\n", - "model_interface.include_magnetism = True\n", - "model_interface._wrapper.update_layer(sld_4_layer.unique_name, magnetism_rhoM=10, magnetism_thetaM=70)\n", - "model_interface._wrapper.update_layer(sld_8_layer.unique_name, magnetism_rhoM=5, magnetism_thetaM=175)\n", "model_data_magnetism_layer_1 = model.interface().reflectity_profile(\n", " model_coords,\n", " model.unique_name,\n", @@ -431,10 +425,9 @@ "# EasyReflectometry\n", "interface.switch('refl1d')\n", "model.interface = interface\n", + "sld_4_layer.magnetism = LayerMagnetism(rho_m=10, theta_m=70, name='Sld 4 moment')\n", + "sld_8_layer.magnetism = LayerMagnetism(rho_m=5, theta_m=175, name='Sld 8 moment')\n", "model_interface = model.interface()\n", - "model_interface.include_magnetism = True\n", - "model_interface._wrapper.update_layer(sld_4_layer.unique_name, magnetism_rhoM=10, magnetism_thetaM=70)\n", - "model_interface._wrapper.update_layer(sld_8_layer.unique_name, magnetism_rhoM=5, magnetism_thetaM=175)\n", "model_data_magnetism_easy = model.interface().reflectity_profile(\n", " model_coords,\n", " model.unique_name,\n", @@ -539,9 +532,7 @@ "source": [ "### All polarization channels in EasyReflectometry\n", "\n", - "The same four channels are available through the EasyReflectometry API via `polarized_reflectivity_profiles`, which returns a dictionary keyed `pp`, `pm`, `mp`, `mm`. We build the equivalent single layer model and enable magnetism.\n", - "\n", - "Note that setting the magnetic layer parameters through `_wrapper.update_layer(...)` is a *temporary workaround*: magnetic layer parameters are not yet part of the public `Layer` API." + "The same four channels are available through the EasyReflectometry API via `polarized_reflectivity_profiles`, which returns a dictionary keyed `pp`, `pm`, `mp`, `mm`. We build the equivalent single layer model and attach a `LayerMagnetism` to its layer, which makes it magnetic and enables `include_magnetism` automatically." ] }, { @@ -580,11 +571,8 @@ "interface.switch('refl1d')\n", "single_layer_model.interface = interface\n", "single_layer_model.resolution_function = PercentageFwhm(0)\n", + "magnetic_layer.magnetism = LayerMagnetism(rho_m=8, theta_m=45, name='Magnetic layer moment')\n", "model_interface = single_layer_model.interface()\n", - "model_interface.include_magnetism = True\n", - "\n", - "# Temporary workaround: set the magnetic layer parameters directly on the wrapper\n", - "model_interface._wrapper.update_layer(magnetic_layer.unique_name, magnetism_rhoM=8, magnetism_thetaM=45)\n", "\n", "channels = single_layer_model.interface.polarized_reflectivity_profiles(\n", " model_coords,\n", @@ -755,6 +743,14 @@ }, "outputs": [], "source": [ + "# The magnetic layers attached earlier in this tutorial live on the `Layer`\n", + "# objects themselves (unlike the old wrapper-storage workaround, which was\n", + "# scoped to one calculator instance and reset on every `interface.switch(...)`).\n", + "# Detach them so this section demonstrates its own point: a sample with no\n", + "# magnetic layers at all.\n", + "sld_4_layer.magnetism = None\n", + "sld_8_layer.magnetism = None\n", + "\n", "# With Magnetism\n", "interface.switch('refl1d')\n", "model.interface = interface\n", @@ -827,4 +823,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/src/easyreflectometry/calculators/refl1d/wrapper.py b/src/easyreflectometry/calculators/refl1d/wrapper.py index 169eddd0..9400bdf4 100644 --- a/src/easyreflectometry/calculators/refl1d/wrapper.py +++ b/src/easyreflectometry/calculators/refl1d/wrapper.py @@ -164,6 +164,10 @@ def remove_layer_magnetism(self, name: str) -> None: # A non-magnetic slab is fine inside a polarized calculation. slab.magnetism = None if self._magnetism and not self._layer_magnetism: + # Goes through the `magnetism` property setter, which calls + # `_remove_magnetism_from_layers()` again (a no-op here since + # `_layer_magnetism` is already empty) and, more importantly, + # resets `_polarization_channel` back to PP. self.magnetism = False def create_model(self, name: str): @@ -336,7 +340,9 @@ def _model_state_token(self, model_name: str) -> tuple: produce the same reflectivity, so cached cross-sections can be reused. The resolution function needs no entry here — it enters through the dq part of the per-grid cache key. Must be extended whenever a new slab or - material attribute starts reaching the kernel. + material attribute starts reaching the kernel: a forgotten kernel input + would silently serve stale reflectivities whenever only that input + changes, since the token would compare equal. """ model = self.storage['model'][model_name] values: list = [model['scale'], model['bkg']] @@ -446,6 +452,10 @@ def magnetic_sld_profile(self, model_name: str) -> Tuple[np.ndarray, np.ndarray, '(`include_magnetism = True` on the calculator / `magnetism = True` on the wrapper).' ) sample = _build_sample(self.storage, model_name) + # Plain (non-polarized) probe: unlike `_polarized_reflectivities`, this + # only renders slabs for `magnetic_smooth_profile()`/`_render_slabs()`, + # never computes a per-channel reflectivity, so the `theta_offset` that + # `magnetism=True` would add (needed by `PolarizedQProbe`) is not required. probe = _get_probe( q_array=np.array([1]), # dummy value dq_array=np.array([1]), # dummy value @@ -535,7 +545,10 @@ def _get_probe( ) # Add theta_offset attribute if magnetism is enabled - # This is required for PolarizedQProbe to work correctly + # This is required for PolarizedQProbe to work correctly: refl1d's + # `PolarizedNeutronQProbe.__init__` -> `_calculate_union` reads `theta_offset` + # off each constituent probe, so a QProbe destined for a PolarizedQProbe must + # carry it even though the plain (unpolarized) QProbe path never touches it. if magnetism: probe.theta_offset = names.Parameter.default(0, name='theta_offset') diff --git a/src/easyreflectometry/calculators/wrapper_base.py b/src/easyreflectometry/calculators/wrapper_base.py index feaab335..32e1d4be 100644 --- a/src/easyreflectometry/calculators/wrapper_base.py +++ b/src/easyreflectometry/calculators/wrapper_base.py @@ -403,6 +403,9 @@ def calculate_channel(self, q_array: np.ndarray, model_name: str, channel: Polar channel = PolarizationChannel(channel) if not self._magnetism: if channel is PolarizationChannel.PP: + # No explicit `.copy()` needed here: `calculate()` always returns + # a fresh array (a cached, shared array only exists on the + # magnetism-enabled `calculate_polarized` path below). return self.calculate(q_array, model_name) raise ValueError(f"Calculating the '{channel.value}' channel requires magnetism to be enabled.") return self.calculate_polarized(q_array, model_name)[channel.value] diff --git a/src/easyreflectometry/fitting.py b/src/easyreflectometry/fitting.py index 21663613..37ba637e 100644 --- a/src/easyreflectometry/fitting.py +++ b/src/easyreflectometry/fitting.py @@ -162,6 +162,93 @@ def _fit_result_reduced_chi(result: FitResults, n_points: int | None = None) -> raise AttributeError('FitResults object has neither reduced_chi nor reduced_chi2') +def _bind_fit_func(func: Callable, unique_name: str) -> Callable: + """Bind a model's fit function to its ``unique_name`` for ``EasyScienceMultiFitter``. + + ``EasyScienceMultiFitter`` calls each fit function positionally as + ``func(x, *extra_args)``; the model's ``interface.fit_func`` expects its + ``unique_name`` as that extra positional argument, so it has to be closed + over here rather than passed through the fitter's call signature. + """ + + def wrapped(*args, **kwargs): + return func(*args, unique_name, **kwargs) + + return wrapped + + +def _emit_array_prep_warnings(stats: dict, y_vals: np.ndarray, label: str, *, action: str = 'fitting', extra: str = '') -> None: + """Warn about zero-variance handling applied by :func:`_prepare_fit_arrays`. + + Parameters + ---------- + stats : dict + The ``stats`` dict returned by :func:`_prepare_fit_arrays`. + y_vals : np.ndarray + The original (pre-transform) y values, used for the "all points" count. + label : str + Identifies what was fitted/sampled, e.g. ``'reflectivity 1'`` or + ``'channel pp'``. + action : str, optional + Verb describing the operation, e.g. ``'fitting'`` or ``'sampling'``. By default, 'fitting'. + extra : str, optional + Extra sentence(s) appended to the Mighell-related warnings (e.g. a + likelihood-validity caveat for MCMC). By default, ''. + """ + if stats['masked'] > 0: + warnings.warn( + f'Masked {stats["masked"]} data point(s) in {label} due to zero variance during {action}.', + UserWarning, + ) + if stats.get('transformed_all_points'): + warnings.warn( + f'Applied Mighell transform to all {len(y_vals)} point(s) in {label} during {action}.{extra}', + UserWarning, + ) + elif stats['mighell_substituted'] > 0: + warnings.warn( + f'Applied Mighell substitution to {stats["mighell_substituted"]} ' + f'zero-variance point(s) in {label} during {action}.{extra}', + UserWarning, + ) + + +def _classical_metrics_for(original: dict, model_curve: np.ndarray, result: FitResults, n_points: int | None = None) -> dict: + """Assemble the classical (positive-variance-only) and objective-space fit metrics. + + Parameters + ---------- + original : dict + Dict with keys ``'y'`` and ``'variances'`` holding the un-transformed + observed values and their variances (σ²). + model_curve : np.ndarray + Model evaluated at the original x values. + result : FitResults + The minimizer's result for this dataset/channel. + n_points : int | None, optional + Number of points actually fitted, used as the ``reduced_chi``/``reduced_chi2`` + fallback's point count. If ``None``, derived from ``result.x``. By default, None. + + Returns + ------- + dict + Keys ``'classical_chi2'``, ``'classical_reduced_chi'``, + ``'objective_chi2'``, ``'objective_reduced_chi'``, ``'n_classical_points'``. + """ + sigma_classical = np.sqrt(np.clip(original['variances'], 0.0, None)) + n_classical_points = int(np.sum(original['variances'] > 0.0)) + classical_chi2 = _compute_weighted_chi2(original['y'], model_curve, sigma_classical) + if n_points is None: + n_points = np.size(result.x) + return { + 'classical_chi2': classical_chi2, + 'classical_reduced_chi': _compute_reduced_chi2(classical_chi2, n_classical_points, result.n_pars), + 'objective_chi2': float(result.chi2), + 'objective_reduced_chi': _fit_result_reduced_chi(result, n_points), + 'n_classical_points': n_classical_points, + } + + class MultiFitter: def __init__(self, *args: Model, objective: str = 'hybrid'): r"""A convenience class for the :py:class:`easyscience.Fitting.Fitting` @@ -180,17 +267,7 @@ def __init__(self, *args: Model, objective: str = 'hybrid'): ``'auto'`` (alias for ``'hybrid'``). By default, 'hybrid'. """ - # This lets the unique_name be passed with the fit_func. - def func_wrapper(func, unique_name): - """Func wrapper.""" - - def wrapped(*args, **kwargs): - """Wrapped function.""" - return func(*args, unique_name, **kwargs) - - return wrapped - - self._fit_func = [func_wrapper(m.interface.fit_func, m.unique_name) for m in args] + self._fit_func = [_bind_fit_func(m.interface.fit_func, m.unique_name) for m in args] self._models = args self.easy_science_multi_fitter = EasyScienceMultiFitter(args, self._fit_func) self._fit_results: list[FitResults] | None = None @@ -223,6 +300,17 @@ def for_experiments( to ``easy_science_multi_fitter.fit(...)`` in the order given by :attr:`fit_datasets`, which lets a GUI drive it from a worker thread. + Note + ---- + Built via ``cls(*models, objective=objective)`` and then overwrites + ``_fit_func`` / ``easy_science_multi_fitter`` with the per-channel + versions — the ``__init__``-built pair is briefly constructed and + discarded. Unlike :meth:`fit_polarized`, the minimizer selection and + its ``tolerance`` / ``max_evaluations`` are *not* carried over: the + returned fitter starts from ``easy_science_multi_fitter``'s defaults, + so a caller that needs a specific minimizer must set it explicitly + before calling ``.fit(...)``. + Parameters ---------- experiments : list[DataSet1D | PolarizedDataSet] @@ -261,21 +349,12 @@ def for_experiments( fitter = cls(*models, objective=objective) - def func_wrapper(func, unique_name): - """Func wrapper.""" - - def wrapped(*args, **kwargs): - """Wrapped function.""" - return func(*args, unique_name, **kwargs) - - return wrapped - fit_funcs = [] for dataset, channel in zip(datasets, channels): model = dataset.model interface = model.interface func = interface.fit_func if channel is None else interface.fit_func_for_channel(channel) - fit_funcs.append(func_wrapper(func, model.unique_name)) + fit_funcs.append(_bind_fit_func(func, model.unique_name)) fitter._fit_func = fit_funcs fitter.easy_science_multi_fitter = EasyScienceMultiFitter(models, fit_funcs) @@ -316,23 +395,7 @@ def fit(self, data: sc.DataGroup, id: int = 0, objective: str | None = None) -> variances = data['data'][f'R_{i}'].variances x_out, y_eff, weights, stats = _prepare_fit_arrays(x_vals, y_vals, variances, obj) - - if stats['masked'] > 0: - warnings.warn( - f'Masked {stats["masked"]} data point(s) in reflectivity {i} due to zero variance during fitting.', - UserWarning, - ) - if stats.get('transformed_all_points'): - warnings.warn( - f'Applied Mighell transform to all {len(y_vals)} point(s) in reflectivity {i} during fitting.', - UserWarning, - ) - elif stats['mighell_substituted'] > 0: - warnings.warn( - f'Applied Mighell substitution to {stats["mighell_substituted"]} ' - f'zero-variance point(s) in reflectivity {i} during fitting.', - UserWarning, - ) + _emit_array_prep_warnings(stats, y_vals, f'reflectivity {i}') x.append(x_out) y.append(y_eff) @@ -356,27 +419,14 @@ def fit(self, data: sc.DataGroup, id: int = 0, objective: str | None = None) -> values=sld_profile[0], unit=(1 / new_data['coords'][f'Qz_{id}'].unit).unit, ) - original = original_arrays[i] - sigma_classical = np.sqrt(np.clip(original['variances'], 0.0, None)) - n_classical_points = int(np.sum(original['variances'] > 0.0)) - classical_chi2 = _compute_weighted_chi2(original['y'], model_curve, sigma_classical) - classical_reduced_chi = _compute_reduced_chi2(classical_chi2, n_classical_points, result[i].n_pars) - objective_chi2 = float(result[i].chi2) - objective_reduced_chi = _fit_result_reduced_chi(result[i], np.size(result[i].x)) - - self._classical_fit_metrics.append({ - 'classical_chi2': classical_chi2, - 'classical_reduced_chi': classical_reduced_chi, - 'objective_chi2': objective_chi2, - 'objective_reduced_chi': objective_reduced_chi, - 'n_classical_points': n_classical_points, - }) - - new_data['objective_chi2'] = objective_chi2 - new_data['objective_reduced_chi'] = objective_reduced_chi - new_data['classical_chi2'] = classical_chi2 - new_data['classical_reduced_chi'] = classical_reduced_chi - new_data['reduced_chi'] = objective_reduced_chi + metrics = _classical_metrics_for(original_arrays[i], model_curve, result[i]) + self._classical_fit_metrics.append(metrics) + + new_data['objective_chi2'] = metrics['objective_chi2'] + new_data['objective_reduced_chi'] = metrics['objective_reduced_chi'] + new_data['classical_chi2'] = metrics['classical_chi2'] + new_data['classical_reduced_chi'] = metrics['classical_reduced_chi'] + new_data['reduced_chi'] = metrics['objective_reduced_chi'] new_data['success'] = result[i].success return new_data @@ -404,42 +454,16 @@ def fit_single_data_set_1d(self, data: DataSet1D, objective: str | None = None) variances = np.asarray(data.ye) x_out, y_eff, weights, stats = _prepare_fit_arrays(x_vals, y_vals, variances, obj) - - if stats['masked'] > 0: - warnings.warn( - f'Masked {stats["masked"]} data point(s) in single-dataset fit due to zero variance during fitting.', - UserWarning, - ) - if stats.get('transformed_all_points'): - warnings.warn( - f'Applied Mighell transform to all {len(y_vals)} point(s) in single-dataset fit during fitting.', - UserWarning, - ) - elif stats['mighell_substituted'] > 0: - warnings.warn( - f'Applied Mighell substitution to {stats["mighell_substituted"]} ' - 'zero-variance point(s) in single-dataset fit during fitting.', - UserWarning, - ) + _emit_array_prep_warnings(stats, y_vals, 'single-dataset fit') if obj == 'legacy_mask' and len(x_out) == 0: raise ValueError('Cannot fit single dataset: all points have zero variance.') result = self.easy_science_multi_fitter.fit(x=[x_out], y=[y_eff], weights=[weights])[0] self._fit_results = [result] - sigma_classical = np.sqrt(np.clip(variances, 0.0, None)) model_curve = self._fit_func[0](x_vals) - n_classical_points = int(np.sum(variances > 0.0)) - classical_chi2 = _compute_weighted_chi2(y_vals, model_curve, sigma_classical) - classical_reduced_chi = _compute_reduced_chi2(classical_chi2, n_classical_points, result.n_pars) self._classical_fit_metrics = [ - { - 'classical_chi2': classical_chi2, - 'classical_reduced_chi': classical_reduced_chi, - 'objective_chi2': float(result.chi2), - 'objective_reduced_chi': _fit_result_reduced_chi(result, len(x_out)), - 'n_classical_points': n_classical_points, - } + _classical_metrics_for({'y': y_vals, 'variances': variances}, model_curve, result, n_points=len(x_out)) ] return result @@ -470,6 +494,12 @@ def fit_polarized(self, data: PolarizedDataSet, objective: str | None = None) -> dict[str, FitResults] Fit results per channel, keyed 'pp', 'pm', 'mp', 'mm' (measured channels only, in that order). + + Note + ---- + Unlike :meth:`for_experiments`, this method does not populate + :attr:`fit_datasets` / :attr:`fit_channels` — those are set only by the + caller-driven, `for_experiments`-built flow. """ obj = _validate_objective(objective) if objective is not None else self._objective if len(self._models) != 1: @@ -482,17 +512,8 @@ def fit_polarized(self, data: PolarizedDataSet, objective: str | None = None) -> if data[channel].model is not model: raise ValueError(f"The '{channel.value}' channel dataset is bound to a different model than the fitter's.") - def func_wrapper(func, unique_name): - """Func wrapper.""" - - def wrapped(*args, **kwargs): - """Wrapped function.""" - return func(*args, unique_name, **kwargs) - - return wrapped - channel_fit_funcs = [ - func_wrapper(model.interface.fit_func_for_channel(channel), model.unique_name) for channel in channels + _bind_fit_func(model.interface.fit_func_for_channel(channel), model.unique_name) for channel in channels ] # One fit function per channel, all bound to the single model. Constructed # per call because the channel set comes from the data; the minimizer @@ -515,23 +536,7 @@ def wrapped(*args, **kwargs): variances = np.asarray(dataset.ye) x_out, y_eff, weights, stats = _prepare_fit_arrays(x_vals, y_vals, variances, obj) - - if stats['masked'] > 0: - warnings.warn( - f'Masked {stats["masked"]} data point(s) in channel {channel.value} due to zero variance during fitting.', - UserWarning, - ) - if stats.get('transformed_all_points'): - warnings.warn( - f'Applied Mighell transform to all {len(y_vals)} point(s) in channel {channel.value} during fitting.', - UserWarning, - ) - elif stats['mighell_substituted'] > 0: - warnings.warn( - f'Applied Mighell substitution to {stats["mighell_substituted"]} ' - f'zero-variance point(s) in channel {channel.value} during fitting.', - UserWarning, - ) + _emit_array_prep_warnings(stats, y_vals, f'channel {channel.value}') if obj == 'legacy_mask' and len(x_out) == 0: raise ValueError(f'Cannot fit channel {channel.value}: all points have zero variance.') @@ -541,22 +546,16 @@ def wrapped(*args, **kwargs): original_arrays.append({'x': x_vals, 'y': y_vals, 'variances': variances}) results = polarized_fitter.fit(x, y, weights=dy) + # All channels are fitted against one parameter vector (the shared model), + # so `result.n_pars` is identical across `results`; `reduced_chi` and + # `classical_reduced_chi` below rely on that invariant. self._fit_results = list(results) self._classical_fit_metrics = [] for index, (channel, result) in enumerate(zip(channels, results)): original = original_arrays[index] model_curve = channel_fit_funcs[index](original['x']) - sigma_classical = np.sqrt(np.clip(original['variances'], 0.0, None)) - n_classical_points = int(np.sum(original['variances'] > 0.0)) - classical_chi2 = _compute_weighted_chi2(original['y'], model_curve, sigma_classical) - self._classical_fit_metrics.append({ - 'classical_chi2': classical_chi2, - 'classical_reduced_chi': _compute_reduced_chi2(classical_chi2, n_classical_points, result.n_pars), - 'objective_chi2': float(result.chi2), - 'objective_reduced_chi': _fit_result_reduced_chi(result, np.size(result.x)), - 'n_classical_points': n_classical_points, - }) + self._classical_fit_metrics.append(_classical_metrics_for(original, model_curve, result)) return {channel.value: result for channel, result in zip(channels, results)} @@ -628,27 +627,16 @@ def mcmc_sample( ) x_out, y_eff, weights, stats = _prepare_fit_arrays(x_vals, y_vals, variances, obj) - - if stats['masked'] > 0: - warnings.warn( - f'Masked {stats["masked"]} data point(s) in reflectivity {i} due to zero variance during sampling.', - UserWarning, - ) - if stats.get('transformed_all_points'): - warnings.warn( - f'Applied Mighell transform to all {len(y_vals)} point(s) in reflectivity {i} during sampling. ' - 'The Mighell transform is a chi-square bias correction, not a true likelihood; ' - 'posterior widths may be unreliable.', - UserWarning, - ) - elif stats['mighell_substituted'] > 0: - warnings.warn( - f'Applied Mighell substitution to {stats["mighell_substituted"]} ' - f'zero-variance point(s) in reflectivity {i} during sampling. ' - 'The Mighell transform is a chi-square bias correction, not a true likelihood; ' - 'posterior widths may be unreliable.', - UserWarning, - ) + _emit_array_prep_warnings( + stats, + y_vals, + f'reflectivity {i}', + action='sampling', + extra=( + ' The Mighell transform is a chi-square bias correction, not a true likelihood; ' + 'posterior widths may be unreliable.' + ), + ) x.append(x_out) y.append(y_eff) dy.append(weights) diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index 1564fee2..e9048713 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -1027,7 +1027,7 @@ def experiment_supports_spin_asymmetry_at_index(self, index: int = 0) -> bool: and then fail. """ channels = self.experiment_channels_at_index(index) - if PolarizationChannel.PP not in channels or PolarizationChannel.MM not in channels: + if not _has_both_nsf_channels(channels): return False experiment = self._experiments[index] try: @@ -1097,7 +1097,7 @@ def spin_asymmetry_for_experiment_at_index(self, index: int = 0) -> Dict[str, ob if index not in self._experiments.keys(): raise IndexError(f'No experiment data for model at index {index}') channels = self.experiment_channels_at_index(index) - if PolarizationChannel.PP not in channels or PolarizationChannel.MM not in channels: + if not _has_both_nsf_channels(channels): raise ValueError(f'Experiment {index} does not have both non-spin-flip channels; spin asymmetry needs pp and mm.') experiment = self._experiments[index] @@ -1386,6 +1386,9 @@ def _as_dict_add_experiments(self, project_dict: dict): project_dict['experiments_names'] = {} for key, experiment in self._experiments.items(): + if isinstance(experiment, PolarizedDataSet): + self._as_dict_add_polarized_experiment(project_dict, key, experiment) + continue project_dict['experiments'][key] = [ list(experiment.x), list(experiment.y), @@ -1396,6 +1399,31 @@ def _as_dict_add_experiments(self, project_dict: dict): project_dict['experiments_models'][key] = experiment.model.name project_dict['experiments_names'][key] = experiment.name + @staticmethod + def _as_dict_add_polarized_experiment(project_dict: dict, key: int, experiment: PolarizedDataSet) -> None: + """Serialize a `PolarizedDataSet`: one (name, x, y, ye, xe) array set per measured channel. + + `experiments[key]` is a plain list for an ordinary `DataSet1D` (see + `_as_dict_add_experiments`); a dict here — tagged `'polarized': True` — + is how `_from_dict_extract_experiments` tells the two apart on load. + """ + project_dict['experiments'][key] = { + 'polarized': True, + 'channels': { + channel.value: [ + experiment[channel].name, + list(experiment[channel].x), + list(experiment[channel].y), + list(experiment[channel].ye), + list(experiment[channel].xe), + ] + for channel in experiment.available_channels + }, + } + if experiment.model is not None: + project_dict['experiments_models'][key] = experiment.model.name + project_dict['experiments_names'][key] = experiment.name + def from_dict(self, project_dict: dict): """From dict.""" keys = list(project_dict.keys()) @@ -1439,21 +1467,45 @@ def from_dict(self, project_dict: dict): # Resolve any pending parameter dependencies (constraints) after all objects are loaded resolve_all_parameter_dependencies(self) - def _from_dict_extract_experiments(self, project_dict: dict) -> Dict[int, DataSet1D]: + def _from_dict_extract_experiments(self, project_dict: dict) -> Dict[int, Union[DataSet1D, PolarizedDataSet]]: """From dict extract experiments.""" experiments = {} - for key in project_dict['experiments'].keys(): + for key, raw in project_dict['experiments'].items(): + if isinstance(raw, dict) and raw.get('polarized'): + experiments[int(key)] = self._polarized_experiment_from_dict(key, raw, project_dict) + continue experiments[int(key)] = DataSet1D( name=project_dict['experiments_names'][key], - x=project_dict['experiments'][key][0], - y=project_dict['experiments'][key][1], - ye=project_dict['experiments'][key][2], - xe=project_dict['experiments'][key][3], + x=raw[0], + y=raw[1], + ye=raw[2], + xe=raw[3], model=self._models[project_dict['experiments_models'][key]], auto_background=False, ) return experiments + def _polarized_experiment_from_dict(self, key: str, raw: dict, project_dict: dict) -> PolarizedDataSet: + """Reconstruct a `PolarizedDataSet` serialized by `_as_dict_add_polarized_experiment`.""" + model = self._models[project_dict['experiments_models'][key]] + channels = { + channel_value: DataSet1D( + name=arrays[0], + x=arrays[1], + y=arrays[2], + ye=arrays[3], + xe=arrays[4], + model=model, + auto_background=False, + ) + for channel_value, arrays in raw['channels'].items() + } + return PolarizedDataSet( + name=project_dict['experiments_names'][key], + channels=channels, + model=model, + ) + def _get_materials_in_models(self) -> MaterialCollection: """Get materials in models.""" materials_in_model = MaterialCollection(populate_if_none=False) @@ -1516,6 +1568,11 @@ def _unwrapped_angle(angle: np.ndarray, mask: np.ndarray) -> np.ndarray: return unwrapped +def _has_both_nsf_channels(channels: List[PolarizationChannel]) -> bool: + """Whether both non-spin-flip channels (pp, mm) — the pair spin asymmetry needs — are present.""" + return PolarizationChannel.PP in channels and PolarizationChannel.MM in channels + + def _ordered_channel_arrays(dataset: DataSet1D) -> tuple: """A channel's (q, reflectivity, variance) arrays, ordered and checked. @@ -1557,6 +1614,12 @@ def _ordered_channel_arrays(dataset: DataSet1D) -> tuple: if not np.array_equal(order, np.arange(q.size)): logger.warning("Channel '%s' is not ordered in q; sorting it before pairing.", name) q, y, variance = q[order], y[order], variance[order] + # A single-point channel has an empty `np.diff`, so the duplicate-q check + # below is vacuously satisfied and it passes through here unrejected. That + # is intentional: a one-point channel is a legitimate (if degenerate) SA + # pair when it lines up exactly with the other channel's grid, and + # `_interpolate_with_variance` handles a size-1 `q_source` correctly (its + # clipped `searchsorted` result always resolves to that single point). if np.any(np.diff(q) <= 0): raise ValueError(f"Channel '{name}' visits the same q more than once; the pairing would be ambiguous.") return q, y, variance diff --git a/src/easyreflectometry/sample/elements/layers/layer.py b/src/easyreflectometry/sample/elements/layers/layer.py index fe580cb4..b926d494 100644 --- a/src/easyreflectometry/sample/elements/layers/layer.py +++ b/src/easyreflectometry/sample/elements/layers/layer.py @@ -162,6 +162,9 @@ def magnetism(self, value: Optional[LayerMagnetism]) -> None: self.interface().remove_layer_magnetism(self.unique_name) # Detach the calculator callbacks of the removed parameters so later # value changes on the detached object no longer reach the backend. + # `property()` (fget/fset/fdel all None) is easyscience's own "no + # callback" sentinel -- `Parameter.__copy__` sets it the same way -- + # so a later `fset` guard in `Parameter` cleanly no-ops. self._magnetism.rho_m._callback = property() self._magnetism.theta_m._callback = property() self._magnetism = value diff --git a/tests/test_polarized_fitting.py b/tests/test_polarized_fitting.py index 073b5904..d84f536f 100644 --- a/tests/test_polarized_fitting.py +++ b/tests/test_polarized_fitting.py @@ -6,6 +6,7 @@ per-channel experiment loading, and simultaneous multi-channel fitting. """ +import json from unittest.mock import patch import numpy as np @@ -111,6 +112,36 @@ def test_fit_func_for_channel(self): fit_func = model.interface.fit_func_for_channel('mm') assert_allclose(fit_func(Q, model.unique_name), reference['mm'], rtol=1e-12) + def test_magnetism_inside_a_repeating_multilayer_raises_instead_of_silently_wrong(self): + """refl1d itself does not support repeated magnetic slabs (`profile.repeat`). + + `magnetism.ipynb` documents this as a known limitation; this test pins + that the failure is loud (`NotImplementedError`) rather than a silent + wrong answer, so the tutorial's claim stays checked against behaviour. + """ + from easyreflectometry.sample import RepeatingMultilayer + + vacuum = Material(sld=0, isld=0, name='Vacuum') + material = Material(sld=4.0, isld=0, name='Fe') + si = Material(sld=2.047, isld=0, name='Si') + superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase') + layer = Layer( + material=material, + thickness=50, + roughness=2, + magnetism=LayerMagnetism(rho_m=5.0, theta_m=40.0), + name='Fe film', + ) + subphase = Layer(material=si, thickness=0, roughness=3, name='Si Subphase') + repeated = RepeatingMultilayer(layer, repetitions=3, name='Repeated Fe') + sample = Sample(Multilayer(superphase), repeated, Multilayer(subphase), name='Repeated magnetic sample') + model = Model(sample=sample, scale=1, background=0, name='Repeated magnetic model') + model.resolution_function = PercentageFwhm(0) + model.interface = _refl1d_interface() + + with pytest.raises(NotImplementedError, match='[Rr]epeat'): + model.interface().polarized_reflectivity_profiles(Q, model.unique_name) + class TestPolarizedCache: def test_repeated_calculation_hits_cache(self): @@ -245,6 +276,59 @@ def test_suggest_polarized_channel_assignment(self, tmp_path): assert suggestion[str(mm_path)] == PolarizationChannel.MM assert suggestion[str(unknown_path)] is None + def test_project_as_dict_and_from_dict_round_trip_a_polarized_experiment(self, tmp_path): + pp_path = self._write_channel_file(tmp_path, 'sample_uu.txt') + mm_path = self._write_channel_file(tmp_path, 'sample_dd.txt') + + project = Project() + project.calculator = 'refl1d' + project.default_model() + project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) + original = project.experiments[0] + + # Must not raise (PolarizedDataSet has no .x/.y/.ye of its own) and must + # be plain-JSON-serializable, since that is what `save_as_json` does with it. + project_dict = project.as_dict(include_materials_not_in_model=True) + json.dumps(project_dict) + + global_object.map._clear() + reloaded_project = Project() + reloaded_project.from_dict(project_dict) + reloaded = reloaded_project.experiments[0] + + assert isinstance(reloaded, PolarizedDataSet) + assert reloaded.name == original.name + assert reloaded.available_channels == original.available_channels + assert reloaded.model is reloaded_project.models[0] + for channel in original.available_channels: + assert reloaded[channel].name == original[channel].name + assert reloaded[channel].model is reloaded_project.models[0] + assert_allclose(reloaded[channel].x, original[channel].x) + assert_allclose(reloaded[channel].y, original[channel].y) + assert_allclose(reloaded[channel].ye, original[channel].ye) + + def test_project_save_as_json_and_load_from_json_round_trip_a_polarized_experiment(self, tmp_path): + pp_path = self._write_channel_file(tmp_path, 'sample_uu.txt') + mm_path = self._write_channel_file(tmp_path, 'sample_dd.txt') + + project = Project() + project.set_path_project_parent(tmp_path) + project.calculator = 'refl1d' + project.default_model() + project._info['name'] = 'Polarized round trip' + project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) + + project.save_as_json() + assert project.path_json.exists() + + global_object.map._clear() + reloaded_project = Project() + reloaded_project.load_from_json(project.path_json) + reloaded = reloaded_project.experiments[0] + + assert isinstance(reloaded, PolarizedDataSet) + assert reloaded.available_channels == [PolarizationChannel.PP, PolarizationChannel.MM] + class TestChannelAwareExperimentAccessors: """`experimental_data_for_model_at_index(index, channel=…)` and friends.""" From cffcd18d5428928124f2da32dd988abe7a2ce080 Mon Sep 17 00:00:00 2001 From: rozyczko Date: Tue, 18 Aug 2026 15:21:42 +0200 Subject: [PATCH 19/22] added polarized fitting example/notebook --- .../advancedfitting/polarized_fitting.ipynb | 528 ++++++++++++++++++ .../advancedfitting/polarized_fitting.py | 417 ++++++++++++++ docs/mkdocs.yml | 1 + 3 files changed, 946 insertions(+) create mode 100644 docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb create mode 100644 docs/docs/tutorials/advancedfitting/polarized_fitting.py diff --git a/docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb b/docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb new file mode 100644 index 00000000..0200940c --- /dev/null +++ b/docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb @@ -0,0 +1,528 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Polarized Neutron Reflectometry: Channels, Depth Profiles & Simultaneous Fitting\n", + "\n", + "`magnetism.ipynb` (in the *Simulation* section) introduces magnetic layers and\n", + "how to select a single polarization channel. This tutorial picks up from\n", + "there and focuses on what is new for **polarization analysis**:\n", + "\n", + "1. Computing all four spin cross-sections (`pp`, `pm`, `mp`, `mm`) in\n", + " a single call.\n", + "2. Reading the spin-resolved depth profile. The potential each neutron\n", + " spin state actually sees.\n", + "3. Loading a polarized experiment from per-channel data files and forming the\n", + " **spin asymmetry**, with correct error propagation.\n", + "4. **Fitting multiple polarization channels simultaneously** against one\n", + " shared model with `MultiFitter.fit_polarized()`.\n", + " First recovering the moment's magnitude from the two\n", + " non-spin-flip channels, then recovering the full magnetization vector\n", + " (magnitude *and* direction) from all four channels.\n", + "\n", + "Only the `refl1d` calculator supports magnetism; `refnx` does\n", + "not. All magnetism handling: enabling it on the calculator, computing\n", + "channels, fitting, requires `refl1d`.\n", + "\n", + "The reflectometry convention used throughout: with the default guide field,\n", + "a moment at ``theta_m = 270`` degrees is aligned with it (no spin-flip\n", + "scattering); ``theta_m = 90`` is anti-aligned. A **canted** moment away from\n", + "270/90 produces spin-flip scattering (`pm`, `mp`) alongside the\n", + "non-spin-flip channels (`pp`, `mm`). This is why the sample below uses\n", + "``theta_m = 45`` degrees rather than a value aligned with the guide field:\n", + "it is the only choice that makes all four channels visually distinct." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import tempfile\n", + "from pathlib import Path\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "from easyreflectometry.calculators import CalculatorFactory\n", + "from easyreflectometry.data import DataSet1D\n", + "from easyreflectometry.data import PolarizedDataSet\n", + "from easyreflectometry.fitting import MultiFitter\n", + "from easyreflectometry.model import Model\n", + "from easyreflectometry.model import ModelCollection\n", + "from easyreflectometry.model import PercentageFwhm\n", + "from easyreflectometry.project import Project\n", + "from easyreflectometry.sample import Layer\n", + "from easyreflectometry.sample import LayerMagnetism\n", + "from easyreflectometry.sample import Material\n", + "from easyreflectometry.sample import Multilayer\n", + "from easyreflectometry.sample import Sample\n", + "\n", + "print('All libraries imported successfully.')" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "## 1. Build a magnetic sample\n", + "\n", + "A single magnetic layer between two non-magnetic media: a thin Fe film\n", + "(nuclear SLD 4, in units of $10^{-6}$ Å$^{-2}$) with an in-plane magnetic\n", + "moment, on a Si substrate below a vacuum superphase.\n", + "\n", + "Magnetism is defined with `Layer.magnetism = LayerMagnetism(rho_m=..., theta_m=...)`\n", + "(or passed directly to `Layer(..., magnetism=...)`, as below). This\n", + "enables magnetism on the calculator once the layer has an interface.\n", + "There is no separate \"turn magnetism on\" step required.\n", + "\n", + "`LayerMagnetism` exposes two standard, fittable `Parameter`s:\n", + "\n", + "| Parameter | Meaning | Unit | Default |\n", + "|-----------|-----------------------------------------|-----------------------------|---------|\n", + "| `rho_m` | Magnetic scattering length density | $10^{-6}$ Å$^{-2}$ | 0.0 |\n", + "| `theta_m` | In-plane moment angle vs. the beam | degree | 270.0 |\n", + "\n", + "We wrap sample construction in a function so the same recipe can be reused\n", + "below to build a \"truth\" model and, later, independent \"fit starting point\"\n", + "models." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "def build_magnetic_model(rho_m: float, theta_m: float, name: str) -> tuple[Model, Layer]:\n", + " \"\"\"Build a Vacuum / Fe(magnetic) / Si model and return it with the magnetic layer.\n", + "\n", + " :param rho_m: Magnetic SLD of the Fe film, in 1e-6/angstrom^2.\n", + " :param theta_m: In-plane moment angle of the Fe film, in degrees.\n", + " :param name: Name for the model.\n", + " :return: The model (interface already switched to refl1d) and the Fe layer,\n", + " so its ``.magnetism`` parameters can be reached directly for fitting.\n", + " \"\"\"\n", + " vacuum = Material(sld=0, isld=0, name='Vacuum')\n", + " iron = Material(sld=4.0, isld=0, name='Fe')\n", + " silicon = Material(sld=2.047, isld=0, name='Si')\n", + "\n", + " superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase')\n", + " film = Layer(\n", + " material=iron,\n", + " thickness=100,\n", + " roughness=0,\n", + " magnetism=LayerMagnetism(rho_m=rho_m, theta_m=theta_m, name='Fe film moment'),\n", + " name='Fe Film',\n", + " )\n", + " subphase = Layer(material=silicon, thickness=0, roughness=0, name='Si Subphase')\n", + "\n", + " sample = Sample(Multilayer(superphase), Multilayer(film), Multilayer(subphase), name='Vacuum / Fe(magnetic) / Si')\n", + " model = Model(sample=sample, scale=1, background=0, name=name)\n", + " model.resolution_function = PercentageFwhm(0) # 0% resolution keeps this simulation clean\n", + "\n", + " interface = CalculatorFactory()\n", + " interface.switch('refl1d') # the only calculator that supports magnetism\n", + " model.interface = interface\n", + "\n", + " return model, film\n", + "\n", + "\n", + "RHO_M_TRUE = 2.5 # 1e-6 / angstrom^2\n", + "THETA_M_TRUE = 45.0 # degrees -- canted, so all four channels differ\n", + "\n", + "truth_model, truth_film = build_magnetic_model(RHO_M_TRUE, THETA_M_TRUE, name='Truth model')\n", + "\n", + "print(f'Fe film magnetism: rho_m = {truth_film.magnetism.rho_m.value}, theta_m = {truth_film.magnetism.theta_m.value}')" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## 2. All four polarization channels\n", + "\n", + "`model.interface.polarized_reflectivity_profiles(q, model_name)` returns a\n", + "dict with all four spin cross-sections at once:\n", + "\n", + "- `'pp'` — non-spin-flip, up-up\n", + "- `'mm'` — non-spin-flip, down-down\n", + "- `'pm'` — spin-flip, up-down\n", + "- `'mp'` — spin-flip, down-up\n", + "\n", + "Internally this is a single `refl1d` kernel evaluation shared by all four\n", + "channels (and cached per model state), so this costs about the same as\n", + "computing one channel. For a single explicit channel without touching any\n", + "calculator state, use `reflectivity_profile_channel(q, model_name, channel)`\n", + "instead. Both are stateless, unlike setting `interface.polarization_channel`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "Q_PLOT = np.linspace(0.001, 0.3, 500)\n", + "\n", + "channels_truth = truth_model.interface.polarized_reflectivity_profiles(Q_PLOT, truth_model.unique_name)\n", + "\n", + "plt.figure(figsize=(8, 5))\n", + "plt.semilogy(Q_PLOT, channels_truth['pp'], '-k', label='pp (non-spin-flip)', linewidth=2)\n", + "plt.semilogy(Q_PLOT, channels_truth['mm'], '-r', label='mm (non-spin-flip)', linewidth=2)\n", + "plt.semilogy(Q_PLOT, channels_truth['pm'], ':k', label='pm (spin-flip)', linewidth=2)\n", + "plt.semilogy(Q_PLOT, channels_truth['mp'], ':r', label='mp (spin-flip)', linewidth=2)\n", + "plt.xlabel('Q / Å⁻¹')\n", + "plt.ylabel('Reflectivity')\n", + "plt.title(f'Four polarization channels (rho_m={RHO_M_TRUE}, theta_m={THETA_M_TRUE}°)')\n", + "plt.legend()\n", + "plt.grid(True, alpha=0.3)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## 3. The spin-resolved depth profile\n", + "\n", + "`Project.magnetic_sld_data_for_model_at_index()` returns the nuclear SLD\n", + "profile alongside the two potentials a neutron in each spin state actually\n", + "experiences: `spin_up = sld + rho_m * cos(theta_m - guide_field_angle)` and\n", + "`spin_down = sld - rho_m * cos(...)`. With a canted moment (not aligned with\n", + "the guide field) the split between the two curves is reduced by that cosine\n", + "factor rather than being the full `rho_m`.\n", + "\n", + "This wraps the model in a `Project`, the same object a GUI application uses\n", + "to manage models, experiments and fitting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "project = Project()\n", + "project.calculator = 'refl1d'\n", + "project.models = ModelCollection(truth_model)\n", + "\n", + "profiles = project.magnetic_sld_data_for_model_at_index(0)\n", + "\n", + "plt.figure(figsize=(8, 5))\n", + "plt.plot(profiles['sld'].x, profiles['sld'].y, '-k', label='Nuclear SLD', linewidth=2)\n", + "plt.plot(profiles['spin_up'].x, profiles['spin_up'].y, '-b', label='Spin-up potential', linewidth=2)\n", + "plt.plot(profiles['spin_down'].x, profiles['spin_down'].y, '-r', label='Spin-down potential', linewidth=2)\n", + "plt.xlabel('z / Å')\n", + "plt.ylabel('SLD / 10⁻⁶ Å⁻²')\n", + "plt.title('Nuclear SLD and the two spin-dependent potentials')\n", + "plt.legend()\n", + "plt.grid(True, alpha=0.3)\n", + "plt.show()\n", + "\n", + "# The moment magnitude and direction are also available on their own, restricted\n", + "# to the depths that actually carry a moment (an angle is meaningless at zero SLD).\n", + "print(f'Peak magnetic SLD in the film: {profiles[\"rho_m\"].y.max():.3f} (expected {RHO_M_TRUE})')\n", + "print(f'Moment angle inside the film: {profiles[\"theta_m\"].y.mean():.1f}° (expected {THETA_M_TRUE}°)')" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "## 4. Loading a polarized experiment and forming the spin asymmetry\n", + "\n", + "A polarized measurement is typically serialized as one data file per spin channel\n", + "(e.g. `..._uu.dat` for up-up, `..._dd.dat` for down-down). Here we simulate\n", + "that by writing the truth model's `pp`/`mm` reflectivity, with 1% relative\n", + "noise, to two files, then loading them back exactly as a user would with\n", + "real instrument output.\n", + "\n", + "The **spin asymmetry** $SA = (R^{++} - R^{--}) / (R^{++} + R^{--})$ is a\n", + "common way to look at polarized data directly: it cancels the non-magnetic\n", + "(nuclear) part of the reflectivity and isolates the magnetic signal, with\n", + "`Project.spin_asymmetry_for_experiment_at_index()` handling the variance\n", + "propagation and dropping points where the denominator is too small to be\n", + "meaningful." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "def add_relative_noise(\n", + " reflectivity: np.ndarray, relative_sigma: float, rng: np.random.Generator\n", + ") -> tuple[np.ndarray, np.ndarray]:\n", + " \"\"\"Add reproducible Gaussian noise scaled to a fixed fraction of the signal.\n", + "\n", + " :param reflectivity: Noise-free reflectivity values.\n", + " :param relative_sigma: Standard deviation as a fraction of the signal (e.g. 0.01 for 1%).\n", + " :param rng: Seeded random number generator, for reproducible tutorial output.\n", + " :return: Noisy reflectivity and the per-point standard deviation (not variance).\n", + " \"\"\"\n", + " sigma = relative_sigma * np.abs(reflectivity)\n", + " noisy = rng.normal(loc=reflectivity, scale=sigma)\n", + " return np.clip(noisy, 1e-12, None), sigma\n", + "\n", + "\n", + "Q_DATA = np.linspace(0.01, 0.25, 60) # a more realistic, instrument-like grid\n", + "rng = np.random.default_rng(seed=42) # fixed seed: this tutorial's output is reproducible\n", + "\n", + "channels_data_grid = truth_model.interface.polarized_reflectivity_profiles(Q_DATA, truth_model.unique_name)\n", + "noisy_channels = {\n", + " channel: add_relative_noise(reflectivity, relative_sigma=0.01, rng=rng)\n", + " for channel, reflectivity in channels_data_grid.items()\n", + "}\n", + "\n", + "tmp_dir = Path(tempfile.mkdtemp(prefix='easyreflectometry_polarized_'))\n", + "pp_path = tmp_dir / 'fe_film_uu.txt'\n", + "mm_path = tmp_dir / 'fe_film_dd.txt'\n", + "np.savetxt(pp_path, np.column_stack([Q_DATA, noisy_channels['pp'][0], noisy_channels['pp'][1]]))\n", + "np.savetxt(mm_path, np.column_stack([Q_DATA, noisy_channels['mm'][0], noisy_channels['mm'][1]]))\n", + "\n", + "# The filename suffixes ('_uu', '_dd') are recognised automatically.\n", + "print(project.suggest_polarized_channel_assignment([pp_path, mm_path]))\n", + "\n", + "experiment_index = project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path})\n", + "loaded_channels = project.experiment_channels_at_index(experiment_index)\n", + "print(f'Loaded polarized experiment at index {experiment_index}, channels: {loaded_channels}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "spin_asymmetry = project.spin_asymmetry_for_experiment_at_index(experiment_index)\n", + "measured, calculated = spin_asymmetry['measured'], spin_asymmetry['calculated']\n", + "\n", + "plt.figure(figsize=(8, 5))\n", + "plt.errorbar(\n", + " measured.x,\n", + " measured.y,\n", + " yerr=np.sqrt(measured.ye),\n", + " fmt='o',\n", + " color='0.3',\n", + " markersize=4,\n", + " alpha=0.6,\n", + " label='Measured (loaded files)',\n", + ")\n", + "plt.plot(calculated.x, calculated.y, '-r', linewidth=2, label='Calculated (truth model)')\n", + "plt.xlabel('Q / Å⁻¹')\n", + "plt.ylabel('Spin asymmetry')\n", + "plt.title('Spin asymmetry: loaded data vs. the model it was generated from')\n", + "plt.legend()\n", + "plt.grid(True, alpha=0.3)\n", + "plt.show()\n", + "\n", + "print(\n", + " f'{measured.x.size} of {Q_DATA.size} points kept '\n", + " f'({spin_asymmetry[\"masked_points\"]} masked: '\n", + " f'{spin_asymmetry[\"low_significance_points\"]} low-significance, '\n", + " f'{spin_asymmetry[\"small_denominator_points\"]} small-denominator).'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## 5. Fitting: recovering the moment magnitude from two channels\n", + "\n", + "The most common polarized experiment measures only the two non-spin-flip\n", + "channels (`pp`, `mm`). If the moment's *direction* is already known from\n", + "other means (sample geometry, prior characterization), that alone is enough\n", + "to recover its *magnitude*. This is the standard polarized-fitting case.\n", + "\n", + "`MultiFitter.fit_polarized()` takes a `PolarizedDataSet` and fits every\n", + "channel it contains **simultaneously against one shared model**: any\n", + "structural parameter (thickness, roughness, nuclear SLD, scale, background)\n", + "is constrained jointly by all measured channels, and so is `rho_m`/`theta_m`.\n", + "Internally `refl1d` still evaluates all cross-sections from a single kernel\n", + "call, so fitting N channels together costs about as much as fitting one.\n", + "\n", + "We start from a deliberately wrong `rho_m` guess and fit against the two\n", + "noisy channels loaded above; `theta_m` stays fixed at its (assumed known)\n", + "true value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "fit_model_2ch, fit_film_2ch = build_magnetic_model(rho_m=1.0, theta_m=THETA_M_TRUE, name='Fit: two channels (rho_m only)')\n", + "\n", + "fit_film_2ch.magnetism.rho_m.fixed = False\n", + "fit_film_2ch.magnetism.rho_m.bounds = (0.0, 5.0)\n", + "fit_film_2ch.magnetism.theta_m.fixed = True # moment direction assumed known\n", + "\n", + "initial_channels_2ch = fit_model_2ch.interface.polarized_reflectivity_profiles(Q_PLOT, fit_model_2ch.unique_name)\n", + "\n", + "fit_data_2ch = PolarizedDataSet(\n", + " name='Fe film (pp, mm)',\n", + " channels={\n", + " 'pp': DataSet1D(name='pp', x=Q_DATA, y=noisy_channels['pp'][0], ye=noisy_channels['pp'][1] ** 2),\n", + " 'mm': DataSet1D(name='mm', x=Q_DATA, y=noisy_channels['mm'][0], ye=noisy_channels['mm'][1] ** 2),\n", + " },\n", + " model=fit_model_2ch, # PolarizedDataSet.model must be the model the fitter is constructed with\n", + ")\n", + "\n", + "fitter_2ch = MultiFitter(fit_model_2ch)\n", + "results_2ch = fitter_2ch.fit_polarized(fit_data_2ch)\n", + "\n", + "print(f'Channels fitted: {list(results_2ch.keys())}, all successful: {all(r.success for r in results_2ch.values())}')\n", + "print(f'rho_m: {fit_film_2ch.magnetism.rho_m.value:.3f} (started at 1.0, true value {RHO_M_TRUE})')\n", + "print(f'Reduced chi^2: {fitter_2ch.reduced_chi:.3f}')\n", + "\n", + "fitted_channels_2ch = fit_model_2ch.interface.polarized_reflectivity_profiles(Q_PLOT, fit_model_2ch.unique_name)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(8, 5))\n", + "plt.errorbar(\n", + " Q_DATA,\n", + " noisy_channels['pp'][0],\n", + " yerr=noisy_channels['pp'][1],\n", + " fmt='o',\n", + " color='0.3',\n", + " markersize=4,\n", + " alpha=0.5,\n", + " label='pp (data)',\n", + ")\n", + "plt.errorbar(\n", + " Q_DATA,\n", + " noisy_channels['mm'][0],\n", + " yerr=noisy_channels['mm'][1],\n", + " fmt='s',\n", + " color='0.6',\n", + " markersize=4,\n", + " alpha=0.5,\n", + " label='mm (data)',\n", + ")\n", + "plt.semilogy(Q_PLOT, initial_channels_2ch['pp'], '--k', linewidth=1, alpha=0.6, label='pp (initial guess)')\n", + "plt.semilogy(Q_PLOT, initial_channels_2ch['mm'], '--r', linewidth=1, alpha=0.6, label='mm (initial guess)')\n", + "plt.semilogy(Q_PLOT, fitted_channels_2ch['pp'], '-k', linewidth=2, label='pp (fitted)')\n", + "plt.semilogy(Q_PLOT, fitted_channels_2ch['mm'], '-r', linewidth=2, label='mm (fitted)')\n", + "plt.yscale('log')\n", + "plt.xlabel('Q / Å⁻¹')\n", + "plt.ylabel('Reflectivity')\n", + "plt.title('Two-channel fit: rho_m recovered from pp and mm together')\n", + "plt.legend(fontsize=8)\n", + "plt.grid(True, alpha=0.3)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "## 6. Fitting: recovering the full magnetization vector from four channels\n", + "\n", + "When the spin-flip channels (`pm`, `mp`) are also measured, `fit_polarized`\n", + "can determine the moment's *direction* as well as its magnitude. Both\n", + "`rho_m` and `theta_m` are freed and constrained jointly by all four\n", + "channels. This is the distinguishing capability of full polarization\n", + "analysis over a non-spin-flip-only measurement.\n", + "\n", + "We start from wrong guesses for **both** parameters this time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "fit_model_4ch, fit_film_4ch = build_magnetic_model(rho_m=1.5, theta_m=60.0, name='Fit: four channels (rho_m and theta_m)')\n", + "\n", + "fit_film_4ch.magnetism.rho_m.fixed = False\n", + "fit_film_4ch.magnetism.rho_m.bounds = (0.0, 5.0)\n", + "fit_film_4ch.magnetism.theta_m.fixed = False\n", + "fit_film_4ch.magnetism.theta_m.bounds = (0.0, 90.0)\n", + "\n", + "fit_data_4ch = PolarizedDataSet(\n", + " name='Fe film (pp, pm, mp, mm)',\n", + " channels={\n", + " channel: DataSet1D(name=channel, x=Q_DATA, y=values[0], ye=values[1] ** 2) for channel, values in noisy_channels.items()\n", + " },\n", + " model=fit_model_4ch,\n", + ")\n", + "\n", + "fitter_4ch = MultiFitter(fit_model_4ch)\n", + "results_4ch = fitter_4ch.fit_polarized(fit_data_4ch)\n", + "\n", + "print(f'Channels fitted: {list(results_4ch.keys())}, all successful: {all(r.success for r in results_4ch.values())}')\n", + "print(f'rho_m: {fit_film_4ch.magnetism.rho_m.value:.3f} (started at 1.5, true value {RHO_M_TRUE})')\n", + "print(f'theta_m: {fit_film_4ch.magnetism.theta_m.value:.1f}° (started at 60.0°, true value {THETA_M_TRUE}°)')\n", + "print(f'Reduced chi^2: {fitter_4ch.reduced_chi:.3f}')" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "See `docs/docs/tutorials/simulation/magnetism.ipynb` for the basics of\n", + "building magnetic samples and selecting a single channel, and\n", + "`tests/test_polarized_fitting.py` for the full, tested API which\n", + "this tutorial is based on." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "easyref", + "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.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/advancedfitting/polarized_fitting.py b/docs/docs/tutorials/advancedfitting/polarized_fitting.py new file mode 100644 index 00000000..b46d2e57 --- /dev/null +++ b/docs/docs/tutorials/advancedfitting/polarized_fitting.py @@ -0,0 +1,417 @@ +# %% [markdown] +# # Polarized Neutron Reflectometry: Channels, Depth Profiles & Simultaneous Fitting +# +# `magnetism.ipynb` (in the *Simulation* section) introduces magnetic layers and +# how to select a single polarization channel. This tutorial picks up from +# there and focuses on what is new for **polarization analysis**: +# +# 1. Computing all four spin cross-sections (`pp`, `pm`, `mp`, `mm`) in a single, +# stateless call. +# 2. Reading off the spin-resolved depth profile — the potential each neutron +# spin state actually sees. +# 3. Loading a polarized experiment from per-channel data files and forming the +# **spin asymmetry**, with proper error propagation. +# 4. **Fitting multiple polarization channels simultaneously** against one +# shared model with `MultiFitter.fit_polarized()` — the headline new +# capability — first recovering just the moment's magnitude from the two +# non-spin-flip channels, then recovering the full magnetization vector +# (magnitude *and* direction) from all four channels. +# +# Only the `refl1d` calculator supports magnetism; `refnx` and `bornagain` do +# not. All magnetism handling — enabling it on the calculator, computing +# channels, fitting — goes through `refl1d`. +# +# The reflectometry convention used throughout: with the default guide field, +# a moment at ``theta_m = 270`` degrees is aligned with it (no spin-flip +# scattering); ``theta_m = 90`` is anti-aligned. A **canted** moment away from +# 270/90 produces spin-flip scattering (`pm`, `mp`) alongside the +# non-spin-flip channels (`pp`, `mm`) — which is why the sample below uses +# ``theta_m = 45`` degrees rather than a value aligned with the guide field: +# it is the only choice that makes all four channels visually distinct. + +# %% +import tempfile +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np + +from easyreflectometry.calculators import CalculatorFactory +from easyreflectometry.data import DataSet1D +from easyreflectometry.data import PolarizedDataSet +from easyreflectometry.fitting import MultiFitter +from easyreflectometry.model import Model +from easyreflectometry.model import ModelCollection +from easyreflectometry.model import PercentageFwhm +from easyreflectometry.project import Project +from easyreflectometry.sample import Layer +from easyreflectometry.sample import LayerMagnetism +from easyreflectometry.sample import Material +from easyreflectometry.sample import Multilayer +from easyreflectometry.sample import Sample + +print('All libraries imported successfully.') + +# %% [markdown] +# ## 1. Build a magnetic sample +# +# A single magnetic layer between two non-magnetic media: a thin Fe film +# (nuclear SLD 4, in units of $10^{-6}$ Å$^{-2}$) with an in-plane magnetic +# moment, sitting on a Si substrate below a vacuum superphase. +# +# Magnetism is attached with `Layer.magnetism = LayerMagnetism(rho_m=..., theta_m=...)` +# (or passed directly to `Layer(..., magnetism=...)`, as below). This +# automatically enables magnetism on the calculator once the layer has an +# interface — there is no separate "turn magnetism on" step required. +# +# `LayerMagnetism` exposes two ordinary, fittable `Parameter`s: +# +# | Parameter | Meaning | Unit | Default | +# |-----------|-----------------------------------------|-----------------------------|---------| +# | `rho_m` | Magnetic scattering length density | $10^{-6}$ Å$^{-2}$ | 0.0 | +# | `theta_m` | In-plane moment angle vs. the beam | degree | 270.0 | +# +# We wrap sample construction in a function so the same recipe can be reused +# below to build a "truth" model and, later, independent "fit starting point" +# models. + + +# %% +def build_magnetic_model(rho_m: float, theta_m: float, name: str) -> tuple[Model, Layer]: + """Build a Vacuum / Fe(magnetic) / Si model and return it with the magnetic layer. + + :param rho_m: Magnetic SLD of the Fe film, in 1e-6/angstrom^2. + :param theta_m: In-plane moment angle of the Fe film, in degrees. + :param name: Name for the model. + :return: The model (interface already switched to refl1d) and the Fe layer, + so its ``.magnetism`` parameters can be reached directly for fitting. + """ + vacuum = Material(sld=0, isld=0, name='Vacuum') + iron = Material(sld=4.0, isld=0, name='Fe') + silicon = Material(sld=2.047, isld=0, name='Si') + + superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase') + film = Layer( + material=iron, + thickness=100, + roughness=0, + magnetism=LayerMagnetism(rho_m=rho_m, theta_m=theta_m, name='Fe film moment'), + name='Fe Film', + ) + subphase = Layer(material=silicon, thickness=0, roughness=0, name='Si Subphase') + + sample = Sample(Multilayer(superphase), Multilayer(film), Multilayer(subphase), name='Vacuum / Fe(magnetic) / Si') + model = Model(sample=sample, scale=1, background=0, name=name) + model.resolution_function = PercentageFwhm(0) # 0% resolution keeps this simulation clean + + interface = CalculatorFactory() + interface.switch('refl1d') # the only calculator that supports magnetism + model.interface = interface + + return model, film + + +RHO_M_TRUE = 2.5 # 1e-6 / angstrom^2 +THETA_M_TRUE = 45.0 # degrees -- canted, so all four channels differ + +truth_model, truth_film = build_magnetic_model(RHO_M_TRUE, THETA_M_TRUE, name='Truth model') + +print(f'Fe film magnetism: rho_m = {truth_film.magnetism.rho_m.value}, theta_m = {truth_film.magnetism.theta_m.value}') + +# %% [markdown] +# ## 2. All four polarization channels, one call +# +# `model.interface.polarized_reflectivity_profiles(q, model_name)` returns a +# dict with all four spin cross-sections at once: +# +# - `'pp'` — non-spin-flip, up-up +# - `'mm'` — non-spin-flip, down-down +# - `'pm'` — spin-flip, up-down +# - `'mp'` — spin-flip, down-up +# +# Internally this is a single `refl1d` kernel evaluation shared by all four +# channels (and cached per model state), so this costs about the same as +# computing one channel. For a single explicit channel without touching any +# calculator state, use `reflectivity_profile_channel(q, model_name, channel)` +# instead — both are stateless, unlike setting `interface.polarization_channel`. + +# %% +Q_PLOT = np.linspace(0.001, 0.3, 500) + +channels_truth = truth_model.interface.polarized_reflectivity_profiles(Q_PLOT, truth_model.unique_name) + +plt.figure(figsize=(8, 5)) +plt.semilogy(Q_PLOT, channels_truth['pp'], '-k', label='pp (non-spin-flip)', linewidth=2) +plt.semilogy(Q_PLOT, channels_truth['mm'], '-r', label='mm (non-spin-flip)', linewidth=2) +plt.semilogy(Q_PLOT, channels_truth['pm'], ':k', label='pm (spin-flip)', linewidth=2) +plt.semilogy(Q_PLOT, channels_truth['mp'], ':r', label='mp (spin-flip)', linewidth=2) +plt.xlabel('Q / Å⁻¹') +plt.ylabel('Reflectivity') +plt.title(f'Four polarization channels (rho_m={RHO_M_TRUE}, theta_m={THETA_M_TRUE}°)') +plt.legend() +plt.grid(True, alpha=0.3) +plt.show() + +# %% [markdown] +# ## 3. The spin-resolved depth profile +# +# `Project.magnetic_sld_data_for_model_at_index()` returns the nuclear SLD +# profile alongside the two potentials a neutron in each spin state actually +# experiences: `spin_up = sld + rho_m * cos(theta_m - guide_field_angle)` and +# `spin_down = sld - rho_m * cos(...)`. With a canted moment (not aligned with +# the guide field) the split between the two curves is reduced by that cosine +# factor rather than being the full `rho_m`. +# +# This wraps the model in a `Project`, the same object a GUI application uses +# to manage models, experiments and fitting — and is the entry point for the +# rest of this tutorial too. + +# %% +project = Project() +project.calculator = 'refl1d' +project.models = ModelCollection(truth_model) + +profiles = project.magnetic_sld_data_for_model_at_index(0) + +plt.figure(figsize=(8, 5)) +plt.plot(profiles['sld'].x, profiles['sld'].y, '-k', label='Nuclear SLD', linewidth=2) +plt.plot(profiles['spin_up'].x, profiles['spin_up'].y, '-b', label='Spin-up potential', linewidth=2) +plt.plot(profiles['spin_down'].x, profiles['spin_down'].y, '-r', label='Spin-down potential', linewidth=2) +plt.xlabel('z / Å') +plt.ylabel('SLD / 10⁻⁶ Å⁻²') +plt.title('Nuclear SLD and the two spin-dependent potentials') +plt.legend() +plt.grid(True, alpha=0.3) +plt.show() + +# The moment magnitude and direction are also available on their own, restricted +# to the depths that actually carry a moment (an angle is meaningless at zero SLD). +print(f'Peak magnetic SLD in the film: {profiles["rho_m"].y.max():.3f} (expected {RHO_M_TRUE})') +print(f'Moment angle inside the film: {profiles["theta_m"].y.mean():.1f}° (expected {THETA_M_TRUE}°)') + +# %% [markdown] +# ## 4. Loading a polarized experiment and forming the spin asymmetry +# +# A polarized measurement typically arrives as one data file per spin channel +# (e.g. `..._uu.dat` for up-up, `..._dd.dat` for down-down). Here we simulate +# that by writing the truth model's `pp`/`mm` reflectivity, with 1% relative +# noise, to two files, then loading them back exactly as a user would with +# real instrument output. +# +# The **spin asymmetry** $SA = (R^{++} - R^{--}) / (R^{++} + R^{--})$ is a +# common way to look at polarized data directly: it cancels the non-magnetic +# (nuclear) part of the reflectivity and isolates the magnetic signal, with +# `Project.spin_asymmetry_for_experiment_at_index()` handling the variance +# propagation and dropping points where the denominator is too small to be +# meaningful. + + +# %% +def add_relative_noise( + reflectivity: np.ndarray, relative_sigma: float, rng: np.random.Generator +) -> tuple[np.ndarray, np.ndarray]: + """Add reproducible Gaussian noise scaled to a fixed fraction of the signal. + + :param reflectivity: Noise-free reflectivity values. + :param relative_sigma: Standard deviation as a fraction of the signal (e.g. 0.01 for 1%). + :param rng: Seeded random number generator, for reproducible tutorial output. + :return: Noisy reflectivity and the per-point standard deviation (not variance). + """ + sigma = relative_sigma * np.abs(reflectivity) + noisy = rng.normal(loc=reflectivity, scale=sigma) + return np.clip(noisy, 1e-12, None), sigma + + +Q_DATA = np.linspace(0.01, 0.25, 60) # a more realistic, instrument-like grid +rng = np.random.default_rng(seed=42) # fixed seed: this tutorial's output is reproducible + +channels_data_grid = truth_model.interface.polarized_reflectivity_profiles(Q_DATA, truth_model.unique_name) +noisy_channels = { + channel: add_relative_noise(reflectivity, relative_sigma=0.01, rng=rng) + for channel, reflectivity in channels_data_grid.items() +} + +tmp_dir = Path(tempfile.mkdtemp(prefix='easyreflectometry_polarized_')) +pp_path = tmp_dir / 'fe_film_uu.txt' +mm_path = tmp_dir / 'fe_film_dd.txt' +np.savetxt(pp_path, np.column_stack([Q_DATA, noisy_channels['pp'][0], noisy_channels['pp'][1]])) +np.savetxt(mm_path, np.column_stack([Q_DATA, noisy_channels['mm'][0], noisy_channels['mm'][1]])) + +# The filename suffixes ('_uu', '_dd') are recognised automatically. +print(project.suggest_polarized_channel_assignment([pp_path, mm_path])) + +experiment_index = project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) +loaded_channels = project.experiment_channels_at_index(experiment_index) +print(f'Loaded polarized experiment at index {experiment_index}, channels: {loaded_channels}') + +# %% +spin_asymmetry = project.spin_asymmetry_for_experiment_at_index(experiment_index) +measured, calculated = spin_asymmetry['measured'], spin_asymmetry['calculated'] + +plt.figure(figsize=(8, 5)) +plt.errorbar( + measured.x, + measured.y, + yerr=np.sqrt(measured.ye), + fmt='o', + color='0.3', + markersize=4, + alpha=0.6, + label='Measured (loaded files)', +) +plt.plot(calculated.x, calculated.y, '-r', linewidth=2, label='Calculated (truth model)') +plt.xlabel('Q / Å⁻¹') +plt.ylabel('Spin asymmetry') +plt.title('Spin asymmetry: loaded data vs. the model it was generated from') +plt.legend() +plt.grid(True, alpha=0.3) +plt.show() + +print( + f'{measured.x.size} of {Q_DATA.size} points kept ' + f'({spin_asymmetry["masked_points"]} masked: ' + f'{spin_asymmetry["low_significance_points"]} low-significance, ' + f'{spin_asymmetry["small_denominator_points"]} small-denominator).' +) + +# %% [markdown] +# ## 5. Fitting: recovering the moment magnitude from two channels +# +# The most common polarized experiment measures only the two non-spin-flip +# channels (`pp`, `mm`). If the moment's *direction* is already known from +# other means (sample geometry, prior characterization), that alone is enough +# to recover its *magnitude* — this is the everyday polarized-fitting case. +# +# `MultiFitter.fit_polarized()` takes a `PolarizedDataSet` and fits every +# channel it contains **simultaneously against one shared model**: any +# structural parameter (thickness, roughness, nuclear SLD, scale, background) +# is constrained jointly by all measured channels, and so is `rho_m`/`theta_m`. +# Internally `refl1d` still evaluates all cross-sections from a single kernel +# call, so fitting N channels together costs about as much as fitting one. +# +# We start from a deliberately wrong `rho_m` guess and fit against the two +# noisy channels loaded above; `theta_m` stays fixed at its (assumed known) +# true value. + +# %% +fit_model_2ch, fit_film_2ch = build_magnetic_model(rho_m=1.0, theta_m=THETA_M_TRUE, name='Fit: two channels (rho_m only)') + +fit_film_2ch.magnetism.rho_m.fixed = False +fit_film_2ch.magnetism.rho_m.bounds = (0.0, 5.0) +fit_film_2ch.magnetism.theta_m.fixed = True # moment direction assumed known + +initial_channels_2ch = fit_model_2ch.interface.polarized_reflectivity_profiles(Q_PLOT, fit_model_2ch.unique_name) + +fit_data_2ch = PolarizedDataSet( + name='Fe film (pp, mm)', + channels={ + 'pp': DataSet1D(name='pp', x=Q_DATA, y=noisy_channels['pp'][0], ye=noisy_channels['pp'][1] ** 2), + 'mm': DataSet1D(name='mm', x=Q_DATA, y=noisy_channels['mm'][0], ye=noisy_channels['mm'][1] ** 2), + }, + model=fit_model_2ch, # PolarizedDataSet.model must be the model the fitter is constructed with +) + +fitter_2ch = MultiFitter(fit_model_2ch) +results_2ch = fitter_2ch.fit_polarized(fit_data_2ch) + +print(f'Channels fitted: {list(results_2ch.keys())}, all successful: {all(r.success for r in results_2ch.values())}') +print(f'rho_m: {fit_film_2ch.magnetism.rho_m.value:.3f} (started at 1.0, true value {RHO_M_TRUE})') +print(f'Reduced chi^2: {fitter_2ch.reduced_chi:.3f}') + +fitted_channels_2ch = fit_model_2ch.interface.polarized_reflectivity_profiles(Q_PLOT, fit_model_2ch.unique_name) + +# %% +plt.figure(figsize=(8, 5)) +plt.errorbar( + Q_DATA, + noisy_channels['pp'][0], + yerr=noisy_channels['pp'][1], + fmt='o', + color='0.3', + markersize=4, + alpha=0.5, + label='pp (data)', +) +plt.errorbar( + Q_DATA, + noisy_channels['mm'][0], + yerr=noisy_channels['mm'][1], + fmt='s', + color='0.6', + markersize=4, + alpha=0.5, + label='mm (data)', +) +plt.semilogy(Q_PLOT, initial_channels_2ch['pp'], '--k', linewidth=1, alpha=0.6, label='pp (initial guess)') +plt.semilogy(Q_PLOT, initial_channels_2ch['mm'], '--r', linewidth=1, alpha=0.6, label='mm (initial guess)') +plt.semilogy(Q_PLOT, fitted_channels_2ch['pp'], '-k', linewidth=2, label='pp (fitted)') +plt.semilogy(Q_PLOT, fitted_channels_2ch['mm'], '-r', linewidth=2, label='mm (fitted)') +plt.yscale('log') +plt.xlabel('Q / Å⁻¹') +plt.ylabel('Reflectivity') +plt.title('Two-channel fit: rho_m recovered from pp and mm together') +plt.legend(fontsize=8) +plt.grid(True, alpha=0.3) +plt.show() + +# %% [markdown] +# ## 6. Fitting: recovering the full magnetization vector from four channels +# +# When the spin-flip channels (`pm`, `mp`) are also measured, `fit_polarized` +# can determine the moment's *direction* as well as its magnitude — both +# `rho_m` and `theta_m` are freed and constrained jointly by all four +# channels. This is the distinguishing capability of full polarization +# analysis over a non-spin-flip-only measurement. +# +# We start from wrong guesses for **both** parameters this time. + +# %% +fit_model_4ch, fit_film_4ch = build_magnetic_model(rho_m=1.5, theta_m=60.0, name='Fit: four channels (rho_m and theta_m)') + +fit_film_4ch.magnetism.rho_m.fixed = False +fit_film_4ch.magnetism.rho_m.bounds = (0.0, 5.0) +fit_film_4ch.magnetism.theta_m.fixed = False +fit_film_4ch.magnetism.theta_m.bounds = (0.0, 90.0) + +fit_data_4ch = PolarizedDataSet( + name='Fe film (pp, pm, mp, mm)', + channels={ + channel: DataSet1D(name=channel, x=Q_DATA, y=values[0], ye=values[1] ** 2) for channel, values in noisy_channels.items() + }, + model=fit_model_4ch, +) + +fitter_4ch = MultiFitter(fit_model_4ch) +results_4ch = fitter_4ch.fit_polarized(fit_data_4ch) + +print(f'Channels fitted: {list(results_4ch.keys())}, all successful: {all(r.success for r in results_4ch.values())}') +print(f'rho_m: {fit_film_4ch.magnetism.rho_m.value:.3f} (started at 1.5, true value {RHO_M_TRUE})') +print(f'theta_m: {fit_film_4ch.magnetism.theta_m.value:.1f}° (started at 60.0°, true value {THETA_M_TRUE}°)') +print(f'Reduced chi^2: {fitter_4ch.reduced_chi:.3f}') + +# %% [markdown] +# ## Summary +# +# New polarization API demonstrated in this tutorial: +# +# - `Layer(..., magnetism=LayerMagnetism(rho_m=, theta_m=))` — attach a +# fittable magnetic moment to a layer; only `refl1d` supports it. +# - `model.interface.polarized_reflectivity_profiles(q, model_name)` — all +# four spin cross-sections (`pp`, `pm`, `mp`, `mm`) from one call. +# - `model.interface.reflectivity_profile_channel(q, model_name, channel)` — +# a single explicit channel, without touching calculator state. +# - `Project.magnetic_sld_data_for_model_at_index()` — nuclear SLD plus the +# spin-up/spin-down potentials. +# - `Project.load_polarized_experiment({'pp': path, 'mm': path, ...})` and +# `Project.suggest_polarized_channel_assignment(paths)` — load and +# auto-detect per-channel data files. +# - `Project.spin_asymmetry_for_experiment_at_index()` — spin asymmetry with +# proper error propagation and physically-motivated point masking. +# - `PolarizedDataSet(channels={...}, model=model)` and +# `MultiFitter(model).fit_polarized(data)` — fit any number of measured +# channels simultaneously against one shared model. +# +# See `docs/docs/tutorials/simulation/magnetism.ipynb` for the basics of +# building magnetic samples and selecting a single channel, and +# `tests/test_polarized_fitting.py` for the full, exhaustively-tested API +# surface this tutorial draws on. diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 1cd19cde..f3ac980d 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -194,6 +194,7 @@ nav: - Solvated Material Fitting: tutorials/fitting/material_solvated.ipynb - Advanced Fitting: - Multi-Contrast Fitting: tutorials/advancedfitting/multi_contrast.ipynb + - Polarized Fitting: tutorials/advancedfitting/polarized_fitting.ipynb - API Reference: - API Reference: api-reference/index.md - Model: api-reference/model.md From f9a444291e77f1eee492a778d32b4aaba3232f2c Mon Sep 17 00:00:00 2001 From: rozyczko Date: Wed, 19 Aug 2026 14:00:58 +0200 Subject: [PATCH 20/22] fixed default sample generation --- src/easyreflectometry/project.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/easyreflectometry/project.py b/src/easyreflectometry/project.py index e9048713..1c7fd29e 100644 --- a/src/easyreflectometry/project.py +++ b/src/easyreflectometry/project.py @@ -312,7 +312,11 @@ def models(self, models: ModelCollection) -> None: self._replace_collection(models, self._models) # Use setter to update indicies for current model, assembly and layer self.current_model_index = 0 - self._materials.extend(self._get_materials_in_models()) + # Only track materials not already in the project's material collection + # (e.g. layers built from self._materials, as in default_model(), would + # otherwise be re-added and trigger a spurious duplicate-item warning). + new_materials = [material for material in self._get_materials_in_models() if material not in self._materials] + self._materials.extend(new_materials) for model in self._models: model.interface = self._calculator self._sync_parameter_states() From 3721c6b3a0dae8292b6690b2942907e082365e80 Mon Sep 17 00:00:00 2001 From: rozyczko Date: Thu, 20 Aug 2026 13:35:58 +0200 Subject: [PATCH 21/22] improved wording in the magnetic fitting notebook --- .../advancedfitting/polarized_fitting.ipynb | 24 +- .../advancedfitting/polarized_fitting.py | 417 ------------------ 2 files changed, 11 insertions(+), 430 deletions(-) delete mode 100644 docs/docs/tutorials/advancedfitting/polarized_fitting.py diff --git a/docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb b/docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb index 0200940c..cb820cbf 100644 --- a/docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb +++ b/docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb @@ -14,18 +14,18 @@ "1. Computing all four spin cross-sections (`pp`, `pm`, `mp`, `mm`) in\n", " a single call.\n", "2. Reading the spin-resolved depth profile. The potential each neutron\n", - " spin state actually sees.\n", + " spin state sees.\n", "3. Loading a polarized experiment from per-channel data files and forming the\n", " **spin asymmetry**, with correct error propagation.\n", "4. **Fitting multiple polarization channels simultaneously** against one\n", " shared model with `MultiFitter.fit_polarized()`.\n", - " First recovering the moment's magnitude from the two\n", + " First, recovering the moment's magnitude from the two\n", " non-spin-flip channels, then recovering the full magnetization vector\n", " (magnitude *and* direction) from all four channels.\n", "\n", - "Only the `refl1d` calculator supports magnetism; `refnx` does\n", - "not. All magnetism handling: enabling it on the calculator, computing\n", - "channels, fitting, requires `refl1d`.\n", + "Only the `refl1d` calculator supports magnetism.\n", + "All magnetism handling: enabling it on the calculator, computing channels,\n", + "fitting, requires `refl1d`.\n", "\n", "The reflectometry convention used throughout: with the default guide field,\n", "a moment at ``theta_m = 270`` degrees is aligned with it (no spin-flip\n", @@ -162,9 +162,9 @@ "\n", "Internally this is a single `refl1d` kernel evaluation shared by all four\n", "channels (and cached per model state), so this costs about the same as\n", - "computing one channel. For a single explicit channel without touching any\n", - "calculator state, use `reflectivity_profile_channel(q, model_name, channel)`\n", - "instead. Both are stateless, unlike setting `interface.polarization_channel`." + "computing one channel. For a single explicit channel use\n", + "`reflectivity_profile_channel(q, model_name, channel)`instead.\n", + "Both are stateless, unlike setting `interface.polarization_channel`." ] }, { @@ -498,15 +498,13 @@ "## Summary\n", "\n", "See `docs/docs/tutorials/simulation/magnetism.ipynb` for the basics of\n", - "building magnetic samples and selecting a single channel, and\n", - "`tests/test_polarized_fitting.py` for the full, tested API which\n", - "this tutorial is based on." + "building magnetic samples and selecting a single channel." ] } ], "metadata": { "kernelspec": { - "display_name": "easyref", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -520,7 +518,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.11" + "version": "3.12.12" } }, "nbformat": 4, diff --git a/docs/docs/tutorials/advancedfitting/polarized_fitting.py b/docs/docs/tutorials/advancedfitting/polarized_fitting.py deleted file mode 100644 index b46d2e57..00000000 --- a/docs/docs/tutorials/advancedfitting/polarized_fitting.py +++ /dev/null @@ -1,417 +0,0 @@ -# %% [markdown] -# # Polarized Neutron Reflectometry: Channels, Depth Profiles & Simultaneous Fitting -# -# `magnetism.ipynb` (in the *Simulation* section) introduces magnetic layers and -# how to select a single polarization channel. This tutorial picks up from -# there and focuses on what is new for **polarization analysis**: -# -# 1. Computing all four spin cross-sections (`pp`, `pm`, `mp`, `mm`) in a single, -# stateless call. -# 2. Reading off the spin-resolved depth profile — the potential each neutron -# spin state actually sees. -# 3. Loading a polarized experiment from per-channel data files and forming the -# **spin asymmetry**, with proper error propagation. -# 4. **Fitting multiple polarization channels simultaneously** against one -# shared model with `MultiFitter.fit_polarized()` — the headline new -# capability — first recovering just the moment's magnitude from the two -# non-spin-flip channels, then recovering the full magnetization vector -# (magnitude *and* direction) from all four channels. -# -# Only the `refl1d` calculator supports magnetism; `refnx` and `bornagain` do -# not. All magnetism handling — enabling it on the calculator, computing -# channels, fitting — goes through `refl1d`. -# -# The reflectometry convention used throughout: with the default guide field, -# a moment at ``theta_m = 270`` degrees is aligned with it (no spin-flip -# scattering); ``theta_m = 90`` is anti-aligned. A **canted** moment away from -# 270/90 produces spin-flip scattering (`pm`, `mp`) alongside the -# non-spin-flip channels (`pp`, `mm`) — which is why the sample below uses -# ``theta_m = 45`` degrees rather than a value aligned with the guide field: -# it is the only choice that makes all four channels visually distinct. - -# %% -import tempfile -from pathlib import Path - -import matplotlib.pyplot as plt -import numpy as np - -from easyreflectometry.calculators import CalculatorFactory -from easyreflectometry.data import DataSet1D -from easyreflectometry.data import PolarizedDataSet -from easyreflectometry.fitting import MultiFitter -from easyreflectometry.model import Model -from easyreflectometry.model import ModelCollection -from easyreflectometry.model import PercentageFwhm -from easyreflectometry.project import Project -from easyreflectometry.sample import Layer -from easyreflectometry.sample import LayerMagnetism -from easyreflectometry.sample import Material -from easyreflectometry.sample import Multilayer -from easyreflectometry.sample import Sample - -print('All libraries imported successfully.') - -# %% [markdown] -# ## 1. Build a magnetic sample -# -# A single magnetic layer between two non-magnetic media: a thin Fe film -# (nuclear SLD 4, in units of $10^{-6}$ Å$^{-2}$) with an in-plane magnetic -# moment, sitting on a Si substrate below a vacuum superphase. -# -# Magnetism is attached with `Layer.magnetism = LayerMagnetism(rho_m=..., theta_m=...)` -# (or passed directly to `Layer(..., magnetism=...)`, as below). This -# automatically enables magnetism on the calculator once the layer has an -# interface — there is no separate "turn magnetism on" step required. -# -# `LayerMagnetism` exposes two ordinary, fittable `Parameter`s: -# -# | Parameter | Meaning | Unit | Default | -# |-----------|-----------------------------------------|-----------------------------|---------| -# | `rho_m` | Magnetic scattering length density | $10^{-6}$ Å$^{-2}$ | 0.0 | -# | `theta_m` | In-plane moment angle vs. the beam | degree | 270.0 | -# -# We wrap sample construction in a function so the same recipe can be reused -# below to build a "truth" model and, later, independent "fit starting point" -# models. - - -# %% -def build_magnetic_model(rho_m: float, theta_m: float, name: str) -> tuple[Model, Layer]: - """Build a Vacuum / Fe(magnetic) / Si model and return it with the magnetic layer. - - :param rho_m: Magnetic SLD of the Fe film, in 1e-6/angstrom^2. - :param theta_m: In-plane moment angle of the Fe film, in degrees. - :param name: Name for the model. - :return: The model (interface already switched to refl1d) and the Fe layer, - so its ``.magnetism`` parameters can be reached directly for fitting. - """ - vacuum = Material(sld=0, isld=0, name='Vacuum') - iron = Material(sld=4.0, isld=0, name='Fe') - silicon = Material(sld=2.047, isld=0, name='Si') - - superphase = Layer(material=vacuum, thickness=0, roughness=0, name='Vacuum Superphase') - film = Layer( - material=iron, - thickness=100, - roughness=0, - magnetism=LayerMagnetism(rho_m=rho_m, theta_m=theta_m, name='Fe film moment'), - name='Fe Film', - ) - subphase = Layer(material=silicon, thickness=0, roughness=0, name='Si Subphase') - - sample = Sample(Multilayer(superphase), Multilayer(film), Multilayer(subphase), name='Vacuum / Fe(magnetic) / Si') - model = Model(sample=sample, scale=1, background=0, name=name) - model.resolution_function = PercentageFwhm(0) # 0% resolution keeps this simulation clean - - interface = CalculatorFactory() - interface.switch('refl1d') # the only calculator that supports magnetism - model.interface = interface - - return model, film - - -RHO_M_TRUE = 2.5 # 1e-6 / angstrom^2 -THETA_M_TRUE = 45.0 # degrees -- canted, so all four channels differ - -truth_model, truth_film = build_magnetic_model(RHO_M_TRUE, THETA_M_TRUE, name='Truth model') - -print(f'Fe film magnetism: rho_m = {truth_film.magnetism.rho_m.value}, theta_m = {truth_film.magnetism.theta_m.value}') - -# %% [markdown] -# ## 2. All four polarization channels, one call -# -# `model.interface.polarized_reflectivity_profiles(q, model_name)` returns a -# dict with all four spin cross-sections at once: -# -# - `'pp'` — non-spin-flip, up-up -# - `'mm'` — non-spin-flip, down-down -# - `'pm'` — spin-flip, up-down -# - `'mp'` — spin-flip, down-up -# -# Internally this is a single `refl1d` kernel evaluation shared by all four -# channels (and cached per model state), so this costs about the same as -# computing one channel. For a single explicit channel without touching any -# calculator state, use `reflectivity_profile_channel(q, model_name, channel)` -# instead — both are stateless, unlike setting `interface.polarization_channel`. - -# %% -Q_PLOT = np.linspace(0.001, 0.3, 500) - -channels_truth = truth_model.interface.polarized_reflectivity_profiles(Q_PLOT, truth_model.unique_name) - -plt.figure(figsize=(8, 5)) -plt.semilogy(Q_PLOT, channels_truth['pp'], '-k', label='pp (non-spin-flip)', linewidth=2) -plt.semilogy(Q_PLOT, channels_truth['mm'], '-r', label='mm (non-spin-flip)', linewidth=2) -plt.semilogy(Q_PLOT, channels_truth['pm'], ':k', label='pm (spin-flip)', linewidth=2) -plt.semilogy(Q_PLOT, channels_truth['mp'], ':r', label='mp (spin-flip)', linewidth=2) -plt.xlabel('Q / Å⁻¹') -plt.ylabel('Reflectivity') -plt.title(f'Four polarization channels (rho_m={RHO_M_TRUE}, theta_m={THETA_M_TRUE}°)') -plt.legend() -plt.grid(True, alpha=0.3) -plt.show() - -# %% [markdown] -# ## 3. The spin-resolved depth profile -# -# `Project.magnetic_sld_data_for_model_at_index()` returns the nuclear SLD -# profile alongside the two potentials a neutron in each spin state actually -# experiences: `spin_up = sld + rho_m * cos(theta_m - guide_field_angle)` and -# `spin_down = sld - rho_m * cos(...)`. With a canted moment (not aligned with -# the guide field) the split between the two curves is reduced by that cosine -# factor rather than being the full `rho_m`. -# -# This wraps the model in a `Project`, the same object a GUI application uses -# to manage models, experiments and fitting — and is the entry point for the -# rest of this tutorial too. - -# %% -project = Project() -project.calculator = 'refl1d' -project.models = ModelCollection(truth_model) - -profiles = project.magnetic_sld_data_for_model_at_index(0) - -plt.figure(figsize=(8, 5)) -plt.plot(profiles['sld'].x, profiles['sld'].y, '-k', label='Nuclear SLD', linewidth=2) -plt.plot(profiles['spin_up'].x, profiles['spin_up'].y, '-b', label='Spin-up potential', linewidth=2) -plt.plot(profiles['spin_down'].x, profiles['spin_down'].y, '-r', label='Spin-down potential', linewidth=2) -plt.xlabel('z / Å') -plt.ylabel('SLD / 10⁻⁶ Å⁻²') -plt.title('Nuclear SLD and the two spin-dependent potentials') -plt.legend() -plt.grid(True, alpha=0.3) -plt.show() - -# The moment magnitude and direction are also available on their own, restricted -# to the depths that actually carry a moment (an angle is meaningless at zero SLD). -print(f'Peak magnetic SLD in the film: {profiles["rho_m"].y.max():.3f} (expected {RHO_M_TRUE})') -print(f'Moment angle inside the film: {profiles["theta_m"].y.mean():.1f}° (expected {THETA_M_TRUE}°)') - -# %% [markdown] -# ## 4. Loading a polarized experiment and forming the spin asymmetry -# -# A polarized measurement typically arrives as one data file per spin channel -# (e.g. `..._uu.dat` for up-up, `..._dd.dat` for down-down). Here we simulate -# that by writing the truth model's `pp`/`mm` reflectivity, with 1% relative -# noise, to two files, then loading them back exactly as a user would with -# real instrument output. -# -# The **spin asymmetry** $SA = (R^{++} - R^{--}) / (R^{++} + R^{--})$ is a -# common way to look at polarized data directly: it cancels the non-magnetic -# (nuclear) part of the reflectivity and isolates the magnetic signal, with -# `Project.spin_asymmetry_for_experiment_at_index()` handling the variance -# propagation and dropping points where the denominator is too small to be -# meaningful. - - -# %% -def add_relative_noise( - reflectivity: np.ndarray, relative_sigma: float, rng: np.random.Generator -) -> tuple[np.ndarray, np.ndarray]: - """Add reproducible Gaussian noise scaled to a fixed fraction of the signal. - - :param reflectivity: Noise-free reflectivity values. - :param relative_sigma: Standard deviation as a fraction of the signal (e.g. 0.01 for 1%). - :param rng: Seeded random number generator, for reproducible tutorial output. - :return: Noisy reflectivity and the per-point standard deviation (not variance). - """ - sigma = relative_sigma * np.abs(reflectivity) - noisy = rng.normal(loc=reflectivity, scale=sigma) - return np.clip(noisy, 1e-12, None), sigma - - -Q_DATA = np.linspace(0.01, 0.25, 60) # a more realistic, instrument-like grid -rng = np.random.default_rng(seed=42) # fixed seed: this tutorial's output is reproducible - -channels_data_grid = truth_model.interface.polarized_reflectivity_profiles(Q_DATA, truth_model.unique_name) -noisy_channels = { - channel: add_relative_noise(reflectivity, relative_sigma=0.01, rng=rng) - for channel, reflectivity in channels_data_grid.items() -} - -tmp_dir = Path(tempfile.mkdtemp(prefix='easyreflectometry_polarized_')) -pp_path = tmp_dir / 'fe_film_uu.txt' -mm_path = tmp_dir / 'fe_film_dd.txt' -np.savetxt(pp_path, np.column_stack([Q_DATA, noisy_channels['pp'][0], noisy_channels['pp'][1]])) -np.savetxt(mm_path, np.column_stack([Q_DATA, noisy_channels['mm'][0], noisy_channels['mm'][1]])) - -# The filename suffixes ('_uu', '_dd') are recognised automatically. -print(project.suggest_polarized_channel_assignment([pp_path, mm_path])) - -experiment_index = project.load_polarized_experiment({'pp': pp_path, 'mm': mm_path}) -loaded_channels = project.experiment_channels_at_index(experiment_index) -print(f'Loaded polarized experiment at index {experiment_index}, channels: {loaded_channels}') - -# %% -spin_asymmetry = project.spin_asymmetry_for_experiment_at_index(experiment_index) -measured, calculated = spin_asymmetry['measured'], spin_asymmetry['calculated'] - -plt.figure(figsize=(8, 5)) -plt.errorbar( - measured.x, - measured.y, - yerr=np.sqrt(measured.ye), - fmt='o', - color='0.3', - markersize=4, - alpha=0.6, - label='Measured (loaded files)', -) -plt.plot(calculated.x, calculated.y, '-r', linewidth=2, label='Calculated (truth model)') -plt.xlabel('Q / Å⁻¹') -plt.ylabel('Spin asymmetry') -plt.title('Spin asymmetry: loaded data vs. the model it was generated from') -plt.legend() -plt.grid(True, alpha=0.3) -plt.show() - -print( - f'{measured.x.size} of {Q_DATA.size} points kept ' - f'({spin_asymmetry["masked_points"]} masked: ' - f'{spin_asymmetry["low_significance_points"]} low-significance, ' - f'{spin_asymmetry["small_denominator_points"]} small-denominator).' -) - -# %% [markdown] -# ## 5. Fitting: recovering the moment magnitude from two channels -# -# The most common polarized experiment measures only the two non-spin-flip -# channels (`pp`, `mm`). If the moment's *direction* is already known from -# other means (sample geometry, prior characterization), that alone is enough -# to recover its *magnitude* — this is the everyday polarized-fitting case. -# -# `MultiFitter.fit_polarized()` takes a `PolarizedDataSet` and fits every -# channel it contains **simultaneously against one shared model**: any -# structural parameter (thickness, roughness, nuclear SLD, scale, background) -# is constrained jointly by all measured channels, and so is `rho_m`/`theta_m`. -# Internally `refl1d` still evaluates all cross-sections from a single kernel -# call, so fitting N channels together costs about as much as fitting one. -# -# We start from a deliberately wrong `rho_m` guess and fit against the two -# noisy channels loaded above; `theta_m` stays fixed at its (assumed known) -# true value. - -# %% -fit_model_2ch, fit_film_2ch = build_magnetic_model(rho_m=1.0, theta_m=THETA_M_TRUE, name='Fit: two channels (rho_m only)') - -fit_film_2ch.magnetism.rho_m.fixed = False -fit_film_2ch.magnetism.rho_m.bounds = (0.0, 5.0) -fit_film_2ch.magnetism.theta_m.fixed = True # moment direction assumed known - -initial_channels_2ch = fit_model_2ch.interface.polarized_reflectivity_profiles(Q_PLOT, fit_model_2ch.unique_name) - -fit_data_2ch = PolarizedDataSet( - name='Fe film (pp, mm)', - channels={ - 'pp': DataSet1D(name='pp', x=Q_DATA, y=noisy_channels['pp'][0], ye=noisy_channels['pp'][1] ** 2), - 'mm': DataSet1D(name='mm', x=Q_DATA, y=noisy_channels['mm'][0], ye=noisy_channels['mm'][1] ** 2), - }, - model=fit_model_2ch, # PolarizedDataSet.model must be the model the fitter is constructed with -) - -fitter_2ch = MultiFitter(fit_model_2ch) -results_2ch = fitter_2ch.fit_polarized(fit_data_2ch) - -print(f'Channels fitted: {list(results_2ch.keys())}, all successful: {all(r.success for r in results_2ch.values())}') -print(f'rho_m: {fit_film_2ch.magnetism.rho_m.value:.3f} (started at 1.0, true value {RHO_M_TRUE})') -print(f'Reduced chi^2: {fitter_2ch.reduced_chi:.3f}') - -fitted_channels_2ch = fit_model_2ch.interface.polarized_reflectivity_profiles(Q_PLOT, fit_model_2ch.unique_name) - -# %% -plt.figure(figsize=(8, 5)) -plt.errorbar( - Q_DATA, - noisy_channels['pp'][0], - yerr=noisy_channels['pp'][1], - fmt='o', - color='0.3', - markersize=4, - alpha=0.5, - label='pp (data)', -) -plt.errorbar( - Q_DATA, - noisy_channels['mm'][0], - yerr=noisy_channels['mm'][1], - fmt='s', - color='0.6', - markersize=4, - alpha=0.5, - label='mm (data)', -) -plt.semilogy(Q_PLOT, initial_channels_2ch['pp'], '--k', linewidth=1, alpha=0.6, label='pp (initial guess)') -plt.semilogy(Q_PLOT, initial_channels_2ch['mm'], '--r', linewidth=1, alpha=0.6, label='mm (initial guess)') -plt.semilogy(Q_PLOT, fitted_channels_2ch['pp'], '-k', linewidth=2, label='pp (fitted)') -plt.semilogy(Q_PLOT, fitted_channels_2ch['mm'], '-r', linewidth=2, label='mm (fitted)') -plt.yscale('log') -plt.xlabel('Q / Å⁻¹') -plt.ylabel('Reflectivity') -plt.title('Two-channel fit: rho_m recovered from pp and mm together') -plt.legend(fontsize=8) -plt.grid(True, alpha=0.3) -plt.show() - -# %% [markdown] -# ## 6. Fitting: recovering the full magnetization vector from four channels -# -# When the spin-flip channels (`pm`, `mp`) are also measured, `fit_polarized` -# can determine the moment's *direction* as well as its magnitude — both -# `rho_m` and `theta_m` are freed and constrained jointly by all four -# channels. This is the distinguishing capability of full polarization -# analysis over a non-spin-flip-only measurement. -# -# We start from wrong guesses for **both** parameters this time. - -# %% -fit_model_4ch, fit_film_4ch = build_magnetic_model(rho_m=1.5, theta_m=60.0, name='Fit: four channels (rho_m and theta_m)') - -fit_film_4ch.magnetism.rho_m.fixed = False -fit_film_4ch.magnetism.rho_m.bounds = (0.0, 5.0) -fit_film_4ch.magnetism.theta_m.fixed = False -fit_film_4ch.magnetism.theta_m.bounds = (0.0, 90.0) - -fit_data_4ch = PolarizedDataSet( - name='Fe film (pp, pm, mp, mm)', - channels={ - channel: DataSet1D(name=channel, x=Q_DATA, y=values[0], ye=values[1] ** 2) for channel, values in noisy_channels.items() - }, - model=fit_model_4ch, -) - -fitter_4ch = MultiFitter(fit_model_4ch) -results_4ch = fitter_4ch.fit_polarized(fit_data_4ch) - -print(f'Channels fitted: {list(results_4ch.keys())}, all successful: {all(r.success for r in results_4ch.values())}') -print(f'rho_m: {fit_film_4ch.magnetism.rho_m.value:.3f} (started at 1.5, true value {RHO_M_TRUE})') -print(f'theta_m: {fit_film_4ch.magnetism.theta_m.value:.1f}° (started at 60.0°, true value {THETA_M_TRUE}°)') -print(f'Reduced chi^2: {fitter_4ch.reduced_chi:.3f}') - -# %% [markdown] -# ## Summary -# -# New polarization API demonstrated in this tutorial: -# -# - `Layer(..., magnetism=LayerMagnetism(rho_m=, theta_m=))` — attach a -# fittable magnetic moment to a layer; only `refl1d` supports it. -# - `model.interface.polarized_reflectivity_profiles(q, model_name)` — all -# four spin cross-sections (`pp`, `pm`, `mp`, `mm`) from one call. -# - `model.interface.reflectivity_profile_channel(q, model_name, channel)` — -# a single explicit channel, without touching calculator state. -# - `Project.magnetic_sld_data_for_model_at_index()` — nuclear SLD plus the -# spin-up/spin-down potentials. -# - `Project.load_polarized_experiment({'pp': path, 'mm': path, ...})` and -# `Project.suggest_polarized_channel_assignment(paths)` — load and -# auto-detect per-channel data files. -# - `Project.spin_asymmetry_for_experiment_at_index()` — spin asymmetry with -# proper error propagation and physically-motivated point masking. -# - `PolarizedDataSet(channels={...}, model=model)` and -# `MultiFitter(model).fit_polarized(data)` — fit any number of measured -# channels simultaneously against one shared model. -# -# See `docs/docs/tutorials/simulation/magnetism.ipynb` for the basics of -# building magnetic samples and selecting a single channel, and -# `tests/test_polarized_fitting.py` for the full, exhaustively-tested API -# surface this tutorial draws on. From e92b6b7904a3b926bc87e6d05acb07fd70f3c9d8 Mon Sep 17 00:00:00 2001 From: rozyczko Date: Thu, 20 Aug 2026 14:13:05 +0200 Subject: [PATCH 22/22] move the most expensive test to integration --- tests/integration/test_ort_file_fitting.py | 128 +++++++++++++++++++++ tests/test_ort_file.py | 97 ---------------- 2 files changed, 128 insertions(+), 97 deletions(-) create mode 100644 tests/integration/test_ort_file_fitting.py diff --git a/tests/integration/test_ort_file_fitting.py b/tests/integration/test_ort_file_fitting.py new file mode 100644 index 00000000..ded7cd99 --- /dev/null +++ b/tests/integration/test_ort_file_fitting.py @@ -0,0 +1,128 @@ +# SPDX-FileCopyrightText: 2025 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + +"""End-to-end fit of a real ORT dataset. + +Not part of ``tests/test_ort_file.py`` because the ``fit_model`` fixture +runs a genuine Bumps_simplex fit (max_evaluations=3000), which takes ~40s +on its own -- by far the single most expensive item in the unit test run. +Run explicitly with ``pixi run integration-tests`` (or +``pytest tests/integration``), not part of the default ``pytest tests``/ +``unit-tests`` run. +""" + +import logging +import os + +import numpy as np +import pytest +from easyscience.fitting import AvailableMinimizers + +import easyreflectometry +from easyreflectometry.calculators import CalculatorFactory +from easyreflectometry.data import load +from easyreflectometry.fitting import MultiFitter +from easyreflectometry.model import Model +from easyreflectometry.model import PercentageFwhm +from easyreflectometry.sample import Layer +from easyreflectometry.sample import Material +from easyreflectometry.sample import Multilayer +from easyreflectometry.sample import Sample + +PATH_STATIC = os.path.join(os.path.dirname(easyreflectometry.__file__), '..', '..', 'tests', '_static') + + +@pytest.fixture(scope='module') +def load_data(): + path = os.path.join(PATH_STATIC, 'amor_reduced_iofq.ort') + logging.info('Loading data from %s', path) + data = load(path) + return data + + +@pytest.fixture(scope='module') +def fit_model(load_data): + data = load_data + # Rescale data + reflectivity = data['data']['R_0'].values + scale_factor = 1 / np.max(reflectivity) + data['data']['R_0'].values *= scale_factor + data['data']['R_0'].variances *= scale_factor**2 + + # Create a model for the sample + + si = Material(sld=2.07, isld=0.0, name='Si') + sio2 = Material(sld=3.47, isld=0.0, name='SiO2') + d2o = Material(sld=6.33, isld=0.0, name='D2O') + dlipids = Material(sld=5.0, isld=0.0, name='DLipids') + + superphase = Layer(material=si, thickness=0, roughness=0, name='Si superphase') + sio2_layer = Layer(material=sio2, thickness=20, roughness=4, name='SiO2 layer') + dlipids_layer = Layer(material=dlipids, thickness=40, roughness=4, name='DLipids layer') + subphase = Layer(material=d2o, thickness=0, roughness=5, name='D2O subphase') + + multi_sample = Sample( + Multilayer(superphase), + Multilayer(sio2_layer), + Multilayer(dlipids_layer), + Multilayer(subphase), + name='Multilayer Structure', + ) + + multi_layer_model = Model( + sample=multi_sample, + scale=1, + background=0.000001, + resolution_function=PercentageFwhm(5), + name='Multilayer Model', + ) + + # Set the fitting parameters + + sio2_layer.roughness.min = 3 + sio2_layer.roughness.max = 12 + sio2_layer.material.sld.min = 3.47 + sio2_layer.material.sld.max = 5 + sio2_layer.thickness.min = 10 + sio2_layer.thickness.max = 30 + + subphase.material.sld.min = 6 + dlipids_layer.thickness.min = 30 + dlipids_layer.thickness.max = 60 + dlipids_layer.roughness.min = 3 + dlipids_layer.roughness.max = 10 + dlipids_layer.material.sld.min = 4 + dlipids_layer.material.sld.max = 6 + multi_layer_model.scale.min = 0.8 + multi_layer_model.scale.max = 1.2 + multi_layer_model.background.min = 1e-6 + multi_layer_model.background.max = 1e-3 + + sio2_layer.roughness.free = True + sio2_layer.material.sld.free = True + sio2_layer.thickness.free = True + subphase.material.sld.free = True + dlipids_layer.thickness.free = True + dlipids_layer.roughness.free = True + dlipids_layer.material.sld.free = True + multi_layer_model.scale.free = True + multi_layer_model.background.free = True + + # Run the model and plot the results + + multi_layer_model.interface = CalculatorFactory() + + fitter1 = MultiFitter(multi_layer_model) + fitter1.switch_minimizer(AvailableMinimizers.Bumps_simplex) + fitter1.easy_science_multi_fitter.max_evaluations = 3000 + + analysed = fitter1.fit(data) + return analysed + + +def test_analyze_reduced_data__fit_model_success(fit_model): + assert fit_model['success'] is True + + +def test_analyze_reduced_data__fit_model_reasonable(fit_model): + assert fit_model['reduced_chi'] < 6.0 diff --git a/tests/test_ort_file.py b/tests/test_ort_file.py index ed8180df..fdaec79d 100644 --- a/tests/test_ort_file.py +++ b/tests/test_ort_file.py @@ -6,18 +6,9 @@ import numpy as np import pytest -from easyscience.fitting import AvailableMinimizers import easyreflectometry -from easyreflectometry.calculators import CalculatorFactory from easyreflectometry.data import load -from easyreflectometry.fitting import MultiFitter -from easyreflectometry.model import Model -from easyreflectometry.model import PercentageFwhm -from easyreflectometry.sample import Layer -from easyreflectometry.sample import Material -from easyreflectometry.sample import Multilayer -from easyreflectometry.sample import Sample PATH_STATIC = os.path.join(os.path.dirname(easyreflectometry.__file__), '..', '..', 'tests', '_static') @@ -30,86 +21,6 @@ def load_data(): return data -@pytest.fixture(scope='module') -def fit_model(load_data): - data = load_data - # Rescale data - reflectivity = data['data']['R_0'].values - scale_factor = 1 / np.max(reflectivity) - data['data']['R_0'].values *= scale_factor - data['data']['R_0'].variances *= scale_factor**2 - - # Create a model for the sample - - si = Material(sld=2.07, isld=0.0, name='Si') - sio2 = Material(sld=3.47, isld=0.0, name='SiO2') - d2o = Material(sld=6.33, isld=0.0, name='D2O') - dlipids = Material(sld=5.0, isld=0.0, name='DLipids') - - superphase = Layer(material=si, thickness=0, roughness=0, name='Si superphase') - sio2_layer = Layer(material=sio2, thickness=20, roughness=4, name='SiO2 layer') - dlipids_layer = Layer(material=dlipids, thickness=40, roughness=4, name='DLipids layer') - subphase = Layer(material=d2o, thickness=0, roughness=5, name='D2O subphase') - - multi_sample = Sample( - Multilayer(superphase), - Multilayer(sio2_layer), - Multilayer(dlipids_layer), - Multilayer(subphase), - name='Multilayer Structure', - ) - - multi_layer_model = Model( - sample=multi_sample, - scale=1, - background=0.000001, - resolution_function=PercentageFwhm(5), - name='Multilayer Model', - ) - - # Set the fitting parameters - - sio2_layer.roughness.min = 3 - sio2_layer.roughness.max = 12 - sio2_layer.material.sld.min = 3.47 - sio2_layer.material.sld.max = 5 - sio2_layer.thickness.min = 10 - sio2_layer.thickness.max = 30 - - subphase.material.sld.min = 6 - dlipids_layer.thickness.min = 30 - dlipids_layer.thickness.max = 60 - dlipids_layer.roughness.min = 3 - dlipids_layer.roughness.max = 10 - dlipids_layer.material.sld.min = 4 - dlipids_layer.material.sld.max = 6 - multi_layer_model.scale.min = 0.8 - multi_layer_model.scale.max = 1.2 - multi_layer_model.background.min = 1e-6 - multi_layer_model.background.max = 1e-3 - - sio2_layer.roughness.free = True - sio2_layer.material.sld.free = True - sio2_layer.thickness.free = True - subphase.material.sld.free = True - dlipids_layer.thickness.free = True - dlipids_layer.roughness.free = True - dlipids_layer.material.sld.free = True - multi_layer_model.scale.free = True - multi_layer_model.background.free = True - - # Run the model and plot the results - - multi_layer_model.interface = CalculatorFactory() - - fitter1 = MultiFitter(multi_layer_model) - fitter1.switch_minimizer(AvailableMinimizers.Bumps_simplex) - fitter1.easy_science_multi_fitter.max_evaluations = 3000 - - analysed = fitter1.fit(data) - return analysed - - def test_read_reduced_data__check_structure(load_data): data_keys = load_data['data'].keys() coord_keys = load_data['coords'].keys() @@ -161,11 +72,3 @@ def test_validate_physical_data__q_values_finite(load_data): @pytest.mark.skip('Currently no meta data to check') def test_validate_meta_data__required_meta_data() -> None: pytest.fail(reason='Currently no meta data to check') - - -def test_analyze_reduced_data__fit_model_success(fit_model): - assert fit_model['success'] is True - - -def test_analyze_reduced_data__fit_model_reasonable(fit_model): - assert fit_model['reduced_chi'] < 6.0