From 1b91c9dbd16dbfffb6684ab3555e660746858a22 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Thu, 18 Feb 2021 19:35:07 -0500 Subject: [PATCH 01/21] method for creating mpcontribs cards. --- .../simulator/tests/test_simulator.py | 3 +- src/mrsimulator/spin_system/site.py | 7 ++ .../spin_system/tests/test_site.py | 2 +- .../spin_system/tests/test_spin_systems.py | 2 +- src/mrsimulator/utils/contribs.py | 95 +++++++++++++++++++ src/mrsimulator/utils/tests/test_contribs.py | 90 ++++++++++++++++++ 6 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 src/mrsimulator/utils/contribs.py create mode 100644 src/mrsimulator/utils/tests/test_contribs.py diff --git a/src/mrsimulator/simulator/tests/test_simulator.py b/src/mrsimulator/simulator/tests/test_simulator.py index f7b9083b6..efb408719 100644 --- a/src/mrsimulator/simulator/tests/test_simulator.py +++ b/src/mrsimulator/simulator/tests/test_simulator.py @@ -217,6 +217,7 @@ def test_sites_to_pandas_df(): zeta = [59.8, 52.1, 69.4, 12.4] eta_n = [0.62, 0.68, 0.6, 0.5] Cq = [None, None, None, 5.3e6] + Cq_test = [None, None, None, 5.3] eta_q = [None, None, None, 0.34] spin_systems = single_site_system_generator( @@ -242,7 +243,7 @@ def test_sites_to_pandas_df(): i if i is not None else None for i in eta_n ] assert list(pd_o["quadrupolar.Cq"]) == [ - f"{i} Hz" if i is not None else None for i in Cq + f"{i} MHz" if i is not None else None for i in Cq_test ] # assert list(pd_o["quadrupolar.eta"]) == [ # i if i is not None else None for i in eta_q diff --git a/src/mrsimulator/spin_system/site.py b/src/mrsimulator/spin_system/site.py index c34195add..164c38a25 100644 --- a/src/mrsimulator/spin_system/site.py +++ b/src/mrsimulator/spin_system/site.py @@ -227,6 +227,13 @@ def parse_dict_with_units(cls, py_dict): return super().parse_dict_with_units(py_dict) + def json(self): + py_dict = super().json() + if "quadrupolar" in py_dict: + value = float(py_dict["quadrupolar"]["Cq"][:-2]) / 1e6 + py_dict["quadrupolar"]["Cq"] = f"{value} MHz" + return py_dict + def to_freq_dict(self, B0): """ Serialize the Site object to a JSON compliant python dictionary object, where diff --git a/src/mrsimulator/spin_system/tests/test_site.py b/src/mrsimulator/spin_system/tests/test_site.py index faa4f944a..77479fb01 100644 --- a/src/mrsimulator/spin_system/tests/test_site.py +++ b/src/mrsimulator/spin_system/tests/test_site.py @@ -167,7 +167,7 @@ def test_site_object_methods(): "alpha": "0.1 rad", "beta": "2.5 rad", }, - "quadrupolar": {"Cq": "10000000.0 Hz", "eta": 0.6}, + "quadrupolar": {"Cq": "10.0 MHz", "eta": 0.6}, } the_site = Site( isotope="27Al", diff --git a/src/mrsimulator/spin_system/tests/test_spin_systems.py b/src/mrsimulator/spin_system/tests/test_spin_systems.py index 117065af7..be07a2948 100644 --- a/src/mrsimulator/spin_system/tests/test_spin_systems.py +++ b/src/mrsimulator/spin_system/tests/test_spin_systems.py @@ -105,7 +105,7 @@ def test_direct_init_spin_system(): { "isotope": "17O", "isotropic_chemical_shift": "-10.0 ppm", - "quadrupolar": {"Cq": "5100000.0 Hz", "eta": 0.5}, + "quadrupolar": {"Cq": "5.1 MHz", "eta": 0.5}, }, ], "abundance": "4.23 %", diff --git a/src/mrsimulator/utils/contribs.py b/src/mrsimulator/utils/contribs.py new file mode 100644 index 000000000..4a1543140 --- /dev/null +++ b/src/mrsimulator/utils/contribs.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +import numpy as np + +from . import flatten_dict + +__author__ = "Deepansh Srivastava" +__email__ = "srivastava.89@osu.edu" + + +SITE_KEYWORDS = { + "isotropic_chemical_shift": "Isotropic", + "shielding_symmetric.zeta": "zeta", + "shielding_symmetric.eta": "eta", + "quadrupolar.Cq": "Cq", + "quadrupolar.eta": "eta", +} + + +def parse_sites(site): + site_ = flatten_dict(site.json()) + dict_ = {"ChemicalShift": {}, "Quadrupolar": {}} + for k, v in site_.items(): + new_key = SITE_KEYWORDS[k] if k in SITE_KEYWORDS else k + if "shielding_symmetric" in k: + dict_["ChemicalShift"][new_key] = ( + f"-{site_[k]}" if "zeta" in k else site_[k] + ) + elif "quadrupolar" in k: + dict_["Quadrupolar"][new_key] = site_[k] + elif "isotropic_chemical_shift" in k: + dict_["ChemicalShift"][new_key] = v + else: + dict_[new_key] = v + + for item in ["ChemicalShift", "Quadrupolar"]: + if dict_[item] == {}: + dict_.pop(item) + return dict_ + + +def parse_method(method): + gamma = method.channels[0].gyromagnetic_ratio + B0 = method.spectral_dimensions[0].events[0].magnetic_flux_density + larmor_frequency = abs(gamma * B0) # MHz + nu_r = method.spectral_dimensions[0].events[0].rotor_frequency # Hz + nu_delta = method.spectral_dimensions[0].spectral_width # Hz + rotor_angle = method.spectral_dimensions[0].events[0].rotor_angle * 180 / np.pi + + return { + "LarmorFrequency": f"{larmor_frequency} MHz", + "SpinningFrequency": f"{nu_r} Hz", + "SpectralWidth": f"{nu_delta} Hz", + "RotorAngle": f"{rotor_angle:.4f} degree", + } + + +def contribs_data(sim, project, chemical_formula=None, identifier=None, exp_dict={}): + """Generate mpcontribs cards for every site in the Simulator object. + + Arguments + --------- + sim: Simulator object from where the sites are extracted. + project: mpcontribs project name. + chemical_formula: Chemical formula for the sample (optional). + identifier: The mp-id of the sample (optional). + exp_dict: Additional metadata to use in contribs card. + + Example + ------- + + >>> contribution_data = contribs_data(sim, 'myproject') # doctest:+SKIP + """ + contrib = [] + for sys in sim.spin_systems: + for site in sys.sites: + data = { + "experiment": "experiment goes here", + "simulation": "simulation goes here", + "site": {**parse_sites(site)}, + "method": {**parse_method(sim.methods[0]), **exp_dict}, + } + + card = { + "data": data, + "project": project, + "formula": chemical_formula, + "identifier": identifier, + } + if chemical_formula is None: + card.pop("formula") + if identifier is None: + card.pop("identifier") + contrib.append(card) + + return contrib diff --git a/src/mrsimulator/utils/tests/test_contribs.py b/src/mrsimulator/utils/tests/test_contribs.py new file mode 100644 index 000000000..98abebf2c --- /dev/null +++ b/src/mrsimulator/utils/tests/test_contribs.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +from mrsimulator import Simulator +from mrsimulator import Site +from mrsimulator import SpinSystem +from mrsimulator.methods import BlochDecaySpectrum +from mrsimulator.utils.contribs import contribs_data +from mrsimulator.utils.contribs import parse_method +from mrsimulator.utils.contribs import parse_sites + + +def test01(): + site = Site(isotope="1H", shielding_symmetric={"zeta": 10, "eta": 0.1}) + method = BlochDecaySpectrum( + channels=["1H"], magnetic_flux_density=9.4, rotor_frequency="15000" + ) + + output_site = parse_sites(site) + output_method = parse_method(method) + + assert output_site == { + "isotope": "1H", + "ChemicalShift": {"Isotropic": "0 ppm", "zeta": "-10.0 ppm", "eta": 0.1}, + } + + omega_0 = abs(method.channels[0].gyromagnetic_ratio * 9.4) + assert output_method == { + "LarmorFrequency": f"{omega_0} MHz", + "SpinningFrequency": "15000.0 Hz", + "SpectralWidth": "25000.0 Hz", + "RotorAngle": "54.7356 degree", + } + + +def test02(): + site = Site(isotope="27Al", quadrupolar={"Cq": 10e6, "eta": 0.4}) + method = BlochDecaySpectrum(channels=["27Al"], magnetic_flux_density=11.7) + + output_site = parse_sites(site) + output_method = parse_method(method) + + assert output_site == { + "isotope": "27Al", + "ChemicalShift": {"Isotropic": "0 ppm"}, + "Quadrupolar": {"Cq": "10.0 MHz", "eta": 0.4}, + } + + omega_0 = abs(method.channels[0].gyromagnetic_ratio * 11.7) + assert output_method == { + "LarmorFrequency": f"{omega_0} MHz", + "SpinningFrequency": "0.0 Hz", + "SpectralWidth": "25000.0 Hz", + "RotorAngle": "54.7356 degree", + } + + +def test_contrib_card(): + site = Site(isotope="27Al", quadrupolar={"Cq": 10e6, "eta": 0.4}) + method = BlochDecaySpectrum(channels=["27Al"], magnetic_flux_density=11.7) + + sim = Simulator() + sim.spin_systems = [SpinSystem(sites=[site])] + sim.methods = [method] + + omega_0 = abs(method.channels[0].gyromagnetic_ratio * 11.7) + card = { + "data": { + "experiment": "experiment goes here", + "simulation": "simulation goes here", + "site": { + "isotope": "27Al", + "ChemicalShift": {"Isotropic": "0 ppm"}, + "Quadrupolar": {"Cq": "10.0 MHz", "eta": 0.4}, + }, + "method": { + "LarmorFrequency": f"{omega_0} MHz", + "SpinningFrequency": "0.0 Hz", + "SpectralWidth": "25000.0 Hz", + "RotorAngle": "54.7356 degree", + }, + }, + "project": "test", + } + + output = contribs_data(sim, "test") + assert output == [card] + + sim.spin_systems = [SpinSystem(sites=[site, site, site])] + output = contribs_data(sim, "test", identifier="mp-5733") + card["identifier"] = "mp-5733" + assert output == [card, card, card] From 9af4bbe58263ce4c44cc64ef772cb7115614fe85 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Fri, 26 Feb 2021 13:23:18 -0500 Subject: [PATCH 02/21] fixed test error. --- src/mrsimulator/utils/tests/test_contribs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mrsimulator/utils/tests/test_contribs.py b/src/mrsimulator/utils/tests/test_contribs.py index 98abebf2c..5d29e031c 100644 --- a/src/mrsimulator/utils/tests/test_contribs.py +++ b/src/mrsimulator/utils/tests/test_contribs.py @@ -19,7 +19,7 @@ def test01(): assert output_site == { "isotope": "1H", - "ChemicalShift": {"Isotropic": "0 ppm", "zeta": "-10.0 ppm", "eta": 0.1}, + "ChemicalShift": {"Isotropic": "0.0 ppm", "zeta": "-10.0 ppm", "eta": 0.1}, } omega_0 = abs(method.channels[0].gyromagnetic_ratio * 9.4) @@ -40,7 +40,7 @@ def test02(): assert output_site == { "isotope": "27Al", - "ChemicalShift": {"Isotropic": "0 ppm"}, + "ChemicalShift": {"Isotropic": "0.0 ppm"}, "Quadrupolar": {"Cq": "10.0 MHz", "eta": 0.4}, } @@ -68,7 +68,7 @@ def test_contrib_card(): "simulation": "simulation goes here", "site": { "isotope": "27Al", - "ChemicalShift": {"Isotropic": "0 ppm"}, + "ChemicalShift": {"Isotropic": "0.0 ppm"}, "Quadrupolar": {"Cq": "10.0 MHz", "eta": 0.4}, }, "method": { From 89f909e84292eb57cd164d9e1f32c07f9699f9d9 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Fri, 26 Feb 2021 16:32:31 -0500 Subject: [PATCH 03/21] fixed sign issue from shielding to shift. --- src/mrsimulator/utils/contribs.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mrsimulator/utils/contribs.py b/src/mrsimulator/utils/contribs.py index 4a1543140..e2eb2faf5 100644 --- a/src/mrsimulator/utils/contribs.py +++ b/src/mrsimulator/utils/contribs.py @@ -17,14 +17,14 @@ def parse_sites(site): + if site.shielding_symmetric is not None: + site.shielding_symmetric.zeta *= -1 site_ = flatten_dict(site.json()) dict_ = {"ChemicalShift": {}, "Quadrupolar": {}} for k, v in site_.items(): new_key = SITE_KEYWORDS[k] if k in SITE_KEYWORDS else k if "shielding_symmetric" in k: - dict_["ChemicalShift"][new_key] = ( - f"-{site_[k]}" if "zeta" in k else site_[k] - ) + dict_["ChemicalShift"][new_key] = site_[k] elif "quadrupolar" in k: dict_["Quadrupolar"][new_key] = site_[k] elif "isotropic_chemical_shift" in k: @@ -54,14 +54,14 @@ def parse_method(method): } -def contribs_data(sim, project, chemical_formula=None, identifier=None, exp_dict={}): +def contribs_data(sim, project, composition=None, identifier=None, exp_dict={}): """Generate mpcontribs cards for every site in the Simulator object. Arguments --------- sim: Simulator object from where the sites are extracted. project: mpcontribs project name. - chemical_formula: Chemical formula for the sample (optional). + composition: Chemical composition for the sample (optional). identifier: The mp-id of the sample (optional). exp_dict: Additional metadata to use in contribs card. @@ -83,11 +83,11 @@ def contribs_data(sim, project, chemical_formula=None, identifier=None, exp_dict card = { "data": data, "project": project, - "formula": chemical_formula, + "composition": composition, "identifier": identifier, } - if chemical_formula is None: - card.pop("formula") + if composition is None: + card.pop("composition") if identifier is None: card.pop("identifier") contrib.append(card) From 98291b79442020b41af030e84210d620cc05e685 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Fri, 5 Mar 2021 11:38:42 -0500 Subject: [PATCH 04/21] . --- .../contribs.py => contribs/__init__.py} | 44 ++++++++++++++++--- .../tests/test_contribs.py | 6 +-- 2 files changed, 42 insertions(+), 8 deletions(-) rename src/mrsimulator/{utils/contribs.py => contribs/__init__.py} (72%) rename src/mrsimulator/{utils => contribs}/tests/test_contribs.py (94%) diff --git a/src/mrsimulator/utils/contribs.py b/src/mrsimulator/contribs/__init__.py similarity index 72% rename from src/mrsimulator/utils/contribs.py rename to src/mrsimulator/contribs/__init__.py index e2eb2faf5..c71c3a13d 100644 --- a/src/mrsimulator/utils/contribs.py +++ b/src/mrsimulator/contribs/__init__.py @@ -1,12 +1,44 @@ # -*- coding: utf-8 -*- import numpy as np +from mrsimulator.utils import flatten_dict -from . import flatten_dict +# import csdmpy as cp +# from pydantic import BaseModel __author__ = "Deepansh Srivastava" __email__ = "srivastava.89@osu.edu" +# class MethodInfo(BaseModel): +# LarmorFrequency: str +# SpinningFrequency: str +# SpectralWidth: str +# RotorAngle: str + +# class ChemicalShiftInfo(BaseModel): +# Isotropic: str +# zeta: str = None +# eta: float = None + +# class QuadrupolarInfo(BaseModel): +# Cq: str +# eta: float + +# class SiteInfo(BaseModel): +# ChemicalShift: ChemicalShiftInfo = None +# Quadrupolar: QuadrupolarInfo = None + +# class SimulatorContrib(BaseModel): +# experiment: str = None +# simulation: str = None +# site: SiteInfo = None +# method: MethodInfo = None + +# class config: +# validate_assignment = True +# arbitrary_types_allowed = True + + SITE_KEYWORDS = { "isotropic_chemical_shift": "Isotropic", "shielding_symmetric.zeta": "zeta", @@ -74,10 +106,12 @@ def contribs_data(sim, project, composition=None, identifier=None, exp_dict={}): for sys in sim.spin_systems: for site in sys.sites: data = { - "experiment": "experiment goes here", - "simulation": "simulation goes here", - "site": {**parse_sites(site)}, - "method": {**parse_method(sim.methods[0]), **exp_dict}, + **dict( + experiment="experiment goes here", + simulation="simulation goes here", + site={**parse_sites(site)}, + method={**parse_method(sim.methods[0]), **exp_dict}, + ) } card = { diff --git a/src/mrsimulator/utils/tests/test_contribs.py b/src/mrsimulator/contribs/tests/test_contribs.py similarity index 94% rename from src/mrsimulator/utils/tests/test_contribs.py rename to src/mrsimulator/contribs/tests/test_contribs.py index 5d29e031c..0326ebd58 100644 --- a/src/mrsimulator/utils/tests/test_contribs.py +++ b/src/mrsimulator/contribs/tests/test_contribs.py @@ -2,10 +2,10 @@ from mrsimulator import Simulator from mrsimulator import Site from mrsimulator import SpinSystem +from mrsimulator.contribs import contribs_data +from mrsimulator.contribs import parse_method +from mrsimulator.contribs import parse_sites from mrsimulator.methods import BlochDecaySpectrum -from mrsimulator.utils.contribs import contribs_data -from mrsimulator.utils.contribs import parse_method -from mrsimulator.utils.contribs import parse_sites def test01(): From 2e9e8f63fa65d503eb2fc880b2280d4187c1bfe8 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Wed, 10 Mar 2021 15:10:18 -0500 Subject: [PATCH 05/21] add example show how to generate mpcontrib cards. --- .../fitting/1D_fitting/plot_2_xonotlite.ipynb | 212 ++++++++++++++++++ .../1D_fitting/plot_4_11B_Quad_NMR.ipynb | 2 +- fitting_source/1D_fitting/plot_2_xonotlite.py | 195 ++++++++++++++++ .../1D_fitting/plot_4_11B_Quad_NMR.py | 4 +- src/mrsimulator/contribs/__init__.py | 4 +- .../contribs/tests/test_contribs.py | 6 +- src/mrsimulator/utils/spectral_fitting.py | 12 +- 7 files changed, 425 insertions(+), 10 deletions(-) create mode 100644 docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb create mode 100644 fitting_source/1D_fitting/plot_2_xonotlite.py diff --git a/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb b/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb new file mode 100644 index 000000000..c00147626 --- /dev/null +++ b/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb @@ -0,0 +1,212 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# This cell is added by sphinx-gallery\n\n%matplotlib inline\n\nimport mrsimulator\nprint(f'You are using mrsimulator v{mrsimulator.__version__}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# 29Si 1D MAS spinning sideband (Xonotlite)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following is a spinning sideband fitting example for $^{29}\\text{Si}$ 1D\nMAS NMR spectrum of Xonotlite crystal, acquired by by Hansen et al. [#f1]_\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import csdmpy as cp\nimport matplotlib as mpl\nimport matplotlib.pyplot as plt\nimport mrsimulator.signal_processing as sp\nimport mrsimulator.signal_processing.apodization as apo\nfrom mrsimulator import Simulator, SpinSystem, Site\nfrom mrsimulator.methods import BlochDecaySpectrum\nfrom lmfit import Minimizer, fit_report\nfrom mrsimulator.utils import get_spectral_dimensions\nfrom mrsimulator.utils.spectral_fitting import LMFIT_min_function, make_LMFIT_params\n\nfont = {\"size\": 9}\nmpl.rc(\"font\", **font)\nmpl.rcParams[\"figure.figsize\"] = [4.5, 3.0]\nmpl.rcParams[\"grid.linestyle\"] = \"--\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import the dataset\nUse the `csdmpy `_\nmodule to load the synthetic dataset as a CSDM object.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "filename = \"xonotlite.csdf\"\nexp_data = cp.load(filename).real\n\n# standard deviation of noise from the dataset\nsigma = 2.819601\n\n# convert the dimension coordinates from Hz to ppm\nexp_data.dimensions[0].to(\"ppm\", \"nmr_frequency_ratio\")\n\n# Normalize the spectrum\nmax_amp = exp_data.max()\nexp_data /= max_amp\nsigma /= max_amp\n\n# Plot of the synthetic dataset.\nax = plt.subplot(projection=\"csdm\")\nax.plot(exp_data, \"k\", alpha=0.5)\nax.invert_xaxis()\nplt.tight_layout()\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create a fitting model\n**Guess model**\n\nCreate a guess list of spin systems. There are three crystallographic\n$^{29}\\text{Si}$ sites in Xonotlite.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "s1 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-97.17, # in ppm,\n shielding_symmetric={\"zeta\": 33, \"eta\": 0.01}, # zeta in ppm\n)\ns2 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-86.3, # in ppm,\n shielding_symmetric={\"zeta\": 50.0, \"eta\": 0.5}, # zeta in ppm\n)\ns3 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-87.2, # in ppm,\n shielding_symmetric={\"zeta\": 44.0, \"eta\": 0.5}, # zeta in ppm\n)\nspin_systems = [\n SpinSystem(name=\"Q3\", sites=[s1], abundance=25),\n SpinSystem(name=\"Q2 (1)\", sites=[s2], abundance=75 / 2),\n SpinSystem(name=\"Q2 (2)\", sites=[s3], abundance=75 / 2),\n]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Method**\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Get the spectral dimension paramters from the experiment.\nspectral_dims = get_spectral_dimensions(exp_data)\n\nmethod = BlochDecaySpectrum(\n channels=[\"29Si\"],\n magnetic_flux_density=14.1, # in T\n rotor_frequency=1800.0, # in Hz\n spectral_dimensions=spectral_dims,\n experiment=exp_data, # add the measurement to the method.\n)\n\n# Optimize the script by pre-setting the transition pathways for each spin system from\n# the das method.\nfor sys in spin_systems:\n sys.transition_pathways = method.get_transition_pathways(sys)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Guess Spectrum**\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Simulation\n# ----------\nsim = Simulator()\nsim.spin_systems = spin_systems\nsim.methods = [method]\nsim.run()\n\n# Post Simulation Processing\n# --------------------------\nprocessor = sp.SignalProcessor(\n operations=[\n sp.IFFT(), # inverse FFT to convert frequency based spectrum to time domain.\n apo.Exponential(FWHM=\"50 Hz\"), # apodization of time domain signal.\n sp.FFT(), # forward FFT to convert time domain signal to frequency spectrum.\n sp.Scale(factor=0.6), # scale the frequency spectrum.\n ]\n)\nprocessed_data = processor.apply_operations(data=sim.methods[0].simulation).real\n\n# Plot of the guess Spectrum\n# --------------------------\nax = plt.subplot(projection=\"csdm\")\nax.plot(exp_data, \"k\", linewidth=1, label=\"Experiment\")\nax.plot(processed_data, \"r\", alpha=0.5, linewidth=2.5, label=\"guess spectrum\")\nax.invert_xaxis()\nplt.legend()\nplt.grid()\nplt.tight_layout()\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Least-squares minimization with LMFIT\nUse the :func:`~mrsimulator.utils.spectral_fitting.make_LMFIT_params` for a quick\nsetup of the fitting parameters.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "params = make_LMFIT_params(sim, processor)\n\nparams.pop(\"sys_0_abundance\")\nparams.pop(\"sys_1_abundance\")\nparams.pop(\"sys_2_abundance\")\nparams[\"sys_0_site_0_shielding_symmetric_eta\"].vary = False\nprint(params.pretty_print(columns=[\"value\", \"min\", \"max\", \"vary\", \"expr\"]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Solve the minimizer using LMFIT**\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "minner = Minimizer(LMFIT_min_function, params, fcn_args=(sim, processor, sigma))\nresult = minner.minimize()\nprint(fit_report(result))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The best fit solution\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sim.run()\nprocessed_data = processor.apply_operations(data=sim.methods[0].simulation).real\n\n# Plot the spectrum\nax = plt.subplot(projection=\"csdm\")\nplt.plot(exp_data, \"k\", linewidth=1, label=\"Experiment\")\nplt.plot(processed_data, \"r\", alpha=0.5, linewidth=2.5, label=\"Best Fit\")\nplt.xlabel(\"$^{17}$O frequency / ppm\")\nplt.legend()\nplt.grid()\nplt.tight_layout()\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mpcontribs export\n\nExport the site data as Mpcontribs card.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from mrsimulator.contribs import mpcontribs_export\nfrom pprint import pprint\n\ncards = mpcontribs_export(\n sim,\n project=\"lsdi_nmr_exp_test\",\n identifier=\"Ca6Si6O17(OH)2\",\n exp_dict={\n \"90degreePulseLength\": \"6 \u00b5s\",\n \"relaxationDelay\": \"8 s\",\n \"numberOfScans\": 7224,\n \"referenceCompound\": \"TMS\",\n },\n)\npprint(cards[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ".. [#f1] Hansen, M. R., Jakobsen, H. J., Skibsted, J., $^{29}\\text{Si}$\n Chemical Shift Anisotropies in Calcium Silicates from High-Field\n $^{29}\\text{Si}$ MAS NMR Spectroscopy, Inorg. Chem. 2003,\n **42**, *7*, 2368-2377.\n `DOI: 10.1021/ic020647f `_\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/docs/notebooks/fitting/1D_fitting/plot_4_11B_Quad_NMR.ipynb b/docs/notebooks/fitting/1D_fitting/plot_4_11B_Quad_NMR.ipynb index ae2980f52..e52d9a30e 100644 --- a/docs/notebooks/fitting/1D_fitting/plot_4_11B_Quad_NMR.ipynb +++ b/docs/notebooks/fitting/1D_fitting/plot_4_11B_Quad_NMR.ipynb @@ -22,7 +22,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The following is a quadrupolar lineshape fitting example for the 11B MAS NMR of\nlithium orthoborate crystal. The dataset was provided by Nathan Barrow.\n\n" + "The following is a quadrupolar lineshape fitting example for $^{11}\\text{B}$\nMAS NMR of lithium orthoborate crystal. The dataset was provided by Nathan Barrow.\n\n" ] }, { diff --git a/fitting_source/1D_fitting/plot_2_xonotlite.py b/fitting_source/1D_fitting/plot_2_xonotlite.py new file mode 100644 index 000000000..ee56c1b99 --- /dev/null +++ b/fitting_source/1D_fitting/plot_2_xonotlite.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +29Si 1D MAS spinning sideband (Xonotlite) +========================================= +""" +# %% +# The following is a spinning sideband fitting example for :math:`^{29}\text{Si}` 1D +# MAS NMR spectrum of Xonotlite crystal, acquired by by Hansen et al. [#f1]_ +import csdmpy as cp +import matplotlib as mpl +import matplotlib.pyplot as plt +import mrsimulator.signal_processing as sp +import mrsimulator.signal_processing.apodization as apo +from mrsimulator import Simulator, SpinSystem, Site +from mrsimulator.methods import BlochDecaySpectrum +from lmfit import Minimizer, fit_report +from mrsimulator.utils import get_spectral_dimensions +from mrsimulator.utils.spectral_fitting import LMFIT_min_function, make_LMFIT_params + +font = {"size": 9} +mpl.rc("font", **font) +mpl.rcParams["figure.figsize"] = [4.5, 3.0] +mpl.rcParams["grid.linestyle"] = "--" +# sphinx_gallery_thumbnail_number = 3 + +# %% +# Import the dataset +# ------------------ +# Use the `csdmpy `_ +# module to load the synthetic dataset as a CSDM object. +filename = "xonotlite.csdf" +exp_data = cp.load(filename).real + +# standard deviation of noise from the dataset +sigma = 2.819601 + +# convert the dimension coordinates from Hz to ppm +exp_data.dimensions[0].to("ppm", "nmr_frequency_ratio") + +# Normalize the spectrum +max_amp = exp_data.max() +exp_data /= max_amp +sigma /= max_amp + +# Plot of the synthetic dataset. +ax = plt.subplot(projection="csdm") +ax.plot(exp_data, "k", alpha=0.5) +ax.invert_xaxis() +plt.tight_layout() +plt.show() + +# %% +# Create a fitting model +# ---------------------- +# **Guess model** +# +# Create a guess list of spin systems. There are three crystallographic +# :math:`^{29}\text{Si}` sites in Xonotlite. +s1 = Site( + isotope="29Si", + isotropic_chemical_shift=-97.17, # in ppm, + shielding_symmetric={"zeta": 33, "eta": 0.01}, # zeta in ppm +) +s2 = Site( + isotope="29Si", + isotropic_chemical_shift=-86.3, # in ppm, + shielding_symmetric={"zeta": 50.0, "eta": 0.5}, # zeta in ppm +) +s3 = Site( + isotope="29Si", + isotropic_chemical_shift=-87.2, # in ppm, + shielding_symmetric={"zeta": 44.0, "eta": 0.5}, # zeta in ppm +) +spin_systems = [ + SpinSystem(name="Q3", sites=[s1], abundance=25), + SpinSystem(name="Q2 (1)", sites=[s2], abundance=75 / 2), + SpinSystem(name="Q2 (2)", sites=[s3], abundance=75 / 2), +] + +# %% +# **Method** + +# Get the spectral dimension paramters from the experiment. +spectral_dims = get_spectral_dimensions(exp_data) + +method = BlochDecaySpectrum( + channels=["29Si"], + magnetic_flux_density=14.1, # in T + rotor_frequency=1800.0, # in Hz + spectral_dimensions=spectral_dims, + experiment=exp_data, # add the measurement to the method. +) + +# Optimize the script by pre-setting the transition pathways for each spin system from +# the das method. +for sys in spin_systems: + sys.transition_pathways = method.get_transition_pathways(sys) + +# %% +# **Guess Spectrum** + +# Simulation +# ---------- +sim = Simulator() +sim.spin_systems = spin_systems +sim.methods = [method] +sim.run() + +# Post Simulation Processing +# -------------------------- +processor = sp.SignalProcessor( + operations=[ + sp.IFFT(), # inverse FFT to convert frequency based spectrum to time domain. + apo.Exponential(FWHM="50 Hz"), # apodization of time domain signal. + sp.FFT(), # forward FFT to convert time domain signal to frequency spectrum. + sp.Scale(factor=0.6), # scale the frequency spectrum. + ] +) +processed_data = processor.apply_operations(data=sim.methods[0].simulation).real + +# Plot of the guess Spectrum +# -------------------------- +ax = plt.subplot(projection="csdm") +ax.plot(exp_data, "k", linewidth=1, label="Experiment") +ax.plot(processed_data, "r", alpha=0.5, linewidth=2.5, label="guess spectrum") +ax.invert_xaxis() +plt.legend() +plt.grid() +plt.tight_layout() +plt.show() + +# %% +# Least-squares minimization with LMFIT +# ------------------------------------- +# Use the :func:`~mrsimulator.utils.spectral_fitting.make_LMFIT_params` for a quick +# setup of the fitting parameters. +params = make_LMFIT_params(sim, processor) + +params.pop("sys_0_abundance") +params.pop("sys_1_abundance") +params.pop("sys_2_abundance") +params["sys_0_site_0_shielding_symmetric_eta"].vary = False +print(params.pretty_print(columns=["value", "min", "max", "vary", "expr"])) + +# %% +# **Solve the minimizer using LMFIT** +minner = Minimizer(LMFIT_min_function, params, fcn_args=(sim, processor, sigma)) +result = minner.minimize() +print(fit_report(result)) + +# %% +# The best fit solution +# --------------------- +sim.run() +processed_data = processor.apply_operations(data=sim.methods[0].simulation).real + +# Plot the spectrum +ax = plt.subplot(projection="csdm") +plt.plot(exp_data, "k", linewidth=1, label="Experiment") +plt.plot(processed_data, "r", alpha=0.5, linewidth=2.5, label="Best Fit") +plt.xlabel("$^{17}$O frequency / ppm") +plt.legend() +plt.grid() +plt.tight_layout() +plt.show() + + +# %% +# Mpcontribs export +# ----------------- +# +# Export the site data as Mpcontribs card. +from mrsimulator.contribs import mpcontribs_export +from pprint import pprint + +cards = mpcontribs_export( + sim, + project="lsdi_nmr_exp_test", + identifier="Ca6Si6O17(OH)2", + exp_dict={ + "90degreePulseLength": "6 µs", + "relaxationDelay": "8 s", + "numberOfScans": 7224, + "referenceCompound": "TMS", + }, +) +pprint(cards[0]) + +# %% +# .. [#f1] Hansen, M. R., Jakobsen, H. J., Skibsted, J., :math:`^{29}\text{Si}` +# Chemical Shift Anisotropies in Calcium Silicates from High-Field +# :math:`^{29}\text{Si}` MAS NMR Spectroscopy, Inorg. Chem. 2003, +# **42**, *7*, 2368-2377. +# `DOI: 10.1021/ic020647f `_ diff --git a/fitting_source/1D_fitting/plot_4_11B_Quad_NMR.py b/fitting_source/1D_fitting/plot_4_11B_Quad_NMR.py index 81795ed9e..105c73df5 100644 --- a/fitting_source/1D_fitting/plot_4_11B_Quad_NMR.py +++ b/fitting_source/1D_fitting/plot_4_11B_Quad_NMR.py @@ -5,8 +5,8 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ """ # %% -# The following is a quadrupolar lineshape fitting example for the 11B MAS NMR of -# lithium orthoborate crystal. The dataset was provided by Nathan Barrow. +# The following is a quadrupolar lineshape fitting example for :math:`^{11}\text{B}` +# MAS NMR of lithium orthoborate crystal. The dataset was provided by Nathan Barrow. import numpy as np import csdmpy as cp import matplotlib as mpl diff --git a/src/mrsimulator/contribs/__init__.py b/src/mrsimulator/contribs/__init__.py index c71c3a13d..3c4a7b8d7 100644 --- a/src/mrsimulator/contribs/__init__.py +++ b/src/mrsimulator/contribs/__init__.py @@ -86,7 +86,7 @@ def parse_method(method): } -def contribs_data(sim, project, composition=None, identifier=None, exp_dict={}): +def mpcontribs_export(sim, project, composition=None, identifier=None, exp_dict={}): """Generate mpcontribs cards for every site in the Simulator object. Arguments @@ -100,7 +100,7 @@ def contribs_data(sim, project, composition=None, identifier=None, exp_dict={}): Example ------- - >>> contribution_data = contribs_data(sim, 'myproject') # doctest:+SKIP + >>> contribution_data = mpcontribs_export(sim, 'myproject') # doctest:+SKIP """ contrib = [] for sys in sim.spin_systems: diff --git a/src/mrsimulator/contribs/tests/test_contribs.py b/src/mrsimulator/contribs/tests/test_contribs.py index 0326ebd58..bf699fdd8 100644 --- a/src/mrsimulator/contribs/tests/test_contribs.py +++ b/src/mrsimulator/contribs/tests/test_contribs.py @@ -2,7 +2,7 @@ from mrsimulator import Simulator from mrsimulator import Site from mrsimulator import SpinSystem -from mrsimulator.contribs import contribs_data +from mrsimulator.contribs import mpcontribs_export from mrsimulator.contribs import parse_method from mrsimulator.contribs import parse_sites from mrsimulator.methods import BlochDecaySpectrum @@ -81,10 +81,10 @@ def test_contrib_card(): "project": "test", } - output = contribs_data(sim, "test") + output = mpcontribs_export(sim, "test") assert output == [card] sim.spin_systems = [SpinSystem(sites=[site, site, site])] - output = contribs_data(sim, "test", identifier="mp-5733") + output = mpcontribs_export(sim, "test", identifier="mp-5733") card["identifier"] = "mp-5733" assert output == [card, card, card] diff --git a/src/mrsimulator/utils/spectral_fitting.py b/src/mrsimulator/utils/spectral_fitting.py index 1d4bbd27e..11f5ddce2 100644 --- a/src/mrsimulator/utils/spectral_fitting.py +++ b/src/mrsimulator/utils/spectral_fitting.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- import mrsimulator.signal_processing as sp +import numpy as np from lmfit import Parameters from mrsimulator import Simulator - __author__ = ["Maxwell C Venetos", "Deepansh Srivastava"] __email__ = ["maxvenetos@gmail.com", "srivastava.89@osu.edu"] @@ -341,7 +341,15 @@ def LMFIT_min_function(params, sim, post_sim=None, sigma=1): else: datum = processed_data.y[0].components[0].real - diff = sim.methods[0].experiment.y[0].components[0] - datum + exp_data = sim.methods[0].experiment + exp_data_y = exp_data.y[0].components[0] + + # If data has negative increment, reverse the data before taking the difference. + index = [-i - 1 for i, item in enumerate(exp_data.x) if item.increment.value < 0] + exp_data_y = exp_data_y if index == [] else np.flip(exp_data_y, axis=tuple(index)) + + diff = exp_data_y - datum + return diff / sigma # MULTIPLE EXPERIMENTS From 232df0e0fdc7c13bcec5966d751605b98cac81a4 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Wed, 10 Mar 2021 15:18:25 -0500 Subject: [PATCH 06/21] . --- docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb | 6 +++--- fitting_source/1D_fitting/plot_2_xonotlite.py | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb b/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb index c00147626..772037cb1 100644 --- a/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb +++ b/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb @@ -40,7 +40,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Import the dataset\nUse the `csdmpy `_\nmodule to load the synthetic dataset as a CSDM object.\n\n" + "## Import the dataset\n\n" ] }, { @@ -51,7 +51,7 @@ }, "outputs": [], "source": [ - "filename = \"xonotlite.csdf\"\nexp_data = cp.load(filename).real\n\n# standard deviation of noise from the dataset\nsigma = 2.819601\n\n# convert the dimension coordinates from Hz to ppm\nexp_data.dimensions[0].to(\"ppm\", \"nmr_frequency_ratio\")\n\n# Normalize the spectrum\nmax_amp = exp_data.max()\nexp_data /= max_amp\nsigma /= max_amp\n\n# Plot of the synthetic dataset.\nax = plt.subplot(projection=\"csdm\")\nax.plot(exp_data, \"k\", alpha=0.5)\nax.invert_xaxis()\nplt.tight_layout()\nplt.show()" + "filename = \"https://sandbox.zenodo.org/record/744498/files/xonotlite.csdf\"\nexp_data = cp.load(filename).real\n\n# standard deviation of noise from the dataset\nsigma = 2.819601\n\n# convert the dimension coordinates from Hz to ppm\nexp_data.dimensions[0].to(\"ppm\", \"nmr_frequency_ratio\")\n\n# Normalize the spectrum\nmax_amp = exp_data.max()\nexp_data /= max_amp\nsigma /= max_amp\n\n# Plot of the synthetic dataset.\nax = plt.subplot(projection=\"csdm\")\nax.plot(exp_data, \"k\", alpha=0.5)\nax.invert_xaxis()\nplt.tight_layout()\nplt.show()" ] }, { @@ -69,7 +69,7 @@ }, "outputs": [], "source": [ - "s1 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-97.17, # in ppm,\n shielding_symmetric={\"zeta\": 33, \"eta\": 0.01}, # zeta in ppm\n)\ns2 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-86.3, # in ppm,\n shielding_symmetric={\"zeta\": 50.0, \"eta\": 0.5}, # zeta in ppm\n)\ns3 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-87.2, # in ppm,\n shielding_symmetric={\"zeta\": 44.0, \"eta\": 0.5}, # zeta in ppm\n)\nspin_systems = [\n SpinSystem(name=\"Q3\", sites=[s1], abundance=25),\n SpinSystem(name=\"Q2 (1)\", sites=[s2], abundance=75 / 2),\n SpinSystem(name=\"Q2 (2)\", sites=[s3], abundance=75 / 2),\n]" + "s1 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-97.17, # in ppm,\n shielding_symmetric={\"zeta\": 35.0, \"eta\": 0.0}, # zeta in ppm\n)\ns2 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-86.3, # in ppm,\n shielding_symmetric={\"zeta\": 50.0, \"eta\": 0.5}, # zeta in ppm\n)\ns3 = Site(\n isotope=\"29Si\",\n isotropic_chemical_shift=-87.2, # in ppm,\n shielding_symmetric={\"zeta\": 44.0, \"eta\": 0.5}, # zeta in ppm\n)\nspin_systems = [\n SpinSystem(name=\"Q3\", sites=[s1], abundance=25),\n SpinSystem(name=\"Q2 (1)\", sites=[s2], abundance=75 / 2),\n SpinSystem(name=\"Q2 (2)\", sites=[s3], abundance=75 / 2),\n]" ] }, { diff --git a/fitting_source/1D_fitting/plot_2_xonotlite.py b/fitting_source/1D_fitting/plot_2_xonotlite.py index ee56c1b99..35ecffe00 100644 --- a/fitting_source/1D_fitting/plot_2_xonotlite.py +++ b/fitting_source/1D_fitting/plot_2_xonotlite.py @@ -27,9 +27,7 @@ # %% # Import the dataset # ------------------ -# Use the `csdmpy `_ -# module to load the synthetic dataset as a CSDM object. -filename = "xonotlite.csdf" +filename = "https://sandbox.zenodo.org/record/744498/files/xonotlite.csdf" exp_data = cp.load(filename).real # standard deviation of noise from the dataset @@ -60,7 +58,7 @@ s1 = Site( isotope="29Si", isotropic_chemical_shift=-97.17, # in ppm, - shielding_symmetric={"zeta": 33, "eta": 0.01}, # zeta in ppm + shielding_symmetric={"zeta": 35.0, "eta": 0.0}, # zeta in ppm ) s2 = Site( isotope="29Si", From a6bfa7c70c76568744de032f65053fb237498948 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Fri, 12 Mar 2021 11:38:58 -0500 Subject: [PATCH 07/21] Added Classes for contribs code - ChemicalShiftInfo - QuadrupolarInfo - SiteInfo - MethodInfo - SimulatorInfo - ContribInfo - Base --- src/mrsimulator/contribs/__init__.py | 94 ++++++------------- src/mrsimulator/contribs/base.py | 73 ++++++++++++++ .../contribs/tests/test_contribs.py | 40 ++++---- 3 files changed, 125 insertions(+), 82 deletions(-) create mode 100644 src/mrsimulator/contribs/base.py diff --git a/src/mrsimulator/contribs/__init__.py b/src/mrsimulator/contribs/__init__.py index 3c4a7b8d7..1ff90381c 100644 --- a/src/mrsimulator/contribs/__init__.py +++ b/src/mrsimulator/contribs/__init__.py @@ -2,45 +2,14 @@ import numpy as np from mrsimulator.utils import flatten_dict -# import csdmpy as cp -# from pydantic import BaseModel +from .base import ContribInfo __author__ = "Deepansh Srivastava" __email__ = "srivastava.89@osu.edu" -# class MethodInfo(BaseModel): -# LarmorFrequency: str -# SpinningFrequency: str -# SpectralWidth: str -# RotorAngle: str - -# class ChemicalShiftInfo(BaseModel): -# Isotropic: str -# zeta: str = None -# eta: float = None - -# class QuadrupolarInfo(BaseModel): -# Cq: str -# eta: float - -# class SiteInfo(BaseModel): -# ChemicalShift: ChemicalShiftInfo = None -# Quadrupolar: QuadrupolarInfo = None - -# class SimulatorContrib(BaseModel): -# experiment: str = None -# simulation: str = None -# site: SiteInfo = None -# method: MethodInfo = None - -# class config: -# validate_assignment = True -# arbitrary_types_allowed = True - - SITE_KEYWORDS = { - "isotropic_chemical_shift": "Isotropic", + "isotropic_chemical_shift": "isotropic", "shielding_symmetric.zeta": "zeta", "shielding_symmetric.eta": "eta", "quadrupolar.Cq": "Cq", @@ -74,19 +43,19 @@ def parse_method(method): gamma = method.channels[0].gyromagnetic_ratio B0 = method.spectral_dimensions[0].events[0].magnetic_flux_density larmor_frequency = abs(gamma * B0) # MHz - nu_r = method.spectral_dimensions[0].events[0].rotor_frequency # Hz - nu_delta = method.spectral_dimensions[0].spectral_width # Hz + rotor_frequency = method.spectral_dimensions[0].events[0].rotor_frequency # Hz + spectral_width = method.spectral_dimensions[0].spectral_width # Hz rotor_angle = method.spectral_dimensions[0].events[0].rotor_angle * 180 / np.pi return { - "LarmorFrequency": f"{larmor_frequency} MHz", - "SpinningFrequency": f"{nu_r} Hz", - "SpectralWidth": f"{nu_delta} Hz", - "RotorAngle": f"{rotor_angle:.4f} degree", + "larmorFrequency": f"{larmor_frequency} MHz", + "spinningFrequency": f"{rotor_frequency} Hz", + "spectralWidth": f"{spectral_width} Hz", + "rotorAngle": f"{rotor_angle:.4f} degree", } -def mpcontribs_export(sim, project, composition=None, identifier=None, exp_dict={}): +def mpcontribs_export(sim, project, identifier, composition=None, exp_dict={}): """Generate mpcontribs cards for every site in the Simulator object. Arguments @@ -102,28 +71,23 @@ def mpcontribs_export(sim, project, composition=None, identifier=None, exp_dict= >>> contribution_data = mpcontribs_export(sim, 'myproject') # doctest:+SKIP """ - contrib = [] - for sys in sim.spin_systems: - for site in sys.sites: - data = { - **dict( - experiment="experiment goes here", - simulation="simulation goes here", - site={**parse_sites(site)}, - method={**parse_method(sim.methods[0]), **exp_dict}, - ) - } - - card = { - "data": data, - "project": project, - "composition": composition, - "identifier": identifier, - } - if composition is None: - card.pop("composition") - if identifier is None: - card.pop("identifier") - contrib.append(card) - - return contrib + contribs = [ + ContribInfo( + data={ + "experiment": "experiment goes here", + "simulation": "simulation goes here", + "site": {**parse_sites(site)}, + "method": {**parse_method(sim.methods[0])}, + }, + project=project, + composition=composition, + identifier=identifier, + ).json() + for sys in sim.spin_systems + for site in sys.sites + ] + + for item in contribs: + item["data"]["method"] = {**item["data"]["method"], **exp_dict} + + return contribs diff --git a/src/mrsimulator/contribs/base.py b/src/mrsimulator/contribs/base.py new file mode 100644 index 000000000..9261d7931 --- /dev/null +++ b/src/mrsimulator/contribs/base.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# import csdmpy as cp +from pydantic import BaseModel + +__author__ = "Deepansh Srivastava" +__email__ = "srivastava.89@osu.edu" + + +class Base(BaseModel): + def json(self): + return Base.fullsimplify(super().dict()) + + @staticmethod + def simplify(val): + """Remove value if it is None.""" + return {k: v for k, v in val.items() if v is not None} + + @staticmethod + def fullsimplify(val): + """Iteratively remove None values from a nested dict.""" + initial = { + k: Base.simplify(Base.fullsimplify(v)) if isinstance(v, dict) else v + for k, v in val.items() + } + return Base.simplify(initial) + + +class ChemicalShiftInfo(Base): + isotropic: str + zeta: str = None + eta: float = None + alpha: str = None + beta: str = None + gamma: str = None + + +class QuadrupolarInfo(Base): + Cq: str + eta: float + alpha: str = None + beta: str = None + gamma: str = None + + +class SiteInfo(Base): + isotope: str + ChemicalShift: ChemicalShiftInfo = None + Quadrupolar: QuadrupolarInfo = None + + +class MethodInfo(Base): + larmorFrequency: str + spinningFrequency: str + spectralWidth: str + rotorAngle: str + + +class SimulatorInfo(Base): + experiment: str = None + simulation: str = None + site: SiteInfo = None + method: MethodInfo = None + + class config: + validate_assignment = True + arbitrary_types_allowed = True + + +class ContribInfo(Base): + data: SimulatorInfo + project: str + identifier: str + composition: str = None diff --git a/src/mrsimulator/contribs/tests/test_contribs.py b/src/mrsimulator/contribs/tests/test_contribs.py index bf699fdd8..c5d8bf3ff 100644 --- a/src/mrsimulator/contribs/tests/test_contribs.py +++ b/src/mrsimulator/contribs/tests/test_contribs.py @@ -19,15 +19,15 @@ def test01(): assert output_site == { "isotope": "1H", - "ChemicalShift": {"Isotropic": "0.0 ppm", "zeta": "-10.0 ppm", "eta": 0.1}, + "ChemicalShift": {"isotropic": "0.0 ppm", "zeta": "-10.0 ppm", "eta": 0.1}, } omega_0 = abs(method.channels[0].gyromagnetic_ratio * 9.4) assert output_method == { - "LarmorFrequency": f"{omega_0} MHz", - "SpinningFrequency": "15000.0 Hz", - "SpectralWidth": "25000.0 Hz", - "RotorAngle": "54.7356 degree", + "larmorFrequency": f"{omega_0} MHz", + "spinningFrequency": "15000.0 Hz", + "spectralWidth": "25000.0 Hz", + "rotorAngle": "54.7356 degree", } @@ -40,16 +40,16 @@ def test02(): assert output_site == { "isotope": "27Al", - "ChemicalShift": {"Isotropic": "0.0 ppm"}, + "ChemicalShift": {"isotropic": "0.0 ppm"}, "Quadrupolar": {"Cq": "10.0 MHz", "eta": 0.4}, } omega_0 = abs(method.channels[0].gyromagnetic_ratio * 11.7) assert output_method == { - "LarmorFrequency": f"{omega_0} MHz", - "SpinningFrequency": "0.0 Hz", - "SpectralWidth": "25000.0 Hz", - "RotorAngle": "54.7356 degree", + "larmorFrequency": f"{omega_0} MHz", + "spinningFrequency": "0.0 Hz", + "spectralWidth": "25000.0 Hz", + "rotorAngle": "54.7356 degree", } @@ -68,23 +68,29 @@ def test_contrib_card(): "simulation": "simulation goes here", "site": { "isotope": "27Al", - "ChemicalShift": {"Isotropic": "0.0 ppm"}, + "ChemicalShift": {"isotropic": "0.0 ppm"}, "Quadrupolar": {"Cq": "10.0 MHz", "eta": 0.4}, }, "method": { - "LarmorFrequency": f"{omega_0} MHz", - "SpinningFrequency": "0.0 Hz", - "SpectralWidth": "25000.0 Hz", - "RotorAngle": "54.7356 degree", + "larmorFrequency": f"{omega_0} MHz", + "spinningFrequency": "0.0 Hz", + "spectralWidth": "25000.0 Hz", + "rotorAngle": "54.7356 degree", + "blah": "blah", }, }, "project": "test", + "identifier": "blah-blah", } - output = mpcontribs_export(sim, "test") + output = mpcontribs_export( + sim, project="test", identifier="blah-blah", exp_dict={"blah": "blah"} + ) assert output == [card] sim.spin_systems = [SpinSystem(sites=[site, site, site])] - output = mpcontribs_export(sim, "test", identifier="mp-5733") + output = mpcontribs_export( + sim, project="test", identifier="mp-5733", exp_dict={"blah": "blah"} + ) card["identifier"] = "mp-5733" assert output == [card, card, card] From 8efc65a69f93b9aa3489dda975cc131ce48698d3 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Fri, 12 Mar 2021 12:03:29 -0500 Subject: [PATCH 08/21] Added keywords from mpcontribs ContributionsSchema to the ContribSchema class. --- src/mrsimulator/contribs/__init__.py | 25 ++++++++--------- src/mrsimulator/contribs/base.py | 28 +++++++++++-------- .../contribs/tests/test_contribs.py | 13 +++++++-- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/mrsimulator/contribs/__init__.py b/src/mrsimulator/contribs/__init__.py index 1ff90381c..89c8b7615 100644 --- a/src/mrsimulator/contribs/__init__.py +++ b/src/mrsimulator/contribs/__init__.py @@ -2,7 +2,7 @@ import numpy as np from mrsimulator.utils import flatten_dict -from .base import ContribInfo +from .base import ContribSchema __author__ = "Deepansh Srivastava" __email__ = "srivastava.89@osu.edu" @@ -55,33 +55,32 @@ def parse_method(method): } -def mpcontribs_export(sim, project, identifier, composition=None, exp_dict={}): - """Generate mpcontribs cards for every site in the Simulator object. +def mpcontribs_export(sim, project, identifier, exp_dict={}, **kwargs): + """Generate mpcontribs contribution entries for every site in the Simulator object. Arguments --------- - sim: Simulator object from where the sites are extracted. - project: mpcontribs project name. - composition: Chemical composition for the sample (optional). - identifier: The mp-id of the sample (optional). - exp_dict: Additional metadata to use in contribs card. + sim: Simulator object from where the site contributions are extracted. + str project: mpcontribs project name (reqiuired). + str identifier: mpcontribs identifier (required). + exp_dict: Additional metadata to use in contribution. + **kwargs: Optional keyword arguments from mpcontribs ContributionsSchema Example ------- - >>> contribution_data = mpcontribs_export(sim, 'myproject') # doctest:+SKIP """ contribs = [ - ContribInfo( + ContribSchema( + project=project, + identifier=identifier, data={ "experiment": "experiment goes here", "simulation": "simulation goes here", "site": {**parse_sites(site)}, "method": {**parse_method(sim.methods[0])}, }, - project=project, - composition=composition, - identifier=identifier, + **kwargs, ).json() for sys in sim.spin_systems for site in sys.sites diff --git a/src/mrsimulator/contribs/base.py b/src/mrsimulator/contribs/base.py index 9261d7931..110c5a9a9 100644 --- a/src/mrsimulator/contribs/base.py +++ b/src/mrsimulator/contribs/base.py @@ -25,7 +25,7 @@ def fullsimplify(val): return Base.simplify(initial) -class ChemicalShiftInfo(Base): +class ChemicalShiftSchema(Base): isotropic: str zeta: str = None eta: float = None @@ -34,7 +34,7 @@ class ChemicalShiftInfo(Base): gamma: str = None -class QuadrupolarInfo(Base): +class QuadrupolarSchema(Base): Cq: str eta: float alpha: str = None @@ -42,32 +42,36 @@ class QuadrupolarInfo(Base): gamma: str = None -class SiteInfo(Base): +class SiteSchema(Base): isotope: str - ChemicalShift: ChemicalShiftInfo = None - Quadrupolar: QuadrupolarInfo = None + ChemicalShift: ChemicalShiftSchema = None + Quadrupolar: QuadrupolarSchema = None -class MethodInfo(Base): +class MethodSchema(Base): larmorFrequency: str spinningFrequency: str spectralWidth: str rotorAngle: str -class SimulatorInfo(Base): +class SimulatorSchema(Base): experiment: str = None simulation: str = None - site: SiteInfo = None - method: MethodInfo = None + site: SiteSchema = None + method: MethodSchema = None class config: validate_assignment = True arbitrary_types_allowed = True -class ContribInfo(Base): - data: SimulatorInfo +class ContribSchema(Base): project: str identifier: str - composition: str = None + formula: str = None + is_public: bool = None + data: SimulatorSchema + structures: list = None + tables: list = None + notebook: dict = None diff --git a/src/mrsimulator/contribs/tests/test_contribs.py b/src/mrsimulator/contribs/tests/test_contribs.py index c5d8bf3ff..1c21d72fa 100644 --- a/src/mrsimulator/contribs/tests/test_contribs.py +++ b/src/mrsimulator/contribs/tests/test_contribs.py @@ -80,17 +80,26 @@ def test_contrib_card(): }, }, "project": "test", + "formula": "ABX", "identifier": "blah-blah", } output = mpcontribs_export( - sim, project="test", identifier="blah-blah", exp_dict={"blah": "blah"} + sim, + project="test", + identifier="blah-blah", + exp_dict={"blah": "blah"}, + formula="ABX", ) assert output == [card] sim.spin_systems = [SpinSystem(sites=[site, site, site])] output = mpcontribs_export( - sim, project="test", identifier="mp-5733", exp_dict={"blah": "blah"} + sim, + project="test", + identifier="mp-5733", + exp_dict={"blah": "blah"}, + formula="ABX", ) card["identifier"] = "mp-5733" assert output == [card, card, card] From 111be04a9cae63605036b4cb6fb668113b77514f Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Sat, 1 May 2021 02:31:35 -0400 Subject: [PATCH 09/21] add dict() to mrsimulator.__init__ file. include attachments to ContribSchema. --- src/mrsimulator/__init__.py | 19 ++++++++++++------- src/mrsimulator/contribs/__init__.py | 4 ++-- src/mrsimulator/contribs/base.py | 6 ++++-- .../contribs/tests/test_contribs.py | 2 -- src/mrsimulator/spin_system/__init__.py | 2 +- src/mrsimulator/transition/base.py | 5 ++--- src/mrsimulator/transition/pathway.py | 6 ++++-- src/mrsimulator/utils/parseable.py | 5 ++++- 8 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/mrsimulator/__init__.py b/src/mrsimulator/__init__.py index 643ef644a..bf6c3350d 100644 --- a/src/mrsimulator/__init__.py +++ b/src/mrsimulator/__init__.py @@ -54,15 +54,9 @@ def save( fit_report=None, with_units: bool = True, ): - sim = simulator.json(True, True) if with_units else simulator.reduced_dict() - if signal_processors is not None: - sim["signal_processors"] = [item.json() for item in signal_processors] - - sim["params"] = None if fit_report is None else fit_report.params.dumps() - with open(filename, "w", encoding="utf8") as outfile: json.dump( - sim, + dict(simulator, signal_processors, fit_report, with_units), outfile, ensure_ascii=False, sort_keys=False, @@ -76,6 +70,17 @@ def load(filename: str, parse_units: bool = True): return parse(val, parse_units) +def dict( + simulator, signal_processors: list = None, fit_report=None, with_units: bool = True +): + data = simulator.json(True, True) if with_units else simulator.reduced_dict() + if signal_processors is not None: + data["signal_processors"] = [item.json() for item in signal_processors] + + data["params"] = None if fit_report is None else fit_report.params.dumps() + return data + + def parse(py_dict, parse_units: bool = True): sim = Simulator.parse(py_dict, parse_units) diff --git a/src/mrsimulator/contribs/__init__.py b/src/mrsimulator/contribs/__init__.py index 89c8b7615..6d56bab3f 100644 --- a/src/mrsimulator/contribs/__init__.py +++ b/src/mrsimulator/contribs/__init__.py @@ -61,14 +61,14 @@ def mpcontribs_export(sim, project, identifier, exp_dict={}, **kwargs): Arguments --------- sim: Simulator object from where the site contributions are extracted. - str project: mpcontribs project name (reqiuired). + str project: mpcontribs project name (required). str identifier: mpcontribs identifier (required). exp_dict: Additional metadata to use in contribution. **kwargs: Optional keyword arguments from mpcontribs ContributionsSchema Example ------- - >>> contribution_data = mpcontribs_export(sim, 'myproject') # doctest:+SKIP + >>> contribution_data = mpcontribs_export(sim, 'my_project') # doctest:+SKIP """ contribs = [ ContribSchema( diff --git a/src/mrsimulator/contribs/base.py b/src/mrsimulator/contribs/base.py index 110c5a9a9..b08b5a3a5 100644 --- a/src/mrsimulator/contribs/base.py +++ b/src/mrsimulator/contribs/base.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- # import csdmpy as cp +from typing import Any +from typing import List + from pydantic import BaseModel __author__ = "Deepansh Srivastava" @@ -56,8 +59,6 @@ class MethodSchema(Base): class SimulatorSchema(Base): - experiment: str = None - simulation: str = None site: SiteSchema = None method: MethodSchema = None @@ -75,3 +76,4 @@ class ContribSchema(Base): structures: list = None tables: list = None notebook: dict = None + attachments: List[Any] = None diff --git a/src/mrsimulator/contribs/tests/test_contribs.py b/src/mrsimulator/contribs/tests/test_contribs.py index 1c21d72fa..dc5f96db8 100644 --- a/src/mrsimulator/contribs/tests/test_contribs.py +++ b/src/mrsimulator/contribs/tests/test_contribs.py @@ -64,8 +64,6 @@ def test_contrib_card(): omega_0 = abs(method.channels[0].gyromagnetic_ratio * 11.7) card = { "data": { - "experiment": "experiment goes here", - "simulation": "simulation goes here", "site": { "isotope": "27Al", "ChemicalShift": {"isotropic": "0.0 ppm"}, diff --git a/src/mrsimulator/spin_system/__init__.py b/src/mrsimulator/spin_system/__init__.py index 2bca8ec19..8a4995a3d 100644 --- a/src/mrsimulator/spin_system/__init__.py +++ b/src/mrsimulator/spin_system/__init__.py @@ -155,7 +155,7 @@ class SpinSystem(Parseable): sites: Union[List[Site], np.ndarray] = [] couplings: Union[List[Coupling], np.ndarray] = None abundance: float = Field(default=100.0, ge=0.0, le=100.0) - transition_pathways: List = None + transition_pathways: List[Union[TransitionPathway, List]] = None property_unit_types: ClassVar = {"abundance": "dimensionless"} property_default_units: ClassVar = {"abundance": "pct"} diff --git a/src/mrsimulator/transition/base.py b/src/mrsimulator/transition/base.py index 1602bbd41..b8d1056ab 100644 --- a/src/mrsimulator/transition/base.py +++ b/src/mrsimulator/transition/base.py @@ -10,9 +10,8 @@ class Transition(BaseModel): - r""" - Base Transition class describes a spin transition between two energy states, where - the energy states are described using the weakly coupled basis. + r"""Base Transition class describes a spin transition between two energy states, + where the energy states are described using the weakly coupled basis. .. math:: |m_{i,0}, m_{i,1}, ... m_{i,N} \rangle \rightarrow diff --git a/src/mrsimulator/transition/pathway.py b/src/mrsimulator/transition/pathway.py index ab02c9eec..97fe8f0d6 100644 --- a/src/mrsimulator/transition/pathway.py +++ b/src/mrsimulator/transition/pathway.py @@ -88,8 +88,7 @@ def filter(self, P=None, PP=None, D=None): class TransitionPathway(TransitionList): - """ - Base TransitionPathway class is a list of connected Transitions. + """Base TransitionPathway class is a list of connected Transitions. Example: >>> from mrsimulator.transition import TransitionPathway, Transition @@ -106,6 +105,9 @@ def __str__(self): def __repr__(self): return " ⟶ ".join([repr(item) for item in self._list]) + def dict(self) -> dict: + return [item.json() for item in self._list] + def json(self) -> dict: """Parse the class object to a JSON compliant python dictionary object. diff --git a/src/mrsimulator/utils/parseable.py b/src/mrsimulator/utils/parseable.py index 6caf81977..305908758 100644 --- a/src/mrsimulator/utils/parseable.py +++ b/src/mrsimulator/utils/parseable.py @@ -143,7 +143,10 @@ def get_list(member, obj): elif isinstance(item, (dict, Enum)): lst.append(member[i].json()) elif item not in [None, ""]: - lst.append(item) + try: + lst.append(item.json()) + except Exception: + lst.append(item) return lst From c982f5c641a70b2d5555d7ba9475630ceef03e22 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Sat, 1 May 2021 03:42:45 -0400 Subject: [PATCH 10/21] improved test coverage --- src/mrsimulator/transition/pathway.py | 9 ++++++++- .../transition/tests/test_transtion_pathway.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mrsimulator/transition/pathway.py b/src/mrsimulator/transition/pathway.py index 97fe8f0d6..0dc6131bc 100644 --- a/src/mrsimulator/transition/pathway.py +++ b/src/mrsimulator/transition/pathway.py @@ -106,7 +106,14 @@ def __repr__(self): return " ⟶ ".join([repr(item) for item in self._list]) def dict(self) -> dict: - return [item.json() for item in self._list] + """Parse the class object to a python dictionary object. + + Example: + >>> pprint(path.json()) + [{'final': [0.5, -0.5], 'initial': [0.5, 0.5]}, + {'final': [-0.5, 0.5], 'initial': [0.5, 0.5]}] + """ + return self.json() def json(self) -> dict: """Parse the class object to a JSON compliant python dictionary object. diff --git a/src/mrsimulator/transition/tests/test_transtion_pathway.py b/src/mrsimulator/transition/tests/test_transtion_pathway.py index 56e7a38b8..6bf15a8b5 100644 --- a/src/mrsimulator/transition/tests/test_transtion_pathway.py +++ b/src/mrsimulator/transition/tests/test_transtion_pathway.py @@ -23,6 +23,7 @@ def test_transition_pathway(): # to dict with unit assert trans_path.json() == [a, b, c] + assert trans_path.dict() == [a, b, c] assert trans_path.tolist() == [ 0.5, From 7edad3a453ff84195f76d949c5b85437246ecf95 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Mon, 31 May 2021 22:12:09 -0400 Subject: [PATCH 11/21] fixed duplicate dict() def. --- src/mrsimulator/__init__.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/mrsimulator/__init__.py b/src/mrsimulator/__init__.py index dca3a6e83..018e92cb7 100644 --- a/src/mrsimulator/__init__.py +++ b/src/mrsimulator/__init__.py @@ -123,17 +123,6 @@ def load(filename: str, parse_units: bool = True): return parse(val, parse_units) -def dict( - simulator, signal_processors: list = None, fit_report=None, with_units: bool = True -): - data = simulator.json(True, True) if with_units else simulator.reduced_dict() - if signal_processors is not None: - data["signal_processors"] = [item.json() for item in signal_processors] - - data["params"] = None if fit_report is None else fit_report.params.dumps() - return data - - def parse(py_dict, parse_units: bool = True): """Parse the dictionary object to respective Simulator, SignalProcessor and optionally lmfit Parameters object. From 68c79c13ab149f43e2743ce4f984bd0e3a3977b7 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Fri, 4 Jun 2021 09:05:39 -0400 Subject: [PATCH 12/21] update contribs module to include attachments. --- .gitignore | 1 + .../fitting/1D_fitting/plot_2_xonotlite.ipynb | 42 +++++--- fitting_source/1D_fitting/plot_2_xonotlite.py | 97 +++++++++++-------- src/mrsimulator/__init__.py | 11 ++- src/mrsimulator/contribs/__init__.py | 75 ++++++++++++-- .../contribs/tests/test_contribs.py | 68 +++++++++---- src/mrsimulator/tests/save_load_test.py | 21 ++-- src/mrsimulator/utils/importer.py | 5 +- tests/spectral_integration_tests/utils.py | 11 +-- 9 files changed, 227 insertions(+), 104 deletions(-) diff --git a/.gitignore b/.gitignore index 4fcf98c94..102253144 100644 --- a/.gitignore +++ b/.gitignore @@ -115,6 +115,7 @@ class-diagram* *.csdf *.csdfe *.mrsys +*.json.gz test_os.py note.txt simulate_lineshape.py diff --git a/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb b/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb index 772037cb1..c488308d5 100644 --- a/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb +++ b/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb @@ -8,7 +8,7 @@ }, "outputs": [], "source": [ - "# This cell is added by sphinx-gallery\n\n%matplotlib inline\n\nimport mrsimulator\nprint(f'You are using mrsimulator v{mrsimulator.__version__}')" + "# This cell is added by sphinx-gallery\n!pip install mrsimulator --quiet\n\n\n%matplotlib inline\n\nimport mrsimulator\nprint(f'You are using mrsimulator v{mrsimulator.__version__}')" ] }, { @@ -22,7 +22,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The following is a spinning sideband fitting example for $^{29}\\text{Si}$ 1D\nMAS NMR spectrum of Xonotlite crystal, acquired by by Hansen et al. [#f1]_\n\n" + "The following is an example for submitting the NMR tensor parameters to mpcontribs.\nWe use the $^{29}\\text{Si}$ 1D MAS NMR spectrum of Xonotlite crystal by Hansen\net al. [#f1]_ for demonstration.\n\n" ] }, { @@ -33,7 +33,7 @@ }, "outputs": [], "source": [ - "import csdmpy as cp\nimport matplotlib as mpl\nimport matplotlib.pyplot as plt\nimport mrsimulator.signal_processing as sp\nimport mrsimulator.signal_processing.apodization as apo\nfrom mrsimulator import Simulator, SpinSystem, Site\nfrom mrsimulator.methods import BlochDecaySpectrum\nfrom lmfit import Minimizer, fit_report\nfrom mrsimulator.utils import get_spectral_dimensions\nfrom mrsimulator.utils.spectral_fitting import LMFIT_min_function, make_LMFIT_params\n\nfont = {\"size\": 9}\nmpl.rc(\"font\", **font)\nmpl.rcParams[\"figure.figsize\"] = [4.5, 3.0]\nmpl.rcParams[\"grid.linestyle\"] = \"--\"" + "import csdmpy as cp\nimport matplotlib.pyplot as plt\nfrom lmfit import Minimizer\n\nfrom mrsimulator import Simulator, SpinSystem, Site\nfrom mrsimulator.methods import BlochDecaySpectrum\nfrom mrsimulator import signal_processing as sp\nfrom mrsimulator.utils import spectral_fitting as sf\nfrom mrsimulator.utils import get_spectral_dimensions" ] }, { @@ -51,7 +51,7 @@ }, "outputs": [], "source": [ - "filename = \"https://sandbox.zenodo.org/record/744498/files/xonotlite.csdf\"\nexp_data = cp.load(filename).real\n\n# standard deviation of noise from the dataset\nsigma = 2.819601\n\n# convert the dimension coordinates from Hz to ppm\nexp_data.dimensions[0].to(\"ppm\", \"nmr_frequency_ratio\")\n\n# Normalize the spectrum\nmax_amp = exp_data.max()\nexp_data /= max_amp\nsigma /= max_amp\n\n# Plot of the synthetic dataset.\nax = plt.subplot(projection=\"csdm\")\nax.plot(exp_data, \"k\", alpha=0.5)\nax.invert_xaxis()\nplt.tight_layout()\nplt.show()" + "filename = \"https://sandbox.zenodo.org/record/744498/files/xonotlite.csdf\"\nexperiment = cp.load(filename).real\n\n# standard deviation of noise from the dataset\nsigma = 2.819601\n\n# Convert the coordinates along each dimension from Hz to ppm.\n_ = [item.to(\"ppm\", \"nmr_frequency_ratio\") for item in experiment.dimensions]\n\n# Plot of the synthetic dataset.\nplt.figure(figsize=(4.25, 3.0))\nax = plt.subplot(projection=\"csdm\")\nax.plot(experiment, \"k\", alpha=0.5)\nax.invert_xaxis()\nplt.tight_layout()\nplt.show()" ] }, { @@ -87,7 +87,7 @@ }, "outputs": [], "source": [ - "# Get the spectral dimension paramters from the experiment.\nspectral_dims = get_spectral_dimensions(exp_data)\n\nmethod = BlochDecaySpectrum(\n channels=[\"29Si\"],\n magnetic_flux_density=14.1, # in T\n rotor_frequency=1800.0, # in Hz\n spectral_dimensions=spectral_dims,\n experiment=exp_data, # add the measurement to the method.\n)\n\n# Optimize the script by pre-setting the transition pathways for each spin system from\n# the das method.\nfor sys in spin_systems:\n sys.transition_pathways = method.get_transition_pathways(sys)" + "# Get the spectral dimension paramters from the experiment.\nspectral_dims = get_spectral_dimensions(experiment)\n\nmethod = BlochDecaySpectrum(\n channels=[\"29Si\"],\n magnetic_flux_density=14.1, # in T\n rotor_frequency=1800.0, # in Hz\n spectral_dimensions=spectral_dims,\n experiment=experiment, # add the measurement to the method.\n)\n\n# Optimize the script by pre-setting the transition pathways for each spin system from\n# the das method.\nfor sys in spin_systems:\n sys.transition_pathways = method.get_transition_pathways(sys)" ] }, { @@ -105,7 +105,7 @@ }, "outputs": [], "source": [ - "# Simulation\n# ----------\nsim = Simulator()\nsim.spin_systems = spin_systems\nsim.methods = [method]\nsim.run()\n\n# Post Simulation Processing\n# --------------------------\nprocessor = sp.SignalProcessor(\n operations=[\n sp.IFFT(), # inverse FFT to convert frequency based spectrum to time domain.\n apo.Exponential(FWHM=\"50 Hz\"), # apodization of time domain signal.\n sp.FFT(), # forward FFT to convert time domain signal to frequency spectrum.\n sp.Scale(factor=0.6), # scale the frequency spectrum.\n ]\n)\nprocessed_data = processor.apply_operations(data=sim.methods[0].simulation).real\n\n# Plot of the guess Spectrum\n# --------------------------\nax = plt.subplot(projection=\"csdm\")\nax.plot(exp_data, \"k\", linewidth=1, label=\"Experiment\")\nax.plot(processed_data, \"r\", alpha=0.5, linewidth=2.5, label=\"guess spectrum\")\nax.invert_xaxis()\nplt.legend()\nplt.grid()\nplt.tight_layout()\nplt.show()" + "# Simulation\n# ----------\nsim = Simulator(spin_systems=spin_systems, methods=[method])\nsim.run()\n\n# Post Simulation Processing\n# --------------------------\nprocessor = sp.SignalProcessor(\n operations=[\n sp.IFFT(), # inverse FFT to convert frequency based spectrum to time domain.\n sp.apodization.Exponential(FWHM=\"50 Hz\"), # apodization of time domain signal.\n sp.FFT(), # forward FFT to convert time domain signal to frequency spectrum.\n sp.Scale(factor=500), # scale the frequency spectrum.\n ]\n)\nprocessed_data = processor.apply_operations(data=sim.methods[0].simulation).real\n\n# Plot of the guess Spectrum\n# --------------------------\nplt.figure(figsize=(4.25, 3.0))\nax = plt.subplot(projection=\"csdm\")\nax.plot(experiment, \"k\", linewidth=1, label=\"Experiment\")\nax.plot(processed_data, \"r\", alpha=0.75, linewidth=1, label=\"guess spectrum\")\nax.invert_xaxis()\nplt.grid()\nplt.legend()\nplt.tight_layout()\nplt.show()" ] }, { @@ -123,7 +123,7 @@ }, "outputs": [], "source": [ - "params = make_LMFIT_params(sim, processor)\n\nparams.pop(\"sys_0_abundance\")\nparams.pop(\"sys_1_abundance\")\nparams.pop(\"sys_2_abundance\")\nparams[\"sys_0_site_0_shielding_symmetric_eta\"].vary = False\nprint(params.pretty_print(columns=[\"value\", \"min\", \"max\", \"vary\", \"expr\"]))" + "params = sf.make_LMFIT_params(sim, processor, include={\"rotor_frequency\"})\n\nparams.pop(\"sys_0_abundance\")\nparams.pop(\"sys_1_abundance\")\nparams.pop(\"sys_2_abundance\")\nparams[\"sys_0_site_0_shielding_symmetric_eta\"].vary = False\nprint(params.pretty_print(columns=[\"value\", \"min\", \"max\", \"vary\", \"expr\"]))" ] }, { @@ -141,7 +141,7 @@ }, "outputs": [], "source": [ - "minner = Minimizer(LMFIT_min_function, params, fcn_args=(sim, processor, sigma))\nresult = minner.minimize()\nprint(fit_report(result))" + "minner = Minimizer(sf.LMFIT_min_function, params, fcn_args=(sim, processor, sigma))\nresult = minner.minimize()\nresult" ] }, { @@ -159,14 +159,14 @@ }, "outputs": [], "source": [ - "sim.run()\nprocessed_data = processor.apply_operations(data=sim.methods[0].simulation).real\n\n# Plot the spectrum\nax = plt.subplot(projection=\"csdm\")\nplt.plot(exp_data, \"k\", linewidth=1, label=\"Experiment\")\nplt.plot(processed_data, \"r\", alpha=0.5, linewidth=2.5, label=\"Best Fit\")\nplt.xlabel(\"$^{17}$O frequency / ppm\")\nplt.legend()\nplt.grid()\nplt.tight_layout()\nplt.show()" + "best_fit = sf.bestfit(sim, processor)[0]\nresiduals = sf.residuals(sim, processor)[0]\n\n# Plot the spectrum\nplt.figure(figsize=(4.25, 3.0))\nax = plt.subplot(projection=\"csdm\")\nax.plot(experiment, \"k\", linewidth=1, label=\"Experiment\")\nax.plot(best_fit, \"r\", alpha=0.75, linewidth=1, label=\"Best Fit\")\nax.plot(residuals, alpha=0.75, linewidth=1, label=\"Residuals\")\nax.invert_xaxis()\nplt.xlabel(\"$^{29}$Si frequency / ppm\")\nplt.grid()\nplt.legend()\nplt.tight_layout()\nplt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Mpcontribs export\n\nExport the site data as Mpcontribs card.\n\n" + "## Submitting data to MPContribs\n\nTo contribute to MPContribs, we need to export the mrsimulator objects to a list of\nmp-compatible data dictionaries. At present, MPContribs only support data contribution\non a per NMR site basis and, therefore, we only generate mp contributions for\nuncoupled spin systems. Use the ``mrsimulator.contribs`` module to create data\ndictionaries as follows.\n\n" ] }, { @@ -177,7 +177,25 @@ }, "outputs": [], "source": [ - "from mrsimulator.contribs import mpcontribs_export\nfrom pprint import pprint\n\ncards = mpcontribs_export(\n sim,\n project=\"lsdi_nmr_exp_test\",\n identifier=\"Ca6Si6O17(OH)2\",\n exp_dict={\n \"90degreePulseLength\": \"6 \u00b5s\",\n \"relaxationDelay\": \"8 s\",\n \"numberOfScans\": 7224,\n \"referenceCompound\": \"TMS\",\n },\n)\npprint(cards[0])" + "from mrsimulator.contribs import mpcontribs_export\nfrom pprint import pprint\n\nmp_project = \"lsdi_nmr_exp_test\" # The mpcontribs project name\ncards = mpcontribs_export(\n sim,\n [processor],\n project=mp_project,\n identifier=\"Ca6Si6O17(OH)2\",\n exp_dict={\n \"90degreePulseLength\": \"6 \u00b5s\",\n \"relaxationDelay\": \"8 s\",\n \"numberOfScans\": 7224,\n \"referenceCompound\": \"TMS\",\n },\n)\nprint(\"Number of contributions\", len(cards))\npprint(cards[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, ``cards`` hold a list of mp-data dictionaries. In this example, it corresponds\nto three---the number of uncoupled spin systems.\nTo submit contributions, use the mpcontribs client as shown below.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# from mpcontribs.client import Client\n#\n# client = Client() # insert your user API key.\n# client.submit_contributions(cards)" ] }, { @@ -204,7 +222,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.8.10" } }, "nbformat": 4, diff --git a/fitting_source/1D_fitting/plot_2_xonotlite.py b/fitting_source/1D_fitting/plot_2_xonotlite.py index 35ecffe00..c246636fd 100644 --- a/fitting_source/1D_fitting/plot_2_xonotlite.py +++ b/fitting_source/1D_fitting/plot_2_xonotlite.py @@ -5,45 +5,37 @@ ========================================= """ # %% -# The following is a spinning sideband fitting example for :math:`^{29}\text{Si}` 1D -# MAS NMR spectrum of Xonotlite crystal, acquired by by Hansen et al. [#f1]_ +# The following is an example for submitting the NMR tensor parameters to mpcontribs. +# We use the :math:`^{29}\text{Si}` 1D MAS NMR spectrum of Xonotlite crystal by Hansen +# et al. [#f1]_ for demonstration. import csdmpy as cp -import matplotlib as mpl import matplotlib.pyplot as plt -import mrsimulator.signal_processing as sp -import mrsimulator.signal_processing.apodization as apo +from lmfit import Minimizer + from mrsimulator import Simulator, SpinSystem, Site from mrsimulator.methods import BlochDecaySpectrum -from lmfit import Minimizer, fit_report +from mrsimulator import signal_processing as sp +from mrsimulator.utils import spectral_fitting as sf from mrsimulator.utils import get_spectral_dimensions -from mrsimulator.utils.spectral_fitting import LMFIT_min_function, make_LMFIT_params -font = {"size": 9} -mpl.rc("font", **font) -mpl.rcParams["figure.figsize"] = [4.5, 3.0] -mpl.rcParams["grid.linestyle"] = "--" # sphinx_gallery_thumbnail_number = 3 # %% # Import the dataset # ------------------ filename = "https://sandbox.zenodo.org/record/744498/files/xonotlite.csdf" -exp_data = cp.load(filename).real +experiment = cp.load(filename).real # standard deviation of noise from the dataset sigma = 2.819601 -# convert the dimension coordinates from Hz to ppm -exp_data.dimensions[0].to("ppm", "nmr_frequency_ratio") - -# Normalize the spectrum -max_amp = exp_data.max() -exp_data /= max_amp -sigma /= max_amp +# Convert the coordinates along each dimension from Hz to ppm. +_ = [item.to("ppm", "nmr_frequency_ratio") for item in experiment.dimensions] # Plot of the synthetic dataset. +plt.figure(figsize=(4.25, 3.0)) ax = plt.subplot(projection="csdm") -ax.plot(exp_data, "k", alpha=0.5) +ax.plot(experiment, "k", alpha=0.5) ax.invert_xaxis() plt.tight_layout() plt.show() @@ -80,14 +72,14 @@ # **Method** # Get the spectral dimension paramters from the experiment. -spectral_dims = get_spectral_dimensions(exp_data) +spectral_dims = get_spectral_dimensions(experiment) method = BlochDecaySpectrum( channels=["29Si"], magnetic_flux_density=14.1, # in T rotor_frequency=1800.0, # in Hz spectral_dimensions=spectral_dims, - experiment=exp_data, # add the measurement to the method. + experiment=experiment, # add the measurement to the method. ) # Optimize the script by pre-setting the transition pathways for each spin system from @@ -100,9 +92,7 @@ # Simulation # ---------- -sim = Simulator() -sim.spin_systems = spin_systems -sim.methods = [method] +sim = Simulator(spin_systems=spin_systems, methods=[method]) sim.run() # Post Simulation Processing @@ -110,21 +100,22 @@ processor = sp.SignalProcessor( operations=[ sp.IFFT(), # inverse FFT to convert frequency based spectrum to time domain. - apo.Exponential(FWHM="50 Hz"), # apodization of time domain signal. + sp.apodization.Exponential(FWHM="50 Hz"), # apodization of time domain signal. sp.FFT(), # forward FFT to convert time domain signal to frequency spectrum. - sp.Scale(factor=0.6), # scale the frequency spectrum. + sp.Scale(factor=500), # scale the frequency spectrum. ] ) processed_data = processor.apply_operations(data=sim.methods[0].simulation).real # Plot of the guess Spectrum # -------------------------- +plt.figure(figsize=(4.25, 3.0)) ax = plt.subplot(projection="csdm") -ax.plot(exp_data, "k", linewidth=1, label="Experiment") -ax.plot(processed_data, "r", alpha=0.5, linewidth=2.5, label="guess spectrum") +ax.plot(experiment, "k", linewidth=1, label="Experiment") +ax.plot(processed_data, "r", alpha=0.75, linewidth=1, label="guess spectrum") ax.invert_xaxis() -plt.legend() plt.grid() +plt.legend() plt.tight_layout() plt.show() @@ -133,7 +124,7 @@ # ------------------------------------- # Use the :func:`~mrsimulator.utils.spectral_fitting.make_LMFIT_params` for a quick # setup of the fitting parameters. -params = make_LMFIT_params(sim, processor) +params = sf.make_LMFIT_params(sim, processor, include={"rotor_frequency"}) params.pop("sys_0_abundance") params.pop("sys_1_abundance") @@ -143,38 +134,47 @@ # %% # **Solve the minimizer using LMFIT** -minner = Minimizer(LMFIT_min_function, params, fcn_args=(sim, processor, sigma)) +minner = Minimizer(sf.LMFIT_min_function, params, fcn_args=(sim, processor, sigma)) result = minner.minimize() -print(fit_report(result)) +result # %% # The best fit solution # --------------------- -sim.run() -processed_data = processor.apply_operations(data=sim.methods[0].simulation).real +best_fit = sf.bestfit(sim, processor)[0] +residuals = sf.residuals(sim, processor)[0] # Plot the spectrum +plt.figure(figsize=(4.25, 3.0)) ax = plt.subplot(projection="csdm") -plt.plot(exp_data, "k", linewidth=1, label="Experiment") -plt.plot(processed_data, "r", alpha=0.5, linewidth=2.5, label="Best Fit") -plt.xlabel("$^{17}$O frequency / ppm") -plt.legend() +ax.plot(experiment, "k", linewidth=1, label="Experiment") +ax.plot(best_fit, "r", alpha=0.75, linewidth=1, label="Best Fit") +ax.plot(residuals, alpha=0.75, linewidth=1, label="Residuals") +ax.invert_xaxis() +plt.xlabel("$^{29}$Si frequency / ppm") plt.grid() +plt.legend() plt.tight_layout() plt.show() # %% -# Mpcontribs export -# ----------------- +# Submitting data to MPContribs +# ----------------------------- # -# Export the site data as Mpcontribs card. +# To contribute to MPContribs, we need to export the mrsimulator objects to a list of +# mp-compatible data dictionaries. At present, MPContribs only support data contribution +# on a per NMR site basis and, therefore, we only generate mp contributions for +# uncoupled spin systems. Use the ``mrsimulator.contribs`` module to create data +# dictionaries as follows. from mrsimulator.contribs import mpcontribs_export from pprint import pprint +mp_project = "lsdi_nmr_exp_test" # The mpcontribs project name cards = mpcontribs_export( sim, - project="lsdi_nmr_exp_test", + [processor], + project=mp_project, identifier="Ca6Si6O17(OH)2", exp_dict={ "90degreePulseLength": "6 µs", @@ -183,8 +183,19 @@ "referenceCompound": "TMS", }, ) +print("Number of contributions", len(cards)) pprint(cards[0]) +# %% +# Here, ``cards`` hold a list of mp-data dictionaries. In this example, it corresponds +# to three---the number of uncoupled spin systems. +# To submit contributions, use the mpcontribs client as shown below. + +# from mpcontribs.client import Client +# +# client = Client() # insert your user API key. +# client.submit_contributions(cards) + # %% # .. [#f1] Hansen, M. R., Jakobsen, H. J., Skibsted, J., :math:`^{29}\text{Si}` # Chemical Shift Anisotropies in Calcium Silicates from High-Field diff --git a/src/mrsimulator/__init__.py b/src/mrsimulator/__init__.py index 018e92cb7..19cf15e1f 100644 --- a/src/mrsimulator/__init__.py +++ b/src/mrsimulator/__init__.py @@ -45,6 +45,7 @@ from mrsimulator.signal_processing import SignalProcessor import json from lmfit import Parameters +from monty.io import zopen def save( @@ -68,15 +69,19 @@ def save( """ py_dict = dict(simulator, signal_processors, params, with_units) - with open(filename, "w", encoding="utf8") as outfile: - json.dump( + check = filename.endswith("gz") + operator = zopen if check else open + with operator(filename, "w") as outfile: + target = json.dumps( py_dict, - outfile, ensure_ascii=False, sort_keys=False, allow_nan=False, separators=(",", ":"), ) + target += "\n" + target = target.encode("utf-8") if check else target + outfile.write(target) def dict( diff --git a/src/mrsimulator/contribs/__init__.py b/src/mrsimulator/contribs/__init__.py index 6d56bab3f..99cf6452e 100644 --- a/src/mrsimulator/contribs/__init__.py +++ b/src/mrsimulator/contribs/__init__.py @@ -1,5 +1,11 @@ # -*- coding: utf-8 -*- +import json +from pathlib import Path + import numpy as np +from monty.io import zopen +from mrsimulator import save +from mrsimulator import Simulator from mrsimulator.utils import flatten_dict from .base import ContribSchema @@ -12,8 +18,14 @@ "isotropic_chemical_shift": "isotropic", "shielding_symmetric.zeta": "zeta", "shielding_symmetric.eta": "eta", + "shielding_symmetric.alpha": "alpha", + "shielding_symmetric.beta": "beta", + "shielding_symmetric.gamma": "gamma", "quadrupolar.Cq": "Cq", "quadrupolar.eta": "eta", + "quadrupolar.alpha": "alpha", + "quadrupolar.beta": "beta", + "quadrupolar.gamma": "gamma", } @@ -55,12 +67,20 @@ def parse_method(method): } -def mpcontribs_export(sim, project, identifier, exp_dict={}, **kwargs): +def mpcontribs_export( + simulator: Simulator, + signal_processors: list, + project: str, + identifier: str, + exp_dict: dict = {}, + **kwargs, +): """Generate mpcontribs contribution entries for every site in the Simulator object. Arguments --------- - sim: Simulator object from where the site contributions are extracted. + Simulator simulator: object from where the site contributions are extracted. + list signal_processors: A list of SignalProcessor objects. str project: mpcontribs project name (required). str identifier: mpcontribs identifier (required). exp_dict: Additional metadata to use in contribution. @@ -68,21 +88,30 @@ def mpcontribs_export(sim, project, identifier, exp_dict={}, **kwargs): Example ------- - >>> contribution_data = mpcontribs_export(sim, 'my_project') # doctest:+SKIP + >>> contribution_data = mpcontribs_export( + ... simulator, + ... processors, + ... project='my_project', + ... identifier='mp-test' + ... ) # doctest:+SKIP """ + + if "attachments" not in kwargs: + kwargs["attachments"] = prepare_attachments( + simulator, signal_processors, project, identifier + ) + contribs = [ ContribSchema( project=project, identifier=identifier, data={ - "experiment": "experiment goes here", - "simulation": "simulation goes here", "site": {**parse_sites(site)}, - "method": {**parse_method(sim.methods[0])}, + "method": {**parse_method(simulator.methods[0])}, }, **kwargs, ).json() - for sys in sim.spin_systems + for sys in simulator.spin_systems for site in sys.sites ] @@ -90,3 +119,35 @@ def mpcontribs_export(sim, project, identifier, exp_dict={}, **kwargs): item["data"]["method"] = {**item["data"]["method"], **exp_dict} return contribs + + +def save_obj(filename, data): + with zopen(filename, "w") as f: + json_str = json.dumps(data) + "\n" # 2. string (i.e. JSON) + json_bytes = json_str.encode("utf-8") + f.write(json_bytes) + + +def load_obj(filename): + with zopen(filename) as f: + json_bytes = f.read() + json_str = json_bytes.decode("utf-8") # 2. string (i.e. JSON) + data = json.loads(json_str) + return data + + +def prepare_attachments(simulator, signal_processors, project, identifier): + prefix = f"{project}-{identifier}" + filename = [f"{prefix}.mrsim.json.gz"] + save(filename[0], simulator, signal_processors) + + ext = "csdf.json.gz" + _ = [ + ( + save_obj(f"{prefix}-exp-{i}.{ext}", mth.experiment.dict()), + filename.append(f"{prefix}-exp-{i}.{ext}"), + ) + for i, mth in enumerate(simulator.methods) + if mth.experiment is not None + ] + return [Path(item) for item in filename] diff --git a/src/mrsimulator/contribs/tests/test_contribs.py b/src/mrsimulator/contribs/tests/test_contribs.py index dc5f96db8..c288f3c34 100644 --- a/src/mrsimulator/contribs/tests/test_contribs.py +++ b/src/mrsimulator/contribs/tests/test_contribs.py @@ -1,13 +1,27 @@ # -*- coding: utf-8 -*- +from pathlib import Path + +import csdmpy as cp +import numpy as np from mrsimulator import Simulator from mrsimulator import Site from mrsimulator import SpinSystem +from mrsimulator.contribs import load_obj from mrsimulator.contribs import mpcontribs_export from mrsimulator.contribs import parse_method from mrsimulator.contribs import parse_sites from mrsimulator.methods import BlochDecaySpectrum +def csdm_obj(): + x = np.arange(1024) + y = np.random.rand(1024) + return cp.CSDM( + dimensions=[cp.as_dimension(x)], + dependent_variables=[cp.as_dependent_variable(y)], + ) + + def test01(): site = Site(isotope="1H", shielding_symmetric={"zeta": 10, "eta": 0.1}) method = BlochDecaySpectrum( @@ -55,49 +69,61 @@ def test02(): def test_contrib_card(): site = Site(isotope="27Al", quadrupolar={"Cq": 10e6, "eta": 0.4}) - method = BlochDecaySpectrum(channels=["27Al"], magnetic_flux_density=11.7) + csdm_data = csdm_obj() + method = BlochDecaySpectrum( + channels=["27Al"], magnetic_flux_density=11.7, experiment=csdm_data + ) sim = Simulator() sim.spin_systems = [SpinSystem(sites=[site])] sim.methods = [method] omega_0 = abs(method.channels[0].gyromagnetic_ratio * 11.7) - card = { - "data": { - "site": { - "isotope": "27Al", - "ChemicalShift": {"isotropic": "0.0 ppm"}, - "Quadrupolar": {"Cq": "10.0 MHz", "eta": 0.4}, - }, - "method": { - "larmorFrequency": f"{omega_0} MHz", - "spinningFrequency": "0.0 Hz", - "spectralWidth": "25000.0 Hz", - "rotorAngle": "54.7356 degree", - "blah": "blah", + + def get_card(project, identifier): + return { + "data": { + "site": { + "isotope": "27Al", + "ChemicalShift": {"isotropic": "0.0 ppm"}, + "Quadrupolar": {"Cq": "10.0 MHz", "eta": 0.4}, + }, + "method": { + "larmorFrequency": f"{omega_0} MHz", + "spinningFrequency": "0.0 Hz", + "spectralWidth": "25000.0 Hz", + "rotorAngle": "54.7356 degree", + "blah": "blah", + }, }, - }, - "project": "test", - "formula": "ABX", - "identifier": "blah-blah", - } + "project": project, + "formula": "ABX", + "identifier": identifier, + "attachments": [ + Path(f"{project}-{identifier}.mrsim.json.gz"), + Path(f"{project}-{identifier}-exp-0.csdf.json.gz"), + ], + } output = mpcontribs_export( sim, + None, project="test", identifier="blah-blah", exp_dict={"blah": "blah"}, formula="ABX", ) - assert output == [card] + assert output == [get_card("test", "blah-blah")] + assert csdm_data.dict() == load_obj("test-blah-blah-exp-0.csdf.json.gz") sim.spin_systems = [SpinSystem(sites=[site, site, site])] output = mpcontribs_export( sim, + None, project="test", identifier="mp-5733", exp_dict={"blah": "blah"}, formula="ABX", ) - card["identifier"] = "mp-5733" + card = get_card("test", "mp-5733") assert output == [card, card, card] diff --git a/src/mrsimulator/tests/save_load_test.py b/src/mrsimulator/tests/save_load_test.py index 820f5f5ab..b3877d72c 100644 --- a/src/mrsimulator/tests/save_load_test.py +++ b/src/mrsimulator/tests/save_load_test.py @@ -40,17 +40,20 @@ def test_save(): assert sim.methods[0].simulation is not None assert sim.methods[1].simulation is None - save("test.mrsim", simulator=sim, signal_processors=processors, params=None) + kwargs = dict(simulator=sim, signal_processors=processors, params=None) + save("test.mrsim", **kwargs), "test file" + save("test.mrsim.gz", **kwargs), "test gz" def test_load(): - sim_r, processors_r, report_r = load("test.mrsim") + for file_, tag in zip(["test.mrsim", "test.mrsim.gz"], ["file", "gz"]): + sim_r, processors_r, report_r = load(file_) - sim, processors = setup() - sim_r.methods[0].simulation = None - sim.methods[0].simulation = None - assert sim_r == sim - assert processors_r == processors - assert report_r is None + sim, processors = setup() + sim_r.methods[0].simulation = None + sim.methods[0].simulation = None + assert sim_r == sim, f"test {tag}" + assert processors_r == processors, f"test {tag}" + assert report_r is None, f"test {tag}" - os.remove("test.mrsim") + os.remove(file_) diff --git a/src/mrsimulator/utils/importer.py b/src/mrsimulator/utils/importer.py index ba3cf744b..f313365db 100644 --- a/src/mrsimulator/utils/importer.py +++ b/src/mrsimulator/utils/importer.py @@ -4,6 +4,7 @@ from urllib.parse import urlparse from csdmpy.dependent_variable.download import download_file_from_url +from monty.io import zopen __author__ = "Deepansh J. Srivastava" @@ -14,6 +15,8 @@ def import_json(filename): res = urlparse(filename) if res[0] not in ["file", ""]: filename = download_file_from_url(filename) - with open(filename, "rb") as f: + + operator = zopen if filename.endswith("gz") else open + with operator(filename, "rb") as f: content = f.read() return json.loads(str(content, encoding="UTF-8")) diff --git a/tests/spectral_integration_tests/utils.py b/tests/spectral_integration_tests/utils.py index 2a89d695a..0b345f5b1 100644 --- a/tests/spectral_integration_tests/utils.py +++ b/tests/spectral_integration_tests/utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import json +# import json from os import path import numpy as np @@ -7,16 +7,11 @@ from mrsimulator import signal_processing as sp from mrsimulator import Simulator from mrsimulator import SpinSystem +from mrsimulator.utils.importer import import_json from numpy.fft import fft from numpy.fft import fftshift -def _import_json(filename): - with open(filename, "rb") as f: - content = f.read() - return json.loads(str(content, encoding="UTF-8")) - - def _get_header_and_footer(source_file): """ Return the number of rows in the header and footer. @@ -44,7 +39,7 @@ def get_data(filename): """Load a simpson or DMfit output file""" # source data - data_object = _import_json(filename) + data_object = import_json(filename) test_data_object = data_object["test_data"] source_file = test_data_object["filename"] From ccc1f544cc79681c29c7a4281675e19eef36d3b5 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Fri, 11 Jun 2021 18:06:51 -0400 Subject: [PATCH 13/21] - add mpcontribs to requirements-dev.txt - Update code use Attachment class for contributions. - update examples. --- docs/api_py/py-fitting.rst | 14 --- docs/api_py/py-utilities.rst | 32 +++++++ docs/index.rst | 2 +- .../fitting/1D_fitting/plot_2_xonotlite.ipynb | 4 +- fitting_source/1D_fitting/plot_2_xonotlite.py | 6 +- requirements-dev.txt | 1 + setup.py | 2 +- src/mrsimulator/contribs/__init__.py | 96 ++++++++++--------- src/mrsimulator/contribs/base.py | 51 +++++++++- .../contribs/tests/test_contribs.py | 36 ++++--- src/mrsimulator/spin_system/site.py | 6 +- 11 files changed, 170 insertions(+), 80 deletions(-) delete mode 100644 docs/api_py/py-fitting.rst create mode 100644 docs/api_py/py-utilities.rst diff --git a/docs/api_py/py-fitting.rst b/docs/api_py/py-fitting.rst deleted file mode 100644 index 93f85c8e3..000000000 --- a/docs/api_py/py-fitting.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _fitting_api: - -Fitting Utility API -=================== - -LMFIT supplement functions --------------------------- - -.. currentmodule:: mrsimulator.utils.spectral_fitting - -.. autofunction:: make_LMFIT_params -.. autofunction:: LMFIT_min_function -.. autofunction:: bestfit -.. autofunction:: residuals diff --git a/docs/api_py/py-utilities.rst b/docs/api_py/py-utilities.rst new file mode 100644 index 000000000..d4b34bf13 --- /dev/null +++ b/docs/api_py/py-utilities.rst @@ -0,0 +1,32 @@ +.. _fitting_api: + +Utilities API +============= + +LMFIT Utilities +--------------- + +.. currentmodule:: mrsimulator.utils.spectral_fitting + +.. autofunction:: make_LMFIT_params +.. autofunction:: LMFIT_min_function +.. autofunction:: bestfit +.. autofunction:: residuals + + +mpcontribs Utilities +-------------------- + +.. currentmodule:: mrsimulator.contribs + +.. autofunction:: mpcontribs_export + + +.. currentmodule:: mrsimulator.contribs.base + +.. autoclass:: ChemicalShiftSchema +.. autoclass:: QuadrupolarSchema +.. autoclass:: SiteSchema +.. autoclass:: MethodSchema +.. autoclass:: SimulatorSchema +.. autoclass:: ContribSchema diff --git a/docs/index.rst b/docs/index.rst index 8ba30da26..b431a97ba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -232,7 +232,7 @@ API and references api_py/py-simulator api_py/py-signal-processing api_py/py-model - api_py/py-fitting + api_py/py-utilities api_c/c_api diff --git a/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb b/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb index c488308d5..d26f38072 100644 --- a/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb +++ b/docs/notebooks/fitting/1D_fitting/plot_2_xonotlite.ipynb @@ -177,7 +177,7 @@ }, "outputs": [], "source": [ - "from mrsimulator.contribs import mpcontribs_export\nfrom pprint import pprint\n\nmp_project = \"lsdi_nmr_exp_test\" # The mpcontribs project name\ncards = mpcontribs_export(\n sim,\n [processor],\n project=mp_project,\n identifier=\"Ca6Si6O17(OH)2\",\n exp_dict={\n \"90degreePulseLength\": \"6 \u00b5s\",\n \"relaxationDelay\": \"8 s\",\n \"numberOfScans\": 7224,\n \"referenceCompound\": \"TMS\",\n },\n)\nprint(\"Number of contributions\", len(cards))\npprint(cards[0])" + "from mrsimulator.contribs import mpcontribs_export\nfrom pprint import pprint\n\nmp_project = \"lsdi_nmr_exp_test\" # this should be your mpcontribs project name\ncards = mpcontribs_export(\n sim,\n [processor],\n project=mp_project,\n identifier=\"Ca6Si6O17(OH)2\",\n exp_dict={\n \"90degreePulseLength\": \"6 \u00b5s\",\n \"relaxationDelay\": \"8 s\",\n \"numberOfScans\": 7224,\n \"referenceCompound\": \"TMS\",\n },\n)\nprint(\"Number of contributions\", len(cards))\npprint(cards[0][\"data\"])" ] }, { @@ -195,7 +195,7 @@ }, "outputs": [], "source": [ - "# from mpcontribs.client import Client\n#\n# client = Client() # insert your user API key.\n# client.submit_contributions(cards)" + "# from mpcontribs.client import Client\n#\n# client = Client() # uses MPCONTRIBS_API_KEY envvar.\n# client.submit_contributions(cards)" ] }, { diff --git a/fitting_source/1D_fitting/plot_2_xonotlite.py b/fitting_source/1D_fitting/plot_2_xonotlite.py index c246636fd..276eb8c5e 100644 --- a/fitting_source/1D_fitting/plot_2_xonotlite.py +++ b/fitting_source/1D_fitting/plot_2_xonotlite.py @@ -170,7 +170,7 @@ from mrsimulator.contribs import mpcontribs_export from pprint import pprint -mp_project = "lsdi_nmr_exp_test" # The mpcontribs project name +mp_project = "lsdi_nmr_exp_test" # this should be your mpcontribs project name cards = mpcontribs_export( sim, [processor], @@ -184,7 +184,7 @@ }, ) print("Number of contributions", len(cards)) -pprint(cards[0]) +pprint(cards[0]["data"]) # %% # Here, ``cards`` hold a list of mp-data dictionaries. In this example, it corresponds @@ -193,7 +193,7 @@ # from mpcontribs.client import Client # -# client = Client() # insert your user API key. +# client = Client() # uses MPCONTRIBS_API_KEY envvar. # client.submit_contributions(cards) # %% diff --git a/requirements-dev.txt b/requirements-dev.txt index 377f57117..b752b8bcd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -9,6 +9,7 @@ pandas>=1.1.3 numexpr>=2.7.1 psutil>=5.4.8 joblib>=1.0.0 +mpcontribs-client>=3.10.1 # Formatting requirements black diff --git a/setup.py b/setup.py index 2468c8536..77f3f2b2e 100644 --- a/setup.py +++ b/setup.py @@ -403,7 +403,7 @@ def fftw_info(self): if USE_CYTHON: ext_modules = cythonize(ext_modules, language_level=3) -extras = {"all": ["matplotlib>=3.3.3"]} +extras = {"all": ["matplotlib>=3.3.3", "mpcontribs-client>=3.10.1"]} description = "A python toolbox for simulating fast real-time solid-state NMR spectra." setup( diff --git a/src/mrsimulator/contribs/__init__.py b/src/mrsimulator/contribs/__init__.py index 99cf6452e..0e680c2c2 100644 --- a/src/mrsimulator/contribs/__init__.py +++ b/src/mrsimulator/contribs/__init__.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- import json -from pathlib import Path import numpy as np from monty.io import zopen -from mrsimulator import save +from mpcontribs.client import Attachment +from mrsimulator import dict from mrsimulator import Simulator from mrsimulator.utils import flatten_dict @@ -14,7 +14,7 @@ __email__ = "srivastava.89@osu.edu" -SITE_KEYWORDS = { +SITE_MAPPABLE = { "isotropic_chemical_shift": "isotropic", "shielding_symmetric.zeta": "zeta", "shielding_symmetric.eta": "eta", @@ -28,30 +28,42 @@ "quadrupolar.gamma": "gamma", } +NEW_MAPPABLE = { + "shielding_symmetric": "ChemicalShift", + "quadrupolar": "Quadrupolar", + "isotropic_chemical_shift": "ChemicalShift", +} + def parse_sites(site): + """Parse the site object and generate site contribution for mpcontribs. + + Args: + Site site: Site object. + """ if site.shielding_symmetric is not None: - site.shielding_symmetric.zeta *= -1 + site.shielding_symmetric.zeta *= -1 # convert to shift anisotropy site_ = flatten_dict(site.json()) + dict_ = {"ChemicalShift": {}, "Quadrupolar": {}} for k, v in site_.items(): - new_key = SITE_KEYWORDS[k] if k in SITE_KEYWORDS else k - if "shielding_symmetric" in k: - dict_["ChemicalShift"][new_key] = site_[k] - elif "quadrupolar" in k: - dict_["Quadrupolar"][new_key] = site_[k] - elif "isotropic_chemical_shift" in k: - dict_["ChemicalShift"][new_key] = v + root = k.split(".")[0] + new_key = SITE_MAPPABLE[k] if k in SITE_MAPPABLE else k + if root in NEW_MAPPABLE: + dict_[NEW_MAPPABLE[root]][new_key] = site_[k] else: dict_[new_key] = v - for item in ["ChemicalShift", "Quadrupolar"]: - if dict_[item] == {}: - dict_.pop(item) + _ = [dict_.pop(i) for i in ["ChemicalShift", "Quadrupolar"] if dict_[i] == {}] return dict_ def parse_method(method): + """Parse the method object and generate method contribution for mpcontribs. + + Args: + Method method: Method object. + """ gamma = method.channels[0].gyromagnetic_ratio B0 = method.spectral_dimensions[0].events[0].magnetic_flux_density larmor_frequency = abs(gamma * B0) # MHz @@ -79,27 +91,30 @@ def mpcontribs_export( Arguments --------- - Simulator simulator: object from where the site contributions are extracted. - list signal_processors: A list of SignalProcessor objects. - str project: mpcontribs project name (required). - str identifier: mpcontribs identifier (required). - exp_dict: Additional metadata to use in contribution. - **kwargs: Optional keyword arguments from mpcontribs ContributionsSchema + Simulator simulator + Object from where the site contributions are extracted. + list signal_processors + A list of SignalProcessor objects. + str project + mpcontribs project name (required). + str identifier + mpcontribs identifier (required). + exp_dict + Additional metadata to use in contribution. + **kwargs + Optional keyword arguments from mpcontribs ContributionsSchema. Example ------- - >>> contribution_data = mpcontribs_export( - ... simulator, - ... processors, - ... project='my_project', - ... identifier='mp-test' - ... ) # doctest:+SKIP + >>> contribution_data = mpcontribs_export( + ... simulator, + ... processors, + ... project='my_project', + ... identifier='mp-test' + ... ) # doctest:+SKIP """ - if "attachments" not in kwargs: - kwargs["attachments"] = prepare_attachments( - simulator, signal_processors, project, identifier - ) + attach = prepare_attachments(simulator, signal_processors, identifier) contribs = [ ContribSchema( @@ -109,11 +124,11 @@ def mpcontribs_export( "site": {**parse_sites(site)}, "method": {**parse_method(simulator.methods[0])}, }, - **kwargs, ).json() for sys in simulator.spin_systems for site in sys.sites ] + _ = [item.update({"attachments": attach}) for item in contribs] for item in contribs: item["data"]["method"] = {**item["data"]["method"], **exp_dict} @@ -136,18 +151,13 @@ def load_obj(filename): return data -def prepare_attachments(simulator, signal_processors, project, identifier): - prefix = f"{project}-{identifier}" - filename = [f"{prefix}.mrsim.json.gz"] - save(filename[0], simulator, signal_processors) - - ext = "csdf.json.gz" - _ = [ - ( - save_obj(f"{prefix}-exp-{i}.{ext}", mth.experiment.dict()), - filename.append(f"{prefix}-exp-{i}.{ext}"), - ) +def prepare_attachments(simulator, signal_processors, identifier): + """Prepares the dict data as attachments for contribution.""" + filename = f"{identifier}.mrsim" + mrsim = Attachment.from_data(filename, dict(simulator, signal_processors)) + data = [ + Attachment.from_data(f"{identifier}-exp{i}.csdf", mth.experiment.dict()) for i, mth in enumerate(simulator.methods) if mth.experiment is not None ] - return [Path(item) for item in filename] + return [mrsim, *data] diff --git a/src/mrsimulator/contribs/base.py b/src/mrsimulator/contribs/base.py index b08b5a3a5..b284e94ba 100644 --- a/src/mrsimulator/contribs/base.py +++ b/src/mrsimulator/contribs/base.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- # import csdmpy as cp -from typing import Any +from pathlib import Path from typing import List +from typing import Union +from mpcontribs.client import Attachment from pydantic import BaseModel __author__ = "Deepansh Srivastava" @@ -29,6 +31,17 @@ def fullsimplify(val): class ChemicalShiftSchema(Base): + """Schema for Chemical shift parameters. + + Args: + str isotropic: Isotropic chemical shift. + str zeta: Chemical shift anisotropy. + float eta: Shift asymmetry. + str alpha: Euler angle alpha. + str beta: Euler angle beta. + str gamma: Euler angle gamma. + """ + isotropic: str zeta: str = None eta: float = None @@ -38,6 +51,16 @@ class ChemicalShiftSchema(Base): class QuadrupolarSchema(Base): + """Schema for Quadrupolar parameters. + + Args: + str Cq: Quadrupolar coupling constant. + float eta: Quadrupolar asymmetry. + str alpha: Euler angle alpha. + str beta: Euler angle beta. + str gamma: Euler angle gamma. + """ + Cq: str eta: float alpha: str = None @@ -46,12 +69,29 @@ class QuadrupolarSchema(Base): class SiteSchema(Base): + """Schema for Site parameters. + + Args: + str isotope: The isotope given as atomic number followed by atomic symbol. + ChemicalShiftSchema ChemicalShift: The chemical shift scheme. + QuadrupolarSchema Quadrupolar: The quadrupolar scheme. + """ + isotope: str ChemicalShift: ChemicalShiftSchema = None Quadrupolar: QuadrupolarSchema = None class MethodSchema(Base): + """Schema for Method parameters. + + Args: + str larmorFrequency: The larmor frequency of observed isotope. + str spinningFrequency: The rotor spinning frequency. + str spectralWidth: The spectral width of the spectrum. + str rotorAngle: Angle of sample rotation axis wrt the z-axis. + """ + larmorFrequency: str spinningFrequency: str spectralWidth: str @@ -59,6 +99,13 @@ class MethodSchema(Base): class SimulatorSchema(Base): + """Schema for Simulator parameters. + + Args: + SiteSchema site: The site object. + MethodSchema method: The method object. + """ + site: SiteSchema = None method: MethodSchema = None @@ -76,4 +123,4 @@ class ContribSchema(Base): structures: list = None tables: list = None notebook: dict = None - attachments: List[Any] = None + attachments: List[Union[Attachment, Path]] = None diff --git a/src/mrsimulator/contribs/tests/test_contribs.py b/src/mrsimulator/contribs/tests/test_contribs.py index c288f3c34..d271c9596 100644 --- a/src/mrsimulator/contribs/tests/test_contribs.py +++ b/src/mrsimulator/contribs/tests/test_contribs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from pathlib import Path +import os import csdmpy as cp import numpy as np @@ -10,6 +10,7 @@ from mrsimulator.contribs import mpcontribs_export from mrsimulator.contribs import parse_method from mrsimulator.contribs import parse_sites +from mrsimulator.contribs import save_obj from mrsimulator.methods import BlochDecaySpectrum @@ -67,7 +68,7 @@ def test02(): } -def test_contrib_card(): +def system_setup(): site = Site(isotope="27Al", quadrupolar={"Cq": 10e6, "eta": 0.4}) csdm_data = csdm_obj() method = BlochDecaySpectrum( @@ -77,8 +78,12 @@ def test_contrib_card(): sim = Simulator() sim.spin_systems = [SpinSystem(sites=[site])] sim.methods = [method] + return sim - omega_0 = abs(method.channels[0].gyromagnetic_ratio * 11.7) + +def test_contrib_card(): + sim = system_setup() + omega_0 = abs(sim.methods[0].channels[0].gyromagnetic_ratio * 11.7) def get_card(project, identifier): return { @@ -97,12 +102,7 @@ def get_card(project, identifier): }, }, "project": project, - "formula": "ABX", "identifier": identifier, - "attachments": [ - Path(f"{project}-{identifier}.mrsim.json.gz"), - Path(f"{project}-{identifier}-exp-0.csdf.json.gz"), - ], } output = mpcontribs_export( @@ -111,11 +111,14 @@ def get_card(project, identifier): project="test", identifier="blah-blah", exp_dict={"blah": "blah"}, - formula="ABX", ) + attachments = output[0].pop("attachments") assert output == [get_card("test", "blah-blah")] - assert csdm_data.dict() == load_obj("test-blah-blah-exp-0.csdf.json.gz") + assert attachments[0].name == "blah-blah.mrsim.json.gz" + assert attachments[1].name == "blah-blah-exp0.csdf.json.gz" + + site = sim.spin_systems[0].sites[0] sim.spin_systems = [SpinSystem(sites=[site, site, site])] output = mpcontribs_export( sim, @@ -123,7 +126,18 @@ def get_card(project, identifier): project="test", identifier="mp-5733", exp_dict={"blah": "blah"}, - formula="ABX", ) + attachments = [item.pop("attachments") for item in output] card = get_card("test", "mp-5733") assert output == [card, card, card] + + for att in attachments: + assert att[0].name == "mp-5733.mrsim.json.gz" + assert att[1].name == "mp-5733-exp0.csdf.json.gz" + + +def test_save_load(): + data = csdm_obj() + save_obj("test-data.csdf.json.gz", data.dict()) + assert data.dict() == load_obj("test-data.csdf.json.gz") + os.remove("test-data.csdf.json.gz") diff --git a/src/mrsimulator/spin_system/site.py b/src/mrsimulator/spin_system/site.py index e9a5b6433..63024842c 100644 --- a/src/mrsimulator/spin_system/site.py +++ b/src/mrsimulator/spin_system/site.py @@ -227,9 +227,9 @@ def parse_dict_with_units(cls, py_dict: dict): return super().parse_dict_with_units(py_dict) - def json(self): - py_dict = super().json() - if "quadrupolar" in py_dict: + def json(self, units=True, **kwargs): + py_dict = super().json(units=units, **kwargs) + if "quadrupolar" in py_dict and units: value = float(py_dict["quadrupolar"]["Cq"][:-2]) / 1e6 py_dict["quadrupolar"]["Cq"] = f"{value} MHz" return py_dict From 7a0216d12617255baf0a3ed5f1151fbc3e85eb1d Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Tue, 28 Sep 2021 19:28:36 -0400 Subject: [PATCH 14/21] include mpcontribs-client to requirements. --- environment-dev.yml | 1 + requirements-dev.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/environment-dev.yml b/environment-dev.yml index 087f8ea71..ab6901597 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -36,3 +36,4 @@ dependencies: - recommonmark - sphinx-version-warning - plotly + - mpcontribs-client diff --git a/requirements-dev.txt b/requirements-dev.txt index d8d8cc523..b76074853 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -31,3 +31,4 @@ sphinx-tabs>=1.1.13 recommonmark sphinx-version-warning plotly +mpcontribs-client From d97009bb18fb2abed4dd09b3012c0588eb21758d Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Tue, 28 Sep 2021 19:32:49 -0400 Subject: [PATCH 15/21] add mpcontribs-client to requirements. --- environment.yml | 1 + requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/environment.yml b/environment.yml index c86c6ce25..ba0963c75 100644 --- a/environment.yml +++ b/environment.yml @@ -22,3 +22,4 @@ dependencies: - lmfit>=1.0.2 - psutil>=5.4.8 - plotly + - mpcontribs-client diff --git a/requirements.txt b/requirements.txt index ffcf220ef..7f8ab6c5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ pandas>=1.1.3 numexpr>=2.7.1 psutil>=5.4.8 joblib>=1.0.0 +mpcontribs-client \ No newline at end of file From 456693495f7be705a396817a726e42c735a945d9 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Tue, 28 Sep 2021 19:47:40 -0400 Subject: [PATCH 16/21] fixed test error. --- src/mrsimulator/transition/pathway.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mrsimulator/transition/pathway.py b/src/mrsimulator/transition/pathway.py index 6dee2502a..2a4e9f10e 100644 --- a/src/mrsimulator/transition/pathway.py +++ b/src/mrsimulator/transition/pathway.py @@ -119,8 +119,9 @@ def dict(self) -> dict: Example: >>> pprint(path.json()) - [{'final': [0.5, -0.5], 'initial': [0.5, 0.5]}, - {'final': [-0.5, 0.5], 'initial': [0.5, 0.5]}] + {'pathway': [{'final': [0.5, -0.5], 'initial': [0.5, 0.5]}, + {'final': [-0.5, 0.5], 'initial': [0.5, 0.5]}], + 'weight': (1+0j)} """ return self.json() From a4251d797765aa301756b3c9121cba834216bdd9 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Tue, 28 Sep 2021 20:12:36 -0400 Subject: [PATCH 17/21] update numpy requirements to v1.20 --- conda-requirements.txt | 2 +- docs/installation/requirements.rst | 2 +- environment-dev.yml | 2 +- environment.yml | 2 +- pyproject.toml | 2 +- requirements-dev.txt | 3 +-- requirements.txt | 2 +- setup.py | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/conda-requirements.txt b/conda-requirements.txt index f5e1358d0..a17fb7f1f 100644 --- a/conda-requirements.txt +++ b/conda-requirements.txt @@ -1,5 +1,5 @@ -numpy>=1.17 +numpy>=1.20 setuptools>=27.3 cython>=0.29.11 requests>=2.21.0 diff --git a/docs/installation/requirements.rst b/docs/installation/requirements.rst index 3334c649f..282a75ff3 100644 --- a/docs/installation/requirements.rst +++ b/docs/installation/requirements.rst @@ -7,7 +7,7 @@ Package dependencies **Required packages** -- `NumPy>=1.17 `_ +- `NumPy>=1.20 `_ - openblas - cython>=0.29.14 - typing-extensions>=3.7 diff --git a/environment-dev.yml b/environment-dev.yml index ab6901597..6307a2007 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -5,7 +5,7 @@ channels: dependencies: - fftw>=3.3.0 - openblas - - numpy>=1.17 + - numpy>=1.20 - nomkl - setuptools>=27.3 - cython>=0.29 diff --git a/environment.yml b/environment.yml index ba0963c75..23856d81c 100644 --- a/environment.yml +++ b/environment.yml @@ -10,7 +10,7 @@ dependencies: - setuptools>=27.3 - pip - pip: - - numpy>=1.17 + - numpy>=1.20 - cython>=0.29 - matplotlib>=3.3.3 - joblib>=1.0.0 diff --git a/pyproject.toml b/pyproject.toml index 448f27083..68ebbfbfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires-python = ">=3.6" requires = [ "setuptools", "wheel", - "numpy>=1.17", + "numpy>=1.20", "cython>=0.29" ] diff --git a/requirements-dev.txt b/requirements-dev.txt index b76074853..32e484572 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -numpy>=1.17 +numpy>=1.20 matplotlib>=3.3.3 csdmpy>=0.4.1 pydantic>=1.0 @@ -31,4 +31,3 @@ sphinx-tabs>=1.1.13 recommonmark sphinx-version-warning plotly -mpcontribs-client diff --git a/requirements.txt b/requirements.txt index 7f8ab6c5e..4ce2b0712 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -numpy>=1.17 +numpy>=1.20 matplotlib>=3.3.3 csdmpy>=0.4.1 pydantic>=1.0 diff --git a/setup.py b/setup.py index d54490bd0..2407f5974 100644 --- a/setup.py +++ b/setup.py @@ -420,7 +420,7 @@ def fftw_info(self): package_dir={"": "src"}, setup_requires=["numpy>=1.17"], install_requires=[ - "numpy>=1.17", + "numpy>=1.20", "csdmpy>=0.4.1", "pydantic>=1.0", "monty>=2.0.4", From 8341ee676741c6536e34f9547000dd8654e15f9f Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Tue, 28 Sep 2021 20:19:59 -0400 Subject: [PATCH 18/21] . --- conda-requirements.txt | 2 +- docs/installation/requirements.rst | 2 +- environment-dev.yml | 2 +- environment.yml | 2 +- pyproject.toml | 2 +- requirements-dev.txt | 2 +- requirements.txt | 2 +- setup.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/conda-requirements.txt b/conda-requirements.txt index a17fb7f1f..f5e1358d0 100644 --- a/conda-requirements.txt +++ b/conda-requirements.txt @@ -1,5 +1,5 @@ -numpy>=1.20 +numpy>=1.17 setuptools>=27.3 cython>=0.29.11 requests>=2.21.0 diff --git a/docs/installation/requirements.rst b/docs/installation/requirements.rst index 282a75ff3..67d165db3 100644 --- a/docs/installation/requirements.rst +++ b/docs/installation/requirements.rst @@ -7,7 +7,7 @@ Package dependencies **Required packages** -- `NumPy>=1.20 `_ +- `numpy>=1.17 `_ - openblas - cython>=0.29.14 - typing-extensions>=3.7 diff --git a/environment-dev.yml b/environment-dev.yml index 6307a2007..ab6901597 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -5,7 +5,7 @@ channels: dependencies: - fftw>=3.3.0 - openblas - - numpy>=1.20 + - numpy>=1.17 - nomkl - setuptools>=27.3 - cython>=0.29 diff --git a/environment.yml b/environment.yml index 23856d81c..ba0963c75 100644 --- a/environment.yml +++ b/environment.yml @@ -10,7 +10,7 @@ dependencies: - setuptools>=27.3 - pip - pip: - - numpy>=1.20 + - numpy>=1.17 - cython>=0.29 - matplotlib>=3.3.3 - joblib>=1.0.0 diff --git a/pyproject.toml b/pyproject.toml index 68ebbfbfd..448f27083 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires-python = ">=3.6" requires = [ "setuptools", "wheel", - "numpy>=1.20", + "numpy>=1.17", "cython>=0.29" ] diff --git a/requirements-dev.txt b/requirements-dev.txt index 32e484572..d8d8cc523 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -numpy>=1.20 +numpy>=1.17 matplotlib>=3.3.3 csdmpy>=0.4.1 pydantic>=1.0 diff --git a/requirements.txt b/requirements.txt index 4ce2b0712..7f8ab6c5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -numpy>=1.20 +numpy>=1.17 matplotlib>=3.3.3 csdmpy>=0.4.1 pydantic>=1.0 diff --git a/setup.py b/setup.py index 2407f5974..d54490bd0 100644 --- a/setup.py +++ b/setup.py @@ -420,7 +420,7 @@ def fftw_info(self): package_dir={"": "src"}, setup_requires=["numpy>=1.17"], install_requires=[ - "numpy>=1.20", + "numpy>=1.17", "csdmpy>=0.4.1", "pydantic>=1.0", "monty>=2.0.4", From 96963a8f87957e7a2933bcd3bf570e55f204687b Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Tue, 28 Sep 2021 20:23:11 -0400 Subject: [PATCH 19/21] . --- .github/workflows/continuous-integration-pip.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/continuous-integration-pip.yml b/.github/workflows/continuous-integration-pip.yml index dbf083260..c1bc9921f 100644 --- a/.github/workflows/continuous-integration-pip.yml +++ b/.github/workflows/continuous-integration-pip.yml @@ -64,6 +64,7 @@ jobs: python -m pip install --upgrade pip pip install pytest cython setuptools pytest-cov sympy pip install -r requirements.txt + pip install numpy -U - name: Build and install package from source run: python setup.py develop @@ -143,6 +144,7 @@ jobs: run: | conda --version which python + pip install numpy -U python setup.py develop - name: Test with pytest shell: pwsh From f4df98038b798b9d867b56bb54e563a85b05281b Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Tue, 28 Sep 2021 20:54:55 -0400 Subject: [PATCH 20/21] . --- environment-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment-dev.yml b/environment-dev.yml index ab6901597..6307a2007 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -5,7 +5,7 @@ channels: dependencies: - fftw>=3.3.0 - openblas - - numpy>=1.17 + - numpy>=1.20 - nomkl - setuptools>=27.3 - cython>=0.29 From 2b6424259da9323fcb77b236136fade887587a81 Mon Sep 17 00:00:00 2001 From: Deepansh Srivastava Date: Tue, 28 Sep 2021 21:19:16 -0400 Subject: [PATCH 21/21] . --- fitting_source/1D_fitting/plot_2_xonotlite.py | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/fitting_source/1D_fitting/plot_2_xonotlite.py b/fitting_source/1D_fitting/plot_2_xonotlite.py index 276eb8c5e..b1eae91b3 100644 --- a/fitting_source/1D_fitting/plot_2_xonotlite.py +++ b/fitting_source/1D_fitting/plot_2_xonotlite.py @@ -167,24 +167,26 @@ # on a per NMR site basis and, therefore, we only generate mp contributions for # uncoupled spin systems. Use the ``mrsimulator.contribs`` module to create data # dictionaries as follows. -from mrsimulator.contribs import mpcontribs_export -from pprint import pprint - -mp_project = "lsdi_nmr_exp_test" # this should be your mpcontribs project name -cards = mpcontribs_export( - sim, - [processor], - project=mp_project, - identifier="Ca6Si6O17(OH)2", - exp_dict={ - "90degreePulseLength": "6 µs", - "relaxationDelay": "8 s", - "numberOfScans": 7224, - "referenceCompound": "TMS", - }, -) -print("Number of contributions", len(cards)) -pprint(cards[0]["data"]) + +# %% +# from mrsimulator.contribs import mpcontribs_export +# from pprint import pprint + +# mp_project = "lsdi_nmr_exp_test" # this should be your mpcontribs project name +# cards = mpcontribs_export( +# sim, +# [processor], +# project=mp_project, +# identifier="Ca6Si6O17(OH)2", +# exp_dict={ +# "90degreePulseLength": "6 µs", +# "relaxationDelay": "8 s", +# "numberOfScans": 7224, +# "referenceCompound": "TMS", +# }, +# ) +# print("Number of contributions", len(cards)) +# pprint(cards[0]["data"]) # %% # Here, ``cards`` hold a list of mp-data dictionaries. In this example, it corresponds