Source code for xmipp3.protocols.protocol_volume_strain

# **************************************************************************
# *
# * Authors:     Carlos Oscar 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 os

from pyworkflow.protocol.params import PointerParam, StringParam
from pwem.protocols import ProtAnalysis3D

from xmipp3.base import getXmippPath
from xmipp3 import Plugin


[docs]class XmippProtVolumeStrain(ProtAnalysis3D): """Compares two volume states to analyze local strains and rotations. This protocol helps study structural changes by quantifying deformation and dynamic behavior between different conformations. AI Generated ## Overview The Calculate Strain protocol compares two 3D volume states and estimates local deformation descriptors between them. The protocol is intended for situations where two maps represent different states of the same structure, such as two conformations or two stages of a motion. It deforms the initial state toward the final state and computes maps describing local strain and local rotation. The main outputs are file-based maps and Chimera scripts for visualization. The protocol produces converted maps for the initial state, final state, initial state deformed to the final state, strain, and local rotation. It also creates scripts to visualize strain, local rotation, and morphing between the initial and final maps. ## Inputs and General Workflow The protocol requires: - an initial-state volume; - a final-state volume; - a mask for the final state; - optionally, a symmetry group. The protocol runs a Matlab-based strain calculation using the Xmipp MIRT environment. The calculation deforms the initial state so that it fits the final state inside the region defined by the mask. The raw outputs are then converted to MRC format, mirrored as required by the coordinate convention, assigned the sampling rate of the input volumes, and optionally symmetrized. Finally, Chimera command scripts are generated for visualizing the strain map, local-rotation map, and morph between states. ## Initial State The **Initial state** parameter defines the starting volume. This is the volume that will be deformed toward the final state. It should represent the same structure as the final state, but in a different conformation or structural condition. The initial and final volumes must have the same box size. The protocol validates this requirement before running. ## Final State The **Final state** parameter defines the target volume. The strain calculation estimates how the initial state must deform to match this final state. The final state should be aligned with the initial state and should represent a comparable molecular region. If the volumes are not in the same coordinate frame, the estimated deformation, strain, and local rotation maps will be difficult to interpret. ## Mask for the Final State The **Mask for the final state** parameter defines where strain and local rotation are calculated. The mask should be a binary mask covering the relevant molecular region in the final-state map. Regions outside the mask are excluded from the deformation analysis. A good mask is important. If it is too tight, it may exclude relevant density. If it is too loose, background or solvent may influence the deformation calculation. ## Symmetry Group The **Symmetry group** parameter defines whether symmetry should be applied to the strain and local-rotation maps. The default is **c1**, meaning no symmetry. If the structure has known symmetry, the corresponding Xmipp symmetry group can be provided. After the strain and local-rotation maps are generated, the protocol can symmetrize these maps using the selected symmetry. Symmetry should only be used when it is biologically justified. Incorrect symmetry may average non-equivalent deformation features. ## Matlab/MIRT Calculation The core calculation is performed by a Matlab command that calls `xmipp_calculate_strain`. The calculation uses: - the final volume; - the initial volume; - the final-state mask; - an output file root. The Xmipp MIRT environment is used for the underlying deformation calculation. This step generates raw files that are later converted into MRC maps. ## Generated Maps The protocol prepares several maps from the raw calculation outputs: - `result_initial.mrc`; - `result_final.mrc`; - `result_initialDeformedToFinal.mrc`; - `result_strain.mrc`; - `result_localrot.mrc`. The initial and final maps are useful for checking the input states. The initial-deformed-to-final map helps inspect whether the deformation successfully brings the initial state toward the final state. The strain and local-rotation maps contain the main deformation descriptors. ## Strain Map The strain map describes local deformation magnitude between the initial and final states. Regions with stronger strain correspond to parts of the structure that undergo larger local deformation during the transformation from the initial state to the final state. This map can help identify flexible domains, hinge regions, interfaces under mechanical distortion, or areas involved in conformational change. The strain map should be interpreted as a deformation descriptor derived from the volume-to-volume registration, not as a direct physical measurement of force or energy. ## Local-Rotation Map The local-rotation map describes local rotational behavior in the deformation field. Regions with stronger local rotation correspond to areas where the deformation involves more rotational motion. This can help identify domains or subregions that rotate relative to the rest of the structure during the conformational transition. As with strain, local rotation should be interpreted in relation to map quality, masking, alignment, and biological context. ## Symmetrized Strain and Local-Rotation Maps If a symmetry group other than **c1** is selected, the protocol symmetrizes the strain and local-rotation maps. The symmetrization is performed without wrapping density across box boundaries. After symmetrization, the sampling rate is assigned again. This can make the deformation descriptors consistent with the symmetry of the structure, but only if that symmetry is appropriate for the conformational change being analyzed. ## Chimera Strain Script The protocol creates a Chimera command script named: `result_strain_chimera.cmd` This script opens the final-state map and the strain map, hides the strain volume, and colors the final map according to the strain values using a rainbow color scale with reversed colors. This provides a convenient way to visualize where strain is concentrated on the final-state structure. ## Chimera Local-Rotation Script The protocol creates a second Chimera command script named: `result_localrot_chimera.cmd` This script opens the final-state map and the local-rotation map, hides the local-rotation volume, and colors the final map according to local rotation. This helps visualize regions undergoing stronger rotational deformation. ## Chimera Morph Script The protocol also creates a morphing script named: `result_morph_chimera.cmd` This script opens the initial and final maps, hides them, and creates a morph between the two volumes using 50 frames. This visualization helps the user inspect the overall transition from the initial state to the final state. ## Output Behavior This protocol mainly produces file-based outputs rather than registered Scipion output volume objects. The important generated files are stored in the protocol extra folder and the Chimera scripts are written in the protocol working directory. Users should inspect the generated MRC files and visualization scripts to analyze the results. ## Validation Rules The protocol checks that the initial and final volumes have the same box size. If the box sizes differ, the protocol reports an error asking the user to make sure that the two volumes have the same size. The user should also ensure, even though not explicitly validated, that the volumes have compatible sampling rate, origin, alignment, and coordinate frame. ## Interpreting the Results The strain and local-rotation maps depend on the quality of the deformation from the initial state to the final state. Meaningful interpretation requires that the two volumes represent related states of the same structure, are aligned, have comparable resolution and sampling, and are analyzed with an appropriate mask. High strain or local rotation may indicate real conformational changes, but it may also arise from noise, misalignment, map artifacts, poor masking, or differences in local resolution. The deformed-initial map should be inspected to check whether the deformation reasonably matches the final state. ## Practical Recommendations Use this protocol for two related conformational states of the same structure. Make sure the initial and final maps have the same box size and are already aligned. Use a mask that covers the region where the deformation should be analyzed. Start with symmetry **c1** unless the deformation itself is expected to respect the structural symmetry. Inspect `result_initialDeformedToFinal.mrc` to verify that the deformation is reasonable before interpreting strain and local rotation. Use the generated Chimera scripts to visualize strain, local rotation, and the morph between states. Interpret high-strain or high-rotation regions together with the original density maps, local resolution, and biological knowledge. ## Final Perspective Calculate Strain is a deformation-analysis protocol for comparing two volume states. For biological users, its main value is that it converts a map-to-map conformational change into spatial descriptors of local strain and local rotation. These descriptors can help identify which regions deform, bend, or rotate between two structural states. The protocol should be used as an exploratory structural-analysis tool. Its results are most meaningful when the input maps are well aligned, comparable in quality, and interpreted together with the original volumes and biological context. """ _label = 'calculate strain' def __init__(self, **args): ProtAnalysis3D.__init__(self, **args) #--------------------------- DEFINE param functions -------------------------------------------- def _defineParams(self, form): form.addSection(label='Input') form.addParam('inputVolume0', PointerParam, label="Initial state", important=True, pointerClass='Volume', help='Initial state of the structure, it will be deformed to fit into the final state') form.addParam('inputVolumeF', PointerParam, label="Final state", important=True, pointerClass='Volume', help='Initial state of the structure, it will be deformed to fit into the final state') form.addParam('inputMask', PointerParam, label="Mask for the final state", important=True, pointerClass='VolumeMask', help='Binary mask that defines where the strains and rotations will be calculated') form.addParam('symmetryGroup', StringParam, default="c1", label='Symmetry group', help='See https://i2pc.github.io/docs/Utils/Conventions/index.html#symmetry for a description of the symmetry groups format' 'If no symmetry is present, give c1') #--------------------------- INSERT steps functions -------------------------------------------- def _getFileName(self, fnRoot, key, **kwargs): return "%s_%s.mrc"%(fnRoot,key) def _insertAllSteps(self): fnVol0 = self.inputVolume0.get().getFileName() fnVolF = self.inputVolumeF.get().getFileName() fnMask = self.inputMask.get().getFileName() self._insertFunctionStep(self.calculateStrain,fnVol0,fnVolF,fnMask) self._insertFunctionStep(self.prepareOutput) self._insertFunctionStep(self.createChimeraScript) #--------------------------- STEPS functions ---------------------------------------------------
[docs] def calculateStrain(self, fnVol0, fnVolF, fnMask): fnRoot=self._getExtraPath('result') mirtDir = getXmippPath('external', 'mirt') # -wait -nodesktop args=('''-r "diary('%s'); xmipp_calculate_strain('%s','%s','%s','%s'); exit"''' % (fnRoot+"_matlab.log",fnVolF,fnVol0,fnMask,fnRoot)) self.runJob("matlab", args, env=Plugin.getMatlabEnviron(mirtDir))
[docs] def prepareOutput(self): volDim = self.inputVolume0.get().getDim()[0] Ts=self.inputVolume0.get().getSamplingRate() fnRoot=self._getExtraPath('result') def symmetrize(key): self.runJob("xmipp_transform_symmetrize", "-i %s --sym %s --dont_wrap" % \ (self._getFileName(fnRoot, key), self.symmetryGroup.get())) def changeSamplingRate(key): self.runJob("xmipp_image_header", "-i %s --sampling_rate %f" % (self._getFileName(fnRoot, key), Ts)) def convert(key): self.runJob("xmipp_image_convert", "-i %s_%s.raw#%d,%d,%d,0,float -o %s" % (fnRoot, key, volDim, volDim, volDim, self._getFileName(fnRoot, key))) self.runJob("xmipp_transform_mirror", "-i %s --flipX" % self._getFileName(fnRoot, key)) changeSamplingRate(key) convert("initial") convert("final") convert("initialDeformedToFinal") convert("strain") convert("localrot") self.runJob("rm","-f "+self._getExtraPath('result_*.raw')) if self.symmetryGroup!="c1": symmetrize("strain") symmetrize("localrot") changeSamplingRate("strain") changeSamplingRate("localrot")
[docs] def createChimeraScript(self): fnRoot = "extra/result" scriptFile = self._getPath('result') + '_strain_chimera.cmd' openStr = "open %s\n" fhCmd = open(scriptFile, 'w') fhCmd.write(openStr % self._getFileName(fnRoot,"final")) fhCmd.write(openStr % self._getFileName(fnRoot,"strain")) fhCmd.write("vol #1 hide\n") fhCmd.write("scolor #0 volume #1 cmap rainbow reverseColors True\n") fhCmd.close() scriptFile = self._getPath('result') + '_localrot_chimera.cmd' fhCmd = open(scriptFile, 'w') fhCmd.write(openStr % self._getFileName(fnRoot,"final")) fhCmd.write(openStr % self._getFileName(fnRoot,"localrot")) fhCmd.write("vol #1 hide\n") fhCmd.write("scolor #0 volume #1 cmap rainbow reverseColors True\n") fhCmd.close() scriptFile = self._getPath('result') + '_morph_chimera.cmd' fhCmd = open(scriptFile, 'w') fhCmd.write(openStr % self._getFileName(fnRoot,"initial")) fhCmd.write(openStr % self._getFileName(fnRoot,"final")) fhCmd.write("vol #0 hide\n") fhCmd.write("vol #1 hide\n") fhCmd.write("vop morph #0,1 frames 50\n") fhCmd.close()
#--------------------------- INFO functions -------------------------------------------- def _validate(self): errors = [] xdim0 = self.inputVolume0.get().getDim()[0] xdimF = self.inputVolumeF.get().getDim()[0] if xdim0 != xdimF: errors.append("Make sure that the two volumes have the same size") return errors