Skip to content
Snippets Groups Projects
  • Studer Gabriel's avatar
    6e60b71d
    Initial Python 3 port commit · 6e60b71d
    Studer Gabriel authored
    This commit doesn't make OpenStructure work with Python 3. The goal of this
    commit was to perform an automated port of the Python code and make it compile.
    The performed steps:
    
    - Edited CMakeLists.txt to search for Python with 3.6 as min version 3.6 is the
      Python version shipped by default with Ubuntu 18.04 LTS
    - Add version 3.6 to cmake_support/FindPython.cmake
    - Adapt setup_boost macro in cmake_support/OST.cmake to prefer versioned
      libraries and not first check for boost_python.so. In the example of
      Ubuntu 18.04, libboost_python.so is specific for Python 2 but
      libboost_python3.so is the one we want.
    - apply the following command: 2to3-2.7 -n -w <OST_DIR>
    - adapt base/pymod/wrap_base.cc, gui/pymod/wrap_gui.cc and
      gui/pymod/export_message_widget.cc as PyString functionalities do not exist
      anymore in the Python 3 interpreter (replaced by PyUnicode)
    - adapt gui/src/python_shell/python_interpreter_worker.hh to resolve issue
      discussed in https://stackoverflow.com/questions/23068700/embedding-python3-in-qt-5
      Long story short: Qt does a typedef for "slots" which causes trouble with
      other headers that are pulled in from the Python interpreter
    6e60b71d
    History
    Initial Python 3 port commit
    Studer Gabriel authored
    This commit doesn't make OpenStructure work with Python 3. The goal of this
    commit was to perform an automated port of the Python code and make it compile.
    The performed steps:
    
    - Edited CMakeLists.txt to search for Python with 3.6 as min version 3.6 is the
      Python version shipped by default with Ubuntu 18.04 LTS
    - Add version 3.6 to cmake_support/FindPython.cmake
    - Adapt setup_boost macro in cmake_support/OST.cmake to prefer versioned
      libraries and not first check for boost_python.so. In the example of
      Ubuntu 18.04, libboost_python.so is specific for Python 2 but
      libboost_python3.so is the one we want.
    - apply the following command: 2to3-2.7 -n -w <OST_DIR>
    - adapt base/pymod/wrap_base.cc, gui/pymod/wrap_gui.cc and
      gui/pymod/export_message_widget.cc as PyString functionalities do not exist
      anymore in the Python 3 interpreter (replaced by PyUnicode)
    - adapt gui/src/python_shell/python_interpreter_worker.hh to resolve issue
      discussed in https://stackoverflow.com/questions/23068700/embedding-python3-in-qt-5
      Long story short: Qt does a typedef for "slots" which causes trouble with
      other headers that are pulled in from the Python interpreter
structure_analysis.py 5.06 KiB
"""
Some functions for analyzing structures

Author: Niklaus Johner (Niklaus.Johner@unibas.ch)
"""
import os
import ost

def GetFrameFromEntity(eh):
  """
  This function returns a CoordFrame from an EntityHandle
  
  :param eh: 
  :type eh: :class:`~ost.mol.EntityHandle`

  :return: :class:`ost.mol.CoordFrame`
  """
  return ost.mol.CreateCoordFrame(eh.GetAtomPosList(ordered_by_index=True))
  
def GetDistanceBetwCenterOfMass(sele1,sele2):
  """
  This function calculates the distance between the centers of mass
  of **sele1** and **sele2**, two selections from the same Entity.

  :param sele1:
  :param sele2:
  :type sele1: :class:`~ost.mol.EntityView`
  :type sele2: :class:`~ost.mol.EntityView`

  :return: :class:`float`
  """
  if not sele1.IsValid() and sele2.IsValid():
    print('invalid view')
    return
  eh=sele1.GetHandle()
  if not eh==sele2.GetHandle():
    print('The two views must be from the same entity')
    return
  f=GetFrameFromEntity(eh)
  return f.GetDistanceBetwCenterOfMass(sele1,sele2)

def GetMinDistanceBetweenViews(sele1,sele2):
  """
  This function calculates the minimal distance between
  **sele1** and **sele2**, two selections from the same Entity.
  
  :param sele1:
  :param sele2:
  :type sele1: :class:`~ost.mol.EntityView`
  :type sele2: :class:`~ost.mol.EntityView`

  :return: :class:`float`
  """
  if not sele1.IsValid() and sele2.IsValid():
    print('invalid view')
    return
  eh=sele1.GetHandle()
  if not eh==sele2.GetHandle():
    print('The two views must be from the same entity')
    return
  f=GetFrameFromEntity(eh)
  return f.GetMinDistance(sele1,sele2)

