Source code for Source_Doc.PyOR_NonlinearNMR

"""
PyOR - Python On Resonance

Author:
    Vineeth Francis Thalakottoor Jose Chacko

Email:
    vineethfrancis.physics@gmail.com

Description:
    This file defines the `NonLinear` class, which provides utilities for incorporating 
    non-linear effects such as radiation damping, dipolar shifts, and Gaussian noise 
    into spin dynamics simulations in nuclear magnetic resonance (NMR).

    The `NonLinear` class enables the simulation of complex feedback mechanisms 
    that are critical for modeling real-world magnetic 
    resonance experiments.
"""


import numpy as np
from numpy import linalg as lina
import re
from IPython.display import display, Latex, Math
from sympy.physics.quantum.cg import CG

try:
    from .PyOR_QuantumObject import QunObj
except ImportError:
    from PyOR_QuantumObject import QunObj


[docs] class NonLinear: def __init__(self, class_QS): """ Initialize the NonLinear class with parameters from the quantum system. Parameters ---------- class_QS : object An instance of the quantum system class which provides simulation parameters. """ self.class_QS = class_QS self.NGaussian = self.class_QS.NGaussian self.Rdamping = self.class_QS.Rdamping self.Sx = self.class_QS.Sx_ self.Sy = self.class_QS.Sy_ self.Sz = self.class_QS.Sz_ self.RDphase = self.class_QS.RDphase self.RDxi = self.class_QS.RDxi self.N_mean = self.class_QS.N_mean self.N_std = self.class_QS.N_std self.N_length = self.class_QS.N_length #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # Radiation Damping and Dipolar Effects #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[docs] def Noise_Gaussian(self, N_mean, N_std, N_length): """ Generate Gaussian noise samples. Parameters ---------- N_mean : float Mean of the Gaussian distribution. N_std : float Standard deviation of the Gaussian distribution. N_length : int Number of random samples to generate. Returns ------- np.ndarray An array of random noise values or zeros depending on configuration. """ if self.NGaussian: return np.random.normal(N_mean, N_std, N_length) else: return np.zeros((N_length))
[docs] def DipoleShift(self, rho): """ Compute dipolar shift from the current density matrix. Parameters ---------- rho : np.ndarray Density matrix of the system. Returns ------- float Average dipolar field shift along z-direction. """ BavgD = 0.0 if self.class_QS.Dipole_Shift: BavgD += self.class_QS.Shift_para * np.trace(np.matmul(np.sum(self.Sz, axis=0), rho)) return BavgD
[docs] def Radiation_Damping(self, rho): """ Compute the radiation damping field based on the system state. This function calculates the transverse magnetization contributions to radiation damping and optionally adds spin noise in two different ways depending on simulation settings. Parameters ---------- rho : np.ndarray Density matrix of the spin system. Returns ------- complex The effective radiation damping field including optional noise. Real part corresponds to the x-component, imaginary part to y-component. """ Sx = self.Sx Sy = self.Sy Brd = 0.0 RDnoise_x = 0.0 RDnoise_y = 0.0 RDnoise_amp = 0.0 RDnoise_ph = 0.0 if self.NGaussian: # Option 1: Add noise separately in real and imaginary components RDnoise_x = self.Noise_Gaussian(self.N_mean, self.N_std, self.N_length) RDnoise_y = self.Noise_Gaussian(self.N_mean, self.N_std, self.N_length) # Option 2: Add noise using amplitude and phase representation RDnoise_amp = self.Noise_Gaussian(self.N_mean, self.N_std, self.N_length) RDnoise_ph = np.random.uniform(low=0.0, high=360.0, size=(self.N_length)) if self.Rdamping: for i in range(self.class_QS.Nspins): Brd_1 = -1j * self.RDxi[i] * np.trace(np.matmul(Sx[i] + 1j * Sy[i], rho)) Brd_1 *= np.exp(-1j * np.pi * self.RDphase[i] / 180.0) Brd += Brd_1 # Add either complex component noise or amplitude-phase based noise if False: return Brd + RDnoise_x + 1j * RDnoise_y else: return Brd + RDnoise_amp * np.exp(1j * RDnoise_ph)