servalcat 0.4.88__cp311-cp311-win_amd64.whl → 0.4.99__cp311-cp311-win_amd64.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/refine/xtal.py CHANGED
@@ -30,7 +30,7 @@ class LL_Xtal:
30
30
  self.free = free
31
31
  self.st = st
32
32
  self.monlib = monlib
33
- self.d_min = hkldata.d_min_max()[0]
33
+ self.d_min_max = hkldata.d_min_max()
34
34
  self.fc_labs = ["FC0"]
35
35
  self.use_solvent = use_solvent
36
36
  if use_solvent:
@@ -54,6 +54,9 @@ class LL_Xtal:
54
54
  logger.writeln("will use {} reflections for parameter estimation".format(self.use_in_est))
55
55
  logger.writeln("will use {} reflections for refinement".format(self.use_in_target))
56
56
 
57
+ def refine_id(self):
58
+ return {"xray": "X-RAY", "electron": "ELECTRON", "neutron": "NEUTRON"}.get(self.source, "") + " DIFFRACTION"
59
+
57
60
  def update_ml_params(self):
58
61
  self.b_aniso = sigmaa.determine_ml_params(self.hkldata, self.is_int, self.fc_labs, self.D_labs, self.b_aniso,
59
62
  self.centric_and_selections, use=self.use_in_est,
@@ -63,7 +66,7 @@ class LL_Xtal:
63
66
  # self.centric_and_selections)
64
67
  def update_fc(self):
65
68
  sigmaa.update_fc(st_list=[self.st], fc_labs=self.fc_labs,
66
- d_min=self.d_min, monlib=self.monlib,
69
+ d_min=self.d_min_max[0], monlib=self.monlib,
67
70
  source=self.source, mott_bethe=self.mott_bethe,
68
71
  hkldata=self.hkldata, twin_data=self.twin_data)
69
72
 
@@ -78,13 +81,13 @@ class LL_Xtal:
78
81
  for sel in self.centric_and_selections[i_bin]])
79
82
  mask = numpy.empty(len(self.hkldata.df.index)) * numpy.nan
80
83
  mask[idxes] = 1 / self.hkldata.debye_waller_factors(b_cart=self.b_aniso)[idxes]**2
81
- self.twin_data.est_f_true(self.hkldata.df.I * mask,
82
- self.hkldata.df.SIGI * mask)
84
+ self.twin_data.est_f_true(self.hkldata.df.I.to_numpy() * mask,
85
+ self.hkldata.df.SIGI.to_numpy() * mask)
83
86
 
84
87
  def overall_scale(self, min_b=0.1):
85
88
  miller_array = self.twin_data.asu if self.twin_data else self.hkldata.miller_array()
86
89
  if self.use_solvent:
87
- Fmask = sigmaa.calc_Fmask(self.st, self.d_min, miller_array)
90
+ Fmask = sigmaa.calc_Fmask(self.st, self.d_min_max[0], miller_array)
88
91
  if self.twin_data:
89
92
  fc_sum = self.twin_data.f_calc[:,:-1].sum(axis=1)
90
93
  else:
@@ -177,7 +180,7 @@ class LL_Xtal:
177
180
  return ret
178
181
 
179
182
  def calc_grad(self, atom_pos, refine_xyz, adp_mode, refine_occ, refine_h, specs=None):
180
- blur = utils.model.determine_blur_for_dencalc(self.st, self.d_min / 3) # TODO need more work
183
+ blur = utils.model.determine_blur_for_dencalc(self.st, self.d_min_max[0] / 3) # TODO need more work
181
184
  logger.writeln("blur for deriv= {:.2f}".format(blur))
182
185
  if self.twin_data:
183
186
  dll_dab, d2ll_dab2 = self.twin_data.ll_der_fc0()
@@ -206,8 +209,8 @@ class LL_Xtal:
206
209
  to = Io[cidxes] / sigIo[cidxes] - sigIo[cidxes] / (c+1) / k_ani[cidxes]**2 / S / epsilon
207
210
  tf = k_ani[cidxes] * Fc_abs / numpy.sqrt(sigIo[cidxes])
208
211
  sig1 = k_ani[cidxes]**2 * epsilon * S / sigIo[cidxes]
