soprano.nmr.tensor

Contents

soprano.nmr.tensor#

Contains the NMRTensor class, simplifying the process of diagonalisation of an NMR tensor as well as its representation in multiple conventions

Functions

check_quadrupole_active(func)

Decorator to check if the nucleus is quadrupole active.

contains_nmr_tensors(values)

Check if values contain NMRTensor objects at any nesting level

has_reference(func)

Decorator to check if a reference chemical shift has been set.

Classes

ElectricFieldGradient(data, species[, ...])

Class containing an electric field gradient tensor, a subclass of NMRTensor.

MagneticShielding(data, species[, order, ...])

Class containing a magnetic shielding tensor, a subclass of NMRTensor

NMRTensor(data[, order])

Class containing an NMR tensor, useful to access all its most important properties and representations.

TensorConvention(value[, names, module, ...])

class soprano.nmr.tensor.ElectricFieldGradient(data, species, order=TensorConvention.NQR, quadrupole_moment=None, gamma=None)[source]#

Bases: NMRTensor

Class containing an electric field gradient tensor, a subclass of NMRTensor.

The EFG tensor is a symmetric 3x3 matrix, with a trace of zero and has therefore only 5 independent degrees of freedom. These are often captured using the following:

  • largest absolute eigenvalue (\(V_{zz}\))

  • the asymmetry parameter ( \(\eta\) )

  • α, β, γ Euler angles describing the orientation of the tensor.

The principal components of the tensor are the eigenvalues of the tensor, and they are usually sorted according to the NQR convention ( \(|V_{zz}| \geq |V_{yy}| \geq |V_{xx}|\) ). Note however, that Simpson uses the convention \(|V_{zz}| \geq |V_{xx}| \geq |V_{yy}|\) . This is can be compensated for by using the order parameter when creating the tensor.

Note that some conventions use the reduced anisotropy ( \(\zeta\) ) instead of the asymmetry parameter.

Initialise the EFGTensor

Create an EFGTensor object from a 3x3 matrix.

Parameters:
  • data (np.ndarray or tuple) – 3x3 matrix containing the tensor, or pair [evals, evecs] for the symmetric part alone.

  • species (str) – Isotope symbol of the atom the tensor refers to. e.g ‘2H’, ‘13C’

  • order (str) – Order to use for eigenvalues/eigenvectors. Can be ‘i’ (ORDER_INCREASING), ‘d’ (ORDER_DECREASING), ‘h’ (ORDER_HAEBERLEN) or ‘n’ (ORDER_NQR). Default is ‘h’ for EFG tensors.

  • quadrupole_moment (float) – Quadrupole moment of the nucleus in millibarn.

  • gamma (float) – Nuclear gyromagnetic ratio in rad/(s*T). If not provided, will be looked up using the species.

property Cq: float#

Calculates the quadrupolar constant in Hz for this EFG tensor. The constant will be zero for non-quadrupole active nuclei. The quadrupole moment used is that for the nucleus of the isotope specified in the species attribute.

This property is defined as

\[C_Q = \frac{e^2qQ}{h}\]

in Hz. It is important to keep in mind that therefore this represents a frequency; the corresponding ‘omega’ (pulsation) would be the same value multiplied by 2*pi. This is, for example, exactly the value required as input in Simpson’s SPINSYS section.

Returns:

Quadrupolar constant value in Hz.

Return type:

float

property NQR#

EFGNQR

Produces an array containing NQR transition frequencies (in Hz) for every atom in a system. For non-quadrupole active nuclei, the we return an empty dictionary. Unless specified otherwise, the spin and quadrupole moment of the most common NMR-active isotope is used.

For reference: the value returned by this property is defined as

\[A = \frac{V_{zz} Q}{4I(2I - 1)} fq = 3A(2m+1)\sqrt{1 + \eta^2/3}\]

in Hz. It is important to keep in mind that therefore this represents a frequency; the corresponding ‘omega’ (pulsation) would be the same value multiplied by 2*pi.

property PAS#

Returns the principal axis system (PAS) of the tensor.

property Pq: float#

Calculates the quadrupolar product in Hz for this EFG tensor.

\[P_Q = C_Q (1+\frac{\eta_Q^2}{3})^{1/2}\]
Returns:

Quadrupolar product value in Hz.

Return type:

float

property Vzz: float#

Returns the largest absolute eigenvalue of the tensor ( the principal component of the EFG tensor). This should be in atomic units (a.u.) if read in from e.g. a magres file.

__array__(dtype=None)#

Return a numpy array representation of the tensor. Required for NDArrayOperatorsMixin.

__array_ufunc__(ufunc, method, *inputs, **kwargs)#

Handle NumPy’s universal functions for tensor operations. This enables proper handling of arithmetic operations.

__repr__()#

Return a string representation of the tensor

Return type:

str

__str__()[source]#

Neatly formatted string representation of the tensor and essential parameters.

_check_binary_op_params(other)#

Check that parameters of two tensors are compatible for binary operations. Warns about any inconsistencies.

Parameters:

other (NMRTensor) – Another tensor to compare parameters with

classmethod _check_compatible(tensors)#

Tensor compatibility checks.

Parameters:

tensors (list[T]) – list of NMRTensor objects

Raises:

ValueError – If the tensors are not compatible

Return type:

None

_create_like(data)#

Create a new tensor of the same type with the provided data but preserving other properties of this tensor.

Parameters:

data (np.ndarray) – New tensor data

Returns:

A new tensor with the same properties

Return type:

NMRTensor

property _initialisation_params#

Return the parameters used for initialisation of the tensor.

copy()#

Create a copy of the NMRTensor object.

Returns:

A new NMRTensor object with the same data and properties.

Return type:

NMRTensor

property degeneracy#

