diff --git a/docs/docs/tutorials/component_collection.ipynb b/docs/docs/tutorials/component_collection.ipynb index 8cd6c790..5a957afc 100644 --- a/docs/docs/tutorials/component_collection.ipynb +++ b/docs/docs/tutorials/component_collection.ipynb @@ -7,7 +7,7 @@ "source": [ "# Component Collection\n", "\n", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + "Most data will be modelled by a sum of components, which is what a ComponentCollection handles. Here we show how to create a ComponentCollection and add components to it." ] }, { @@ -67,7 +67,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "easydynamics_newbase", "language": "python", "name": "python3" }, diff --git a/docs/docs/tutorials/components.ipynb b/docs/docs/tutorials/components.ipynb index 5ab8836f..7815bcd4 100644 --- a/docs/docs/tutorials/components.ipynb +++ b/docs/docs/tutorials/components.ipynb @@ -7,7 +7,9 @@ "source": [ "# Components\n", "\n", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + "Components are the basic ingredients for all models. Currently, the available components are Gaussian, Lorentzian, Voigt (the convolution of a Gaussian with a Lorentzian), delta function, damped harmonic oscillator and polynomial. This notebooks shows how to use the components. \n", + "\n", + "Note in particular that a Gaussian, Lorentzian, Voigt or delta function where the center has not been given will be centered at 0." ] }, { diff --git a/docs/docs/tutorials/convolution.ipynb b/docs/docs/tutorials/convolution.ipynb index a586ea5d..922970f9 100644 --- a/docs/docs/tutorials/convolution.ipynb +++ b/docs/docs/tutorials/convolution.ipynb @@ -7,7 +7,11 @@ "source": [ "# Convolution\n", "\n", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + "The experimental resolution function must be taken into account when analysing neutron scattering data, especially QENS. In general, the scattering is given by the convolution of the model of the scattering from the sample with the model of the resolution function. Here, both the scattering from the sample and the resolution function is modelled by a ComponentCollection.\n", + "\n", + "Analytical expressions exist for the convolution between Gaussians, Lorentzians and Voigt functions, as well as between delta functions and any other function. We use these expressions whenever possible. When analytical convolution is not possible, e.g. for a Damped Harmonic Oscillator or if detailed balancing is included, we use numerical convolution based on the Fast fourier transform algorithm. The accuracy of numerical convolution depends on several factors such as the width of the peaks related to the bin size and full span of the data. Warnings are given if it seems the accuracy is low, and several settings are available to improve the accuracy.\n", + "\n", + "For most purposes, the convolution will happen behind the scenes, and you will not need to call it yourself. However, we here show how to use it and play around with the settings." ] }, { @@ -93,6 +97,7 @@ "sample_components.append_component(gaussian)\n", "sample_components.append_component(dho)\n", "sample_components.append_component(lorentzian)\n", + "sample_components.append_component(delta)\n", "\n", "resolution_components = ComponentCollection()\n", "resolution_gaussian = Gaussian(display_name='Resolution Gaussian', width=0.15, area=0.8)\n", diff --git a/docs/docs/tutorials/detailed_balance.ipynb b/docs/docs/tutorials/detailed_balance.ipynb index d3bca0ee..d09a2546 100644 --- a/docs/docs/tutorials/detailed_balance.ipynb +++ b/docs/docs/tutorials/detailed_balance.ipynb @@ -6,8 +6,13 @@ "metadata": {}, "source": [ "# Detailed Balance\n", + "Detailed balance describes the relationship between the intensity of inelastic and quasielastic scattering at positive and negative energy transfers. The equation is $S(-{\\bf Q}, E) = \\exp(-\\beta E) S({\\bf Q}, E)$, where $E$ is the energy transfer, ${\\bf {Q}}$ is the momentum transfer and $\\beta=1/k_BT$ is the inverse of the temperature ($T$) multiplied by Boltzman's constant ($k_B$). To enforce this relationship, we can multiply our scattering function using the Detailed Balance Factor (DBF), defined by $DBF = E (1+n)$, where $n$ is the Bose occupation factor.\n", "\n", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + "In some communities it is customary to normalise the DBF by temperature, i.e. $DBF_N = E/(k_B T) (1+n)$.\n", + "\n", + "This notebook shows how to calculate and use the DBF. Note that it will be automatically applied if temperature is set, so you do not have to think about it.\n", + "\n", + "Details on detailed balancing can be found in most textbooks on neutron scattering." ] }, { @@ -46,7 +51,8 @@ "plt.xlabel('Energy transfer (meV)')\n", "plt.ylabel('Detailed balance factor')\n", "plt.title(\n", - " 'Detailed balance factor for different temperatures, normalized to 1 at zero energy transfer'\n", + " 'Detailed balance factor for different temperatures, \\n '\n", + " 'normalized to 1 at zero energy transfer'\n", ")\n", "plt.show()" ] @@ -76,39 +82,11 @@ "plt.title('Detailed balance factor for different temperatures, not normalized')\n", "plt.show()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4", - "metadata": {}, - "outputs": [], - "source": [ - "import scipp as sc\n", - "\n", - "temperatures = [1, 10, 100]\n", - "temperature_unit = 'K'\n", - "energy = np.linspace(-1, 1, 100)\n", - "# energy=1.0\n", - "energy_unit = 'meV'\n", - "\n", - "plt.figure()\n", - "for temperature in temperatures:\n", - " DBF = detailed_balance_factor(\n", - " energy, temperature, sc.Unit('meV'), sc.Unit('K'), divide_by_temperature=False\n", - " )\n", - " plt.plot(energy, DBF, label=f'T={temperature} K')\n", - "plt.legend()\n", - "plt.xlabel('Energy transfer (meV)')\n", - "plt.ylabel('Detailed balance factor')\n", - "plt.title('Detailed balance factor for different temperatures, not normalized')\n", - "plt.show()" - ] } ], "metadata": { "kernelspec": { - "display_name": "newdynamics", + "display_name": "easydynamics_newbase", "language": "python", "name": "python3" }, @@ -122,7 +100,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.13" + "version": "3.12.12" } }, "nbformat": 4, diff --git a/docs/docs/tutorials/diffusion_data_example.h5 b/docs/docs/tutorials/diffusion_data_example.h5 new file mode 100644 index 00000000..78baba30 Binary files /dev/null and b/docs/docs/tutorials/diffusion_data_example.h5 differ diff --git a/docs/docs/tutorials/diffusion_model.ipynb b/docs/docs/tutorials/diffusion_model.ipynb index c093d760..9277486e 100644 --- a/docs/docs/tutorials/diffusion_model.ipynb +++ b/docs/docs/tutorials/diffusion_model.ipynb @@ -6,8 +6,7 @@ "metadata": {}, "source": [ "# Diffusion Model\n", - "\n", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + "We support several standard models of diffusion. Here we show an example of Browniand Translational Diffusion, where the scattering is a Lorentzian with width ($\\Gamma$) given by $\\Gamma = D Q^2$, where $D$ is the diffusion coefficient (in m$^2$/s) and $Q$ is the momentum transfer." ] }, { @@ -66,6 +65,26 @@ "plt.ylabel('Intensity (arb. units)')\n", "plt.title('Brownian Translational Diffusion Model')" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a50c67ec", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate and plot the half width at half maximum (HWHM) as function\n", + "# of Q\n", + "Q = np.linspace(0.1, 2, 101)\n", + "HWHM = diffusion_model.calculate_width(Q)\n", + "plt.figure()\n", + "plt.plot(Q, HWHM)\n", + "plt.xlabel('Q (Å$^{-1}$)')\n", + "plt.ylabel('HWHM (meV)')\n", + "plt.xlim(0, 2.5)\n", + "plt.ylim(0, max(HWHM) * 1.1)\n", + "plt.title('HWHM vs Q for Brownian Translational Diffusion')" + ] } ], "metadata": { diff --git a/docs/docs/tutorials/experiment.ipynb b/docs/docs/tutorials/experiment.ipynb new file mode 100644 index 00000000..6319c61f --- /dev/null +++ b/docs/docs/tutorials/experiment.ipynb @@ -0,0 +1,83 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "906b959a", + "metadata": {}, + "source": [ + "# Experiment\n", + "The experimental data is stored in an Experiment class. Underneath we use Scipp and Plopp to handle and plot the data. We here show how to load an example data set, rebin it and plot it in various ways." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7d23add", + "metadata": {}, + "outputs": [], + "source": [ + "from easydynamics.experiment import Experiment\n", + "\n", + "%matplotlib widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b7c5ca8", + "metadata": {}, + "outputs": [], + "source": [ + "# Load and plot example vanadium data\n", + "vanadium_experiment = Experiment('Vanadium')\n", + "vanadium_experiment.load_hdf5(filename='vanadium_data_example.h5')\n", + "\n", + "vanadium_experiment.plot_data()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "238ba6ee", + "metadata": {}, + "outputs": [], + "source": [ + "# Rebin the data and plot again\n", + "vanadium_experiment.rebin({'Q': 5, 'energy': 50})\n", + "vanadium_experiment.plot_data()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc32ab1f", + "metadata": {}, + "outputs": [], + "source": [ + "# Plot using the plopp slicer with extra arguments\n", + "vanadium_experiment.plot_data(slicer=True, keep='energy', vmin=0, vmax=2.0)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "easydynamics_newbase", + "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/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md index c59f6283..ec6ed05e 100644 --- a/docs/docs/tutorials/index.md +++ b/docs/docs/tutorials/index.md @@ -17,9 +17,16 @@ The tutorials are organized into the following categories: ## Getting Started -- [Component collection](component_collection.ipynb) – Learn how to ... -- [Components](components.ipynb) – Learn how to ... -- [Convolution](convolution.ipynb) – Learn how to ... -- [Detailed balance](detailed_balance.ipynb) – Learn how to ... -- [Diffusion model](diffusion_model.ipynb) – Learn how to ... -- [Sample model](sample_model.ipynb) – Learn how to ... +- [Component collection](component_collection.ipynb) – Learn how to + create a collectin of components for fitting +- [Components](components.ipynb) – Learn how to use the EasyDynamics + components +- [Convolution](convolution.ipynb) – Learn how to calculate the + convolution of your resolution function with your model +- [Detailed balance](detailed_balance.ipynb) – Learn how to apply + detailed balancing to your model +- [Diffusion model](diffusion_model.ipynb) – Learn how to create and use + a model of diffusion +- [Sample model](sample_model.ipynb) – Learn how to create a model of + the scattering from your sample +- [Experiment](experiment.ipynb) - Learn how to load and bin your data diff --git a/docs/docs/tutorials/sample_model.ipynb b/docs/docs/tutorials/sample_model.ipynb index dbd8e0bb..802aff0b 100644 --- a/docs/docs/tutorials/sample_model.ipynb +++ b/docs/docs/tutorials/sample_model.ipynb @@ -6,8 +6,11 @@ "metadata": {}, "source": [ "# Sample Model\n", + "We here introduce the SampleModel, ResolutionModel and BackgroundModel, which all function in a similar way. They are, as the name implies, used to describe scattering from the sample, the resolution function and the background, respectively.\n", "\n", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + "The models describe the scattering from the sample as function of ${\\bf Q}$ and $E$. This is done by generating ComponentCollection's for each ${\\bf Q}$, where each ComponentCollection contains a collection of ModelComponents such as Gaussians and Lorentzians. The model can be given a single template component or a collection of components that will be copied to each ${\\bf Q}$\n", + "\n", + "We further support various models of diffusion, which typically generate one or more Lorentzian components, where the width (and usually the area) has a particular dependence on $Q$." ] }, { diff --git a/docs/docs/tutorials/vanadium_data_example.h5 b/docs/docs/tutorials/vanadium_data_example.h5 new file mode 100644 index 00000000..0c7534a3 Binary files /dev/null and b/docs/docs/tutorials/vanadium_data_example.h5 differ diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 697941c6..88b1bda5 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -178,6 +178,7 @@ nav: - Detailed balance: tutorials/detailed_balance.ipynb - Diffusion model: tutorials/diffusion_model.ipynb - Sample model: tutorials/sample_model.ipynb + - Experiment: tutorials/experiment.ipynb - API Reference: - API Reference: api-reference/index.md - convolution: api-reference/convolution.md diff --git a/pixi.lock b/pixi.lock index e6c7a920..d9461637 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5,6 +5,8 @@ environments: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -78,7 +80,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/ea/b4/694159c15c52b9f7ec7adf49d50e5f8ee71d3e9ef38adb4445d13dd56c20/coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -137,6 +139,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -185,6 +188,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -331,7 +335,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/ce/8a/87af46cccdfa78f53db747b09f5f9a21d5fc38d796834adac09b30a8ce74/coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -390,6 +394,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -438,6 +443,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -584,7 +590,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/82/a8/6e22fdc67242a4a5a153f9438d05944553121c8f4ba70cb072af4c41362e/coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -643,6 +649,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -691,6 +698,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -830,7 +838,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/fa/dc/7282856a407c621c2aad74021680a01b23010bb8ebf427cf5eacda2e876f/coverage-7.13.1-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -889,6 +897,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -936,6 +945,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -1021,6 +1031,8 @@ environments: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -1094,7 +1106,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/f7/7c/347280982982383621d29b8c544cf497ae07ac41e44b1ca4903024131f55/coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -1153,6 +1165,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -1202,6 +1215,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -1348,7 +1362,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/b4/9b/77baf488516e9ced25fc215a6f75d803493fc3f6a1a1227ac35697910c2a/coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -1407,6 +1421,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -1456,6 +1471,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -1602,7 +1618,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/d7/cd/7ab01154e6eb79ee2fab76bf4d89e94c6648116557307ee4ebbb85e5c1bf/coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -1661,6 +1677,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -1710,6 +1727,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -1849,7 +1867,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/27/56/c216625f453df6e0559ed666d246fcbaaa93f3aa99eaa5080cea1229aa3d/coverage-7.13.1-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -1908,6 +1926,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -1956,6 +1975,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -2041,6 +2061,8 @@ environments: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -2114,7 +2136,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/ea/b4/694159c15c52b9f7ec7adf49d50e5f8ee71d3e9ef38adb4445d13dd56c20/coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -2173,6 +2195,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -2221,6 +2244,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -2367,7 +2391,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/ce/8a/87af46cccdfa78f53db747b09f5f9a21d5fc38d796834adac09b30a8ce74/coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -2426,6 +2450,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -2474,6 +2499,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -2620,7 +2646,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/82/a8/6e22fdc67242a4a5a153f9438d05944553121c8f4ba70cb072af4c41362e/coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -2679,6 +2705,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -2727,6 +2754,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -2866,7 +2894,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl - - pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 + - pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 - pypi: https://files.pythonhosted.org/packages/fa/dc/7282856a407c621c2aad74021680a01b23010bb8ebf427cf5eacda2e876f/coverage-7.13.1-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f2/f2/728f041460f1b9739b85ee23b45fa5a505962ea11fd85bdbe2a02b021373/darkdetect-0.8.0-py3-none-any.whl @@ -2925,6 +2953,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/fe/8c/e27aaea0a3fbea002f0e138902432e64f35b39d942cfa13bdc5dd63ce310/jupytext-1.19.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/38/7e/7b91c89a4cf0f543a83be978657afb20c86af6d725253e319589dcc4ce52/lmfit-1.3.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d2/f0/834e479e47e499b6478e807fb57b31cc2db696c4db30557bb6f5aea4a90b/mando-0.7.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl @@ -2972,6 +3001,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c4/36/ce5f75aa7c736a663a901766edc3580098c7ea3959a0e878363a54a3714e/pixi_kernel-0.7.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/79/ad/45312df6b63ba64ea35b8d8f5f0c577aac16e6b416eafe8e1cb34e03f9a7/plumbum-1.10.0-py3-none-any.whl @@ -4061,8 +4091,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 999.0.0+devdirty44 - sha256: a67202e3a0848ef4cdb1a02a62ce4938bb4ef4a8e2c148fb2a36ed9ebfb02bbf + version: 0.1.0+devdirty7 + sha256: de299c914d4a865b9e2fdefa5e3947f37b1f26f73ff9087f7918ee417f3dd288 requires_dist: - darkdetect - easyscience @ git+https://github.com/easyscience/corelib.git@develop @@ -4071,6 +4101,7 @@ packages: - jupyterlab - pandas - pixi-kernel + - plopp - plotly - pooch - py3dmol @@ -4103,8 +4134,7 @@ packages: - validate-pyproject[all] ; extra == 'dev' - versioningit ; extra == 'dev' requires_python: '>=3.11' - editable: true -- pypi: git+https://github.com/easyscience/corelib.git?rev=develop#bd106537fcf522336aa0176aa6ccf215be8a5b86 +- pypi: git+https://github.com/easyscience/corelib.git#bd106537fcf522336aa0176aa6ccf215be8a5b86 name: easyscience version: 2.1.0 requires_dist: @@ -5436,6 +5466,18 @@ packages: - atomicwrites ; extra == 'atomic-cache' - interegular>=0.3.1,<0.4.0 ; extra == 'interegular' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl + name: lazy-loader + version: '0.4' + sha256: 342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc + requires_dist: + - packaging + - importlib-metadata ; python_full_version < '3.8' + - changelist==0.5 ; extra == 'dev' + - pre-commit==3.7.0 ; extra == 'lint' + - pytest>=7.4 ; extra == 'test' + - pytest-cov>=4.1 ; extra == 'test' + requires_python: '>=3.7' - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_105.conda sha256: 1027bd8aa0d5144e954e426ab6218fd5c14e54a98f571985675468b339c808ca md5: 3ec0aa5037d39b06554109a01e6fb0c6 @@ -8534,6 +8576,37 @@ packages: - pytest>=8.4.2 ; extra == 'test' - mypy>=1.18.2 ; extra == 'type' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/84/4a/d070dc6a36c2eb8b8a19b31908d0817e2a85fe0b70f9db20834a495a74e1/plopp-25.11.0-py3-none-any.whl + name: plopp + version: 25.11.0 + sha256: b449415fed4fe9254140393df75c3b57640de7ba0571c12463331b12cbf54180 + requires_dist: + - lazy-loader>=0.4 + - matplotlib>=3.8 + - scipp>=25.5.0 ; extra == 'scipp' + - scipp>=25.5.0 ; extra == 'all' + - ipympl>0.8.4 ; extra == 'all' + - pythreejs>=2.4.1 ; extra == 'all' + - mpltoolbox>=24.6.0 ; extra == 'all' + - ipywidgets>=8.1.0 ; extra == 'all' + - graphviz>=0.20.3 ; extra == 'all' + - graphviz>=0.20.3 ; extra == 'test' + - h5py>=3.12 ; extra == 'test' + - ipympl>=0.8.4 ; extra == 'test' + - ipywidgets>=8.1.0 ; extra == 'test' + - ipykernel<7 ; extra == 'test' + - mpltoolbox>=24.6.0 ; extra == 'test' + - pandas>=2.2.2 ; extra == 'test' + - plotly>=5.15.0 ; extra == 'test' + - pooch>=1.5 ; extra == 'test' + - pyarrow>=13.0.0 ; extra == 'test' + - pytest>=7.0 ; extra == 'test' + - pythreejs>=2.4.1 ; extra == 'test' + - scipp>=25.5.0 ; extra == 'test' + - scipy>=1.10.0 ; extra == 'test' + - xarray>=2024.5.0 ; extra == 'test' + - anywidget>=0.9.0 ; extra == 'test' + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/8a/67/f95b5460f127840310d2187f916cf0023b5875c0717fdf893f71e1325e87/plotly-6.5.2-py3-none-any.whl name: plotly version: 6.5.2 diff --git a/pyproject.toml b/pyproject.toml index 88d8b131..46ab2717 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ dependencies = [ 'ipywidgets', # Jupyter widgets 'jupyterlab', # Jupyter notebooks 'pixi-kernel', # Pixi Jupyter kernel + 'plopp', # Plotting library ] [project.optional-dependencies] diff --git a/src/easydynamics/experiment/__init__.py b/src/easydynamics/experiment/__init__.py new file mode 100644 index 00000000..6b3a8a44 --- /dev/null +++ b/src/easydynamics/experiment/__init__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2025-2026 EasyDynamics contributors +# SPDX-License-Identifier: BSD-3-Clause + +from .experiment import Experiment + +__all__ = [ + 'Experiment', +] diff --git a/src/easydynamics/experiment/experiment.py b/src/easydynamics/experiment/experiment.py new file mode 100644 index 00000000..b3df2a11 --- /dev/null +++ b/src/easydynamics/experiment/experiment.py @@ -0,0 +1,307 @@ +import os +import warnings +from typing import Optional + +import plopp as pp +import scipp as sc +from easyscience.base_classes.new_base import NewBase +from scipp.io import load_hdf5 as sc_load_hdf5 +from scipp.io import save_hdf5 as sc_save_hdf5 + + +class Experiment(NewBase): + """Holds data from an experiment as a sc.DataArray along with + metadata. + + This is a minimal implementation that will be extended in the + future. + """ + + def __init__( + self, + display_name: str = 'MyExperiment', + unique_name: str | None = None, + data: sc.DataArray | str | None = None, + ): + super().__init__( + display_name=display_name, + unique_name=unique_name, + ) + + if data is None: + self._data: Optional[sc.DataArray] = None + elif isinstance(data, str): + self.load_hdf5(filename=data) + elif isinstance(data, sc.DataArray): + self._validate_coordinates(data) + self._data = data + else: + raise TypeError( + f'Data must be a sc.DataArray or a filename string, not {type(data).__name__}' + ) + + self._binned_data = ( + self._convert_to_bin_centers(self._data) if self._data is not None else None + ) + + ########### + # Properties + ########### + + @property + def data(self) -> sc.DataArray | None: + """Get the dataset associated with this experiment.""" + return self._data + + @data.setter + def data(self, value: sc.DataArray): + """Set the dataset associated with this experiment.""" + if not isinstance(value, sc.DataArray): + raise TypeError(f'Data must be a sc.DataArray, not {type(value).__name__}') + self._validate_coordinates(value) + self._data = value + self._binned_data = ( + self._convert_to_bin_centers(self._data) if self._data is not None else None + ) + + @property + def binned_data(self) -> sc.DataArray | None: + """Get the binned dataset associated with this experiment.""" + return self._binned_data + + @binned_data.setter + def binned_data(self, value: sc.DataArray): + """Set the binned dataset associated with this experiment.""" + raise AttributeError('binned_data is a read-only property. Use rebin() to rebin the data') + + @property + def Q(self) -> sc.Variable | None: + """Get the Q values from the dataset.""" + if self._data is None: + warnings.warn('No data loaded.', UserWarning) + return None + return self._binned_data.coords['Q'] + + @Q.setter + def Q(self, value: sc.Variable): + """Set the Q values for the dataset.""" + raise AttributeError('Q is a read-only property derived from the data.') + + @property + def energy(self) -> sc.Variable: + """Get the energy values from the dataset.""" + if self._data is None: + warnings.warn('No data loaded.', UserWarning) + return None + return self._binned_data.coords['energy'] + + @energy.setter + def energy(self, value: sc.Variable): + """Set the energy values for the dataset.""" + raise AttributeError('energy is a read-only property derived from the data.') + + ########### + # Handle data + ########### + + def load_hdf5(self, filename: str, display_name: str | None = None): + """Load data from an HDF5 file. + + Args: + filename (str ): Path to the HDF5 file. + display_name (str | None): Optional display name for the + experiment. + """ + if not isinstance(filename, str): + raise TypeError(f'Filename must be a string, not {type(filename).__name__}') + + if display_name is not None: + if not isinstance(display_name, str): + raise TypeError( + f'Display name must be a string, not {type(display_name).__name__}' + ) + self.display_name = display_name + + loaded_data = sc_load_hdf5(filename) + if not isinstance(loaded_data, sc.DataArray): + raise TypeError( + f'Loaded data must be a sc.DataArray, not {type(loaded_data).__name__}' + ) + self._validate_coordinates(loaded_data) + self.data = loaded_data + + def save_hdf5(self, filename: str | None = None): + """Save the dataset to HDF5. + + Args: + filename (str | None): Path to the output HDF5 file. + """ + + if filename is None: + filename = f'{self.unique_name}.h5' + + if not isinstance(filename, str): + raise TypeError(f'Filename must be a string, not {type(filename).__name__}') + + if self._data is None: + raise ValueError('No data to save.') + + dir_name = os.path.dirname(filename) + if dir_name: + os.makedirs(dir_name, exist_ok=True) + + sc_save_hdf5(self._data, filename) + + def remove_data(self): + """Remove the dataset from the experiment.""" + self._data = None + self._binned_data = None + + def rebin(self, dimensions: dict[str, int | sc.Variable]) -> None: + """Rebin the dataset along specified dimensions. + + Args: + dimensions (dict[str, int | sc.Variable]): A dictionary + mapping dimension names to number of bins (int) or bin edges + (sc.Variable). + Raises: + TypeError: If dimensions is not a dictionary or if + keys/values are of incorrect types. KeyError: If a specified + dimension is not in the dataset. + """ + + if not isinstance(dimensions, dict): + raise TypeError( + 'dimensions must be a dictionary mapping dimension names ' + 'to number of bins or bin values as sc.Variable.' + ) + if self._data is None: + raise ValueError('No data to rebin. Please load data first.') + binned_data = self._data.copy() + dim_copy = dimensions.copy() + for dim, value in dim_copy.items(): + if not isinstance(dim, str): + raise TypeError( + f'Dimension keys must be strings. Got {type(dim)} for {dim} instead.' + ) + if dim not in self._data.dims: + raise KeyError( + f"Dimension '{dim}' not a valid dimension for rebinning. " + f'Should be one of {self._data.dims}.' + ) + if isinstance(value, float) and value.is_integer(): # I allow eg. 2.0 as well as 2 + value = int(value) + # This line can be removed when scipp resize support + # resizing with coordinates + dimensions[dim] = value + if not (isinstance(value, int) or isinstance(value, sc.Variable)): + raise TypeError( + f'Dimension values must be integers or sc.Variable. ' + f"Got {type(value)} for dimension '{dim}' instead." + ) + binned_data = binned_data.bin({dim: value}) + + binned_data = binned_data.bins.mean() + binned_data = self._convert_to_bin_centers(binned_data) + self._binned_data = binned_data + + ########### + # other methods + ########### + + def plot_data(self, slicer=False, **kwargs) -> None: + """Plot the dataset using plopp.""" + + if self._binned_data is None: + raise ValueError('No data to plot. Please load data first.') + + if not self._in_notebook(): + raise RuntimeError('plot_data() can only be used in a Jupyter notebook environment.') + + from IPython.display import display + + plot_kwargs_defaults = { + 'title': self.display_name, + } + # Overwrite defaults with any user-provided kwargs + plot_kwargs_defaults.update(kwargs) + if slicer: + fig = pp.slicer( + self._binned_data, + **plot_kwargs_defaults, + ) + else: + fig = pp.plot( + self._binned_data.transpose(dims=['energy', 'Q']), + **plot_kwargs_defaults, + ) + display(fig) + + ########### + # private methods + ########### + + @staticmethod + def _in_notebook() -> bool: + """Check if the code is running in a Jupyter notebook. + + Returns: + bool: True if in a Jupyter notebook, False otherwise. + """ + try: + from IPython import get_ipython + + shell = get_ipython().__class__.__name__ + if shell == 'ZMQInteractiveShell': + return True # Jupyter notebook or JupyterLab + elif shell == 'TerminalInteractiveShell': + return False # Terminal IPython + else: + return False + except (NameError, ImportError): + return False # Standard Python (no IPython) + + @staticmethod + def _validate_coordinates(data: sc.DataArray) -> None: + """Validate that required coordinates are present in the data. + + Raises: + ValueError: If required coordinates are missing. + """ + if not isinstance(data, sc.DataArray): + raise TypeError('Data must be a sc.DataArray.') + + required_coords = ['Q', 'energy'] + for coord in required_coords: + if coord not in data.coords: + raise ValueError(f"Data is missing required coordinate: '{coord}'") + + def _convert_to_bin_centers(self, data: sc.DataArray) -> sc.DataArray: + """Convert the coordinates of the data to bin centers. + + Args: + data (sc.DataArray): The data to check. + + Returns: + sc.DataArray: The data with coordinates at bin centers. + """ + for dim in data.dims: + coord = data.coords[dim] + if coord.ndim == 1 and coord.size == data.sizes[dim] + 1: + # Coordinate is at bin edges, convert to bin centers + data = data.assign_coords({dim: sc.midpoints(coord)}) + return data + + ######## + # dunder methods + ########### + + def __repr__(self) -> str: + return f'Experiment `{self.unique_name}` with data: {self._data}' + + def __copy__(self) -> 'Experiment': + """Return a copy of the object.""" + temp = self.to_dict(skip=['unique_name']) + new_obj = self.__class__.from_dict(temp) + new_obj.data = self.data.copy() if self.data is not None else None + return new_obj diff --git a/tests/conftest.py b/tests/conftest.py index 5a32716c..aefc6c0b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,13 +7,19 @@ import easyscience.global_object import pytest -from easyscience.global_object.map import Map +# from easyscience.global_object.map import Map -@pytest.fixture(autouse=True) -def reset_global_object(monkeypatch): - # Before each test - monkeypatch.setattr(easyscience.global_object, 'map', Map()) - yield - # After each test (cleanup) - monkeypatch.setattr(easyscience.global_object, 'map', Map()) + +# @pytest.fixture(autouse=True) +# def reset_global_object(monkeypatch): +# # Before each test +# monkeypatch.setattr(easyscience.global_object, 'map', Map()) +# yield +# # After each test (cleanup) +# monkeypatch.setattr(easyscience.global_object, 'map', Map()) + + +@pytest.fixture(autouse=False) +def reset_global_object(): + easyscience.global_object.map._clear() diff --git a/tests/unit/easydynamics/experiment/test_experiment.py b/tests/unit/easydynamics/experiment/test_experiment.py new file mode 100644 index 00000000..067a2017 --- /dev/null +++ b/tests/unit/easydynamics/experiment/test_experiment.py @@ -0,0 +1,474 @@ +from copy import copy +from unittest.mock import MagicMock +from unittest.mock import patch + +import numpy as np +import pytest +import scipp as sc + +from easydynamics.experiment import Experiment + + +class TestExperiment: + @pytest.fixture + def experiment(self): + Q = sc.linspace('Q', 0.5, 1.5, num=10, unit='1/Angstrom') + energy = sc.linspace('energy', -5, 5, num=11, unit='meV') + values = sc.array(dims=['Q', 'energy'], values=np.ones((10, 11))) + data = sc.DataArray(data=values, coords={'Q': Q, 'energy': energy}) + + experiment = Experiment(display_name='test_experiment', data=data) + return experiment + + ############## + # test init + ############## + + def test_init_array(self, experiment): + "Test initialization with a Scipp DataArray" + # WHEN THEN EXPECT + assert experiment.display_name == 'test_experiment' + assert isinstance(experiment._data, sc.DataArray) + assert 'Q' in experiment._data.dims + assert 'energy' in experiment._data.dims + assert experiment._data.sizes['Q'] == 10 + assert experiment._data.sizes['energy'] == 11 + assert sc.identical( + experiment._data.data, + sc.array(dims=['Q', 'energy'], values=np.ones((10, 11))), + ) + + def test_init_string(self, tmp_path): + "Test initialization with a filename string," + 'should load the file' + # WHEN + Q = sc.linspace('Q', 0.5, 1.5, num=10, unit='1/Angstrom') + energy = sc.linspace('energy', -5, 5, num=11, unit='meV') + values = sc.array(dims=['Q', 'energy'], values=np.ones((10, 11))) + data = sc.DataArray(data=values, coords={'Q': Q, 'energy': energy}) + + filename = tmp_path / 'test_experiment.h5' + sc.io.save_hdf5(data, filename) + + # THEN + experiment = Experiment(display_name='loaded_experiment', data=str(filename)) + + # EXPECT + assert experiment.display_name == 'loaded_experiment' + assert isinstance(experiment._data, sc.DataArray) + assert 'Q' in experiment._data.dims + assert 'energy' in experiment._data.dims + assert experiment._data.sizes['Q'] == 10 + assert experiment._data.sizes['energy'] == 11 + assert sc.identical( + experiment._data.data, + sc.array(dims=['Q', 'energy'], values=np.ones((10, 11))), + ) + + def test_init_no_data(self): + "Test initialization with no data" + # WHEN + experiment = Experiment(display_name='empty_experiment') + + # THEN EXPECT + assert experiment.display_name == 'empty_experiment' + assert experiment._data is None + + def test_init_invalid_data(self): + "Test initialization with invalid data type" + # WHEN / THEN EXPECT + with pytest.raises(TypeError): + Experiment(data=123) + + ############## + # test data manipulation + ############## + + def test_load_hdf5(self, tmp_path, experiment): + "Test loading data from an HDF5 file." + 'First use scipp to save data to a file, ' + 'then load it using the method.' + # WHEN + # First create a file to load from + filename = tmp_path / 'test.h5' + data_to_save = experiment.data + sc.io.save_hdf5(data_to_save, filename) + + # THEN + new_experiment = Experiment(display_name='new_experiment') + new_experiment.load_hdf5(str(filename), display_name='loaded_data') + loaded_data = new_experiment.data + + # EXPECT + assert sc.identical(data_to_save, loaded_data) + assert new_experiment.display_name == 'loaded_data' + + def test_load_hdf5_invalid_name_raises(self, experiment): + "Test loading data from an HDF5 file," + 'giving the Experiment an invalid name' + # WHEN / THEN EXPECT + with pytest.raises(TypeError): + experiment.load_hdf5('some_file.h5', display_name=123) + + def test_load_hdf5_invalid_filename_raises(self, experiment): + "Test loading data from an HDF5 file with an invalid filename" + # WHEN / THEN EXPECT + with pytest.raises(TypeError, match='must be a string'): + experiment.load_hdf5(123) + + def test_load_hdf5_invalid_file_raises(self, experiment): + "Test loading data from a non-existent HDF5 file" + # WHEN / THEN EXPECT + + with pytest.raises(OSError): + experiment.load_hdf5('non_existent_file.h5') + + def test_save_hdf5(self, tmp_path, experiment): + "Test saving data to an HDF5 file. Load the saved file" + 'using scipp and compare to the original data.' + # WHEN THEN + filename = tmp_path / 'saved_data.h5' + experiment.save_hdf5(str(filename)) + + # EXPECT + loaded_data = sc.io.load_hdf5(str(filename)) + original_data = experiment.data + assert sc.identical(original_data, loaded_data) + + def test_save_hdf5_default_filename(self, tmp_path, experiment, monkeypatch): + "Test saving data to an HDF5 file with default filename" + # WHEN + monkeypatch.chdir(tmp_path) + + # THEN + experiment.save_hdf5() + + # EXPECT + expected_filename = tmp_path / f'{experiment.unique_name}.h5' + loaded_data = sc.io.load_hdf5(str(expected_filename)) + original_data = experiment.data + assert sc.identical(original_data, loaded_data) + + def test_save_hdf5_no_data_raises(self): + "Test saving data to an HDF5 file when no data is present" + 'in the experiment' + # WHEN + experiment = Experiment() + + # THEN EXPECT + with pytest.raises(ValueError): + experiment.save_hdf5('should_fail.h5') + + def test_save_hdf5_invalid_filename_raises(self, experiment): + "Test saving data to an HDF5 file with an invalid filename" + # WHEN / THEN EXPECT + with pytest.raises(TypeError, match='must be a string'): + experiment.save_hdf5(123) + + def test_remove_data(self, experiment): + "Test removing data from the experiment" + # WHEN + experiment.remove_data() + + # THEN EXPECT + assert experiment._data is None + + @pytest.mark.parametrize( + 'new_Q_bins, new_energy_bins', + [ + ( + sc.linspace('Q', 0.5, 1.5, num=7, unit='1/Angstrom'), + sc.linspace('energy', -5, 5, num=8, unit='meV'), + ), + ( + 6, + 7, + ), + ( + 6.0, + 7.0, + ), + ( + sc.linspace('Q', 0.5, 1.5, num=7, unit='1/Angstrom'), + 7, + ), + ], + ids=['sc_bins', 'integers_bins', 'float_bins', 'mixed_bins'], + ) + def test_rebin(self, experiment, new_Q_bins, new_energy_bins): + "Test rebinning data in the experiment" + # WHEN + + # THEN + experiment.rebin({'Q': new_Q_bins, 'energy': new_energy_bins}) + + # EXPECT + rebinned_data = experiment.binned_data + assert rebinned_data.sizes['Q'] == 6 + assert rebinned_data.sizes['energy'] == 7 + + def test_rebin_no_data_raises(self): + "Test rebinning data when no data is present" + # WHEN + experiment = Experiment() + + # THEN EXPECT + with pytest.raises(ValueError): + experiment.rebin({'Q': 6, 'energy': 7}) + + def test_rebin_invalid_dimensions_raises(self, experiment): + "Test rebinning data with invalid dimensions" + # WHEN / THEN EXPECT + with pytest.raises(TypeError): + experiment.rebin('invalid_dimensions') + + def test_rebin_invalid_dimension_name_raises(self, experiment): + "Test rebinning data with invalid dimension name" + # WHEN / THEN EXPECT + with pytest.raises(TypeError, match='Dimension keys must be strings'): + experiment.rebin({123: 6, 'energy': 7}) + + def test_rebin_dimension_not_in_data_raises(self, experiment): + "Test rebinning data with a dimension not in the data" + # WHEN / THEN EXPECT + with pytest.raises(KeyError, match="Dimension 'time' not a valid"): + experiment.rebin({'time': 6, 'energy': 7}) + + def test_rebin_invalid_bin_values_raises(self, experiment): + "Test rebinning data with invalid bin values" + # WHEN / THEN EXPECT + with pytest.raises( + TypeError, + match='Dimension values must be integers or', + ): + experiment.rebin({'Q': [0.5, 1.0, 1.5], 'energy': 7}) + + ############## + # test setters and getters + ############## + + def test_data_setter_raises_type_error(self, experiment): + "Test setting data to an invalid type raises TypeError" + # WHEN THEN EXPECT + with pytest.raises(TypeError): + experiment.data = 123 + + def test_binned_data_setter_raises(self, experiment): + "Test that setting binned data raises AttributeError" + # WHEN THEN EXPECT + with pytest.raises(AttributeError): + experiment.binned_data = experiment.binned_data + + def test_energy_setter_raises(self, experiment): + "Test that setting energy data raises AttributeError" + # WHEN THEN EXPECT + with pytest.raises(AttributeError): + experiment.energy = experiment.energy + + def test_Q_setter_raises(self, experiment): + "Test that setting Q data raises AttributeError" + # WHEN THEN EXPECT + with pytest.raises(AttributeError): + experiment.Q = experiment.Q + + def test_Q_getter_warns_no_data(self): + "Test that getting Q data with no data raises Warning" + # WHEN + experiment = Experiment() + + # THEN EXPECT + with pytest.warns(UserWarning, match='No data loaded'): + _ = experiment.Q + + def test_energy_getter_warns_no_data(self): + "Test that getting energy data with no data raises Warning" + # WHEN + experiment = Experiment() + + # THEN EXPECT + with pytest.warns(UserWarning, match='No data loaded'): + _ = experiment.energy + + ############## + # test plotting + ############## + + def test_plot_data_success(self, experiment): + "Test plotting data successfully when in notebook environment" + # WHEN + with ( + patch.object(Experiment, '_in_notebook', return_value=True), + patch('plopp.plot') as mock_plot, + patch('IPython.display.display') as mock_display, + ): + mock_fig = MagicMock() + mock_plot.return_value = mock_fig + + # THEN + experiment.plot_data() + + # EXPECT + mock_plot.assert_called_once() + args, kwargs = mock_plot.call_args + assert sc.identical(args[0], experiment._data.transpose()) + assert kwargs['title'] == f'{experiment.display_name}' + mock_display.assert_called_once_with(mock_fig) + + def test_plot_data_no_data_raises(self): + "Test plotting data raises ValueError when no data is present" + # WHEN + experiment = Experiment() + + # THEN EXPECT + with pytest.raises(ValueError, match='No data to plot'): + experiment.plot_data() + + def test_plot_data_not_in_notebook_raises(self, experiment): + "Test plotting data raises RuntimeError" + 'when not in notebook environment' + # WHEN + with patch.object(Experiment, '_in_notebook', return_value=False): + # THEN EXPECT + with pytest.raises( + RuntimeError, + match='plot_data\\(\\) can only be used in a Jupyter notebook environment', + ): + experiment.plot_data() + + ############## + # test private methods + ############## + + def test_in_notebook_returns_true_for_jupyter(self, monkeypatch): + """Should return True when IPython shell is + ZMQInteractiveShell (Jupyter).""" + + # WHEN + class ZMQInteractiveShell: + __name__ = 'ZMQInteractiveShell' + + # THEN + monkeypatch.setattr('IPython.get_ipython', lambda: ZMQInteractiveShell()) + + # EXPECT + assert Experiment._in_notebook() is True + + def test_in_notebook_returns_false_for_terminal_ipython(self, monkeypatch): + """Should return False when IPython shell is + TerminalInteractiveShell.""" + + # WHEN + class TerminalInteractiveShell: + __name__ = 'TerminalInteractiveShell' + + # THEN + + monkeypatch.setattr('IPython.get_ipython', lambda: TerminalInteractiveShell()) + + # EXPECT + assert Experiment._in_notebook() is False + + def test_in_notebook_returns_false_for_unknown_shell(self, monkeypatch): + """Should return False when IPython shell type is + unrecognized.""" + + # WHEN + class UnknownShell: + __name__ = 'UnknownShell' + + # THEN + monkeypatch.setattr('IPython.get_ipython', lambda: UnknownShell()) + # EXPECT + assert Experiment._in_notebook() is False + + def test_in_notebook_returns_false_when_no_ipython(self, monkeypatch): + """Should return False when IPython is not installed or + available.""" + + # WHEN + def raise_import_error(*args, **kwargs): + raise ImportError + + # THEN + monkeypatch.setattr('builtins.__import__', raise_import_error) + + # EXPECT + assert Experiment._in_notebook() is False + + def test_validate_coordinates(self, experiment): + "Test that _validate_coordinates does not raise for valid data" + # WHEN / THEN EXPECT + experiment._validate_coordinates(experiment._data) + + def test_validate_coordinates_raises_missing_Q(self, experiment): + "Test that _validate_coordinates raises ValueError when Q coord" + 'is missing' + # WHEN + invalid_data = experiment._data.copy() + invalid_data.coords.pop('Q') + + # THEN EXPECT + with pytest.raises(ValueError, match='missing required coordinate'): + experiment._validate_coordinates(invalid_data) + + def test_validate_coordinates_raises_missing_energy(self, experiment): + "Test that _validate_coordinates raises ValueError when energy" + 'coord is missing' + # WHEN + invalid_data = experiment._data.copy() + invalid_data.coords.pop('energy') + + # THEN EXPECT + with pytest.raises(ValueError, match='missing required coordinate'): + experiment._validate_coordinates(invalid_data) + + def test_validate_coordinates_raises_not_DataArray(self): + "Test that _validate_coordinates raises TypeError when data is" + 'not a Scipp DataArray' + # WHEN THEN EXPECT + with pytest.raises(TypeError, match='must be a'): + Experiment()._validate_coordinates('not_a_data_array') + + def test_convert_to_bin_centers(self, experiment): + "Test that _convert_to_bin_centers converts edges to centers" + # WHEN + Q_edges = sc.linspace('Q', 0.0, 2.0, num=11, unit='1/Angstrom') + energy_edges = sc.linspace('energy', -6, 6, num=13, unit='meV') + values = sc.array(dims=['Q', 'energy'], values=np.ones((10, 12))) + binned_data = sc.DataArray(data=values, coords={'Q': Q_edges, 'energy': energy_edges}) + + # THEN + experiment._data = binned_data # Set data to avoid warnings + converted_data = experiment._convert_to_bin_centers(binned_data) + + # EXPECT + expected_Q = 0.5 * (Q_edges[:-1] + Q_edges[1:]) + expected_energy = 0.5 * (energy_edges[:-1] + energy_edges[1:]) + + assert sc.identical(converted_data.coords['Q'], expected_Q) + assert sc.identical(converted_data.coords['energy'], expected_energy) + assert sc.identical(converted_data.data, binned_data.data) + + ############## + # test dunder methods + ############## + + def test_repr(self, experiment): + # WHEN + repr_str = repr(experiment) + + # THEN EXPECT + assert repr_str == f'Experiment `{experiment.unique_name}` with data: {experiment._data}' + + def test_copy_experiment(self, experiment): + "Test copying an Experiment object." + 'The copied object should have the same attributes ' + 'but be a different object in memory.' + # WHEN + copied_experiment = copy(experiment) + + # THEN EXPECT + assert copied_experiment.display_name == experiment.display_name + assert sc.identical(copied_experiment.data, experiment.data) + assert copied_experiment is not experiment + assert copied_experiment.data is not experiment.data diff --git a/tests/unit/easydynamics/sample_model/test_model_base.py b/tests/unit/easydynamics/sample_model/test_model_base.py index 44541e12..31feb66a 100644 --- a/tests/unit/easydynamics/sample_model/test_model_base.py +++ b/tests/unit/easydynamics/sample_model/test_model_base.py @@ -14,7 +14,7 @@ class TestModelBase: @pytest.fixture - def model_base(self): + def model_base(self, reset_global_object): component1 = Gaussian( display_name='TestGaussian1', area=1.0, diff --git a/tests/unit/easydynamics/sample_model/test_resolution_model.py b/tests/unit/easydynamics/sample_model/test_resolution_model.py index d45eee19..120cbf9b 100644 --- a/tests/unit/easydynamics/sample_model/test_resolution_model.py +++ b/tests/unit/easydynamics/sample_model/test_resolution_model.py @@ -89,7 +89,7 @@ def test_init_raises_with_invalid_components(self, invalid_component, expected_e collection.append_component(invalid_component) ResolutionModel(components=collection) - def test_append_and_remove_and_clear_component(self, resolution_model): + def test_append_and_remove_and_clear_component(self, resolution_model, reset_global_object): # WHEN new_component = Gaussian(unique_name='NewGaussian') @@ -136,7 +136,9 @@ def test_append_component_collection(self, resolution_model): ], ids=['DeltaFunction', 'Polynomial'], ) - def test_append_invalid_component_type_raises(self, resolution_model, invalid_component): + def test_append_invalid_component_type_raises( + self, resolution_model, invalid_component, reset_global_object + ): # WHEN / THEN / EXPECT # appending a single component with pytest.raises(