# **************************************************************************
# *
# * Authors:     Grigory Sharov (gsharov@mrc-lmb.cam.ac.uk)
# *
# * MRC Laboratory of Molecular Biology, MRC-LMB
# *
# * 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 3 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'
# *
# **************************************************************************
from emtable import Table
from pyworkflow.protocol.params import StringParam
from pyworkflow.constants import PROD
from pyworkflow.object import String
from pwem.constants import ALIGN_PROJ
from pwem.protocols import ProtProcessParticles
import relion.convert as convert
 
[docs]class ProtRelionExpandSymmetry(ProtProcessParticles):
    """ This protocols wraps relion_particle_symmetry_expand program.
    Given an input set of particles with angular assignment,
    expand the set by applying a pseudo-symmetry.
    """
    _label = 'expand symmetry'
    _devStatus = PROD
    # -------------------------- DEFINE param functions -----------------------
    def _defineProcessParams(self, form):
        form.addParam('symmetryGroup', StringParam, default="c1",
                      label='Symmetry group',
                      help='See [[Relion Symmetry][http://www2.mrc-lmb.cam.ac.uk/'
                           'relion/index.php/Conventions_%26_File_formats#Symmetry]] '
                           'page for a description of the symmetry format '
                           'accepted by Relion')
        form.addParallelSection(threads=0, mpi=0)
    # -------------------------- INSERT steps functions -----------------------
    def _insertAllSteps(self):
        imgsFn = self._getPath('input_particles.star')
        self._insertFunctionStep('convertInputStep', imgsFn)
        self._insertFunctionStep('expandSymmetryStep', imgsFn)
        self._insertFunctionStep('createOutputStep')
    # -------------------------- STEPS functions ------------------------------
[docs]    def expandSymmetryStep(self, imgsFn):
        outImagesMd = self._getExtraPath('expanded_particles.star')
        args = " --i %s --sym %s --o %s" % (imgsFn, self.symmetryGroup.get(),
                                            outImagesMd)
        self.runJob("relion_particle_symmetry_expand", args) 
[docs]    def createOutputStep(self):
        imgSet = self.inputParticles.get()
        partSet = self._createSetOfParticles()
        partSet.copyInfo(imgSet)
        outImagesMd = self._getExtraPath('expanded_particles.star')
        # remove repeating rlnImageId column
        mdOptics = Table(fileName=outImagesMd, tableName='optics')
        mdOut = Table(fileName=outImagesMd, tableName='particles')
        mdOut.removeColumns("rlnImageId")
        with open(outImagesMd, "w") as f:
            mdOut.writeStar(f, tableName='particles')
            mdOptics.writeStar(f, tableName='optics')
        reader = convert.createReader()
        reader.readSetOfParticles(
            outImagesMd, partSet,
            alignType=ALIGN_PROJ,
            postprocessImageRow=self._postprocessImageRow)
        self._defineOutputs(outputParticles=partSet)
        self._defineSourceRelation(imgSet, partSet) 
    # -------------------------- INFO functions -------------------------------
    def _summary(self):
        summary = []
        if not hasattr(self, 'outputParticles'):
            summary.append("Output particles not ready yet.")
        else:
            summary.append("Symmetry used: %s" % self.symmetryGroup.get())
        return summary
    
    def _validate(self):
        errors = []
        if not self.inputParticles.get().hasAlignmentProj():
            errors.append('Input particles must have angular assignment.')
        return errors
        
    def _citations(self):
        return []
    
    def _methods(self):
        methods = ['Input particle dataset was artificially expanded according'
                   ' to pseudo-symmetric %s point group' %
                   self.symmetryGroup.get()]
        return methods
    # -------------------------- Utils functions ------------------------------
    def _postprocessImageRow(self, item, row):
        if hasattr(row, 'rlnGroupName'):
            item._rlnGroupName = String(row.rlnGroupName)