merge cfunc -> main

This commit is contained in:
Dominik Demuth 2023-04-30 14:59:58 +02:00
commit 7671a56b6f
13 changed files with 421 additions and 109 deletions

View File

@ -36,10 +36,10 @@ AppDir:
include: include:
# for /usr/bin/env # for /usr/bin/env
- coreutils # - coreutils
- dash # - dash
- zsync # - zsync
- hicolor-icon-theme # - hicolor-icon-theme
- libatlas3-base - libatlas3-base
- python3.9-minimal - python3.9-minimal
- python3-numpy - python3-numpy
@ -57,17 +57,24 @@ AppDir:
- libqt5test5 - libqt5test5
- libqt5xml5 - libqt5xml5
- qtbase5-dev-tools - qtbase5-dev-tools
- qtchooser
- pyqt5-dev-tools - pyqt5-dev-tools
- libavahi-client3 - libavahi-client3
- libavahi-common-data - libavahi-common-data
- libavahi-common3 - libavahi-common3
- libwacom2 - libwacom2
- libwacom-common - libwacom-common
after_bundle: |
echo "MONSTER SED FOLLOWING...(uncomment if needed for mpl-data)" files:
# sed -i s,\'/usr/share/matplotlib/mpl-data\',"f\"\{os.environ.get\('APPDIR'\,'/'\)\}/usr/share/matplotlib/mpl-data\"", ${TARGET_APPDIR}/usr/lib/python3/dist-packages/matplotlib/__init__.py exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.}*
runtime: runtime:
# if needed, apparently replaces hardcoded location with APPDIR location
# path_mappings:
# - /usr/share/matplotlib/mpl-data:$APPDIR/usr/share/matplotlib/mpl-data
version: "continuous" version: "continuous"
env: env:
PATH: '${APPDIR}/usr/bin:${PATH}' PATH: '${APPDIR}/usr/bin:${PATH}'

View File