def GetMinDistBetwCenterOfMassAndView(sele1,sele2):
  """
  This function calculates the minimal distance between **sele2** and
  the center of mass of **sele1**, two selections from the same Entity.

  :param sele1: The selection from which the center of mass is taken
  :param sele2:
  :type sele1: :class:`~ost.mol.EntityView`
  :type sele2: :class:`~ost.mol.EntityView`

  :return: distance (\ :class:`float`\ )
  """
  if not sele1.IsValid() and sele2.IsValid():
    print('invalid view')
    return
  eh=sele1.GetHandle()
  if not eh==sele2.GetHandle():
    print('The two views must be from the same entity')
    return
  f=GetFrameFromEntity(eh)
  return f.GetMinDistBetwCenterOfMassAndView(sele1,sele2)
  

def GetAlphaHelixContent(sele1):
  """
  This function calculates the content of alpha helix in a view.
  All residues in the view have to ordered and adjacent (no gaps allowed)
  
  :param sele1:
  :type sele1: :class:`~ost.mol.EntityView`

  :return: :class:`float`
  """
  if not sele1.IsValid():
    print('invalid view')
    return
  eh=sele1.GetHandle()
  f=GetFrameFromEntity(eh)
  return f.GetAlphaHelixContent(sele1)


def CalculateBestFitLine(sele1):
  """
  This function calculates the best fit line to the atoms in **sele1**.
  
  :param sele1:
  :type sele1: :class:`~ost.mol.EntityView`

  :return: :class:`~ost.geom.Line3`
  """
  if not sele1.IsValid():
    print('invalid view')
    return
  eh=sele1.GetHandle()
  f=GetFrameFromEntity(eh)
  return f.GetODRLine(sele1)

def CalculateBestFitPlane(sele1):
  """
  This function calculates the best fit plane to the atoms in **sele1**.
  
  :param sele1:
  :type sele1: :class:`~ost.mol.EntityView`

  :return: :class:`~ost.geom.Plane`
  """
  if not sele1.IsValid():
    print('invalid view')
    return
  eh=sele1.GetHandle()
  f=GetFrameFromEntity(eh)
  return f.GetODRPlane(sele1)

def CalculateHelixAxis(sele1):
  """
  This function calculates the best fit cylinder to the CA atoms in **sele1**,
  and returns its axis.  Residues should be ordered correctly
  in **sele1**.
  
  :param sele1:
  :type sele1: :class:`~ost.mol.EntityView`

  :return: :class:`~ost.geom.Line3`
  """
  if not sele1.IsValid():
    print('invalid view')
    return
  eh=sele1.GetHandle()
  f=GetFrameFromEntity(eh)
  return f.FitCylinder(sele1)[0]


def CalculateDistanceDifferenceMatrix(sele1,sele2):
  """
  This function calculates the pairwise distance differences between two selections (\ :class:`~ost.mol.EntityView`\ ).
  The two selections should have the same number of atoms
  It returns an NxN DistanceDifferenceMatrix M (where N is the number of atoms in sele1)
  where M[i,j]=||(sele2.atoms[i].pos-sele2.atoms[j].pos)||-||(sele1.atoms[i].pos-sele1.atoms[j].pos)||

  :param sele1:
  :param sele2:
  :type sele1: :class:`~ost.mol.EntityView`
  :type sele2: :class:`~ost.mol.EntityView`

  :return: NxN numpy matrix
  """
  try:import numpy as npy
  except ImportError:
    LogError("Function needs numpy, but I could not import it.")
    raise
  if not sele1.IsValid() and sele2.IsValid():
    print('invalid view')
    return
  if not sele1.GetAtomCount()==sele2.GetAtomCount():
    print('The two views must have the same number of atoms')
    return
  n_atoms=sele1.GetAtomCount()
  M=npy.zeros([n_atoms,n_atoms])
  for i,a1 in enumerate(sele1.atoms):
    for j,a2 in enumerate(sele1.atoms):
      if i>=j:continue
      d1=ost.geom.Distance(a1.pos,a2.pos)
      d2=ost.geom.Distance(sele2.atoms[i].pos,sele2.atoms[j].pos)
      M[i,j]=d2-d1
      M[j,i]=d2-d1
  return M