Returns the degeneracy of the tensor. For example, a tensor with eigenvalues [1, 1, 1] has a degeneracy of 3. A tensor with eigenvalues [1, 1, 2] has a degeneracy of 2. A tensor with eigenvalues [1, 2, 3] has a degeneracy of 1.

equivalent_euler_angles(convention='zyz', passive=False)#

Returns the equivalent Euler angles of the tensor.

Parameters:
  • use (convention {str} -- Euler angles convention to)

  • (default – {‘zyz’})

  • angles (passive {bool} -- Whether to return the passive Euler)

  • (default – {False})

Returns:

np.array – Euler angles in radians.

Size of the array is (4, 3) as there are 4 equivalent sets of Euler angles.

equivalent_euler_to(other, convention='zyz', passive=False)#

Returns the equivalent Euler angles that rotate the tensor to the other tensor.

property eta: float#

Returns the asymmetry parameter of the tensor.

euler_angles(convention='zyz', passive=False, degrees=False)#

Return Euler angles of the PAS for this tensor in the required convention (currently supported: zyz, zxz).

Parameters:
  • {Literal["zyz" (convention)

  • use ("zxz"]} -- Euler angles convention to)

  • (default – {‘zyz’})

  • angles (passive {bool} -- Whether to return the passive Euler)

  • (default – {False})

  • convention (Literal['zyz', 'zxz'])

  • passive (bool)

  • degrees (bool)

Returns:

np.ndarray – Euler angles in radians. Size of the array is (3,)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

euler_to(other, convention='zyz', passive=False, eps=1e-06)#

Returns the Euler angles that rotate the tensor to the other tensor.

get_larmor_frequency(Bext)[source]#

Returns the Larmor frequency of the nucleus in an external magnetic field in Hz.

\[\nu_L = \gamma B_{ext} / (2\pi)\]

where \(\gamma\) is the gyromagnetic ratio of the nucleus in rad/(s*T) and \(B_{ext}\) is the external magnetic field in T.

Parameters:

Bext (float) – External magnetic field in T.

Returns:

Larmor frequency in Hz.

Return type:

float

property is_quadrupole_active: bool#

Returns True if the nucleus is quadrupole active, False otherwise.

static make_dipolar(a, i, j, cell=[0, 0, 0], isotopes={}, isotope_i=None, isotope_j=None, rotation_axis=None)#

Create a dipolar NMRTensor

Create a dipolar NMR tensor from an atoms object and the indices of two atoms. Values are in Hz.

Args:
a (ase.Atoms): Atoms object of the structure to compute the
tensor for
i (int): index of first atom
j (int): index of second atom
cell (np.array): vector of the cell of the second atom, for
couplings between atoms in different cells.
By default is [0,0,0].
isotopes (dict): dictionary of specific isotopes to use, by element
symbol. If the isotope doesn’t exist an error will
be raised.
isotope_i (int): isotope of atom i. To be used if
different atoms of the same element are supposed
to be of different isotopes. If None it will fall
back on the previous definitions. Otherwise it
overrides everything else.
isotope_j (int): isotope of atom j. See above.
rotation_axis (np.array): an axis around which the selected pair
is rotating. If present, the tensor
will be averaged for infinitely fast
rotation around it.
Returns:
diptens (NMRTensor): an NMRTensor object with the dipolar
coupling matrix as data.
make_isotropic_like()#

Create an isotropic NMRTensor with the same isotropy value as this tensor. The initialisation parameters are kept the same.

Returns:

An isotropic NMRTensor object.

Return type:

NMRTensor

classmethod mean(tensor_list, axis=None, weights=None)#

Calculate the mean of a list of NMRTensor objects along a specified axis.

Parameters:
  • tensor_list (list[Any]) – list or nested list of NMRTensor objects to average. For a 2D array with shape [N, M], this represents N rows of M tensors.

  • axis (int, optional) – Axis along which the mean is computed: - None: Average all tensors into a single tensor - 0: Average over rows (result has length M) - 1: Average over columns (result has length N) - etc. for higher dimensions

  • weights (np.ndarray, optional) – Array of weights to use for weighted average. Must have length matching the dimension being averaged: - If axis=None: weights should have same length as flattened tensor_list - If axis=0: weights should have length N (one weight per row) - If axis=1: weights should have length M (one weight per column)

Returns:

A new tensor or list of tensors with averaged values

Return type:

Union[T, list[T]]

Raises:

ValueError – If tensor_list is empty, has incompatible dimensions, or if weights have incorrect shape

rotation_to(other)#

Returns the rotation matrix that rotates the tensor to the other tensor.

property spherical_repr#

Spherical representation of the tensor

Returns a 3x3x3 array containing the isotropic, antisymmetric and symmetric parts of the tensor. The isotropic part is the average of the trace of the tensor, the antisymmetric part is the difference between the tensor and its transpose divided by 2, and the symmetric part is the sum of the tensor and its transpose divided by 2 minus the isotropic part. This construction is such that the sum of the components is equal to the original tensor.

\[\sigma = \sigma_{iso} + \sigma_{A} + \sigma_{S}\]

where

\[ \begin{align}\begin{aligned}\sigma_{iso} = \frac{1}{3} \text{Tr}(\sigma) \mathbf{I}\\\sigma_{A} = \frac{1}{2} (\sigma - \sigma^T)\\\sigma_{S} = \frac{1}{2} (\sigma + \sigma^T) - \sigma_{iso}\end{aligned}\end{align} \]
Returns:

np.ndarray – 3x3x3 array containing the isotropic, antisymmetric and symmetric parts of the tensor

property zeta: float#

Returns the reduced anisotropy of the tensor.

class soprano.nmr.tensor.MagneticShielding(data, species, order=TensorConvention.Haeberlen, reference=None, gradient=-1.0, tag=None)[source]#

