Source code for ccpn.core.StructureEnsemble

"""
"""
#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (http://www.ccpn.ac.uk) 2014 - 2021"
__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 http://www.ccpn.ac.uk/v3-software/downloads/license")
__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, http://doi.org/10.1007/s10858-016-0060-y")
#=========================================================================================
# Last code modification
#=========================================================================================
__modifiedBy__ = "$modifiedBy: Ed Brooksbank $"
__dateModified__ = "$dateModified: 2021-09-13 19:25:07 +0100 (Mon, September 13, 2021) $"
__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
#=========================================================================================

import collections
from ccpnmodel.ccpncore.api.ccp.molecule.MolStructure import StructureEnsemble as ApiStructureEnsemble
from ccpn.core.Project import Project
from ccpn.core._implementation.AbstractWrapperObject import AbstractWrapperObject
from ccpn.core.lib import Pid
from ccpn.core.lib.ContextManagers import newObject, renameObject
from ccpn.util.StructureData import EnsembleData
from ccpn.util.decorators import logCommand
from ccpn.util.Logging import getLogger
from ccpn.util import Common as commonUtil


logger = getLogger()


[docs]class StructureEnsemble(AbstractWrapperObject): """Ensemble of coordinate structures.""" #: Short class name, for PID. shortClassName = 'SE' # Attribute is necessary as subclasses must use superclass className className = 'StructureEnsemble' _parentClass = Project #: Name of plural link to instances of class _pluralLinkName = 'structureEnsembles' #: List of child classes. _childClasses = [] # Qualified name of matching API class _apiClassQualifiedName = ApiStructureEnsemble._metaclass.qualifiedName() # CCPN properties @property def _apiStructureEnsemble(self) -> ApiStructureEnsemble: """ CCPN api StructureEnsemble matching StructureEnsemble""" return self._wrappedData @property def _key(self) -> str: """id string - ID number converted to string""" # return str(self._wrappedData.ensembleId) # return str(self.name) + '_' + str(self.serial) return self._wrappedData.name.translate(Pid.remapSeparators) @property def serial(self) -> int: """ID number of StructureEnsemble, used in Pid and to identify the StructureEnsemble. """ return self._wrappedData.ensembleId @property def _parent(self) -> Project: """Parent (containing) object.""" return self._project @property def name(self) -> str: """Name of StructureEnsemble, part of identifier""" return self._wrappedData.name @name.setter def name(self, value: str): """set name of StructureEnsemble.""" self.rename(value) @property def data(self) -> EnsembleData: """EnsembleData (Pandas DataFrame) with structure data. Note that modifying the data via setValues, 'data[column] = ' or 'data.column = ' will be echoed and put on the undo stack. Changing the data by direct pandas access will not.""" apiObj = self._wrappedData.findFirstParameter(name='data') if apiObj is None: return None else: return apiObj.value @data.setter def data(self, value: EnsembleData): wrappedData = self._wrappedData if isinstance(value, EnsembleData): apiObj = wrappedData.findFirstParameter(name='data') if apiObj is None: wrappedData.newParameter(name='data', value=value) else: apiObj.value = value else: raise TypeError("Value is not of type EnsembleData") # value._containingObject = self
[docs] def resetModels(self): """Remove models without data, add models to reflect modelNumbers present""" data = self.data if data.shape[0]: # data present modelNumbers = set(x for x in data['modelNumber'] if x is not None) serial2Model = collections.OrderedDict((x.serial, x) for x in self.models) # remove models without data for serial, model in serial2Model.items(): if serial not in modelNumbers: model.delete() # Add model for model-less data for modelNumber in modelNumbers: if modelNumber not in serial2Model: _model = self.newModel() _model._resetSerial(modelNumber)
#========================================================================================= # Implementation functions #========================================================================================= @classmethod def _getAllWrappedData(cls, parent: Project) -> list: """get wrappedData for all NmrConstraintStores linked to NmrProject""" return parent._wrappedData.molSystem.sortedStructureEnsembles()
[docs] @renameObject() @logCommand(get='self') def rename(self, value: str): """Rename StructureEnsemble, changing its name and Pid. NB, the serial remains immutable. """ return self._rename(value)
#========================================================================================= # CCPN functions #========================================================================================= #=========================================================================================== # new'Object' and other methods # Call appropriate routines in their respective locations #===========================================================================================
[docs] @logCommand(get='self') def newModel(self, label: str = None, comment: str = None, **kwds): """Create new Model. See the Model class for details. Optional keyword arguments can be passed in; see Model._newModel for details. :param label: new name for the model. :param comment: optional comment string. :return: a new Model instance. """ from ccpn.core.Model import _newModel return _newModel(self, label=label, comment=comment, **kwds)
#========================================================================================= # Connections to parents: #========================================================================================= @newObject(StructureEnsemble) def _newStructureEnsemble(self: Project, name: str = None, data: EnsembleData = None, comment: str = None) -> StructureEnsemble: """Create new StructureEnsemble. See the StructureEnsemble class for details. :param name: name of the structureEnsemble :param data: an EnsembleData instance :param comment: any comment string :return: a new StructureEnsemble instance. """ # avoiding circular import from ccpn.core.Model import Model name = StructureEnsemble._uniqueName(project=self, name=name) nmrProject = self._wrappedData ll = nmrProject.root.structureEnsembles serial = max(x.ensembleId for x in ll) + 1 if ll else 1 params = {'molSystem': nmrProject.molSystem, 'ensembleId': serial, 'details': comment} if name: params['name'] = name newApiStructureEnsemble = nmrProject.root.newStructureEnsemble(**params) result = self._data2Obj[newApiStructureEnsemble] if result is None: raise RuntimeError('Unable to generate new StructureEnsemble item') # if serial is not None: # try: # result.resetSerial(serial) # except ValueError: # self.project._logger.warning("Could not reset serial of %s to %s - keeping original value" # % (result, serial)) if data is None: result.data = EnsembleData() else: result.data = data data._containingObject = result for modelNumber in sorted(data['modelNumber'].unique()): # _validateName _label = 'my%s_%s' % (Model.className, modelNumber) _model = result.newModel(label=_label) _model._resetSerial(modelNumber) return result #EJB 20181204: moved to Project # Project.newStructureEnsemble = _newStructureEnsemble # del _newStructureEnsemble # Notifiers: