From d37bfb32772951c5ae2efd9f124b5baa56abfd35 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 17 Nov 2022 18:29:48 +0100 Subject: [PATCH 1/9] add parameter configuration file so you can fine tunning the propgram --- .../protocols/protocol_model_angelo.py | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/modelangelo/protocols/protocol_model_angelo.py b/modelangelo/protocols/protocol_model_angelo.py index 19ea324..37f0fde 100644 --- a/modelangelo/protocols/protocol_model_angelo.py +++ b/modelangelo/protocols/protocol_model_angelo.py @@ -25,9 +25,6 @@ # * # ************************************************************************** -# TODO the test: -# Use 26126 (emdb) and 7tu5 (pdb) + small mask - import os.path from pwem.objects import Volume, AtomStruct, Sequence, VolumeMask @@ -83,10 +80,53 @@ def _defineParams(self, form): label="Use GPU for execution", help="This protocol has both CPU and GPU implementation." "Select the one you want to use.") + form.addHidden(GPU_LIST, params.StringParam, default='0', expertLevel=LEVEL_ADVANCED, label="Choose GPU ID (single one)", help="GPU device to be used") + + form.addParam('configFile', params.StringParam, + label="Configuration File", + default="", + expertLevel=LEVEL_ADVANCED, + help="""this option is only for VERY advanced users,\n +Follows an example of config file + +{ + "standardize_mrc_args": + { + "target_voxel_size": 1.5, + "crop_z": 0, + "bfactor_to_apply": 0, + "auto_mask": false + }, + "ca_infer_args": + { + "model_checkpoint": "chkpt.torch", + "bfactor": 0, + "batch_size": 4, + "stride": 16, + "dont_mask_input": true, + "threshold": 0.05, + "save_real_coordinates": false, + "save_cryo_em_grid": false, + "do_nucleotides": false, + "save_backbone_trace": false, + "save_ca_grid": false, + "crop": 6 + }, + "gnn_infer_args": + { + "num_rounds": 3, + "crop_length": 200, + "repeat_per_residue": 3, + "esm_model": "esm1b_t33_650M_UR50S", + "aggressive_pruning": false, + "seq_attention_batch_size": 200 + } +} +""") # --------------------------- STEPS functions ------------------------------ def _insertAllSteps(self): @@ -109,6 +149,7 @@ def createInputFastaFile(self, seqs): def predictStep(self): seqs = self.inputSequenceS mask = self.inputMask.get() + configFile = self.configFile.get() args = [] if seqs: @@ -123,10 +164,14 @@ def predictStep(self): if mask: args.extend(["--mask-path", mask.getFileName()]) + # Gpu or cpu args.extend(["--device", ("%s" % self.getGpuList()[0]) if self.useGpu else "cpu"]) + if configFile: + args.extend(["-c", configFile]) + try: # Call model angelo: self.runJob(Plugin.getModelAngeloCmd(), args) From 11621c5a6ba829e5d30558df5131d2f8e448b3d5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 17 Nov 2022 18:30:30 +0100 Subject: [PATCH 2/9] add parameter configuration file so you can fine tunning the propgram --- modelangelo/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modelangelo/__init__.py b/modelangelo/__init__.py index 6334baa..892cb03 100644 --- a/modelangelo/__init__.py +++ b/modelangelo/__init__.py @@ -31,7 +31,7 @@ from pyworkflow.utils import runJob from scipion.install.funcs import VOID_TGZ -__version__ = "3.0.3" +__version__ = "3.0.4" _logo = "icon.jpeg" _references = ['mabioarxive'] From 5d44af5f2f63436f44b27c705e387c62b9b4d682 Mon Sep 17 00:00:00 2001 From: rmarabini Date: Tue, 22 Nov 2022 10:40:19 +0100 Subject: [PATCH 3/9] Update protocol_model_angelo.py change StringParam by FilePAram --- modelangelo/protocols/protocol_model_angelo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modelangelo/protocols/protocol_model_angelo.py b/modelangelo/protocols/protocol_model_angelo.py index 37f0fde..945906e 100644 --- a/modelangelo/protocols/protocol_model_angelo.py +++ b/modelangelo/protocols/protocol_model_angelo.py @@ -86,7 +86,7 @@ def _defineParams(self, form): label="Choose GPU ID (single one)", help="GPU device to be used") - form.addParam('configFile', params.StringParam, + form.addParam('configFile', params.FileParam, label="Configuration File", default="", expertLevel=LEVEL_ADVANCED, From be1fbede2271cda6efeab33d0a2a19124c17d182 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 19 May 2023 14:46:16 +0000 Subject: [PATCH 4/9] check for 3d map file type and if oit is not mrc fix it --- .../protocols/protocol_model_angelo.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/modelangelo/protocols/protocol_model_angelo.py b/modelangelo/protocols/protocol_model_angelo.py index 945906e..6bc6d27 100644 --- a/modelangelo/protocols/protocol_model_angelo.py +++ b/modelangelo/protocols/protocol_model_angelo.py @@ -32,6 +32,7 @@ from pyworkflow.utils import Message from pwem.protocols import EMProtocol from pyworkflow.protocol import GPU_LIST, USE_GPU +from pwem.convert.headers import Ccp4Header from modelangelo import Plugin @@ -131,11 +132,28 @@ def _defineParams(self, form): # --------------------------- STEPS functions ------------------------------ def _insertAllSteps(self): # Insert processing steps + self._insertFunctionStep(self.convertInputStep) self._insertFunctionStep(self.predictStep) self._insertFunctionStep(self.createOutputStep) + def convertInputStep(self): + """ convert 3D maps to MRC '.mrc' format + with extension mrc, map extension is not handled by modelangelo + """ + vol = self.inputVolume.get() + inVolName = vol.getFileName() + if inVolName.endswith(".mrc"): + self.newFn = inVolName + else: + self.newFn = self._getExtraPath("inputVol.mrc") + origin = vol.getOrigin(force=True).getShifts() + sampling = vol.getSamplingRate() + Ccp4Header.fixFile(inVolName, self.newFn, origin, sampling, Ccp4Header.START) # ORIGIN + + def createInputFastaFile(self, seqs): """ Get sequence as string and create the corresponding fasta file. """ + fastaFileName = self._getExtraPath('sequence.fasta') with open(fastaFileName, "w") as f: @@ -158,7 +176,7 @@ def predictStep(self): else: args.append("build_no_seq") - args.extend(["--volume-path", self.inputVolume.get().getFileName(), + args.extend(["--volume-path", self.newFn, "--output-dir", self._getExtraPath()]) if mask: From 406674278348e156e5c9e7913ed4e5f9037a7386 Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Sun, 10 Sep 2023 16:21:14 +0100 Subject: [PATCH 5/9] pep8 cleaning --- .gitignore | 22 +++++--- CHANGES.txt | 5 +- MANIFEST.in | 4 +- modelangelo/__init__.py | 19 +++---- modelangelo/bibtex.py | 17 +++---- modelangelo/icon.jpeg | Bin 26218 -> 0 bytes modelangelo/protocols/__init__.py | 30 ++++++++--- .../protocols/protocol_model_angelo.py | 47 ++++++------------ modelangelo/tests/__init__.py | 2 +- modelangelo/tests/tests_model_angelo.py | 8 ++- modelangelo/viewers/__init__.py | 3 +- modelangelo/viewers/viewer.py | 12 ++--- modelangelo/wizards/__init__.py | 25 ---------- setup.py | 13 ++++- 14 files changed, 98 insertions(+), 109 deletions(-) delete mode 100644 modelangelo/icon.jpeg delete mode 100644 modelangelo/wizards/__init__.py diff --git a/.gitignore b/.gitignore index 76e90d9..5721543 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,16 @@ -# Python compilation files -*.pyc - -# Ignore temporary packaging folder -*.egg-info - -# IDE files +#### Eclipse and so on +.project +.cproject +.pydevproject +.classpath .idea + +#### Python +build/ +dist/ +*.egg-info/ +*.egg +*.py[cod] +__pycache__/ +*.so +*~ diff --git a/CHANGES.txt b/CHANGES.txt index 50a27d0..846b0f3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,2 @@ -3.0.1 ------ - - Installation does not fail but is never complete. So installb modelangelo will git pull all the time. \ No newline at end of file +3.1: updated +3.0.1: Installation does not fail but is never complete. So installb modelangelo will git pull all the time. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index 9775f31..d4b7d19 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ include *.txt include *.rst -include LICENSE \ No newline at end of file +include LICENSE +include modelangelo/protocols.conf +include modelangelo/logo.jpeg \ No newline at end of file diff --git a/modelangelo/__init__.py b/modelangelo/__init__.py index 892cb03..2a4d8fa 100644 --- a/modelangelo/__init__.py +++ b/modelangelo/__init__.py @@ -31,9 +31,9 @@ from pyworkflow.utils import runJob from scipion.install.funcs import VOID_TGZ -__version__ = "3.0.4" -_logo = "icon.jpeg" -_references = ['mabioarxive'] +__version__ = "3.1" +_logo = "logo.jpeg" +_references = ['jamali2023'] # TODO: move to constants MA_VERSION = 'git' @@ -57,7 +57,7 @@ def _defineVariables(cls): cls._defineEmVar(TORCH_HOME_VAR, MODELS_PKG_NAME + "-" + MODELS_VERSION) @classmethod - def getModelAngeloCmd(cls, *args): + def getModelAngeloCmd(cls): cmd = cls.getVar(MODEL_ANGELO_ACTIVATION_VAR) if not cmd: cmd = cls.getCondaActivationCmd() @@ -72,6 +72,7 @@ def getEnviron(cls): # For GPU, we need to add to LD_LIBRARY_PATH the path to Cuda/lib environ.set(TORCH_HOME_VAR, torch_home) return environ + @classmethod def getActivationCmd(cls, version): return'conda activate modelangelo-' + version @@ -111,16 +112,12 @@ def getCondaInstallation(version): # Models download installationCmd = "" installationCmd += 'export TORCH_HOME=$PWD && ' - installationCmd += cls.getCondaActivationCmd() + " " + cls.getActivationCmd(MA_VERSION) + ' && ' + installationCmd += cls.getCondaActivationCmd() + " " + cls.getActivationCmd(MA_VERSION) + ' && ' installationCmd += 'python -m model_angelo.utils.setup_weights --bundle-name original && ' installationCmd += 'python -m model_angelo.utils.setup_weights --bundle-name original_no_seq' - env.addPackage('modelangelomodels', version="0.1", - commands=[(installationCmd,["hub/checkpoints/model_angelo/original_no_seq/success.txt", - "hub/checkpoints/model_angelo/original/success.txt"])], + commands=[(installationCmd, ["hub/checkpoints/model_angelo/original_no_seq/success.txt", + "hub/checkpoints/model_angelo/original/success.txt"])], tar=VOID_TGZ, default=True) - - - diff --git a/modelangelo/bibtex.py b/modelangelo/bibtex.py index 35b524b..362d315 100644 --- a/modelangelo/bibtex.py +++ b/modelangelo/bibtex.py @@ -26,15 +26,14 @@ # ************************************************************************** """ -@article{mabioarxive, - doi = "https://doi.org/10.48550/arxiv.2210.00006", - url = "https://arxiv.org/abs/2210.00006", - author = "Jamali, Kiarash and Kimanius, Dari and Scheres, Sjors", - keywords = "Quantitative Methods (q-bio.QM), Artificial Intelligence (cs.AI), Machine Learning (cs.LG), Biomolecules (q-bio.BM), FOS: Biological sciences, FOS: Biological sciences, FOS: Computer and information sciences, FOS: Computer and information sciences", - title = "ModelAngelo: Automated Model Building in Cryo-EM Maps", - publisher = "arXiv", - year = "2022", - copyright = "Creative Commons Attribution 4.0 International" +@article{jamali2023, +author = {Kiarash Jamali and Lukas Kall and Rui Zhang and Alan Brown and Dari Kimanius and Sjors Scheres}, +title = {Automated model building and protein identification in cryo-EM maps}, +year = {2023}, +doi = {https://dx.doi.org/10.1101/2023.05.16.541002}, +publisher = {Cold Spring Harbor Laboratory}, +URL = {https://www.biorxiv.org/content/early/2023/05/16/2023.05.16.541002}, +journal = {bioRxiv} } """ diff --git a/modelangelo/icon.jpeg b/modelangelo/icon.jpeg deleted file mode 100644 index 9280080609d9cc4df6108aa47286be7279c7becd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26218 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 diff --git a/modelangelo/protocols/__init__.py b/modelangelo/protocols/__init__.py index e54d899..5236da8 100644 --- a/modelangelo/protocols/__init__.py +++ b/modelangelo/protocols/__init__.py @@ -1,6 +1,24 @@ -# -*- coding: utf-8 -*- -# ************************************************************************** -# Module to declare protocols -# Find documentation here: https://scipion-em.github.io/docs/docs/developer/creating-a-protocol -# ************************************************************************** -from .protocol_model_angelo import ProtModelAngelo \ No newline at end of file +# *************************************************************************** +# * Authors: Roberto Marabini (roberto@cnb.csic.es) +# * +# * +# * 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' +# *************************************************************************** + +from .protocol_model_angelo import ProtModelAngelo diff --git a/modelangelo/protocols/protocol_model_angelo.py b/modelangelo/protocols/protocol_model_angelo.py index 6bc6d27..4fb1594 100644 --- a/modelangelo/protocols/protocol_model_angelo.py +++ b/modelangelo/protocols/protocol_model_angelo.py @@ -27,7 +27,7 @@ import os.path -from pwem.objects import Volume, AtomStruct, Sequence, VolumeMask +from pwem.objects import Volume, AtomStruct, VolumeMask from pyworkflow.protocol import params, LEVEL_ADVANCED from pyworkflow.utils import Message from pwem.protocols import EMProtocol @@ -58,41 +58,40 @@ def _defineParams(self, form): Params: form: this is the form to be populated with sections and params. """ - # You need a params to belong to a section: form.addSection(label=Message.LABEL_INPUT) form.addParam('inputVolume', params.PointerParam, pointerClass=Volume, label='Refined volume', important=True, - help='Refined cryo em map.') + help='Refined cryo-em map.') form.addParam('inputSequenceS', params.MultiPointerParam, pointerClass="Sequence", allowsNull=True, important=True, label='Protein sequences', help="Include here one or more sequences to be modeled\n" - "Leave empty to use the *model_no_seq* option. ") + "Leave empty to use the *model_no_seq* option.") form.addParam('inputMask', params.PointerParam, pointerClass=VolumeMask, label='Volume mask', allowsNull=True, important=True, - help='Mask. Search will be done inside the mask.\n' - 'That is, voxels in the mask NON zero valued') + help='Search will be done inside the mask.\n' + 'That is, voxels inside the mask should be NON zero.') form.addHidden(USE_GPU, params.BooleanParam, default=True, label="Use GPU for execution", - help="This protocol has both CPU and GPU implementation." + help="This protocol has both CPU and GPU implementation. " "Select the one you want to use.") - + form.addHidden(GPU_LIST, params.StringParam, default='0', expertLevel=LEVEL_ADVANCED, label="Choose GPU ID (single one)", help="GPU device to be used") - + form.addParam('configFile', params.FileParam, - label="Configuration File", - default="", - expertLevel=LEVEL_ADVANCED, - help="""this option is only for VERY advanced users,\n -Follows an example of config file + label="Configuration File", + default="", + expertLevel=LEVEL_ADVANCED, + help="""This option is only for VERY advanced users.\n +Below is an example of a config file: { "standardize_mrc_args": @@ -150,7 +149,6 @@ def convertInputStep(self): sampling = vol.getSamplingRate() Ccp4Header.fixFile(inVolName, self.newFn, origin, sampling, Ccp4Header.START) # ORIGIN - def createInputFastaFile(self, seqs): """ Get sequence as string and create the corresponding fasta file. """ @@ -182,10 +180,8 @@ def predictStep(self): if mask: args.extend(["--mask-path", mask.getFileName()]) - # Gpu or cpu - args.extend(["--device", ("%s" % self.getGpuList()[0]) - if self.useGpu else "cpu"]) + args.extend(["--device", ("%s" % self.getGpuList()[0]) if self.useGpu else "cpu"]) if configFile: args.extend(["-c", configFile]) @@ -204,14 +200,14 @@ def predictStep(self): "for more details." % line) from None def createOutputStep(self): - "Register atomic models, raw and pruned" + """Register atomic models, raw and pruned""" # check if files exists before registering # I think build_no_seq creates a single output file (no raw file) if os.path.exists(self._getExtraPath('extra_raw.cif')): self._registerAtomStruct(OUTPUT_RAW_NAME, self._getExtraPath('extra_raw.cif')) self._registerAtomStruct(OUTPUT_NAME, self._getExtraPath('extra.cif')) - def _registerAtomStruct(self,name, path): + def _registerAtomStruct(self, name, path): if not os.path.exists(path): raise Exception("Output %s not found." % path) @@ -225,17 +221,6 @@ def _registerAtomStruct(self,name, path): self._defineSourceRelation(seq, output) # --------------------------- INFO functions ----------------------------------- - def _summary(self): - """ Summarize what the protocol has done""" - summary = [] - - return summary - - def _methods(self): - methods = [] - - return methods - def _validate(self): """ Should be implemented in subclasses. See warning. """ errors = [] diff --git a/modelangelo/tests/__init__.py b/modelangelo/tests/__init__.py index 7752be5..3306c1c 100644 --- a/modelangelo/tests/__init__.py +++ b/modelangelo/tests/__init__.py @@ -24,4 +24,4 @@ # * # ************************************************************************** -from .tests_model_angelo import TestModelAngelo \ No newline at end of file +from .tests_model_angelo import TestModelAngelo diff --git a/modelangelo/tests/tests_model_angelo.py b/modelangelo/tests/tests_model_angelo.py index 1678615..2ddd849 100644 --- a/modelangelo/tests/tests_model_angelo.py +++ b/modelangelo/tests/tests_model_angelo.py @@ -21,15 +21,13 @@ # * e-mail address 'scipion@cnb.csic.es' # ***************************************************************************/ -# protocol to test the operation on PDB files from os.path import exists -from collections import Counter -#from ..protocols import ProtModelAngelo from pyworkflow.tests import BaseTest, setupTestProject import pwem.protocols as emprot from ..protocols import ProtModelAngelo + class TestModelAngelo(BaseTest): @classmethod def setUpClass(cls): @@ -66,7 +64,7 @@ def test_ProtModelAngeloBuild(self): 'inputSequenceS': listSeq, 'useGpu': True, 'gpuList': "0", - } + } prot2 = self.newProtocol(ProtModelAngelo, **args) prot2.setObjLabel('model angelo') self.launchProtocol(prot2) @@ -91,7 +89,7 @@ def test_ProtModelAngeloBuildNoSeq(self): args = {'inputVolume': vol, 'useGpu': True, 'gpuList': "0", - } + } prot2 = self.newProtocol(ProtModelAngelo, **args) prot2.setObjLabel('model angelo\n No sequence') self.launchProtocol(prot2) diff --git a/modelangelo/viewers/__init__.py b/modelangelo/viewers/__init__.py index 5ec2c92..ef2c6a7 100644 --- a/modelangelo/viewers/__init__.py +++ b/modelangelo/viewers/__init__.py @@ -24,5 +24,4 @@ # * # ************************************************************************** -from .viewer import (ProtModelAngeloViewer, - ) \ No newline at end of file +from .viewer import ProtModelAngeloViewer diff --git a/modelangelo/viewers/viewer.py b/modelangelo/viewers/viewer.py index 51ea4b3..9fbbe2a 100644 --- a/modelangelo/viewers/viewer.py +++ b/modelangelo/viewers/viewer.py @@ -25,15 +25,15 @@ import os from pyworkflow.viewer import Viewer -from ..protocols.protocol_model_angelo import ProtModelAngelo -from pwem.viewers.viewer_chimera import (Chimera) +from pwem.viewers.viewer_chimera import Chimera from pwem.emlib.image import ImageHandler +from ..protocols.protocol_model_angelo import ProtModelAngelo + class ProtModelAngeloViewer(Viewer): _label = 'viewer model angelo' _targets = [ProtModelAngelo] - def visInputVolume(self, f, vol, counter): inputVolFileName = ImageHandler.removeFileType(vol.getFileName()) f.write("open %s\n" % inputVolFileName) @@ -62,10 +62,10 @@ def visualize(self, obj, **args): fnCmd = self.protocol._getExtraPath("model_angelo_viewer.cxc") f = open(fnCmd, 'w') f.write("open %s\n" % bildFileName) - models +=1 + models += 1 f.write("cofr 0,0,0\n") # set center of coordinates # change to workingDir - # If we do not use cd and the project name has an space + # If we do not use cd and the project name has a space # the protocol fails even if we pass absolute paths f.write('cd %s\n' % os.getcwd()) @@ -75,7 +75,7 @@ def visualize(self, obj, **args): fileName = os.path.abspath(eval(f'self.protocol.{output}.getFileName()')) if fileName.endswith(".cif") or fileName.endswith(".pdb"): f.write("open %s\n" % fileName) - models +=1 + models += 1 # set alphafold colormap f.write("color bfactor palette alphafold\n") f.write("key red:low orange: yellow: cornflowerblue: blue:high\n") diff --git a/modelangelo/wizards/__init__.py b/modelangelo/wizards/__init__.py deleted file mode 100644 index 348ed76..0000000 --- a/modelangelo/wizards/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# ************************************************************************** -# * -# * Authors: Roberto Marabini -# * -# * -# * 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' -# * -# ************************************************************************** diff --git a/setup.py b/setup.py index fa9a899..9a2ae7d 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,15 @@ install_requires=[requirements], entry_points={'pyworkflow.plugin': 'modelangelo = modelangelo'}, package_data={ # Optional - 'modelangelo': ['icon.jpeg', 'protocols.conf'], - } + 'modelangelo': ['logo.jpeg', 'protocols.conf'], + }, + classifiers=[ # Optional + 'Development Status :: 4 - Beta', + 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', + 'Programming Language :: Python :: 3' + ], + project_urls={ # Optional + 'Bug Reports': 'https://github.com/scipion-em/scipion-em-modelangelo/issues', + 'Source': 'https://github.com/scipion-em/scipion-em-modelangelo/', + }, ) From c4dd302c9194d377c463a6d9eeac5f29be4d06ac Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Sun, 10 Sep 2023 16:21:34 +0100 Subject: [PATCH 6/9] logo --- modelangelo/logo.jpeg | Bin 0 -> 26218 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 modelangelo/logo.jpeg diff --git a/modelangelo/logo.jpeg b/modelangelo/logo.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..9280080609d9cc4df6108aa47286be7279c7becd GIT binary patch literal 26218 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 literal 0 HcmV?d00001 From 53f5153dd4bbbd0eaf9c8488e26cc9dfa57ac18c Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Sun, 10 Sep 2023 16:50:51 +0100 Subject: [PATCH 7/9] 1) update installation; 2) create constants; 3) add MODEL_ANGELO_CUDA_LIB --- README.rst | 2 +- modelangelo/__init__.py | 48 +++++++--------- modelangelo/bibtex.py | 2 +- modelangelo/constants.py | 37 +++++++++++++ modelangelo/protocols/__init__.py | 2 +- .../protocols/protocol_model_angelo.py | 55 ++++++++++--------- modelangelo/tests/__init__.py | 2 +- modelangelo/tests/tests_model_angelo.py | 2 +- modelangelo/viewers/__init__.py | 2 +- modelangelo/viewers/viewer.py | 2 +- 10 files changed, 93 insertions(+), 61 deletions(-) create mode 100644 modelangelo/constants.py diff --git a/README.rst b/README.rst index d6caba4..e01ec8e 100644 --- a/README.rst +++ b/README.rst @@ -54,7 +54,7 @@ conda activate will not be prepended. For example (loading model-angelo as a mod MODEL_ANGELO_ACTIVATION = module load model-angelo/main - +If you need to use CUDA different from the one used during Scipion installation (defined by *CUDA_LIB*), you can add *MODEL_ANGELO_CUDA_LIB* variable to the config file. Protocols --------- diff --git a/modelangelo/__init__.py b/modelangelo/__init__.py index 2a4d8fa..c05b7e4 100644 --- a/modelangelo/__init__.py +++ b/modelangelo/__init__.py @@ -6,7 +6,7 @@ # * # * 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 +# * 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, @@ -23,30 +23,19 @@ # * e-mail address 'scipion@cnb.csic.es' # * # ************************************************************************** -import datetime +from datetime import datetime as dt import os import pwem -import pyworkflow -from pyworkflow.utils import runJob +import pyworkflow.utils as pwutils from scipion.install.funcs import VOID_TGZ +from .constants import * + __version__ = "3.1" _logo = "logo.jpeg" _references = ['jamali2023'] -# TODO: move to constants -MA_VERSION = 'git' -# Use this variable to activate an environment from the Scipion conda -MODEL_ANGELO_ENV_ACTIVATION_VAR = "MODEL_ANGELO_ENV_ACTIVATION" -# Use this general activation variable when installed outside Scipion -MODEL_ANGELO_ACTIVATION_VAR = "MODEL_ANGELO_ACTIVATION" - -# models -MODELS_VERSION = '0.1' -MODELS_PKG_NAME = 'modelangelomodels' -TORCH_HOME_VAR = 'TORCH_HOME' - class Plugin(pwem.Plugin): @@ -55,6 +44,7 @@ def _defineVariables(cls): cls._defineVar(MODEL_ANGELO_ACTIVATION_VAR, '') cls._defineVar(MODEL_ANGELO_ENV_ACTIVATION_VAR, cls.getActivationCmd(MA_VERSION)) cls._defineEmVar(TORCH_HOME_VAR, MODELS_PKG_NAME + "-" + MODELS_VERSION) + cls._defineVar(MODEL_ANGELO_CUDA_LIB, pwem.Config.CUDA_LIB) @classmethod def getModelAngeloCmd(cls): @@ -67,10 +57,13 @@ def getModelAngeloCmd(cls): @classmethod def getEnviron(cls): - environ = pyworkflow.utils.Environ(os.environ) + environ = pwutils.Environ(os.environ) torch_home = cls.getVar(TORCH_HOME_VAR) - # For GPU, we need to add to LD_LIBRARY_PATH the path to Cuda/lib environ.set(TORCH_HOME_VAR, torch_home) + + cudaLib = cls.getVar(MODEL_ANGELO_CUDA_LIB, pwem.Config.CUDA_LIB) + environ.addLibrary(cudaLib) + return environ @classmethod @@ -82,13 +75,14 @@ def defineBinaries(cls, env): def defineModelAngeloInstallation(version): - installed = "last-pull-%s.txt" % datetime.datetime.now().strftime("%y%h%d-%H%M%S") + installed = "last-pull-%s.txt" % dt.now().strftime("%y%h%d-%H%M%S") # For modelangelo - modelangelo_commands = [] - modelangelo_commands.append(('git clone https://github.com/3dem/model-angelo.git', 'model-angelo')) - modelangelo_commands.append((getCondaInstallation(version), 'env-created.txt')) - modelangelo_commands.append(('cd model-angelo && git pull && touch ../%s' % installed, installed)) + modelangelo_commands = [ + ('git clone https://github.com/3dem/model-angelo.git', 'model-angelo'), + (getCondaInstallation(version), 'env-created.txt'), + ('cd model-angelo && git pull && touch ../%s' % installed, installed) + ] env.addPackage('modelangelo', version=version, commands=modelangelo_commands, @@ -97,11 +91,11 @@ def defineModelAngeloInstallation(version): def getCondaInstallation(version): installationCmd = cls.getCondaActivationCmd() - installationCmd += 'conda create -y -n modelangelo-' + version + ' python=3.9 && ' + installationCmd += 'conda create -y -n modelangelo-' + version + ' python=3.10 && ' installationCmd += cls.getActivationCmd(version) + ' && ' - installationCmd += 'cd model-angelo && python -m pip install -r requirements.txt && ' - installationCmd += 'conda install -y pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch && ' - installationCmd += 'python -m pip install -e . && ' + installationCmd += 'conda install pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia && ' + installationCmd += 'cd model-angelo && pip install -r requirements.txt && ' + installationCmd += 'pip install -e . && ' installationCmd += 'touch ../env-created.txt' return installationCmd diff --git a/modelangelo/bibtex.py b/modelangelo/bibtex.py index 362d315..a976040 100644 --- a/modelangelo/bibtex.py +++ b/modelangelo/bibtex.py @@ -7,7 +7,7 @@ # * # * 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 +# * 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, diff --git a/modelangelo/constants.py b/modelangelo/constants.py new file mode 100644 index 0000000..e354ee2 --- /dev/null +++ b/modelangelo/constants.py @@ -0,0 +1,37 @@ +# ************************************************************************** +# * +# * 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' +# * +# ************************************************************************** + +MA_VERSION = 'git' +# Use this variable to activate an environment from the Scipion conda +MODEL_ANGELO_ENV_ACTIVATION_VAR = "MODEL_ANGELO_ENV_ACTIVATION" +# Use this general activation variable when installed outside Scipion +MODEL_ANGELO_ACTIVATION_VAR = "MODEL_ANGELO_ACTIVATION" +MODEL_ANGELO_CUDA_LIB = "MODEL_ANGELO_CUDA_LIB" + +# models +MODELS_VERSION = '0.1' +MODELS_PKG_NAME = 'modelangelomodels' +TORCH_HOME_VAR = 'TORCH_HOME' diff --git a/modelangelo/protocols/__init__.py b/modelangelo/protocols/__init__.py index 5236da8..45f00df 100644 --- a/modelangelo/protocols/__init__.py +++ b/modelangelo/protocols/__init__.py @@ -4,7 +4,7 @@ # * # * 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 +# * 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, diff --git a/modelangelo/protocols/protocol_model_angelo.py b/modelangelo/protocols/protocol_model_angelo.py index 4fb1594..3db1634 100644 --- a/modelangelo/protocols/protocol_model_angelo.py +++ b/modelangelo/protocols/protocol_model_angelo.py @@ -7,7 +7,7 @@ # * # * 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 +# * 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, @@ -128,13 +128,14 @@ def _defineParams(self, form): } """) - # --------------------------- STEPS functions ------------------------------ + # -------------------------- INSERT steps functions ----------------------- def _insertAllSteps(self): # Insert processing steps self._insertFunctionStep(self.convertInputStep) self._insertFunctionStep(self.predictStep) self._insertFunctionStep(self.createOutputStep) + # --------------------------- STEPS functions ------------------------------ def convertInputStep(self): """ convert 3D maps to MRC '.mrc' format with extension mrc, map extension is not handled by modelangelo @@ -149,19 +150,6 @@ def convertInputStep(self): sampling = vol.getSamplingRate() Ccp4Header.fixFile(inVolName, self.newFn, origin, sampling, Ccp4Header.START) # ORIGIN - def createInputFastaFile(self, seqs): - """ Get sequence as string and create the corresponding fasta file. """ - - fastaFileName = self._getExtraPath('sequence.fasta') - - with open(fastaFileName, "w") as f: - for seq in seqs: - s = seq.get() - f.write(f"> {s.getId()}\n") - f.write(f"{s.getSequence()}\n") - - return fastaFileName - def predictStep(self): seqs = self.inputSequenceS mask = self.inputMask.get() @@ -207,9 +195,33 @@ def createOutputStep(self): self._registerAtomStruct(OUTPUT_RAW_NAME, self._getExtraPath('extra_raw.cif')) self._registerAtomStruct(OUTPUT_NAME, self._getExtraPath('extra.cif')) + # --------------------------- INFO functions ----------------------------------- + def _validate(self): + errors = [] + gpus = self.getGpuList() + + if len(gpus) > 1: + errors.append('Only one GPU can be used.') + + return errors + + # -------------------------- UTILS functions ------------------------------ + def createInputFastaFile(self, seqs): + """ Get sequence as string and create the corresponding fasta file. """ + + fastaFileName = self._getExtraPath('sequence.fasta') + + with open(fastaFileName, "w") as f: + for seq in seqs: + s = seq.get() + f.write(f"> {s.getId()}\n") + f.write(f"{s.getSequence()}\n") + + return fastaFileName + def _registerAtomStruct(self, name, path): if not os.path.exists(path): - raise Exception("Output %s not found." % path) + raise FileNotFoundError("Output %s not found." % path) output = AtomStruct(filename=path) self._defineOutputs(**{name: output}) @@ -219,14 +231,3 @@ def _registerAtomStruct(self, name, path): if seqs: for seq in seqs: self._defineSourceRelation(seq, output) - - # --------------------------- INFO functions ----------------------------------- - def _validate(self): - """ Should be implemented in subclasses. See warning. """ - errors = [] - gpus = self.getGpuList() - - if len(gpus) > 1: - errors.append('Only one GPU can be used.') - - return errors diff --git a/modelangelo/tests/__init__.py b/modelangelo/tests/__init__.py index 3306c1c..3f06d23 100644 --- a/modelangelo/tests/__init__.py +++ b/modelangelo/tests/__init__.py @@ -6,7 +6,7 @@ # * # * 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 +# * 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, diff --git a/modelangelo/tests/tests_model_angelo.py b/modelangelo/tests/tests_model_angelo.py index 2ddd849..0562e80 100644 --- a/modelangelo/tests/tests_model_angelo.py +++ b/modelangelo/tests/tests_model_angelo.py @@ -4,7 +4,7 @@ # * # * 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 +# * 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, diff --git a/modelangelo/viewers/__init__.py b/modelangelo/viewers/__init__.py index ef2c6a7..0bf80d3 100644 --- a/modelangelo/viewers/__init__.py +++ b/modelangelo/viewers/__init__.py @@ -6,7 +6,7 @@ # * # * 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 +# * 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, diff --git a/modelangelo/viewers/viewer.py b/modelangelo/viewers/viewer.py index 9fbbe2a..a163762 100644 --- a/modelangelo/viewers/viewer.py +++ b/modelangelo/viewers/viewer.py @@ -5,7 +5,7 @@ # * # * 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 +# * 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, From 35faaacf2e544d5316cce09a78c28983f8ea3671 Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Sun, 10 Sep 2023 17:09:16 +0100 Subject: [PATCH 8/9] fix models installation --- CHANGES.txt | 5 ++++- modelangelo/__init__.py | 11 ++++------- modelangelo/constants.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 846b0f3..2842b1c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,2 +1,5 @@ -3.1: updated +3.1: + - update installation + - create constants + - add MODEL_ANGELO_CUDA_LIB 3.0.1: Installation does not fail but is never complete. So installb modelangelo will git pull all the time. \ No newline at end of file diff --git a/modelangelo/__init__.py b/modelangelo/__init__.py index c05b7e4..3cd2c86 100644 --- a/modelangelo/__init__.py +++ b/modelangelo/__init__.py @@ -74,10 +74,8 @@ def getActivationCmd(cls, version): def defineBinaries(cls, env): def defineModelAngeloInstallation(version): - installed = "last-pull-%s.txt" % dt.now().strftime("%y%h%d-%H%M%S") - # For modelangelo modelangelo_commands = [ ('git clone https://github.com/3dem/model-angelo.git', 'model-angelo'), (getCondaInstallation(version), 'env-created.txt'), @@ -93,14 +91,13 @@ def getCondaInstallation(version): installationCmd = cls.getCondaActivationCmd() installationCmd += 'conda create -y -n modelangelo-' + version + ' python=3.10 && ' installationCmd += cls.getActivationCmd(version) + ' && ' - installationCmd += 'conda install pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia && ' + installationCmd += 'conda install -y pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia && ' installationCmd += 'cd model-angelo && pip install -r requirements.txt && ' installationCmd += 'pip install -e . && ' installationCmd += 'touch ../env-created.txt' return installationCmd - # Define model angelo installations defineModelAngeloInstallation(MA_VERSION) # Models download @@ -110,8 +107,8 @@ def getCondaInstallation(version): installationCmd += 'python -m model_angelo.utils.setup_weights --bundle-name original && ' installationCmd += 'python -m model_angelo.utils.setup_weights --bundle-name original_no_seq' - env.addPackage('modelangelomodels', version="0.1", - commands=[(installationCmd, ["hub/checkpoints/model_angelo/original_no_seq/success.txt", - "hub/checkpoints/model_angelo/original/success.txt"])], + env.addPackage('modelangelomodels', version=MODELS_VERSION, + commands=[(installationCmd, [f"hub/checkpoints/model_angelo_v{MODELS_VERSION}/original_no_seq/success.txt", + f"hub/checkpoints/model_angelo_v{MODELS_VERSION}/original/success.txt"])], tar=VOID_TGZ, default=True) diff --git a/modelangelo/constants.py b/modelangelo/constants.py index e354ee2..223377c 100644 --- a/modelangelo/constants.py +++ b/modelangelo/constants.py @@ -32,6 +32,6 @@ MODEL_ANGELO_CUDA_LIB = "MODEL_ANGELO_CUDA_LIB" # models -MODELS_VERSION = '0.1' +MODELS_VERSION = '1.0' MODELS_PKG_NAME = 'modelangelomodels' TORCH_HOME_VAR = 'TORCH_HOME' From e464004b09af1015754dbcaa470f6cc40b50cbe7 Mon Sep 17 00:00:00 2001 From: Grigory Sharov Date: Mon, 11 Sep 2023 09:24:47 +0100 Subject: [PATCH 9/9] remove unnecessary default value --- modelangelo/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modelangelo/__init__.py b/modelangelo/__init__.py index 3cd2c86..875dbfa 100644 --- a/modelangelo/__init__.py +++ b/modelangelo/__init__.py @@ -61,7 +61,7 @@ def getEnviron(cls): torch_home = cls.getVar(TORCH_HOME_VAR) environ.set(TORCH_HOME_VAR, torch_home) - cudaLib = cls.getVar(MODEL_ANGELO_CUDA_LIB, pwem.Config.CUDA_LIB) + cudaLib = cls.getVar(MODEL_ANGELO_CUDA_LIB) environ.addLibrary(cudaLib) return environ