Source code for ccpn.core.PseudoDimension

# Licence, Reference and Credits
__copyright__ = "Copyright (C) CCPN project ( 2014 - 2022"
__credits__ = ("Ed Brooksbank, Joanna Fox, Victoria A Higman, Luca Mureddu, Eliza Płoskoń",
               "Timothy J Ragan, Brian O Smith, Gary S Thompson & Geerten W Vuister")
__licence__ = ("CCPN licence. See")
__reference__ = ("Skinner, S.P., Fogh, R.H., Boucher, W., Ragan, T.J., Mureddu, L.G., & Vuister, G.W.",
                 "CcpNmr AnalysisAssign: a flexible platform for integrated NMR analysis",
                 "J.Biomol.Nmr (2016), 66, 111-124,")
# Last code modification
__modifiedBy__ = "$modifiedBy: Ed Brooksbank $"
__dateModified__ = "$dateModified: 2022-01-21 11:22:07 +0000 (Fri, January 21, 2022) $"
__version__ = "$Revision: 3.0.4 $"
# Created
__author__ = "$Author: CCPN $"
__date__ = "$Date: 2017-04-07 10:28:41 +0000 (Fri, April 07, 2017) $"
# Start of code

from typing import Tuple, Sequence, Optional

from ccpn.core._implementation.AbstractWrapperObject import AbstractWrapperObject
from ccpn.core._implementation.SpectrumDimensionAttributes import SpectrumDimensionAttributes
from ccpn.core.Project import Project
from ccpn.core.Spectrum import Spectrum
from ccpnmodel.ccpncore.api.ccp.nmr.Nmr import SampledDataDim as ApiSampledDataDim
from ccpnmodel.ccpncore.api.ccp.nmr.Nmr import ExpDimRef as ApiExpDimRef

