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.

Files changed (44) hide show
  1. servalcat/__init__.py +10 -0
  2. servalcat/__main__.py +120 -0
  3. servalcat/ext.cpython-312-darwin.so +0 -0
  4. servalcat/refine/__init__.py +0 -0
  5. servalcat/refine/cgsolve.py +100 -0
  6. servalcat/refine/refine.py +733 -0
  7. servalcat/refine/refine_geom.py +207 -0
  8. servalcat/refine/refine_spa.py +327 -0
  9. servalcat/refine/refine_xtal.py +242 -0
  10. servalcat/refine/spa.py +132 -0
  11. servalcat/refine/xtal.py +227 -0
  12. servalcat/refmac/__init__.py +0 -0
  13. servalcat/refmac/exte.py +182 -0
  14. servalcat/refmac/refmac_keywords.py +536 -0
  15. servalcat/refmac/refmac_wrapper.py +360 -0
  16. servalcat/spa/__init__.py +0 -0
  17. servalcat/spa/fofc.py +462 -0
  18. servalcat/spa/fsc.py +385 -0
  19. servalcat/spa/localcc.py +188 -0
  20. servalcat/spa/realspcc_from_var.py +128 -0
  21. servalcat/spa/run_refmac.py +961 -0
  22. servalcat/spa/shift_maps.py +293 -0
  23. servalcat/spa/shiftback.py +137 -0
  24. servalcat/spa/translate.py +129 -0
  25. servalcat/utils/__init__.py +35 -0
  26. servalcat/utils/commands.py +1277 -0
  27. servalcat/utils/fileio.py +745 -0
  28. servalcat/utils/generate_operators.py +296 -0
  29. servalcat/utils/hkl.py +699 -0
  30. servalcat/utils/logger.py +116 -0
  31. servalcat/utils/maps.py +340 -0
  32. servalcat/utils/model.py +774 -0
  33. servalcat/utils/refmac.py +747 -0
  34. servalcat/utils/restraints.py +605 -0
  35. servalcat/utils/symmetry.py +295 -0
  36. servalcat/xtal/__init__.py +0 -0
  37. servalcat/xtal/french_wilson.py +250 -0
  38. servalcat/xtal/run_refmac_small.py +240 -0
  39. servalcat/xtal/sigmaa.py +1403 -0
  40. servalcat-0.4.60.dist-info/METADATA +56 -0
  41. servalcat-0.4.60.dist-info/RECORD +44 -0
  42. servalcat-0.4.60.dist-info/WHEEL +5 -0
  43. servalcat-0.4.60.dist-info/entry_points.txt +4 -0
  44. 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)