servalcat 0.4.88__cp38-cp38-macosx_11_0_arm64.whl → 0.4.99__cp38-cp38-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-38-darwin.so +0 -0
- servalcat/refine/refine.py +125 -42
- servalcat/refine/refine_geom.py +24 -11
- servalcat/refine/refine_spa.py +51 -30
- servalcat/refine/refine_xtal.py +20 -7
- servalcat/refine/spa.py +12 -4
- servalcat/refine/xtal.py +11 -8
- servalcat/refmac/refmac_wrapper.py +3 -11
- servalcat/spa/fofc.py +9 -3
- servalcat/spa/fsc.py +8 -10
- servalcat/spa/run_refmac.py +16 -11
- servalcat/spa/translate.py +2 -2
- servalcat/utils/commands.py +154 -4
- servalcat/utils/fileio.py +15 -8
- servalcat/utils/hkl.py +24 -22
- servalcat/utils/logger.py +25 -1
- servalcat/utils/maps.py +2 -2
- servalcat/utils/model.py +10 -10
- servalcat/utils/refmac.py +1 -1
- servalcat/utils/restraints.py +27 -28
- servalcat/utils/symmetry.py +5 -5
- servalcat/xtal/french_wilson.py +7 -5
- servalcat/xtal/sigmaa.py +52 -30
- servalcat/xtal/twin.py +12 -6
- {servalcat-0.4.88.dist-info → servalcat-0.4.99.dist-info}/METADATA +4 -4
- servalcat-0.4.99.dist-info/RECORD +45 -0
- {servalcat-0.4.88.dist-info → servalcat-0.4.99.dist-info}/WHEEL +1 -1
- servalcat-0.4.88.dist-info/RECORD +0 -45
- {servalcat-0.4.88.dist-info → servalcat-0.4.99.dist-info}/entry_points.txt +0 -0
- {servalcat-0.4.88.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
|
|
@@ -53,7 +53,7 @@ class Geom:
|
|
|
53
53
|
n_sym = len(images) + 1
|
|
54
54
|
self.geom.specials.append(ext.Geometry.Special(atom, matp, mata, n_sym))
|
|
55
55
|
self.adpr_w = adpr_w
|
|
56
|
-
self.occr_w =
|
|
56
|
+
self.occr_w = occr_w
|
|
57
57
|
self.unrestrained = unrestrained
|
|
58
58
|
if shake_rms > 0:
|
|
59
59
|
numpy.random.seed(0)
|
|
@@ -225,6 +225,26 @@ def write_stats_json_safe(stats, json_out):
|
|
|
225
225
|
logger.writeln(f"Refinement statistics saved: {json_out}")
|
|
226
226
|
# write_stats_json_safe()
|
|
227
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
|
+
|
|
228
248
|
class GroupOccupancy:
|
|
229
249
|
# TODO max may not be one. should check multiplicity
|
|
230
250
|
def __init__(self, st, params):
|
|
@@ -293,6 +313,7 @@ class GroupOccupancy:
|
|
|
293
313
|
vals = []
|
|
294
314
|
for _, atoms in self.groups:
|
|
295
315
|
occ = numpy.mean([a.occ for a in atoms])
|
|
316
|
+
occ = min(1, max(1e-3, occ))
|
|
296
317
|
vals.append(occ)
|
|
297
318
|
for is_comp, idxes in self.consts:
|
|
298
319
|
sum_occ = sum(vals[i] for i in idxes)
|
|
@@ -311,6 +332,7 @@ class GroupOccupancy:
|
|
|
311
332
|
for p, (_, atoms) in zip(x, self.groups):
|
|
312
333
|
for a in atoms:
|
|
313
334
|
a.occ = p
|
|
335
|
+
#a.occ = max(1, min(1e-3, p))
|
|
314
336
|
|
|
315
337
|
def target(self, x, ll, ls, u):
|
|
316
338
|
self.set_x(x)
|
|
@@ -425,7 +447,7 @@ class Refine:
|
|
|
425
447
|
self.geom.set_h_parents()
|
|
426
448
|
if params and params.get("write_trajectory"):
|
|
427
449
|
self.st_traj = self.st.clone()
|
|
428
|
-
self.st_traj[-1].
|
|
450
|
+
self.st_traj[-1].num = 0
|
|
429
451
|
assert self.geom.group_occ.groups or self.n_params() > 0
|
|
430
452
|
# __init__()
|
|
431
453
|
|
|
@@ -442,6 +464,9 @@ class Refine:
|
|
|
442
464
|
logger.writeln(" sigmas: {}".format(" ".join("{:.2f}".format(x) for x in g.adpr_kl_sigs)))
|
|
443
465
|
else:
|
|
444
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))
|
|
445
470
|
|
|
446
471
|
def scale_shifts(self, dx, scale):
|
|
447
472
|
n_atoms = self.geom.n_refine_atoms
|
|
@@ -525,7 +550,7 @@ class Refine:
|
|
|
525
550
|
elif self.adp_mode == 2:
|
|
526
551
|
a = x[offset_b + 6 * j: offset_b + 6 * (j+1)]
|
|
527
552
|
a = gemmi.SMat33d(*a)
|
|
528
|
-
M =
|
|
553
|
+
M = a.as_mat33().array
|
|
529
554
|
v, Q = numpy.linalg.eigh(M) # eig() may return complex due to numerical precision?
|
|
530
555
|
v = numpy.maximum(v, 0.5) # avoid NPD with minimum B = 0.5
|
|
531
556
|
M2 = Q.dot(numpy.diag(v)).dot(Q.T)
|
|
@@ -736,7 +761,7 @@ class Refine:
|
|
|
736
761
|
weight /= 1.1
|
|
737
762
|
if self.st_traj is not None:
|
|
738
763
|
self.st_traj.add_model(self.st[0])
|
|
739
|
-
self.st_traj[-1].
|
|
764
|
+
self.st_traj[-1].num = len(self.st_traj)
|
|
740
765
|
if stats_json_out:
|
|
741
766
|
write_stats_json_safe(stats, stats_json_out)
|
|
742
767
|
|
|
@@ -781,43 +806,101 @@ class Refine:
|
|
|
781
806
|
lstr = utils.make_loggraph_str(df, "stats vs cycle", forplot,
|
|
782
807
|
float_format="{:.4f}".format)
|
|
783
808
|
logger.writeln(lstr)
|
|
784
|
-
self.update_meta(stats[-1])
|
|
785
809
|
return stats
|
|
786
810
|
|
|
787
|
-
def update_meta(self, stats):
|
|
788
|
-
# TODO write stats. probably geom.reporting.get_summary_table should return with _refine_ls_restr.type names
|
|
789
|
-
# should remove st.mod_residues?
|
|
790
|
-
self.st.helices.clear()
|
|
791
|
-
self.st.sheets.clear()
|
|
792
|
-
raw_remarks = [f'REMARK 3',
|
|
793
|
-
f'REMARK 3 REFINEMENT.',
|
|
794
|
-
f'REMARK 3 PROGRAM : SERVALCAT {servalcat.__version__}',
|
|
795
|
-
f'REMARK 3 AUTHORS : YAMASHITA,MURSHUDOV',
|
|
796
|
-
f'REMARK 3',
|
|
797
|
-
]
|
|
798
|
-
si = gemmi.SoftwareItem()
|
|
799
|
-
si.classification = gemmi.SoftwareItem.Classification.Refinement
|
|
800
|
-
si.name = "Servalcat"
|
|
801
|
-
si.version = servalcat.__version__
|
|
802
|
-
si.date = servalcat.__date__
|
|
803
|
-
self.st.meta.software = [si]
|
|
804
|
-
|
|
805
|
-
ri = gemmi.RefinementInfo()
|
|
806
|
-
if "geom" in stats:
|
|
807
|
-
restr_stats = []
|
|
808
|
-
raw_remarks.append("REMARK 3 RMS DEVIATIONS FROM IDEAL VALUES COUNT RMS WEIGHT")
|
|
809
|
-
for k, n, l, pl in (("r.m.s.d.", "Bond distances, non H", "s_bond_nonh_d", "BOND LENGTHS REFINED ATOMS (A)"),
|
|
810
|
-
("r.m.s.d.", "Bond angles, non H", "s_angle_nonh_d", "BOND ANGLES REFINED ATOMS (DEGREES)")):
|
|
811
|
-
if k in stats["geom"]["summary"] and n in stats["geom"]["summary"][k]:
|
|
812
|
-
rr = gemmi.RefinementInfo.Restr(l)
|
|
813
|
-
rr.dev_ideal = stats["geom"]["summary"][k].get(n)
|
|
814
|
-
rr.count = stats["geom"]["summary"]["N restraints"].get(n)
|
|
815
|
-
rr.weight = stats["geom"]["summary"]["Mn(sigma)"].get(n)
|
|
816
|
-
restr_stats.append(rr)
|
|
817
|
-
raw_remarks.append(f"REMARK 3 {pl}:{rr.count:6d} ;{rr.dev_ideal:6.3f} ;{rr.weight:6.3f}")
|
|
818
|
-
ri.restr_stats = restr_stats
|
|
819
|
-
raw_remarks.append("REMARK 3")
|
|
820
|
-
self.st.meta.refinement = [ri]
|
|
821
|
-
self.st.raw_remarks = raw_remarks
|
|
822
|
-
|
|
823
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):
|
|
@@ -93,9 +93,14 @@ def refine_and_update_dictionary(cif_in, monomer_dir, output_prefix, randomize=0
|
|
|
93
93
|
if len(st) > 0: break
|
|
94
94
|
else:
|
|
95
95
|
raise SystemExit("No model in the cif file")
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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))
|
|
99
104
|
all_stats = []
|
|
100
105
|
for i_macro in 0, 1:
|
|
101
106
|
try:
|
|
@@ -121,16 +126,19 @@ def refine_and_update_dictionary(cif_in, monomer_dir, output_prefix, randomize=0
|
|
|
121
126
|
for row in block.find("_chem_comp_atom.", ["atom_id", "?x", "?y", "?z",
|
|
122
127
|
"?pdbx_model_Cartn_x_ideal",
|
|
123
128
|
"?pdbx_model_Cartn_y_ideal",
|
|
124
|
-
"?pdbx_model_Cartn_z_ideal"
|
|
129
|
+
"?pdbx_model_Cartn_z_ideal",
|
|
130
|
+
"?model_Cartn_x", "?model_Cartn_y", "?model_Cartn_z"]):
|
|
125
131
|
p = pos[row.str(0)]
|
|
126
132
|
for i in range(3):
|
|
127
133
|
if row.has(i+1):
|
|
128
134
|
row[i+1] = "{:.3f}".format(p[i])
|
|
129
135
|
if row.has(i+4):
|
|
130
136
|
row[i+4] = "{:.3f}".format(p[i])
|
|
137
|
+
if row.has(i+7):
|
|
138
|
+
row[i+7] = "{:.3f}".format(p[i])
|
|
131
139
|
# add description
|
|
132
140
|
add_program_info_to_dictionary(block, st[0][0][0].name)
|
|
133
|
-
doc.write_file(output_prefix + "_updated.cif",
|
|
141
|
+
doc.write_file(output_prefix + "_updated.cif", options=gemmi.cif.Style.Aligned)
|
|
134
142
|
logger.writeln("Updated dictionary saved: {}".format(output_prefix + "_updated.cif"))
|
|
135
143
|
with open(output_prefix + "_stats.json", "w") as ofs:
|
|
136
144
|
json.dump([convert_stats_to_dicts(x) for x in all_stats],
|
|
@@ -147,11 +155,13 @@ def refine_geom(model_in, monomer_dir, cif_files, h_change, ncycle, output_prefi
|
|
|
147
155
|
logger.writeln("Take NCS constraints into account.")
|
|
148
156
|
st2.expand_ncs(gemmi.HowToNameCopiedChain.Dup, merge_dist=0)
|
|
149
157
|
utils.fileio.write_model(st2, file_name="input_expanded.pdb")
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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))
|
|
155
165
|
utils.restraints.find_and_fix_links(st, monlib, find_metal_links=find_links,
|
|
156
166
|
add_found=find_links) # should remove unknown id here?
|
|
157
167
|
try:
|
|
@@ -161,6 +171,8 @@ def refine_geom(model_in, monomer_dir, cif_files, h_change, ncycle, output_prefi
|
|
|
161
171
|
except RuntimeError as e:
|
|
162
172
|
raise SystemExit("Error: {}".format(e))
|
|
163
173
|
|
|
174
|
+
print_h_options(h_change, st[0].has_hydrogen(), refine_h=True, hout=True, geom_only=True)
|
|
175
|
+
|
|
164
176
|
if use_ncsr:
|
|
165
177
|
ncslist = utils.restraints.prepare_ncs_restraints(st)
|
|
166
178
|
else:
|
|
@@ -169,6 +181,7 @@ def refine_geom(model_in, monomer_dir, cif_files, h_change, ncycle, output_prefi
|
|
|
169
181
|
refiner = Refine(st, geom, params=params)
|
|
170
182
|
stats = refiner.run_cycles(ncycle,
|
|
171
183
|
stats_json_out=output_prefix + "_stats.json")
|
|
184
|
+
update_meta(st, stats[-1])
|
|
172
185
|
refiner.st.name = output_prefix
|
|
173
186
|
utils.fileio.write_model(refiner.st, output_prefix, pdb=True, cif=True)
|
|
174
187
|
if params["write_trajectory"]:
|
servalcat/refine/refine_spa.py
CHANGED
|
@@ -14,12 +14,12 @@ from servalcat import utils
|
|
|
14
14
|
from servalcat.spa.run_refmac import check_args, process_input, calc_fsc, calc_fofc
|
|
15
15
|
from servalcat.spa import fofc
|
|
16
16
|
from servalcat.refine import spa
|
|
17
|
-
from servalcat.refine.refine import Geom, Refine
|
|
17
|
+
from servalcat.refine.refine import Geom, Refine, update_meta, print_h_options
|
|
18
18
|
from servalcat.refmac import refmac_keywords
|
|
19
19
|
b_to_u = utils.model.b_to_u
|
|
20
20
|
|
|
21
21
|
def add_arguments(parser):
|
|
22
|
-
parser.description = "
|
|
22
|
+
parser.description = "program to refine cryo-EM SPA structures"
|
|
23
23
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
24
24
|
group.add_argument("--halfmaps", nargs=2, help="Input half map files")
|
|
25
25
|
group.add_argument("--map", help="Use this only if you really do not have half maps.")
|
|
@@ -68,7 +68,7 @@ def add_arguments(parser):
|
|
|
68
68
|
help="Jelly body only (experimental, may not be useful)")
|
|
69
69
|
utils.symmetry.add_symmetry_args(parser) # add --pg etc
|
|
70
70
|
parser.add_argument('--contacting_only', action="store_true", help="Filter out non-contacting strict NCS copies")
|
|
71
|
-
parser.add_argument('--ignore_symmetry',
|
|
71
|
+
parser.add_argument('--ignore_symmetry', action='store_true',
|
|
72
72
|
help='Ignore symmetry information (MTRIX/_struct_ncs_oper) in the model file')
|
|
73
73
|
parser.add_argument('--find_links', action='store_true',
|
|
74
74
|
help='Automatically add links')
|
|
@@ -94,6 +94,8 @@ def add_arguments(parser):
|
|
|
94
94
|
help='Bond rmsz range for weight adjustment (default: %(default)s)')
|
|
95
95
|
parser.add_argument('--adpr_weight', type=float, default=1.,
|
|
96
96
|
help="ADP restraint weight (default: %(default)f)")
|
|
97
|
+
parser.add_argument('--occr_weight', type=float, default=1.,
|
|
98
|
+
help="Occupancy restraint weight (default: %(default)f)")
|
|
97
99
|
parser.add_argument('--ncsr', action='store_true',
|
|
98
100
|
help='Use local NCS restraints')
|
|
99
101
|
parser.add_argument('--bfactor', type=float,
|
|
@@ -108,8 +110,9 @@ def add_arguments(parser):
|
|
|
108
110
|
parser.add_argument('--adp_restraint_exp_fac', type=float)
|
|
109
111
|
parser.add_argument('--adp_restraint_no_long_range', action='store_true')
|
|
110
112
|
parser.add_argument('--adp_restraint_mode', choices=["diff", "kldiv"], default="diff")
|
|
113
|
+
parser.add_argument('--unrestrained', action='store_true', help="No positional restraints")
|
|
111
114
|
parser.add_argument('--refine_h', action="store_true", help="Refine hydrogen against data (default: only restraints apply)")
|
|
112
|
-
parser.add_argument("--source", choices=["electron", "xray", "neutron"], default="electron")
|
|
115
|
+
parser.add_argument("-s", "--source", choices=["electron", "xray", "neutron"], default="electron")
|
|
113
116
|
parser.add_argument('-o','--output_prefix', default="refined")
|
|
114
117
|
parser.add_argument('--cross_validation', action='store_true',
|
|
115
118
|
help='Run cross validation. Only "throughout" mode is available (no "shake" mode)')
|
|
@@ -142,12 +145,23 @@ def main(args):
|
|
|
142
145
|
params["write_trajectory"] = args.write_trajectory
|
|
143
146
|
|
|
144
147
|
st = utils.fileio.read_structure(args.model)
|
|
145
|
-
|
|
146
|
-
monlib =
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
if args.unrestrained:
|
|
149
|
+
monlib = gemmi.MonLib()
|
|
150
|
+
topo = None
|
|
151
|
+
if args.hydrogen == "all":
|
|
152
|
+
logger.writeln("\nWARNING: in unrestrained refinement hydrogen atoms are not generated.\n")
|
|
153
|
+
args.hydrogen = "yes"
|
|
154
|
+
elif args.hydrogen == "no":
|
|
155
|
+
st.remove_hydrogens()
|
|
156
|
+
for i, cra in enumerate(st[0].all()):
|
|
157
|
+
cra.atom.serial = i + 1
|
|
158
|
+
else:
|
|
159
|
+
try:
|
|
160
|
+
monlib = utils.restraints.load_monomer_library(st, monomer_dir=args.monlib, cif_files=args.ligand,
|
|
161
|
+
stop_for_unknowns=not args.newligand_continue,
|
|
162
|
+
params=params)
|
|
163
|
+
except RuntimeError as e:
|
|
164
|
+
raise SystemExit("Error: {}".format(e))
|
|
151
165
|
if not args.keep_entities:
|
|
152
166
|
utils.model.setup_entities(st, clear=True, force_subchain_names=True, overwrite_entity_type=True)
|
|
153
167
|
if not args.keep_charges:
|
|
@@ -186,6 +200,8 @@ def main(args):
|
|
|
186
200
|
except RuntimeError as e:
|
|
187
201
|
raise SystemExit("Error: {}".format(e))
|
|
188
202
|
|
|
203
|
+
print_h_options(h_change, st[0].has_hydrogen(), args.refine_h, args.hout, geom_only=False)
|
|
204
|
+
|
|
189
205
|
# initialize ADP
|
|
190
206
|
utils.model.reset_adp(st[0], args.bfactor, args.adp)
|
|
191
207
|
|
|
@@ -214,8 +230,8 @@ def main(args):
|
|
|
214
230
|
ncslist = utils.restraints.prepare_ncs_restraints(st)
|
|
215
231
|
else:
|
|
216
232
|
ncslist = False
|
|
217
|
-
geom = Geom(st, topo, monlib, shake_rms=args.randomize, adpr_w=args.adpr_weight,
|
|
218
|
-
params=params, unrestrained=args.jellyonly,
|
|
233
|
+
geom = Geom(st, topo, monlib, shake_rms=args.randomize, adpr_w=args.adpr_weight, occr_w=args.occr_weight,
|
|
234
|
+
params=params, unrestrained=args.unrestrained or args.jellyonly,
|
|
219
235
|
ncslist=ncslist)
|
|
220
236
|
ll = spa.LL_SPA(hkldata, st, monlib,
|
|
221
237
|
lab_obs="F_map1" if args.cross_validation else "FP",
|
|
@@ -224,6 +240,7 @@ def main(args):
|
|
|
224
240
|
refine_xyz=not args.fix_xyz,
|
|
225
241
|
adp_mode=dict(fix=0, iso=1, aniso=2)[args.adp],
|
|
226
242
|
refine_h=args.refine_h,
|
|
243
|
+
unrestrained=args.unrestrained,
|
|
227
244
|
params=params,
|
|
228
245
|
refine_occ=args.refine_all_occ)
|
|
229
246
|
|
|
@@ -247,12 +264,8 @@ def main(args):
|
|
|
247
264
|
refiner.st.cell = maps[0][0].unit_cell
|
|
248
265
|
refiner.st.setup_cell_images()
|
|
249
266
|
|
|
250
|
-
refiner.st.name = args.output_prefix
|
|
251
|
-
utils.fileio.write_model(refiner.st, args.output_prefix, pdb=True, cif=True, hout=args.hout)
|
|
252
267
|
if params["write_trajectory"]:
|
|
253
268
|
utils.fileio.write_model(refiner.st_traj, args.output_prefix + "_traj", cif=True)
|
|
254
|
-
if args.hklin:
|
|
255
|
-
return
|
|
256
269
|
|
|
257
270
|
# Expand sym here
|
|
258
271
|
st_expanded = refiner.st.clone()
|
|
@@ -261,20 +274,28 @@ def main(args):
|
|
|
261
274
|
utils.fileio.write_model(st_expanded, args.output_prefix+"_expanded", pdb=True, cif=True, hout=args.hout)
|
|
262
275
|
|
|
263
276
|
# Calc FSC
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
277
|
+
if args.hklin: # cannot update a mask
|
|
278
|
+
stats_for_meta = stats[-1]
|
|
279
|
+
else:
|
|
280
|
+
mask = utils.fileio.read_ccp4_map(args.mask)[0] if args.mask else None
|
|
281
|
+
fscavg_text, stats2 = calc_fsc(st_expanded, args.output_prefix, maps,
|
|
282
|
+
args.resolution, mask=mask, mask_radius=args.mask_radius if not args.no_mask else None,
|
|
283
|
+
soft_edge=args.mask_soft_edge,
|
|
284
|
+
b_before_mask=args.b_before_mask,
|
|
285
|
+
no_sharpen_before_mask=args.no_sharpen_before_mask,
|
|
286
|
+
make_hydrogen="yes", # no change needed in the model
|
|
287
|
+
monlib=monlib,
|
|
288
|
+
blur=args.blur,
|
|
289
|
+
d_min_fsc=args.fsc_resolution,
|
|
290
|
+
cross_validation=args.cross_validation,
|
|
291
|
+
cross_validation_method=args.cross_validation_method
|
|
292
|
+
)
|
|
293
|
+
stats_for_meta = {"geom": stats[-1]["geom"], "data": stats2}
|
|
294
|
+
update_meta(refiner.st, stats_for_meta, ll)
|
|
295
|
+
refiner.st.name = args.output_prefix
|
|
296
|
+
utils.fileio.write_model(refiner.st, args.output_prefix, pdb=True, cif=True, hout=args.hout)
|
|
297
|
+
if args.hklin:
|
|
298
|
+
return
|
|
278
299
|
# Calc Fo-Fc (and updated) maps
|
|
279
300
|
diffmap_prefix = "{}_diffmap".format(args.output_prefix)
|
|
280
301
|
calc_fofc(refiner.st, st_expanded, maps, monlib, ".mmcif", args, diffmap_prefix=diffmap_prefix)
|
servalcat/refine/refine_xtal.py
CHANGED
|
@@ -16,13 +16,13 @@ from servalcat.utils import logger
|
|
|
16
16
|
from servalcat import utils
|
|
17
17
|
from servalcat.xtal.sigmaa import decide_mtz_labels, process_input, calculate_maps, calculate_maps_int, calculate_maps_twin
|
|
18
18
|
from servalcat.refine.xtal import LL_Xtal
|
|
19
|
-
from servalcat.refine.refine import Geom, Refine
|
|
19
|
+
from servalcat.refine.refine import Geom, Refine, update_meta, print_h_options
|
|
20
20
|
from servalcat.refmac import refmac_keywords
|
|
21
21
|
from servalcat import ext
|
|
22
22
|
b_to_u = utils.model.b_to_u
|
|
23
23
|
|
|
24
24
|
def add_arguments(parser):
|
|
25
|
-
parser.description = "
|
|
25
|
+
parser.description = "program to refine crystallographic structures"
|
|
26
26
|
parser.add_argument("--hklin", required=True)
|
|
27
27
|
parser.add_argument("-d", '--d_min', type=float)
|
|
28
28
|
parser.add_argument('--d_max', type=float)
|
|
@@ -64,10 +64,14 @@ def add_arguments(parser):
|
|
|
64
64
|
help="refinement weight (default: auto)")
|
|
65
65
|
parser.add_argument('--no_weight_adjust', action='store_true',
|
|
66
66
|
help='Do not adjust weight during refinement')
|
|
67
|
+
parser.add_argument('--target_bond_rmsz_range', nargs=2, type=float, default=[0.5, 1.],
|
|
68
|
+
help='Bond rmsz range for weight adjustment (default: %(default)s)')
|
|
67
69
|
parser.add_argument('--ncsr', action='store_true',
|
|
68
70
|
help='Use local NCS restraints')
|
|
69
71
|
parser.add_argument('--adpr_weight', type=float, default=1.,
|
|
70
72
|
help="ADP restraint weight (default: %(default)f)")
|
|
73
|
+
parser.add_argument('--occr_weight', type=float, default=1.,
|
|
74
|
+
help="Occupancy restraint weight (default: %(default)f)")
|
|
71
75
|
parser.add_argument('--bfactor', type=float,
|
|
72
76
|
help="reset all atomic B values to specified value")
|
|
73
77
|
parser.add_argument('--fix_xyz', action="store_true")
|
|
@@ -88,6 +92,8 @@ def add_arguments(parser):
|
|
|
88
92
|
help="Use work reflections in ML parameter estimates")
|
|
89
93
|
parser.add_argument('--keep_charges', action='store_true',
|
|
90
94
|
help="Use scattering factor for charged atoms. Use it with care.")
|
|
95
|
+
parser.add_argument("--keep_entities", action='store_true',
|
|
96
|
+
help="Do not override entities")
|
|
91
97
|
parser.add_argument('--allow_unusual_occupancies', action="store_true", help="Allow negative or more than one occupancies")
|
|
92
98
|
parser.add_argument('-o','--output_prefix')
|
|
93
99
|
parser.add_argument("--write_trajectory", action='store_true',
|
|
@@ -159,10 +165,12 @@ def main(args):
|
|
|
159
165
|
if args.unrestrained:
|
|
160
166
|
monlib = gemmi.MonLib()
|
|
161
167
|
topo = None
|
|
168
|
+
h_change = gemmi.HydrogenChange.NoChange
|
|
162
169
|
if args.hydrogen == "all":
|
|
163
|
-
logger.writeln("
|
|
170
|
+
logger.writeln("\nWARNING: in unrestrained refinement hydrogen atoms are not generated.\n")
|
|
164
171
|
elif args.hydrogen == "no":
|
|
165
172
|
st.remove_hydrogens()
|
|
173
|
+
h_change = gemmi.HydrogenChange.Remove
|
|
166
174
|
for i, cra in enumerate(st[0].all()):
|
|
167
175
|
cra.atom.serial = i + 1
|
|
168
176
|
else:
|
|
@@ -172,7 +180,8 @@ def main(args):
|
|
|
172
180
|
params=params)
|
|
173
181
|
except RuntimeError as e:
|
|
174
182
|
raise SystemExit("Error: {}".format(e))
|
|
175
|
-
|
|
183
|
+
if not args.keep_entities:
|
|
184
|
+
utils.model.setup_entities(st, clear=True, force_subchain_names=True, overwrite_entity_type=True)
|
|
176
185
|
utils.restraints.find_and_fix_links(st, monlib, find_metal_links=args.find_links,
|
|
177
186
|
add_found=args.find_links)
|
|
178
187
|
h_change = {"all":gemmi.HydrogenChange.ReAddKnown,
|
|
@@ -185,6 +194,8 @@ def main(args):
|
|
|
185
194
|
except RuntimeError as e:
|
|
186
195
|
raise SystemExit("Error: {}".format(e))
|
|
187
196
|
|
|
197
|
+
print_h_options(h_change, st[0].has_hydrogen(), args.refine_h, args.hout, geom_only=False)
|
|
198
|
+
|
|
188
199
|
# initialize ADP
|
|
189
200
|
utils.model.reset_adp(st[0], args.bfactor, args.adp)
|
|
190
201
|
|
|
@@ -199,7 +210,7 @@ def main(args):
|
|
|
199
210
|
ncslist = utils.restraints.prepare_ncs_restraints(st)
|
|
200
211
|
else:
|
|
201
212
|
ncslist = False
|
|
202
|
-
geom = Geom(st, topo, monlib, shake_rms=args.randomize, adpr_w=args.adpr_weight, params=params,
|
|
213
|
+
geom = Geom(st, topo, monlib, shake_rms=args.randomize, adpr_w=args.adpr_weight, occr_w=args.occr_weight, params=params,
|
|
203
214
|
unrestrained=args.unrestrained or args.jellyonly, use_nucleus=(args.source=="neutron"),
|
|
204
215
|
ncslist=ncslist)
|
|
205
216
|
geom.geom.angle_von_mises = args.vonmises
|
|
@@ -225,7 +236,9 @@ def main(args):
|
|
|
225
236
|
|
|
226
237
|
stats = refiner.run_cycles(args.ncycle, weight=args.weight,
|
|
227
238
|
weight_adjust=not args.no_weight_adjust,
|
|
239
|
+
weight_adjust_bond_rmsz_range=args.target_bond_rmsz_range,
|
|
228
240
|
stats_json_out=args.output_prefix + "_stats.json")
|
|
241
|
+
update_meta(st, stats[-1], ll)
|
|
229
242
|
refiner.st.name = args.output_prefix
|
|
230
243
|
utils.fileio.write_model(refiner.st, args.output_prefix, pdb=True, cif=True, hout=args.hout)
|
|
231
244
|
if params["write_trajectory"]:
|
|
@@ -244,7 +257,7 @@ def main(args):
|
|
|
244
257
|
|
|
245
258
|
# Write mtz file
|
|
246
259
|
if ll.twin_data:
|
|
247
|
-
labs = ["F_est"]
|
|
260
|
+
labs = ["F_est", "F_exp", "FOM"]
|
|
248
261
|
elif is_int:
|
|
249
262
|
labs = ["I", "SIGI", "FOM"]
|
|
250
263
|
else:
|
|
@@ -258,7 +271,7 @@ def main(args):
|
|
|
258
271
|
labs.append("FREE")
|
|
259
272
|
labs += ll.D_labs + ["S"] # for debugging, for now
|
|
260
273
|
mtz_out = args.output_prefix+".mtz"
|
|
261
|
-
hkldata.write_mtz(mtz_out, labs=labs, types={"FOM": "W", "FP":"F", "SIGFP":"Q", "I":"J", "SIGI":"Q", "F_est": "F"})
|
|
274
|
+
hkldata.write_mtz(mtz_out, labs=labs, types={"FOM": "W", "FP":"F", "SIGFP":"Q", "I":"J", "SIGI":"Q", "F_est": "F", "F_exp": "F"})
|
|
262
275
|
|
|
263
276
|
# main()
|
|
264
277
|
|
servalcat/refine/spa.py
CHANGED
|
@@ -38,8 +38,14 @@ class LL_SPA:
|
|
|
38
38
|
self.lab_obs = lab_obs
|
|
39
39
|
self.st = st
|
|
40
40
|
self.monlib = monlib
|
|
41
|
-
self.
|
|
41
|
+
self.d_min_max = hkldata.d_min_max()
|
|
42
42
|
self.ll = None
|
|
43
|
+
self.b_aniso = None
|
|
44
|
+
|
|
45
|
+
def refine_id(self):
|
|
46
|
+
if self.source == "electron":
|
|
47
|
+
return "ELECTRON MICROSCOPY"
|
|
48
|
+
return "NON-EM SPA" # does not happen, I guess
|
|
43
49
|
|
|
44
50
|
def update_ml_params(self):
|
|
45
51
|
# FIXME make sure D > 0
|
|
@@ -52,7 +58,7 @@ class LL_SPA:
|
|
|
52
58
|
else:
|
|
53
59
|
st = self.st
|
|
54
60
|
|
|
55
|
-
self.hkldata.df["FC"] = utils.model.calc_fc_fft(st, self.
|
|
61
|
+
self.hkldata.df["FC"] = utils.model.calc_fc_fft(st, self.d_min_max[0] - 1e-6,
|
|
56
62
|
monlib=self.monlib,
|
|
57
63
|
source=self.source,
|
|
58
64
|
mott_bethe=self.mott_bethe,
|
|
@@ -92,15 +98,17 @@ class LL_SPA:
|
|
|
92
98
|
stats = fsc.calc_fsc_all(self.hkldata, labs_fc=["FC"], lab_f=self.lab_obs)
|
|
93
99
|
fsca = fsc.fsc_average(stats.ncoeffs, stats.fsc_FC_full)
|
|
94
100
|
logger.writeln("FSCaverage = {:.4f}".format(fsca))
|
|
101
|
+
ret = {"summary": {"FSCaverage": fsca, "-LL": self.calc_target()}}
|
|
95
102
|
# XXX in fsc object, _full is misleading - it's not full in cross validation mode
|
|
96
103
|
if "D" in self.hkldata.binned_df and "S" in self.hkldata.binned_df:
|
|
97
104
|
stats[["D", "S"]] = self.hkldata.binned_df[["D", "S"]]
|
|
98
|
-
|
|
105
|
+
ret["bin_stats"] = stats
|
|
106
|
+
return ret
|
|
99
107
|
|
|
100
108
|
def calc_grad(self, atom_pos, refine_xyz, adp_mode, refine_occ, refine_h, specs):
|
|
101
109
|
dll_dab = numpy.empty_like(self.hkldata.df[self.lab_obs])
|
|
102
110
|
d2ll_dab2 = numpy.zeros(len(self.hkldata.df.index))
|
|
103
|
-
blur = utils.model.determine_blur_for_dencalc(self.st, self.
|
|
111
|
+
blur = utils.model.determine_blur_for_dencalc(self.st, self.d_min_max[0] / 3) # TODO need more work
|
|
104
112
|
logger.writeln("blur for deriv= {:.2f}".format(blur))
|
|
105
113
|
for i_bin, idxes in self.hkldata.binned():
|
|
106
114
|
D = self.hkldata.binned_df.D[i_bin]
|