[docs]class PseudoDimension(AbstractWrapperObject, SpectrumDimensionAttributes): """A sampled Spectrum axis with non-gridded values. Can be used (V2 legacy) to describe sampled-value axes in pseudo-2D and nD experiments, such as the time delay axis for T1 experiments.""" #: Short class name, for PID. shortClassName = 'PD' # Attribute it necessary as subclasses must use superclass className className = 'PseudoDimension' _parentClass = Spectrum # Type of dimension. Always 'Freq' for frequency (Fourier transformed) dimension dimensionType = 'Sampled' #: Name of plural link to instances of class _pluralLinkName = 'pseudoDimensions' #: List of child classes. _childClasses = [] # Qualified name of matching API class _apiClassQualifiedName = ApiSampledDataDim._metaclass.qualifiedName() #----------------------------------------------------------------------------------------- def __init__(self, project, wrappedData): super().__init__(project, wrappedData) #----------------------------------------------------------------------------------------- # CCPN properties #----------------------------------------------------------------------------------------- # CCPN properties @property def _apiSampledDataDim(self) -> ApiSampledDataDim: """ CCPN DataSource matching Spectrum""" return self._wrappedData @property def _dataDim(self): """ :return: dataDim instance """ return self._wrappedData @property def _dataDimRef(self): """ :return: dataDim instance; not present for SampledDataDim """ return None @property def _expDim(self): """ :return: expDim instance """ return self._dataDim.expDim @property def _expDimRef(self): """ :return: expDimRef instance """ return list(self._expDim.expDimRefs)[0] @property def _key(self) -> str: """object identifier, used for id""" return str(self._wrappedData.dim) @property def _localCcpnSortKey(self) -> Tuple: """Local sorting key, in context of parent.""" return (self._wrappedData.dim,) @property def _parent(self) -> Spectrum: """Spectrum containing PseudoDimension.""" return self._project._data2Obj[self._wrappedData.dataSource] spectrum = _parent #----------------------------------------------------------------------------------------- # Object properties; specific to PseudoDimension #----------------------------------------------------------------------------------------- @property def axisParameter(self) -> str: """Name of the condition or parameter that is varied along the axis. Use 'sample' if samples are different for each point on the dimension""" return self._wrappedData.conditionVaried @axisParameter.setter def axisParameter(self, value: str): self._wrappedData.conditionVaried = value @property def pointValues(self) -> Tuple[float, ...]: """point values for PseudoDimension.""" return tuple(self._wrappedData.pointValues) @pointValues.setter def pointValues(self, value: Sequence) -> tuple: self._wrappedData.pointValues = value @property def pointErrors(self) -> Tuple[float, ...]: """point errors for PseudoDimension).""" return tuple(self._wrappedData.pointErrors) @pointErrors.setter def pointErrors(self, value: Sequence) -> tuple: self._wrappedData.pointErrors = value @property def isAcquisition(self) -> bool: """Always False""" return False @isAcquisition.setter def isAcquisition(self, value): pass @property def isReversed(self) -> bool: """Always False """ return False @isReversed.setter def isReversed(self, value): """Set whether the axis is reversed - isReversed implies that ppm values decrease as point values increase """ pass @property def maxAliasedFrequency(self) -> float: """maximum possible peak frequency; emulated to be always ppmValue of pointCounts +0.5 """ return self.pointToValue(float(self.pointCount)+0.5) @maxAliasedFrequency.setter def maxAliasedFrequency(self, value): pass @property def minAliasedFrequency(self) -> float: """minimum possible frequency; emulated to be always ppmValue of point 0.5 """ result = self.pointToValue(0.5) return result @minAliasedFrequency.setter def minAliasedFrequency(self, value): pass @property def spectrumLimits(self) -> Tuple[float, float]: """Return the limits of this spectrum dimension as a tuple of floats""" return (self.pointToValue(1.0), self.pointToValue(float(self.pointCount))) @property def foldingLimits(self) -> Tuple[float, float]: """Return the foldingLimits of this dimension as a tuple of floats. """ return (self.pointToValue(0.5), self.pointToValue(float(self.pointCount) + 0.5)) @property def referencePoint(self) -> float: """point used for axis (chemical shift) referencing. emulated to always be 1.0 """ return 1.0 @referencePoint.setter def referencePoint(self, value): pass @property def referenceValue(self) -> float: """ppm-value used for axis (chemical shift) referencing. emulated to always be 1.0 (as axis is not reversed) """ return 1.0 @referenceValue.setter def referenceValue(self, value: float): pass @property def spectrometerFrequency(self) -> float: """Absolute frequency at carrier (or at splitting 0.0). In MHz or dimensionless. Emulated to always be 1.0 """ return 1.0 @spectrometerFrequency.setter def spectrometerFrequency(self, value): pass @property def spectralWidthHz(self) -> float: """spectral width in Hz emulated to always be 1.0 * spectrometerFrequency """ return self.spectralWidth*self.spectrometerFrequency @spectralWidthHz.setter def spectralWidthHz(self, value: float): pass @property def spectralWidth(self) -> float: """spectral width in ppm emulated to always be self.pointCount """ return float(self.pointCount) @spectralWidth.setter def spectralWidth(self, value: float): pass @property def _valuePerPoint(self) -> float: """Value per point in Hz for Frequency domain data, in secs for time/fid domain data""" return self.spectralWidthHz / self.pointCount @_valuePerPoint.setter def _valuePerPoint(self, value: float): pass @property def phase0(self) -> Optional[float]: """Zero-order phase; always None""" return None @phase0.setter def phase0(self, value): pass @property def phase1(self) -> Optional[float]: """First-order phase; always None""" return None @phase1.setter def phase1(self, value): pass @property def assignmentTolerance(self) -> float: """Assignment Tolerance; Always 0.0 """ return 0.0 @assignmentTolerance.setter def assignmentTolerance(self, value): pass @property def defaultAssignmentTolerance(self) -> float: """Default assignment tolerance (in ppm); """ return 0.0 #========================================================================================= # CCPN functions #=========================================================================================
[docs] def pointToValue(self, point: float) -> float: """:return ppm-value corresponding to point (float) """ pointOffset = point - self.referencePoint ppmPerPoint = self._valuePerPoint / self.spectrometerFrequency factor = -1.0 if self.isReversed else 1.0 value = self.referenceValue + factor * pointOffset * ppmPerPoint return value
pointToPpm = pointToValue
[docs] def valueToPoint(self, value: float) -> float: """:return point (float) corresponding to ppm-value""" ppmPerPoint = self._valuePerPoint / self.spectrometerFrequency valOffset = value - self.referenceValue factor = -1.0 if self.isReversed else 1.0 point = self.referencePoint + factor * valOffset / ppmPerPoint return point
ppmToPoint = valueToPoint #========================================================================================= # Implementation functions #========================================================================================= @classmethod def _getAllWrappedData(cls, parent: Spectrum) -> list: """get wrappedData (Nmr.SampledDataDims) for all DataDim children of parent Spectrum""" return [x for x in parent._wrappedData.sortedDataDims() if x.className == 'SampledDataDim']
# No 'new' function - PseudoDimensions are made on spectrum load #========================================================================================= # Connections to parents: #========================================================================================= # Notifiers: def _expDimRefHasChanged(project: Project, apiExpDimRef: ApiExpDimRef): """Refresh PseudoDimension when ExpDimRef has changed""" for dataDim in apiExpDimRef.expDim.dataDims: if isinstance(dataDim, ApiSampledDataDim): project._modifiedApiObject(dataDim) Project._setupApiNotifier(_expDimRefHasChanged, ApiExpDimRef, '') del _expDimRefHasChanged