Bases: NMRTensor

Class containing a magnetic shielding tensor, a subclass of NMRTensor

It provides easy access to common representations of the tensor in common conventions such as: IUPAC, Herzfeld-Berger, Haeberlen, Maryland, and Mehring. For more information on these conventions, see the documentation for the corresponding NamedTuple and also [2]

Initialise the MSTensor

Create an MSTensor object from a 3x3 matrix.

Parameters:
  • species (str) – Element or isotope symbol of the atom the tensor refers to. e.g ‘H’, ‘C’, ‘13C’

  • data (np.ndarray or tuple) – 3x3 matrix containing the tensor, or pair [evals, evecs] for the symmetric part alone.

  • order (str) – Order to use for eigenvalues/eigenvectors. Can be ‘i’ (ORDER_INCREASING), ‘d’ (ORDER_DECREASING), ‘h’ (ORDER_HAEBERLEN) or ‘n’ (ORDER_NQR). Default is ‘h’ for MS tensors.

  • reference (float) – Reference chemical shift for the tensor. If set, the isotropic chemical shift can be calculated. Defaults to None.

  • gradient (float) – Gradient for the magnetic shielding to shift conversion. Defaults to -1.0.

  • tag (str) – Optional tag to identify the tensor. In a magres file, this would be the ‘ms_sometag’ though for most magres file ms is not decomposed into contributions and the tag is simply ‘ms’. By default, this is None.

class HaeberlenShielding(sigma_iso, zeta, delta, eta)[source]#

Bases: NamedTuple

Haeberlen convention [1] follows this ordering:

\[|\sigma_{zz} - \sigma_{iso} | \geq |\sigma_{xx} - \sigma_{iso} | \geq |\sigma_{yy} - \sigma_{iso} |\]

and uses the following parameters to describe the shielding tensor:

  • \(\sigma_{iso}\) : isotropic magnetic shielding

  • \(\zeta = \sigma_{zz} - \sigma_{iso}\) : the reduced anisotropy

  • \(\Delta = \sigma_{zz} - (\sigma_{xx} + \sigma_{yy}) / 2 = 3\zeta / 2\) : the anisotropy

  • \(\eta = (\sigma_{yy} - \sigma_{xx}) / \zeta\) : the asymmetry ( \(0 \leq \eta \leq +1\) )

Create new instance of HaeberlenShielding(sigma_iso, zeta, delta, eta)