209
- k_num = 0.5 if c == 0 else 0. # acentric:0.5, centric: 0.
210
- r = ext.integ_J_ratio(k_num, k_num - 0.5, True, to, tf, sig1, c+1,
212
+ k_num = numpy.repeat(0.5 if c == 0 else 0., to.size) # acentric:0.5, centric: 0.
213
+ r = ext.integ_J_ratio(k_num, k_num - 0.5, True, to, tf, sig1, numpy.repeat(c+1, to.size),
211
214
  integr.exp2_threshold, integr.h, integr.N, integr.ewmax)
212
215
  r *= numpy.sqrt(sigIo[cidxes]) / k_ani[cidxes]
213
216
  g = (2-c) * (Fc_abs - r) / epsilon / S * Ds[:,0]
@@ -11,7 +11,6 @@ import numpy
11
11
  import json
12
12
  import os
13
13
  import sys
14
- import io
15
14
  import tempfile
16
15
  import subprocess
17
16
  import argparse
@@ -126,8 +125,7 @@ def prepare_crd(st, crdout, ligand, make, monlib_path=None, h_pos="elec",
126
125
  max_seq_num = max([max(res.seqid.num for res in chain) for model in st for chain in model])
127
126
  if max_seq_num > 9999:
128
127
  logger.writeln("Max residue number ({}) exceeds 9999. Needs workaround.".format(max_seq_num))
129
- sio = io.StringIO()
130
- topo = gemmi.prepare_topology(st, monlib, warnings=sio, ignore_unknown_links=True)
128
+ topo = gemmi.prepare_topology(st, monlib, warnings=logger.silent(), ignore_unknown_links=True)
131
129
  refmac_fixes.fix_before_topology(st, topo,
132
130
  fix_microheterogeneity=False,
133
131
  fix_resimax=True,
@@ -135,8 +133,7 @@ def prepare_crd(st, crdout, ligand, make, monlib_path=None, h_pos="elec",
135
133
 
136
134
  if unre:
137
135
  # Refmac5 does not seem to do anything to hydrogen when unre regardless of "make hydr"
138
- sio = io.StringIO()
139
- topo = gemmi.prepare_topology(st, monlib, warnings=sio, ignore_unknown_links=True)
136
+ topo = gemmi.prepare_topology(st, monlib, warnings=logger.silent(), ignore_unknown_links=True)
140
137
  metal_kws = []
141
138
  else:
142
139
  if make.get("hydr") == "a": logger.writeln("(re)generating hydrogen atoms")
@@ -194,7 +191,7 @@ def prepare_crd(st, crdout, ligand, make, monlib_path=None, h_pos="elec",
194
191
  if st.name.lower() in block_names:
195
192
  st.name = st.name + str(i)
196
193
  doc = gemmi.prepare_refmac_crd(st, topo, monlib, h_change)
197
- doc.write_file(crdout, style=gemmi.cif.Style.NoBlankLines)
194
+ doc.write_file(crdout, options=gemmi.cif.Style.NoBlankLines)
198
195
  logger.writeln("crd file written: {}".format(crdout))
199
196
  return refmac_fixes, [x+"\n" for x in metal_kws]
200
197
  # prepare_crd()
@@ -276,11 +273,6 @@ def modify_output(pdbout, cifout, fixes, hout, cispeps, keep_original_output=Fal
276
273
  logger.writeln("This structure cannot be saved as an official PDB format. Using hybrid-36. Header part may be inaccurate.")
277
274
  if not hout:
278
275
  st.remove_hydrogens() # remove hydrogen from pdb, while kept in mmcif
279
- # Use short name in pdb
280
- st.shorten_ccd_codes()
281
- if st.shortened_ccd_codes:
282
- msg = " ".join("{}->{}".format(o,n) for o,n in st.shortened_ccd_codes)
283
- logger.writeln("Using shortened residue names in the output pdb file: " + msg)
284
276
  os.rename(pdbout, pdbout + suffix)
285
277
  utils.fileio.write_pdb(st, pdbout)
286
278
  if not keep_original_output:
servalcat/spa/fofc.py CHANGED
@@ -44,9 +44,11 @@ def add_arguments(parser):
44
44
  parser.add_argument("--monlib",
45
45
  help="Monomer library path. Default: $CLIBD_MON")
46
46
  parser.add_argument("--omit_proton", action='store_true',
47
- help="Omit proton from model in map calculation")
47
+ #help="Omit hydrogen proton (leaving electrons) from model in map calculation")
48
+ help=argparse.SUPPRESS)
48
49
  parser.add_argument("--omit_h_electron", action='store_true',
49
- help="Omit hydrogen electrons from model in map calculation")
50
+ #help="Omit hydrogen electrons (leaving protons) from model in map calculation")
51
+ help=argparse.SUPPRESS)
50
52
  parser.add_argument("--source", choices=["electron", "xray", "neutron"], default="electron")
51
53
  parser.add_argument('-o','--output_prefix', default="diffmap",
52
54
  help='output file name prefix (default: %(default)s)')
@@ -464,7 +466,11 @@ def main(args):
464
466
  logger.writeln("coot --script " + py_out)
465
467
  if mask is not None:
466
468
  logger.writeln("\nWant to list Fo-Fc map peaks? Try:")
467
- logger.writeln("servalcat util map_peaks --map {}_normalized_fofc.mrc --model {} --abs_level 4.0".format(args.output_prefix, args.model))
469
+ if omit_h_electron:
470
+ logger.writeln("servalcat util map_peaks --map {}_normalized_fofc_flipsign.mrc --model {} --abs_level 4.0".format(args.output_prefix, args.model))
471
+ else:
472
+ logger.writeln("servalcat util map_peaks --map {}_normalized_fofc.mrc --model {} --abs_level 4.0".format(args.output_prefix, args.model))
473
+
468
474
  # main()
469
475
 
470
476
  if __name__ == "__main__":
servalcat/spa/fsc.py CHANGED
@@ -20,13 +20,14 @@ def add_arguments(parser):
20
20
 
21
21
  parser.add_argument('--model',
22
22
  help="")
23
- parser.add_argument('--map',
23
+ group = parser.add_mutually_exclusive_group(required=True)
24
+ group.add_argument('--map',
24
25
  help='Input map file(s)')
25
- parser.add_argument('--mtz',
26
+ group.add_argument("--halfmaps", nargs=2)
27
+ group.add_argument('--mtz',
26
28
  help='Input mtz file.')
27
29
  parser.add_argument('--labin', nargs=2,
28
30
  help='label (F and PHI) for mtz')
29
- parser.add_argument("--halfmaps", nargs=2)
30
31
  parser.add_argument('--pixel_size', type=float,
31
32
  help='Override pixel size (A)')
32
33
  parser.add_argument('--mask', help='Mask file')
@@ -68,13 +69,10 @@ def write_loggraph(stats, labs_fc, log_out):
68
69
  model_labs1 = [l for l in stats if any(l.startswith("fsc_"+fc) for fc in labs_fc)]
69
70
  model_labs2 = [l for l in stats if any(l.startswith(("cc_"+fc, "mcos_"+fc)) for fc in labs_fc)]
70
71
  power_labs = [l for l in stats if l.startswith("power_")]
71
- half_labs1 = ["fsc_half_unmasked", "fsc_half_masked", "fsc_half_masked_rand", "fsc_half_masked_corrected"]
72
- half_labs2 = ["cc_half", "mcos_half"]
73
- if not all(l in stats for l in half_labs1):
74
- if "fsc_half" in stats:
75
- half_labs1 = ["fsc_half"]
76
- else:
77
- half_labs1 = []
72
+ half_labs1 = [l for l in ("fsc_half_unmasked", "fsc_half_masked", "fsc_half_masked_rand", "fsc_half_masked_corrected") if l in stats]
73
+ half_labs2 = [l for l in ("cc_half", "mcos_half") if l in stats]
74
+ if not half_labs1 and "fsc_half" in stats:
75
+ half_labs1 = ["fsc_half"]
78
76
 
79
77
  stats2 = stats.copy()
80
78
  stats2.insert(0, "bin", stats.index)
@@ -10,7 +10,6 @@ import gemmi
10
10
  import numpy
11
11
  import json
12
12
  import os
13
- import io
14
13
  import shutil
15
14
  import argparse
16
15
  from servalcat.utils import logger
@@ -58,7 +57,7 @@ def add_arguments(parser):
58
57
  help='Sharpening or blurring B')
59
58
  utils.symmetry.add_symmetry_args(parser) # add --pg etc
60
59
  parser.add_argument('--contacting_only', action="store_true", help="Filter out non-contacting NCS")
61
- parser.add_argument('--ignore_symmetry',
60
+ parser.add_argument('--ignore_symmetry', action='store_true',
62
61
  help='Ignore symmetry information (MTRIX/_struct_ncs_oper) in the model file')
63
62
  parser.add_argument('--find_links', action='store_true',
64
63
  help='Automatically add links')
@@ -150,6 +149,7 @@ def calc_fsc(st, output_prefix, maps, d_min, mask, mask_radius, soft_edge, b_bef
150
149
  assert st_sr is None
151
150
 
152
151
  logger.writeln("Calculating map-model FSC..")
152
+ ret = {"summary": {}}
153
153
 
154
154
  if d_min_fsc is None:
155
155
  d_min_fsc = utils.maps.nyquist_resolution(maps[0][0])
@@ -220,12 +220,17 @@ def calc_fsc(st, output_prefix, maps, d_min, mask, mask_radius, soft_edge, b_bef
220
220
  s.drop(columns=[x for x in s if x.startswith("fsc_FC") and x.endswith(("half1","half2"))], inplace=True)
221
221
 
222
222
  # FSCaverages
223
+ ret["summary"]["d_min"] = d_min
224
+ ret["summary"]["FSCaverage"] = spa.fsc.fsc_average(stats2.ncoeffs, stats2.fsc_model)
225
+ if cross_validation:
226
+ ret["summary"]["FSCaverage_half1"] = spa.fsc.fsc_average(stats2.ncoeffs, stats2.fsc_model_half1)
227
+ ret["summary"]["FSCaverage_half2"] = spa.fsc.fsc_average(stats2.ncoeffs, stats2.fsc_model_half2)
223
228
  fscavg_text = "Map-model FSCaverages (at {:.2f} A):\n".format(d_min)
224
- fscavg_text += " FSCaverage(full) = {: .4f}\n".format(spa.fsc.fsc_average(stats2.ncoeffs, stats2.fsc_model))
229
+ fscavg_text += " FSCaverage(full) = {: .4f}\n".format(ret["summary"]["FSCaverage"])
225
230
  if cross_validation:
226
231
  fscavg_text += "Cross-validated map-model FSCaverages:\n"
227
- fscavg_text += " FSCaverage(half1)= {: .4f}\n".format(spa.fsc.fsc_average(stats2.ncoeffs, stats2.fsc_model_half1))
228
- fscavg_text += " FSCaverage(half2)= {: .4f}\n".format(spa.fsc.fsc_average(stats2.ncoeffs, stats2.fsc_model_half2))
232
+ fscavg_text += " FSCaverage(half1)= {: .4f}\n".format(ret["summary"]["FSCaverage_half1"])
233
+ fscavg_text += " FSCaverage(half2)= {: .4f}\n".format(ret["summary"]["FSCaverage_half2"])
229
234
 
230
235
  # for loggraph
231
236
  fsc_logfile = "{}_fsc.log".format(output_prefix)
@@ -269,8 +274,8 @@ def calc_fsc(st, output_prefix, maps, d_min, mask, mask_radius, soft_edge, b_bef
269
274
  json.dump(stats.to_dict("records"),
270
275
  open("{}_fsc.json".format(output_prefix), "w"),
271
276
  indent=True)
272
-
273
- return fscavg_text
277
+ ret["binned"] = stats2.to_dict(orient="records")
278
+ return fscavg_text, ret
274
279
  # calc_fsc()
275
280
 
276
281
  def calc_fofc(st, st_expanded, maps, monlib, model_format, args, diffmap_prefix="diffmap"):
@@ -462,7 +467,7 @@ def process_input(st, maps, resolution, monlib, mask_in, args,
462
467
  unit_cell = maps[0][0].unit_cell
463
468
  spacegroup = gemmi.SpaceGroup(1)
464
469
  start_xyz = numpy.array(maps[0][0].get_position(*grid_start).tolist())
465
- A = numpy.array(unit_cell.orthogonalization_matrix.tolist())
470
+ A = unit_cell.orth.mat.array
466
471
  center = numpy.sum(A, axis=1) / 2 #+ start_xyz
467
472
 
468
473
  # Create mask
@@ -593,7 +598,7 @@ def process_input(st, maps, resolution, monlib, mask_in, args,
593
598
  topo, metal_kws = utils.restraints.prepare_topology(st, monlib, h_change=h_change, raise_error=False)
594
599
  args.keywords = metal_kws + args.keywords
595
600
  elif not no_refmac_fix:
596
- topo = gemmi.prepare_topology(st, monlib, warnings=io.StringIO(), ignore_unknown_links=True)
601
+ topo = gemmi.prepare_topology(st, monlib, warnings=logger.silent(), ignore_unknown_links=True)
597
602
  else:
598
603
  topo = None # not used
599
604
  if not no_refmac_fix:
@@ -618,7 +623,7 @@ def process_input(st, maps, resolution, monlib, mask_in, args,
618
623
  args.keywords.append("make cr prepared")
619
624
  gemmi.setup_for_crd(st)
620
625
  doc = gemmi.prepare_refmac_crd(st, topo, monlib, h_change)
621
- doc.write_file(crdout, style=gemmi.cif.Style.NoBlankLines)
626
+ doc.write_file(crdout, options=gemmi.cif.Style.NoBlankLines)
622
627
  logger.writeln("crd file written: {}".format(crdout))
623
628
 
624
629
  hkldata = utils.maps.mask_and_fft_maps(maps, resolution, None, with_000=False)
@@ -955,7 +960,7 @@ def main(args):
955
960
  monlib=monlib, cross_validation=args.cross_validation,
956
961
  blur=args.blur,
957
962
  d_min_fsc=args.fsc_resolution,
958
- cross_validation_method=args.cross_validation_method, st_sr=st_sr_expanded)
963
+ cross_validation_method=args.cross_validation_method, st_sr=st_sr_expanded)[0]
959
964
 
960
965
  # Calc Fo-Fc (and updated) maps
961
966
  calc_fofc(st, st_expanded, maps, monlib, model_format, args)
@@ -60,9 +60,9 @@ def find_peak(tf_map, ini_pos):
60
60
 
61
61
  x = tf_map.unit_cell.fractionalize(ini_pos)
62
62
  logger.writeln(" x0: [{}, {}, {}]".format(*x.tolist()))
63
- logger.writeln(" f0: {}".format(-tf_map.tricubic_interpolation(x)))
63
+ logger.writeln(" f0: {}".format(-tf_map.interpolate_value(x, order=3)))
64
64
 
65
- res = scipy.optimize.minimize(fun=lambda x:-tf_map.tricubic_interpolation(gemmi.Fractional(*x)),
65
+ res = scipy.optimize.minimize(fun=lambda x:-tf_map.interpolate_value(gemmi.Fractional(*x), order=3),
66
66
  x0=x.tolist(),
67
67
  jac=lambda x:-numpy.array(tf_map.tricubic_interpolation_der(gemmi.Fractional(*x))[1:]))
68
68
  logger.writeln(str(res))
@@ -185,6 +185,17 @@ def add_arguments(p):
185
185
  parser.add_argument('-o', '--output_prefix',
186
186
  help="default: taken from input file")
187
187
 
188
+ # conf
189
+ parser = subparsers.add_parser("conf", description = 'Compare conformations')
190
+ parser.add_argument('models', nargs="+")
191
+ parser.add_argument("--min_diff", type=float, default=60.)
192
+ parser.add_argument('--ligand', nargs="*", action="append")
193
+ parser.add_argument("--monlib",
194
+ help="Monomer library path. Default: $CLIBD_MON")
195
+ parser.add_argument("--same_chain", action='store_true', help="Only between same chains (more than one file)")
196
+ parser.add_argument('-o', '--output_prefix', default="conf",
197
+ help="")
198
+
188
199
  # adp
189
200
  parser = subparsers.add_parser("adp", description = 'ADP analysis')
190
201
  parser.add_argument('model')
@@ -281,6 +292,9 @@ def add_arguments(p):
281
292
  parser = subparsers.add_parser("seq", description = 'Print/align model sequence')
282
293
  parser.add_argument("--model", required=True)
283
294
  parser.add_argument('--seq', nargs="*", action="append", help="Sequence file(s)")
295
+ parser.add_argument('--scoring', nargs=6, type=int, default=(1, 0, -1, -1, 0, -1),
296
+ metavar=("match", "mismatch", "gapo", "gape", "good_gapo", "bad_gapo"),
297
+ help="scoring function. default: %(default)s")
284
298
 
285
299
  # dnarna
286
300
  parser = subparsers.add_parser("dnarna", description = 'DNA to RNA or RNA to DNA model conversion')
@@ -919,6 +933,135 @@ def geometry(args):
919
933
  fileio.write_model(st, file_name="{}_per_atom_score{}".format(args.output_prefix, model_format))
920
934
  # geometry()
921
935
 
936
+ def compare_conf(args):
937
+ def angle_abs_diff(a, b, full=360.):
938
+ # from gemmi/math.hpp
939
+ d = abs(a - b)
940
+ if d > full:
941
+ d -= numpy.floor(d / full) * full
942
+ return min(d, full - d)
943
+ # angle_abs_diff()
944
+
945
+ if args.ligand: args.ligand = sum(args.ligand, [])
946
+ st = None
947
+ for i, f in enumerate(args.models):
948
+ tmp = fileio.read_structure(f)
949
+ if len(args.models) > 1:
950
+ for chain in tmp[0]:
951
+ chain.name = f"{i+1}_{chain.name}"
952
+ if i == 0:
953
+ st = tmp
954
+ else:
955
+ for chain in tmp[0]:
956
+ st[0].add_chain(chain)
957
+ try:
958
+ monlib = restraints.load_monomer_library(st, monomer_dir=args.monlib, cif_files=args.ligand,
959
+ stop_for_unknowns=True)
960
+ except RuntimeError as e:
961
+ raise SystemExit(f"Error: {e}")
962
+
963
+ model.setup_entities(st, clear=True, force_subchain_names=True, overwrite_entity_type=True)
964
+ try:
965
+ topo, _ = restraints.prepare_topology(st, monlib, h_change=gemmi.HydrogenChange.NoChange,
966
+ check_hydrogen=False)
967
+ except RuntimeError as e:
968
+ raise SystemExit(f"Error: {e}")
969
+ ncslist = restraints.prepare_ncs_restraints(st)
970
+ lookup = {x.atom: x for x in st[0].all()}
971
+ ptypes = {x.name: x.polymer_type for x in st.entities}
972
+ resn_lookup = {(chain.name, res.seqid): res.name for chain in st[0] for res in chain}
973
+ confs = {}
974
+ for t in topo.torsions:
975
+ cra = lookup[t.atoms[0]]
976
+ ptype = ptypes[cra.residue.entity_id]
977
+ is_peptide = ptype in (gemmi.PolymerType.PeptideL, gemmi.PolymerType.PeptideD)
978
+ is_peptide_tors = t.restr.label.startswith("chi") or t.restr.label in ("omega", "phi", "psi")
979
+ is_na = ptype in (gemmi.PolymerType.Dna, gemmi.PolymerType.Rna, gemmi.PolymerType.DnaRnaHybrid)
980
+ is_na_tors = t.restr.label in ("C2e-chi", "alpha", "beta", "gamma", "C2e-nyu0", "epsilon", "zeta")
981
+ if (is_peptide and is_peptide_tors) or (is_na and is_na_tors):
982
+ confs.setdefault(cra.chain.name, {}).setdefault(cra.residue.seqid, {})[t.restr.label] = numpy.rad2deg(t.calculate())
983
+ fulls = {("ARG", "chi5"): 180., ("TYR", "chi2"): 180., ("PHE", "chi2"): 180., ("ASP", "chi2"): 180., ("GLU", "chi3"): 180.}
984
+ ret = []
985
+ for_coot = []
986
+ for ncs in ncslist.ncss:
987
+ c1, c2 = ncs.chains
988
+ if args.same_chain and len(args.models) > 1 and c1[c1.index("_"):] != c2[c2.index("_"):]:
989
+ continue
990
+ for s1, s2 in ncs.seqids:
991
+ if c1 in confs and s1 in confs[c1] and c2 in confs and s2 in confs[c2]:
992
+ conf1, conf2 = confs[c1][s1], confs[c2][s2]
993
+ resn = resn_lookup[(c1, s1)]
994
+ for t in conf1:
995
+ if t in conf2:
996
+ d = angle_abs_diff(conf1[t], conf2[t], fulls.get((resn, t), 360.))
997
+ ret.append((c1, s1, c2, s2, resn, t, conf1[t], conf2[t], d))
998
+ if d > args.min_diff:
999
+ for_coot.append((c1, s1.num, c2, s2.num, resn, t, d))
1000
+ df = pandas.DataFrame(ret, columns=["chain_1", "seq_1", "chain_2", "seq_2", "resn", "label", "conf_1", "conf_2", "diff"])
1001
+ df.sort_values("diff", ascending=False, inplace=True)
1002
+ logger.writeln(f"\nList of torsion angle differences (>{args.min_diff})")
1003
+ logger.writeln(df[df["diff"] > args.min_diff].to_string(index=False))
1004
+
1005
+ for_coot.sort(key=lambda x:-x[-1])
1006
+ coot_out = args.output_prefix + "_coot.py"
1007
+ with open(coot_out, "w") as ofs:
1008
+ # https://python-gtk-3-tutorial.readthedocs.io/en/latest/treeview.html
1009
+ ofs.write("""\
1010
+ from __future__ import absolute_import, division, print_function
1011
+ import re
1012
+ import gtk
1013
+ class coot_serval_conf_list:
1014
+ def __init__(self):
1015
+ window = gtk.Window(gtk.WINDOW_TOPLEVEL)
1016
+ window.set_title("Different conformations (Servalcat)")
1017
+ window.set_default_size(600, 600)
1018
+ scrolled_win = gtk.ScrolledWindow()
1019
+ scrolled_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
1020
+ vbox = gtk.VBox(False, 2)
1021
+ self.liststore = gtk.ListStore(str, int, str, int, str, str, float)
1022
+ self.filter = self.liststore.filter_new()
1023
+ self.treeview = gtk.TreeView(model=self.filter)
1024
+ for i, column_title in enumerate(["chain_1", "seq_1", "chain_2", "seq_2", "resn", "label", "diff"]):
1025
+ renderer = gtk.CellRendererText()
1026
+ column = gtk.TreeViewColumn(column_title, renderer, text=i)
1027
+ self.treeview.append_column(column)
1028
+ self.data = {}
1029
+ self.add_data()
1030
+ scrolled_win.add_with_viewport(self.treeview) # add?
1031
+ vbox.pack_start(scrolled_win, True, True, 0)
1032
+ window.add(vbox)
1033
+ window.show_all()
1034
+ self.treeview.connect("row-activated", self.on_row_activated)
1035
+
1036
+ def on_row_activated(self, treeview, path, column):
1037
+ assert len(path) == 1
1038
+ col_idx = [i for i, c in enumerate(treeview.get_columns()) if column == c][0]
1039
+ row = self.liststore[path[0]]
1040
+ if col_idx < 2:
1041
+ chain, resi = row[0], row[1]
1042
+ elif col_idx < 4:
1043
+ chain, resi = row[2], row[3]
1044
+ else:
1045
+ return
1046
+ if re.search("^[0-9]+_[0-9A-Za-z]", chain):
1047
+ chain = chain[chain.index("_")+1:]
1048
+ imol = active_atom_spec()[1][0]
1049
+ for name in (" CA ", " C1'"):
1050
+ a = get_atom(imol, chain, resi, "", name)
1051
+ if a:
1052
+ set_rotation_center(*a[2])
1053
+ break
1054
+
1055
+ def add_data(self):
1056
+ for i, d in enumerate(self.data):
1057
+ self.liststore.append(d)
1058
+
1059
+ gui = coot_serval_conf_list()
1060
+ """.format(for_coot))
1061
+ logger.writeln("\nRun:")
1062
+ logger.writeln(f"coot --script {coot_out}")
1063
+ # compare_conf()
1064
+
922
1065
  def adp_stats(args):
923
1066
  if not args.output_prefix: args.output_prefix = fileio.splitext(os.path.basename(args.model))[0] + "_adp"
924
1067
  st = fileio.read_structure(args.model)
@@ -1242,6 +1385,9 @@ def seq(args):
1242
1385
  for sf in args.seq:
1243
1386
  seqs.extend(fileio.read_sequence_file(sf))
1244
1387
 
1388
+ sc = gemmi.AlignmentScoring()
1389
+ sc.match, sc.mismatch, sc.gapo, sc.gape, sc.good_gapo, sc.bad_gapo = args.scoring
1390
+
1245
1391
  st = fileio.read_structure(args.model) # TODO option to (or not to) expand NCS
1246
1392
  model.setup_entities(st, clear=True, force_subchain_names=True, overwrite_entity_type=True)
1247
1393
  for chain in st[0]:
@@ -1257,17 +1403,20 @@ def seq(args):
1257
1403
  gemmi.PolymerType.Rna: gemmi.ResidueKind.RNA}.get(p_type, gemmi.ResidueKind.AA)
1258
1404
  s = [gemmi.expand_one_letter(x, kind) for x in seq]
1259
1405
  if None in s: continue
1260
- results.append([name, gemmi.align_sequence_to_polymer(s, p, p_type), seq])
1406
+ #als = [gemmi.align_sequence_to_polymer(s, p, p_type, gemmi.AlignmentScoring(x)) for x in ("s", "p")]
1407
+ #results.append([name, max(als, key=lambda x: x.match_count), seq])
1408
+ results.append([name, gemmi.align_sequence_to_polymer(s, p, p_type, sc), seq])
1261
1409
 
1262
1410
  if results:
1263
1411
  logger.writeln("Chain: {}".format(chain.name))
1264
1412
  logger.writeln(" polymer type: {}".format(str(p_type).replace("PolymerType.", "")))
1265
- name, al, s1 = max(results, key=lambda x: x[1].score)
1413
+ name, al, s1 = max(results, key=lambda x: (x[1].match_count, x[1].score))
1266
1414
  logger.writeln(" match: {}".format(name))
1415
+ logger.writeln(" aligned: {}".format(al.match_count))
1267
1416
  logger.writeln(" score: {}".format(al.score))
1268
1417
  p1, p2 = al.add_gaps(s1, 1), al.add_gaps(p_seq, 2)
1269
- unkseq = [x.start() for x in re.finditer("\-", p1)]
1270
- mismatches = [x.start() for x in re.finditer("\.", al.match_string)]
1418
+ unkseq = [x.start() for x in re.finditer(r"\-", p1)]
1419
+ mismatches = [x.start() for x in re.finditer(r"\.", al.match_string)]
1271
1420
  if mismatches or unkseq:
1272
1421
  idxes = {x.start(): i for i, x in enumerate(re.finditer("[^-]", p2))}
1273
1422
  seqnums = [str(x.seqid) for x in p]
@@ -1371,6 +1520,7 @@ def main(args):
1371
1520
  merge_models=merge_models,
1372
1521
  merge_dicts=merge_dicts,
1373
1522
  geom=geometry,
1523
+ conf=compare_conf,
1374
1524
  adp=adp_stats,
1375
1525
  power=show_power,
1376
1526
  fcalc=fcalc,
servalcat/utils/fileio.py CHANGED
@@ -17,7 +17,6 @@ import re
17
17
  import subprocess
18
18
  import gemmi
19
19
  import numpy
20
- import numpy.lib.recfunctions
21
20
  import gzip
22
21
 
23
22
  def splitext(path):
@@ -83,10 +82,12 @@ def write_mmcif(st, cif_out, cif_ref=None):
83
82
  groups.scale = True
84
83
  groups.assembly = True
85
84
  groups.entity = True
85
+ groups.entity_poly = True
86
86
  groups.entity_poly_seq = True
87
87
  groups.cis = True
88
88
  groups.conn = True
89
89
  groups.software = True
90
+ groups.auth_all = True
90
91
  # FIXME is this all?
91
92
  try:
92
93
  doc = read_cif_safe(cif_ref)
@@ -107,23 +108,27 @@ def write_mmcif(st, cif_out, cif_ref=None):
107
108
  block.find_mmcif_category("_atom_sites.").erase()
108
109
  st_new.update_mmcif_block(block, groups)
109
110
  if "_entry.id" in st_new.info: st_new.info["_entry.id"] = st_new.info["_entry.id"][:78]
110
- doc.write_file(cif_out, style=gemmi.cif.Style.Aligned)
111
+ doc.write_file(cif_out, options=gemmi.cif.Style.Aligned)
111
112
  else:
112
113
  st_new.name = st_new.name[:78] # this will become _entry.id
113
114
  if "_entry.id" in st_new.info: st_new.info["_entry.id"] = st_new.info["_entry.id"][:78]
114
- groups = gemmi.MmcifOutputGroups(True)
115
+ groups = gemmi.MmcifOutputGroups(True, auth_all=True)
115
116
  doc = gemmi.cif.Document()
116
117
  block = doc.add_new_block("new")
117
118
  st_new.update_mmcif_block(block, groups)
118
- doc.write_file(cif_out, style=gemmi.cif.Style.Aligned)
119
+ doc.write_file(cif_out, options=gemmi.cif.Style.Aligned)
119
120
  # write_mmcif()
120
121
 
121
122
  def write_pdb(st, pdb_out):
122
123
  logger.writeln("Writing PDB file: {}".format(pdb_out))
124
+ st = st.clone()
123
125
  chain_id_lens = [len(x) for x in model.all_chain_ids(st)]
124
126
  if chain_id_lens and max(chain_id_lens) > 2:
125
- st = st.clone()
126
127
  st.shorten_chain_names()
128
+ st.shorten_ccd_codes()
129
+ if st.shortened_ccd_codes:
130
+ msg = " ".join("{}->{}".format(o,n) for o,n in st.shortened_ccd_codes)
131
+ logger.writeln(" Using shortened residue names in the output pdb file: " + msg)
127
132
  st.write_pdb(pdb_out, use_linkr=True)
128
133
  # write_pdb()
129
134
 
@@ -339,6 +344,8 @@ def read_structure(xyz_in, assign_het_flags=True, merge_chain_parts=True):
339
344
  if st is None:
340
345
  logger.writeln("Reading chemical component file: {}".format(xyz_in))
341
346
  st = gemmi.make_structure_from_chemcomp_block(block)
347
+ for i in range(len(st)-1):
348
+ del st[1]
342
349
  elif spext[1].lower() in (".ins", ".res"):
343
350
  logger.writeln("Reading SHELX ins/res file: {}".format(xyz_in))
344
351
  st = model.cx_to_mx(read_shelx_ins(ins_in=xyz_in)[0])
@@ -456,7 +463,7 @@ def merge_ligand_cif(cifs_in, cif_out):
456
463
  if b.name not in list_names:
457
464
  doc.add_copied_block(b)
458
465
 
459
- doc.write_file(cif_out, style=gemmi.cif.Style.Aligned)
466
+ doc.write_file(cif_out, options=gemmi.cif.Style.Aligned)
460
467
  # merge_ligand_cif()
461
468
 
462
469
  def read_shelx_ins(ins_in=None, lines_in=None, ignore_q_peaks=True): # TODO support gz?
@@ -571,7 +578,7 @@ def read_shelx_ins(ins_in=None, lines_in=None, ignore_q_peaks=True): # TODO supp
571
578
  symms.extend([x*gemmi.Op("-x,-y,-z") for x in symms])
572
579
 
573
580
  ss.symops = [op.triplet() for op in set(symms)]
574
- ss.set_spacegroup("s")
581
+ ss.determine_and_set_spacegroup("s")
575
582
  # in case of non-regular setting, gemmi.SpaceGroup cannot be constructed anyway.
576
583
  if ss.spacegroup is None:
577
584
  raise RuntimeError("Cannot construct space group from symbols: {}".format(ss.symops))
@@ -598,7 +605,7 @@ def read_shelx_hkl(cell, sg, hklf, file_in=None, lines_in=None):
598
605
  # wavelength = l[32:40]
599
606
 
600
607
  ints = gemmi.Intensities()
601
- ints.set_data(cell, sg, hkls, vals, sigs)
608
+ ints.set_data(cell, sg, numpy.asarray(hkls), numpy.asarray(vals), numpy.asarray(sigs))
602
609
  ints.merge_in_place(gemmi.DataType.Anomalous)
603
610
  if not (ints.isign_array < 0).any(): ints.type = gemmi.DataType.Mean
604
611
  logger.writeln(" Multiplicity: max= {} mean= {:.1f} min= {}".format(numpy.max(ints.nobs_array),