servalcat 0.4.72__cp39-cp39-macosx_11_0_arm64.whl → 0.4.99__cp39-cp39-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 +2 -2
- servalcat/ext.cpython-39-darwin.so +0 -0
- servalcat/refine/refine.py +152 -67
- servalcat/refine/refine_geom.py +32 -13
- servalcat/refine/refine_spa.py +70 -40
- servalcat/refine/refine_xtal.py +45 -13
- servalcat/refine/spa.py +15 -4
- servalcat/refine/xtal.py +147 -98
- servalcat/refmac/exte.py +7 -5
- servalcat/refmac/refmac_keywords.py +11 -9
- servalcat/refmac/refmac_wrapper.py +87 -60
- servalcat/spa/fofc.py +20 -3
- servalcat/spa/fsc.py +11 -11
- servalcat/spa/run_refmac.py +27 -12
- servalcat/spa/translate.py +2 -2
- servalcat/utils/commands.py +154 -4
- servalcat/utils/fileio.py +20 -10
- servalcat/utils/hkl.py +43 -29
- servalcat/utils/logger.py +25 -1
- servalcat/utils/maps.py +2 -2
- servalcat/utils/model.py +23 -10
- servalcat/utils/refmac.py +20 -1
- servalcat/utils/restraints.py +34 -25
- servalcat/utils/symmetry.py +5 -5
- servalcat/xtal/french_wilson.py +39 -31
- servalcat/xtal/sigmaa.py +382 -152
- servalcat/xtal/twin.py +121 -0
- {servalcat-0.4.72.dist-info → servalcat-0.4.99.dist-info}/METADATA +4 -4
- servalcat-0.4.99.dist-info/RECORD +45 -0
- {servalcat-0.4.72.dist-info → servalcat-0.4.99.dist-info}/WHEEL +1 -1
- servalcat-0.4.72.dist-info/RECORD +0 -44
- {servalcat-0.4.72.dist-info → servalcat-0.4.99.dist-info}/entry_points.txt +0 -0
- {servalcat-0.4.72.dist-info → servalcat-0.4.99.dist-info}/licenses/LICENSE +0 -0
servalcat/__init__.py
CHANGED
|
Binary file
|
servalcat/refine/refine.py
CHANGED
|
@@ -28,7 +28,7 @@ b_to_u = utils.model.b_to_u
|
|
|
28
28
|
#atexit.register(profile.print_stats)
|
|
29
29
|
|
|
30
30
|
class Geom:
|
|
31
|
-
def __init__(self, st, topo, monlib, adpr_w=1, shake_rms=0,
|
|
31
|
+
def __init__(self, st, topo, monlib, adpr_w=1, occr_w=1, shake_rms=0,
|
|
32
32
|
params=None, unrestrained=False, use_nucleus=False,
|
|
33
33
|
ncslist=None, atom_pos=None):
|
|
34
34
|
self.st = st
|
|
@@ -40,7 +40,12 @@ class Geom:
|
|
|
40
40
|
self.atom_pos = list(range(len(self.atoms)))
|
|
41
41
|
self.n_refine_atoms = max(self.atom_pos) + 1
|
|
42
42
|
self.lookup = {x.atom: x for x in self.st[0].all()}
|
|
43
|
-
|
|
43
|
+
try:
|
|
44
|
+
self.geom = ext.Geometry(self.st, self.atom_pos, monlib.ener_lib)
|
|
45
|
+
except TypeError as e:
|
|
46
|
+
raise SystemExit(f"An error occurred while creating the Geometry object:\n{e}\n\n"
|
|
47
|
+
"This likely indicates an installation issue. "
|
|
48
|
+
"Please verify that you have the correct version of gemmi installed and that both gemmi and servalcat were compiled in the same environment.")
|
|
44
49
|
self.specs = utils.model.find_special_positions(self.st)
|
|
45
50
|
#cs_count = len(self.st.find_spacegroup().operations())
|
|
46
51
|
for atom, images, matp, mata in self.specs:
|
|
@@ -48,7 +53,7 @@ class Geom:
|
|
|
48
53
|
n_sym = len(images) + 1
|
|
49
54
|
self.geom.specials.append(ext.Geometry.Special(atom, matp, mata, n_sym))
|
|
50
55
|
self.adpr_w = adpr_w
|
|
51
|
-
self.occr_w =
|
|
56
|
+
self.occr_w = occr_w
|
|
52
57
|
self.unrestrained = unrestrained
|
|
53
58
|
if shake_rms > 0:
|
|
54
59
|
numpy.random.seed(0)
|
|
@@ -58,7 +63,6 @@ class Geom:
|
|
|
58
63
|
self.calc_kwds = {"use_nucleus": self.use_nucleus}
|
|
59
64
|
if params is None:
|
|
60
65
|
params = {}
|
|
61
|
-
exte.read_external_restraints(params.get("exte", []), self.st, self.geom)
|
|
62
66
|
for k in ("wbond", "wangle", "wtors", "wplane", "wchir", "wvdw", "wncs"):
|
|
63
67
|
if k in params:
|
|
64
68
|
self.calc_kwds[k] = params[k]
|
|
@@ -70,26 +74,13 @@ class Geom:
|
|
|
70
74
|
self.group_occ = GroupOccupancy(self.st, params.get("occu"))
|
|
71
75
|
if not self.unrestrained:
|
|
72
76
|
self.geom.load_topo(topo)
|
|
73
|
-
|
|
77
|
+
exte.read_external_restraints(params.get("exte", []), self.st, self.geom)
|
|
74
78
|
self.geom.finalize_restraints()
|
|
75
79
|
self.outlier_sigmas = dict(bond=5, angle=5, torsion=5, vdw=5, ncs=5, chir=5, plane=5, staca=5, stacd=5, per_atom=5)
|
|
76
80
|
self.parents = {}
|
|
77
81
|
self.ncslist = ncslist
|
|
78
82
|
# __init__()
|
|
79
83
|
|
|
80
|
-
def check_chemtypes(self, enerlib_path, topo):
|
|
81
|
-
block = gemmi.cif.read(enerlib_path).sole_block()
|
|
82
|
-
all_types = set(block.find_values("_lib_atom.type"))
|
|
83
|
-
for ci in topo.chain_infos:
|
|
84
|
-
for ri in ci.res_infos:
|
|
85
|
-
cc_all = {x: ri.get_final_chemcomp(x) for x in set(a.altloc for a in ri.res)}
|
|
86
|
-
for a in ri.res:
|
|
87
|
-
cca = cc_all[a.altloc].find_atom(a.name)
|
|
88
|
-
if cca is None: # I believe it won't happen..
|
|
89
|
-
logger.writeln("WARNING: restraint for {} not found.".format(self.lookup[a]))
|
|
90
|
-
elif cca.chem_type not in all_types:
|
|
91
|
-
raise RuntimeError("Energy type {} of {} not found in ener_lib.".format(cca.chem_type,
|
|
92
|
-
self.lookup[a]))
|
|
93
84
|
def set_h_parents(self):
|
|
94
85
|
self.parents = {}
|
|
95
86
|
for bond in self.geom.bonds:
|
|
@@ -176,15 +167,16 @@ class Geom:
|
|
|
176
167
|
logger.writeln(df.to_string(float_format="{:.3f}".format, index=False) + "\n")
|
|
177
168
|
|
|
178
169
|
# Per-atom score
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
df.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
170
|
+
if 0:
|
|
171
|
+
peratom = self.geom.reporting.per_atom_score(len(self.atoms), self.use_nucleus, "mean")
|
|
172
|
+
df = pandas.DataFrame(peratom)
|
|
173
|
+
df.insert(0, "atom", [str(self.lookup[x]) for x in self.atoms])
|
|
174
|
+
df = df[df["total"] >= self.outlier_sigmas["per_atom"]]
|
|
175
|
+
if show_outliers and len(df.index) > 0:
|
|
176
|
+
df.sort_values("total", ascending=False, inplace=True)
|
|
177
|
+
ret["outliers"]["per_atom"] = df
|
|
178
|
+
logger.writeln(" *** Per-atom violations (Z >= {}) ***\n".format(self.outlier_sigmas["per_atom"]))
|
|
179
|
+
logger.writeln(df.to_string(float_format="{:.2f}".format, index=False) + "\n")
|
|
188
180
|
|
|
189
181
|
df = pandas.DataFrame(self.geom.reporting.get_summary_table(self.use_nucleus))
|
|
190
182
|
df = df.set_index("Restraint type").rename_axis(index=None)
|
|
@@ -233,6 +225,26 @@ def write_stats_json_safe(stats, json_out):
|
|
|
233
225
|
logger.writeln(f"Refinement statistics saved: {json_out}")
|
|
234
226
|
# write_stats_json_safe()
|
|
235
227
|
|
|
228
|
+
def print_h_options(h_change, h_present, refine_h, hout, geom_only):
|
|
229
|
+
if not h_present:
|
|
230
|
+
h_change = gemmi.HydrogenChange.Remove
|
|
231
|
+
logger.writeln("Hydrogen related options")
|
|
232
|
+
logger.write(" use in refinement{}: hydrogen atoms ".format("" if geom_only else "/map calculation"))
|
|
233
|
+
logger.writeln({gemmi.HydrogenChange.ReAddButWater: "have been (re)generated",
|
|
234
|
+
gemmi.HydrogenChange.ReAdd: "(including water) have been (re)generated",
|
|
235
|
+
gemmi.HydrogenChange.ReAddKnown: "(except for rotatable) have been (re) generated",
|
|
236
|
+
gemmi.HydrogenChange.NoChange: "from the input model have been retained",
|
|
237
|
+
gemmi.HydrogenChange.Remove: "have either been removed or were not present"}[h_change])
|
|
238
|
+
if h_present:
|
|
239
|
+
logger.write(" target: hydrogen atoms will be ")
|
|
240
|
+
if geom_only or not refine_h:
|
|
241
|
+
logger.writeln("just optimized according to geometric restraints")
|
|
242
|
+
else:
|
|
243
|
+
logger.writeln("refined against experimental data")
|
|
244
|
+
logger.writeln(" in output model: " + ("written" if hout and h_present else "not written"))
|
|
245
|
+
logger.writeln("")
|
|
246
|
+
# print_hydrogen_options()
|
|
247
|
+
|
|
236
248
|
class GroupOccupancy:
|
|
237
249
|
# TODO max may not be one. should check multiplicity
|
|
238
250
|
def __init__(self, st, params):
|
|
@@ -301,6 +313,7 @@ class GroupOccupancy:
|
|
|
301
313
|
vals = []
|
|
302
314
|
for _, atoms in self.groups:
|
|
303
315
|
occ = numpy.mean([a.occ for a in atoms])
|
|
316
|
+
occ = min(1, max(1e-3, occ))
|
|
304
317
|
vals.append(occ)
|
|
305
318
|
for is_comp, idxes in self.consts:
|
|
306
319
|
sum_occ = sum(vals[i] for i in idxes)
|
|
@@ -319,6 +332,7 @@ class GroupOccupancy:
|
|
|
319
332
|
for p, (_, atoms) in zip(x, self.groups):
|
|
320
333
|
for a in atoms:
|
|
321
334
|
a.occ = p
|
|
335
|
+
#a.occ = max(1, min(1e-3, p))
|
|
322
336
|
|
|
323
337
|
def target(self, x, ll, ls, u):
|
|
324
338
|
self.set_x(x)
|
|
@@ -433,7 +447,7 @@ class Refine:
|
|
|
433
447
|
self.geom.set_h_parents()
|
|
434
448
|
if params and params.get("write_trajectory"):
|
|
435
449
|
self.st_traj = self.st.clone()
|
|
436
|
-
self.st_traj[-1].
|
|
450
|
+
self.st_traj[-1].num = 0
|
|
437
451
|
assert self.geom.group_occ.groups or self.n_params() > 0
|
|
438
452
|
# __init__()
|
|
439
453
|
|
|
@@ -450,6 +464,9 @@ class Refine:
|
|
|
450
464
|
logger.writeln(" sigmas: {}".format(" ".join("{:.2f}".format(x) for x in g.adpr_kl_sigs)))
|
|
451
465
|
else:
|
|
452
466
|
raise LookupError("unknown adpr_mode")
|
|
467
|
+
if self.refine_occ:
|
|
468
|
+
logger.writeln(" Occupancy restraints")
|
|
469
|
+
logger.writeln(" weight: {}".format(self.geom.occr_w))
|
|
453
470
|
|
|
454
471
|
def scale_shifts(self, dx, scale):
|
|
455
472
|
n_atoms = self.geom.n_refine_atoms
|
|
@@ -533,7 +550,7 @@ class Refine:
|
|
|
533
550
|
elif self.adp_mode == 2:
|
|
534
551
|
a = x[offset_b + 6 * j: offset_b + 6 * (j+1)]
|
|
535
552
|
a = gemmi.SMat33d(*a)
|
|
536
|
-
M =
|
|
553
|
+
M = a.as_mat33().array
|
|
537
554
|
v, Q = numpy.linalg.eigh(M) # eig() may return complex due to numerical precision?
|
|
538
555
|
v = numpy.maximum(v, 0.5) # avoid NPD with minimum B = 0.5
|
|
539
556
|
M2 = Q.dot(numpy.diag(v)).dot(Q.T)
|
|
@@ -556,6 +573,8 @@ class Refine:
|
|
|
556
573
|
self.geom.setup_nonbonded(self.refine_xyz) # if refine_xyz=False, no need to do it every time
|
|
557
574
|
self.geom.geom.setup_target(self.refine_xyz, self.adp_mode, self.refine_occ, self.use_occr)
|
|
558
575
|
logger.writeln("vdws = {}".format(len(self.geom.geom.vdws)))
|
|
576
|
+
logger.writeln(f"atoms = {self.geom.geom.target.n_atoms()}")
|
|
577
|
+
logger.writeln(f"pairs = {self.geom.geom.target.n_pairs()}")
|
|
559
578
|
|
|
560
579
|
def get_x(self):
|
|
561
580
|
n_atoms = self.geom.n_refine_atoms
|
|
@@ -675,6 +694,8 @@ class Refine:
|
|
|
675
694
|
self.geom.setup_nonbonded(self.refine_xyz)
|
|
676
695
|
self.geom.geom.setup_target(self.refine_xyz, self.adp_mode, self.refine_occ, self.use_occr)
|
|
677
696
|
logger.writeln("vdws = {}".format(len(self.geom.geom.vdws)))
|
|
697
|
+
logger.writeln(f"atoms = {self.geom.geom.target.n_atoms()}")
|
|
698
|
+
logger.writeln(f"pairs = {self.geom.geom.target.n_pairs()}")
|
|
678
699
|
stats[-1]["geom"] = self.geom.show_model_stats(refine_xyz=self.refine_xyz and not self.unrestrained,
|
|
679
700
|
adp_mode=self.adp_mode,
|
|
680
701
|
use_occr=self.refine_occ,
|
|
@@ -683,9 +704,12 @@ class Refine:
|
|
|
683
704
|
self.ll.update_fc()
|
|
684
705
|
self.ll.overall_scale()
|
|
685
706
|
self.ll.update_ml_params()
|
|
707
|
+
self.ll.prepare_target()
|
|
686
708
|
llstats = self.ll.calc_stats(bin_stats=True)
|
|
687
709
|
stats[-1]["data"] = {"summary": llstats["summary"],
|
|
688
710
|
"binned": llstats["bin_stats"].to_dict(orient="records")}
|
|
711
|
+
if "twin_alpha" in llstats:
|
|
712
|
+
stats[-1]["twin_alpha"] = llstats["twin_alpha"]
|
|
689
713
|
show_binstats(llstats["bin_stats"], 0)
|
|
690
714
|
if self.adp_mode > 0:
|
|
691
715
|
utils.model.adp_analysis(self.st)
|
|
@@ -713,12 +737,15 @@ class Refine:
|
|
|
713
737
|
self.ll.overall_scale()
|
|
714
738
|
f0 = self.ll.calc_target()
|
|
715
739
|
self.ll.update_ml_params()
|
|
740
|
+
self.ll.prepare_target()
|
|
716
741
|
llstats = self.ll.calc_stats(bin_stats=True)#(i==ncycles-1))
|
|
717
742
|
if llstats["summary"]["-LL"] > f0:
|
|
718
743
|
logger.writeln("WARNING: -LL has increased after ML parameter optimization:"
|
|
719
744
|
"{} to {}".format(f0, llstats["summary"]["-LL"]))
|
|
720
745
|
stats[-1]["data"] = {"summary": llstats["summary"],
|
|
721
746
|
"binned": llstats["bin_stats"].to_dict(orient="records")}
|
|
747
|
+
if "twin_alpha" in llstats:
|
|
748
|
+
stats[-1]["twin_alpha"] = llstats["twin_alpha"]
|
|
722
749
|
show_binstats(llstats["bin_stats"], i+1)
|
|
723
750
|
if self.adp_mode > 0:
|
|
724
751
|
utils.model.adp_analysis(self.st)
|
|
@@ -734,7 +761,7 @@ class Refine:
|
|
|
734
761
|
weight /= 1.1
|
|
735
762
|
if self.st_traj is not None:
|
|
736
763
|
self.st_traj.add_model(self.st[0])
|
|
737
|
-
self.st_traj[-1].
|
|
764
|
+
self.st_traj[-1].num = len(self.st_traj)
|
|
738
765
|
if stats_json_out:
|
|
739
766
|
write_stats_json_safe(stats, stats_json_out)
|
|
740
767
|
|
|
@@ -779,43 +806,101 @@ class Refine:
|
|
|
779
806
|
lstr = utils.make_loggraph_str(df, "stats vs cycle", forplot,
|
|
780
807
|
float_format="{:.4f}".format)
|
|
781
808
|
logger.writeln(lstr)
|
|
782
|
-
self.update_meta(stats[-1])
|
|
783
809
|
return stats
|
|
784
810
|
|
|
785
|
-
def update_meta(self, stats):
|
|
786
|
-
# TODO write stats. probably geom.reporting.get_summary_table should return with _refine_ls_restr.type names
|
|
787
|
-
# should remove st.mod_residues?
|
|
788
|
-
self.st.helices.clear()
|
|
789
|
-
self.st.sheets.clear()
|
|
790
|
-
raw_remarks = [f'REMARK 3',
|
|
791
|
-
f'REMARK 3 REFINEMENT.',
|
|
792
|
-
f'REMARK 3 PROGRAM : SERVALCAT {servalcat.__version__}',
|
|
793
|
-
f'REMARK 3 AUTHORS : YAMASHITA,MURSHUDOV',
|
|
794
|
-
f'REMARK 3',
|
|
795
|
-
]
|
|
796
|
-
si = gemmi.SoftwareItem()
|
|
797
|
-
si.classification = gemmi.SoftwareItem.Classification.Refinement
|
|
798
|
-
si.name = "Servalcat"
|
|
799
|
-
si.version = servalcat.__version__
|
|
800
|
-
si.date = servalcat.__date__
|
|
801
|
-
self.st.meta.software = [si]
|
|
802
|
-
|
|
803
|
-
ri = gemmi.RefinementInfo()
|
|
804
|
-
if "geom" in stats:
|
|
805
|
-
restr_stats = []
|
|
806
|
-
raw_remarks.append("REMARK 3 RMS DEVIATIONS FROM IDEAL VALUES COUNT RMS WEIGHT")
|
|
807
|
-
for k, n, l, pl in (("r.m.s.d.", "Bond distances, non H", "s_bond_nonh_d", "BOND LENGTHS REFINED ATOMS (A)"),
|
|
808
|
-
("r.m.s.d.", "Bond angles, non H", "s_angle_nonh_d", "BOND ANGLES REFINED ATOMS (DEGREES)")):
|
|
809
|
-
if k in stats["geom"]["summary"] and n in stats["geom"]["summary"][k]:
|
|
810
|
-
rr = gemmi.RefinementInfo.Restr(l)
|
|
811
|
-
rr.dev_ideal = stats["geom"]["summary"][k].get(n)
|
|
812
|
-
rr.count = stats["geom"]["summary"]["N restraints"].get(n)
|
|
813
|
-
rr.weight = stats["geom"]["summary"]["Mn(sigma)"].get(n)
|
|
814
|
-
restr_stats.append(rr)
|
|
815
|
-
raw_remarks.append(f"REMARK 3 {pl}:{rr.count:6d} ;{rr.dev_ideal:6.3f} ;{rr.weight:6.3f}")
|
|
816
|
-
ri.restr_stats = restr_stats
|
|
817
|
-
raw_remarks.append("REMARK 3")
|
|
818
|
-
self.st.meta.refinement = [ri]
|
|
819
|
-
self.st.raw_remarks = raw_remarks
|
|
820
|
-
|
|
821
811
|
# class Refine
|
|
812
|
+
|
|
813
|
+
def update_meta(st, stats, ll=None):
|
|
814
|
+
# TODO write stats. probably geom.reporting.get_summary_table should return with _refine_ls_restr.type names
|
|
815
|
+
# should remove st.mod_residues?
|
|
816
|
+
st.helices.clear()
|
|
817
|
+
st.sheets.clear()
|
|
818
|
+
raw_remarks = [f'REMARK 3',
|
|
819
|
+
f'REMARK 3 REFINEMENT.',
|
|
820
|
+
f'REMARK 3 PROGRAM : SERVALCAT {servalcat.__version__}',
|
|
821
|
+
f'REMARK 3 AUTHORS : YAMASHITA,MURSHUDOV',
|
|
822
|
+
f'REMARK 3',
|
|
823
|
+
]
|
|
824
|
+
si = gemmi.SoftwareItem()
|
|
825
|
+
si.classification = gemmi.SoftwareItem.Classification.Refinement
|
|
826
|
+
si.name = "Servalcat"
|
|
827
|
+
si.version = servalcat.__version__
|
|
828
|
+
si.date = servalcat.__date__
|
|
829
|
+
st.meta.software = [si]
|
|
830
|
+
|
|
831
|
+
ri = gemmi.RefinementInfo()
|
|
832
|
+
if "geom" in stats:
|
|
833
|
+
restr_stats = []
|
|
834
|
+
raw_remarks.append("REMARK 3 RMS DEVIATIONS FROM IDEAL VALUES COUNT RMS WEIGHT")
|
|
835
|
+
for k, n, l, pl in (("r.m.s.d.", "Bond distances, non H", "s_bond_nonh_d", "BOND LENGTHS REFINED ATOMS (A)"),
|
|
836
|
+
("r.m.s.d.", "Bond angles, non H", "s_angle_nonh_deg", "BOND ANGLES REFINED ATOMS (DEGREES)"),
|
|
837
|
+
("r.m.s.d.", "Torsion angles, period 1", "s_dihedral_angle_1_deg", "TORSION ANGLES, PERIOD 1 (DEGREES)"),
|
|
838
|
+
("r.m.s.d.", "Torsion angles, period 2", "s_dihedral_angle_2_deg", "TORSION ANGLES, PERIOD 2 (DEGREES)"),
|
|
839
|
+
("r.m.s.d.", "Torsion angles, period 3", "s_dihedral_angle_3_deg", "TORSION ANGLES, PERIOD 3 (DEGREES)"),
|
|
840
|
+
("r.m.s.d.", "Torsion angles, period 6", "s_dihedral_angle_6_deg", "TORSION ANGLES, PERIOD 6 (DEGREES)"),
|
|
841
|
+
("r.m.s.d.", "Chiral centres", "s_chiral_restr", "CHIRAL-CENTER RESTRAINTS (A**3)"),
|
|
842
|
+
("r.m.s.d.", "Planar groups", "s_planes", "GENERAL PLANES REFINED ATOMS (A)"),
|
|
843
|
+
("r.m.s.d.", "VDW nonbonded", "s_nbd", ""),
|
|
844
|
+
("r.m.s.d.", "VDW torsion", "s_nbtor", ""),
|
|
845
|
+
("r.m.s.d.", "VDW hbond", "s_hbond_nbd", ""),
|
|
846
|
+
("r.m.s.d.", "VDW metal", "s_metal_ion", ""),
|
|
847
|
+
("r.m.s.d.", "VDW dummy", "s_dummy_nbd", ""),
|
|
848
|
+
("r.m.s.d.", "VDW nonbonded, symmetry", "s_symmetry_nbd", ""),
|
|
849
|
+
("r.m.s.d.", "VDW torsion, symmetry", "s_symmetry_nbtor", ""),
|
|
850
|
+
("r.m.s.d.", "VDW hbond, symmetry", "s_symmetry_hbond_nbd", ""),
|
|
851
|
+
("r.m.s.d.", "VDW metal, symmetry", "s_symmetry_metal_ion", ""),
|
|
852
|
+
("r.m.s.d.", "VDW dummy, symmetry", "s_symmetry_dummy_nbd", "")):
|
|
853
|
+
if k in stats["geom"]["summary"] and n in stats["geom"]["summary"][k]:
|
|
854
|
+
rr = gemmi.RefinementInfo.Restr(l)
|
|
855
|
+
rr.dev_ideal = round(stats["geom"]["summary"][k].get(n), 4)
|
|
856
|
+
rr.count = stats["geom"]["summary"]["N restraints"].get(n)
|
|
857
|
+
rr.weight = round(stats["geom"]["summary"]["Mn(sigma)"].get(n), 4)
|
|
858
|
+
restr_stats.append(rr)
|
|
859
|
+
if pl:
|
|
860
|
+
raw_remarks.append(f"REMARK 3 {pl}:{rr.count:6d} ;{rr.dev_ideal:6.3f} ;{rr.weight:6.3f}")
|
|
861
|
+
ri.restr_stats = restr_stats
|
|
862
|
+
raw_remarks.append("REMARK 3")
|
|
863
|
+
if ll is not None:
|
|
864
|
+
ri.id = ll.refine_id()
|
|
865
|
+
ri.mean_b = round(numpy.mean([cra.atom.b_iso for cra in st[0].all()]), 2)
|
|
866
|
+
if ll.b_aniso is not None:
|
|
867
|
+
ri.aniso_b = ll.b_aniso
|
|
868
|
+
for k, kd, nd in (("Rwork", "r_work", 4), ("Rfree", "r_free", 4), ("R", "r_all", 4),
|
|
869
|
+
("FSCaverage", "fsc_work", 4),
|
|
870
|
+
("FSCaverage_half1", "fsc_work", 4), ("FSCaverage_half2", "fsc_free", 4)):
|
|
871
|
+
if k in stats["data"]["summary"]:
|
|
872
|
+
setattr(ri, kd, round(stats["data"]["summary"][k], nd))
|
|
873
|
+
bins = []
|
|
874
|
+
n_all = 0
|
|
875
|
+
for b in stats["data"]["binned"]:
|
|
876
|
+
bri = gemmi.BasicRefinementInfo()
|
|
877
|
+
bri.resolution_high = round(b["d_min"], 3)
|
|
878
|
+
bri.resolution_low = round(b["d_max"], 3)
|
|
879
|
+
for k, kd, nd in (("Rwork", "r_work", 4), ("Rfree", "r_free", 4),
|
|
880
|
+
("R1work", "r_work", 4), ("R1free", "r_free", 4),
|
|
881
|
+
("R", "r_all", 4), ("R1", "r_all", 4),
|
|
882
|
+
("CCI", "cc_intensity_work", 4), ("CCF", "cc_fo_fc_work", 4),
|
|
883
|
+
("CCIwork", "cc_intensity_work", 4), ("CCIfree", "cc_intensity_free", 4),
|
|
884
|
+
("CCFwork", "cc_fo_fc_work", 4), ("CCFfree", "cc_fo_fc_free", 4),
|
|
885
|
+
("fsc_FC_full", "fsc_work", 4), ("fsc_model", "fsc_work", 4),
|
|
886
|
+
("fsc_model_half1", "fsc_work", 4), ("fsc_model_half2", "fsc_free", 4),
|
|
887
|
+
("n_work", "work_set_count", 0), ("n_free", "rfree_set_count", 0),
|
|
888
|
+
("n_obs", "reflection_count", 0), ("ncoeffs", "reflection_count", 0)):
|
|
889
|
+
if k in b: setattr(bri, kd, round(b[k], nd))
|
|
890
|
+
if "n_all" in b and "n_obs" in b:
|
|
891
|
+
bri.completeness = round(b["n_obs"] / b["n_all"] * 100, 2)
|
|
892
|
+
n_all += b["n_all"]
|
|
893
|
+
bins.append(bri)
|
|
894
|
+
ri.rfree_set_count = max(-1, sum(b.rfree_set_count for b in bins))
|
|
895
|
+
ri.work_set_count = max(-1, sum(b.work_set_count for b in bins))
|
|
896
|
+
ri.reflection_count = max(-1, sum(b.reflection_count for b in bins))
|
|
897
|
+
ri.resolution_high = round(min(b.resolution_high for b in bins), 3)
|
|
898
|
+
ri.resolution_low = round(max(b.resolution_low for b in bins), 3)
|
|
899
|
+
if ri.reflection_count > 0 and n_all > 0:
|
|
900
|
+
ri.completeness = round(ri.reflection_count / n_all * 100, 2)
|
|
901
|
+
ri.bins = bins
|
|
902
|
+
if ri.rfree_set_count > 0:
|
|
903
|
+
ri.cross_validation_method = "THROUGHOUT"
|
|
904
|
+
st.meta.refinement = [ri]
|
|
905
|
+
st.raw_remarks = raw_remarks
|
|
906
|
+
# update_meta()
|
servalcat/refine/refine_geom.py
CHANGED
|
@@ -14,7 +14,7 @@ import json
|
|
|
14
14
|
import servalcat # for version
|
|
15
15
|
from servalcat.utils import logger
|
|
16
16
|
from servalcat import utils
|
|
17
|
-
from servalcat.refine.refine import Geom, Refine, convert_stats_to_dicts
|
|
17
|
+
from servalcat.refine.refine import Geom, Refine, convert_stats_to_dicts, update_meta, print_h_options
|
|
18
18
|
from servalcat.refmac import refmac_keywords
|
|
19
19
|
|
|
20
20
|
def add_arguments(parser):
|
|
@@ -44,6 +44,8 @@ def add_arguments(parser):
|
|
|
44
44
|
help="refmac keyword file(s)")
|
|
45
45
|
parser.add_argument('-o','--output_prefix',
|
|
46
46
|
help="Output prefix")
|
|
47
|
+
parser.add_argument("--write_trajectory", action='store_true',
|
|
48
|
+
help="Write all output from cycles")
|
|
47
49
|
|
|
48
50
|
# add_arguments()
|
|
49
51
|
|
|
@@ -91,9 +93,14 @@ def refine_and_update_dictionary(cif_in, monomer_dir, output_prefix, randomize=0
|
|
|
91
93
|
if len(st) > 0: break
|
|
92
94
|
else:
|
|
93
95
|
raise SystemExit("No model in the cif file")
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
for i in range(len(st)-1):
|
|
97
|
+
del st[1]
|
|
98
|
+
try:
|
|
99
|
+
monlib = utils.restraints.load_monomer_library(st, monomer_dir=monomer_dir, # monlib is needed for ener_lib
|
|
100
|
+
cif_files=[cif_in],
|
|
101
|
+
stop_for_unknowns=True)
|
|
102
|
+
except RuntimeError as e:
|
|
103
|
+
raise SystemExit("Error: {}".format(e))
|
|
97
104
|
all_stats = []
|
|
98
105
|
for i_macro in 0, 1:
|
|
99
106
|
try:
|
|
@@ -119,16 +126,19 @@ def refine_and_update_dictionary(cif_in, monomer_dir, output_prefix, randomize=0
|
|
|
119
126
|
for row in block.find("_chem_comp_atom.", ["atom_id", "?x", "?y", "?z",
|
|
120
127
|
"?pdbx_model_Cartn_x_ideal",
|
|
121
128
|
"?pdbx_model_Cartn_y_ideal",
|
|
122
|
-
"?pdbx_model_Cartn_z_ideal"
|
|
129
|
+
"?pdbx_model_Cartn_z_ideal",
|
|
130
|
+
"?model_Cartn_x", "?model_Cartn_y", "?model_Cartn_z"]):
|
|
123
131
|
p = pos[row.str(0)]
|
|
124
132
|
for i in range(3):
|
|
125
133
|
if row.has(i+1):
|
|
126
134
|
row[i+1] = "{:.3f}".format(p[i])
|
|
127
135
|
if row.has(i+4):
|
|
128
136
|
row[i+4] = "{:.3f}".format(p[i])
|
|
137
|
+
if row.has(i+7):
|
|
138
|
+
row[i+7] = "{:.3f}".format(p[i])
|
|
129
139
|
# add description
|
|
130
140
|
add_program_info_to_dictionary(block, st[0][0][0].name)
|
|
131
|
-
doc.write_file(output_prefix + "_updated.cif",
|
|
141
|
+
doc.write_file(output_prefix + "_updated.cif", options=gemmi.cif.Style.Aligned)
|
|
132
142
|
logger.writeln("Updated dictionary saved: {}".format(output_prefix + "_updated.cif"))
|
|
133
143
|
with open(output_prefix + "_stats.json", "w") as ofs:
|
|
134
144
|
json.dump([convert_stats_to_dicts(x) for x in all_stats],
|
|
@@ -145,12 +155,15 @@ def refine_geom(model_in, monomer_dir, cif_files, h_change, ncycle, output_prefi
|
|
|
145
155
|
logger.writeln("Take NCS constraints into account.")
|
|
146
156
|
st2.expand_ncs(gemmi.HowToNameCopiedChain.Dup, merge_dist=0)
|
|
147
157
|
utils.fileio.write_model(st2, file_name="input_expanded.pdb")
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
158
|
+
try:
|
|
159
|
+
monlib = utils.restraints.load_monomer_library(st, monomer_dir=monomer_dir,
|
|
160
|
+
cif_files=cif_files,
|
|
161
|
+
stop_for_unknowns=True,
|
|
162
|
+
params=params)
|
|
163
|
+
except RuntimeError as e:
|
|
164
|
+
raise SystemExit("Error: {}".format(e))
|
|
165
|
+
utils.restraints.find_and_fix_links(st, monlib, find_metal_links=find_links,
|
|
166
|
+
add_found=find_links) # should remove unknown id here?
|
|
154
167
|
try:
|
|
155
168
|
topo, _ = utils.restraints.prepare_topology(st, monlib, h_change=h_change,
|
|
156
169
|
check_hydrogen=(h_change==gemmi.HydrogenChange.NoChange),
|
|
@@ -158,16 +171,21 @@ def refine_geom(model_in, monomer_dir, cif_files, h_change, ncycle, output_prefi
|
|
|
158
171
|
except RuntimeError as e:
|
|
159
172
|
raise SystemExit("Error: {}".format(e))
|
|
160
173
|
|
|
174
|
+
print_h_options(h_change, st[0].has_hydrogen(), refine_h=True, hout=True, geom_only=True)
|
|
175
|
+
|
|
161
176
|
if use_ncsr:
|
|
162
177
|
ncslist = utils.restraints.prepare_ncs_restraints(st)
|
|
163
178
|
else:
|
|
164
179
|
ncslist = False
|
|
165
180
|
geom = Geom(st, topo, monlib, shake_rms=randomize, params=params, ncslist=ncslist)
|
|
166
|
-
refiner = Refine(st, geom)
|
|
181
|
+
refiner = Refine(st, geom, params=params)
|
|
167
182
|
stats = refiner.run_cycles(ncycle,
|
|
168
183
|
stats_json_out=output_prefix + "_stats.json")
|
|
184
|
+
update_meta(st, stats[-1])
|
|
169
185
|
refiner.st.name = output_prefix
|
|
170
186
|
utils.fileio.write_model(refiner.st, output_prefix, pdb=True, cif=True)
|
|
187
|
+
if params["write_trajectory"]:
|
|
188
|
+
utils.fileio.write_model(refiner.st_traj, output_prefix + "_traj", cif=True)
|
|
171
189
|
# refine_geom()
|
|
172
190
|
|
|
173
191
|
def main(args):
|
|
@@ -177,6 +195,7 @@ def main(args):
|
|
|
177
195
|
decide_prefix = lambda f: utils.fileio.splitext(os.path.basename(f))[0] + "_refined"
|
|
178
196
|
if args.model:
|
|
179
197
|
params = refmac_keywords.parse_keywords(keywords)
|
|
198
|
+
params["write_trajectory"] = args.write_trajectory
|
|
180
199
|
if not args.output_prefix:
|
|
181
200
|
args.output_prefix = decide_prefix(args.model)
|
|
182
201
|
if args.ligand:
|