@ -8,28 +8,34 @@ PYRCC = pyrcc5
RESOURCE_DIR = src/resources/_ui RESOURCE_DIR = src/resources/_ui
#Directory for compiled resources #Directory for compiled resources
COMPILED_DIR = src/gui_qt/_py PYQT_DIR = src/gui_qt/_py
#UI files to compile, uses every *.ui found in RESOURCE_DIR #UI files to compile, uses every *.ui found in RESOURCE_DIR
UI_FILES = $(foreach dir, $(RESOURCE_DIR), $(notdir $(wildcard $(dir)/*.ui))) UI_FILES = $(foreach dir, $(RESOURCE_DIR), $(notdir $(wildcard $(dir)/*.ui)))
COMPILED_UI = $(UI_FILES:%.ui=$(COMPILED_DIR)/%.py) PYQT_UI = $(UI_FILES:%.ui=$(PYQT_DIR)/%.py)
SVG_FILES = $(foreach dir, $(RCC_DIR), $(notdir $(wildcard $(dir)/*.svg))) SVG_FILES = $(foreach dir, $(RCC_DIR), $(notdir $(wildcard $(dir)/*.svg)))
PNG_FILES = $(SVG_FILES:%.svg=$(RCC_DIR)/%.png) PNG_FILES = $(SVG_FILES:%.svg=$(RCC_DIR)/%.png)
all : ui CC = gcc
CFLAGS = -O2 -fPIC
ui : $(COMPILED_UI) LDFLAGS = -shared
rcc: $(PNG_FILES) C_DIR = src/nmreval/clib
all : ui compile
ui : $(PYQT_UI)
rcc : $(PNG_FILES)
# only one C file at the moment
compile : $(C_DIR)/integrate.c
$(CC) $(LDFLAGS) $(CFLAGS) -o $(C_DIR)/integrate.so $<
$(COMPILED_DIR)/%.py : $(RESOURCE_DIR)/%.ui $(COMPILED_DIR)/%.py : $(RESOURCE_DIR)/%.ui
$(PYUIC) $< -o $@ $(PYUIC) $< -o $@
# replace import of ressource to correct path
# @sed -i s/images_rc/nmrevalqt.$(COMPILED_DIR).images_rc/g $@
# @sed -i /images_rc/d $@
$(RCC_DIR)/%.png : $(RCC_DIR)/%.svg $(RCC_DIR)/%.png : $(RCC_DIR)/%.svg
convert -background none $< $@ convert -background none $< $@
$(PYRCC) $(RCC_DIR)/images.qrc -o $(COMPILED_DIR)/images_rc.py $(PYRCC) $(RCC_DIR)/images.qrc -o $(COMPILED_DIR)/images_rc.py

View File

@ -0,0 +1,158 @@
/* integrands used in quadrature integration with scipy's LowLevelCallables */
#include <math.h>
const double KB = 8.617333262145179e-05;
/* FFHS functions */
double ffhsSD(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double u = x*x;
double res = u*u * tau;
res /= 81. + 9.*u - 2.*u*u + u*u*u;
res /= u*u + omega*omega * tau*tau;
return res;
}
/* log-gaussian functions */
double logNormalDist(double tau, double tau0, double sigma) {
return exp(- pow((log(tau/tau0) / sigma), 2) / 2.) / sqrt(2*M_PI)/sigma;
}
double logGaussian_imag_high(double u, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(-u);
double dist = logNormalDist(1./uu, tau, sigma);
return dist * omega * uu / (pow(uu, 2) + pow(omega, 2));
}
double logGaussian_imag_low(double u, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(u);
double dist = logNormalDist(uu, tau, sigma);
return dist * omega * uu / (1. + pow(omega*uu, 2));
}
double logGaussian_real_high(double u, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(-2.*u);
double dist = logNormalDist(exp(uu), tau, sigma);
return dist * uu / (uu + pow(omega, 2));
}
double logGaussian_real_low(double u, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(u);
double dist = logNormalDist(uu, tau, sigma);
return dist / (1. + pow(omega*uu, 2));
}
double logGaussianCorrelation(double x, void *user_data) {
double *c = (double *)user_data;
double t = c[0];
double tau = c[1];
double sigma = c[2];
double uu = exp(x);
double dist = logNormalDist(uu, tau, sigma);
return dist * exp(-t/uu);
}
// functions for distribution of energy
double normalDist(double x, double x0, double sigma) {
return exp(- pow((x-x0) / sigma, 2) / 2.) / sqrt(2 * M_PI) / sigma;
}
double rate(double tau0, double ea, double t) {
return exp(-ea / t / KB) / tau0;
}
double energyDist_SD(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau0 = c[1];
double e_m = c[2];
double e_b = c[3];
double temp = c[4];
double r = rate(tau0, x, temp);
return r/(pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
}
double energyDistSuscReal(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau0 = c[1];
double e_m = c[2];
double e_b = c[3];
double temp = c[4];
double r = rate(tau0, x, temp);
return 1 / (pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
}
double energyDistSuscImag(double x, void *user_data) {
double *c = (double *)user_data;
double omega = c[0];
double tau0 = c[1];
double e_m = c[2];
double e_b = c[3];
double temp = c[4];
double r = rate(tau0, x, temp);
return omega * r / (pow(r, 2) + pow(omega, 2)) * normalDist(x, e_m, e_b);
}
double energyDistCorrelation(double x, void *user_data) {
double *c = (double *)user_data;
double t = c[0];
double tau0 = c[1];
double e_m = c[2];
double e_b = c[3];
double temp = c[4];
double r = rate(tau0, x, temp);
return normalDist(x, e_m, e_b) * exp(-t * r);
}

BIN
src/nmreval/clib/integrate.so Executable file

Binary file not shown.

View File

@ -29,7 +29,7 @@ class Distribution(abc.ABC):
@staticmethod @staticmethod
@abc.abstractmethod @abc.abstractmethod
def susceptibility(omega, tau, *args): def susceptibility(omega: ArrayLike, tau: ArrayLike, *args: Any):
pass pass
@classmethod @classmethod

View File

@ -1,13 +1,18 @@
from itertools import product from itertools import product
from ctypes import c_double, cast, pointer, c_void_p
import numpy as np import numpy as np
from scipy import LowLevelCallable
from scipy.integrate import quad, simps as simpson from scipy.integrate import quad, simps as simpson
from .base import Distribution from .base import Distribution
from ..lib.utils import ArrayLike from ..lib.utils import ArrayLike
from ..utils.constants import kB from ..utils.constants import kB
from .helper import HAS_C_FUNCS, lib
# noinspection PyMethodOverriding
class EnergyBarriers(Distribution): class EnergyBarriers(Distribution):
name = 'Energy barriers' name = 'Energy barriers'
parameter = [r'\tau_{0}', r'E_{m}', r'\Delta E'] parameter = [r'\tau_{0}', r'E_{m}', r'\Delta E']
@ -23,24 +28,30 @@ class EnergyBarriers(Distribution):
return np.exp(-e_a / (kB * te)) / t0 return np.exp(-e_a / (kB * te)) / t0
@staticmethod @staticmethod
def energydistribution(e_a, mu, sigma): def energy_distribution(e_a, mu, sigma):
return np.exp(-0.5 * ((mu-e_a) / sigma) ** 2) / (np.sqrt(2 * np.pi) * sigma) return np.exp(-0.5 * ((mu-e_a) / sigma) ** 2) / (np.sqrt(2 * np.pi) * sigma)
@staticmethod @staticmethod
def correlation(t, temperature, *args): def correlation(t: ArrayLike, temperature: ArrayLike, tau0: float, e_m: float, e_b: float) -> ArrayLike:
tau0, e_m, e_b = args
def integrand(e_a, ti, t0, mu, sigma, te):
# correlation time would go to inf for higher energies, so we use rate
return np.exp(-ti*EnergyBarriers.rate(t0, e_a, te)) * EnergyBarriers.energydistribution(e_a, mu, sigma)
t = np.atleast_1d(t) t = np.atleast_1d(t)
temperature = np.atleast_1d(temperature) temperature = np.atleast_1d(temperature)
e_axis = np.linspace(max(0, e_m-50*e_b), e_m+50*e_b, num=5001) if HAS_C_FUNCS:
ret_val = _integrate_c(lib.energyDistCorrelation, t, temperature, tau0, e_m, e_b)
else:
ret_val = _integrate_py(_integrand_time, t, temperature, tau0, e_m, e_b)
ret_val = np.array([simpson(integrand(e_axis, o, tau0, e_m, e_b, tt), e_axis) return ret_val
for o in t for tt in temperature])
@staticmethod
def specdens(omega: ArrayLike, temperature: ArrayLike, tau0: float, e_m: float, e_b: float) -> ArrayLike:
omega = np.atleast_1d(omega)
temperature = np.atleast_1d(temperature)
if HAS_C_FUNCS:
ret_val = _integrate_c(lib.energyDist_SD, omega, temperature, tau0, e_m, e_b)
else:
ret_val = _integrate_py(_integrand_sd, omega, temperature, tau0, e_m, e_b)
return ret_val return ret_val
@ -51,70 +62,73 @@ class EnergyBarriers(Distribution):
omega = np.atleast_1d(omega) omega = np.atleast_1d(omega)
temperature = np.atleast_1d(temperature) temperature = np.atleast_1d(temperature)
e_axis = np.linspace(max(0, e_m-50*e_b), e_m+50*e_b, num=5001) if HAS_C_FUNCS:
ret_val = [] ret_val = _integrate_c(lib.energyDistSuscReal, omega, temperature, tau0, e_m, e_b) + \
for o, tt in product(omega, temperature): 1j * _integrate_c(lib.energyDistSuscImag, omega, temperature, tau0, e_m, e_b)
ret_val.append(simpson(_integrand_freq_real(e_axis, o, tau0, e_m, e_b, tt), e_axis) - else:
1j * simpson(_integrand_freq_imag(e_axis, o, tau0, e_m, e_b, tt), e_axis)) ret_val = _integrate_py(_integrand_susc_real, omega, temperature, tau0, e_m, e_b) + \
1j * _integrate_py(_integrand_susc_imag, omega, temperature, tau0, e_m, e_b)
return np.array(ret_val)
@staticmethod
def specdens(omega, temperature, *args):
# in contrast to other spectral densities, it's omega and temperature
tau0, e_m, e_b = args
def integrand(e_a, w, t0, mu, sigma, t):
r = EnergyBarriers.rate(t0, e_a, t)
return r/(r**2 + w**2) * EnergyBarriers.energydistribution(e_a, mu, sigma)
omega = np.atleast_1d(omega)
temperature = np.atleast_1d(temperature)
e_axis = np.linspace(max(0, e_m-50*e_b), e_m+50*e_b, num=5001)
ret_val = np.array([simpson(integrand(e_axis, o, tau0, e_m, e_b, tt), e_axis)
for o in omega for tt in temperature])
return ret_val return ret_val
@staticmethod @staticmethod
def mean(*args): def mean(temperature, tau0, ea):
return args[1]*np.exp(args[2]/(kB*args[0])) return tau0*np.exp(ea/(kB*temperature))
@staticmethod @staticmethod
def logmean(*args): def logmean(temperature, tau0, ea):
return args[1] + args[2] / (kB * args[0]) return tau0 + ea / (kB * temperature)
@staticmethod @staticmethod
def max(*args): def max(*args):
return args[1] * np.exp(args[2] / (kB * args[0])) return args[1] * np.exp(args[2] / (kB * args[0]))
def _integrate_process_imag(args): # helper functions
pass def _integrate_c(func, omega: np.ndarray, temperature: np.ndarray, tau0: float, e_m: float, e_b: float) -> np.ndarray:
res = []
for o, t in product(omega, temperature):
c = (c_double * 5)(o, tau0, e_m, e_b, t)
user_data = cast(pointer(c), c_void_p)
area = quad(LowLevelCallable(func, user_data), 0, np.infty, epsabs=1e-13)[0]
res.append(area)
ret_val = np.array(res).reshape(omega.shape[0], temperature.shape[0])
return ret_val.squeeze()
def _integrate_process_real(args): def _integrate_py(func, axis, temp, tau0, e_m, e_b):
omega_i, t, tau0, mu, sigma, temp_j = args x = np.atleast_1d(axis)
return quad(_integrand_freq_real(), 0, 10, args=(omega_i, t, tau0, mu, sigma, temp_j))[0] temperature = np.atleast_1d(temp)
e_axis = np.linspace(max(0., e_m - 50*e_b), e_m + 50*e_b, num=5001)
ret_val = []
for o, tt in product(x, temperature):
ret_val.append(simpson(func(e_axis, o, tau0, e_m, e_b, tt), e_axis))
ret_val = np.array(ret_val).reshape(x.shape[0], temperature.shape[0])
return ret_val.squeeze()
def _integrate_process_time(args): # python integrands
omega_i, t, tau0, mu, sigma, temp_j = args def _integrand_sd(u, omega, tau0, mu, sigma, temp):
return quad(_integrand_time, 0, 10, args=(omega_i, t, tau0, mu, sigma, temp_j))[0]
def _integrand_freq_real(u, omega, tau0, mu, sigma, temp):
r = EnergyBarriers.rate(tau0, u, temp) r = EnergyBarriers.rate(tau0, u, temp)
return 1 / (r**2 + omega**2) * EnergyBarriers.energydistribution(u, mu, sigma) return r / (r**2 + omega**2) * EnergyBarriers.energy_distribution(u, mu, sigma)
def _integrand_freq_imag(u, omega, tau0, mu, sigma, temp): def _integrand_susc_real(u, omega, tau0, mu, sigma, temp):
r = EnergyBarriers.rate(tau0, u, temp)
return 1 / (r**2 + omega**2) * EnergyBarriers.energy_distribution(u, mu, sigma)
def _integrand_susc_imag(u, omega, tau0, mu, sigma, temp):
rate = EnergyBarriers.rate(tau0, u, temp) rate = EnergyBarriers.rate(tau0, u, temp)
return omega * rate / (rate**2 + omega**2) * EnergyBarriers.energydistribution(u, mu, sigma) return omega * rate / (rate**2 + omega**2) * EnergyBarriers.energy_distribution(u, mu, sigma)
def _integrand_time(u, t, tau0, mu, sigma, temp): def _integrand_time(u, t, tau0, mu, sigma, temp):
rate = EnergyBarriers.rate(tau0, u, temp) rate = EnergyBarriers.rate(tau0, u, temp)
return EnergyBarriers.energydistribution(u, mu, sigma) * np.exp(-t*rate) return EnergyBarriers.energy_distribution(u, mu, sigma) * np.exp(-t*rate)

View File

@ -0,0 +1,48 @@
from pathlib import Path
from ctypes import CDLL, c_double, c_void_p
from ..lib.logger import logger
lib = None
try:
lib = CDLL(str(Path(__file__).parents[1] / 'clib' / 'integrate.so'))
# FFHS integrand
lib.ffhsSD.restype = c_double
lib.ffhsSD.argtypes = (c_double, c_void_p)
# Log-Gaussian integrands
lib.logGaussian_imag_high.restype = c_double
lib.logGaussian_imag_high.argtypes = (c_double, c_void_p)
lib.logGaussian_imag_low.restype = c_double
lib.logGaussian_imag_low.argtypes = (c_double, c_void_p)
lib.logGaussian_real_high.restype = c_double
lib.logGaussian_real_high.argtypes = (c_double, c_void_p)
lib.logGaussian_real_low.restype = c_double
lib.logGaussian_real_low.argtypes = (c_double, c_void_p)
lib.logGaussianCorrelation.restype = c_double
lib.logGaussianCorrelation.argtypes = (c_double, c_void_p)
# integrands for distribution of energies
lib.energyDist_SD.restype = c_double
lib.energyDist_SD.argtypes = (c_double, c_void_p)
lib.energyDistCorrelation.restype = c_double
lib.energyDistCorrelation.argtypes = (c_double, c_void_p)
lib.energyDistSuscReal.restype = c_double
lib.energyDistSuscReal.argtypes = (c_double, c_void_p)
lib.energyDistSuscImag.restype = c_double
lib.energyDistSuscImag.argtypes = (c_double, c_void_p)
HAS_C_FUNCS = True
logger.info('Use C functions')
except OSError:
HAS_C_FUNCS = False
logger.info('Use python functions')

View File

@ -1,9 +1,16 @@
import ctypes
import numpy as np import numpy as np
from scipy import LowLevelCallable
from scipy.integrate import quad from scipy.integrate import quad
from .helper import HAS_C_FUNCS, lib
from .base import Distribution from .base import Distribution
# Everything except spectral density is implemented in Python only because the only use case of FFHS is NMR
# field cycling measurements with T1 results
class FFHS(Distribution): class FFHS(Distribution):
name = 'Intermolecular (FFHS)' name = 'Intermolecular (FFHS)'
parameter = None parameter = None
@ -24,7 +31,7 @@ class FFHS(Distribution):
return ret_val return ret_val
@staticmethod @staticmethod
def specdens(omega, tau0, *args): def specdens_py(omega, tau0):
def integrand(u, o, tau0): def integrand(u, o, tau0):
return u**4 * tau0 / (81 + 9*u**2 - 2*u**4 + u**6) / (u**4 + (o*tau0)**2) return u**4 * tau0 / (81 + 9*u**2 - 2*u**4 + u**6) / (u**4 + (o*tau0)**2)
# return FFHS.distribution(u, tau0) * u / (1+o**2 * u**2) # return FFHS.distribution(u, tau0) * u / (1+o**2 * u**2)
@ -33,6 +40,17 @@ class FFHS(Distribution):
return ret_val * 54 / np.pi return ret_val * 54 / np.pi
@staticmethod
def specdens_c(omega, tau0):
res = []
for o in omega:
c = (ctypes.c_double * 2)(o, tau0)
user_data = ctypes.cast(ctypes.pointer(c), ctypes.c_void_p)
func = LowLevelCallable(lib.ffhsSD, user_data)
res.append(quad(func, 0, np.infty)[0])
return np.array(res) * 54 / np.pi
@staticmethod @staticmethod
def susceptibility(omega, tau0, *args): def susceptibility(omega, tau0, *args):
def integrand_real(u, o, tau0): def integrand_real(u, o, tau0):
@ -48,6 +66,9 @@ class FFHS(Distribution):
return ret_val return ret_val
FFHS.specdens = FFHS.specdens_c if HAS_C_FUNCS else FFHS.specdens_py
# class Bessel(Distribution): # class Bessel(Distribution):
# name = 'Intermolecular (Bessel)' # name = 'Intermolecular (Bessel)'
# parameter = None # parameter = None

View File

@ -1,7 +1,12 @@
import ctypes
from multiprocessing import Pool, cpu_count from multiprocessing import Pool, cpu_count
from itertools import product from itertools import product
from typing import Callable
import numpy as np import numpy as np
from scipy import LowLevelCallable
from nmreval.lib.utils import ArrayLike
try: try:
from scipy.integrate import simpson from scipy.integrate import simpson
@ -9,19 +14,21 @@ except ImportError:
from scipy.integrate import simps as simpson from scipy.integrate import simps as simpson
from scipy.integrate import quad from scipy.integrate import quad
from .base import Distribution from nmreval.distributions.helper import HAS_C_FUNCS, lib
from nmreval.distributions.base import Distribution
__all__ = ['LogGaussian'] __all__ = ['LogGaussian']
# noinspection PyMethodOverriding
class LogGaussian(Distribution): class LogGaussian(Distribution):
name = 'Log-Gaussian' name = 'Log-Gaussian'
parameter = [r'\sigma'] parameter = [r'\sigma']
bounds = [(0, 10)] bounds = [(0, 10)]
@staticmethod @staticmethod
def distribution(tau, tau0, sigma: float): def distribution(tau: ArrayLike, tau0: ArrayLike, sigma: float) -> ArrayLike:
return np.exp(-0.5*(np.log(tau/tau0)/sigma)**2)/np.sqrt(2*np.pi)/sigma return np.exp(-0.5*(np.log(tau/tau0)/sigma)**2)/np.sqrt(2*np.pi)/sigma
@staticmethod @staticmethod
@ -29,54 +36,87 @@ class LogGaussian(Distribution):
_t = np.atleast_1d(t) _t = np.atleast_1d(t)
_tau = np.atleast_1d(tau0) _tau = np.atleast_1d(tau0)
pool = Pool(processes=min(cpu_count(), 4)) if HAS_C_FUNCS:
integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_t, _tau)] res = _integrate_correlation_c(_t, _tau, sigma)
else:
res = _integration_parallel(_t, _tau, sigma, _integrate_process_time)
with np.errstate(divide='ignore'): return res.squeeze()
res = np.array(pool.map(_integrate_process_time, integration_ranges))
ret_val = res.reshape((_t.shape[0], _tau.shape[0]))
return ret_val.squeeze()
@staticmethod @staticmethod
def susceptibility(omega, tau0, sigma: float): def susceptibility(omega, tau0, sigma: float):
_omega = np.atleast_1d(omega) _omega = np.atleast_1d(omega)
_tau = np.atleast_1d(tau0) _tau = np.atleast_1d(tau0)
pool = Pool(processes=min(cpu_count(), 4)) if HAS_C_FUNCS:
integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_omega, _tau)] res_real = _integrate_susc_real_c(_omega, _tau, sigma)
res_imag = _integrate_susc_imag_c(_omega, _tau, sigma)
else:
res_real = _integration_parallel(_omega, _tau, sigma, _integrate_process_imag)
res_imag = _integration_parallel(_omega, _tau, sigma, _integrate_process_real)
with np.errstate(divide='ignore'): return (res_real + 1j * res_imag).squeeze()
res_real = np.array(pool.map(_integrate_process_imag, integration_ranges))
res_imag = np.array(pool.map(_integrate_process_real, integration_ranges))
ret_val = (res_real+1j*res_imag).reshape((_omega.shape[0], _tau.shape[0]))
return ret_val.squeeze()
@staticmethod @staticmethod
def specdens(omega, tau0, sigma): def specdens(omega: ArrayLike, tau: ArrayLike, sigma: float) -> np.ndarray:
_omega = np.atleast_1d(omega) _omega = np.atleast_1d(omega)
_tau = np.atleast_1d(tau0) _tau = np.atleast_1d(tau)
pool = Pool(processes=min(cpu_count(), 4)) if HAS_C_FUNCS:
integration_ranges = [(omega_i, tau_j, sigma) for (omega_i, tau_j) in product(_omega, _tau)] ret_val = _integrate_susc_imag_c(_omega, _tau, sigma)
else:
with np.errstate(divide='ignore'): ret_val = _integration_parallel(_omega, _tau, sigma, _integrate_process_imag)
res = np.array(pool.map(_integrate_process_imag, integration_ranges))
ret_val = res.reshape((_omega.shape[0], _tau.shape[0]))
ret_val /= _omega[:, None] ret_val /= _omega[:, None]
return ret_val.squeeze() return ret_val.squeeze()
def mean(*args): @staticmethod
return args[0]*np.exp(args[1]**2 / 2) def mean(tau, sigma):
return tau*np.exp(sigma**2 / 2)
def _integration_parallel(x: np.ndarray, tau: np.ndarray, sigma: float, func: Callable) -> np.ndarray:
pool = Pool(processes=min(cpu_count(), 4))
integration_ranges = [(x_i, tau_j, sigma) for (x_i, tau_j) in product(x, tau)]
with np.errstate(divide='ignore'):
res = pool.map(func, integration_ranges)
res = np.array(res).reshape((x.shape[0], tau.shape[0]))
return res
def _integrate_susc_imag_c(omega: np.ndarray, tau: np.ndarray, sigma: float) -> np.ndarray:
return _integrate_susc_c(lib.logGaussian_imag_low, lib.logGaussian_imag_high, omega, tau, sigma)
def _integrate_susc_real_c(omega: np.ndarray, tau: np.ndarray, sigma: float) -> np.ndarray:
return _integrate_susc_c(lib.logGaussian_real_low, lib.logGaussian_real_high, omega, tau, sigma)
def _integrate_susc_c(lowfunc, highfunc, omega, tau, sigma):
res = []
for o, t in product(omega, tau):
c = (ctypes.c_double * 3)(o, t, sigma)
user_data = ctypes.cast(ctypes.pointer(c), ctypes.c_void_p)
area = quad(LowLevelCallable(highfunc, user_data), 0, np.infty, epsabs=1e-12, epsrel=1e-12)[0]
area += quad(LowLevelCallable(lowfunc, user_data), -np.infty, 0, epsabs=1e-12, epsrel=1e-12)[0]
res.append(area)
res = np.asanyarray(res).reshape((omega.shape[0], tau.shape[0]))
return res
def _integrate_process_imag(args): def _integrate_process_imag(args):
omega_i, tau_j, sigma = args omega_i, tau_j, sigma = args
area = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma))[0] area = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma), epsabs=1e-12, epsrel=1e-12)[0]
area += quad(_integrand_freq_imag_low, -50, 0, args=(omega_i, tau_j, sigma))[0] area += quad(_integrand_freq_imag_low, -50, 0, args=(omega_i, tau_j, sigma), epsabs=1e-12, epsrel=1e-12)[0]
return area return area
@ -89,9 +129,25 @@ def _integrate_process_real(args):
return area return area
def _integrate_correlation_c(t, tau, sigma):
res = []
for t_i, tau_i in product(t, tau):
c = (ctypes.c_double * 3)(t_i, tau_i, sigma)
user_data = ctypes.cast(ctypes.pointer(c), ctypes.c_void_p)
area = quad(LowLevelCallable(lib.logGaussianCorrelation, user_data), -np.infty, np.infty)[0]
res.append(area)
res = np.asanyarray(res).reshape((t.shape[0], tau.shape[0]))
return res
def _integrate_process_time(args): def _integrate_process_time(args):
omega_i, tau_j, sigma = args omega_i, tau_j, sigma = args
return quad(_integrand_time, -50, 50, args=(omega_i, tau_j, sigma))[0] return quad(_integrand_time, -50, 50, args=(omega_i, tau_j, sigma), epsabs=1e-12, epsrel=1e-12)[0]
def _integrand_time(u, t, tau, sigma): def _integrand_time(u, t, tau, sigma):

