mirror of
https://github.com/nqrduck/nqr-blochsimulator.git
synced 2024-06-27 07:49:07 +00:00
Added calculation of reference voltage.
This commit is contained in:
parent
87232bdaec
commit
df7bf4f2ce
|
@ -1,6 +1,6 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import logging
|
import logging
|
||||||
from scipy import signal
|
from scipy.constants import h, Boltzmann
|
||||||
from .sample import Sample
|
from .sample import Sample
|
||||||
from .pulse import PulseArray
|
from .pulse import PulseArray
|
||||||
|
|
||||||
|
@ -25,7 +25,8 @@ class Simulation:
|
||||||
power_amplifier_power : float,
|
power_amplifier_power : float,
|
||||||
pulse : PulseArray,
|
pulse : PulseArray,
|
||||||
averages: int,
|
averages: int,
|
||||||
gain: float
|
gain: float,
|
||||||
|
temperature: float
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Constructs all the necessary attributes for the simulation object.
|
Constructs all the necessary attributes for the simulation object.
|
||||||
|
@ -56,6 +57,8 @@ class Simulation:
|
||||||
The number of averages that are used for the simulation.
|
The number of averages that are used for the simulation.
|
||||||
gain:
|
gain:
|
||||||
The gain of the amplifier.
|
The gain of the amplifier.
|
||||||
|
temperature:
|
||||||
|
The temperature of the sample in Kelvin.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.sample = sample
|
self.sample = sample
|
||||||
|
@ -70,9 +73,13 @@ class Simulation:
|
||||||
self.pulse = pulse
|
self.pulse = pulse
|
||||||
self.averages = averages
|
self.averages = averages
|
||||||
self.gain = gain
|
self.gain = gain
|
||||||
|
self.temperature = temperature
|
||||||
|
|
||||||
def simulate(self):
|
def simulate(self):
|
||||||
|
reference_voltage = self.calculate_reference_voltage()
|
||||||
|
logger.debug(reference_voltage * 1e6)
|
||||||
B1 = self.calc_B1() * 1e3 # I think this is multiplied by 1e3 because everything is in mT
|
B1 = self.calc_B1() * 1e3 # I think this is multiplied by 1e3 because everything is in mT
|
||||||
|
# B1 = 17.3 # Something might be wrong with the calculation of the B1 field. This has to be checked.
|
||||||
self.sample.gamma = self.sample.gamma * 1e-6 # We need our gamma in MHz / T
|
self.sample.gamma = self.sample.gamma * 1e-6 # We need our gamma in MHz / T
|
||||||
self.sample.T1 = self.sample.T1 * 1e3 # We need our T1 in ms
|
self.sample.T1 = self.sample.T1 * 1e3 # We need our T1 in ms
|
||||||
self.sample.T2 = self.sample.T2 * 1e3 # We need our T2 in ms
|
self.sample.T2 = self.sample.T2 * 1e3 # We need our T2 in ms
|
||||||
|
@ -82,8 +89,6 @@ class Simulation:
|
||||||
real_pulsepower = self.pulse.get_real_pulsepower()
|
real_pulsepower = self.pulse.get_real_pulsepower()
|
||||||
imag_pulsepower = self.pulse.get_imag_pulsepower()
|
imag_pulsepower = self.pulse.get_imag_pulsepower()
|
||||||
M_sy1 = self.bloch_symmetric_strang_splitting(B1, xdis, real_pulsepower, imag_pulsepower)
|
M_sy1 = self.bloch_symmetric_strang_splitting(B1, xdis, real_pulsepower, imag_pulsepower)
|
||||||
logger.debug("Shape of Msy1: %s", M_sy1.shape)
|
|
||||||
|
|
||||||
|
|
||||||
# Z-Component
|
# Z-Component
|
||||||
Mlong = np.squeeze(M_sy1[2,:,:]) # Indices start at 0 in Python
|
Mlong = np.squeeze(M_sy1[2,:,:]) # Indices start at 0 in Python
|
||||||
|
@ -94,8 +99,8 @@ class Simulation:
|
||||||
Mtrans = np.squeeze(M_sy1[0,:,:] + 1j*M_sy1[1,:,:]) # Indices start at 0 in Python
|
Mtrans = np.squeeze(M_sy1[0,:,:] + 1j*M_sy1[1,:,:]) # Indices start at 0 in Python
|
||||||
Mtrans_avg = np.mean(Mtrans, axis=0)
|
Mtrans_avg = np.mean(Mtrans, axis=0)
|
||||||
Mtrans_avg = np.delete(Mtrans_avg, -1) # Remove the last element
|
Mtrans_avg = np.delete(Mtrans_avg, -1) # Remove the last element
|
||||||
reference = 4.5502
|
|
||||||
sigtrans = Mtrans_avg * reference * self.averages * self.gain
|
sigtrans = Mtrans_avg * reference_voltage * 1e6 * self.averages * self.gain
|
||||||
return sigtrans
|
return sigtrans
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,6 +113,12 @@ class Simulation:
|
||||||
The B1 field of the solenoid coil.
|
The B1 field of the solenoid coil.
|
||||||
xdis : np.array
|
xdis : np.array
|
||||||
The x distribution of the isochromats.
|
The x distribution of the isochromats.
|
||||||
|
real_pulsepower : np.array
|
||||||
|
The real part of the pulse power.
|
||||||
|
imag_pulsepower : np.array
|
||||||
|
The imaginary part of the pulse power.
|
||||||
|
relax : float
|
||||||
|
If relax = 1, the relaxation is taken into account. If relax = 0, the relaxation is not taken into account.
|
||||||
"""
|
"""
|
||||||
Nx = self.number_isochromats
|
Nx = self.number_isochromats
|
||||||
Nu = real_pulsepower.shape[0]
|
Nu = real_pulsepower.shape[0]
|
||||||
|
@ -146,8 +157,6 @@ class Simulation:
|
||||||
D = np.diag([np.exp(-1 / self.sample.T2 * relax * dt), np.exp(-1 / self.sample.T2 * relax * dt), np.exp(-1 / self.sample.T1 * relax * dt)])
|
D = np.diag([np.exp(-1 / self.sample.T2 * relax * dt), np.exp(-1 / self.sample.T2 * relax * dt), np.exp(-1 / self.sample.T1 * relax * dt)])
|
||||||
b = np.array([0, 0, self.initial_magnetization]) - np.array([0, 0, self.initial_magnetization * np.exp(-1 / self.sample.T1 * relax * dt)])
|
b = np.array([0, 0, self.initial_magnetization]) - np.array([0, 0, self.initial_magnetization * np.exp(-1 / self.sample.T1 * relax * dt)])
|
||||||
|
|
||||||
logger.debug(b)
|
|
||||||
|
|
||||||
for n in range(Nu): # time loop
|
for n in range(Nu): # time loop
|
||||||
|
|
||||||
Mrot = np.zeros((3, Nx))
|
Mrot = np.zeros((3, Nx))
|
||||||
|
@ -187,7 +196,6 @@ class Simulation:
|
||||||
"""
|
"""
|
||||||
# Df is the Full Width at Half Maximum (FWHM) of Lorentzian in Hz
|
# Df is the Full Width at Half Maximum (FWHM) of Lorentzian in Hz
|
||||||
Df = 1 / np.pi / self.sample.T2_star
|
Df = 1 / np.pi / self.sample.T2_star
|
||||||
logger.debug("Df: %s", Df)
|
|
||||||
|
|
||||||
# Randomly generating frequency offset using Cauchy distribution
|
# Randomly generating frequency offset using Cauchy distribution
|
||||||
uu = np.random.rand(self.number_isochromats, 1) - 0.5
|
uu = np.random.rand(self.number_isochromats, 1) - 0.5
|
||||||
|
@ -195,10 +203,26 @@ class Simulation:
|
||||||
|
|
||||||
# xdis is a spatial function, but it is being repurposed here to convert through the gradient to a phase difference per time -> T2 dispersion of the isochromats
|
# xdis is a spatial function, but it is being repurposed here to convert through the gradient to a phase difference per time -> T2 dispersion of the isochromats
|
||||||
xdis = np.linspace(-1, 1, self.number_isochromats)
|
xdis = np.linspace(-1, 1, self.number_isochromats)
|
||||||
logger.debug(self.sample.gamma)
|
|
||||||
xdis = (foffr.T * 1e-6) / (self.sample.gamma / 2 / np.pi) / (self.gradient * 1e-3)
|
xdis = (foffr.T * 1e-6) / (self.sample.gamma / 2 / np.pi) / (self.gradient * 1e-3)
|
||||||
|
|
||||||
return xdis
|
return xdis
|
||||||
|
|
||||||
|
def calculate_reference_voltage(self):
|
||||||
|
"""This calculates the reference voltage of the measurement setup for the sample at a certain temperature.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
reference_voltage : float
|
||||||
|
The reference voltage of the measurement setup for the sample at a certain temperature in Volts.
|
||||||
|
"""
|
||||||
|
u = 4 * np.pi * 1e-7 # permeability of free space
|
||||||
|
|
||||||
|
magnetization = self.sample.gamma * 2 * self.sample.atoms / (2 * self.sample.nuclear_spin +1) * h**2 * self.sample.resonant_frequency/ Boltzmann / self.temperature * self.sample.spin_factor
|
||||||
|
|
||||||
|
coil_crossection = np.pi * (self.diameter_coil / 2) ** 2
|
||||||
|
reference_voltage = self.number_turns * coil_crossection * u * self.sample.gamma * 2 * self.sample.atoms / (2 * self.sample.nuclear_spin +1) * h**2 * self.sample.resonant_frequency **2 / Boltzmann / self.temperature * self.sample.spin_factor
|
||||||
|
reference_voltage = reference_voltage * self.sample.powder_factor * self.sample.filling_factor
|
||||||
|
return reference_voltage
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sample(self) -> Sample:
|
def sample(self) -> Sample:
|
||||||
|
@ -307,3 +331,12 @@ class Simulation:
|
||||||
@gain.setter
|
@gain.setter
|
||||||
def gain(self, gain):
|
def gain(self, gain):
|
||||||
self._gain = gain
|
self._gain = gain
|
||||||
|
|
||||||
|
@property
|
||||||
|
def temperature(self) -> float:
|
||||||
|
"""Temperature of the sample."""
|
||||||
|
return self._temperature
|
||||||
|
|
||||||
|
@temperature.setter
|
||||||
|
def temperature(self, temperature):
|
||||||
|
self._temperature = temperature
|
||||||
|
|
|
@ -16,9 +16,9 @@ class TestSimulation(unittest.TestCase):
|
||||||
gamma=4.342e7, #Hz/T
|
gamma=4.342e7, #Hz/T
|
||||||
nuclear_spin=9/2,
|
nuclear_spin=9/2,
|
||||||
spin_factor=2,
|
spin_factor=2,
|
||||||
powder_factor=1,
|
powder_factor=0.75,
|
||||||
filling_factor=0.7,
|
filling_factor=0.7,
|
||||||
T1=82.6e-5, #s
|
T1=83.5e-5, #s
|
||||||
T2=396e-6, #s
|
T2=396e-6, #s
|
||||||
T2_star=50e-6, #s
|
T2_star=50e-6, #s
|
||||||
)
|
)
|
||||||
|
@ -50,12 +50,16 @@ class TestSimulation(unittest.TestCase):
|
||||||
power_amplifier_power=500,
|
power_amplifier_power=500,
|
||||||
pulse = self.pulse,
|
pulse = self.pulse,
|
||||||
averages = 1,
|
averages = 1,
|
||||||
gain = 6000
|
gain = 6000,
|
||||||
|
temperature=77,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_simulation(self):
|
def test_simulation(self):
|
||||||
M = self.simulation.simulate()
|
M = self.simulation.simulate()
|
||||||
|
|
||||||
# Plotting the results
|
# Plotting the results
|
||||||
plt.plot(self.time_array, abs(M))
|
plt.plot(self.time_array * 1e6, abs(M))
|
||||||
|
plt.xlabel("Time (µs)")
|
||||||
|
plt.ylabel("Magnetization (a.u.)")
|
||||||
|
plt.title("FID of BiPh3")
|
||||||
plt.show()
|
plt.show()
|
Loading…
Reference in a new issue