servalcat 0.4.131__cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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.
Files changed (45) hide show
  1. servalcat/__init__.py +10 -0
  2. servalcat/__main__.py +120 -0
  3. servalcat/ext.cpython-314t-x86_64-linux-gnu.so +0 -0
  4. servalcat/refine/__init__.py +0 -0
  5. servalcat/refine/cgsolve.py +100 -0
  6. servalcat/refine/refine.py +1162 -0
  7. servalcat/refine/refine_geom.py +245 -0
  8. servalcat/refine/refine_spa.py +400 -0
  9. servalcat/refine/refine_xtal.py +339 -0
  10. servalcat/refine/spa.py +151 -0
  11. servalcat/refine/xtal.py +312 -0
  12. servalcat/refmac/__init__.py +0 -0
  13. servalcat/refmac/exte.py +191 -0
  14. servalcat/refmac/refmac_keywords.py +660 -0
  15. servalcat/refmac/refmac_wrapper.py +423 -0
  16. servalcat/spa/__init__.py +0 -0
  17. servalcat/spa/fofc.py +488 -0
  18. servalcat/spa/fsc.py +391 -0
  19. servalcat/spa/localcc.py +197 -0
  20. servalcat/spa/realspcc_from_var.py +128 -0
  21. servalcat/spa/run_refmac.py +979 -0
  22. servalcat/spa/shift_maps.py +293 -0
  23. servalcat/spa/shiftback.py +137 -0
  24. servalcat/spa/translate.py +129 -0
  25. servalcat/utils/__init__.py +35 -0
  26. servalcat/utils/commands.py +1629 -0
  27. servalcat/utils/fileio.py +836 -0
  28. servalcat/utils/generate_operators.py +296 -0
  29. servalcat/utils/hkl.py +811 -0
  30. servalcat/utils/logger.py +140 -0
  31. servalcat/utils/maps.py +345 -0
  32. servalcat/utils/model.py +933 -0
  33. servalcat/utils/refmac.py +759 -0
  34. servalcat/utils/restraints.py +888 -0
  35. servalcat/utils/symmetry.py +298 -0
  36. servalcat/xtal/__init__.py +0 -0
  37. servalcat/xtal/french_wilson.py +262 -0
  38. servalcat/xtal/run_refmac_small.py +240 -0
  39. servalcat/xtal/sigmaa.py +1954 -0
  40. servalcat/xtal/twin.py +316 -0
  41. servalcat-0.4.131.dist-info/METADATA +60 -0
  42. servalcat-0.4.131.dist-info/RECORD +45 -0
  43. servalcat-0.4.131.dist-info/WHEEL +6 -0
  44. servalcat-0.4.131.dist-info/entry_points.txt +4 -0
  45. servalcat-0.4.131.dist-info/licenses/LICENSE +373 -0