Parameters:
  • sigma_iso (float)

  • zeta (float)

  • delta (float)

  • eta (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new HaeberlenShielding object from a sequence or iterable

_replace(**kwds)#

Return a new HaeberlenShielding object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

delta: float#

Alias for field number 2

eta: float#

Alias for field number 3

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

sigma_iso: float#

Alias for field number 0

zeta: float#

Alias for field number 1

class HaeberlenShift(delta_iso, zeta, delta, eta)[source]#

Bases: NamedTuple

The same as the HaeberlenShielding, but for chemical shifts. i.e. sigma -> delta. Therefore:

\[|\delta_{zz} - \delta_{iso} | \geq |\delta_{xx} - \delta_{iso} | \geq |\delta_{yy} - \delta_{iso} |\]

Note that there is a corresponding swap in the sign of the anisotropy and reduced anisotropy values.

Create new instance of HaeberlenShift(delta_iso, zeta, delta, eta)

Parameters:
  • delta_iso (float)

  • zeta (float)

  • delta (float)

  • eta (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new HaeberlenShift object from a sequence or iterable

_replace(**kwds)#

Return a new HaeberlenShift object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

delta: float#

Alias for field number 2

delta_iso: float#

Alias for field number 0

eta: float#

Alias for field number 3

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

zeta: float#

Alias for field number 1

class HerzfeldBergerShielding(sigma_iso, omega, kappa)[source]#

Bases: NamedTuple

Herzfeld-Berger convention [3] uses the following parameters:

  • \(\sigma_{iso}\) : isotropic magnetic shielding

  • \(\Omega = \sigma_{33} - \sigma_{11}\) (where these are the max and min principal components respectively): the span

  • \(\kappa = 3(\sigma_{iso} - \sigma_{22}) / \Omega\) (where \(\sigma_{22}\) is the median principal component): the skew

Create new instance of HerzfeldBergerShielding(sigma_iso, omega, kappa)

Parameters:
  • sigma_iso (float)

  • omega (float)

  • kappa (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new HerzfeldBergerShielding object from a sequence or iterable

_replace(**kwds)#

Return a new HerzfeldBergerShielding object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

kappa: float#

Alias for field number 2

omega: float#

Alias for field number 1

sigma_iso: float#

Alias for field number 0

class HerzfeldBergerShift(delta_iso, omega, kappa)[source]#

Bases: NamedTuple

The same as the Herzfeld-BergerShielding, but for chemical shifts. i.e. sigma -> delta. Therefore there is a corresponding swap in the sign of the skew (but not the span) value:

\[\Omega = \delta_{11} - \delta_{33} \kappa = 3(\delta_{22} - \delta_{iso}) / \Omega\]

Create new instance of HerzfeldBergerShift(delta_iso, omega, kappa)

Parameters:
  • delta_iso (float)

  • omega (float)

  • kappa (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new HerzfeldBergerShift object from a sequence or iterable

_replace(**kwds)#

Return a new HerzfeldBergerShift object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

delta_iso: float#

Alias for field number 0

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

kappa: float#

Alias for field number 2

omega: float#

Alias for field number 1

class IUPACShielding(sigma_iso, sigma_11, sigma_22, sigma_33)[source]#

Bases: NamedTuple

IUPAC convention [4] (equivalent to the Mehring convention [5]). Follows the high frequency-positive order. Therefore: \(\sigma_{11}\) corresponds to the direction of least shielding, with the highest frequency, \(\sigma_{33}\) corresponds to the direction of highest shielding, with the lowest frequency.

\[\sigma_{11} \leq \sigma_{22} \leq \sigma_{33}\]

The isotropic value, \(\sigma_{iso}\), is the average values of the principal components, and corresponds to the center of gravity of the line shape.

Note that the IUPAC convention is equivalent to the Mehring convention.

Create new instance of IUPACShielding(sigma_iso, sigma_11, sigma_22, sigma_33)

Parameters:
  • sigma_iso (float)

  • sigma_11 (float)

  • sigma_22 (float)

  • sigma_33 (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new IUPACShielding object from a sequence or iterable

_replace(**kwds)#

Return a new IUPACShielding object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

sigma_11: float#

Alias for field number 1

sigma_22: float#

Alias for field number 2

sigma_33: float#

Alias for field number 3

sigma_iso: float#

Alias for field number 0

class IUPACShift(delta_iso, delta_11, delta_22, delta_33)[source]#

Bases: NamedTuple

The same as the IUPACShielding, but for chemical shifts. i.e. sigma -> delta. Therefore:

\[\delta_{33} \leq \delta_{22} \leq \delta_{11}\]

Create new instance of IUPACShift(delta_iso, delta_11, delta_22, delta_33)

Parameters:
  • delta_iso (float)

  • delta_11 (float)

  • delta_22 (float)

  • delta_33 (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new IUPACShift object from a sequence or iterable

_replace(**kwds)#

Return a new IUPACShift object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

delta_11: float#

Alias for field number 1

delta_22: float#

Alias for field number 2

delta_33: float#

Alias for field number 3

delta_iso: float#

Alias for field number 0

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

class MarylandShielding(sigma_iso, omega, kappa)[source]#

Bases: HerzfeldBergerShielding

The same as the HerzfeldBergerShielding notation.

Create new instance of HerzfeldBergerShielding(sigma_iso, omega, kappa)

Parameters:
  • sigma_iso (float)

  • omega (float)

  • kappa (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new HerzfeldBergerShielding object from a sequence or iterable

_replace(**kwds)#

Return a new HerzfeldBergerShielding object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

kappa: float#

Alias for field number 2

omega: float#

Alias for field number 1

sigma_iso: float#

Alias for field number 0

class MarylandShift(delta_iso, omega, kappa)[source]#

Bases: HerzfeldBergerShift

The same as the HerzfeldBergerShift.

Create new instance of HerzfeldBergerShift(delta_iso, omega, kappa)

Parameters:
  • delta_iso (float)

  • omega (float)

  • kappa (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new HerzfeldBergerShift object from a sequence or iterable

_replace(**kwds)#

Return a new HerzfeldBergerShift object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

delta_iso: float#

Alias for field number 0

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

kappa: float#

Alias for field number 2

omega: float#

Alias for field number 1

class MehringShielding(sigma_iso, sigma_11, sigma_22, sigma_33)[source]#

Bases: IUPACShielding

Mehring convention [5] is equivalent to the IUPAC convention.

Create new instance of IUPACShielding(sigma_iso, sigma_11, sigma_22, sigma_33)

Parameters:
  • sigma_iso (float)

  • sigma_11 (float)

  • sigma_22 (float)

  • sigma_33 (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new IUPACShielding object from a sequence or iterable

_replace(**kwds)#

Return a new IUPACShielding object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

sigma_11: float#

Alias for field number 1

sigma_22: float#

Alias for field number 2

sigma_33: float#

Alias for field number 3

sigma_iso: float#

Alias for field number 0

class MehringShift(delta_iso, delta_11, delta_22, delta_33)[source]#

Bases: IUPACShift

The same as the MehringShielding, but for chemical shifts. i.e. sigma -> delta.

Create new instance of IUPACShift(delta_iso, delta_11, delta_22, delta_33)

Parameters:
  • delta_iso (float)

  • delta_11 (float)

  • delta_22 (float)

  • delta_33 (float)

_asdict()#

Return a new dict which maps field names to their values.

classmethod _make(iterable)#

Make a new IUPACShift object from a sequence or iterable

_replace(**kwds)#

Return a new IUPACShift object replacing specified fields with new values

count(value, /)#

Return number of occurrences of value.

delta_11: float#

Alias for field number 1

delta_22: float#

Alias for field number 2

delta_33: float#

Alias for field number 3

delta_iso: float#

Alias for field number 0

index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

property PAS#

Returns the principal axis system (PAS) of the tensor.

__array__(dtype=None)#

Return a numpy array representation of the tensor. Required for NDArrayOperatorsMixin.

__array_ufunc__(ufunc, method, *inputs, **kwargs)#

Handle NumPy’s universal functions for tensor operations. This enables proper handling of arithmetic operations.

__repr__()#

Return a string representation of the tensor

Return type:

str

__str__()[source]#

Neatly formatted string representation of the tensor and Haeberlen description.

_check_binary_op_params(other)#

Check that parameters of two tensors are compatible for binary operations. Warns about any inconsistencies.

Parameters:

other (NMRTensor) – Another tensor to compare parameters with

classmethod _check_compatible(tensors)#

Tensor compatibility checks.

Parameters:

tensors (list[T]) – list of NMRTensor objects

Raises:

ValueError – If the tensors are not compatible

Return type:

None

_create_like(data)#

Create a new tensor of the same type with the provided data but preserving other properties of this tensor.

Parameters:

data (np.ndarray) – New tensor data

Returns:

A new tensor with the same properties

Return type:

NMRTensor

static _get_referenced_eigenvalues(eigenvalues, reference, gradient)[source]#

Get the referenced eigenvalues for the magnetic shielding tensor.

Parameters:
  • eigenvalues (Union[list, np.ndarray]) – Eigenvalues of the magnetic shielding tensor (ppm)

  • reference (float) – Reference shielding (ppm)

  • gradient (float) – Gradient for the magnetic shielding to shift conversion

Returns:

Chemical shift eigenvalues (ppm)

Return type:

np.ndarray

property _initialisation_params#

Return the parameters used for initialisation of the tensor.

copy()#

Create a copy of the NMRTensor object.

Returns:

A new NMRTensor object with the same data and properties.

Return type:

NMRTensor

property degeneracy#

Returns the degeneracy of the tensor. For example, a tensor with eigenvalues [1, 1, 1] has a degeneracy of 3. A tensor with eigenvalues [1, 1, 2] has a degeneracy of 2. A tensor with eigenvalues [1, 2, 3] has a degeneracy of 1.

property element#

Returns the element of the tensor. Species could have the isotope info, but here we just want the element. e.g. 13C -> C 1H -> H

equivalent_euler_angles(convention='zyz', passive=False)#

Returns the equivalent Euler angles of the tensor.

Parameters:
  • use (convention {str} -- Euler angles convention to)

  • (default – {‘zyz’})

  • angles (passive {bool} -- Whether to return the passive Euler)

  • (default – {False})

Returns:

np.array – Euler angles in radians.

Size of the array is (4, 3) as there are 4 equivalent sets of Euler angles.

equivalent_euler_to(other, convention='zyz', passive=False)#

Returns the equivalent Euler angles that rotate the tensor to the other tensor.

euler_angles(convention='zyz', passive=False, degrees=False)#

Return Euler angles of the PAS for this tensor in the required convention (currently supported: zyz, zxz).

Parameters:
  • {Literal["zyz" (convention)

  • use ("zxz"]} -- Euler angles convention to)

  • (default – {‘zyz’})

  • angles (passive {bool} -- Whether to return the passive Euler)

  • (default – {False})

  • convention (Literal['zyz', 'zxz'])

  • passive (bool)

  • degrees (bool)

Returns:

np.ndarray – Euler angles in radians. Size of the array is (3,)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

euler_to(other, convention='zyz', passive=False, eps=1e-06)#

Returns the Euler angles that rotate the tensor to the other tensor.

property haeberlen_shielding: HaeberlenShielding#

The magnetic shielding tensor in Haeberlen Notation.

property herzfeldberger_shielding: HerzfeldBergerShielding#

The shielding tensor in Herzfeld-Berger Notation.

property iupac_shielding: IUPACShielding#

The magnetic shielding tensor in IUPAC Notation.

static make_dipolar(a, i, j, cell=[0, 0, 0], isotopes={}, isotope_i=None, isotope_j=None, rotation_axis=None)#

Create a dipolar NMRTensor

Create a dipolar NMR tensor from an atoms object and the indices of two atoms. Values are in Hz.

Args:
a (ase.Atoms): Atoms object of the structure to compute the
tensor for
i (int): index of first atom
j (int): index of second atom
cell (np.array): vector of the cell of the second atom, for
couplings between atoms in different cells.
By default is [0,0,0].
isotopes (dict): dictionary of specific isotopes to use, by element
symbol. If the isotope doesn’t exist an error will
be raised.
isotope_i (int): isotope of atom i. To be used if
different atoms of the same element are supposed
to be of different isotopes. If None it will fall
back on the previous definitions. Otherwise it
overrides everything else.
isotope_j (int): isotope of atom j. See above.
rotation_axis (np.array): an axis around which the selected pair
is rotating. If present, the tensor
will be averaged for infinitely fast
rotation around it.
Returns:
diptens (NMRTensor): an NMRTensor object with the dipolar
coupling matrix as data.
make_isotropic_like()#

Create an isotropic NMRTensor with the same isotropy value as this tensor. The initialisation parameters are kept the same.

Returns:

An isotropic NMRTensor object.

Return type:

NMRTensor

property maryland_shielding: MarylandShielding#

The magnetic shielding tensor in Maryland Notation.

classmethod mean(tensor_list, axis=None, weights=None)#

Calculate the mean of a list of NMRTensor objects along a specified axis.

Parameters:
  • tensor_list (list[Any]) – list or nested list of NMRTensor objects to average. For a 2D array with shape [N, M], this represents N rows of M tensors.

  • axis (int, optional) – Axis along which the mean is computed: - None: Average all tensors into a single tensor - 0: Average over rows (result has length M) - 1: Average over columns (result has length N) - etc. for higher dimensions

  • weights (np.ndarray, optional) – Array of weights to use for weighted average. Must have length matching the dimension being averaged: - If axis=None: weights should have same length as flattened tensor_list - If axis=0: weights should have length N (one weight per row) - If axis=1: weights should have length M (one weight per column)

Returns:

A new tensor or list of tensors with averaged values

Return type:

Union[T, list[T]]

Raises:

ValueError – If tensor_list is empty, has incompatible dimensions, or if weights have incorrect shape

property mehring_shielding: MehringShielding#

The magnetic shielding tensor in Mehring Notation.

rotation_to(other)#

Returns the rotation matrix that rotates the tensor to the other tensor.

set_gradient(gradient)[source]#

Set the gradient for this tensor.

Parameters:

gradient (float)

set_reference(reference)[source]#

Set the reference chemical shift for this tensor.

Parameters:

reference (float)

property shift_asymmetry: float#

The asymmetry is not dependent on the referencing so we just return the shielding asymmetry

property shift_span: float#

The span is not dependent on the referencing so we just return the shielding span

property spherical_repr#

Spherical representation of the tensor

Returns a 3x3x3 array containing the isotropic, antisymmetric and symmetric parts of the tensor. The isotropic part is the average of the trace of the tensor, the antisymmetric part is the difference between the tensor and its transpose divided by 2, and the symmetric part is the sum of the tensor and its transpose divided by 2 minus the isotropic part. This construction is such that the sum of the components is equal to the original tensor.

\[\sigma = \sigma_{iso} + \sigma_{A} + \sigma_{S}\]

where

\[ \begin{align}\begin{aligned}\sigma_{iso} = \frac{1}{3} \text{Tr}(\sigma) \mathbf{I}\\\sigma_{A} = \frac{1}{2} (\sigma - \sigma^T)\\\sigma_{S} = \frac{1}{2} (\sigma + \sigma^T) - \sigma_{iso}\end{aligned}\end{align} \]
Returns:

np.ndarray – 3x3x3 array containing the isotropic, antisymmetric and symmetric parts of the tensor

class soprano.nmr.tensor.NMRTensor(data, order=TensorConvention.Increasing)[source]#

Bases: NDArrayOperatorsMixin

Class containing an NMR tensor, useful to access all its most important properties and representations.

Initialise the NMRTensor

Create an NMRTensor object from a 3x3 matrix.

Parameters:
  • data (np.ndarray or tuple) – 3x3 matrix containing the tensor, or pair [evals, evecs] for the symmetric part alone.

  • order (str) – Order to use for eigenvalues/eigenvectors. Can be ‘i’ (ORDER_INCREASING), ‘d’ (ORDER_DECREASING), ‘h’ (ORDER_HAEBERLEN) or ‘n’ (ORDER_NQR). Default is ‘i’.

property PAS#

Returns the principal axis system (PAS) of the tensor.

__array__(dtype=None)[source]#

Return a numpy array representation of the tensor. Required for NDArrayOperatorsMixin.

__array_ufunc__(ufunc, method, *inputs, **kwargs)[source]#

Handle NumPy’s universal functions for tensor operations. This enables proper handling of arithmetic operations.

__eq__(other)[source]#

Check if two NMRTensor objects are equal.

NMRTensors are considered equal if they have: - The same order convention - Equivalent eigenvalues (within numerical precision) - Equivalent eigenvectors (within numerical precision)

Parameters: other (NMRTensor): Another NMRTensor object to compare against

Returns: bool: True if tensors are equivalent, False otherwise

Parameters:

other (NMRTensor)

Return type:

bool

__hash__()[source]#

Generate a hash for the NMRTensor object.

This allows NMRTensor objects to be used in sets and as dictionary keys.

Returns: int: A hash value for the NMRTensor object

Return type:

int

__repr__()[source]#

Return a string representation of the tensor

Return type:

str

__str__()[source]#

Return a string representation of the tensor. Nicely print out the 3x3 matrix (data), the conventions, and the derived properties.

Return type:

str

_check_binary_op_params(other)[source]#

Check that parameters of two tensors are compatible for binary operations. Warns about any inconsistencies.

Parameters:

other (NMRTensor) – Another tensor to compare parameters with

classmethod _check_compatible(tensors)[source]#

Tensor compatibility checks.

Parameters:

tensors (list[T]) – list of NMRTensor objects

Raises:

ValueError – If the tensors are not compatible

Return type:

None

_create_like(data)[source]#

Create a new tensor of the same type with the provided data but preserving other properties of this tensor.

Parameters:

data (np.ndarray) – New tensor data

Returns:

A new tensor with the same properties

Return type:

NMRTensor

property _initialisation_params#

Return the parameters used for the initialisation of the tensor. This is useful for debugging and understanding how the tensor was created.

copy()[source]#

Create a copy of the NMRTensor object.

Returns:

A new NMRTensor object with the same data and properties.

Return type:

NMRTensor

property degeneracy#

Returns the degeneracy of the tensor. For example, a tensor with eigenvalues [1, 1, 1] has a degeneracy of 3. A tensor with eigenvalues [1, 1, 2] has a degeneracy of 2. A tensor with eigenvalues [1, 2, 3] has a degeneracy of 1.

equivalent_euler_angles(convention='zyz', passive=False)[source]#

Returns the equivalent Euler angles of the tensor.

Parameters:
  • use (convention {str} -- Euler angles convention to)

  • (default – {‘zyz’})

  • angles (passive {bool} -- Whether to return the passive Euler)

  • (default – {False})

Returns:

np.array – Euler angles in radians.

Size of the array is (4, 3) as there are 4 equivalent sets of Euler angles.

equivalent_euler_to(other, convention='zyz', passive=False)[source]#

Returns the equivalent Euler angles that rotate the tensor to the other tensor.

euler_angles(convention='zyz', passive=False, degrees=False)[source]#

Return Euler angles of the PAS for this tensor in the required convention (currently supported: zyz, zxz).

Parameters:
  • {Literal["zyz" (convention)

  • use ("zxz"]} -- Euler angles convention to)

  • (default – {‘zyz’})

  • angles (passive {bool} -- Whether to return the passive Euler)

  • (default – {False})

  • convention (Literal['zyz', 'zxz'])

  • passive (bool)

  • degrees (bool)

Returns:

np.ndarray – Euler angles in radians. Size of the array is (3,)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

euler_to(other, convention='zyz', passive=False, eps=1e-06)[source]#

Returns the Euler angles that rotate the tensor to the other tensor.

static make_dipolar(a, i, j, cell=[0, 0, 0], isotopes={}, isotope_i=None, isotope_j=None, rotation_axis=None)[source]#

Create a dipolar NMRTensor

Create a dipolar NMR tensor from an atoms object and the indices of two atoms. Values are in Hz.

Args:
a (ase.Atoms): Atoms object of the structure to compute the
tensor for
i (int): index of first atom
j (int): index of second atom
cell (np.array): vector of the cell of the second atom, for
couplings between atoms in different cells.
By default is [0,0,0].
isotopes (dict): dictionary of specific isotopes to use, by element
symbol. If the isotope doesn’t exist an error will
be raised.
isotope_i (int): isotope of atom i. To be used if
different atoms of the same element are supposed
to be of different isotopes. If None it will fall
back on the previous definitions. Otherwise it
overrides everything else.
isotope_j (int): isotope of atom j. See above.
rotation_axis (np.array): an axis around which the selected pair
is rotating. If present, the tensor
will be averaged for infinitely fast
rotation around it.
Returns:
diptens (NMRTensor): an NMRTensor object with the dipolar
coupling matrix as data.
make_isotropic_like()[source]#

Create an isotropic NMRTensor with the same isotropy value as this tensor. The initialisation parameters are kept the same.

Returns:

An isotropic NMRTensor object.

Return type:

NMRTensor

classmethod mean(tensor_list, axis=None, weights=None)[source]#

Calculate the mean of a list of NMRTensor objects along a specified axis.

Parameters:
  • tensor_list (list[Any]) – list or nested list of NMRTensor objects to average. For a 2D array with shape [N, M], this represents N rows of M tensors.

  • axis (int, optional) – Axis along which the mean is computed: - None: Average all tensors into a single tensor - 0: Average over rows (result has length M) - 1: Average over columns (result has length N) - etc. for higher dimensions

  • weights (np.ndarray, optional) – Array of weights to use for weighted average. Must have length matching the dimension being averaged: - If axis=None: weights should have same length as flattened tensor_list - If axis=0: weights should have length N (one weight per row) - If axis=1: weights should have length M (one weight per column)

Returns:

A new tensor or list of tensors with averaged values

Return type:

Union[T, list[T]]

Raises:

ValueError – If tensor_list is empty, has incompatible dimensions, or if weights have incorrect shape

rotation_to(other)[source]#

Returns the rotation matrix that rotates the tensor to the other tensor.

property spherical_repr#

Spherical representation of the tensor

Returns a 3x3x3 array containing the isotropic, antisymmetric and symmetric parts of the tensor. The isotropic part is the average of the trace of the tensor, the antisymmetric part is the difference between the tensor and its transpose divided by 2, and the symmetric part is the sum of the tensor and its transpose divided by 2 minus the isotropic part. This construction is such that the sum of the components is equal to the original tensor.

\[\sigma = \sigma_{iso} + \sigma_{A} + \sigma_{S}\]

where

\[ \begin{align}\begin{aligned}\sigma_{iso} = \frac{1}{3} \text{Tr}(\sigma) \mathbf{I}\\\sigma_{A} = \frac{1}{2} (\sigma - \sigma^T)\\\sigma_{S} = \frac{1}{2} (\sigma + \sigma^T) - \sigma_{iso}\end{aligned}\end{align} \]
Returns:

np.ndarray – 3x3x3 array containing the isotropic, antisymmetric and symmetric parts of the tensor

class soprano.nmr.tensor.TensorConvention(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]#

Bases: str, Enum

__dir__()#

Returns public methods and other interesting attributes.

capitalize()#

Return a capitalized version of the string.

More specifically, make the first character have upper case and the rest lower case.

casefold()#

Return a version of the string suitable for caseless comparisons.

center(width, fillchar=' ', /)#

Return a centered string of length width.

Padding is done using the specified fill character (default is a space).

count(sub[, start[, end]]) int#

Return the number of non-overlapping occurrences of substring sub in string S[start:end]. Optional arguments start and end are interpreted as in slice notation.

encode(encoding='utf-8', errors='strict')#

Encode the string using the codec registered for encoding.

encoding

The encoding in which to encode the string.

errors

The error handling scheme to use for encoding errors. The default is ‘strict’ meaning that encoding errors raise a UnicodeEncodeError. Other possible values are ‘ignore’, ‘replace’ and ‘xmlcharrefreplace’ as well as any other name registered with codecs.register_error that can handle UnicodeEncodeErrors.

endswith(suffix[, start[, end]]) bool#

Return True if S ends with the specified suffix, False otherwise. With optional start, test S beginning at that position. With optional end, stop comparing S at that position. suffix can also be a tuple of strings to try.

expandtabs(tabsize=8)#

Return a copy where all tab characters are expanded using spaces.

If tabsize is not given, a tab size of 8 characters is assumed.

find(sub[, start[, end]]) int#

Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Return -1 on failure.

format(*args, **kwargs) str#

Return a formatted version of S, using substitutions from args and kwargs. The substitutions are identified by braces (‘{’ and ‘}’).

format_map(mapping) str#

Return a formatted version of S, using substitutions from mapping. The substitutions are identified by braces (‘{’ and ‘}’).

index(sub[, start[, end]]) int#

Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Raises ValueError when the substring is not found.

isalnum()#

Return True if the string is an alpha-numeric string, False otherwise.

A string is alpha-numeric if all characters in the string are alpha-numeric and there is at least one character in the string.

isalpha()#

Return True if the string is an alphabetic string, False otherwise.

A string is alphabetic if all characters in the string are alphabetic and there is at least one character in the string.

isascii()#

Return True if all characters in the string are ASCII, False otherwise.

ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too.

isdecimal()#

Return True if the string is a decimal string, False otherwise.

A string is a decimal string if all characters in the string are decimal and there is at least one character in the string.

isdigit()#

Return True if the string is a digit string, False otherwise.

A string is a digit string if all characters in the string are digits and there is at least one character in the string.

isidentifier()#

Return True if the string is a valid Python identifier, False otherwise.

Call keyword.iskeyword(s) to test whether string s is a reserved identifier, such as “def” or “class”.

islower()#

Return True if the string is a lowercase string, False otherwise.

A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string.

isnumeric()#

Return True if the string is a numeric string, False otherwise.

A string is numeric if all characters in the string are numeric and there is at least one character in the string.

isprintable()#

Return True if the string is printable, False otherwise.

A string is printable if all of its characters are considered printable in repr() or if it is empty.

isspace()#

Return True if the string is a whitespace string, False otherwise.

A string is whitespace if all characters in the string are whitespace and there is at least one character in the string.

istitle()#

Return True if the string is a title-cased string, False otherwise.

In a title-cased string, upper- and title-case characters may only follow uncased characters and lowercase characters only cased ones.

isupper()#

Return True if the string is an uppercase string, False otherwise.

A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string.

join(iterable, /)#

Concatenate any number of strings.

The string whose method is called is inserted in between each given string. The result is returned as a new string.

Example: ‘.’.join([‘ab’, ‘pq’, ‘rs’]) -> ‘ab.pq.rs’

ljust(width, fillchar=' ', /)#

Return a left-justified string of length width.

Padding is done using the specified fill character (default is a space).

lower()#

Return a copy of the string converted to lowercase.

lstrip(chars=None, /)#

Return a copy of the string with leading whitespace removed.

If chars is given and not None, remove characters in chars instead.

static maketrans()#

Return a translation table usable for str.translate().

If there is only one argument, it must be a dictionary mapping Unicode ordinals (integers) or characters to Unicode ordinals, strings or None. Character keys will be then converted to ordinals. If there are two arguments, they must be strings of equal length, and in the resulting dictionary, each character in x will be mapped to the character at the same position in y. If there is a third argument, it must be a string, whose characters will be mapped to None in the result.

partition(sep, /)#

Partition the string into three parts using the given separator.

This will search for the separator in the string. If the separator is found, returns a 3-tuple containing the part before the separator, the separator itself, and the part after it.

If the separator is not found, returns a 3-tuple containing the original string and two empty strings.

removeprefix(prefix, /)#

Return a str with the given prefix string removed if present.

If the string starts with the prefix string, return string[len(prefix):]. Otherwise, return a copy of the original string.

removesuffix(suffix, /)#

Return a str with the given suffix string removed if present.

If the string ends with the suffix string and that suffix is not empty, return string[:-len(suffix)]. Otherwise, return a copy of the original string.

replace(old, new, count=-1, /)#

Return a copy with all occurrences of substring old replaced by new.

count

Maximum number of occurrences to replace. -1 (the default value) means replace all occurrences.

If the optional argument count is given, only the first count occurrences are replaced.

rfind(sub[, start[, end]]) int#

Return the highest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Return -1 on failure.

rindex(sub[, start[, end]]) int#

Return the highest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Raises ValueError when the substring is not found.

rjust(width, fillchar=' ', /)#

Return a right-justified string of length width.

Padding is done using the specified fill character (default is a space).

rpartition(sep, /)#

Partition the string into three parts using the given separator.

This will search for the separator in the string, starting at the end. If the separator is found, returns a 3-tuple containing the part before the separator, the separator itself, and the part after it.

If the separator is not found, returns a 3-tuple containing two empty strings and the original string.

rsplit(sep=None, maxsplit=-1)#

Return a list of the substrings in the string, using sep as the separator string.

sep

The separator used to split the string.

When set to None (the default value), will split on any whitespace character (including n r t f and spaces) and will discard empty strings from the result.

maxsplit

Maximum number of splits. -1 (the default value) means no limit.

Splitting starts at the end of the string and works to the front.

rstrip(chars=None, /)#

Return a copy of the string with trailing whitespace removed.

If chars is given and not None, remove characters in chars instead.

split(sep=None, maxsplit=-1)#

Return a list of the substrings in the string, using sep as the separator string.

sep

The separator used to split the string.

When set to None (the default value), will split on any whitespace character (including n r t f and spaces) and will discard empty strings from the result.

maxsplit

Maximum number of splits. -1 (the default value) means no limit.

Splitting starts at the front of the string and works to the end.

Note, str.split() is mainly useful for data that has been intentionally delimited. With natural text that includes punctuation, consider using the regular expression module.

splitlines(keepends=False)#

Return a list of the lines in the string, breaking at line boundaries.

Line breaks are not included in the resulting list unless keepends is given and true.

startswith(prefix[, start[, end]]) bool#

Return True if S starts with the specified prefix, False otherwise. With optional start, test S beginning at that position. With optional end, stop comparing S at that position. prefix can also be a tuple of strings to try.

strip(chars=None, /)#

Return a copy of the string with leading and trailing whitespace removed.

If chars is given and not None, remove characters in chars instead.

swapcase()#

Convert uppercase characters to lowercase and lowercase characters to uppercase.

title()#

Return a version of the string where each word is titlecased.

More specifically, words start with uppercased characters and all remaining cased characters have lower case.

translate(table, /)#

Replace each character in the string using the given translation table.

table

Translation table, which must be a mapping of Unicode ordinals to Unicode ordinals, strings, or None.

The table must implement lookup/indexing via __getitem__, for instance a dictionary or list. If this operation raises LookupError, the character is left untouched. Characters mapped to None are deleted.

upper()#

Return a copy of the string converted to uppercase.

zfill(width, /)#

Pad a numeric string with zeros on the left, to fill a field of the given width.

The string is never truncated.

soprano.nmr.tensor.check_quadrupole_active(func)[source]#

Decorator to check if the nucleus is quadrupole active.

soprano.nmr.tensor.contains_nmr_tensors(values)[source]#

Check if values contain NMRTensor objects at any nesting level

soprano.nmr.tensor.has_reference(func)[source]#

Decorator to check if a reference chemical shift has been set. Raises a ValueError if the reference is not set.