diff --git a/VERSION b/VERSION
index 795460fc..0f1acbd5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v1.1.0
+v1.1.2
diff --git a/deerlab/dd_models.py b/deerlab/dd_models.py
index afcd381d..dc32cbaa 100644
--- a/deerlab/dd_models.py
+++ b/deerlab/dd_models.py
@@ -125,7 +125,7 @@ def _multirice3dfun(r,nu,sig):
r = r.T
s2 = sig**2
I_scaled = spc.ive(n/2-1, nu*r/s2)
- P = nu**(n/2-1)/s2*r**(n/2)*np.exp(-(r**2+nu**2)/(2*s2)+nu*r/s2)*I_scaled
+ P = nu**(1-n/2)/s2*r**(n/2)*np.exp(-(r**2+nu**2)/(2*s2)+nu*r/s2)*I_scaled
P[P<0] = 0
# Normalization
diff --git a/deerlab/solvers.py b/deerlab/solvers.py
index 8c1c6a29..1052d7b3 100644
--- a/deerlab/solvers.py
+++ b/deerlab/solvers.py
@@ -426,6 +426,11 @@ def snlls(y, Amodel, par0=None, lb=None, ub=None, lbl=None, ubl=None, nnlsSolver
* ``0`` : Work silently (default).
* ``1`` : Display progress including the non-linear least-squares' solver termination report.
* ``2`` : Display progress including the non-linear least-squares' solver iteration details.
+
+ .. caution::
+
+ The verbose output from the non-linear least-squares solver uses a different definition of the cost function than DeerLab.
+ DeerLab uses the sum of squares of the residuals divided by the number of data points, whereas the non-linear least-squares solver uses the sum of squares of the residuals divided by 2.
Returns
-------
@@ -594,7 +599,8 @@ def linear_problem(y,A,optimize_alpha,alpha):
if optimize_alpha:
- output = dl.selregparam((y-yfrozen)[mask], Ared[mask,:], linSolver, regparam,
+ linsolver_result = lambda AtA, Aty: parseResult(linSolver(AtA, Aty))
+ output = dl.selregparam((y-yfrozen)[mask], Ared[mask,:], linsolver_result, regparam,
weights=weights[mask], regop=L, candidates=regparamrange,
noiselvl=noiselvl,searchrange=regparamrange,full_output=True)
alpha = output[0]
diff --git a/deerlab/utils.py b/deerlab/utils.py
index 5079bd5b..3b7f69d1 100644
--- a/deerlab/utils.py
+++ b/deerlab/utils.py
@@ -833,7 +833,7 @@ def sophegrid(octants,maxphi,size):
weights = np.zeros(nOrientations)
sindth2 = np.sin(dtheta/2)
- w1 = 0.5
+ w1 = 1.0
# North pole (z orientation)
phi[0] = 0
@@ -841,7 +841,7 @@ def sophegrid(octants,maxphi,size):
weights[0] = maxphi*(1 - np.cos(dtheta/2))
# All but equatorial slice
- Start = 2
+ Start = 1
for iSlice in np.arange(2,size):
nPhi = nOct*(iSlice-1) + 1
dPhi = maxphi/(nPhi - 1)
@@ -854,13 +854,13 @@ def sophegrid(octants,maxphi,size):
# Equatorial slice
nPhi = nOct*(size - 1) + 1
dPhi = maxphi/(nPhi - 1)
- idx = Start + (np.arange(0,nPhi) - 1)
+ idx = Start + np.arange(0,nPhi)
phi[idx] = np.linspace(0,maxphi,nPhi)
theta[idx] = np.pi/2
weights[idx] = sindth2*dPhi*np.concatenate([[w1], np.ones(nPhi-2), [0.5]])
# Border removal
- rmv = np.cumsum(nOct*np.arange(1,size-1)+1)+1
+ rmv = np.cumsum(nOct*np.arange(1,size)+1)
phi = np.delete(phi,rmv)
theta = np.delete(theta,rmv)
weights = np.delete(weights,rmv)
diff --git a/docsrc/source/changelog.rst b/docsrc/source/changelog.rst
index 9da8425e..894c269f 100644
--- a/docsrc/source/changelog.rst
+++ b/docsrc/source/changelog.rst
@@ -24,6 +24,18 @@ Release Notes
- |fix| : Something which was not working as expected or leading to errors has been fixed.
- |api| : This will require changes in your scripts or code.
+Release ``v1.1.2`` - November 2023
+------------------------------------------
+- |fix| : Fixes an issue with sophgrid (:pr:`463`).
+- |fix| : Fixes an error in the normalisation of the rice models (:pr:`459`).
+- |fix| : Removes the broken three spin example (:pr:`427`).
+- |fix| : Fixes an error in the linear solver for linearly constrained not non-negative problems.
+
+Release ``v1.1.1`` - August 2023
+------------------------------------------
+- |fix| : Fixes an error in the `FitResult.evaluate` function. (:pr:`454`)
+
+
Release ``v1.1.0`` - August 2023
------------------------------------------
- |api| : The definition of the dipolar time axis for RIDME has changed to match the one for 4-pulse DEER (:pr:`436`).
diff --git a/docsrc/source/installation.rst b/docsrc/source/installation.rst
index dd974a46..95257bc3 100644
--- a/docsrc/source/installation.rst
+++ b/docsrc/source/installation.rst
@@ -45,7 +45,7 @@ DeerLab installs the following packages:
* `joblib `_ - Library lightweight pipelining and parallelization.
* `tqdm `_ - A lightweight package for smart progress meters.
* `dill `_ - An extension of Python's pickle module for serializing and de-serializing python objects.
-* `quadprog `_ - A quadratic programming solver
+* `quadprog `_ - A quadratic programming solver (Only for Python versions < 3.11)
Importing DeerLab
------------------
@@ -74,9 +74,6 @@ Any DeerLab version released after v0.10.0 can be installed via ``pip`` using th
python -m pip install deerlab==x.y.z
-DeerLab version prior to 0.10 are written in MATLAB and are still available from an `archived repository `_.
-Download and installation instruction for the MATLAB environment are provided in the released documentation. MATLAB releases have been deprecated and no further support is provided.
-
Installing from source
*****************************
@@ -130,10 +127,18 @@ On a **Windows** computer, if you are trying to run a DeerLab function, you migh
ImportError: DLL load failed: The specified module could not be found.
-This happens when the MKL libraries have not been properly linked in ``numpy``, ``scipy`` or ``cvxopt``
-installations (typically ``numpy``). This can happen due to firewall restrictions,
-user rights, or internet connection issues during the DeerLab installation. To solve this, the
-best solution is to manually install as follows.
+This issue can occur when a specific python package is not installled properly. This typicaly happens when installing the MKL linked libaries but can
+occur when installing packages normaly from PyPI. This can happen due to firewall restrictions,
+user rights, or internet connection issues during the DeerLab installation.
+
+If your where installing packages from PyPI (i.e. via pip) then please uninstall the package and reinstall it.
+
+.. code-block:: text
+
+ python -m pip uninstall
+ python -m pip install
+
+If you were trying to install the MKL linked libraries then please follow the instructions below.
1) Go to https://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy
@@ -167,7 +172,7 @@ During installation on certain systems (e.g. some computation clusters) using on
the following error might be raised during the installation:
.. code-block:: text
-
+
Error while finding module specification for 'setup.py'
(ModuleNotFoundError: __path__ attribute not found on 'setup' while trying to find 'setup.py')
diff --git a/examples/advanced/ex_long_threespin_analysis.py b/examples/advanced/ex_long_threespin_analysis.py
deleted file mode 100644
index cdbde826..00000000
--- a/examples/advanced/ex_long_threespin_analysis.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#%%
-"""
-Analyzing 4-pulse DEER data acquired on three-spin systems
-============================================================================
-
-As in the publication referenced below, this example will take two 4-pulse DEER signals acquired
-on the same protein sample conatining three nitroxide spins with different attenuation levels of the pump
-pulse power.
-
-For the original model and more information on these systems please refer to:
-L. Fábregas Ibáñez, M. H. Tessmer, G. Jeschke, and S. Stoll.
-Dipolar pathways in multi-spin and multi-dimensional dipolar EPR spectroscopy
-Phys. Chem. Chem. Phys., 24 2022, 22645-22660
-"""
-#%%
-import numpy as np
-import deerlab as dl
-
-# Load experimental data
-files = [f'../data/triradical_protein_deer_{dB}dB.DTA' for dB in [0,6,9]]
-
-# Experiment information
-t0 = 0.280 # Start time, μs
-tau1 = 0.40 # First interpulse delay, μs
-tau2 = 9.00 # Second interpulse delay, μs
-
-# Construct 4-pulse DEER experiment model
-my4pdeer = dl.ex_4pdeer(tau1,tau2,pathways=[1])
-
-# Loop over the different datasets
-Vmodels,Vexps,ts,Vexps_sub,ts_sub = [],[],[],[],[]
-for n,file in enumerate(files):
- # Load the dataset
- t,Vexp, descriptor = dl.deerload(file,full_output=True)
- t = t[:-80]
- Vexp = Vexp[:-80]
- # Adjust the Start time
- t = t - t[0] + t0
-
- # Pre-processing
- Vexp = dl.correctphase(Vexp)
- Vexp /= np.max(Vexp)
-
- # Store the pre-processed datasets in a list
- Vexps.append(Vexp)
- ts.append(t)
-
- # Subsampling
- # (required for efficient analysis in densely sampled datasets)
- sampling = np.arange(0,len(t),4) # Take every 4th point
- t_sub = t[sampling]
- Vexp_sub = Vexp[sampling]
-
- # Store the subsampled datasets in a list
- Vexps_sub.append(Vexp_sub)
- ts_sub.append(t_sub)
-
- # Construct the three-spin dipolar model
- Vmodel = dl.dipolarmodel(t_sub,spins=3,experiment=my4pdeer, minamp=0.01)
-
- # Add dipolar model to list of models
- Vmodels.append(Vmodel)
-
-# Construct a global dipolar model describing all datasets
-Vglobal = dl.merge(*Vmodels)
-Vglobal = dl.link(Vglobal,
- rmean1=[f'rmean1_{n+1}' for n in range(len(Vmodels))],
- rmean2=[f'rmean2_{n+1}' for n in range(len(Vmodels))],
- rmean3=[f'rmean3_{n+1}' for n in range(len(Vmodels))],
- chol11=[f'chol11_{n+1}' for n in range(len(Vmodels))],
- chol22=[f'chol22_{n+1}' for n in range(len(Vmodels))],
- chol33=[f'chol33_{n+1}' for n in range(len(Vmodels))],
- chol21=[f'chol21_{n+1}' for n in range(len(Vmodels))],
- chol31=[f'chol31_{n+1}' for n in range(len(Vmodels))],
- chol32=[f'chol32_{n+1}' for n in range(len(Vmodels))],
- conc=[f'conc_{n+1}' for n in range(len(Vmodels))],
- reftime1=[f'reftime1_{n+1}' for n in range(len(Vmodels))],
-)
-# Freeze the Cholesky-factors accounting for the correlation coefficients
-# to zero (not always applicable)
-Vglobal.chol21.freeze(0)
-Vglobal.chol31.freeze(0)
-Vglobal.chol32.freeze(0)
-
-# Fit the model to the data
-results = dl.fit(Vglobal, Vexps_sub, reg=False, ftol=1e-5)
-
-# %%
-# Plot the fitted datasets
-results.plot(axis=ts_sub,xlabel='time (μs)')
-
-# Print the fit summary
-print(results)
-
-# %%
diff --git a/test/test_utils.py b/test/test_utils.py
new file mode 100644
index 00000000..2df9a899
--- /dev/null
+++ b/test/test_utils.py
@@ -0,0 +1,16 @@
+import numpy as np
+from deerlab import sophegrid
+
+
+# ======================================================================
+# Test sophegrid function
+
+def test_sophegrid():
+ # Test that the function returns the values and weights summing to 1
+ # Comparison taken from EasySpin 6.0
+ phi, theta, weights = sophegrid(4,np.pi*2,3)
+
+ assert np.allclose(weights.sum(),1)
+ assert np.allclose(phi, np.array([0,0,1.57079632679490,3.14159265358979,4.71238898038469,0,0.785398163397448,1.57079632679490,2.35619449019235,3.14159265358979,3.92699081698724,4.71238898038469,5.49778714378214]))
+ assert np.allclose(theta, np.array([0,0.785398163397448,0.785398163397448,0.785398163397448,0.785398163397448,1.57079632679490,1.57079632679490,1.57079632679490,1.57079632679490,1.57079632679490,1.57079632679490,1.57079632679490,1.57079632679490]))
+ assert np.allclose(weights*4*np.pi, np.array([0.956558005801449,1.70021769237074,1.70021769237074,1.70021769237074,1.70021769237074,0.601117729884346,0.601117729884346,0.601117729884346,0.601117729884346,0.601117729884346,0.601117729884346,0.601117729884346,0.601117729884346]))
\ No newline at end of file