View File

@ -1,4 +1,3 @@
import time
import warnings import warnings
from itertools import product from itertools import product

View File

@ -9,11 +9,14 @@ import _compat_pickle
from pickle import * from pickle import *
from pickle import _Unframer, bytes_types, _Stop, _getattribute from pickle import _Unframer, bytes_types, _Stop, _getattribute
from nmreval.lib.logger import logger
try: try:
import bsddb3 import bsddb3
HAS_BSDDB3 = True HAS_BSDDB3 = True
except ImportError: except ImportError:
warnings.warn('bsdbb3 is not installed, reading legacy .nmr files is not possible.') logger.warn('bsdbb3 is not installed, reading legacy .nmr files is not possible.')
HAS_BSDDB3 = False HAS_BSDDB3 = False

View File

@ -86,7 +86,7 @@ class EnergyFC(_AbstractFC):
name = 'Energy distribution' name = 'Energy distribution'
params = ['C', 'T'] + EnergyBarriers.parameter params = ['C', 'T'] + EnergyBarriers.parameter
bounds = [(0, None), (0, None), (0, None), (0, None)] bounds = [(0, None), (0, None), (0, None), (0, None)]
ralax = Relaxation(distribution=EnergyBarriers) relax = Relaxation(distribution=EnergyBarriers)
class _AbstractFCDipolar(_AbstractFC): class _AbstractFCDipolar(_AbstractFC):

View File

@ -14,7 +14,7 @@ from collections import OrderedDict
from ..utils.constants import gamma_full, hbar_joule, pi, gamma, mu0 from ..utils.constants import gamma_full, hbar_joule, pi, gamma, mu0
__all__ = ['Quadrupolar', 'Czjzek', 'HeteroDipolar', __all__ = ['Coupling', 'Quadrupolar', 'Czjzek', 'HeteroDipolar',
'HomoDipolar', 'Constant', 'CSA'] 'HomoDipolar', 'Constant', 'CSA']