servalcat 0.4.60__cp312-cp312-macosx_11_0_arm64.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.
Potentially problematic release.
This version of servalcat might be problematic. Click here for more details.
- servalcat/__init__.py +10 -0
- servalcat/__main__.py +120 -0
- servalcat/ext.cpython-312-darwin.so +0 -0
- servalcat/refine/__init__.py +0 -0
- servalcat/refine/cgsolve.py +100 -0
- servalcat/refine/refine.py +733 -0
- servalcat/refine/refine_geom.py +207 -0
- servalcat/refine/refine_spa.py +327 -0
- servalcat/refine/refine_xtal.py +242 -0
- servalcat/refine/spa.py +132 -0
- servalcat/refine/xtal.py +227 -0
- servalcat/refmac/__init__.py +0 -0
- servalcat/refmac/exte.py +182 -0
- servalcat/refmac/refmac_keywords.py +536 -0
- servalcat/refmac/refmac_wrapper.py +360 -0
- servalcat/spa/__init__.py +0 -0
- servalcat/spa/fofc.py +462 -0
- servalcat/spa/fsc.py +385 -0
- servalcat/spa/localcc.py +188 -0
- servalcat/spa/realspcc_from_var.py +128 -0
- servalcat/spa/run_refmac.py +961 -0
- servalcat/spa/shift_maps.py +293 -0
- servalcat/spa/shiftback.py +137 -0
- servalcat/spa/translate.py +129 -0
- servalcat/utils/__init__.py +35 -0
- servalcat/utils/commands.py +1277 -0
- servalcat/utils/fileio.py +745 -0
- servalcat/utils/generate_operators.py +296 -0
- servalcat/utils/hkl.py +699 -0
- servalcat/utils/logger.py +116 -0
- servalcat/utils/maps.py +340 -0
- servalcat/utils/model.py +774 -0
- servalcat/utils/refmac.py +747 -0
- servalcat/utils/restraints.py +605 -0
- servalcat/utils/symmetry.py +295 -0
- servalcat/xtal/__init__.py +0 -0
- servalcat/xtal/french_wilson.py +250 -0
- servalcat/xtal/run_refmac_small.py +240 -0
- servalcat/xtal/sigmaa.py +1403 -0
- servalcat-0.4.60.dist-info/METADATA +56 -0
- servalcat-0.4.60.dist-info/RECORD +44 -0
- servalcat-0.4.60.dist-info/WHEEL +5 -0
- servalcat-0.4.60.dist-info/entry_points.txt +4 -0
- servalcat-0.4.60.dist-info/licenses/LICENSE +373 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: "Keitaro Yamashita, Garib N. Murshudov"
|
|
3
|
+
MRC Laboratory of Molecular Biology
|
|
4
|
+
|
|
5
|
+
This software is released under the
|
|
6
|
+
Mozilla Public License, version 2.0; see LICENSE.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import absolute_import, division, print_function, generators
|
|
9
|
+
import argparse
|
|
10
|
+
import os
|
|
11
|
+
import gemmi
|
|
12
|
+
import numpy
|
|
13
|
+
import json
|
|
14
|
+
import servalcat # for version
|
|
15
|
+
from servalcat.utils import logger
|
|
16
|
+
from servalcat import utils
|
|
17
|
+
from servalcat.refine.refine import Geom, Refine
|
|
18
|
+
|
|
19
|
+
def add_arguments(parser):
|
|
20
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
21
|
+
group.add_argument('--model',
|
|
22
|
+
help='Input atomic model file')
|
|
23
|
+
group.add_argument('--update_dictionary',
|
|
24
|
+
help="Dictionary file to be updated")
|
|
25
|
+
parser.add_argument("--monlib",
|
|
26
|
+
help="Monomer library path. Default: $CLIBD_MON")
|
|
27
|
+
parser.add_argument('--ligand', nargs="*", action="append",
|
|
28
|
+
help="restraint dictionary cif file(s)")
|
|
29
|
+
parser.add_argument('--ncycle', type=int, default=10,
|
|
30
|
+
help="number of CG cycles (default: %(default)d)")
|
|
31
|
+
parser.add_argument('--hydrogen', default="all", choices=["all", "yes", "no"],
|
|
32
|
+
help="all: add riding hydrogen atoms, yes: use hydrogen atoms if present, no: remove hydrogen atoms in input. "
|
|
33
|
+
"Default: %(default)s")
|
|
34
|
+
parser.add_argument('--find_links', action='store_true',
|
|
35
|
+
help='Automatically add links')
|
|
36
|
+
parser.add_argument('--randomize', type=float, default=0,
|
|
37
|
+
help='Shake coordinates with specified rmsd')
|
|
38
|
+
parser.add_argument('--ncsr', action='store_true',
|
|
39
|
+
help='Use local NCS restraints')
|
|
40
|
+
parser.add_argument('--keywords', nargs='+', action="append",
|
|
41
|
+
help="refmac keyword(s)")
|
|
42
|
+
parser.add_argument('--keyword_file', nargs='+', action="append",
|
|
43
|
+
help="refmac keyword file(s)")
|
|
44
|
+
parser.add_argument('-o','--output_prefix',
|
|
45
|
+
help="Output prefix")
|
|
46
|
+
|
|
47
|
+
# add_arguments()
|
|
48
|
+
|
|
49
|
+
def parse_args(arg_list):
|
|
50
|
+
parser = argparse.ArgumentParser()
|
|
51
|
+
add_arguments(parser)
|
|
52
|
+
return parser.parse_args(arg_list)
|
|
53
|
+
# parse_args()
|
|
54
|
+
|
|
55
|
+
def add_program_info_to_dictionary(block, comp_id, program_name="servalcat", descriptor="optimization tool"):
|
|
56
|
+
tab = block.find("_pdbx_chem_comp_description_generator.", ["program_name", "program_version", "descriptor"])
|
|
57
|
+
# just overwrite version if it's there
|
|
58
|
+
for row in tab:
|
|
59
|
+
if row.str(0) == program_name and row.str(2) == descriptor:
|
|
60
|
+
row[1] = gemmi.cif.quote(servalcat.__version__)
|
|
61
|
+
return
|
|
62
|
+
loop = tab.loop
|
|
63
|
+
if not loop:
|
|
64
|
+
loop = block.init_loop("_pdbx_chem_comp_description_generator.", ["comp_id",
|
|
65
|
+
"program_name",
|
|
66
|
+
"program_version",
|
|
67
|
+
"descriptor"])
|
|
68
|
+
tags = [x[x.index(".")+1:] for x in loop.tags]
|
|
69
|
+
row = ["" for _ in range(len(tags))]
|
|
70
|
+
for tag, val in (("comp_id", comp_id),
|
|
71
|
+
("program_name", program_name),
|
|
72
|
+
("program_version", servalcat.__version__),
|
|
73
|
+
("descriptor", descriptor)):
|
|
74
|
+
if tag in tags: row[tags.index(tag)] = val
|
|
75
|
+
loop.add_row(gemmi.cif.quote_list(row))
|
|
76
|
+
# add_program_info_to_dictionary()
|
|
77
|
+
|
|
78
|
+
def refine_and_update_dictionary(cif_in, monomer_dir, output_prefix, randomize=0, ncycle1=10, ncycle2=30):
|
|
79
|
+
doc = gemmi.cif.read(cif_in)
|
|
80
|
+
for block in doc: # this block will be reused below
|
|
81
|
+
st = gemmi.make_structure_from_chemcomp_block(block)
|
|
82
|
+
if len(st) > 0: break
|
|
83
|
+
else:
|
|
84
|
+
raise SystemExit("No model in the cif file")
|
|
85
|
+
monlib = utils.restraints.load_monomer_library(st, monomer_dir=monomer_dir, # monlib is needed for ener_lib
|
|
86
|
+
cif_files=[cif_in],
|
|
87
|
+
stop_for_unknowns=True)
|
|
88
|
+
all_stats = []
|
|
89
|
+
for i_macro in 0, 1:
|
|
90
|
+
try:
|
|
91
|
+
topo, _ = utils.restraints.prepare_topology(st, monlib, h_change=[gemmi.HydrogenChange.Remove, gemmi.HydrogenChange.ReAdd][i_macro],
|
|
92
|
+
check_hydrogen=(i_macro == 1))
|
|
93
|
+
except RuntimeError as e:
|
|
94
|
+
raise SystemExit("Error: {}".format(e))
|
|
95
|
+
|
|
96
|
+
geom = Geom(st, topo, monlib, shake_rms=randomize)
|
|
97
|
+
refiner = Refine(st, geom)
|
|
98
|
+
logger.writeln("Running {} cycles with wchir=4 wvdw=2 {} hydrogen".format(ncycle1, ["without","with"][i_macro]))
|
|
99
|
+
geom.calc_kwds["wchir"] = 4
|
|
100
|
+
geom.calc_kwds["wvdw"] = 2
|
|
101
|
+
all_stats.append(refiner.run_cycles(ncycle1))
|
|
102
|
+
|
|
103
|
+
logger.writeln("Running {} cycles with wchir=1 wvdw=2 {} hydrogen".format(ncycle2, ["without","with"][i_macro]))
|
|
104
|
+
geom.calc_kwds["wchir"] = 1
|
|
105
|
+
geom.calc_kwds["wvdw"] = 2
|
|
106
|
+
all_stats.append(refiner.run_cycles(ncycle2))
|
|
107
|
+
|
|
108
|
+
# replace xyz
|
|
109
|
+
pos = {cra.atom.name: cra.atom.pos.tolist() for cra in refiner.st[0].all()}
|
|
110
|
+
for row in block.find("_chem_comp_atom.", ["atom_id", "?x", "?y", "?z",
|
|
111
|
+
"?pdbx_model_Cartn_x_ideal",
|
|
112
|
+
"?pdbx_model_Cartn_y_ideal",
|
|
113
|
+
"?pdbx_model_Cartn_z_ideal"]):
|
|
114
|
+
p = pos[row.str(0)]
|
|
115
|
+
for i in range(3):
|
|
116
|
+
if row.has(i+1):
|
|
117
|
+
row[i+1] = "{:.3f}".format(p[i])
|
|
118
|
+
if row.has(i+4):
|
|
119
|
+
row[i+4] = "{:.3f}".format(p[i])
|
|
120
|
+
# add description
|
|
121
|
+
add_program_info_to_dictionary(block, st[0][0][0].name)
|
|
122
|
+
doc.write_file(output_prefix + "_updated.cif", style=gemmi.cif.Style.Aligned)
|
|
123
|
+
logger.writeln("Updated dictionary saved: {}".format(output_prefix + "_updated.cif"))
|
|
124
|
+
with open(output_prefix + "_stats.json", "w") as ofs:
|
|
125
|
+
for stats in all_stats:
|
|
126
|
+
for s in stats:
|
|
127
|
+
s["geom"] = s["geom"].to_dict()
|
|
128
|
+
json.dump(all_stats, ofs, indent=2)
|
|
129
|
+
logger.writeln("Refinement statistics saved: {}".format(ofs.name))
|
|
130
|
+
# refine_and_update_dictionary()
|
|
131
|
+
|
|
132
|
+
def refine_geom(model_in, monomer_dir, cif_files, h_change, ncycle, output_prefix, randomize, refmac_keywords,
|
|
133
|
+
find_links=False, use_ncsr=False):
|
|
134
|
+
st = utils.fileio.read_structure(model_in)
|
|
135
|
+
utils.model.setup_entities(st, clear=True, force_subchain_names=True, overwrite_entity_type=True)
|
|
136
|
+
if st.ncs:
|
|
137
|
+
st2 = st.clone()
|
|
138
|
+
logger.writeln("Take NCS constraints into account.")
|
|
139
|
+
st2.expand_ncs(gemmi.HowToNameCopiedChain.Dup, merge_dist=0)
|
|
140
|
+
utils.fileio.write_model(st2, file_name="input_expanded.pdb")
|
|
141
|
+
|
|
142
|
+
monlib = utils.restraints.load_monomer_library(st, monomer_dir=monomer_dir,
|
|
143
|
+
cif_files=cif_files,
|
|
144
|
+
stop_for_unknowns=True)
|
|
145
|
+
utils.restraints.find_and_fix_links(st, monlib, add_found=find_links) # should remove unknown id here?
|
|
146
|
+
try:
|
|
147
|
+
topo, metal_kws = utils.restraints.prepare_topology(st, monlib, h_change=h_change,
|
|
148
|
+
check_hydrogen=(h_change==gemmi.HydrogenChange.NoChange))
|
|
149
|
+
except RuntimeError as e:
|
|
150
|
+
raise SystemExit("Error: {}".format(e))
|
|
151
|
+
refmac_keywords = metal_kws + refmac_keywords
|
|
152
|
+
if use_ncsr:
|
|
153
|
+
ncslist = utils.restraints.prepare_ncs_restraints(st)
|
|
154
|
+
else:
|
|
155
|
+
ncslist = False
|
|
156
|
+
geom = Geom(st, topo, monlib, shake_rms=randomize, refmac_keywords=refmac_keywords, ncslist=ncslist)
|
|
157
|
+
refiner = Refine(st, geom)
|
|
158
|
+
stats = refiner.run_cycles(ncycle)
|
|
159
|
+
refiner.st.name = output_prefix
|
|
160
|
+
utils.fileio.write_model(refiner.st, output_prefix, pdb=True, cif=True)
|
|
161
|
+
with open(output_prefix + "_stats.json", "w") as ofs:
|
|
162
|
+
for s in stats: s["geom"] = s["geom"].to_dict()
|
|
163
|
+
json.dump(stats, ofs, indent=2)
|
|
164
|
+
logger.writeln("Refinement statistics saved: {}".format(ofs.name))
|
|
165
|
+
# refine_geom()
|
|
166
|
+
|
|
167
|
+
def main(args):
|
|
168
|
+
keywords = []
|
|
169
|
+
if args.keywords: keywords = sum(args.keywords, [])
|
|
170
|
+
if args.keyword_file: keywords.extend(l for f in sum(args.keyword_file, []) for l in open(f))
|
|
171
|
+
decide_prefix = lambda f: utils.fileio.splitext(os.path.basename(f))[0] + "_refined"
|
|
172
|
+
if args.model:
|
|
173
|
+
if not args.output_prefix:
|
|
174
|
+
args.output_prefix = decide_prefix(args.model)
|
|
175
|
+
if args.ligand:
|
|
176
|
+
args.ligand = sum(args.ligand, [])
|
|
177
|
+
h_change = {"all":gemmi.HydrogenChange.ReAddButWater,
|
|
178
|
+
"full":gemmi.HydrogenChange.ReAdd,
|
|
179
|
+
"yes":gemmi.HydrogenChange.NoChange,
|
|
180
|
+
"no":gemmi.HydrogenChange.Remove}[args.hydrogen]
|
|
181
|
+
refine_geom(model_in=args.model,
|
|
182
|
+
monomer_dir=args.monlib,
|
|
183
|
+
cif_files=args.ligand,
|
|
184
|
+
h_change=h_change,
|
|
185
|
+
ncycle=args.ncycle,
|
|
186
|
+
output_prefix=args.output_prefix,
|
|
187
|
+
randomize=args.randomize,
|
|
188
|
+
refmac_keywords=keywords,
|
|
189
|
+
find_links=args.find_links,
|
|
190
|
+
use_ncsr=args.ncsr)
|
|
191
|
+
else:
|
|
192
|
+
if not args.output_prefix:
|
|
193
|
+
args.output_prefix = decide_prefix(args.update_dictionary)
|
|
194
|
+
if args.ligand:
|
|
195
|
+
logger.writeln("WARNING: monlib and ligand are ignored in the dictionary updating mode")
|
|
196
|
+
if keywords:
|
|
197
|
+
logger.writeln("WARNING: refmac keywords are ignored in the dictionary updating mode")
|
|
198
|
+
refine_and_update_dictionary(cif_in=args.update_dictionary,
|
|
199
|
+
monomer_dir=args.monlib,
|
|
200
|
+
output_prefix=args.output_prefix,
|
|
201
|
+
randomize=args.randomize)
|
|
202
|
+
# main()
|
|
203
|
+
|
|
204
|
+
if __name__ == "__main__":
|
|
205
|
+
import sys
|
|
206
|
+
args = parse_args(sys.argv[1:])
|
|
207
|
+
main(args)
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Author: "Keitaro Yamashita, Garib N. Murshudov"
|
|
3
|
+
MRC Laboratory of Molecular Biology
|
|
4
|
+
|
|
5
|
+
This software is released under the
|
|
6
|
+
Mozilla Public License, version 2.0; see LICENSE.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import absolute_import, division, print_function, generators
|
|
9
|
+
import gemmi
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import numpy
|
|
13
|
+
from servalcat.utils import logger
|
|
14
|
+
from servalcat import utils
|
|
15
|
+
from servalcat.spa.run_refmac import check_args, process_input, calc_fsc, calc_fofc
|
|
16
|
+
from servalcat.spa import fofc
|
|
17
|
+
from servalcat.refine import spa
|
|
18
|
+
from servalcat.refine.refine import Geom, Refine
|
|
19
|
+
b_to_u = utils.model.b_to_u
|
|
20
|
+
|
|
21
|
+
def add_arguments(parser):
|
|
22
|
+
parser.description = "EXPERIMENTAL program to refine cryo-EM SPA structures"
|
|
23
|
+
group = parser.add_mutually_exclusive_group(required=True)
|
|
24
|
+
group.add_argument("--halfmaps", nargs=2, help="Input half map files")
|
|
25
|
+
group.add_argument("--map", help="Use this only if you really do not have half maps.")
|
|
26
|
+
group.add_argument("--hklin", help="Use mtz file. With limited functionality.")
|
|
27
|
+
parser.add_argument('--pixel_size', type=float,
|
|
28
|
+
help='Override pixel size (A)')
|
|
29
|
+
parser.add_argument('--labin',
|
|
30
|
+
help='F,PHI for hklin')
|
|
31
|
+
parser.add_argument('--model', required=True,
|
|
32
|
+
help='Input atomic model file')
|
|
33
|
+
parser.add_argument("-d", '--resolution', type=float, required=True)
|
|
34
|
+
parser.add_argument('-r', '--mask_radius', type=float, default=3, help="mask radius")
|
|
35
|
+
parser.add_argument('--padding',
|
|
36
|
+
type=float,
|
|
37
|
+
help='Default: 2*mask_radius')
|
|
38
|
+
parser.add_argument('--no_mask', action='store_true')
|
|
39
|
+
parser.add_argument('--no_trim',
|
|
40
|
+
action='store_true',
|
|
41
|
+
help='Keep original box (not recommended)')
|
|
42
|
+
parser.add_argument('--mask_soft_edge',
|
|
43
|
+
type=float, default=0,
|
|
44
|
+
help='Add soft edge to model mask. Should use with --no_sharpen_before_mask?')
|
|
45
|
+
parser.add_argument('--no_sharpen_before_mask', action='store_true',
|
|
46
|
+
help='By default half maps are sharpened before masking by std of signal and unsharpened after masking. This option disables it.')
|
|
47
|
+
parser.add_argument("--b_before_mask", type=float,
|
|
48
|
+
help="sharpening B value for sharpen-mask-unsharpen procedure. By default it is determined automatically.")
|
|
49
|
+
parser.add_argument('--blur',
|
|
50
|
+
type=float, default=0,
|
|
51
|
+
help='Sharpening or blurring B')
|
|
52
|
+
parser.add_argument("--monlib",
|
|
53
|
+
help="Monomer library path. Default: $CLIBD_MON")
|
|
54
|
+
parser.add_argument('--ligand', nargs="*", action="append",
|
|
55
|
+
help="restraint dictionary cif file(s)")
|
|
56
|
+
parser.add_argument('--hydrogen', default="all", choices=["all", "yes", "no"],
|
|
57
|
+
help="all: add riding hydrogen atoms, yes: use hydrogen atoms if present, no: remove hydrogen atoms in input. "
|
|
58
|
+
"Default: %(default)s")
|
|
59
|
+
parser.add_argument('--jellybody', action='store_true',
|
|
60
|
+
help="Use jelly body restraints")
|
|
61
|
+
parser.add_argument('--jellybody_params', nargs=2, type=float,
|
|
62
|
+
metavar=("sigma", "dmax"), default=[0.01, 4.2],
|
|
63
|
+
help="Jelly body sigma and dmax (default: %(default)s)")
|
|
64
|
+
parser.add_argument('--jellyonly', action='store_true',
|
|
65
|
+
help="Jelly body only (experimental, may not be useful)")
|
|
66
|
+
utils.symmetry.add_symmetry_args(parser) # add --pg etc
|
|
67
|
+
parser.add_argument('--contacting_only', action="store_true", help="Filter out non-contacting NCS")
|
|
68
|
+
parser.add_argument('--ignore_symmetry',
|
|
69
|
+
help='Ignore symmetry information (MTRIX/_struct_ncs_oper) in the model file')
|
|
70
|
+
parser.add_argument('--find_links', action='store_true',
|
|
71
|
+
help='Automatically add links')
|
|
72
|
+
parser.add_argument('--no_check_ncs_overlaps', action='store_true',
|
|
73
|
+
help='Disable model overlap (e.g. expanded model is used with --pg) test')
|
|
74
|
+
parser.add_argument('--no_check_ncs_map', action='store_true',
|
|
75
|
+
help='Disable map NCS consistency test')
|
|
76
|
+
parser.add_argument('--no_check_mask_with_model', action='store_true',
|
|
77
|
+
help='Disable mask test using model')
|
|
78
|
+
parser.add_argument('--keywords', nargs='+', action="append",
|
|
79
|
+
help="refmac keyword(s)")
|
|
80
|
+
parser.add_argument('--keyword_file', nargs='+', action="append",
|
|
81
|
+
help="refmac keyword file(s)")
|
|
82
|
+
parser.add_argument('--randomize', type=float, default=0,
|
|
83
|
+
help='Shake coordinates with specified rmsd')
|
|
84
|
+
parser.add_argument('--ncycle', type=int, default=10,
|
|
85
|
+
help="number of CG cycles (default: %(default)d)")
|
|
86
|
+
parser.add_argument('--weight', type=float,
|
|
87
|
+
help="refinement weight. default: automatic")
|
|
88
|
+
parser.add_argument('--adpr_weight', type=float, default=1.,
|
|
89
|
+
help="ADP restraint weight in B (default: %(default)f)")
|
|
90
|
+
parser.add_argument('--ncsr', action='store_true',
|
|
91
|
+
help='Use local NCS restraints')
|
|
92
|
+
parser.add_argument('--bfactor', type=float,
|
|
93
|
+
help="reset all atomic B values to specified value")
|
|
94
|
+
parser.add_argument('--fix_xyz', action="store_true")
|
|
95
|
+
parser.add_argument('--adp', choices=["fix", "iso", "aniso"], default="iso")
|
|
96
|
+
parser.add_argument('--max_dist_for_adp_restraint', type=float, default=4.)
|
|
97
|
+
parser.add_argument('--adp_restraint_power', type=float)
|
|
98
|
+
parser.add_argument('--adp_restraint_exp_fac', type=float)
|
|
99
|
+
parser.add_argument('--adp_restraint_no_long_range', action='store_true')
|
|
100
|
+
parser.add_argument('--adp_restraint_mode', choices=["diff", "kldiv"], default="diff")
|
|
101
|
+
parser.add_argument('--refine_h', action="store_true", help="Refine hydrogen (default: restraints only)")
|
|
102
|
+
parser.add_argument("--source", choices=["electron", "xray", "neutron"], default="electron")
|
|
103
|
+
parser.add_argument('-o','--output_prefix', default="refined")
|
|
104
|
+
parser.add_argument('--cross_validation', action='store_true',
|
|
105
|
+
help='Run cross validation. Only "throughout" mode is available (no "shake" mode)')
|
|
106
|
+
group = parser.add_mutually_exclusive_group()
|
|
107
|
+
group.add_argument('--mask_for_fofc', help="Mask file for Fo-Fc map calculation")
|
|
108
|
+
group.add_argument('--mask_radius_for_fofc', type=float, help="Mask radius for Fo-Fc map calculation")
|
|
109
|
+
parser.add_argument("--fsc_resolution", type=float,
|
|
110
|
+
help="High resolution limit for FSC calculation. Default: Nyquist")
|
|
111
|
+
parser.add_argument('--keep_charges', action='store_true',
|
|
112
|
+
help="Use scattering factor for charged atoms. Use it with care.")
|
|
113
|
+
parser.add_argument("--keep_entities", action='store_true',
|
|
114
|
+
help="Do not override entities")
|
|
115
|
+
# add_arguments()
|
|
116
|
+
|
|
117
|
+
def parse_args(arg_list):
|
|
118
|
+
parser = argparse.ArgumentParser()
|
|
119
|
+
add_arguments(parser)
|
|
120
|
+
return parser.parse_args(arg_list)
|
|
121
|
+
# parse_args()
|
|
122
|
+
|
|
123
|
+
def main(args):
|
|
124
|
+
args.mask = None
|
|
125
|
+
args.invert_mask = False
|
|
126
|
+
args.trim_fofc_mtz = args.mask_for_fofc is not None
|
|
127
|
+
args.cross_validation_method = "throughout"
|
|
128
|
+
check_args(args)
|
|
129
|
+
refmac_keywords = args.keywords + [l for f in args.keyword_file for l in open(f)]
|
|
130
|
+
|
|
131
|
+
st = utils.fileio.read_structure(args.model)
|
|
132
|
+
try:
|
|
133
|
+
monlib = utils.restraints.load_monomer_library(st, monomer_dir=args.monlib, cif_files=args.ligand,
|
|
134
|
+
stop_for_unknowns=True)
|
|
135
|
+
except RuntimeError as e:
|
|
136
|
+
raise SystemExit("Error: {}".format(e))
|
|
137
|
+
if not args.keep_entities:
|
|
138
|
+
utils.model.setup_entities(st, clear=True, force_subchain_names=True, overwrite_entity_type=True)
|
|
139
|
+
if not args.keep_charges:
|
|
140
|
+
utils.model.remove_charge([st])
|
|
141
|
+
utils.model.check_atomsf([st], args.source)
|
|
142
|
+
if args.hklin:
|
|
143
|
+
assert not args.cross_validation
|
|
144
|
+
mtz = utils.fileio.read_mmhkl(args.hklin)
|
|
145
|
+
hkldata = utils.hkl.hkldata_from_mtz(mtz, args.labin.split(","),
|
|
146
|
+
newlabels=["FP", ""],
|
|
147
|
+
require_types=["F", "P"])
|
|
148
|
+
hkldata.df = hkldata.df.dropna() # workaround for missing data
|
|
149
|
+
#hkldata.setup_relion_binning()
|
|
150
|
+
hkldata.setup_binning(n_bins=10) # need to sort out
|
|
151
|
+
st.cell = hkldata.cell
|
|
152
|
+
st.spacegroup_hm = hkldata.sg.xhm()
|
|
153
|
+
st.setup_cell_images()
|
|
154
|
+
info = {}
|
|
155
|
+
utils.restraints.find_and_fix_links(st, monlib, add_found=args.find_links)
|
|
156
|
+
else:
|
|
157
|
+
if args.halfmaps:
|
|
158
|
+
maps = utils.fileio.read_halfmaps(args.halfmaps, pixel_size=args.pixel_size)
|
|
159
|
+
else:
|
|
160
|
+
maps = [utils.fileio.read_ccp4_map(args.map, pixel_size=args.pixel_size)]
|
|
161
|
+
hkldata, info = process_input(st, maps, resolution=args.resolution - 1e-6, monlib=monlib,
|
|
162
|
+
mask_in=args.mask, args=args, use_refmac=False,
|
|
163
|
+
find_links=args.find_links)
|
|
164
|
+
h_change = {"all":gemmi.HydrogenChange.ReAddKnown,
|
|
165
|
+
"yes":gemmi.HydrogenChange.NoChange,
|
|
166
|
+
"no":gemmi.HydrogenChange.Remove}[args.hydrogen]
|
|
167
|
+
try:
|
|
168
|
+
topo, metal_kws = utils.restraints.prepare_topology(st, monlib, h_change=h_change,
|
|
169
|
+
check_hydrogen=(args.hydrogen=="yes"))
|
|
170
|
+
except RuntimeError as e:
|
|
171
|
+
raise SystemExit("Error: {}".format(e))
|
|
172
|
+
refmac_keywords = metal_kws + refmac_keywords
|
|
173
|
+
# initialize ADP
|
|
174
|
+
if args.adp != "fix":
|
|
175
|
+
utils.model.reset_adp(st[0], args.bfactor, args.adp == "aniso")
|
|
176
|
+
|
|
177
|
+
# auto weight
|
|
178
|
+
if args.weight is None:
|
|
179
|
+
# from 230303_weight_test using 472 test cases
|
|
180
|
+
reso = info["d_eff"] if "d_eff" in info else args.resolution
|
|
181
|
+
if "vol_ratio" in info:
|
|
182
|
+
if "d_eff" in info:
|
|
183
|
+
rlmc = (-2.3976, 0.5933, 3.5160)
|
|
184
|
+
else:
|
|
185
|
+
rlmc = (-2.5151, 0.6681, 3.6467)
|
|
186
|
+
logger.writeln("Estimating weight auto scale using resolution and volume ratio")
|
|
187
|
+
ws = numpy.exp(rlmc[0] + reso*rlmc[1] +info["vol_ratio"]*rlmc[2])
|
|
188
|
+
else:
|
|
189
|
+
if "d_eff" in info:
|
|
190
|
+
rlmc = (-1.6908, 0.5668)
|
|
191
|
+
else:
|
|
192
|
+
rlmc = (-1.7588, 0.6311)
|
|
193
|
+
logger.writeln("Estimating weight auto scale using resolution")
|
|
194
|
+
ws = numpy.exp(rlmc[0] + args.resolution*rlmc[1])
|
|
195
|
+
args.weight = max(0.2, min(18.0, ws))
|
|
196
|
+
logger.writeln(" Will use weight= {:.2f}".format(args.weight))
|
|
197
|
+
|
|
198
|
+
if args.ncsr:
|
|
199
|
+
ncslist = utils.restraints.prepare_ncs_restraints(st)
|
|
200
|
+
else:
|
|
201
|
+
ncslist = False
|
|
202
|
+
geom = Geom(st, topo, monlib, shake_rms=args.randomize, adpr_w=args.adpr_weight,
|
|
203
|
+
refmac_keywords=refmac_keywords, unrestrained=args.jellyonly,
|
|
204
|
+
ncslist=ncslist)
|
|
205
|
+
ll = spa.LL_SPA(hkldata, st, monlib,
|
|
206
|
+
lab_obs="F_map1" if args.cross_validation else "FP",
|
|
207
|
+
source=args.source)
|
|
208
|
+
refiner = Refine(st, geom, ll,
|
|
209
|
+
refine_xyz=not args.fix_xyz,
|
|
210
|
+
adp_mode=dict(fix=0, iso=1, aniso=2)[args.adp],
|
|
211
|
+
refine_h=args.refine_h,
|
|
212
|
+
refmac_keywords=refmac_keywords)
|
|
213
|
+
|
|
214
|
+
geom.geom.adpr_max_dist = args.max_dist_for_adp_restraint
|
|
215
|
+
if args.adp_restraint_power is not None: geom.geom.adpr_d_power = args.adp_restraint_power
|
|
216
|
+
if args.adp_restraint_exp_fac is not None: geom.geom.adpr_exp_fac = args.adp_restraint_exp_fac
|
|
217
|
+
if args.adp_restraint_no_long_range: geom.geom.adpr_long_range = False
|
|
218
|
+
geom.geom.adpr_mode = args.adp_restraint_mode
|
|
219
|
+
if args.jellybody or args.jellyonly: geom.geom.ridge_sigma, geom.geom.ridge_dmax = args.jellybody_params
|
|
220
|
+
if args.jellyonly: geom.geom.ridge_exclude_short_dist = False
|
|
221
|
+
|
|
222
|
+
#logger.writeln("TEST: shift x+0.3 A")
|
|
223
|
+
#for cra in st[0].all():
|
|
224
|
+
# cra.atom.pos += gemmi.Position(0.3,0,0)
|
|
225
|
+
|
|
226
|
+
stats = refiner.run_cycles(args.ncycle, weight=args.weight)
|
|
227
|
+
if not args.hklin and not args.no_trim:
|
|
228
|
+
refiner.st.cell = maps[0][0].unit_cell
|
|
229
|
+
refiner.st.setup_cell_images()
|
|
230
|
+
|
|
231
|
+
refiner.st.name = args.output_prefix
|
|
232
|
+
utils.fileio.write_model(refiner.st, args.output_prefix, pdb=True, cif=True)
|
|
233
|
+
with open(args.output_prefix + "_stats.json", "w") as ofs:
|
|
234
|
+
for s in stats:
|
|
235
|
+
if "geom" in s: s["geom"] = s["geom"].to_dict()
|
|
236
|
+
json.dump(stats, ofs, indent=2)
|
|
237
|
+
logger.writeln("Refinement statistics saved: {}".format(ofs.name))
|
|
238
|
+
|
|
239
|
+
if args.hklin:
|
|
240
|
+
return
|
|
241
|
+
|
|
242
|
+
# Expand sym here
|
|
243
|
+
st_expanded = refiner.st.clone()
|
|
244
|
+
if not all(op.given for op in st.ncs):
|
|
245
|
+
utils.model.expand_ncs(st_expanded)
|
|
246
|
+
utils.fileio.write_model(st_expanded, args.output_prefix+"_expanded", pdb=True, cif=True)
|
|
247
|
+
|
|
248
|
+
# Calc FSC
|
|
249
|
+
mask = utils.fileio.read_ccp4_map(args.mask)[0] if args.mask else None
|
|
250
|
+
fscavg_text = calc_fsc(st_expanded, args.output_prefix, maps,
|
|
251
|
+
args.resolution, mask=mask, mask_radius=args.mask_radius if not args.no_mask else None,
|
|
252
|
+
soft_edge=args.mask_soft_edge,
|
|
253
|
+
b_before_mask=args.b_before_mask,
|
|
254
|
+
no_sharpen_before_mask=args.no_sharpen_before_mask,
|
|
255
|
+
make_hydrogen=args.hydrogen,
|
|
256
|
+
monlib=monlib,
|
|
257
|
+
blur=args.blur,
|
|
258
|
+
d_min_fsc=args.fsc_resolution,
|
|
259
|
+
cross_validation=args.cross_validation,
|
|
260
|
+
cross_validation_method=args.cross_validation_method
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Calc Fo-Fc (and updated) maps
|
|
264
|
+
diffmap_prefix = "{}_diffmap".format(args.output_prefix)
|
|
265
|
+
calc_fofc(refiner.st, st_expanded, maps, monlib, ".mmcif", args, diffmap_prefix=diffmap_prefix)
|
|
266
|
+
|
|
267
|
+
# Final summary
|
|
268
|
+
adpstats_txt = ""
|
|
269
|
+
adp_stats = utils.model.adp_stats_per_chain(refiner.st[0])
|
|
270
|
+
max_chain_len = max([len(x[0]) for x in adp_stats])
|
|
271
|
+
max_num_len = max([len(str(x[1])) for x in adp_stats])
|
|
272
|
+
for chain, natoms, qs in adp_stats:
|
|
273
|
+
adpstats_txt += " Chain {0:{1}s}".format(chain, max_chain_len) if chain!="*" else " {0:{1}s}".format("All", max_chain_len+6)
|
|
274
|
+
adpstats_txt += " ({0:{1}d} atoms) min={2:5.1f} median={3:5.1f} max={4:5.1f} A^2\n".format(natoms, max_num_len, qs[0],qs[2],qs[4])
|
|
275
|
+
|
|
276
|
+
if "geom" in stats[-1]:
|
|
277
|
+
rmsbond = stats[-1]["geom"]["r.m.s.d."]["Bond distances, non H"]
|
|
278
|
+
rmsangle = stats[-1]["geom"]["r.m.s.d."]["Bond angles, non H"]
|
|
279
|
+
else:
|
|
280
|
+
rmsbond, rmsangle = numpy.nan, numpy.nan
|
|
281
|
+
if args.mask_for_fofc:
|
|
282
|
+
map_peaks_str = """\
|
|
283
|
+
List Fo-Fc map peaks in the ASU:
|
|
284
|
+
servalcat util map_peaks --map {diffmap_prefix}_normalized_fofc.mrc --model {prefix}.pdb --abs_level 4.0 \
|
|
285
|
+
""".format(prefix=args.output_prefix, diffmap_prefix=diffmap_prefix)
|
|
286
|
+
else:
|
|
287
|
+
map_peaks_str = "WARNING: --mask_for_fofc was not given, so the Fo-Fc map was not normalized."
|
|
288
|
+
|
|
289
|
+
logger.writeln("""
|
|
290
|
+
=============================================================================
|
|
291
|
+
* Final Summary *
|
|
292
|
+
|
|
293
|
+
Rmsd from ideal
|
|
294
|
+
bond lengths: {rmsbond:.4f} A
|
|
295
|
+
bond angles: {rmsangle:.3f} deg
|
|
296
|
+
|
|
297
|
+
{fscavgs}
|
|
298
|
+
Run loggraph {fsclog} to see plots
|
|
299
|
+
|
|
300
|
+
ADP statistics
|
|
301
|
+
{adpstats}
|
|
302
|
+
|
|
303
|
+
Weight used: {final_weight:.3e}
|
|
304
|
+
If you want to change the weight, give larger (looser restraints)
|
|
305
|
+
or smaller (tighter) value to --weight=.
|
|
306
|
+
|
|
307
|
+
Open refined model and {diffmap_prefix}.mtz with COOT:
|
|
308
|
+
coot --script {prefix}_coot.py
|
|
309
|
+
|
|
310
|
+
{map_peaks_msg}
|
|
311
|
+
=============================================================================
|
|
312
|
+
""".format(rmsbond=rmsbond,
|
|
313
|
+
rmsangle=rmsangle,
|
|
314
|
+
fscavgs=fscavg_text.rstrip(),
|
|
315
|
+
fsclog="{}_fsc.log".format(args.output_prefix),
|
|
316
|
+
adpstats=adpstats_txt.rstrip(),
|
|
317
|
+
final_weight=args.weight,
|
|
318
|
+
prefix=args.output_prefix,
|
|
319
|
+
diffmap_prefix=diffmap_prefix,
|
|
320
|
+
map_peaks_msg=map_peaks_str))
|
|
321
|
+
|
|
322
|
+
# main()
|
|
323
|
+
|
|
324
|
+
if __name__ == "__main__":
|
|
325
|
+
import sys
|
|
326
|
+
args = parse_args(sys.argv[1:])
|
|
327
|
+
main(args)
|