added flexibility
This commit is contained in:
0
python/__init__.py
Normal file
0
python/__init__.py
Normal file
77
python/helpers.py
Normal file
77
python/helpers.py
Normal file
@ -0,0 +1,77 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
from itertools import product
|
||||
|
||||
def prepare_rw_parameter(parameter: dict) -> list:
|
||||
"""
|
||||
Converts a dictionary of iterables to list of dictionaries
|
||||
|
||||
Example:
|
||||
If Input is {'a': [1, 2, 3], 'b' = [4, 5]}, output is cartesian product of dictionary values, i.e.,
|
||||
[{'a': 1, 'b': 4}, {'a': 1, 'b': 5}, {'a': 2, 'b': 4}, {'a': 2, 'b': 5}, {'a': 3, 'b': 4}, {'a': 3, 'b': 5}]
|
||||
|
||||
:param parameter: dictionary of list values
|
||||
:return: list of dictionaries
|
||||
"""
|
||||
output = {}
|
||||
for k, v in parameter.items():
|
||||
if isinstance(v, (float, int)):
|
||||
v = [v]
|
||||
|
||||
output[k] = v
|
||||
|
||||
output = list(dict(zip(parameter.keys(), step)) for step in product(*output.values()))
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def run_sims(
|
||||
motion: str,
|
||||
distribution: str,
|
||||
ste: bool = True,
|
||||
spectrum: bool = False,
|
||||
exec_file: str = './rwsim',
|
||||
config_file: str = './config.txt',
|
||||
**kwargs
|
||||
) -> None:
|
||||
|
||||
# set positional arguments
|
||||
arguments = [exec_file, config_file, motion, distribution]
|
||||
|
||||
if ste:
|
||||
arguments += ['--ste']
|
||||
if spectrum:
|
||||
arguments += ['--spectrum']
|
||||
|
||||
# add optional parameters that overwrite those given by config file
|
||||
for k, v in kwargs.items():
|
||||
arguments += [f'-{k.upper()}', f'{v}']
|
||||
|
||||
subprocess.run(arguments)
|
||||
|
||||
|
||||
def find_config_file(var_params: dict) -> pathlib.Path:
|
||||
# TODO handle situation if multiple files fit
|
||||
pattern = re.compile('|'.join(([f'{k}={v:1.6e}' for (k, v) in var_params.items()])).replace('.', '\.').replace('+', '\+'))
|
||||
|
||||
for p_file in pathlib.Path('.').glob('*_parameter.txt'):
|
||||
if len(re.findall(pattern, str(p_file))) == len(var_params):
|
||||
return p_file
|
||||
|
||||
|
||||
def read_parameter_file(path: str | pathlib.Path) -> dict[str, float]:
|
||||
path = pathlib.Path(path)
|
||||
if not path.exists():
|
||||
raise ValueError(f"No parameter file found at {path}")
|
||||
|
||||
parameter_dict = {}
|
||||
with path.open('r') as f:
|
||||
for line in f.readlines():
|
||||
k, v = line.split('=')
|
||||
parameter_dict[k] = float(v)
|
||||
|
||||
k, v = line.split('=')
|
||||
return parameter_dict
|
70
python/spectrum.py
Normal file
70
python/spectrum.py
Normal file
@ -0,0 +1,70 @@
|
||||
import numpy
|
||||
import numpy as np
|
||||
from matplotlib import pyplot
|
||||
|
||||
|
||||
|
||||
# parameter for spectrum simulations
|
||||
lb = 2e3
|
||||
pulse_length = 2e-6
|
||||
|
||||
|
||||
def dampening(x: np.ndarray, apod: float) -> np.ndarray:
|
||||
"""
|
||||
Calculate additional dampening to account e.g. for field inhomogeneities.
|
||||
|
||||
:param x: Time axis in seconds
|
||||
:param apod: Dampening factor in 1/seconds
|
||||
:return: Exponential dampening
|
||||
"""
|
||||
|
||||
return np.exp(-apod * x)
|
||||
|
||||
|
||||
def pulse_attn(freq: np.ndarray, t_pulse: float) -> np.ndarray:
|
||||
"""
|
||||
Calculate attenuation of signal to account for finite pulse lengths.
|
||||
|
||||
See Schmitt-Rohr/Spieß, eq. 2.126 for more information.
|
||||
|
||||
:param freq: Frequency axis in Hz
|
||||
:param t_pulse: Assumed pulse length in s
|
||||
:return: Attenuation factor.
|
||||
"""
|
||||
# cf. Schmitt-Rohr/Spieß eq. 2.126; omega_1 * t_p = pi/2
|
||||
pi_half_squared = np.pi**2 / 4
|
||||
omega = 2 * np.pi * freq
|
||||
|
||||
numerator = np.sin(np.sqrt(pi_half_squared + omega**2 * t_pulse**2 / 2))
|
||||
denominator = np.sqrt(pi_half_squared + omega**2 * t_pulse**2 / 4)
|
||||
|
||||
return np.pi * numerator / denominator / 2
|
||||
|
||||
|
||||
def post_process_spectrum(taus, apod, tpulse):
|
||||
reduction_factor = np.zeros((taus.size, 5)) # hard-coded t_echo :(
|
||||
|
||||
for i, tau in enumerate(taus):
|
||||
try:
|
||||
raw_data = np.loadtxt(f'fid_tau={tau:.6e}.dat')
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
t = raw_data[:, 0]
|
||||
timesignal = raw_data[:, 1:]
|
||||
|
||||
timesignal *= dampening(t, apod)[:, None]
|
||||
timesignal[0, :] /= 2
|
||||
|
||||
# FT to spectrum
|
||||
freq = np.fft.fftshift(np.fft.fftfreq(t.size, d=1e-6))
|
||||
spec = np.fft.fftshift(np.fft.fft(timesignal, axis=0), axes=0).real
|
||||
spec *= pulse_attn(freq, t_pulse=tpulse)[:, None]
|
||||
|
||||
reduction_factor[i, :] = 2*timesignal[0, :]
|
||||
|
||||
plt.plot(freq, spec)
|
||||
plt.show()
|
||||
|
||||
plt.semilogx(taus, reduction_factor, '.')
|
||||
plt.show()
|
102
python/ste.py
Normal file
102
python/ste.py
Normal file
@ -0,0 +1,102 @@
|
||||
import pathlib
|
||||
|
||||
import numpy
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
from scipy.optimize import curve_fit
|
||||
|
||||
from python.helpers import read_parameter_file
|
||||
|
||||
|
||||
def ste_decay(x: np.ndarray, m0: float, t: float, beta: float, finfty: float) -> np.ndarray:
|
||||
"""
|
||||
Calculate stimulated-echo decay.
|
||||
|
||||
:param x: Mixing times in seconds.
|
||||
:param m0: Amplitude
|
||||
:param t: Correlation time in seconds
|
||||
:param beta: Stretching parameter
|
||||
:param finfty: Final plateau
|
||||
:return: Stimulated-echo decay
|
||||
"""
|
||||
return m0 * ((1-finfty) * np.exp(-(x/t)**beta) + finfty)
|
||||
|
||||
|
||||
def fit_decay(x: np.ndarray, y: np.ndarray, tevo: np.ndarray, verbose: bool = True) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
||||
num_evo = y.shape[1]
|
||||
|
||||
tau_fit = np.empty((num_evo, 2))
|
||||
tau_fit[:, 0] = tevo
|
||||
|
||||
beta_fit = np.empty((num_evo, 2))
|
||||
beta_fit[:, 0] = tevo
|
||||
|
||||
finfty_fit = np.empty((num_evo, 2))
|
||||
finfty_fit[:, 0] = tevo
|
||||
|
||||
scaled_y = (y-y[-1, :]) / (y[0, :]-y[-1, :])
|
||||
|
||||
for j in range(num_evo):
|
||||
p0 = [scaled_y[0, 1], x[np.argmin(np.abs(scaled_y[:, j]-np.exp(-1)))], 0.5, 0.1]
|
||||
|
||||
try:
|
||||
res = curve_fit(ste_decay, x, y[:, j], p0, bounds=[(0, 0, 0., 0), (np.inf, np.inf, 1, 1)])
|
||||
except RuntimeError as e:
|
||||
print(f'Fit {j+1} of {num_evo} failed with {e.args}')
|
||||
continue
|
||||
|
||||
m0, tauc, beta, finfty = res[0]
|
||||
|
||||
if verbose:
|
||||
print(f'Fit {j+1} of {num_evo}: tau_c = {tauc:.6e}, beta={beta:.4e}, amplitude = {m0: .4e}, f_infty={finfty:.4e}')
|
||||
|
||||
tau_fit[j, 1] = tauc
|
||||
beta_fit[j, 1] = beta
|
||||
finfty_fit[j, 1] = finfty
|
||||
|
||||
return tau_fit, beta_fit, finfty_fit
|
||||
|
||||
|
||||
def fit_and_save_ste(parameter_file: pathlib.Path, plot_decays: bool = True, verbose: bool = True):
|
||||
# read simulation parameters
|
||||
parameter = read_parameter_file(parameter_file)
|
||||
|
||||
# files have form ste_arg=0.000000e+01_parameter.txt, first remove ste part then parameter.txt to get variables
|
||||
varied_string = str(parameter_file).partition('_')[-1].rpartition('_')[0]
|
||||
|
||||
# make evolution times
|
||||
tevo = np.linspace(parameter['tevo_start'], parameter['tevo_stop'], num=int(parameter['tevo_steps']))
|
||||
|
||||
raw_data_cc = np.loadtxt(f'coscos_{varied_string}.dat')
|
||||
raw_data_ss = np.loadtxt(f'sinsin_{varied_string}.dat')
|
||||
|
||||
t_mix = raw_data_cc[:, 0]
|
||||
cc_decay = raw_data_cc[:, 1:]
|
||||
ss_decay = raw_data_ss[:, 1:]
|
||||
|
||||
if plot_decays:
|
||||
fig_cc, ax_cc = plt.subplots()
|
||||
ax_cc.set_title('Cos-Cos')
|
||||
ax_cc.semilogx(t_mix, cc_decay, '.')
|
||||
|
||||
fig_ss, ax_ss = plt.subplots()
|
||||
ax_ss.set_title('Sin-Sin')
|
||||
ax_ss.semilogx(t_mix, ss_decay, '.')
|
||||
|
||||
plt.show()
|
||||
|
||||
print('Fit Cos-Cos')
|
||||
tau_cc, beta_cc, finfty_cc = fit_decay(t_mix, cc_decay, tevo, verbose=verbose)
|
||||
|
||||
np.savetxt(f'tau_cc_{varied_string}.dat', tau_cc)
|
||||
np.savetxt(f'beta_cc_{varied_string}.dat', beta_cc)
|
||||
np.savetxt(f'finfty_cc_{varied_string}.dat', finfty_cc)
|
||||
|
||||
print('Fit Sin-Sin')
|
||||
tau_ss, beta_ss, finfty_ss = fit_decay(t_mix, ss_decay, tevo, verbose=verbose)
|
||||
|
||||
np.savetxt(f'tau_ss_{varied_string}.dat', tau_ss)
|
||||
np.savetxt(f'beta_ss_{varied_string}.dat', beta_ss)
|
||||
np.savetxt(f'finfty_ss_{varied_string}.dat', finfty_ss)
|
||||
|
||||
return varied_string, tau_cc, beta_cc, finfty_cc, tau_ss, beta_ss, finfty_ss
|
Reference in New Issue
Block a user