Source code for xmipp3.protocols.protocol_rotational_symmetry

# **************************************************************************
# *
# * Authors:     Carlos Oscar S. Sorzano (coss@cnb.csic.es)
# *
# * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 2 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307  USA
# *
# *  All comments concerning this program package may be sent to the
# *  e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************

import pyworkflow
import pyworkflow.object as pwobj
from pwem.emlib.image import ImageHandler
from pwem.objects import Volume

from pwem.protocols import (ProtPreprocessVolumes)
from pyworkflow.protocol import (PointerParam, IntParam, EnumParam, FloatParam)

from pwem.emlib import MetaData, MDL_ANGLE_ROT, MDL_ANGLE_TILT
from xmipp3.convert import getImageLocation


[docs]class XmippProtRotationalSymmetry(ProtPreprocessVolumes): """ Estimate the orientation of a rotational axis and symmetrize. The user should know the order of the axis (two-fold, three-fold, ...) If this is unknown you may try several and see the most consistent results. AI Generated ## Overview The Rotational Symmetry protocol estimates the orientation of a rotational symmetry axis in a 3D volume and then symmetrizes the volume according to that axis. Many cryo-EM structures have rotational symmetry, such as two-fold, three-fold, or higher-order axes. If the symmetry order is known, this protocol can search for the axis orientation that best explains the symmetry present in the map. After the axis has been estimated, the protocol rotates the volume into the corresponding orientation and applies cyclic symmetrization. The protocol is useful when the user knows the expected order of the symmetry axis but does not know its exact orientation in the volume. The main output is a symmetrized volume. The protocol also outputs the estimated rotational and tilt angles of the symmetry axis. ## Inputs and General Workflow The input is a single 3D volume. The user provides the expected rotational symmetry order and chooses how the axis orientation should be searched. The search can be global, local, or a combination of global and local. The protocol first copies the input volume to a working file. It then searches for the symmetry axis using the selected mode. Once the best axis orientation has been found, the volume is rotated according to the estimated angles and symmetrized with the selected cyclic order. Finally, the symmetrized volume is registered as the output volume, and the estimated axis angles are stored as additional outputs. ## Input Volume The **Input volume** parameter defines the map in which the rotational symmetry axis will be searched. The volume should already be reasonably centered and should contain the structure whose symmetry is being analyzed. Strong miscentering, artifacts, large background regions, or severe asymmetry may make the symmetry-axis estimation less reliable. The protocol does not refine the reconstruction. It works only on the provided map and produces a symmetrized version of it. ## Symmetry Order The **Symmetry order** parameter defines the order of the rotational symmetry axis. For example: - 2 means a two-fold rotational axis; - 3 means a three-fold rotational axis; - 4 means a four-fold rotational axis. The user is expected to know or hypothesize this order before running the protocol. If the order is unknown, the user can try several possible values and compare which results are most consistent with the map and with the biological structure. Using an incorrect symmetry order may produce an artificially symmetrized volume and can obscure real asymmetric features. ## Search Mode The **Search mode** parameter controls how the orientation of the symmetry axis is estimated. There are three options: **Global** performs a coarse search over the specified rotational and tilt angle ranges. **Local** starts from user-provided initial rotational and tilt angles and refines the axis orientation locally. **Global+Local** first performs a global coarse search and then refines the best result locally. This is the default and usually the safest option when the axis orientation is not known. The search mode should be chosen according to how much the user already knows about the axis orientation. ## Global Search In global search mode, the protocol scans a range of possible symmetry-axis orientations. The user defines the minimum, maximum, and step values for: - rotational angle; - tilt angle. The protocol evaluates possible axis orientations over this grid and writes the best result to an intermediate metadata file. Global search is useful when the symmetry axis could be anywhere in the volume. However, the accuracy of the initial result depends on the angular step size. A smaller step gives a finer search but increases computation time. ## Local Search In local search mode, the user provides an initial estimate of the symmetry axis orientation. The parameters are: - **Initial rotational angle**; - **Initial tilt angle**. The protocol then performs a local refinement around this starting orientation. Local search is useful when the user already has a good approximate axis orientation, for example from previous analysis, visual inspection, or a related map. It is faster than a full global search but can fail if the initial orientation is too far from the correct axis. ## Global+Local Search In Global+Local mode, the protocol first performs a coarse global search and then refines the best candidate with a local search. This mode combines the robustness of global exploration with the accuracy of local refinement. It is recommended when the symmetry order is known but the axis orientation is not known precisely. The global step reduces the risk of starting the local refinement from a poor orientation. ## Rotational Angle Range The **Rotational angle** parameters define the angular range explored during global or Global+Local search. The parameters are: - minimum rotational angle; - maximum rotational angle; - rotational step. All values are expressed in degrees. A broad range such as 0 to 360 degrees explores the full rotational space. A smaller range can be used when the approximate direction is already known. ## Tilt Angle Range The **Tilt angle** parameters define the tilt range explored during global or Global+Local search. In this convention, tilt = 0 degrees corresponds to a top axis, while tilt = 90 degrees corresponds to a side axis. The parameters are: - minimum tilt angle; - maximum tilt angle; - tilt step. As with the rotational range, smaller steps give a finer search but increase computation time. ## Symmetrization Step After estimating the symmetry-axis orientation, the protocol symmetrizes the volume. It first rotates the volume using the estimated Euler orientation of the symmetry axis. It then applies cyclic symmetry of the selected order. For example, if the symmetry order is 3, the protocol applies C3 symmetrization. If the order is 4, it applies C4 symmetrization. The result is a new volume in which density has been averaged according to the estimated rotational symmetry. ## Output Volume The main output is **outputVolume**. This volume is the symmetrized version of the input map. It is written as `volume_symmetrized.mrc` and keeps the sampling rate and metadata copied from the input volume. The original input volume is not modified. The output can be used for visualization, further processing, or comparison with the unsymmetrized input map. ## Estimated Symmetry-Axis Angles The protocol also outputs two scalar values: - **rotSym**, the estimated rotational angle of the symmetry axis; - **tiltSym**, the estimated tilt angle of the symmetry axis. These values describe the orientation of the detected symmetry axis in degrees. They are also shown in the protocol summary, helping the user document the axis orientation found by the search. ## Interpreting the Result The symmetrized output should be interpreted carefully. If the symmetry order and axis are correct, symmetrization can improve the apparent signal by averaging equivalent density. It can make symmetric features clearer and reduce asymmetric noise. However, if the symmetry order is wrong, if the axis is incorrectly estimated, or if the biological structure is only approximately symmetric, the output may contain artificial density or may erase real asymmetric features. The symmetrized volume should therefore be compared with the original input map. ## Practical Recommendations Use this protocol when the expected rotational symmetry order is known. Use **Global+Local** search when the axis orientation is unknown. Use **Local** search only when a reliable approximate orientation is already available. Start with moderate angular steps for global search. If the result is promising, repeat with a finer search or use local refinement. Try different symmetry orders if the correct order is uncertain, but interpret the results biologically rather than selecting only the visually sharpest map. Always compare the symmetrized volume with the original volume to check whether real asymmetric features have been lost. Do not use this protocol as evidence that a structure truly has a given symmetry. It is a tool for estimating and applying a hypothesized rotational symmetry. ## Final Perspective Rotational Symmetry is a volume-processing protocol for detecting the orientation of a known-order rotational axis and applying the corresponding cyclic symmetrization. For biological users, it is useful when a map is expected to contain rotational symmetry but the axis is not aligned with the standard coordinate axes. The protocol provides both a symmetrized map and the estimated axis angles, making it useful for reorientation, symmetry validation, visualization, and preparation of maps for downstream workflows that assume a particular symmetry axis. """ _label = 'rotational symmetry' _version = pyworkflow.VERSION_1_1 GLOBAL_SEARCH = 0 LOCAL_SEARCH = 1 GLOBAL_LOCAL_SEARCH = 2 #--------------------------- DEFINE param functions ------------------------ def _defineParams(self, form): form.addSection(label='General parameters') form.addParam('inputVolume', PointerParam, pointerClass="Volume", label='Input volume') form.addParam('symOrder',IntParam, default=3, label='Symmetry order', help="3 for a three-fold symmetry axis, " "4 for a four-fold symmetry axis, ...") form.addParam('searchMode', EnumParam,label='Search mode', choices=['Global','Local','Global+Local'], default=self.GLOBAL_LOCAL_SEARCH) form.addParam('rot', FloatParam, default=0, condition='searchMode==%d' % self.LOCAL_SEARCH, label='Initial rotational angle', help="In degrees") form.addParam('tilt', FloatParam, default=0, condition='searchMode==%d' % self.LOCAL_SEARCH, label='Initial tilt angle', help="In degrees. tilt=0 is a top axis while " "tilt=90 defines a side axis") rot = form.addLine('Rotational angle', condition='searchMode!=%d' % self.LOCAL_SEARCH, help='Minimum, maximum and step values for ' 'rotational angle range, all in degrees.') rot.addParam('rot0', FloatParam, default=0, label='Min') rot.addParam('rotF', FloatParam, default=360, label='Max') rot.addParam('rotStep', FloatParam, default=5, label='Step') tilt = form.addLine('Tilt angle', condition='searchMode!=%d' % self.LOCAL_SEARCH, help='In degrees. tilt=0 is a top axis while ' 'tilt=90 defines a side axis') tilt.addParam('tilt0', FloatParam, default=0, label='Min') tilt.addParam('tiltF', FloatParam, default=180, label='Max') tilt.addParam('tiltStep', FloatParam, default=5, label='Step') self.rotSym = pwobj.Float() self.tiltSym = pwobj.Float() form.addParallelSection(threads=4, mpi=0) #--------------------------- INSERT steps functions ------------------------ def _insertAllSteps(self): self._insertFunctionStep('copyInput') if self.searchMode.get()!=self.LOCAL_SEARCH: self._insertFunctionStep('coarseSearch') if self.searchMode.get()!=self.GLOBAL_SEARCH: self._insertFunctionStep('fineSearch') self._insertFunctionStep('symmetrize') self._insertFunctionStep('createOutput') self.fnVol = getImageLocation(self.inputVolume.get()) self.fnVolSym=self._getPath('volume_symmetrized.mrc') [self.height,_,_]=self.inputVolume.get().getDim() #--------------------------- STEPS functions -------------------------------
[docs] def copyInput(self): ImageHandler().convert(self.inputVolume.get(), self.fnVolSym)
[docs] def coarseSearch(self): self.runJob("xmipp_volume_find_symmetry", "-i %s -o %s --rot %f %f %f --tilt %f %f %f --sym rot %d --thr %d" % (self.fnVolSym, self._getExtraPath('coarse.xmd'), self.rot0, self.rotF, self.rotStep, self.tilt0, self.tiltF, self.tiltStep, self.symOrder, self.numberOfThreads))
[docs] def getAngles(self, fnAngles=""): if fnAngles=="": if self.searchMode.get()==self.GLOBAL_SEARCH: fnAngles = self._getExtraPath("coarse.xmd") else: fnAngles = self._getExtraPath("fine.xmd") md = MetaData(fnAngles) objId = md.firstObject() rot0 = md.getValue(MDL_ANGLE_ROT, objId) tilt0 = md.getValue(MDL_ANGLE_TILT, objId) return (rot0,tilt0)
[docs] def fineSearch(self): if self.searchMode == self.LOCAL_SEARCH: rot0 = self.rot.get() tilt0 = self.tilt.get() else: rot0, tilt0 = self.getAngles(self._getExtraPath('coarse.xmd')) self.runJob("xmipp_volume_find_symmetry", "-i %s -o %s --localRot %f %f --sym rot %d"% (self.fnVolSym,self._getExtraPath('fine.xmd'), rot0, tilt0, self.symOrder.get()))
[docs] def symmetrize(self): rot0, tilt0 = self.getAngles() self.runJob("xmipp_transform_geometry", "-i %s --rotate_volume euler %f %f 0 --dont_wrap" % (self.fnVolSym,rot0,tilt0)) self.runJob("xmipp_transform_symmetrize", "-i %s --sym c%d --dont_wrap" % (self.fnVolSym,self.symOrder.get()))
[docs] def createOutput(self): Ts = self.inputVolume.get().getSamplingRate() self.runJob("xmipp_image_header","-i %s --sampling_rate %f" %(self.fnVolSym,Ts)) volume = Volume() volume.setFileName(self.fnVolSym) volume.copyInfo(self.inputVolume.get()) self._defineOutputs(outputVolume=volume) self._defineTransformRelation(self.inputVolume, self.outputVolume) rot0, tilt0 = self.getAngles() self._defineOutputs(rotSym=pwobj.Float(rot0), tiltSym=pwobj.Float(tilt0))
#--------------------------- INFO functions -------------------------------- def _summary(self): messages = [] if self.rotSym.hasValue(): messages.append('Rot. Angle of Symmetry axis=%0.2f (degrees)' % self.rotSym.get()) messages.append('Tilt.Angle of Symmetry axis=%0.2f (degrees)' % self.tiltSym.get()) return messages def _methods(self): messages = [] messages.append('We looked for the %d-fold rotational axis of the ' 'volume %s using Xmipp [delaRosaTrevin2013]. ' 'We found it to be with an orientation given by a ' 'rotational angle of %f and a tilt angle of %f degrees.' % (self.symOrder, self.getObjectTag('inputVolume'), self.rotSym, self.tiltSym)) return messages