modelcraft 5.0.2__py3-none-any.whl → 6.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modelcraft/__init__.py +16 -31
- modelcraft/__main__.py +0 -1
- modelcraft/arguments.py +35 -7
- modelcraft/combine.py +22 -41
- modelcraft/contents.py +188 -164
- modelcraft/environ.py +0 -7
- modelcraft/geometry.py +39 -27
- modelcraft/job.py +6 -5
- modelcraft/jobs/acedrg.py +2 -0
- modelcraft/jobs/buccaneer.py +22 -4
- modelcraft/jobs/comit.py +2 -0
- modelcraft/jobs/ctruncate.py +3 -1
- modelcraft/jobs/emda.py +2 -0
- modelcraft/jobs/findwaters.py +2 -0
- modelcraft/jobs/freerflag.py +2 -0
- modelcraft/jobs/libg.py +2 -0
- modelcraft/jobs/molrep.py +2 -0
- modelcraft/jobs/nautilus.py +28 -14
- modelcraft/jobs/nucleofind.py +88 -0
- modelcraft/jobs/parrot.py +13 -2
- modelcraft/jobs/phasematch.py +2 -1
- modelcraft/jobs/refmac.py +3 -1
- modelcraft/jobs/servalcat.py +38 -4
- modelcraft/jobs/sheetbend.py +2 -0
- modelcraft/modelcraftem.py +49 -6
- modelcraft/modelcraftxray.py +90 -42
- modelcraft/monlib.py +55 -52
- modelcraft/pdbe.py +54 -0
- modelcraft/pipeline.py +1 -1
- modelcraft/prune.py +69 -0
- modelcraft/reflections.py +11 -1
- modelcraft/scripts/contents.py +5 -215
- modelcraft/scripts/copies.py +26 -17
- modelcraft/scripts/modelcraft.py +1 -0
- modelcraft/scripts/sidechains.py +141 -0
- modelcraft/scripts/validate.py +81 -0
- modelcraft/sequence.py +106 -0
- modelcraft/solvent.py +42 -113
- modelcraft/structure.py +64 -41
- modelcraft/tests/ccp4/__init__.py +7 -11
- modelcraft/tests/ccp4/test_acedrg.py +2 -0
- modelcraft/tests/ccp4/test_arguments.py +3 -0
- modelcraft/tests/ccp4/test_buccaneer.py +3 -2
- modelcraft/tests/ccp4/test_cell.py +4 -1
- modelcraft/tests/ccp4/test_comit.py +2 -0
- modelcraft/tests/ccp4/test_contents.py +99 -17
- modelcraft/tests/ccp4/test_copies.py +1 -0
- modelcraft/tests/ccp4/test_ctruncate.py +2 -0
- modelcraft/tests/ccp4/test_findwaters.py +2 -0
- modelcraft/tests/ccp4/test_freerflag.py +2 -0
- modelcraft/tests/ccp4/test_libg.py +1 -0
- modelcraft/tests/ccp4/test_molrep.py +3 -0
- modelcraft/tests/ccp4/test_monlib.py +75 -45
- modelcraft/tests/ccp4/test_nautilus.py +5 -3
- modelcraft/tests/ccp4/test_nucleofind.py +62 -0
- modelcraft/tests/ccp4/test_parrot.py +3 -1
- modelcraft/tests/ccp4/test_phasematch.py +2 -0
- modelcraft/tests/ccp4/test_prune.py +17 -0
- modelcraft/tests/ccp4/test_reflections.py +110 -1
- modelcraft/tests/ccp4/test_refmac.py +3 -0
- modelcraft/tests/{unittests/test_contents.py → ccp4/test_sequence.py} +5 -12
- modelcraft/tests/ccp4/test_servalcat.py +52 -0
- modelcraft/tests/ccp4/test_sheetbend.py +4 -3
- modelcraft/tests/ccp4/test_sidechains.py +25 -0
- modelcraft/tests/ccp4/test_solvent.py +12 -26
- modelcraft/tests/ccp4/test_structure.py +1 -0
- modelcraft/tests/ccp4/test_validation.py +19 -0
- modelcraft/tests/ccp4/test_xray.py +12 -6
- modelcraft/tests/ccpem/test_em.py +3 -0
- modelcraft/tests/ccpem/test_emda.py +2 -0
- modelcraft/tests/ccpem/test_refmac.py +1 -0
- modelcraft/tests/ccpem/test_servalcat.py +4 -3
- modelcraft/utils.py +16 -4
- modelcraft/validation.py +101 -0
- modelcraft-6.0.0.dist-info/METADATA +76 -0
- modelcraft-6.0.0.dist-info/RECORD +85 -0
- {modelcraft-5.0.2.dist-info → modelcraft-6.0.0.dist-info}/WHEEL +1 -1
- {modelcraft-5.0.2.dist-info → modelcraft-6.0.0.dist-info}/entry_points.txt +2 -0
- modelcraft/coot/prune.py +0 -1085
- modelcraft/coot/sidechains.py +0 -68
- modelcraft/jobs/acorn.py +0 -114
- modelcraft/jobs/coot.py +0 -104
- modelcraft/tests/ccp4/test_coot.py +0 -29
- modelcraft/tests/ccp4/test_geometry.py +0 -20
- modelcraft/tests/unittests/__init__.py +0 -0
- modelcraft/tests/unittests/test_reflections.py +0 -101
- modelcraft-5.0.2.dist-info/LICENSE +0 -504
- modelcraft-5.0.2.dist-info/METADATA +0 -48
- modelcraft-5.0.2.dist-info/RECORD +0 -82
- {modelcraft-5.0.2.dist-info → modelcraft-6.0.0.dist-info}/top_level.txt +0 -0
modelcraft/jobs/ctruncate.py
CHANGED
modelcraft/jobs/emda.py
CHANGED
modelcraft/jobs/findwaters.py
CHANGED
modelcraft/jobs/freerflag.py
CHANGED
modelcraft/jobs/libg.py
CHANGED
modelcraft/jobs/molrep.py
CHANGED
modelcraft/jobs/nautilus.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import xml.etree.ElementTree as ET
|
|
3
|
+
|
|
3
4
|
import gemmi
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
from ..contents import AsuContents
|
|
5
7
|
from ..job import Job
|
|
6
8
|
from ..reflections import DataItem, write_mtz
|
|
7
|
-
from ..
|
|
9
|
+
from ..sequence import DNA_CODES, PolymerType
|
|
10
|
+
from ..structure import read_structure, write_mmcif
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
@dataclasses.dataclass
|
|
@@ -16,6 +19,18 @@ class NautilusResult:
|
|
|
16
19
|
longest_fragment: int
|
|
17
20
|
seconds: float
|
|
18
21
|
|
|
22
|
+
def __init__(self, job: Job):
|
|
23
|
+
job._check_files_exist("xmlout.xml", "xyzout.cif")
|
|
24
|
+
xml = ET.parse(job._path("xmlout.xml")).getroot()
|
|
25
|
+
structure = read_structure(job._path("xyzout.cif"))
|
|
26
|
+
_deoxyfy(structure)
|
|
27
|
+
self.structure = structure
|
|
28
|
+
self.fragments_built = int(xml.find("Final/FragmentsBuilt").text)
|
|
29
|
+
self.residues_built = int(xml.find("Final/ResiduesBuilt").text)
|
|
30
|
+
self.residues_sequenced = int(xml.find("Final/ResiduesSequenced").text)
|
|
31
|
+
self.longest_fragment = int(xml.find("Final/ResiduesLongestFragment").text)
|
|
32
|
+
self.seconds = job._seconds
|
|
33
|
+
|
|
19
34
|
|
|
20
35
|
class Nautilus(Job):
|
|
21
36
|
def __init__(
|
|
@@ -63,15 +78,14 @@ class Nautilus(Job):
|
|
|
63
78
|
self._args += ["-xmlout", "xmlout.xml"]
|
|
64
79
|
|
|
65
80
|
def _result(self) -> NautilusResult:
|
|
66
|
-
self
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
81
|
+
return NautilusResult(self)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _deoxyfy(structure: gemmi.Structure) -> None:
|
|
85
|
+
dna_codes = set(DNA_CODES.values())
|
|
86
|
+
for chain in structure[0]:
|
|
87
|
+
for residue in chain:
|
|
88
|
+
if residue.name in dna_codes and "O2'" in residue:
|
|
89
|
+
for i, atom in reversed(list(enumerate(residue))):
|
|
90
|
+
if atom.name == "O2'":
|
|
91
|
+
del residue[i]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
import gemmi
|
|
4
|
+
|
|
5
|
+
from ..contents import AsuContents, PolymerType
|
|
6
|
+
from ..job import Job
|
|
7
|
+
from ..jobs.nautilus import NautilusResult
|
|
8
|
+
from ..reflections import DataItem, write_mtz
|
|
9
|
+
from ..structure import write_mmcif
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclasses.dataclass
|
|
13
|
+
class NucleoFindPrediction:
|
|
14
|
+
phosphate: gemmi.Ccp4Map
|
|
15
|
+
sugar: gemmi.Ccp4Map
|
|
16
|
+
base: gemmi.Ccp4Map
|
|
17
|
+
seconds: float
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NucleoFindPredict(Job):
|
|
21
|
+
def __init__(self, fphi: DataItem):
|
|
22
|
+
super().__init__("nucleofind")
|
|
23
|
+
self.fphi = fphi
|
|
24
|
+
|
|
25
|
+
def _setup(self) -> None:
|
|
26
|
+
write_mtz(self._path("hklin.mtz"), [self.fphi])
|
|
27
|
+
self._args += ["--input", "hklin.mtz"]
|
|
28
|
+
self._args += ["--amplitude", self.fphi.label(0)]
|
|
29
|
+
self._args += ["--phase", self.fphi.label(1)]
|
|
30
|
+
self._args += ["--model", "core"]
|
|
31
|
+
self._args += ["--gpu"]
|
|
32
|
+
self._args += ["--nthreads", "1"]
|
|
33
|
+
self._args += ["--output", "."]
|
|
34
|
+
|
|
35
|
+
def _result(self) -> NucleoFindPrediction:
|
|
36
|
+
self._check_files_exist("nucleofind-phosphate.map")
|
|
37
|
+
self._check_files_exist("nucleofind-sugar.map")
|
|
38
|
+
self._check_files_exist("nucleofind-base.map")
|
|
39
|
+
|
|
40
|
+
return NucleoFindPrediction(
|
|
41
|
+
phosphate=gemmi.read_ccp4_map(self._path("nucleofind-phosphate.map")),
|
|
42
|
+
sugar=gemmi.read_ccp4_map(self._path("nucleofind-sugar.map")),
|
|
43
|
+
base=gemmi.read_ccp4_map(self._path("nucleofind-base.map")),
|
|
44
|
+
seconds=self._seconds,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class NucleoFindBuild(Job):
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
contents: AsuContents,
|
|
52
|
+
fphi: DataItem,
|
|
53
|
+
prediction: NucleoFindPrediction,
|
|
54
|
+
structure: gemmi.Structure = None,
|
|
55
|
+
em: bool = False,
|
|
56
|
+
):
|
|
57
|
+
super().__init__("nucleofind-build")
|
|
58
|
+
self.contents = contents
|
|
59
|
+
self.fphi = fphi
|
|
60
|
+
self.prediction = prediction
|
|
61
|
+
self.structure = structure
|
|
62
|
+
self.em = em
|
|
63
|
+
|
|
64
|
+
def _setup(self) -> None:
|
|
65
|
+
types = [PolymerType.RNA, PolymerType.DNA]
|
|
66
|
+
self.contents.write_sequence_file(self._path("seqin.seq"), types)
|
|
67
|
+
self._args += ["--seqin", "seqin.seq"]
|
|
68
|
+
write_mtz(self._path("hklin.mtz"), [self.fphi])
|
|
69
|
+
self._args += ["--mtzin", "hklin.mtz"]
|
|
70
|
+
self._args += ["--colin-fc", self.fphi.label()]
|
|
71
|
+
self.prediction.phosphate.write_ccp4_map(self._path("phosin.map"))
|
|
72
|
+
self.prediction.sugar.write_ccp4_map(self._path("sugarin.map"))
|
|
73
|
+
self.prediction.base.write_ccp4_map(self._path("basein.map"))
|
|
74
|
+
self._args += ["--phosin", "phosin.map"]
|
|
75
|
+
self._args += ["--sugarin", "sugarin.map"]
|
|
76
|
+
self._args += ["--basein", "basein.map"]
|
|
77
|
+
if self.structure is not None:
|
|
78
|
+
write_mmcif(self._path("xyzin.cif"), self.structure)
|
|
79
|
+
self._args += ["--pdbin", "xyzin.cif"]
|
|
80
|
+
self._args += ["--cycles", "3"]
|
|
81
|
+
self._args += ["--pdbout", "xyzout.cif"]
|
|
82
|
+
self._args += ["--xmlout", "xmlout.xml"]
|
|
83
|
+
self._args += ["--remove_clashing_protein"]
|
|
84
|
+
if self.em:
|
|
85
|
+
self._args += ["--em"]
|
|
86
|
+
|
|
87
|
+
def _result(self) -> NautilusResult:
|
|
88
|
+
return NautilusResult(self)
|
modelcraft/jobs/parrot.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import dataclasses
|
|
2
|
+
|
|
2
3
|
import gemmi
|
|
4
|
+
|
|
3
5
|
from ..contents import AsuContents
|
|
4
6
|
from ..job import Job
|
|
7
|
+
from ..monlib import MonLib
|
|
5
8
|
from ..reflections import DataItem, write_mtz
|
|
6
9
|
from ..solvent import solvent_fraction
|
|
7
10
|
from ..structure import write_mmcif
|
|
@@ -23,12 +26,14 @@ class Parrot(Job):
|
|
|
23
26
|
phases: DataItem,
|
|
24
27
|
fphi: DataItem = None,
|
|
25
28
|
structure: gemmi.Structure = None,
|
|
29
|
+
monlib: MonLib = None,
|
|
26
30
|
):
|
|
27
31
|
super().__init__("cparrot")
|
|
28
32
|
self.contents = contents
|
|
29
33
|
self.fsigf = fsigf
|
|
30
34
|
self.freer = freer
|
|
31
35
|
self.phases = phases
|
|
36
|
+
self.monlib = monlib
|
|
32
37
|
self.fphi = fphi
|
|
33
38
|
self.structure = structure
|
|
34
39
|
|
|
@@ -49,8 +54,14 @@ class Parrot(Job):
|
|
|
49
54
|
if self.structure is not None:
|
|
50
55
|
write_mmcif(self._path("xyzin.cif"), self.structure)
|
|
51
56
|
self._args += ["-pdbin-mr", "xyzin.cif"]
|
|
52
|
-
solvent_content = solvent_fraction(
|
|
53
|
-
|
|
57
|
+
solvent_content = solvent_fraction(
|
|
58
|
+
contents=self.contents,
|
|
59
|
+
cell=self.fsigf.cell,
|
|
60
|
+
spacegroup=self.fsigf.spacegroup,
|
|
61
|
+
resolution=self.fsigf.resolution_high(),
|
|
62
|
+
monlib=self.monlib,
|
|
63
|
+
)
|
|
64
|
+
self._args += ["-solvent-content", f"{solvent_content:.3f}"]
|
|
54
65
|
self._args += ["-cycles", "5"]
|
|
55
66
|
self._args += ["-anisotropy-correction"]
|
|
56
67
|
self._args += ["-mtzout", "hklout.mtz"]
|
modelcraft/jobs/phasematch.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dataclasses
|
|
2
|
+
|
|
2
3
|
from ..job import Job
|
|
3
4
|
from ..reflections import DataItem, write_mtz
|
|
4
5
|
|
|
@@ -35,7 +36,7 @@ class PhaseMatch(Job):
|
|
|
35
36
|
def _result(self) -> PhaseMatchResult:
|
|
36
37
|
self._check_files_exist("hklout.mtz")
|
|
37
38
|
path = self._path("stdout.txt")
|
|
38
|
-
with open(path) as stream:
|
|
39
|
+
with open(path, encoding="utf-8") as stream:
|
|
39
40
|
for line in stream:
|
|
40
41
|
if line[:19] == "Overall statistics:":
|
|
41
42
|
keys = next(stream).split()
|
modelcraft/jobs/refmac.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import shutil
|
|
3
3
|
import xml.etree.ElementTree as ET
|
|
4
|
+
|
|
4
5
|
import gemmi
|
|
6
|
+
|
|
5
7
|
from ..job import Job
|
|
6
8
|
from ..reflections import DataItem, write_mtz
|
|
7
9
|
from ..structure import read_structure, write_mmcif
|
|
@@ -98,7 +100,7 @@ class Refmac(Job):
|
|
|
98
100
|
abcd=DataItem(mtz, "HLACOMB,HLBCOMB,HLCCOMB,HLDCOMB"),
|
|
99
101
|
fphi_best=DataItem(mtz, "FWT,PHWT"),
|
|
100
102
|
fphi_diff=DataItem(mtz, "DELFWT,PHDELWT"),
|
|
101
|
-
fphi_calc=DataItem(mtz, "
|
|
103
|
+
fphi_calc=DataItem(mtz, "FC_ALL,PHIC_ALL"),
|
|
102
104
|
rwork=float(rworks[-1].text),
|
|
103
105
|
rfree=float(rfrees[-1].text),
|
|
104
106
|
fsc=float(fscs[-1].text),
|
modelcraft/jobs/servalcat.py
CHANGED
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import shutil
|
|
3
|
+
|
|
3
4
|
import gemmi
|
|
5
|
+
|
|
4
6
|
from ..job import Job
|
|
5
7
|
from ..maps import read_map
|
|
6
|
-
from ..reflections import DataItem
|
|
8
|
+
from ..reflections import DataItem, write_mtz
|
|
7
9
|
from ..structure import read_structure, write_mmcif
|
|
8
10
|
|
|
9
11
|
|
|
12
|
+
@dataclasses.dataclass
|
|
13
|
+
class ServalcatFwResult:
|
|
14
|
+
fmean: DataItem
|
|
15
|
+
fanom: DataItem
|
|
16
|
+
imean: DataItem
|
|
17
|
+
seconds: float
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ServalcatFw(Job):
|
|
21
|
+
def __init__(self, observations: DataItem):
|
|
22
|
+
super().__init__("servalcat")
|
|
23
|
+
self.observations = observations
|
|
24
|
+
|
|
25
|
+
def _setup(self) -> None:
|
|
26
|
+
write_mtz(self._path("hklin.mtz"), [self.observations])
|
|
27
|
+
self._args += ["fw", "--hklin", "hklin.mtz", "-o", "output"]
|
|
28
|
+
|
|
29
|
+
def _result(self) -> ServalcatFwResult:
|
|
30
|
+
self._check_files_exist("output.mtz")
|
|
31
|
+
mtz = gemmi.read_mtz_file(self._path("output.mtz"))
|
|
32
|
+
result = ServalcatFwResult(
|
|
33
|
+
fmean=DataItem(mtz, "F,SIGF"),
|
|
34
|
+
fanom=None,
|
|
35
|
+
imean=None,
|
|
36
|
+
seconds=self._seconds,
|
|
37
|
+
)
|
|
38
|
+
if self.observations.types == "KMKM":
|
|
39
|
+
result.fanom = DataItem(mtz, "F(+),SIGF(+),F(-),SIGF(-)")
|
|
40
|
+
result.imean = DataItem(mtz, "I,SIGI")
|
|
41
|
+
return result
|
|
42
|
+
|
|
43
|
+
|
|
10
44
|
@dataclasses.dataclass
|
|
11
45
|
class ServalcatNemapResult:
|
|
12
46
|
fphi: DataItem
|
|
@@ -38,8 +72,8 @@ class ServalcatNemap(Job):
|
|
|
38
72
|
self._args += ["--mask", "mask.ccp4"]
|
|
39
73
|
|
|
40
74
|
def _result(self) -> ServalcatNemapResult:
|
|
41
|
-
self._check_files_exist("
|
|
42
|
-
mtz = gemmi.read_mtz_file(self._path("
|
|
75
|
+
self._check_files_exist("nemap_maps.mtz")
|
|
76
|
+
mtz = gemmi.read_mtz_file(self._path("nemap_maps.mtz"))
|
|
43
77
|
return ServalcatNemapResult(
|
|
44
78
|
fphi=DataItem(mtz, "FWT,PHWT"),
|
|
45
79
|
seconds=self._seconds,
|
|
@@ -190,7 +224,7 @@ class ServalcatFsc(Job):
|
|
|
190
224
|
def _result(self) -> ServalcatFscResult:
|
|
191
225
|
self._check_files_exist("fsc.dat")
|
|
192
226
|
fsc = None
|
|
193
|
-
with open(self._path("fsc.dat")) as text:
|
|
227
|
+
with open(self._path("fsc.dat"), encoding="utf-8") as text:
|
|
194
228
|
for line in text:
|
|
195
229
|
if line.startswith("# FSCaverage of fsc_FC_full ="):
|
|
196
230
|
fsc = float(line.strip().split()[-1])
|
modelcraft/jobs/sheetbend.py
CHANGED
modelcraft/modelcraftem.py
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import shutil
|
|
2
3
|
import time
|
|
4
|
+
|
|
3
5
|
import gemmi
|
|
6
|
+
|
|
4
7
|
from . import __version__
|
|
5
8
|
from .jobs.buccaneer import Buccaneer
|
|
6
9
|
from .jobs.emda import EmdaMapMask
|
|
7
10
|
from .jobs.nautilus import Nautilus
|
|
11
|
+
from .jobs.nucleofind import NucleoFindBuild, NucleoFindPredict
|
|
8
12
|
from .jobs.refmac import RefmacMapToMtz
|
|
9
13
|
from .jobs.servalcat import ServalcatFsc, ServalcatNemap, ServalcatRefine, ServalcatTrim
|
|
10
14
|
from .maps import read_map
|
|
@@ -28,24 +32,36 @@ class ModelCraftEm(Pipeline):
|
|
|
28
32
|
self.maps = {}
|
|
29
33
|
self.fmean = None
|
|
30
34
|
self.phases = None
|
|
35
|
+
self.fphi = None
|
|
36
|
+
self.nucleofind_prediction = None
|
|
31
37
|
|
|
32
38
|
def run(self):
|
|
33
39
|
print(f"# ModelCraft {__version__}", flush=True)
|
|
34
40
|
os.makedirs(self.args.directory, exist_ok=self.args.overwrite_directory)
|
|
35
41
|
self.start_time = time.time()
|
|
36
42
|
self._read_input_maps()
|
|
43
|
+
original_map_centre = self.map_centre()
|
|
37
44
|
self._trim_input_maps()
|
|
38
45
|
self._calculate_fmean_and_phases()
|
|
39
46
|
structure = self.args.model
|
|
40
47
|
best_fsc = None
|
|
41
48
|
cycles_without_improvement = 0
|
|
49
|
+
build_nucleic = self.args.contents.rnas or self.args.contents.dnas
|
|
50
|
+
if build_nucleic and shutil.which("nucleofind"):
|
|
51
|
+
try:
|
|
52
|
+
self.nucleofind_prediction = NucleoFindPredict(self.fphi).run(self)
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
print("Warning: nucleofind prediction failed", flush=True)
|
|
42
55
|
for cycle in range(1, self.args.cycles + 1):
|
|
43
56
|
print(f"\n## Cycle {cycle}\n", flush=True)
|
|
44
57
|
if self.args.contents.proteins:
|
|
45
58
|
structure = self.buccaneer(structure)
|
|
46
59
|
structure = self.servalcat_refine(structure)
|
|
47
|
-
if
|
|
48
|
-
|
|
60
|
+
if build_nucleic:
|
|
61
|
+
if self.nucleofind_prediction is None:
|
|
62
|
+
structure = self.nautilus(structure)
|
|
63
|
+
else:
|
|
64
|
+
structure = self.nucleofind(structure)
|
|
49
65
|
structure = self.servalcat_refine(structure)
|
|
50
66
|
model_stats = ModelStats(structure)
|
|
51
67
|
fsc = self.servalcat_fsc(structure)
|
|
@@ -54,6 +70,7 @@ class ModelCraftEm(Pipeline):
|
|
|
54
70
|
if best_fsc is None or fsc > best_fsc:
|
|
55
71
|
best_fsc = fsc
|
|
56
72
|
cycles_without_improvement = 0
|
|
73
|
+
self.shift(structure, original_map_centre)
|
|
57
74
|
write_mmcif(self.path("modelcraft.cif"), structure)
|
|
58
75
|
self.report["final"] = stats
|
|
59
76
|
else:
|
|
@@ -88,21 +105,21 @@ class ModelCraftEm(Pipeline):
|
|
|
88
105
|
density=self.maps["build_map"],
|
|
89
106
|
resolution=self.args.resolution,
|
|
90
107
|
).run(self)
|
|
91
|
-
fphi = refmac.fphi
|
|
108
|
+
self.fphi = refmac.fphi
|
|
92
109
|
elif self.args.half_maps:
|
|
93
110
|
nemap = ServalcatNemap(
|
|
94
111
|
halfmap1=self.maps["half_map1"],
|
|
95
112
|
halfmap2=self.maps["half_map2"],
|
|
96
113
|
resolution=self.args.resolution,
|
|
97
114
|
).run(self)
|
|
98
|
-
fphi = nemap.fphi
|
|
115
|
+
self.fphi = nemap.fphi
|
|
99
116
|
else:
|
|
100
117
|
refmac = RefmacMapToMtz(
|
|
101
118
|
density=self.maps["single_map"],
|
|
102
119
|
resolution=self.args.resolution,
|
|
103
120
|
).run(self)
|
|
104
|
-
fphi = refmac.fphi
|
|
105
|
-
self.fmean, self.phases = convert_to_fsigf_and_phifom(fphi)
|
|
121
|
+
self.fphi = refmac.fphi
|
|
122
|
+
self.fmean, self.phases = convert_to_fsigf_and_phifom(self.fphi)
|
|
106
123
|
|
|
107
124
|
def buccaneer(self, structure: gemmi.Structure) -> gemmi.Structure:
|
|
108
125
|
result = Buccaneer(
|
|
@@ -126,6 +143,16 @@ class ModelCraftEm(Pipeline):
|
|
|
126
143
|
).run(self)
|
|
127
144
|
return result.structure
|
|
128
145
|
|
|
146
|
+
def nucleofind(self, structure: gemmi.Structure) -> gemmi.Structure:
|
|
147
|
+
result = NucleoFindBuild(
|
|
148
|
+
contents=self.args.contents,
|
|
149
|
+
fphi=self.fphi,
|
|
150
|
+
prediction=self.nucleofind_prediction,
|
|
151
|
+
structure=structure,
|
|
152
|
+
em=True,
|
|
153
|
+
).run(self)
|
|
154
|
+
return result.structure
|
|
155
|
+
|
|
129
156
|
def servalcat_refine(self, structure: gemmi.Structure) -> gemmi.Structure:
|
|
130
157
|
if ModelStats(structure).residues == 0:
|
|
131
158
|
self.terminate(reason="No residues to refine")
|
|
@@ -148,3 +175,19 @@ class ModelCraftEm(Pipeline):
|
|
|
148
175
|
density=self.maps.get("single_map"),
|
|
149
176
|
).run(self)
|
|
150
177
|
return result.fsc
|
|
178
|
+
|
|
179
|
+
def map_centre(self):
|
|
180
|
+
first_map = next(iter(self.maps.values()))
|
|
181
|
+
fractional = gemmi.Fractional(0.5, 0.5, 0.5)
|
|
182
|
+
return first_map.grid.unit_cell.orthogonalize(fractional)
|
|
183
|
+
|
|
184
|
+
def shift(self, structure: gemmi.Structure, original_map_centre: gemmi.Position):
|
|
185
|
+
model_centre = structure[0].calculate_center_of_mass()
|
|
186
|
+
diff = original_map_centre - model_centre
|
|
187
|
+
shift = gemmi.Vec3(
|
|
188
|
+
round(diff[0] / structure.cell.a) * structure.cell.a,
|
|
189
|
+
round(diff[1] / structure.cell.b) * structure.cell.b,
|
|
190
|
+
round(diff[2] / structure.cell.c) * structure.cell.c,
|
|
191
|
+
)
|
|
192
|
+
transform = gemmi.Transform(gemmi.Mat33(), shift)
|
|
193
|
+
structure[0].transform_pos_and_adp(transform)
|