@@ -0,0 +1,312 @@
1
+ """
2
+ Author: "Keitaro Yamashita, Garib N. Murshudov"
3
+ MRC Laboratory of Molecular Biology
4
+
5
+ This software is released under the
6
+ Mozilla Public License, version 2.0; see LICENSE.
7
+ """
8
+ from __future__ import absolute_import, division, print_function, generators
9
+ import gemmi
10
+ import numpy
11
+ import json
12
+ import scipy.sparse
13
+ from servalcat.utils import logger
14
+ from servalcat.xtal import sigmaa
15
+ from servalcat import utils
16
+ from servalcat import ext
17
+ from servalcat.xtal.twin import find_twin_domains_from_data, estimate_twin_fractions_from_model, mlopt_twin_fractions
18
+ b_to_u = utils.model.b_to_u
19
+ u_to_b = utils.model.u_to_b
20
+ integr = sigmaa.integr
21
+
22
+ class LL_Xtal:
23
+ def __init__(self, hkldata, free, st, monlib, source="xray", mott_bethe=True,
24
+ use_solvent=0, use_in_est="all", use_in_target="all", twin=False, twin_mlalpha=False,
25
+ addends=None, addends2=None, is_int=None):
26
+ assert source in ("electron", "xray", "neutron", "custom")
27
+ self.source = source
28
+ self.mott_bethe = False if source != "electron" else mott_bethe
29
+ self.hkldata = hkldata
30
+ self.free = free
31
+ self.st = st
32
+ self.monlib = monlib
33
+ self.d_min_max = hkldata.d_min_max()
34
+ self.fc_labs = ["FC0"]
35
+ self.use_solvent = use_solvent
36
+ if use_solvent: # 0: no solvent, 1: use solvent, 2: non-binary solvent mask
37
+ self.fc_labs.append("FCbulk")
38
+ self.hkldata.df["FCbulk"] = 0j
39
+ self.use_non_binary_mask = use_solvent == 2
40
+ self.D_labs = ["D{}".format(i) for i in range(len(self.fc_labs))]
41
+ self.k_overall = numpy.ones(len(self.hkldata.df.index))
42
+ self.b_aniso = gemmi.SMat33d(0,0,0,0,0,0)
43
+ self.hkldata.df["k_aniso"] = 1.
44
+ self.use_in_est = use_in_est
45
+ self.use_in_target = use_in_target
46
+ self.ll = None
47
+ self.addends = addends
48
+ self.addends2 = addends2
49
+ self.scaling = sigmaa.LsqScale()
50
+ if twin:
51
+ self.twin_data, _ = find_twin_domains_from_data(self.hkldata)
52
+ else:
53
+ self.twin_data = None
54
+ self.twin_mlalpha = twin_mlalpha
55
+ if self.twin_data:
56
+ self.twin_data.setup_f_calc(len(self.fc_labs))
57
+ if is_int is None:
58
+ self.is_int = "I" in self.hkldata.df
59
+ else:
60
+ self.is_int = is_int
61
+ logger.writeln("will use {} reflections for parameter estimation".format(self.use_in_est))
62
+ logger.writeln("will use {} reflections for refinement".format(self.use_in_target))
63
+
64
+ def refine_id(self):
65
+ return {"xray": "X-RAY", "electron": "ELECTRON", "neutron": "NEUTRON", "custom": "CUSTOM"}.get(self.source, "") + " DIFFRACTION"
66
+
67
+ def update_ml_params(self):
68
+ self.b_aniso = sigmaa.determine_ml_params(self.hkldata, self.is_int, self.fc_labs, self.D_labs, self.b_aniso,
69
+ use=self.use_in_est,
70
+ twin_data=self.twin_data)#D_trans="splus", S_trans="splus")
71
+ self.hkldata.df["k_aniso"] = self.hkldata.debye_waller_factors(b_cart=self.b_aniso)
72
+ #determine_mlf_params_from_cc(self.hkldata, self.fc_labs, self.D_labs)
73
+ if self.twin_data and self.twin_mlalpha:
74
+ mlopt_twin_fractions(self.hkldata, self.twin_data, self.b_aniso)
75
+ def update_fc(self):
76
+ # modify st before fc calculation
77
+ b_resid = sigmaa.subtract_common_aniso_from_model([self.st])
78
+ self.b_aniso += gemmi.SMat33d(*b_resid.elements_pdb()) # needed for target calculation
79
+ d_min = max(self.twin_data.s2_array)**(-0.5) if self.twin_data else self.d_min_max[0]
80
+ sigmaa.update_fc(st_list=[self.st], fc_labs=self.fc_labs,
81
+ d_min=d_min, monlib=self.monlib,
82
+ source=self.source, mott_bethe=self.mott_bethe,
83
+ hkldata=self.hkldata, twin_data=self.twin_data,
84
+ addends=self.addends, addends2=self.addends2)
85
+
86
+ def prepare_target(self):
87
+ if self.twin_data:
88
+ if self.use_in_target == "all":
89
+ idxes = numpy.concatenate([sel[i] for i_bin, _ in self.hkldata.binned("ml")
90
+ for sel in self.hkldata.centric_and_selections["ml"][i_bin] for i in (1,2)])
91
+ else:
92
+ i = 1 if self.use_in_target == "work" else 2
93
+ idxes = numpy.concatenate([sel[i] for i_bin, _ in self.hkldata.binned("ml")
94
+ for sel in self.hkldata.centric_and_selections["ml"][i_bin]])
95
+ mask = numpy.empty(len(self.hkldata.df.index)) * numpy.nan
96
+ mask[idxes] = 1 / self.hkldata.debye_waller_factors(b_cart=self.b_aniso)[idxes]**2
97
+ self.twin_data.est_f_true(self.hkldata.df.I.to_numpy() * mask,
98
+ self.hkldata.df.SIGI.to_numpy() * mask)
99
+ self.k_ani2_inv_masked = mask
100
+
101
+ def _overall_scale(self, min_b=0.1):
102
+ miller_array = self.twin_data.asu if self.twin_data else self.hkldata.miller_array()
103
+ d_min = max(self.twin_data.s2_array)**(-0.5) if self.twin_data else self.d_min_max[0]
104
+ if self.use_solvent:
105
+ Fmask = sigmaa.calc_Fmask(self.st, d_min, miller_array, self.use_non_binary_mask)
106
+ if self.twin_data:
107
+ fc_sum = self.twin_data.f_calc[:,:-1].sum(axis=1)
108
+ else:
109
+ fc_sum = self.hkldata.df[self.fc_labs[:-1]].sum(axis=1).to_numpy()
110
+ fc_list = [fc_sum, Fmask]
111
+ else:
112
+ if self.twin_data:
113
+ fc_list = [self.twin_data.f_calc.sum(axis=1)]
114
+ else:
115
+ fc_list = [self.hkldata.df[self.fc_labs].sum(axis=1).to_numpy()]
116
+ self.scaling.set_data(self.hkldata, fc_list, self.is_int, sigma_cutoff=0, twin_data=self.twin_data)
117
+ self.scaling.scale()
118
+ self.b_aniso = self.scaling.b_aniso
119
+ b = self.scaling.b_iso
120
+ min_b_iso = self.st[0].calculate_b_aniso_range()[0] # actually min of aniso too
121
+ tmp = min_b_iso + b
122
+ if tmp < min_b: # perhaps better only adjust b_iso that went too small, but we need to recalculate Fc
123
+ logger.writeln(" Adjusting overall B to avoid too small value")
124
+ b += min_b - tmp
125
+ logger.writeln(" Applying overall B to model: {:.2f}".format(b))
126
+ utils.model.shift_b(self.st[0], b)
127
+ k_iso = self.hkldata.debye_waller_factors(b_iso=b)
128
+ self.hkldata.df["k_aniso"] = self.hkldata.debye_waller_factors(b_cart=self.b_aniso)
129
+ if self.use_solvent:
130
+ if self.twin_data:
131
+ s2 = numpy.asarray(self.twin_data.s2_array)
132
+ else:
133
+ s2 = 1. / self.hkldata.d_spacings().to_numpy()**2
134
+ Fbulk = Fmask * self.scaling.get_solvent_scale(self.scaling.k_sol, self.scaling.b_sol, s2)
135
+ if self.twin_data:
136
+ self.twin_data.f_calc[:,-1] = Fbulk
137
+ else:
138
+ self.hkldata.df[self.fc_labs[-1]] = Fbulk
139
+ if "I" in self.hkldata.df:
140
+ o_labs = self.hkldata.df.columns.intersection(["I", "SIGI",
141
+ "I(+)","SIGI(+)", "I(-)", "SIGI(-)"])
142
+ self.hkldata.df[o_labs] /= self.scaling.k_overall**2
143
+ if "FP" in self.hkldata.df:
144
+ o_labs = self.hkldata.df.columns.intersection(["FP", "SIGFP",
145
+ "F(+)","SIGF(+)", "F(-)", "SIGF(-)"])
146
+ self.hkldata.df[o_labs] /= self.scaling.k_overall
147
+
148
+ if self.twin_data:
149
+ self.twin_data.f_calc[:] *= self.twin_data.debye_waller_factors(b_iso=b)[:,None]
150
+ else:
151
+ for lab in self.fc_labs: self.hkldata.df[lab] *= k_iso
152
+ self.hkldata.df["FC"] = self.hkldata.df[self.fc_labs].sum(axis=1)
153
+
154
+ # for next cycle
155
+ self.scaling.k_overall = 1.
156
+ self.scaling.b_iso = 0.
157
+ # _overall_scale()
158
+
159
+ def overall_scale(self, min_b=0.1):
160
+ self._overall_scale(min_b)
161
+ if self.twin_data:
162
+ estimate_twin_fractions_from_model(self.twin_data, self.hkldata)
163
+ self._overall_scale(min_b)
164
+ # overall_scale()
165
+
166
+ def calc_target(self): # -LL target for MLF or MLI
167
+ ret = 0
168
+ if self.twin_data:
169
+ Io = self.hkldata.df.I.to_numpy() * self.k_ani2_inv_masked
170
+ sigIo = self.hkldata.df.SIGI.to_numpy() * self.k_ani2_inv_masked
171
+ self.twin_data.est_f_true(Io, sigIo)
172
+ ret = self.twin_data.ll(Io, sigIo)
173
+ else:
174
+ k_aniso = self.hkldata.debye_waller_factors(b_cart=self.b_aniso)
175
+ f = sigmaa.mli if self.is_int else sigmaa.mlf
176
+ for i_bin, _ in self.hkldata.binned("ml"):
177
+ if self.use_in_target == "all":
178
+ idxes = numpy.concatenate([sel[i] for sel in self.hkldata.centric_and_selections["ml"][i_bin] for i in (1,2)])
179
+ else:
180
+ i = 1 if self.use_in_target == "work" else 2
181
+ idxes = numpy.concatenate([sel[i] for sel in self.hkldata.centric_and_selections["ml"][i_bin]])
182
+ ret += f(self.hkldata.df,
183
+ self.fc_labs,
184
+ numpy.vstack([self.hkldata.df[lab].to_numpy()[idxes] for lab in self.D_labs]).T,
185
+ self.hkldata.df.S.to_numpy()[idxes],
186
+ k_aniso,
187
+ idxes)
188
+ return ret * 2 # friedel mates
189
+ # calc_target()
190
+
191
+ def calc_stats(self, bin_stats=False):
192
+ stats, overall = sigmaa.calc_r_and_cc(self.hkldata, self.twin_data)
193
+ ret = {"summary": overall}
194
+ ret["summary"]["-LL"] = self.calc_target()
195
+ if self.twin_data:
196
+ ret["twin_alpha"] = {op.as_hkl().triplet(): a
197
+ # ops does not include identity
198
+ for op, a in zip([gemmi.Op()]+self.twin_data.ops, self.twin_data.alphas)}
199
+ if bin_stats:
200
+ ret["bin_stats"] = stats
201
+ ret["ml"] = self.hkldata.binned_df["ml"].copy()
202
+ for lab in "R", "CC":
203
+ logger.writeln(" ".join("{} = {:.4f}".format(x, overall[x]) for x in overall if x.startswith(lab)))
204
+ if self.is_int:
205
+ logger.writeln("R1 is calculated for reflections with I/sigma>2.")
206
+ return ret
207
+
208
+ def calc_grad(self, refine_params, specs=None):
209
+ blur = utils.model.determine_blur_for_dencalc(self.st, self.d_min_max[0] / 3) # TODO need more work
210
+ logger.writeln("blur for deriv= {:.2f}".format(blur))
211
+ if self.twin_data:
212
+ Io = self.hkldata.df.I.to_numpy() * self.k_ani2_inv_masked
213
+ sigIo = self.hkldata.df.SIGI.to_numpy() * self.k_ani2_inv_masked
214
+ dll_dab, d2ll_dab2 = self.twin_data.ll_der_fc0(Io, sigIo)
215
+ dll_dab *= self.twin_data.debye_waller_factors(b_iso=-blur)
216
+ else:
217
+ dll_dab = numpy.zeros(len(self.hkldata.df.FC), dtype=numpy.complex128)
218
+ d2ll_dab2 = numpy.empty(len(self.hkldata.df.index))
219
+ d2ll_dab2[:] = numpy.nan
220
+ k_ani = self.hkldata.debye_waller_factors(b_cart=self.b_aniso)
221
+ for i_bin, _ in self.hkldata.binned("ml"):
222
+ for c, work, test in self.hkldata.centric_and_selections["ml"][i_bin]:
223
+ if self.use_in_target == "all":
224
+ cidxes = numpy.concatenate([work, test])
225
+ else:
226
+ cidxes = work if self.use_in_target == "work" else test
227
+ epsilon = self.hkldata.df.epsilon.to_numpy()[cidxes]
228
+ Fcs = numpy.vstack([self.hkldata.df[lab].to_numpy()[cidxes] for lab in self.fc_labs]).T
229
+ Ds = numpy.vstack([self.hkldata.df[lab].to_numpy()[cidxes] for lab in self.D_labs]).T
230
+ S = self.hkldata.df["S"].to_numpy()[cidxes]
231
+ Fc = (Ds * Fcs).sum(axis=1)
232
+ Fc_abs = numpy.abs(Fc)
233
+ expip = numpy.exp(1j * numpy.angle(Fc))
234
+ if self.is_int:
235
+ Io = self.hkldata.df.I.to_numpy()
236
+ sigIo = self.hkldata.df.SIGI.to_numpy()
237
+ to = Io[cidxes] / sigIo[cidxes] - sigIo[cidxes] / (c+1) / k_ani[cidxes]**2 / S / epsilon
238
+ tf = k_ani[cidxes] * Fc_abs / numpy.sqrt(sigIo[cidxes])
239
+ sig1 = k_ani[cidxes]**2 * epsilon * S / sigIo[cidxes]
240
+ k_num = numpy.repeat(0.5 if c == 0 else 0., to.size) # acentric:0.5, centric: 0.
241
+ r = ext.integ_J_ratio(k_num, k_num - 0.5, True, to, tf, sig1, numpy.repeat(c+1, to.size),
242
+ integr.exp2_threshold, integr.h, integr.N, integr.ewmax)
243
+ r *= numpy.sqrt(sigIo[cidxes]) / k_ani[cidxes]
244
+ g = (2-c) * (Fc_abs - r) / epsilon / S * Ds[:,0]
245
+ dll_dab[cidxes] = g * expip
246
+ #d2ll_dab2[cidxes] = (2-c)**2 / S / epsilon * Ds[0]**2 # approximation
247
+ #d2ll_dab2[cidxes] = ((2-c) / S / epsilon + ((2-c) * r / k_ani[cidxes] / epsilon / S)**2) * Ds[0]**2
248
+ d2ll_dab2[cidxes] = g**2
249
+ else:
250
+ Fo = self.hkldata.df.FP.to_numpy()[cidxes] / k_ani[cidxes]
251
+ SigFo = self.hkldata.df.SIGFP.to_numpy()[cidxes] / k_ani[cidxes]
252
+ if c == 0: # acentric
253
+ Sigma = 2 * SigFo**2 + epsilon * S
254
+ X = 2 * Fo * Fc_abs / Sigma
255
+ m = gemmi.bessel_i1_over_i0(X)
256
+ g = 2 * (Fc_abs - m * Fo) / Sigma * Ds[:,0] # XXX assuming 0 is atomic structure
257
+ dll_dab[cidxes] = g * expip
258
+ d2ll_dab2[cidxes] = (2 / Sigma - (1 - m / X - m**2) * (2 * Fo / Sigma)**2) * Ds[:,0]**2
259
+ else:
260
+ Sigma = SigFo**2 + epsilon * S
261
+ X = Fo * Fc_abs / Sigma
262
+ #X = X.astype(numpy.float64)
263
+ m = numpy.tanh(X)
264
+ g = (Fc_abs - m * Fo) / Sigma * Ds[:,0]
265
+ dll_dab[cidxes] = g * expip
266
+ d2ll_dab2[cidxes] = (1. / Sigma - (Fo / Sigma)**2 * (1. - m**2)) * Ds[:,0]**2
267
+ dll_dab *= self.hkldata.debye_waller_factors(b_iso=-blur)
268
+
269
+ if self.mott_bethe:
270
+ d2 = numpy.reciprocal(self.twin_data.s2_array) if self.twin_data else self.hkldata.d_spacings()**2
271
+ dll_dab *= d2 * gemmi.mott_bethe_const()
272
+ d2ll_dab2 *= gemmi.mott_bethe_const()**2
273
+
274
+ if not self.twin_data:
275
+ dll_dab *= self.hkldata.df.llweight
276
+ d2ll_dab2 *= self.hkldata.df.llweight
277
+
278
+ # we need V**2/n for gradient.
279
+ if self.twin_data:
280
+ dll_dab_den = utils.hkl.fft_map(self.hkldata.cell, self.hkldata.sg, self.twin_data.asu, data=dll_dab)
281
+ else:
282
+ dll_dab_den = self.hkldata.fft_map(data=dll_dab)
283
+ dll_dab_den.array[:] *= self.hkldata.cell.volume**2 / dll_dab_den.point_count
284
+ #asu = dll_dab_den.masked_asu()
285
+ #dll_dab_den.array[:] *= 1 - asu.mask_array # 0 to use
286
+
287
+ self.ll = ext.LL(self.st, refine_params, self.mott_bethe, self.addends)
288
+ self.ll.set_ncs([x.tr for x in self.st.ncs if not x.given])
289
+ if self.source == "custom":
290
+ self.ll.calc_grad_custom(dll_dab_den, blur)
291
+ elif self.source == "neutron":
292
+ self.ll.calc_grad_n92(dll_dab_den, blur)
293
+ else:
294
+ self.ll.calc_grad_it92(dll_dab_den, blur)
295
+
296
+ # second derivative
297
+ s_array = numpy.sqrt(self.twin_data.s2_array) if self.twin_data else 1./self.hkldata.d_spacings().to_numpy()
298
+ if self.source == "custom":
299
+ self.ll.make_fisher_table_diag_direct_custom(s_array, d2ll_dab2)
300
+ self.ll.fisher_diag_from_table_custom()
301
+ elif self.source == "neutron":
302
+ self.ll.make_fisher_table_diag_direct_n92(s_array, d2ll_dab2)
303
+ self.ll.fisher_diag_from_table_n92()
304
+ else:
305
+ self.ll.make_fisher_table_diag_direct_it92(s_array, d2ll_dab2)
306
+ self.ll.fisher_diag_from_table_it92()
307
+ #json.dump(dict(b=self.ll.table_bs, pp1=self.ll.pp1, bb=self.ll.bb),
308
+ # open("ll_fisher.json", "w"), indent=True)
309
+ #a, (b,c) = ll.fisher_for_coo()
310
+ #json.dump(([float(x) for x in a], ([int(x) for x in b], [int(x) for x in c])), open("fisher.json", "w"))
311
+ if specs is not None:
312
+ self.ll.spec_correction(specs)
File without changes
@@ -0,0 +1,191 @@
1
+ """
2
+ Author: "Keitaro Yamashita, Garib N. Murshudov"
3
+ MRC Laboratory of Molecular Biology
4
+
5
+ This software is released under the
6
+ Mozilla Public License, version 2.0; see LICENSE.
7
+ """
8
+ from __future__ import absolute_import, division, print_function, generators
9
+ import gemmi
10
+ import numpy
11
+ from servalcat.utils import logger
12
+ from servalcat import ext
13
+
14
+ """import line_profiler
15
+ profile = line_profiler.LineProfiler()
16
+ import atexit
17
+ atexit.register(profile.print_stats)
18
+ @profile"""
19
+ def read_external_restraints(params, st, geom):
20
+ # default or current values
21
+ defs = dict(symall_block=False, exclude_self_block=False, type_default=2, alpha_default=1.,
22
+ ext_verbose=False, scale_sigma_dist=1., scale_sigma_angl=1., scale_sigma_tors=1.,
23
+ scale_sigma_chir=1., scale_sigma_plan=1., scale_sigma_inte=1.,
24
+ sigma_min_loc=0., sigma_max_loc=100., ignore_undefined=False, ignore_hydrogens=True,
25
+ dist_max_external=numpy.inf, dist_min_external=-numpy.inf, use_atoms="a", prefix_ch=" ")
26
+ #exte = gemmi.ExternalRestraints(st)
27
+ extypes = dict(dist=ext.Geometry.Bond,
28
+ angl=ext.Geometry.Angle,
29
+ chir=ext.Geometry.Chirality,
30
+ tors=ext.Geometry.Torsion,
31
+ plan=ext.Geometry.Plane,
32
+ inte=ext.Geometry.Interval,
33
+ harm=ext.Geometry.Harmonic,
34
+ spec=ext.Geometry.Special,
35
+ stac=ext.Geometry.Stacking)
36
+ exlists = dict(dist=geom.bonds, angl=geom.angles, tors=geom.torsions,
37
+ chir=geom.chirs, plan=geom.planes, inte=geom.intervals,
38
+ stac=geom.stackings, harm=geom.harmonics, spec=geom.specials)
39
+ num_org = {x: len(exlists[x]) for x in exlists}
40
+
41
+ # XXX There may be duplication (same chain, resi, name, and alt) - we should give error?
42
+ lookup = {(cra.chain.name, cra.residue.seqid.num, cra.residue.seqid.icode,
43
+ cra.atom.name, cra.atom.altloc) : cra.atom for cra in st[0].all()}
44
+
45
+ # TODO main chain / side chain filtering
46
+ for r in params:
47
+ if not r: continue
48
+ defs.update(r["defaults"])
49
+ if "rest_type" not in r: continue
50
+ if r["rest_type"] not in extypes:
51
+ logger.writeln("Warning: unknown external restraint type: {}".format(r["rest_type"]))
52
+ continue
53
+
54
+ atoms = []
55
+ skip = False
56
+ for i, spec in enumerate(r["restr"].get("specs", [])):
57
+ if r["rest_type"] == "stac":
58
+ atoms.append([])
59
+ if "ifirst" in spec:
60
+ for chain in st[0]:
61
+ if chain.name != spec["chain"]: continue
62
+ for res in chain:
63
+ if spec["ifirst"] is not None and res.seqid.num < spec["ifirst"]: continue
64
+ if spec["ilast"] is not None and res.seqid.num > spec["ilast"]: continue
65
+ atoms.extend([a for a in res if spec.get("atom", "*") == "*" or a.name == spec["atom"]])
66
+ else:
67
+ for name in spec["names"]: # only same altloc allowed?
68
+ key = (spec["chain"], spec["resi"], spec.get("icode", " "),
69
+ name, spec.get("altloc", "\0"))
70
+ atom = lookup.get(key)
71
+ if atom is None:
72
+ if defs["ignore_undefined"]:
73
+ logger.writeln("Warning: atom not found: {}".format(key))
74
+ skip = True
75
+ continue
76
+ raise RuntimeError("Atom not found: {}".format(key))
77
+ if defs["ignore_hydrogens"] and atom.is_hydrogen():
78
+ logger.writeln("External restraints with hydrogen atoms will be ignored: {}".format(key))
79
+ if r["rest_type"] in ("dist", "angl", "tors", "inte"):
80
+ skip = True
81
+ continue
82
+ if r["rest_type"] == "stac":
83
+ atoms[i].append(atom)
84
+ else:
85
+ atoms.append(atom)
86
+ if skip or not atoms:
87
+ continue
88
+ if r["rest_type"] in ("spec", "harm"):
89
+ if r["restr"]["rectype"] == "auto":
90
+ assert r["rest_type"] == "spec"
91
+ atoms = [cra.atom for cra in st[0].all()]
92
+ for atom in atoms:
93
+ ex = extypes[r["rest_type"]](atom)
94
+ if r["rest_type"] == "spec":
95
+ # TODO check if it is on special position. using r["restr"]["toler"]
96
+ ex.sigma_t = r["restr"]["sigma_t"]
97
+ ex.sigma_u =r["restr"]["sigma_u"]
98
+ ex.u_val_incl = r["restr"]["u_val_incl"]
99
+ # ex.trans_t =
100
+ # ex.mat_u =
101
+ else:
102
+ ex.sigma = r["restr"]["sigma_t"]
103
+ exlists[r["rest_type"]].append(ex)
104
+ continue
105
+ elif r["rest_type"] == "plan":
106
+ ex = extypes[r["rest_type"]](atoms)
107
+ else:
108
+ ex = extypes[r["rest_type"]](*atoms)
109
+ if r["rest_type"] in ("dist", "angl", "chir", "tors"):
110
+ value = r["restr"]["value"]
111
+ sigma = r["restr"]["sigma_value"] / defs["scale_sigma_{}".format(r["rest_type"])]
112
+ if r["rest_type"] == "chir":
113
+ ex.value = value
114
+ ex.sigma = sigma
115
+ else:
116
+ if r["rest_type"] == "dist":
117
+ sigma = min(max(sigma, defs["sigma_min_loc"]), defs["sigma_max_loc"])
118
+ vals = (value, sigma, value, sigma) # nucleus
119
+ elif r["rest_type"] == "tors":
120
+ vals = (value, sigma, 1) # period. # Refmac does not seem to read it from instruction
121
+ else:
122
+ vals = (value, sigma)
123
+ ex.values.append(extypes[r["rest_type"]].Value(*vals))
124
+
125
+ if r["rest_type"] == "dist":
126
+ if not (defs["dist_min_external"] < r["restr"]["value"] < defs["dist_max_external"]):
127
+ continue
128
+ ex.alpha = r["restr"].get("alpha_in", defs["alpha_default"])
129
+ ex.type = r["restr"].get("itype_in", defs["type_default"])
130
+ symm1 = any([spec.get("symm") for spec in r["restr"]["specs"]]) # is it the intention?
131
+ if r["restr"].get("symm_in", defs["symall_block"]) or symm1:
132
+ asu = gemmi.Asu.Different if defs["exclude_self_block"] else gemmi.Asu.Any
133
+ ex.set_image(st.cell, asu)
134
+ #print("dist=", ex.alpha, ex.type, ex.values[-1].value, ex.values[-1].sigma, ex.sym_idx, ex.pbc_shift, ex.atoms)
135
+ elif r["rest_type"] == "angl":
136
+ if any(spec.get("symm") for spec in r["restr"]["specs"]):
137
+ asus = [gemmi.Asu.Different if r["restr"]["specs"][i].get("symm") else gemmi.Asu.Same
138
+ for i in range(3)]
139
+ if atoms[0].serial > atoms[2].serial:
140
+ asus = asus[::-1]
141
+ ex.set_images(st.cell, asus[0], asus[2])
142
+ #print("angl=", ex.values[-1].value, ex.values[-1].sigma, ex.atoms)
143
+ elif r["rest_type"] == "tors":
144
+ pass
145
+ #print("tors=", ex.values[-1].value, ex.values[-1].sigma, ex.atoms)
146
+ elif r["rest_type"] == "chir":
147
+ #print("chir=", ex.value, ex.sigma, ex.atoms)
148
+ ex.sign = gemmi.ChiralityType.Positive if ex.value > 0 else gemmi.ChiralityType.Negative
149
+ ex.value = abs(ex.value)
150
+ elif r["rest_type"] == "plan":
151
+ ex.sigma = r["restr"]["sigma_value"] / defs["scale_sigma_{}".format(r["rest_type"])]
152
+ #print("plan=", ex.sigma, ex.atoms)
153
+ elif r["rest_type"] == "inte":
154
+ dmin, dmax = r["restr"].get("dmin"), r["restr"].get("dmax")
155
+ smin, smax = r["restr"].get("smin"), r["restr"].get("smax")
156
+ if (smin,smax).count(None) == 2:
157
+ smin = smax = 0.05
158
+ else:
159
+ if smin is None: smin = smax
160
+ if smax is None: smax = smin
161
+ smin /= defs["scale_sigma_inte"]
162
+ smax /= defs["scale_sigma_inte"]
163
+ if (dmin,dmax).count(None) == 1:
164
+ if dmin is None: dmin = dmax
165
+ if dmax is None: dmax = dmin
166
+ ex.dmin = dmin
167
+ ex.dmax = dmax
168
+ ex.smin = smin
169
+ ex.smax = smax
170
+ symm1 = any(spec.get("symm") for spec in r["restr"]["specs"]) # not tested
171
+ if r["restr"].get("symm_in", defs["symall_block"]) or symm1:
172
+ asu = gemmi.Asu.Different if defs["exclude_self_block"] else gemmi.Asu.Any
173
+ ex.set_image(st.cell, asu)
174
+ #print("inte=", ex.dmin, ex.dmax, ex.smin, ex.smax, ex.atoms)
175
+ elif r["rest_type"] == "stac":
176
+ ex.dist = r["restr"]["dist_id"]
177
+ ex.sd_dist = r["restr"]["dist_sd"]
178
+ ex.angle = r["restr"].get("angle_id", 0.)
179
+ ex.sd_angle = r["restr"]["angle_sd"]
180
+ #print("stac=", ex.dist, ex.sd_dist, ex.angle, ex.sd_angle, ex.planes)
181
+
182
+ exlists[r["rest_type"]].append(ex)
183
+
184
+ logger.writeln("External restraints from Refmac instructions")
185
+ labs = dict(dist="distances", angl="angles", tors="torsions",
186
+ chir="chirals", plan="planes", inte="intervals",
187
+ stac="stackings", harm="harmonics", spec="special positions")
188
+ for lab in labs:
189
+ logger.writeln(" Number of {:18s} : {}".format(labs[lab], len(exlists[lab]) - num_org[lab]))
190
+ logger.writeln("")
191
+ # read_external_restraints()