diff --git a/CHANGELOG.md b/CHANGELOG.md index ff49d7cb..8f2af855 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,32 @@ Changelog All notable changes to this project will be documented in this file. -## [1.3] - + +## [1.4] - 2022-09-25 +### Added +- Export to CSV file option. +- Fitting: color boxes added to interface, indicating color per site +- Bruker fid load now works for up to 8 dimensions +- Custom x-axis can be restored after an operation cleared this +- Bruker type data from WSolids can now be loaded +- Fitting: improved parameter save. +- Dipolar tool: Now also calculates second moments (credits: Henrik Bradtmüller) +- Fitting: Include separate Lorentz value for satellite transitions (credits: Julien Trébosc) +- Fitting: Adds average Pq and peak Cq values to the Czjzek Distribution Plot (credits: Henrik Bradtmüller) +### Removed +- Python2 support. +- Qt4 support. +### Changed +- Bruker spectra load: better intensity scaling using NC_proc setting +- MQMAS fitting: Now has chemical shift distribution settings instead of Gauss +- Colour plot shading set to 'hanning' +- Diffusion fit: 2 * pi factor added +- NMR table: update quadrupolar moments to latest values. +### Fixed +- Diagonal projection in 2D plot with a small SW in the indirect dim +- A bug occurring when switching between 1/2D plots for high dimensional data + +## [1.3] - 2020-10-15 ### Added - Added warning that PyQt4 and python2 support will be dropped soon - Extract part function now has step size diff --git a/DocSrc/ReferenceManual.tex b/DocSrc/ReferenceManual.tex old mode 100755 new mode 100644 index f73c14b4..9f6683a5 --- a/DocSrc/ReferenceManual.tex +++ b/DocSrc/ReferenceManual.tex @@ -885,15 +885,6 @@ \subsection{Correct digital filter} Bruker and JEOL data, the required phase shift is extracted from the parameters files. Failing to use this function for these data leads to a spectrum with a strong first order phasing error. - -\subsection{Scale SW} -A function to scale the spectra width (i.e. the sweepwidth) of the current dimension. Essentially, -this can also be done in the main windows, by filling in $a * b$, with $a$ the current SW and $b$ -the multiplication factor. This tool is included for MQMAS data processing, were a scaling of SW of the -indirect dimension can be used to get a true isotropic dimension (i.e. 1 ppm chemical shift -corresponds to a 1 ppm peak shift). This scaling factor for this is $Q$ and $I$ dependent, and can be selected in -this tool via an easy dropdown menu. - % \subsubsection*{Background} % In theory, the recording of an NMR signal starts directly after the excitation point. The signal can then be easily described by a decaying exponential (or another function) and the Fourier transform does not show surprises. NMR data measured on a Bruker device, however, has the peculiar attribute that it starts \textit{before} the physical start of the signal. The origin of this lies in the way Bruker spectrometers use their digital filter, of which the details will not be described here. Direct Fourier transformation of this signal will lead to massive oscillations in the baseline of the resulting spectrum. @@ -904,6 +895,14 @@ \subsection{Scale SW} Using this tool opens a windows, in which you have to open (again) the current Bruker file. ssNake then finds the desired phase correction and applies it to all traces. +\subsection{Scale SW} +A function to scale the spectra width (i.e. the sweepwidth) of the current dimension. Essentially, +this can also be done in the main windows, by filling in $a * b$, with $a$ the current SW and $b$ +the multiplication factor. This tool is included for MQMAS data processing, were a scaling of SW of the +indirect dimension can be used to get a true isotropic dimension (i.e. 1 ppm chemical shift +corresponds to a 1 ppm peak shift). This scaling factor for this is $Q$ and $I$ dependent, and can be selected in +this tool via an easy dropdown menu. + \subsection{Reference} This is a collection of tools to set the current reference (0 frequency) of the current spectrum. @@ -1558,7 +1557,8 @@ \subsection{CSA} \item The MAS method: static, finite or infinite \item The spinning speed in kHz (when MAS is on `finite'). \item The number of sidebands (when MAS is on `finite'). This value must always be larger then the - expected number of spinning sidebands. When too few sidebands are simulated, the sideband intensities will be off. Larger values take more time to simulate. For efficiency powers of two should be used. + expected number of spinning sidebands. When too few sidebands are simulated, the sideband intensities will be off. +Larger values take more time to simulate. For efficiency powers of two should be used. \item The rotor angle in radian (when MAS is not static). Default is the magic angle: $\text{arctan}(\sqrt{2})$ \end{itemize} @@ -1567,7 +1567,8 @@ \subsection{CSA} \item The three chemical shift values (depend on the definition) in units of the current axis. \item Integral: total integral of the lineshape \item Lorentz: amount of Lorentzian linebroading in Hz -\item Gauss: amount of Gaussian linebroading in Hz. +\item Gauss: amount of Gaussian linebroading in units of the current axis. Note that Gauss in ppm should be used to + describe chemical shift distribution. \end{itemize} A maximum of 10 sites can be fitted simultaneously. @@ -1626,18 +1627,22 @@ \subsection{Quadrupole} \item The rotor angle in radian (when MAS is not `static'). Default is the magic angle: $\text{arctan}(\sqrt{2})$ \item The number of sidebands (when MAS is on `finite'). This value must always be larger then the - expected number of spinning sidebands. When too few sidebands are simulated, the sideband intensities will be off. Larger values take more time to simulate. For efficiency powers of two should be used. + expected number of spinning sidebands. When too few sidebands are simulated, the sideband intensities +will be off. Larger values take more time to simulate. For efficiency powers of two should be used. \item The spinning speed in kHz (when MAS is on `finite'). \item The background (constant value added to all data points) \end{itemize} And for each site: \begin{itemize} -\item Position: centre of the lineshape. This value is added to all $x$-values from the simulation. Scale is in Hz. +\item Position: centre of the lineshape in units of the current axis. This value is added to all + $x$-values from the simulation. \item $C_\text{Q}$: quadrupolar coupling constant in MHz. \item $\eta$: the asymmetry parameter \item Integral: total integral of the lineshape -\item Lorentz: amount of Lorentzian linebroading in Hz -\item Gauss: amount of Gaussian linebroading in Hz. +\item Lorentz: amount of Lorentzian linebroading of the central transition in Hz +\item LorentzST: amount of Lorentzian linebroading of satellite transitions in Hz +\item Gauss: amount of Gaussian linebroading in units of the current axis. In ppm unit Gauss can +describe chemical shift distribution \end{itemize} A maximum of 10 sites can be fitted simultaneously. @@ -1734,12 +1739,16 @@ \subsubsection*{Equations} \subsection{Quadrupole+CSA} -Quadrupole + CSA fitting combines first and second order quadrupolar effects and chemical shift anisotropy. Both spinning and static spectra can be simulated. For the effects of the individual quadrupole or CSA settings, see above at their respective sections. +Quadrupole + CSA fitting combines first and second order quadrupolar effects and chemical shift anisotropy. +Both spinning and static spectra can be simulated. For the effects of the individual quadrupole or CSA +settings, see above at their respective sections. -Three additional parameters that need to be given are the three angles defining the rotation of the CSA tensor with respect to the EFG tensor ($\alpha$, $\beta$, $\gamma$). +Three additional parameters that need to be given are the three angles defining the rotation of the CSA +tensor with respect to the EFG tensor ($\alpha$, $\beta$, $\gamma$). We order the EFG tensor values in the following way: $|V_{zz}|>|V_{xx}|>|V_{yy}|$. We define the CSA and quadrupole angles in the same way as SIMPSON does, but we rotate via -an Z-X-Z euler rotation, while SIMPSON uses Z-Y-Z. This leads to the following conversion table, showing the ssNake angles when using $\alpha$, $\beta$, $\gamma$ in another software package: +an Z-X-Z euler rotation, while SIMPSON uses Z-Y-Z. This leads to the following conversion table, showing +the ssNake angles when using $\alpha$, $\beta$, $\gamma$ in another software package: \begin{center} \begin{tabular}{cc} @@ -1812,7 +1821,8 @@ \subsubsection*{Equations} \int \int \int \sin(\beta) \text{d}\alpha \text{d}\beta \text{d}\gamma \nonumber \\ & \exp \left\{ -\frac{1}{2\sigma^2} \left[ C_\text{Q,0}^2 \left(1 + \frac{\eta_0^2}{3}\right) + C_\text{Q}^2 \left(1+ \frac{\eta^2}{3}\right) \right.\right. \\ - & \left.\vphantom{\frac{a^1}{1}} \left.\vphantom{\frac{a^1}{1}} - \frac{2}{\sqrt{3}} C_\text{Q} C_\text{Q,0} \left( \sqrt{3} a_{11} + \eta_0 a_{15} + \eta \left( + & \left.\vphantom{\frac{a^1}{1}} \left.\vphantom{\frac{a^1}{1}} - \frac{2}{\sqrt{3}} C_\text{Q} C_\text{Q,0} + \left( \sqrt{3} a_{11} + \eta_0 a_{15} + \eta \left( a_{51} + \frac{\eta_0 a_{55}}{\sqrt{3}} \right) \right) \right] \right\} \nonumber \end{align} with @@ -1841,19 +1851,33 @@ \subsubsection*{Equations} tests for the \texttt{numba} package itself, so regular installation of this package should be sufficient. \subsection{MQMAS} -This fitting routine can be used to fit an MQMAS spectrum. It is a 2D fitting routine, showing a contour plot of the data. The fit result is plot on top of this using a different contour colour. As there are now two plotting dimensions, there are two Lorentz and Gauss broadening settings. +This fitting routine can be used to fit an MQMAS spectrum. It is a 2D fitting routine, showing a contour plot +of the data. The fit result is plot on top of this using a different contour colour. Related to the additional dimension +a Lorentz1 parameter is added to describe relaxation in D1. Regardin Gaussian broadening, Gauss parameter is used in +both dimensions to describe chemical shift distribution. -For simulation of MQMAS data, we need to define the quadrupolar parameters as before (see the section on fitting of quadrupole lines). In this case, no satellite signals are included. Only infinite MAS spinning speeds are supported (no spinning sidebands are calculated). Moreover, it is an ideal simulation, so no excitation profiles are taken into account. +For simulation of MQMAS data, we need to define the quadrupolar parameters as before (see the section on fitting +of quadrupole lines). In this case, no satellite signals are included. Only infinite MAS spinning speeds are + supported (no spinning sidebands are calculated). Moreover, it is an ideal simulation, so no excitation profiles +are taken into account. -Apart from the spin quantum number I, MQMAS simulation also requires selected the multiple quantum transition to be supplied. This is labelled `MQ' in the interface. +Apart from the spin quantum number I, MQMAS simulation also requires selected the multiple quantum transition +evolving in D1 to be supplied. This is labelled `MQ' in the interface. -Two additional parameters that need to be supplied are the shearing and spectral width scale factors. These are required for the processing of MQMAS data, as is also described in our advanced tutorial on MQMAS processing. By default, the shearing is set at 0, and the sw scaling at 1. This results in unsheared data, and can be used to fit experimental data that has also not been sheared. +Two additional parameters that need to be supplied are the shearing and spectral width scale factors. These allow +to model 2D MQMAS spectra processed using diferent shearing schemes. Check our advanced tutorial on MQMAS processing. +By default, the shearing is set at 0, and the sw scaling at 1. This results in unsheared data with D1 corresponding +to 3Q coherence evolution, and can be used to fit experimental data that has not been sheared. -The required sharing and sw scaling factors depend on both I and MQ. Using the 'Auto' push button in the interface, the correct values are filled in in the boxes, depending on the current I and MQ setting. +The required sharing and sw scaling factors depend on both I and MQ. Using the 'Auto' push button in the interface, +the correct values are filled in in the boxes, depending on the current I and MQ setting. \subsection{Czjzek MQMAS} -This routine can be used to fit an MQMAS spectrum of a species that is influenced by a Czjzek distribution. The required inputs are describe in the section on 1D Czjzek fitting, and regular MQMAS fitting respectively. Note that this again means that firstly a library needs to be generated of a $C_\text{Q}$ and $\eta$ grid. The spectrum sharing and sw scaling are as described in the MQMAS section. +This routine can be used to fit an MQMAS spectrum of a species that is influenced by a Czjzek distribution. The +required inputs are describe in the section on 1D Czjzek fitting, and regular MQMAS fitting respectively. Note +that this again means that firstly a library needs to be generated of a $C_\text{Q}$ and $\eta$ grid. The spectrum +sharing and sw scaling are as described in the MQMAS section. \subsection{External} @@ -2119,7 +2143,7 @@ \subsection{Multi plot} to add additional data (from other, or the same workspace). Things that can be changed are: \begin{itemize} \item Scale: linear multiplier of the signal ($y$ value) - \item Offset: gives an offset to the current data. The step size depends on the data, and is shown between + \item Offset: gives a vertical offset to the current data. The step size depends on the data, and is shown between brackets. \item Shift: horizontal shift in units of the current axis. \item Dimension selector: selects the dimension and trace to be shown (as with a regular 1D plot). diff --git a/DocSrc/Title.tex b/DocSrc/Title.tex index 4b093feb..9a7bd22a 100755 --- a/DocSrc/Title.tex +++ b/DocSrc/Title.tex @@ -27,7 +27,7 @@ \large Wouter Franssen \& Bas van Meerten \vspace{1cm} -\large Version 1.4b +\large Version 1.4 \vfill \includegraphics[width=0.5\textwidth]{Images/logo.pdf}\ diff --git a/README.md b/README.md index ea59ee72..be8c6a74 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,8 @@ Contact For question and suggestions mail to: ssnake@science.ru.nl +Or use the GitHib [discussion forum](https://github.com/smeerten/ssnake/discussions) + License ------- diff --git a/ReferenceManual.pdf b/ReferenceManual.pdf index 63f6ed7a..f6fd5f30 100644 Binary files a/ReferenceManual.pdf and b/ReferenceManual.pdf differ diff --git a/Tutorial/Tutorial.pdf b/Tutorial/Tutorial.pdf index 8b376cb6..8f200e53 100644 Binary files a/Tutorial/Tutorial.pdf and b/Tutorial/Tutorial.pdf differ diff --git a/Tutorial/src/Title.tex b/Tutorial/src/Title.tex index eb2d413f..c8e61755 100644 --- a/Tutorial/src/Title.tex +++ b/Tutorial/src/Title.tex @@ -27,7 +27,7 @@ \large Wouter Franssen \& Bas van Meerten \vspace{1cm} -\large Version 1.4b +\large Version 1.4 \vfill \includegraphics[width=0.7\textwidth]{Images/logo.pdf}\ diff --git a/src/Czjzek.py b/src/Czjzek.py index 14ec2871..f60fc657 100644 --- a/src/Czjzek.py +++ b/src/Czjzek.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/Icons/CSV.png b/src/Icons/CSV.png new file mode 100644 index 00000000..4f5d3216 Binary files /dev/null and b/src/Icons/CSV.png differ diff --git a/src/Icons/Sources/CSV.svg b/src/Icons/Sources/CSV.svg new file mode 100644 index 00000000..0bc70514 --- /dev/null +++ b/src/Icons/Sources/CSV.svg @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="10mm" + height="10mm" + viewBox="0 0 28.346458 28.346456" + version="1.2" + id="svg45" + sodipodi:docname="CSV.svg" + inkscape:version="1.0.2 (e86c870879, 2021-01-15)" + inkscape:export-filename="/media/HDD/ssnake/ssnake/src/Icons/CSV.png" + inkscape:export-xdpi="162" + inkscape:export-ydpi="162"> + <metadata + id="metadata49"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="1015" + id="namedview47" + showgrid="false" + units="mm" + inkscape:zoom="9.5144324" + inkscape:cx="10.038412" + inkscape:cy="20.731622" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg45" + inkscape:document-rotation="0" /> + <defs + id="defs22"> + <g + id="g20"> + <symbol + overflow="visible" + id="glyph0-0" + style="overflow:visible"> + <path + style="stroke:none" + d="m 0.96875,0 h 5.1875 v -7.203125 h -5.1875 z m 1.1875,-0.625 0.796875,-1.453125 0.59375,-1.125 H 3.59375 l 0.5625,1.125 L 4.953125,-0.625 Z M 3.546875,-4.21875 3,-5.25 2.28125,-6.546875 h 2.5625 L 4.125,-5.25 3.59375,-4.21875 Z m -1.875,3.015625 v -5.03125 l 1.390625,2.53125 z m 3.765625,0 -1.375,-2.5 1.375,-2.53125 z m 0,0" + id="path2" + inkscape:connector-curvature="0" /> + </symbol> + <symbol + overflow="visible" + id="glyph0-1" + style="overflow:visible"> + <path + style="stroke:none" + d="M 1.828125,0 H 2.71875 v -6.953125 h -0.6875 c -0.390625,0.21875 -0.84375,0.40625 -1.484375,0.5 V -5.875 h 1.28125 z m 0,0" + id="path5" + inkscape:connector-curvature="0" /> + </symbol> + <symbol + overflow="visible" + id="glyph1-0" + style="overflow:visible"> + <path + style="stroke:none" + d="" + id="path8" + inkscape:connector-curvature="0" /> + </symbol> + <symbol + overflow="visible" + id="glyph1-1" + style="overflow:visible"> + <path + style="stroke:none" + d="m 0.34375,0 h 2.640625 v -0.421875 l -0.75,-0.0625 L 2.0625,-0.625 v -2.6875 h 1.71875 l 0.140625,0.15625 0.109375,0.765625 h 0.484375 v -2.3125 H 4.03125 L 3.921875,-3.9375 3.78125,-3.796875 H 2.0625 v -2.75 h 2.359375 l 0.125,0.15625 0.15625,1 H 5.28125 V -7.03125 H 0.265625 v 0.421875 l 0.671875,0.0625 0.1875,0.15625 V -0.625 l -0.1875,0.140625 -0.59375,0.0625 z m 0,0" + id="path11" + inkscape:connector-curvature="0" /> + </symbol> + <symbol + overflow="visible" + id="glyph1-2" + style="overflow:visible"> + <path + style="stroke:none" + d="m 0.171875,-7.03125 v 1.625 H 0.75 l 0.140625,-0.984375 0.140625,-0.15625 h 1.625 V -0.625 l -0.203125,0.140625 -0.6875,0.0625 V 0 h 2.71875 v -0.421875 l -0.6875,-0.0625 L 3.59375,-0.625 v -5.921875 h 1.625 L 5.359375,-6.390625 5.5,-5.40625 h 0.578125 v -1.625 z m 0,0" + id="path14" + inkscape:connector-curvature="0" /> + </symbol> + <symbol + overflow="visible" + id="glyph1-3" + style="overflow:visible"> + <path + style="stroke:none" + d="M 0.3125,0 H 2.84375 V -0.375 L 2.109375,-0.421875 1.921875,-0.59375 v -3 c 0.4375,-0.5 0.796875,-0.765625 1.140625,-0.765625 0.21875,0 0.40625,0.09375 0.53125,0.25 H 3.9375 V -5 c -0.21875,-0.09375 -0.421875,-0.140625 -0.625,-0.140625 -0.09375,0 -0.1875,0 -0.265625,0.015625 -0.375,0.3125 -0.75,0.640625 -1.125,1.015625 V -5.09375 H 1.53125 L 0.3125,-4.703125 v 0.3125 h 0.75 V -0.59375 L 0.875,-0.421875 0.3125,-0.375 Z m 0,0" + id="path17" + inkscape:connector-curvature="0" /> + </symbol> + </g> + </defs> + <rect + style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.80000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect4245" + width="28.346457" + height="28.346457" + x="-1.5809044e-07" + y="-1.5809044e-07" /> + <text + xml:space="preserve" + style="font-size:31.1985px;line-height:1.25;font-family:FreeSerif;-inkscape-font-specification:FreeSerif;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;stroke-width:1.16994" + x="6.3424172" + y="12.956489" + id="text850"><tspan + sodipodi:role="line" + id="tspan848" + x="6.3424172" + y="12.956489" + style="font-size:62.3969px;stroke-width:1.16994">,</tspan></text> +</svg> diff --git a/src/IsotopeProperties b/src/IsotopeProperties index f009211a..9e85f6ab 100644 --- a/src/IsotopeProperties +++ b/src/IsotopeProperties @@ -1,6 +1,6 @@ Atom Nucleus Name Mass Spin Abundance(%) Gamma Q(fm^2) FreqRatio Refsample SampleCondition Lifetime(y) -1 H Hydrogen 1 0.5 99.9885 26.7522128 - 100.000000 Me<sub>4</sub>Si CDCl<sub>3</sub>, φ = 1% - -1 H Hydrogen 2 1 0.0115 4.10662791 0.2860 15.350609 (CD<sub>3</sub>)<sub>4</sub>Si neat - +1 H Hydrogen 1 0.5 99.9885 26.7522128 0 100.000000 Me<sub>4</sub>Si CDCl<sub>3</sub>, φ = 1% - +1 H Hydrogen 2 1 0.0115 4.10662791 0.285783 15.350609 (CD<sub>3</sub>)<sub>4</sub>Si neat - 1 H Hydrogen 3 0.5 - 28.5349779 0 106.663974 Me<sub>4</sub>Si-t1 - 12.32 y 2 He Helium 3 0.5 1.37e-4 -20.3801587 0 76.179437 He gas - 3 Li Lithium 6 1 7.59 3.9371709 -0.0808 14.716086 LiCl D<sub>2</sub>O, 9.7 m - @@ -20,14 +20,14 @@ Atom Nucleus Name Mass Spin Abundance(%) Gamma Q(fm^2) FreqRatio Refsample Sampl 13 Al Aluminium 27 2.5 100 6.9762715 14.66 26.056859 Al(NO<sub>3</sub>)<sub>3</sub> D<sub>2</sub>O, 1.1 m - 14 Si Silicon 29 0.5 4.6832 -5.3190 0 19.867187 Me<sub>4</sub>Si CDCl<sub>3</sub>, φ = 1% - 15 P Phosphorus 31 0.5 100 10.8394 0 40.480742 H<sub>3</sub>PO<sub>4</sub> - - -16 S Sulfur 33 1.5 0.76 2.055685 -6.78 7.676000 (NH<sub>4</sub>)<sub>2</sub>SO<sub>4</sub> D<sub>2</sub>O, sat. - -17 Cl Chlorine 35 1.5 75.78 2.624198 -8.165 9.797909 NaCl D<sub>2</sub>O, 0.1 M - -17 Cl Chlorine 37 1.5 24.22 2.184368 -6.435 8.155725 NaCl D<sub>2</sub>O, 0.1 M - +16 S Sulfur 33 1.5 0.76 2.055685 -6.94 7.676000 (NH<sub>4</sub>)<sub>2</sub>SO<sub>4</sub> D<sub>2</sub>O, sat. - +17 Cl Chlorine 35 1.5 75.78 2.624198 -8.112 9.797909 NaCl D<sub>2</sub>O, 0.1 M - +17 Cl Chlorine 37 1.5 24.22 2.184368 -6.393 8.155725 NaCl D<sub>2</sub>O, 0.1 M - 18 Ar Argon 37 1.5 - 3.656 7.6 13.67 - - 35.0 d 18 Ar Argon 39 3.5 - 2.17 -12 8.13 - - 268 y -19 K Potassium 39 1.5 93.2581 1.2500608 5.85 4.666373 KCl D<sub>2</sub>O, 0.1 M - -19 K Potassium 40 4 0.0117 -1.5542854 -7.3 5.802018 KCl D<sub>2</sub>O, 0.1 M 1.248e9 y -19 K Potassium 41 1.5 6.7302 0.68606808 7.11 2.561305 KCl D<sub>2</sub>O, 0.1 M - +19 K Potassium 39 1.5 93.2581 1.2500608 6.03 4.666373 KCl D<sub>2</sub>O, 0.1 M - +19 K Potassium 40 4 0.0117 -1.5542854 -7.50 5.802018 KCl D<sub>2</sub>O, 0.1 M 1.248e9 y +19 K Potassium 41 1.5 6.7302 0.68606808 7.34 2.561305 KCl D<sub>2</sub>O, 0.1 M - 20 Ca Calcium 43 3.5 0.135 -1.803069 -4.08 6.730029 CaCl<sub>2</sub> D<sub>2</sub>O, 0.1 M - 21 Sc Scandium 45 3.5 100 6.5087973 -22.0 24.291747 Sc(NO<sub>3</sub>)<sub>3</sub> D<sub>2</sub>O, 0.06 M - 22 Ti Titanium 47 2.5 7.44 -1.5105 30.2 5.637534 TiCl<sub>4</sub> neat - @@ -41,18 +41,18 @@ Atom Nucleus Name Mass Spin Abundance(%) Gamma Q(fm^2) FreqRatio Refsample Sampl 28 Ni Nickel 61 1.5 1.1399 -2.3948 16.2 8.936051 Ni(CO)<sub>4</sub> neat/C<sub>6</sub>D<sub>6</sub> - 29 Cu Copper 63 1.5 69.17 7.1117890 -22.0 26.515473 [Cu(CH<sub>3</sub>CN)<sub>4</sub>][ClO<sub>4</sub>] CH<sub>3</sub>CN, sat. - 29 Cu Copper 65 1.5 30.83 7.60435 -20.4 28.403693 [Cu(CH<sub>3</sub>CN)<sub>4</sub>][ClO<sub>4</sub>] CH<sub>3</sub>CN, sat. - -30 Zn Zinc 67 2.5 4.10 1.676688 15.0 6.256803 Zn(NO<sub>3</sub>)<sub>2</sub> D<sub>2</sub>O, sat. - +30 Zn Zinc 67 2.5 4.10 1.676688 12.2 6.256803 Zn(NO<sub>3</sub>)<sub>2</sub> D<sub>2</sub>O, sat. - 31 Ga Gallium 69 1.5 60.108 6.438855 17.1 24.001354 Ga(NO<sub>3</sub>)<sub>3</sub> D<sub>2</sub>O, 1.1 m - 31 Ga Gallium 71 1.5 39.892 8.181171 10.7 30.496704 Ga(NO<sub>3</sub>)<sub>3</sub> D<sub>2</sub>O, 1.1 m - 32 Ge Germanium 73 4.5 7.73 -0.9360303 -19.6 3.488315 (CH<sub>3</sub>)<sub>4</sub>Ge neat - -33 As Arsenic 75 1.5 100 4.596163 31.4 17.122614 NaAsF<sub>6</sub> CD<sub>3</sub>CN, 0.5 M - +33 As Arsenic 75 1.5 100 4.596163 31.1 17.122614 NaAsF<sub>6</sub> CD<sub>3</sub>CN, 0.5 M - 34 Se Selenium 77 0.5 7.63 5.1253857 0 19.071513 Me<sub>2</sub>Se neat/C<sub>6</sub>D<sub>6</sub> - -35 Br Bromine 79 1.5 50.69 6.725616 31.3 25.053980 NaBr D<sub>2</sub>O, 0.01 M - -35 Br Bromine 81 1.5 49.31 7.249776 26.2 27.006518 NaBr D<sub>2</sub>O, 0.01 M - +35 Br Bromine 79 1.5 50.69 6.725616 30.87 25.053980 NaBr D<sub>2</sub>O, 0.01 M - +35 Br Bromine 81 1.5 49.31 7.249776 25.79 27.006518 NaBr D<sub>2</sub>O, 0.01 M - 36 Kr Krypton 83 4.5 11.49 -1.03310 25.9 3.847600 Kr gas - 37 Rb Rubidium 85 2.5 72.17 2.5927050 27.6 9.654943 RbCl D<sub>2</sub>O, 0.01 M - 37 Rb Rubidium 87 1.5 27.83 8.786400 13.35 32.720454 RbCl D<sub>2</sub>O, 0.01 M 4.97e10 y -38 Sr Strontium 87 4.5 7.00 -1.1639376 33.5 4.333822 SrCl<sub>2</sub> D<sub>2</sub>O, 0.5 M - +38 Sr Strontium 87 4.5 7.00 -1.1639376 30.5 4.333822 SrCl<sub>2</sub> D<sub>2</sub>O, 0.5 M - 39 Y Yttrium 89 0.5 100 -1.3162791 0 4.900198 Y(NO<sub>3</sub>)<sub>3</sub> H<sub>2</sub>O/D<sub>2</sub>O - 40 Zr Zirconium 91 2.5 11.22 -2.49743 -17.6 9.296298 Zr(C<sub>5</sub>H<sub>5</sub>)<sub>2</sub>Cl<sub>2</sub> CH<sub>2</sub>Cl<sub>2</sub>, sat. - 41 Nb Niobium 93 4.5 100 6.5674 -32.0 24.476170 K[NbCl<sub>6</sub>] CH<sub>3</sub>CN, sat. - @@ -67,23 +67,23 @@ Atom Nucleus Name Mass Spin Abundance(%) Gamma Q(fm^2) FreqRatio Refsample Sampl 47 Ag Silver 109 0.5 48.161 -1.2518634 0 4.653533 AgNO<sub>3</sub> D<sub>2</sub>O, sat. - 48 Cd Cadmium 111 0.5 12.80 -5.6983131 0 21.215480 Me<sub>2</sub>Cd neat - 48 Cd Cadmium 113 0.5 12.22 -5.9609155 0 22.193175 Me<sub>2</sub>Cd neat 8.04e15 y -49 In Indium 113 4.5 4.29 5.8845 79.9 21.865755 In(NO<sub>3</sub>)<sub>3</sub> D<sub>2</sub>O, 0.1 M - -49 In Indium 115 4.5 95.71 5.8972 81.0 21.912629 In(NO<sub>3</sub>)<sub>3</sub> D<sub>2</sub>O, 0.1 M 4.4e14 y +49 In Indium 113 4.5 4.29 5.8845 76.1 21.865755 In(NO<sub>3</sub>)<sub>3</sub> D<sub>2</sub>O, 0.1 M - +49 In Indium 115 4.5 95.71 5.8972 77.2 21.912629 In(NO<sub>3</sub>)<sub>3</sub> D<sub>2</sub>O, 0.1 M 4.4e14 y 50 Sn Tin 115 0.5 0.34 -8.8013 0 32.718749 Me<sub>4</sub>Sn neat/C<sub>6</sub>D<sub>6</sub> - 50 Sn Tin 117 0.5 7.68 -9.58879 0 35.632259 Me<sub>4</sub>Sn neat/C<sub>6</sub>D<sub>6</sub> - 50 Sn Tin 119 0.5 8.59 -10.0317 0 37.290632 Me<sub>4</sub>Sn neat/C<sub>6</sub>D<sub>6</sub> - -51 Sb Antimony 121 2.5 57.21 6.4435 -36.0 23.930577 KSbCl<sub>6</sub> CH<sub>3</sub>CN, sat. - -51 Sb Antimony 123 3.5 42.79 3.4892 -49.0 12.959217 KSbCl<sub>6</sub> CH<sub>3</sub>CN, sat. - +51 Sb Antimony 121 2.5 57.21 6.4435 -54.3 23.930577 KSbCl<sub>6</sub> CH<sub>3</sub>CN, sat. - +51 Sb Antimony 123 3.5 42.79 3.4892 -69.2 12.959217 KSbCl<sub>6</sub> CH<sub>3</sub>CN, sat. - 52 Te Tellurium 123 0.5 0.89 -7.059098 0 26.169742 Me<sub>2</sub>Te neat/C<sub>6</sub>D<sub>6</sub> - 52 Te Tellurium 125 0.5 7.07 -8.5108404 0 31.549769 Me<sub>2</sub>Te neat/C<sub>6</sub>D<sub>6</sub> - -53 I Iodine 127 2.5 100 5.389573 -71.0 20.007486 KI D<sub>2</sub>O, 0.01 M - +53 I Iodine 127 2.5 100 5.389573 -68.822 20.007486 KI D<sub>2</sub>O, 0.01 M - 54 Xe Xenon 129 0.5 26.44 -7.452103 0 27.810186 XeOF<sub>4</sub> neat - -54 Xe Xenon 131 1.5 21.18 2.209076 -11.4 8.243921 XeOF<sub>4</sub> neat - +54 Xe Xenon 131 1.5 21.18 2.209076 -11.46 8.243921 XeOF<sub>4</sub> neat - 55 Cs Cesium 133 3.5 100 3.5332539 -0.343 13.116142 CsNO<sub>3</sub> D<sub>2</sub>O, 0.1 M - -56 Ba Barium 135 1.5 6.592 2.67550 16.0 9.934457 BaCl<sub>2</sub> D<sub>2</sub>O, 0.5 M - -56 Ba Barium 137 1.5 11.232 2.99295 24.5 11.112928 BaCl<sub>2</sub> D<sub>2</sub>O, 0.5 M - +56 Ba Barium 135 1.5 6.592 2.67550 15.3 9.934457 BaCl<sub>2</sub> D<sub>2</sub>O, 0.5 M - +56 Ba Barium 137 1.5 11.232 2.99295 23.6 11.112928 BaCl<sub>2</sub> D<sub>2</sub>O, 0.5 M - 57 La Lanthanum 138 5 0.090 3.557239 45.0 13.194300 LaCl<sub>3</sub> D<sub>2</sub>O/H<sub>2</sub>O 1.06e11 y -57 La Lanthanum 139 3.5 99.910 3.8083318 20.0 14.125641 LaCl<sub>3</sub> D<sub>2</sub>O, 0.01 M - +57 La Lanthanum 139 3.5 99.910 3.8083318 20.6 14.125641 LaCl<sub>3</sub> D<sub>2</sub>O, 0.01 M - 58 Ce Cerium 137 1.5 - 3.07 - 11.5 - - 9.0 h 58 Ce Cerium 139 1.5 - 3.39 - 12.7 - - 137.6 d 58 Ce Cerium 141 3.5 - 1.49 - 5.57 - - 32.50 d @@ -91,9 +91,9 @@ Atom Nucleus Name Mass Spin Abundance(%) Gamma Q(fm^2) FreqRatio Refsample Sampl 60 Nd Neodymium 143 3.5 12.2 -1.457 -63.0 5.45 - - - 60 Nd Neodymium 145 3.5 8.3 -0.898 -33.0 3.36 - - - 61 Pm Promethium 143 2.5 - 7.282 - 13.2 - - 265 d -61 Pm Promethium 147 3.5 - 3.53 74 21.0 - - 2.623 y +61 Pm Promethium 147 3.5 - 3.53 74.0 21.0 - - 2.623 y 62 Sm Samarium 147 3.5 14.99 -1.115 -25.9 4.17 - - 1.06e11 y -62 Sm Samarium 149 3.5 13.82 -0.9192 7.4 3.44 - - 1e16 y +62 Sm Samarium 149 3.5 13.82 -0.9192 7.5 3.44 - - 1e16 y 63 Eu Europium 151 2.5 47.81 6.6510 90.3 24.86 - - 5e18 y 63 Eu Europium 153 2.5 52.19 2.9369 241.2 10.98 - - 5.5e17 y 64 Gd Gadolinium 155 1.5 14.80 -0.82132 127.0 3.07 - - - @@ -103,8 +103,8 @@ Atom Nucleus Name Mass Spin Abundance(%) Gamma Q(fm^2) FreqRatio Refsample Sampl 66 Dy Dysprosium 163 2.5 24.90 1.289 264.8 4.82 - - - 67 Ho Holmium 165 3.5 100 5.710 358.0 21.34 - - - 68 Er Erbium 167 3.5 22.93 -0.77157 356.5 2.88 - - - -69 Tm Thullum 169 0.5 100 -2.218 - 8.29 - - - -70 Yb Ytterbium 171 0.5 14.28 4.7288 - 17.499306 Yb(η-C<sub>5</sub>Me<sub>5</sub>)<sub>2</sub>(THF)<sub>2</sub> 0.171 M in THF - +69 Tm Thullum 169 0.5 100 -2.218 0 8.29 - - - +70 Yb Ytterbium 171 0.5 14.28 4.7288 0 17.499306 Yb(η-C<sub>5</sub>Me<sub>5</sub>)<sub>2</sub>(THF)<sub>2</sub> 0.171 M in THF - 70 Yb Ytterbium 173 2.5 16.13 -1.3025 280.0 4.821 - - - 71 Lu Lutetium 175 3.5 97.41 3.0552 349.0 11.404 - - - 71 Lu Lutetium 176 7 2.59 2.1684 497.0 8.131 - - 3.73e10 y @@ -121,7 +121,7 @@ Atom Nucleus Name Mass Spin Abundance(%) Gamma Q(fm^2) FreqRatio Refsample Sampl 78 Pt Platinum 195 0.5 33.832 5.8385 0 21.496784 Na<sub>2</sub>PtCl<sub>6</sub> D<sub>2</sub>O, 1.2 M 7.3e18 y 79 Au Gold 197 1.5 100 0.473060 54.7 1.729 - - - 80 Hg Mercury 199 0.5 16.87 4.8457916 0 17.910822 Me<sub>2</sub>Hg neat - -80 Hg Mercury 201 1.5 13.18 -1.788769 38.6 6.611583 C(CH<sub>3</sub>)<sub>2</sub>Hg neat - +80 Hg Mercury 201 1.5 13.18 -1.788769 38.7 6.611583 C(CH<sub>3</sub>)<sub>2</sub>Hg neat - 81 Tl Thalium 203 0.5 29.524 15.5393338 0 57.123200 Tl(NO<sub>3</sub>)<sub>3</sub> - - 81 Tl Thalium 205 0.5 70.476 15.6921808 0 57.683838 Tl(NO<sub>3</sub>)<sub>3</sub> - - 82 Pb Lead 207 0.5 22.1 5.58046 0 20.920599 Me<sub>4</sub>Pb neat/C<sub>6</sub>D<sub>6</sub> >1.9e21 y @@ -132,13 +132,13 @@ Atom Nucleus Name Mass Spin Abundance(%) Gamma Q(fm^2) FreqRatio Refsample Sampl 87 Fr Francium 223 1.5 - 3.85 117 14.0 - - 22.0 m 88 Ra Radium 223 1.5 - 0.86369 121 3.2293 - - 11.43 d 88 Ra Radium 225 0.5 - 7.0290 0 26.275 - - 14.9 d -89 Ac Actinium 227 1.5 - 3.5 170 13 - - 21.77 y -90 Th Thorium 229 2.5 - 0.40 430 1.5 - - 7.93e3 y -91 Pa Protactinium 231 1.5 100 3.21 -172 12.0 - - 3.25e4 y +89 Ac Actinium 227 1.5 - 3.5 170.0 13 - - 21.77 y +90 Th Thorium 229 2.5 - 0.40 311.0 1.5 - - 7.93e3 y +91 Pa Protactinium 231 1.5 100 3.21 -172.0 12.0 - - 3.25e4 y 92 U Uranium 235 3.5 0.7200 -0.52 493.6 1.841400 UF<sub>6</sub> C<sub>6</sub>D<sub>6</sub>, φ = 10% 7.03e8 y 93 Np Neptunium 237 2.5 - 6.01 388.6 22.5 - - 2.14e6 y 94 Pu Plutonium 239 0.5 - 0.974 0 3.63 - - 2.410e4 y -95 Am Amercium 243 2.5 - 1.54 421 5.76 - - 7.37e3 y +95 Am Amercium 243 2.5 - 1.54 420.0 5.76 - - 7.37e3 y 96 Cm Curium 247 4.5 - 0.20 - 0.75 - - 1.56e7 y 97 Bk Berkelium - - - - - - - - - 98 Cf Californium - - - - - - - - - diff --git a/src/fitting.py b/src/fitting.py index 7abee08a..5de679c5 100644 --- a/src/fitting.py +++ b/src/fitting.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # @@ -968,6 +968,10 @@ def __init__(self, parent, rootwindow, isMain=True): self.isMain = isMain # display fitting buttons self.ticks = {key: [] for key in self.SINGLENAMES + self.MULTINAMES} self.entries = {key: [] for key in self.SINGLENAMES + self.MULTINAMES + self.EXTRANAMES} + + # sets a default position of MULTINAMES according to their position in definition list + self.MULTINAMES_ORDER = {self.MULTINAMES[i]:i for i in range(len(self.MULTINAMES))} + tmp = np.array(self.parent.data.shape(), dtype=int) tmp = np.delete(tmp, self.parent.axes) self.fitParamList = np.zeros(tmp, dtype=object) @@ -1141,7 +1145,7 @@ def setRMSD(self, val=None): self.rmsdEdit.setText(('%#.' + str(self.rootwindow.tabWindow.PRECIS) + 'g') % val) self.rmsdEdit.setCursorPosition(0) - def addMultiLabel(self, name, text, num, tooltip=""): + def addMultiLabel(self, name, text, tooltip=""): """ Creates a label for a parameter with multiple sites and adds it to frame3. @@ -1151,8 +1155,6 @@ def addMultiLabel(self, name, text, num, tooltip=""): Name of the parameter. text : str The text on the label. - num : int - The column to place the label widget. tootip : str A description of the parameter to be shown as tooltip. @@ -1163,13 +1165,14 @@ def addMultiLabel(self, name, text, num, tooltip=""): QLabel The label. """ + num = self.MULTINAMES_ORDER[name] tick = QtWidgets.QCheckBox('') tick.setChecked(self.DEFAULTS[name][1]) tick.stateChanged.connect(lambda state, self=self: self.changeAllTicks(state, name)) - self.frame3.addWidget(tick, 1, num) + self.frame3.addWidget(tick, 1, 2*num+1) # 1st widget is color widget label = wc.QLabel(text) label.setToolTip(tooltip) - self.frame3.addWidget(label, 1, num + 1) + self.frame3.addWidget(label, 1, 2*num+2) return tick, label def changeAllTicks(self, state, name): @@ -1932,8 +1935,46 @@ def genMask(self): if removeLimits['invert']: mask = np.abs(mask-1.0) return mask + def update_LorentzST_state(self): + """ disable/enable LorentzST entries and checkboxes if exist. + """ + if 'LorentzST' in self.MULTINAMES: + # OK there should be LorentzST entry + if 'satBool' in self.entries.keys(): + satBool = self.entries['satBool'][-1].isChecked() + if satBool : + #update the column labels and global check button + CB = self.frame3.layout().itemAt(2*self.MULTINAMES_ORDER['LorentzST']+1).widget() + CB.setEnabled(True) + #update the sites widgets + for site in range(self.FITNUM): + self.entries['LorentzST'][site].setEnabled(True) + self.ticks['LorentzST'][site].setEnabled(True) + else: + #update the column labels and global check button + CB = self.frame3.layout().itemAt(2*self.MULTINAMES_ORDER['LorentzST']+1).widget() + CB.setEnabled(False) + CB.setChecked(True) + for site in range(self.FITNUM): + self.entries['LorentzST'][site].setEnabled(False) + self.ticks['LorentzST'][site].setChecked(True) + self.ticks['LorentzST'][site].setEnabled(False) - + def populates_MULTINAMES_sites(self): + """ Add the QTextEdit and QCheckBox widgets to frame3 grid for each site + """ + for i in range(self.FITNUM): + colorbar = QtWidgets.QWidget() + colorbar.setMaximumWidth(5) + colorbar.setMinimumWidth(5) + colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") + self.frame3.addWidget(colorbar, i + 2, 0) + for name in self.MULTINAMES: + self.ticks[name].append(QtWidgets.QCheckBox('')) + self.frame3.addWidget(self.ticks[name][i], i + 2, 2 * self.MULTINAMES_ORDER[name] + 1) + self.entries[name].append(wc.FitQLineEdit(self, name, '')) + self.frame3.addWidget(self.entries[name][i], i + 2, 2 * self.MULTINAMES_ORDER[name] + 2) + ############################################################################## def lstSqrs(dataList, maskList, *args): @@ -2298,25 +2339,17 @@ def __init__(self, parent, rootwindow, isMain=True): self.numExp.addItems([str(x + 1) for x in range(self.FITNUM)]) self.numExp.currentIndexChanged.connect(self.changeNum) self.frame3.addWidget(self.numExp, 0, 0, 1, 2) - self.addMultiLabel("Coefficient", "Coefficient:", 1) - self.addMultiLabel("T", "T [s]:", 3) + self.addMultiLabel("Coefficient", "Coefficient:") + self.addMultiLabel("T", "T [s]:") self.xlog = QtWidgets.QCheckBox('x-log') self.xlog.stateChanged.connect(self.setLog) self.optframe.addWidget(self.xlog, 0, 0, QtCore.Qt.AlignTop) self.ylog = QtWidgets.QCheckBox('y-log') self.ylog.stateChanged.connect(self.setLog) self.optframe.addWidget(self.ylog, 1, 0, QtCore.Qt.AlignTop) - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j], ('%#.' + str(self.rootwindow.tabWindow.PRECIS) + 'g') % self.fitParamList[locList][self.MULTINAMES[j]][i][0])) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.populates_MULTINAMES_sites() + # WARNING entries line is different in populates_MULTINAMES_sites (what is the need) + #self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j], ('%#.' + str(self.rootwindow.tabWindow.PRECIS) + 'g') % self.fitParamList[locList][self.MULTINAMES[j]][i][0])) self.reset() def reset(self): @@ -2422,8 +2455,8 @@ def __init__(self, parent, rootwindow, isMain=True): self.numExp.addItems([str(x + 1) for x in range(self.FITNUM)]) self.numExp.currentIndexChanged.connect(self.changeNum) self.frame3.addWidget(self.numExp, 0, 0, 1, 2) - self.addMultiLabel('Coefficient', "Coefficient:", 1) - self.addMultiLabel('D', "D [m^2/s]:", 3) + self.addMultiLabel('Coefficient', "Coefficient:") + self.addMultiLabel('D', "D [m^2/s]:") self.frame3.setColumnStretch(20, 1) self.frame3.setAlignment(QtCore.Qt.AlignTop) self.xlog = QtWidgets.QCheckBox('x-log') @@ -2432,17 +2465,7 @@ def __init__(self, parent, rootwindow, isMain=True): self.ylog = QtWidgets.QCheckBox('y-log') self.ylog.stateChanged.connect(self.setLog) self.optframe.addWidget(self.ylog, 1, 0) - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j], '')) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.populates_MULTINAMES_sites() self.reset() def reset(self): @@ -2609,21 +2632,11 @@ def __init__(self, parent, rootwindow, isMain=True): self.numExp.addItems([str(x + 1) for x in range(self.FITNUM)]) self.numExp.currentIndexChanged.connect(self.changeNum) self.frame3.addWidget(self.numExp, 0, 0, 1, 2) - self.addMultiLabel("Position", "Position [" + self.axUnit + "]:", 1) - self.addMultiLabel("Integral", "Integral:", 3) - self.addMultiLabel("Lorentz", "Lorentz [Hz]:", 5) - self.addMultiLabel("Gauss", f"Gauss [{self.axUnit}]:", 7) - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j])) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.addMultiLabel("Position", "Position [" + self.axUnit + "]:") + self.addMultiLabel("Integral", "Integral:") + self.addMultiLabel("Lorentz", "Lorentz [Hz]:") + self.addMultiLabel("Gauss", f"Gauss [{self.axUnit}]:") + self.populates_MULTINAMES_sites() self.reset() def reset(self): @@ -2794,56 +2807,46 @@ def __init__(self, parent, rootwindow, isMain=True): else: axUnit = ['Hz', 'kHz', 'MHz'][self.parent.getAxType()] # Labels - self.addMultiLabel("Definition1", "", 1) - self.addMultiLabel("Definition2", "", 3) - self.addMultiLabel("Definition3", "", 5) + self.addMultiLabel("Definition1", "") + self.addMultiLabel("Definition2", "") + self.addMultiLabel("Definition3", "") self.label11 = wc.QLabel(u'δ' + '<sub>11</sub> [' + axUnit + '] :') self.label22 = wc.QLabel(u'δ' + '<sub>22</sub> [' + axUnit + '] :') self.label33 = wc.QLabel(u'δ' + '<sub>33</sub> [' + axUnit + '] :') - self.frame3.addWidget(self.label11, 1, 2) - self.frame3.addWidget(self.label22, 1, 4) - self.frame3.addWidget(self.label33, 1, 6) + self.frame3.addWidget(self.label11, 1, 2*self.MULTINAMES_ORDER['Definition1']+2) + self.frame3.addWidget(self.label22, 1, 2*self.MULTINAMES_ORDER['Definition2']+2) + self.frame3.addWidget(self.label33, 1, 2*self.MULTINAMES_ORDER['Definition3']+2) self.labelxx = wc.QLabel(u'δ' + '<sub>xx</sub> [' + axUnit + '] :') self.labelyy = wc.QLabel(u'δ' + '<sub>yy</sub> [' + axUnit + '] :') self.labelzz = wc.QLabel(u'δ' + '<sub>zz</sub> [' + axUnit + '] :') self.labelxx.hide() self.labelyy.hide() self.labelzz.hide() - self.frame3.addWidget(self.labelxx, 1, 2) - self.frame3.addWidget(self.labelyy, 1, 4) - self.frame3.addWidget(self.labelzz, 1, 6) + self.frame3.addWidget(self.labelxx, 1, 2*self.MULTINAMES_ORDER['Definition1']+2) + self.frame3.addWidget(self.labelyy, 1, 2*self.MULTINAMES_ORDER['Definition2']+2) + self.frame3.addWidget(self.labelzz, 1, 2*self.MULTINAMES_ORDER['Definition3']+2) self.labeliso = wc.QLabel(u'δ' + '<sub>iso</sub> [' + axUnit + '] :') self.labelaniso = wc.QLabel(u'δ' + '<sub>aniso</sub> [' + axUnit + '] :') self.labeleta = wc.QLabel(u'η:') self.labeliso.hide() self.labelaniso.hide() self.labeleta.hide() - self.frame3.addWidget(self.labeliso, 1, 2) - self.frame3.addWidget(self.labelaniso, 1, 4) - self.frame3.addWidget(self.labeleta, 1, 6) + self.frame3.addWidget(self.labeliso, 1, 2*self.MULTINAMES_ORDER['Definition1']+2) + self.frame3.addWidget(self.labelaniso, 1, 2*self.MULTINAMES_ORDER['Definition2']+2) + self.frame3.addWidget(self.labeleta, 1, 2*self.MULTINAMES_ORDER['Definition3']+2) self.labeliso2 = wc.QLabel(u'δ' + '<sub>iso</sub> [' + axUnit + '] :') self.labelspan = wc.QLabel(u'Ω [' + axUnit + '] :') self.labelskew = wc.QLabel(u'κ:') self.labeliso2.hide() self.labelspan.hide() self.labelskew.hide() - self.frame3.addWidget(self.labeliso2, 1, 2) - self.frame3.addWidget(self.labelspan, 1, 4) - self.frame3.addWidget(self.labelskew, 1, 6) - self.addMultiLabel("Integral", "Integral:", 7) - self.addMultiLabel("Lorentz", "Lorentz [Hz]:", 9, "Lorentzian broadening (transverse relaxation)") - self.addMultiLabel("Gauss", f"Gauss [{axUnit}]:", 11, "Gaussian broadening (FWHM of chemical shift distribution)") - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j])) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.frame3.addWidget(self.labeliso2, 1, 2*self.MULTINAMES_ORDER['Definition1']+2) + self.frame3.addWidget(self.labelspan, 1, 2*self.MULTINAMES_ORDER['Definition2']+2) + self.frame3.addWidget(self.labelskew, 1, 2*self.MULTINAMES_ORDER['Definition3']+2) + self.addMultiLabel("Integral", "Integral:") + self.addMultiLabel("Lorentz", "Lorentz [Hz]:", "Lorentzian broadening (transverse relaxation)") + self.addMultiLabel("Gauss", f"Gauss [{axUnit}]:", "Gaussian broadening (FWHM of chemical shift distribution)") + self.populates_MULTINAMES_sites() self.reset() def MASChange(self, MAStype): @@ -3153,25 +3156,17 @@ def __init__(self, parent, rootwindow, isMain=True): else: axUnit = ['Hz', 'kHz', 'MHz'][self.parent.getAxType()] # Labels - self.addMultiLabel("Position", u"Position [" + axUnit + "]:", 1, "Isotropic chemical shift") - self.addMultiLabel("Cq", u"C<sub>Q</sub> [MHz]:", 3, "Quadrupolar anisotopy") - self.addMultiLabel("eta", u"η:", 5, "Quadrupolar asymmetry (0-1)") - self.addMultiLabel("Integral", "Integral:", 7) - self.addMultiLabel("Lorentz", "Lorentz [Hz]:", 9, "Lorentzian broadening of central transition (transverse relaxation rate)") - self.addMultiLabel("Gauss", f"Gauss [{axUnit}]:", 11, "Gaussian broadening (FWHM of chemical shift distribution)") - self.addMultiLabel("LorentzST", "ST Lorentz [Hz]:", 13, "Lorentzian broadening of satellite transition(transverse relaxation rate)") - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j])) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.addMultiLabel("Position", u"Position [" + axUnit + "]:", "Isotropic chemical shift") + self.addMultiLabel("Cq", u"C<sub>Q</sub> [MHz]:", "Quadrupolar anisotopy") + self.addMultiLabel("eta", u"η:", "Quadrupolar asymmetry (0-1)") + self.addMultiLabel("Integral", "Integral:") + self.addMultiLabel("Lorentz", "Lorentz [Hz]:", "Lorentzian broadening of central transition (transverse relaxation rate)") + self.addMultiLabel("Gauss", f"Gauss [{axUnit}]:", "Gaussian broadening (FWHM of chemical shift distribution)") + self.addMultiLabel("LorentzST", "ST Lorentz [Hz]:", "Lorentzian broadening of satellite transition(transverse relaxation rate)") + self.populates_MULTINAMES_sites() self.reset() + self.entries['satBool'][-1].stateChanged.connect(self.update_LorentzST_state) + self.update_LorentzST_state() def MASChange(self, MAStype): """ @@ -3397,9 +3392,9 @@ def __init__(self, parent, rootwindow, isMain=True): else: axUnit = ['Hz', 'kHz', 'MHz'][self.parent.getAxType()] # Labels - self.addMultiLabel("Definition1", "", 1, "CSA tensor discontinuity 1") - self.addMultiLabel("Definition2", "", 3, "CSA tensor discontinuity 2") - self.addMultiLabel("Definition3", "", 5, "CSA tensor discontinuity 3") + self.addMultiLabel("Definition1", "", "CSA tensor discontinuity 1") + self.addMultiLabel("Definition2", "", "CSA tensor discontinuity 2") + self.addMultiLabel("Definition3", "", "CSA tensor discontinuity 3") self.label11 = wc.QLabel(u'δ' + '<sub>11</sub> [' + axUnit + '] :') self.label22 = wc.QLabel(u'δ' + '<sub>22</sub> [' + axUnit + '] :') self.label33 = wc.QLabel(u'δ' + '<sub>33</sub> [' + axUnit + '] :') @@ -3433,27 +3428,19 @@ def __init__(self, parent, rootwindow, isMain=True): self.frame3.addWidget(self.labeliso2, 1, 2) self.frame3.addWidget(self.labelspan, 1, 4) self.frame3.addWidget(self.labelskew, 1, 6) - self.addMultiLabel("Cq", u"C<sub>Q</sub> [MHz]:", 7, "Quadrupolar anisotropy") - self.addMultiLabel("eta", u"η:", 9, "Quadrupolar asymmetry") - self.addMultiLabel("Alpha", u"α [deg]:", 11, "euler angle defining CSA orientation in Quad Frame") - self.addMultiLabel("Beta", u"β [deg]:", 13, "euler angle defining CSA orientation in Quad Frame") - self.addMultiLabel("Gamma", u"γ [deg]:", 15, "euler angle defining CSA orientation in Quad Frame") - self.addMultiLabel("Integral", "Integral:", 17) - self.addMultiLabel("Lorentz", "Lorentz [Hz]:", 19, "Lorentzian broadening of central transition (transverse relaxation rate)") - self.addMultiLabel("Gauss", f"Gauss [{axUnit}]:", 21, "Gaussian broadening (FWHM of chemical shift distribution)") - self.addMultiLabel("LorentzST", "LorentzST [Hz]:", 23, "Lorentzian broadening of satellite transitions (transverse relaxation rate)") - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j])) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.addMultiLabel("Cq", u"C<sub>Q</sub> [MHz]:", "Quadrupolar anisotropy") + self.addMultiLabel("eta", u"η:", "Quadrupolar asymmetry") + self.addMultiLabel("Alpha", u"α [deg]:", "euler angle defining CSA orientation in Quad Frame") + self.addMultiLabel("Beta", u"β [deg]:", "euler angle defining CSA orientation in Quad Frame") + self.addMultiLabel("Gamma", u"γ [deg]:", "euler angle defining CSA orientation in Quad Frame") + self.addMultiLabel("Integral", "Integral:") + self.addMultiLabel("Lorentz", "Lorentz [Hz]:", "Lorentzian broadening of central transition (transverse relaxation rate)") + self.addMultiLabel("Gauss", f"Gauss [{axUnit}]:", "Gaussian broadening (FWHM of chemical shift distribution)") + self.addMultiLabel("LorentzST", "LorentzST [Hz]:", "Lorentzian broadening of satellite transitions (transverse relaxation rate)") + self.populates_MULTINAMES_sites() self.reset() + self.entries['satBool'][-1].stateChanged.connect(self.update_LorentzST_state) + self.update_LorentzST_state() def MASChange(self, MAStype): """ @@ -3791,6 +3778,10 @@ def __init__(self, parent, mqmas=False): self.saveButton = QtWidgets.QPushButton("Save", parent=self) self.saveButton.clicked.connect(self.saveDist) grid.addWidget(self.saveButton, 14, 5) + self.pqCheckBox = QtWidgets.QCheckBox('Plot PQ') + self.pqCheckBox.toggled.connect(self.plotDist) + grid.addWidget(self.pqCheckBox, 14, 6) + grid.addWidget(wc.QLabel("Site:"), 14, 2) self.site = QtWidgets.QSpinBox() self.site.setMinimum(1) @@ -3895,10 +3886,38 @@ def plotDist(self): self.czjzek = Czjzek.czjzekIntensities(sigma, d, cq.flatten(), eta.flatten()) else: self.czjzek = Czjzek.czjzekIntensities(sigma, d, cq.flatten(), eta.flatten(), cq0, eta0) + self.czjzek = self.czjzek.reshape(etasteps, cqsteps) - self.ax.contour(cq.transpose(), eta.transpose(), self.czjzek.transpose(), 10) - self.ax.set_xlabel(u"C$_Q$ [MHz]") - self.ax.set_ylabel(u"η") + # Calculate average and peak CQ and PQ values + # peak CQ is obtained from DMFit + PQs = cq * np.sqrt(1 + eta/3) + PQavg = np.average(PQs, None, self.czjzek) + CQavg = np.average(cq, None, self.czjzek) + indices = np.unravel_index(self.czjzek.argmax(), self.czjzek.shape) + peakCQ = float(cq[indices]) + peakPQ = float(PQs[indices]) + if self.pqCheckBox.isChecked(): + self.ax.contour(PQs.transpose(), eta.transpose(), self.czjzek.transpose(), 15) + self.ax.set_xlabel(u"P$_Q$ [MHz]") + self.ax.scatter(peakPQ, eta[indices], color='w', edgecolor = 'k') + self.ax.text(peakPQ * 0.92, eta[indices] * 0.95, '$P_{Q,peak}$', color='k', size = 8) + self.ax.axvline(x=PQavg, color='k') + self.ax.text(PQavg * 1.025, 0.5, '$\overline{P_Q}$', color='k', size = 8) + else: + self.ax.contour(cqArray, etaArray, self.czjzek, 15) + self.ax.set_xlabel(u"C$_Q$ [MHz]") + self.ax.scatter(peakCQ, eta[indices], color='w', edgecolor = 'b') + self.ax.text(peakCQ * 0.92, eta[indices] * 0.95, '$C_{Q,peak}$', color='b', size = 8) + self.ax.axvline(x=CQavg, color='b') + self.ax.text(CQavg * 1.025, 0.5, '$\overline{C_Q}$', color='b', size = 8) + + self.ax.text(0, 1.075, '$\overline{P_Q}$ = ' + str(np.round(PQavg, decimals=3)) + + ' MHz' + ' $P_{Q,peak}$ = ' + str(np.round(peakPQ, decimals=3)) + + ' MHz', color='k', size = 9) + self.ax.text(0, 1.025, '$\overline{C_Q}$ = ' + str(np.round(CQavg, decimals=3)) + + ' MHz' + ' $C_{Q,peak}$ = ' + str(np.round(peakCQ, decimals=3)) + + ' MHz', color='b', size = 9) + self.canvas.draw() def saveDist(self): @@ -3979,6 +3998,7 @@ def generate(self, *args): self.father.angle = self.angleEntry.text() self.father.numssb = self.numssbEntry.value() self.father.satBool = self.satBoolEntry.isChecked() + # satBool else: self.father.I = self.Ientry.currentIndex() + 1.5 inp = safeEval(self.cqmax.text(), Type='FI') @@ -4127,24 +4147,14 @@ def __init__(self, parent, rootwindow, isMain=True): axUnit = 'ppm' else: axUnit = ['Hz', 'kHz', 'MHz'][self.parent.getAxType()] - self.addMultiLabel("Position", "Pos [" + axUnit + "]:", 1, "Isotropic chemical shift") - self.addMultiLabel("Sigma", u"σ [MHz]:", 3, "Quadrupolar anisotropy variance: most probable (average) Cq is 2*σ") - self.addMultiLabel("Cq0", u"C<sub>Q</sub>0 [MHz]:", 5) - self.addMultiLabel("eta0", u"η0:", 7) - self.addMultiLabel("Integral", "Integral:", 9) - self.addMultiLabel("Lorentz", "Lorentz [Hz]:", 11, "Lorentzian broadening (transverse relaxation rate)") - self.addMultiLabel("Gauss", f"Gauss [{axUnit}]:", 13, "Gaussian broadening (FWHM of chemical shift distribution") - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j])) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.addMultiLabel("Position", "Pos [" + axUnit + "]:", "Isotropic chemical shift") + self.addMultiLabel("Sigma", u"σ [MHz]:", "Quadrupolar anisotropy variance: most probable (average) Cq is 2*σ") + self.addMultiLabel("Cq0", u"C<sub>Q</sub>0 [MHz]:") + self.addMultiLabel("eta0", u"η0:") + self.addMultiLabel("Integral", "Integral:") + self.addMultiLabel("Lorentz", "Lorentz [Hz]:", "Lorentzian broadening (transverse relaxation rate)") + self.addMultiLabel("Gauss", f"Gauss [{axUnit}]:", "Gaussian broadening (FWHM of chemical shift distribution") + self.populates_MULTINAMES_sites() self.reset() def reset(self): @@ -4183,19 +4193,31 @@ def changeType(self, index): Czjzek fitting type (0=regular, 1=extended). """ if index == 0: + #update the column labels and global check button + CB = self.frame3.layout().itemAt(2*self.MULTINAMES_ORDER['Cq0']+1).widget() + CB.setEnabled(False) + CB.setChecked(True) + CB = self.frame3.layout().itemAt(2*self.MULTINAMES_ORDER['eta0']+1).widget() + CB.setEnabled(False) + CB.setChecked(True) for i in range(self.FITNUM): self.entries["Cq0"][i].setEnabled(False) - self.entries['eta0'][i].setEnabled(False) + self.entries["eta0"][i].setEnabled(False) self.ticks["Cq0"][i].setChecked(True) - self.ticks['eta0'][i].setChecked(True) + self.ticks["eta0"][i].setChecked(True) self.ticks["Cq0"][i].setEnabled(False) - self.ticks['eta0'][i].setEnabled(False) + self.ticks["eta0"][i].setEnabled(False) elif index == 1: + #update the column labels and global check button + CB = self.frame3.layout().itemAt(2*self.MULTINAMES_ORDER['Cq0']+1).widget() + CB.setEnabled(True) + CB = self.frame3.layout().itemAt(2*self.MULTINAMES_ORDER['eta0']+1).widget() + CB.setEnabled(True) for i in range(self.FITNUM): self.entries["Cq0"][i].setEnabled(True) - self.entries['eta0'][i].setEnabled(True) + self.entries["eta0"][i].setEnabled(True) self.ticks["Cq0"][i].setEnabled(True) - self.ticks['eta0'][i].setEnabled(True) + self.ticks["eta0"][i].setEnabled(True) def createCzjzekPrefWindow(self, *args): CzjzekPrefWindow(self) @@ -4407,6 +4429,7 @@ def analyseScript(self, inFile): for name in self.MULTINAMES: self.DEFAULTS[name] = [0.0, False] self.MULTINAMES.extend(["Integral", "Lorentz", "Gauss"]) + self.MULTINAMES_ORDER = {self.MULTINAMES[i]:i for i in range(len(self.MULTINAMES))} self.labels = {"Offset": [wc.QLabel("Offset:")], "Multiplier": [wc.QLabel("Multiplier:")]} self.ticks = {"Offset": [], "Multiplier": []} self.entries = {"Offset": [], "Multiplier": []} @@ -4422,14 +4445,10 @@ def analyseScript(self, inFile): self.frame2.addWidget(self.entries["Multiplier"][-1], 4, 1) for i in range(len(self.MULTINAMES)): name = self.MULTINAMES[i] - self.labels[name] = self.addMultiLabel(name, name, 2*i+2) + self.labels[name] = self.addMultiLabel(name, name) self.ticks[name] = [] self.entries[name] = [] - for j in range(self.FITNUM): - self.ticks[name].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[name][j], 2+j, 2*i+2) - self.entries[name].append(wc.FitQLineEdit(self, name)) - self.frame3.addWidget(self.entries[name][j], 2+j, 2*i+3) + self.populates_MULTINAMES_sites() self.reset() def extraParamToFile(self): @@ -4556,19 +4575,16 @@ def functionInputSetup(self): self.MULTINAMES = [e[1:-1] for e in matches] for name in self.MULTINAMES: self.DEFAULTS[name] = [0.0, False] + self.MULTINAMES_ORDER = {self.MULTINAMES[i]:i for i in range(len(self.MULTINAMES))} self.labels = {} self.ticks = {} self.entries = {} for i in range(len(self.MULTINAMES)): name = self.MULTINAMES[i] - self.labels[name] = self.addMultiLabel(name, name, 2*i) + self.labels[name] = self.addMultiLabel(name, name) self.ticks[name] = [] self.entries[name] = [] - for j in range(self.FITNUM): - self.ticks[name].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[name][j], 2+j, 2*i) - self.entries[name].append(wc.FitQLineEdit(self, name)) - self.frame3.addWidget(self.entries[name][j], 2+j, 2*i+1) + self.populates_MULTINAMES_sites() self.reset() def extraParamToFile(self): @@ -4810,26 +4826,16 @@ def __init__(self, parent, rootwindow, isMain=True): else: axUnit = ['Hz', 'kHz', 'MHz'][self.parent.getAxType()] # Labels - self.addMultiLabel("Position", u"Position [" + axUnit + "]:", 1, "Isotropic chemical shift") - self.addMultiLabel("Gauss", f"σ<sub>CS</sub> [{axUnit}]:", 3, "Gaussian broadening (FWHM of chemical shift distribution)") - self.addMultiLabel("Cq", u"C<sub>Q</sub> [MHz]:", 5, "Quadrupolar anisotropy") - self.addMultiLabel("eta", u"η:", 7, "Quadrupolar asymmetry") - self.addMultiLabel("Integral", "Integral:", 9) - self.addMultiLabel("Lorentz", "Lorentz 2 [Hz]:", 11, "Lorentzian broadening (transverse relaxation rate) in direct dimension") - self.addMultiLabel("Lorentz1", "Lorentz 1 [Hz]:", 13, "Lorentzian broadening (transverse relaxation rate) in indirect dimension") -# self.addMultiLabel("Gauss2", "Gauss 2 [Hz]:", 15) -# self.addMultiLabel("Gauss1", "Gauss 1 [Hz]:", 17) - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j])) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.addMultiLabel("Position", u"Position [" + axUnit + "]:", "Isotropic chemical shift") + self.addMultiLabel("Gauss", f"σ<sub>CS</sub> [{axUnit}]:", "Gaussian broadening (FWHM of chemical shift distribution)") + self.addMultiLabel("Cq", u"C<sub>Q</sub> [MHz]:", "Quadrupolar anisotropy") + self.addMultiLabel("eta", u"η:", "Quadrupolar asymmetry") + self.addMultiLabel("Integral", "Integral:") + self.addMultiLabel("Lorentz", "Lorentz 2 [Hz]:", "Lorentzian broadening (transverse relaxation rate) in direct dimension") + self.addMultiLabel("Lorentz1", "Lorentz 1 [Hz]:", "Lorentzian broadening (transverse relaxation rate) in indirect dimension") +# self.addMultiLabel("Gauss2", "Gauss 2 [Hz]:") +# self.addMultiLabel("Gauss1", "Gauss 1 [Hz]:") + self.populates_MULTINAMES_sites() self.reset() def reset(self): @@ -5067,27 +5073,17 @@ def __init__(self, parent, rootwindow, isMain=True): axUnit = 'ppm' else: axUnit = ['Hz', 'kHz', 'MHz'][self.parent.getAxType()] - self.addMultiLabel("Position", "Pos [" + axUnit + "]:", 1, "Isotropic chemical shift") - self.addMultiLabel("Gauss", f"σ<sub>CS</sub> [{axUnit}]:", 3, "Gaussian broadening (FWHM of chemical shift distribution)") - self.addMultiLabel("Sigma", u"σ<sub>Q<sub> [MHz]:", 5, "Quadrupolar anisotropy variance: most probable (average) Cq is 2*σ") - self.addMultiLabel("Cq0", u"C<sub>Q</sub>0 [MHz]:", 7) - self.addMultiLabel("eta0", u"η0:", 9) - self.addMultiLabel("Integral", "Integral:", 11) - self.addMultiLabel("Lorentz", "Lorentz 2 [Hz]:", 13, "Lorentzian broadening (transverse relaxation rate) in direct dimension") - self.addMultiLabel("Lorentz1", "Lorentz 1 [Hz]:", 15, "Lorentzian broadening (transverse relaxation rate) in indirect dimension") -# self.addMultiLabel("Gauss2", "Gauss 2 [Hz]:", 17) -# self.addMultiLabel("Gauss1", "Gauss 1 [Hz]:", 19) - for i in range(self.FITNUM): - colorbar = QtWidgets.QWidget() - colorbar.setMaximumWidth(5) - colorbar.setMinimumWidth(5) - colorbar.setStyleSheet(f"QWidget {{ background-color : {self.fit_color_list[i%len(self.fit_color_list)]};}}") - self.frame3.addWidget(colorbar, i + 2, 0) - for j in range(len(self.MULTINAMES)): - self.ticks[self.MULTINAMES[j]].append(QtWidgets.QCheckBox('')) - self.frame3.addWidget(self.ticks[self.MULTINAMES[j]][i], i + 2, 2 * j + 1) - self.entries[self.MULTINAMES[j]].append(wc.FitQLineEdit(self, self.MULTINAMES[j])) - self.frame3.addWidget(self.entries[self.MULTINAMES[j]][i], i + 2, 2 * j + 2) + self.addMultiLabel("Position", "Pos [" + axUnit + "]:", "Isotropic chemical shift") + self.addMultiLabel("Gauss", f"σ<sub>CS</sub> [{axUnit}]:", "Gaussian broadening (FWHM of chemical shift distribution)") + self.addMultiLabel("Sigma", u"σ<sub>Q<sub> [MHz]:", "Quadrupolar anisotropy variance: most probable (average) Cq is 2*σ") + self.addMultiLabel("Cq0", u"C<sub>Q</sub>0 [MHz]:") + self.addMultiLabel("eta0", u"η0:") + self.addMultiLabel("Integral", "Integral:") + self.addMultiLabel("Lorentz", "Lorentz 2 [Hz]:", "Lorentzian broadening (transverse relaxation rate) in direct dimension") + self.addMultiLabel("Lorentz1", "Lorentz 1 [Hz]:", "Lorentzian broadening (transverse relaxation rate) in indirect dimension") +# self.addMultiLabel("Gauss2", "Gauss 2 [Hz]:") +# self.addMultiLabel("Gauss1", "Gauss 1 [Hz]:") + self.populates_MULTINAMES_sites() self.reset() def reset(self): diff --git a/src/functions.py b/src/functions.py index d069c57e..5565ca54 100644 --- a/src/functions.py +++ b/src/functions.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/hypercomplex.py b/src/hypercomplex.py index 24092539..6d93a170 100644 --- a/src/hypercomplex.py +++ b/src/hypercomplex.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/loadIsotopes.py b/src/loadIsotopes.py index 2c1baa6c..6c3541d4 100644 --- a/src/loadIsotopes.py +++ b/src/loadIsotopes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/nmrTable.py b/src/nmrTable.py index 8bb813ad..f057773c 100644 --- a/src/nmrTable.py +++ b/src/nmrTable.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/nus.py b/src/nus.py index 84174349..da8777fb 100644 --- a/src/nus.py +++ b/src/nus.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/reimplement.py b/src/reimplement.py index 9347a95f..b8177ed2 100644 --- a/src/reimplement.py +++ b/src/reimplement.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/safeEval.py b/src/safeEval.py index 32d831cc..11ed68c5 100644 --- a/src/safeEval.py +++ b/src/safeEval.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/saveFigure.py b/src/saveFigure.py index 680bb441..73c85489 100644 --- a/src/saveFigure.py +++ b/src/saveFigure.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # @@ -130,8 +130,8 @@ def __init__(self, father, oldMainWindow): self.dpiEntry = QtWidgets.QSpinBox() self.dpiEntry.setSingleStep(1) self.dpiEntry.setMinimum(0) - self.dpiEntry.setMaximum(1e6) - self.dpiEntry.setValue(self.fig.dpi) + self.dpiEntry.setMaximum(int(1e6)) + self.dpiEntry.setValue(int(self.fig.dpi)) self.dpiEntry.valueChanged.connect(self.updatePlot) self.dimensionsFrame.addWidget(self.dpiEntry, 2, 1) self.dimensionsGroup.setLayout(self.dimensionsFrame) diff --git a/src/simFunctions.py b/src/simFunctions.py index 038a0bfd..e8ddd4bb 100644 --- a/src/simFunctions.py +++ b/src/simFunctions.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/specIO.py b/src/specIO.py index 37af6434..d3f71e56 100644 --- a/src/specIO.py +++ b/src/specIO.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # @@ -1961,7 +1961,7 @@ def loadJCAMP(filePath): masterData = sc.Spectrum(spectDat, (filePath, None), [freq], [sw], spec=[True], ref=[None]) return masterData -def saveASCIIFile(filePath, spectrum, axMult=1): +def saveASCIIFile(filePath, spectrum, axMult=1, delim = '\t'): """ Save to ASCII format data. @@ -1985,7 +1985,7 @@ def saveASCIIFile(filePath, spectrum, axMult=1): splitdata[:, line * 2] = np.real(data[:, line]) splitdata[:, line * 2 + 1] = np.imag(data[:, line]) data = np.concatenate((axis, splitdata), axis=1) - np.savetxt(filePath, data, delimiter='\t') + np.savetxt(filePath, data, delimiter=delim) def loadAscii(filePath, asciiInfo=None): """ diff --git a/src/spectrum.py b/src/spectrum.py index 57ba5854..5e2f730f 100644 --- a/src/spectrum.py +++ b/src/spectrum.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/spectrumFrame.py b/src/spectrumFrame.py index 7f7ac896..46fab746 100644 --- a/src/spectrumFrame.py +++ b/src/spectrumFrame.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/ssNake.py b/src/ssNake.py index 3a9e1d5c..b069cada 100644 --- a/src/ssNake.py +++ b/src/ssNake.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # @@ -96,7 +96,7 @@ def import_lib(name, nameAs, className, splashStep): np.set_printoptions(threshold=sys.maxsize) QtCore.QLocale.setDefault(QtCore.QLocale('en_US')) -VERSION = 'v1.4b' +VERSION = 'v1.4' # Required library version NPVERSION = '1.11.0' MPLVERSION = '1.5.0' @@ -411,6 +411,7 @@ def initToolbar(self): ['File --> Export --> Figure', self.savefigAct], ['File --> Export --> Simpson', self.saveSimpsonAct], ['File --> Export --> ASCII (1D/2D)', self.saveASCIIAct], + ['File --> Export --> CSV (1D/2D)', self.saveCSVAct], ['File --> Preferences', self.preferencesAct], ['File --> Quit', self.quitAct], ['Workspaces --> Duplicate', self.newAct], @@ -554,14 +555,16 @@ def initMenu(self): self.saveSimpsonAct.setToolTip('Export as Simpson File') self.saveASCIIAct = self.exportmenu.addAction(QtGui.QIcon(IconDirectory + 'ASCII.png'), 'ASCII (1D/2D)', self.saveASCIIFile) self.saveASCIIAct.setToolTip('Save as ASCII Text File') + self.saveCSVAct = self.exportmenu.addAction(QtGui.QIcon(IconDirectory + 'CSV.png'),'CSV (1D/2D)', self.saveCSVFile) + self.saveCSVAct.setToolTip('Save as CSV Text File') self.preferencesAct = self.filemenu.addAction(QtGui.QIcon(IconDirectory + 'preferences.png'), '&Preferences', lambda: PreferenceWindow(self)) self.preferencesAct.setToolTip('Open Preferences Window') self.quitAct = self.filemenu.addAction(QtGui.QIcon(IconDirectory + 'quit.png'), '&Quit', self.fileQuit, QtGui.QKeySequence.Quit) self.quitAct.setToolTip('Close ssNake') self.saveActList = [self.saveAct, self.saveMatAct] - self.exportActList = [self.savefigAct, self.saveSimpsonAct, self.saveASCIIAct] + self.exportActList = [self.savefigAct, self.saveSimpsonAct, self.saveASCIIAct,self.saveCSVAct] self.fileActList = [self.openAct, self.saveAct, self.saveMatAct, - self.savefigAct, self.saveSimpsonAct, self.saveASCIIAct, + self.savefigAct, self.saveSimpsonAct, self.saveASCIIAct,self.saveCSVAct, self.combineLoadAct, self.preferencesAct, self.quitAct] # Workspaces menu self.workspacemenu = QtWidgets.QMenu('&Workspaces', self) @@ -1556,6 +1559,9 @@ def saveSimpsonFile(self): def saveASCIIFile(self): self.mainWindow.get_mainWindow().saveASCIIFile() + def saveCSVFile(self): + self.mainWindow.get_mainWindow().saveCSVFile() + def saveJSONFile(self): self.mainWindow.get_mainWindow().saveJSONFile() @@ -1822,6 +1828,19 @@ def saveASCIIFile(self): axMult = self.current.getCurrentAxMult() io.saveASCIIFile(name, self.masterData, axMult) + def saveCSVFile(self): + if self.masterData.ndim() > 2: + raise SsnakeException('Saving to CSV format only allowed for 1D and 2D data!') + WorkspaceName = self.father.workspaceNames[self.father.workspaceNum] # Set name of file to be saved to workspace name to start + name = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File', self.father.lastLocation + os.path.sep + WorkspaceName + '.txt', 'CSV file (*.csv)') + if isinstance(name, tuple): + name = name[0] + if not name: + return + self.father.lastLocation = os.path.dirname(name) # Save used path + axMult = self.current.getCurrentAxMult() + io.saveASCIIFile(name, self.masterData, axMult,delim = ',') + def reloadLast(self): self.current.reload() self.updAllFrames() @@ -7173,7 +7192,7 @@ def __init__(self, parent): pythonVersion = pythonVersion[:pythonVersion.index(' ')] from scipy import __version__ as scipyVersion self.text.setText('<p><b>ssNake ' + VERSION + '</b></p>' + - '<p>Copyright (©) 2016–2021 Bas van Meerten & Wouter Franssen</p>' + '<p>Email: <a href="mailto:ssnake@science.ru.nl" >ssnake@science.ru.nl</a></p>' + + '<p>Copyright (©) 2016–2022 Bas van Meerten & Wouter Franssen</p>' + '<p>Email: <a href="mailto:ssnake@science.ru.nl" >ssnake@science.ru.nl</a></p>' + '<p>Publication: <a href="https://doi.org/10.1016/j.jmr.2019.02.006" >https://doi.org/10.1016/j.jmr.2019.02.006</a></p>' + '<b>Library versions</b>:<br>Python ' + pythonVersion + '<br>numpy ' + np.__version__ + '<br>SciPy ' + scipyVersion + @@ -7182,7 +7201,9 @@ def __init__(self, parent): '<br>Qt ' + QtCore.QT_VERSION_STR) self.thanks = QtWidgets.QTextEdit(self) self.thanks.setReadOnly(True) - self.thanks.setHtml('<p><b>The ssNake team wishes to thank:</b></p>Prof. Arno Kentgens<br>Koen Tijssen<br>Ole Brauckmann<br>Merijn Blaakmeer<br>Vincent Breukels<br>Ernst van Eck<br>Fleur van Zelst<br>Sander Lambregts<br>Dr. Andreas Brinkmann') + self.thanks.setHtml('<p><b>The ssNake team wishes to thank:</b></p>Prof. Arno Kentgens<br>Koen Tijssen<br>' + + 'Ole Brauckmann<br>Merijn Blaakmeer<br>Vincent Breukels<br>Ernst van Eck<br>Fleur van Zelst<br>' + + 'Sander Lambregts<br>Dr. Andreas Brinkmann<br>Julien Trébosc<br>Henrik Bradtmüller') self.tabs.addTab(self.text, 'Version') self.tabs.addTab(self.thanks, 'Thanks') self.tabs.addTab(self.license, 'License') @@ -7560,17 +7581,21 @@ class dipolarDistanceWindow(wc.ToolWindow): NAME = "Dipolar Distance Calculation" RESIZABLE = True MENUDISABLE = False + Ioptions = ['1/2','1', '3/2', '2', '5/2', '3', '7/2', '4', '9/2', '5', '6', '7'] + Ivalues = [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0] def __init__(self, parent): super(dipolarDistanceWindow, self).__init__(parent) - self.comGroup = QtWidgets.QGroupBox("Gyromagnetic Ratios:") + # Nuclei group + self.comGroup = QtWidgets.QGroupBox("Gyromagnetic Ratios and Spin Quantum Numbers:") self.comFrame = QtWidgets.QGridLayout() gamma1label = wc.QLabel(u'γ<sub>1</sub> [10<sup>7</sup> rad/s/T]:') self.comFrame.addWidget(gamma1label, 0, 0) gammaindex = [x for (x, val) in enumerate(ISOTOPES['gamma']) if val is not None] self.gammaValues = [0.0] + [val for (x, val) in enumerate(ISOTOPES['gamma']) if x in gammaindex] - self.names = ['User'] + [val for (x, val) in enumerate(ISOTOPES['formatName']) if x in gammaindex] + self.spinValues = [0.0] + [val for (x, val) in enumerate(ISOTOPES['spin']) if x in gammaindex] + self.names = ['User'] + [val for (x, val) in enumerate(ISOTOPES['formatName']) if x in gammaindex] self.gamma1Drop = QtWidgets.QComboBox() self.gamma1Drop.addItems(self.names) self.gamma1Drop.currentIndexChanged.connect(self.setGamma1) @@ -7583,39 +7608,93 @@ def __init__(self, parent): self.gamma1.setMinimumWidth(100) self.gamma1.textEdited.connect(self.gamma1Changed) self.comFrame.addWidget(self.gamma1, 2, 0) + spin1label = wc.QLabel(u'<i>I</i><sub>1</sub>:') + self.comFrame.addWidget(spin1label, 3, 0) + + self.spin1 = QtWidgets.QComboBox() + self.spin1.addItems(self.Ioptions) + self.spin1.currentIndexChanged.connect(self.spin1Changed) + self.comFrame.addWidget(self.spin1, 4, 0) gamma2label = wc.QLabel(u'γ<sub>2</sub> [10<sup>7</sup> rad/s/T]:') self.comFrame.addWidget(gamma2label, 0, 1) self.gamma2 = wc.QLineEdit("0.0") self.gamma2.setMinimumWidth(100) self.gamma2.textEdited.connect(self.gamma2Changed) self.comFrame.addWidget(self.gamma2, 2, 1) + spin2label = wc.QLabel(u'<i>I</i><sub>2</sub>:') + self.comFrame.addWidget(spin2label, 3, 1) + self.spin2 = QtWidgets.QComboBox() + self.spin2.addItems(self.Ioptions) + self.spin2.currentIndexChanged.connect(self.spin2Changed) + self.comFrame.addWidget(self.spin2, 4, 1) self.comGroup.setLayout(self.comFrame) - self.grid.addWidget(self.comGroup, 1, 0, 1, 3) + #addWidget - fromRow, fromColumn, rowSpan, columnSpan + self.grid.addWidget(self.comGroup, 0, 0, 5, 2) + # Distance group self.distanceGroup = QtWidgets.QGroupBox("Distance:") self.distanceFrame = QtWidgets.QGridLayout() distancelabel = wc.QLabel(u'r [Å]') - self.distanceFrame.addWidget(distancelabel, 3, 1) + self.distanceFrame.addWidget(distancelabel, 0, 1) distanceGO = QtWidgets.QPushButton("Go") - self.distanceFrame.addWidget(distanceGO, 4, 0) + self.distanceFrame.addWidget(distanceGO, 1, 0) distanceGO.clicked.connect(lambda: self.Calc(0)) self.distance = wc.QLineEdit("0") self.distance.setMinimumWidth(100) - self.distanceFrame.addWidget(self.distance, 4, 1) + self.distanceFrame.addWidget(self.distance, 1, 1) self.distanceGroup.setLayout(self.distanceFrame) - self.grid.addWidget(self.distanceGroup, 3, 0, 1, 2) - + self.grid.addWidget(self.distanceGroup, 5, 0, 2, 2) + # Dipolar coupling group self.dipolarGroup = QtWidgets.QGroupBox("Dipolar Coupling:") self.dipolarFrame = QtWidgets.QGridLayout() dipolarlabel = wc.QLabel(u'D [kHz]') - self.dipolarFrame.addWidget(dipolarlabel, 3, 1) + self.dipolarFrame.addWidget(dipolarlabel, 0, 1) dipolarGO = QtWidgets.QPushButton("Go") - self.dipolarFrame.addWidget(dipolarGO, 4, 0) + self.dipolarFrame.addWidget(dipolarGO, 1, 0) dipolarGO.clicked.connect(lambda: self.Calc(1)) self.dipolar = wc.QLineEdit("0") self.dipolar.setMinimumWidth(100) - self.dipolarFrame.addWidget(self.dipolar, 4, 1) + self.dipolarFrame.addWidget(self.dipolar, 1, 1) + self.grid.addWidget(self.dipolarGroup, 7, 0, 2, 2) self.dipolarGroup.setLayout(self.dipolarFrame) - self.grid.addWidget(self.dipolarGroup, 4, 0, 1, 2) + # Second-moment group + self.M2Group = QtWidgets.QGroupBox(u"Second Moments [10⁶ rad²/s²]:") + self.M2Frame = QtWidgets.QGridLayout() + + self.M2hetGO = QtWidgets.QPushButton("Go") + self.M2Frame.addWidget(self.M2hetGO, 1, 0) + self.M2hetGO.clicked.connect(lambda: self.Calc(2)) + #M2label1 = wc.QLabel(u'M<sub>2</sub>') + #self.M2Frame.addWidget(M2label1, 0, 1) + self.M2label = wc.QLabel(u'Heteronuclear') + self.M2Frame.addWidget(self.M2label, 0, 0) + self.M2 = wc.QLineEdit("0") + self.M2.setMinimumWidth(100) + self.M2Frame.addWidget(self.M2, 1, 1) + # self.HetroWidgets = [self.M2hetGO,self.M2hetlabel,self.M2het] + + # self.M2homGO = QtWidgets.QPushButton("Go") + # self.M2Frame.addWidget(self.M2homGO, 4, 0) + # self.M2homGO.clicked.connect(lambda: self.Calc(3)) + # self.noqplabel = wc.QLabel(u'No quadrupolar interaction') + # self.M2Frame.addWidget(self.noqplabel, 2, 1) + # self.M2homlabel = wc.QLabel(u'Homonuclear') + # self.M2Frame.addWidget(self.M2homlabel, 2, 0) + # self.M2hom = wc.QLineEdit("0") + # self.M2hom.setMinimumWidth(100) + # self.M2Frame.addWidget(self.M2hom, 4, 1) + # self.HomoWidgets = [self.M2homGO,self.noqplabel,self.M2homlabel,self.M2hom] + + self.M2SEDGO = QtWidgets.QPushButton("Go") + self.M2Frame.addWidget(self.M2SEDGO, 6, 0) + self.M2SEDGO.clicked.connect(lambda: self.Calc(3)) + self.M2SEDlabel = wc.QLeftLabel(u'Spin Echo Decay Envelope (C<sub>Q</sub> > 0)') + self.M2Frame.addWidget(self.M2SEDlabel, 5, 0,1,2) + self.M2SED = wc.QLineEdit("0") + self.M2SED.setMinimumWidth(100) + self.M2Frame.addWidget(self.M2SED, 6, 1) + + self.grid.addWidget(self.M2Group, 9, 0, 2, 2) + self.M2Group.setLayout(self.M2Frame) # Reset self.cancelButton.setText("Close") @@ -7625,24 +7704,68 @@ def __init__(self, parent): self.okButton.clicked.disconnect() self.okButton.clicked.connect(self.valueReset) + self.checkHomoHetro() + + + def checkHomoHetro(self): + gamma1 = float(safeEval(self.gamma1.text(), Type='FI')) * 1e7 + gamma2 = float(safeEval(self.gamma2.text(), Type='FI')) * 1e7 + I1 = self.Ivalues[self.spin1.currentIndex()] + I2 = self.Ivalues[self.spin2.currentIndex()] + if gamma1 == gamma2 and I1 == I2: + self.M2label.setText(u'M₂ Homonuclear') + if I1 > 0.5: + self.M2SEDGO.show() + self.M2SEDlabel.show() + self.M2SED.show() + else: + self.M2SEDGO.hide() + self.M2SEDlabel.hide() + self.M2SED.hide() + else: + self.M2label.setText(u'M₂ Hetronuclear') + self.M2SEDGO.hide() + self.M2SEDlabel.hide() + self.M2SED.hide() + + def spin1Changed(self): + self.gamma1Drop.setCurrentIndex(0) + self.checkHomoHetro() + + def spin2Changed(self): + self.gamma2Drop.setCurrentIndex(0) + self.checkHomoHetro() + def gamma1Changed(self): self.gamma1Drop.setCurrentIndex(0) + self.checkHomoHetro() def gamma2Changed(self): self.gamma2Drop.setCurrentIndex(0) + self.checkHomoHetro() def setGamma1(self, index): if index != 0: + self.spin1.setCurrentIndex(self.Ivalues.index(self.spinValues[index])) self.gamma1.setText(str(self.gammaValues[index])) + self.gamma1Drop.setCurrentIndex(index) #Needed to avoid resets by other boxes + self.checkHomoHetro() def setGamma2(self, index): if index != 0: + self.spin2.setCurrentIndex(self.Ivalues.index(self.spinValues[index])) self.gamma2.setText(str(self.gammaValues[index])) + self.gamma2Drop.setCurrentIndex(index) #Needed to avoid resets by other boxes + self.checkHomoHetro() def Calc(self, Type): try: gamma1 = float(safeEval(self.gamma1.text(), Type='FI')) * 1e7 gamma2 = float(safeEval(self.gamma2.text(), Type='FI')) * 1e7 + I1 = self.Ivalues[self.spin1.currentIndex()] + I2 = self.Ivalues[self.spin2.currentIndex()] + #spin1 = float(safeEval(self.spin1.text(), Type='FI')) + #spin2 = float(safeEval(self.spin2.text(), Type='FI')) except Exception: raise SsnakeException("Dipolar Distance: Invalid input in gamma values") if Type == 0: # Distance as input @@ -7650,34 +7773,129 @@ def Calc(self, Type): r = abs(float(safeEval(self.distance.text(), Type='FI'))) except Exception: raise SsnakeException("Dipolar Distance: Invalid input in r") - if Type == 1: + if Type == 1: # Dipolar coupling as input try: D = abs(float(safeEval(self.dipolar.text(), Type='FI'))) except Exception: raise SsnakeException("Dipolar Distance: Invalid input in D") + if Type == 2: # Heteronuclear second-moment as input + try: + M2 = abs(float(safeEval(self.M2.text(), Type='FI'))) + except Exception: + raise SsnakeException("Dipolar Distance: Invalid input in M2") + + if Type == 3: # Homonuclear second-moment from spin echo decay + try: + M2SED = abs(float(safeEval(self.M2SED.text(), Type='FI'))) + except Exception: + raise SsnakeException("Dipolar Distance: Invalid input in M2") + hbar = 1.054573e-34 + def wm(m): + return (1/2) * np.sqrt(I2 * (I2 + 1) - m * (m + 1)) + + M2_Factor = (2/(9*(2*I2+1))) * (1 + 4*wm(-1/2)**2 + 4*wm(-1/2)**4 + wm(1/2)**4) + if Type == 0: if r == 0.0: D = np.inf + M2 = np.inf + M2SED = np.inf else: D = abs(- 1e-7 * gamma1 * gamma2 * hbar / (r * 10**-10) **3 / (2 * np.pi)) D /= 1000 + if gamma1 == gamma2: + M2SED = abs(M2_Factor * (4/5) * (9/4) * 1e-14 * gamma1**4 * hbar**2 / ((r * 10**-10) **6)) + # factor 4/5 results after powder averaging + M2SED /= 1e6 + M2 = abs((3/5) * 1e-14 * gamma1**4 * I2 * (I2 + 1)* hbar**2 / ((r * 10**-10) **6)) + M2 /= 1e6 + + else: + M2 = abs((4/15) * 1e-14 * gamma1**2 * gamma2**2 * I2 * (I2 + 1) * hbar**2 / ((r * 10**-10) **6)) + M2 /= 1e6 + M2SED = 0 + if Type == 1: if D == 0.0: r = np.inf + M2 = 0.0 + M2SED = 0.0 else: - r = 1 / abs(D * 1000 /gamma1 / gamma2 / hbar / 1e-7 * (2 * np.pi))**(1.0/3) + r = 1 / abs(D * 1000 / gamma1 / gamma2 / hbar / 1e-7 * (2 * np.pi))**(1.0/3) r *= 1e10 + if gamma1 == gamma2: + M2SED = abs(M2_Factor * (4/5) * (9/4) * 1e-14 * gamma1**4 * hbar**2 / ((r * 10**-10) **6)) + M2SED /= 1e6 + M2 = abs((3/5) * 1e-14 * gamma1**4 * I2 * (I2 + 1)* hbar**2 / ((r * 10**-10) **6)) + M2 /= 1e6 + #factor 4/5 results after powder averaging + + else: + M2 = abs((4/15) * 1e-14 * gamma1**2 * gamma2**2 * I2 * (I2 + 1) * hbar**2 / ((r * 10**-10) **6)) + M2 /= 1e6 + M2SED = 0.0 + + if Type == 2: + if M2 == 0: + r = np.inf + D = 0.0 + M2 = 0.0 + else: + if gamma1 == gamma2: + r = abs((3/5) * 1e-14 * gamma1**4 * I2 * (I2 + 1) * hbar**2 / (M2 * 10**6)) + r = r**(1/6) + r *= 10**10 + D = abs(- 1e-7 * gamma1 * gamma2 * hbar / (r * 10**-10) **3 / (2 * np.pi)) + D /= 1000 + M2SED = abs(M2_Factor * (4/5) * (9/4) * 1e-14 * gamma1**4 * hbar**2 / ((r * 10**-10) **6)) + M2SED /= 1e6 + else: + r = abs((4/15) * 1e-14 * gamma1**2 * gamma2**2 * I2 * (I2 + 1) * hbar**2 / (M2 * 10**6)) + r = r**(1/6) + r *= 10**10 + D = abs(- 1e-7 * gamma1 * gamma2 * hbar / (r * 10**-10) **3 / (2 * np.pi)) + D /= 1000 + M2SED = 0.0 + + if Type == 3: + if M2SED == 0: + r = np.inf + D = 0.0 + M2 = 0.0 + else: + if gamma1 == gamma2: + r = abs(M2_Factor * (4/5) * (9/4) * 1e-14 * gamma1**4 * hbar**2 / (M2SED * 10**6)) + # factors 4/5 and 9/4 result from powder averaging + r = r**(1/6) + r *= 10**10 + D = abs(- 1e-7 * gamma1 * gamma2 * hbar / (r * 10**-10) **3 / (2 * np.pi)) + D /= 1000 + M2 = abs((3/5) * 1e-14 * gamma1**4 * I2 * (I2 + 1)* hbar**2 / ((r * 10**-10) **6)) + M2 /= 1e6 + else: + raise SsnakeException("Choose two identical nuclei.") + # else: + self.dipolar.setText('%#.5g' % D) self.distance.setText('%#.5g' % r) + self.M2.setText('%#.5g' % M2) + self.M2SED.setText('%#.5g' % M2SED) + # Implement FWHM of dipolar line as input def valueReset(self): # Resets all the boxes to 0 self.dipolar.setText('0.0') self.distance.setText('0.0') + self.M2het.setText('0.0') + self.M2hom.setText('0.0') + self.M2SED.setText('0.0') self.gamma1.setText('0.0') self.gamma2.setText('0.0') + self.spin1.setCurrentIndex(0) + self.spin1.setCurrentIndex(0) self.gamma1Drop.setCurrentIndex(0) self.gamma2Drop.setCurrentIndex(0) + self.checkHomoHetro() def closeEvent(self, *args): self.deleteLater() diff --git a/src/updateWindow.py b/src/updateWindow.py index aef8db31..262c1fdc 100644 --- a/src/updateWindow.py +++ b/src/updateWindow.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/views.py b/src/views.py index 007bb3d5..9bea6181 100644 --- a/src/views.py +++ b/src/views.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. # diff --git a/src/widgetClasses.py b/src/widgetClasses.py index 2bc36ca5..9961cd5b 100644 --- a/src/widgetClasses.py +++ b/src/widgetClasses.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2016 - 2021 Bas van Meerten and Wouter Franssen +# Copyright 2016 - 2022 Bas van Meerten and Wouter Franssen # This file is part of ssNake. #