servalcat 0.4.72__cp312-cp312-macosx_11_0_arm64.whl → 0.4.99__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 CHANGED
@@ -6,5 +6,5 @@ This software is released under the
6
6
  Mozilla Public License, version 2.0; see LICENSE.
7
7
  """
8
8
 
9
- __version__ = '0.4.72'
10
- __date__ = '2024-06-06'
9
+ __version__ = '0.4.99'
10
+ __date__ = '2024-12-04'
Binary file
@@ -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
- self.geom = ext.Geometry(self.st, self.atom_pos, monlib.ener_lib)
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 = 1.
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
- self.check_chemtypes(os.path.join(monlib.path(), "ener_lib.cif"), topo)
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
- peratom = self.geom.reporting.per_atom_score(len(self.atoms), self.use_nucleus, "mean")
180
- df = pandas.DataFrame(peratom)
181
- df.insert(0, "atom", [str(self.lookup[x]) for x in self.atoms])
182
- df = df[df["total"] >= self.outlier_sigmas["per_atom"]]
183
- if show_outliers and len(df.index) > 0:
184
- df.sort_values("total", ascending=False, inplace=True)
185
- ret["outliers"]["per_atom"] = df
186
- logger.writeln(" *** Per-atom violations (Z >= {}) ***\n".format(self.outlier_sigmas["per_atom"]))
187
- logger.writeln(df.to_string(float_format="{:.2f}".format, index=False) + "\n")
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].name = "0"
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 = numpy.array(a.as_mat33())
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].name = str(i+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()
@@ -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
- monlib = utils.restraints.load_monomer_library(st, monomer_dir=monomer_dir, # monlib is needed for ener_lib
95
- cif_files=[cif_in],
96
- stop_for_unknowns=True)
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", style=gemmi.cif.Style.Aligned)
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
- monlib = utils.restraints.load_monomer_library(st, monomer_dir=monomer_dir,
150
- cif_files=cif_files,
151
- stop_for_unknowns=True,
152
- params=params)
153
- utils.restraints.find_and_fix_links(st, monlib, add_found=find_links) # should remove unknown id here?
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: