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,298 @@
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 subprocess
11
+ import numpy
12
+ import re
13
+ from servalcat.utils import logger
14
+ from servalcat.utils import fileio
15
+ from servalcat.utils import model
16
+ from servalcat.utils import generate_operators
17
+
18
+ def add_symmetry_args(parser, require_pg=False):
19
+ parser.add_argument('--pg', required=require_pg, help="Point group symbol")
20
+ parser.add_argument('--twist', type=float, help="Helical twist (degree)")
21
+ parser.add_argument('--rise', type=float, help="Helical rise (Angstrom)")
22
+ parser.add_argument('--center', type=float, nargs=3, help="Origin of symmetry. Default: center of the box")
23
+ parser.add_argument('--axis1', type=float, nargs=3, help="Axis1 (if I: 5-fold, O: 4-fold, T: 3-fold)")
24
+ parser.add_argument('--axis2', type=float, nargs=3, help="Axis2 (if I: 5-fold, O: 4-fold, T: 3-fold, Dn: 2-fold)")
25
+ # add_symmetry_args()
26
+
27
+ def update_ncs_from_args(args, st, map_and_start=None, filter_contacting=False,
28
+ helical_min_n=None, helical_max_n=None):
29
+ is_helical = args.twist is not None
30
+ if not is_helical and not args.pg:
31
+ if len(st.ncs) > 0:
32
+ logger.writeln("Strict NCS detected from model.")
33
+ show_ncs_operators_axis_angle(st.ncs)
34
+ return
35
+
36
+ if len(st.ncs) > 0:
37
+ logger.writeln(" WARNING: NCS information in model file will be ignored")
38
+
39
+ ncsops = ncsops_from_args(args, st.cell, map_and_start=map_and_start, st=st,
40
+ helical_min_n=helical_min_n, helical_max_n=helical_max_n)
41
+
42
+ st.ncs = [x for x in ncsops if not x.tr.is_identity()]
43
+ # To write identity op to the output model
44
+ idop_id = next((x.id for x in ncsops if x.tr.is_identity()), None)
45
+ if idop_id:
46
+ st.info["_struct_ncs_oper.id"] = idop_id
47
+
48
+ if filter_contacting:
49
+ model.filter_contacting_ncs(st)
50
+ # update_ncs_from_args()
51
+
52
+ def ncsops_from_args(args, cell, map_and_start=None, st=None, helical_min_n=None, helical_max_n=None):
53
+ is_helical = args.twist is not None
54
+
55
+ if map_and_start is not None:
56
+ start_xyz = numpy.array(map_and_start[0].get_position(*map_and_start[1]).tolist())
57
+ else:
58
+ start_xyz = numpy.zeros(3)
59
+
60
+ if args.center is None:
61
+ A = cell.orth.mat.array
62
+ center = numpy.sum(A, axis=1) / 2 #+ start_xyz
63
+ logger.writeln("Center: {}".format(center))
64
+ else:
65
+ center = numpy.array(args.center)
66
+
67
+ if is_helical:
68
+ ncsops = generate_helical_operators(start_xyz, center,
69
+ args.pg, args.twist, args.rise,
70
+ axis1=args.axis1, axis2=args.axis2,
71
+ st=st, min_n=helical_min_n, max_n=helical_max_n)
72
+ logger.writeln("{} helical operators found".format(len(ncsops)))
73
+ else:
74
+ _, _, ops = operators_from_symbol(args.pg, axis1=args.axis1, axis2=args.axis2)
75
+ logger.writeln("{} operators found for {}".format(len(ops), args.pg))
76
+ show_operators_axis_angle(ops)
77
+ ncsops = make_NcsOps_from_matrices(ops, cell=cell, center=center)
78
+
79
+ return ncsops
80
+ # ncsops_from_args()
81
+
82
+ def get_matrices_using_relion(sym):
83
+ ps = subprocess.check_output(["relion_refine", "--sym", sym.strip(), "--print_symmetry_ops"])
84
+
85
+ ret = []
86
+ read_flag = -1
87
+ for l in ps.splitlines():
88
+ if b"R(" in l:
89
+ ret.append(numpy.zeros((3,3)))
90
+ read_flag = 0
91
+ elif 0 <= read_flag < 3:
92
+ ret[-1][read_flag,:] = [float(x) for x in l.split()]
93
+ read_flag += 1
94
+ elif read_flag >= 3:
95
+ read_flag = -1
96
+
97
+ return ret
98
+ # get_matrices_using_relion()
99
+
100
+ def operators_from_symbol(op, axis1=None, axis2=None):
101
+ r = re.search("^([CDITO])([0-9]*)$", op.upper())
102
+ if not r:
103
+ raise RuntimeError("Invalid point group symbol: {}".format(op))
104
+ a, n = r.groups()
105
+ if n:
106
+ n = int(n)
107
+ if n <= 0:
108
+ raise RuntimeError("Non positive order given: {}".format(op))
109
+ elif a in ("T", "O"):
110
+ raise RuntimeError("You cannot give number after T,O: {}".format(op))
111
+ elif a == "I" and n > 4:
112
+ raise RuntimeError("Only I1-4 are supported for I variants: {}".format(op))
113
+
114
+ if axis1 is not None: axis1 = numpy.array(axis1)
115
+ if axis2 is not None: axis2 = numpy.array(axis2)
116
+
117
+ # RELION's conventions
118
+ if a == "I":
119
+ order1 = order2 = 5
120
+ if not n or n == 2:
121
+ if axis1 is None: axis1 = numpy.array([0, 0.85065, 0.52573])
122
+ if axis2 is None: axis2 = numpy.array([0.52573, 0, 0.85065])
123
+ elif n == 1:
124
+ if axis1 is None: axis1 = numpy.array([0.85065, 0., 0.52573])
125
+ if axis2 is None: axis2 = numpy.array([0.52573, 0.85065, 0.])
126
+ elif n == 3:
127
+ if axis1 is None: axis1 = numpy.array([0.723606, 0.525732, 0.447214])
128
+ if axis2 is None: axis2 = numpy.array([-0.276391, -0.850645, 0.447216])
129
+ elif n == 4:
130
+ if axis1 is None: axis1 = numpy.array([-0.723606, -0.525732, 0.447214])
131
+ if axis2 is None: axis2 = numpy.array([0., 0., 1.])
132
+ elif a == "O":
133
+ order1 = order2 = 4
134
+ if axis1 is None: axis1 = numpy.array([0,0,1.0])
135
+ if axis2 is None: axis2 = numpy.array([0,1.0,0])
136
+ elif a == "T":
137
+ order1 = order2 = 3
138
+ if axis1 is None: axis1 = numpy.array([0.0,0.0,1.0])
139
+ if axis2 is None: axis2 = numpy.array([0.0,0.94280904,-0.33333333])
140
+ elif a == "D":
141
+ order1 = n
142
+ if axis1 is None: axis1 = numpy.array([0,0,1.0])
143
+ order2 = 2
144
+ if axis2 is None: axis2 = numpy.array([1.,0.,0.])
145
+ elif a == "C":
146
+ order1 = n
147
+ if axis1 is None: axis1 = numpy.array([0,0,1.0])
148
+ order2 = 0
149
+ axis2 = None
150
+ return generate_operators.generate_all_elements(axis1, order1, axis2, order2)
151
+ # operators_from_symbol()
152
+
153
+ def show_operators_axis_angle(ops):
154
+ for i, op in enumerate(ops):
155
+ ax, ang = generate_operators.Rotation2AxisAngle_general(op)
156
+ logger.writeln(" operator {:3d} angle= {:7.3f} deg axis= {}".format(i+1, numpy.rad2deg(ang), numpy.array_str(ax)))
157
+ # show_operators_axis_angle()
158
+
159
+ def show_ncs_operators_axis_angle(ops):
160
+ # ops: List of gemmi.NcsOp
161
+ for i, op in enumerate(ops):
162
+ op2 = op.tr.mat.array
163
+ ax, ang = generate_operators.Rotation2AxisAngle_general(op2)
164
+ axlab = "[{: .4f}, {: .4f}, {: .4f}]".format(*ax)
165
+ trlab = "[{: 9.4f}, {: 9.4f}, {: 9.4f}]".format(*op.tr.vec.tolist())
166
+ logger.writeln(" operator {:3s} angle= {:7.3f} deg axis= {} trans= {} {}".format(op.id, numpy.rad2deg(ang),
167
+ axlab, trlab,
168
+ "given" if op.given else ""))
169
+ # show_operators_axis_angle()
170
+
171
+ def read_helical_parameters_from_mmcif(cif_in):
172
+ doc = fileio.read_cif_safe(cif_in)
173
+ b = list(filter(lambda b: b.find_loop("_atom_site.id"), doc))[0]
174
+ deltaphi = b.find_value("_em_helical_entity.angular_rotation_per_subunit")
175
+ deltaz = b.find_value("_em_helical_entity.axial_rise_per_subunit")
176
+ deltaphi, deltaz = float(deltaphi), float(deltaz)
177
+ axsym = b.find_value("_em_helical_entity.axial_symmetry")
178
+ return axsym, deltaphi, deltaz
179
+ # read_helical_parameters_from_mmcif()
180
+
181
+ def generate_helical_operators(start_xyz, center, axsym, deltaphi, deltaz, axis1=None, axis2=None,
182
+ st=None, min_n=None, max_n=None, padding=2.):
183
+ if not axsym: axsym = "C1"
184
+ if axis1 is None: axis1 = numpy.array([0,0,1.])
185
+ else: axis1 /= numpy.linalg.norm(axis1)
186
+ _, _, axtrs = operators_from_symbol(axsym, axis1, axis2)
187
+ if min_n is None or max_n is None:
188
+ assert st is not None
189
+ all_z = numpy.dot([cra.atom.pos.tolist() for cra in st[0].all()], axis1)
190
+ min_z, max_z = numpy.min(all_z), numpy.max(all_z)
191
+ direc = numpy.argmax([numpy.dot(axis1, v) for v in ((1.,0,0), (0,1.,0), (0,0,1.))]) # assume axis1 along any of a,b,c axis
192
+ if min_n is None:
193
+ min_n = -int((min_z - padding - start_xyz[direc]) / deltaz)
194
+ if max_n is None:
195
+ max_n = int((st.cell.parameters[direc] + start_xyz[direc] - max_z - padding) / deltaz)
196
+ if min_n > max_n:
197
+ min_n, max_n = max_n, min_n
198
+ ops = []
199
+ for i in range(min_n, max_n+1):
200
+ deg = deltaphi*i
201
+ t = numpy.deg2rad(deg)
202
+ m = generate_operators.AngleAxis2rotatin(axis1, t)
203
+ s = numpy.array(axis1 * deltaz*i)
204
+ for a in axtrs:
205
+ mat = numpy.dot(m, a)
206
+ news = s + numpy.dot(mat, -center) + center
207
+ tr = gemmi.Transform(gemmi.Mat33(mat), gemmi.Vec3(*news))
208
+ newop = gemmi.NcsOp(tr, str(len(ops)+1), tr.is_identity())
209
+ ops.append(newop)
210
+
211
+ return ops
212
+ # generate_helical_operators()
213
+
214
+ def make_NcsOps_from_matrices(matrices, cell=None, center=None):
215
+ if center is None:
216
+ A = cell.orth.mat.array
217
+ center = numpy.sum(A,axis=1) / 2
218
+
219
+ center = gemmi.Vec3(*center)
220
+ ops = []
221
+ for i, m in enumerate(matrices):
222
+ m = gemmi.Mat33(m)
223
+ transl = m.multiply(-center) + center
224
+ op = gemmi.NcsOp(gemmi.Transform(m, transl), str(i+1), m.is_identity())
225
+ ops.append(op)
226
+
227
+ return ops
228
+ # make_NcsOps_from_matrices()
229
+
230
+ def find_center_of_origin(mat, vec): # may not be unique.
231
+ tmp = numpy.identity(3) - numpy.array(mat.array)
232
+ ret = numpy.dot(numpy.linalg.pinv(tmp), vec.tolist())
233
+ resid = vec.tolist() - (numpy.dot(mat.array, -ret) + ret)
234
+ return gemmi.Vec3(*ret), gemmi.Vec3(*resid)
235
+ # find_center_of_origin()
236
+
237
+ def ncs_ops_for_refmac(ncs_ops):
238
+ def make_line(tr):
239
+ m = tr.mat.tolist()
240
+ m_str = " ".join([str(m[i][j]) for i in range(3) for j in range(3)])
241
+ t_str = " ".join([str(x) for x in tr.vec.tolist()])
242
+ return "ncsc matrix {} {}".format(m_str, t_str)
243
+
244
+ ret = []
245
+ # REFMAC requires identity op
246
+ if not any(x.tr.is_identity() for x in ncs_ops):
247
+ ret.append(make_line(gemmi.Transform()))
248
+
249
+ for op in ncs_ops:
250
+ if not op.given: ret.append(make_line(op.tr))
251
+ return ret
252
+ # ncs_ops_for_refmac()
253
+
254
+ # TODO def euler2matrix(euler):
255
+ # TODO def polar2matrix(polar):
256
+
257
+ def parse_ncsc_keywords(kwd_str):
258
+ # FIXME handle lines ending with -
259
+ lines = kwd_str.splitlines()
260
+ ret = []
261
+ for l in lines:
262
+ l = l.split()
263
+ if len(l) < 3: continue
264
+ if l[0].lower().startswith("ncsc"):
265
+ if l[1].lower().startswith("matr"):
266
+ vals = [float(x) for x in l[2:]]
267
+ if len(vals) != 12:
268
+ print("Bad nsc matrix line: {}".format(" ".join(l)))
269
+ continue
270
+ op = gemmi.NcsOp()
271
+ op.tr.mat.fromlist([vals[3*x:3*x+3] for x in range(3)])
272
+ op.tr.vec.fromlist(vals[9:])
273
+ op.id = str(len(ret)+1)
274
+ op.given = op.tr.is_identity()
275
+ ret.append(op)
276
+ elif l[1].lower().startswith("eule"):
277
+ pass # TODO
278
+ elif l[1].lower().startswith("pola"):
279
+ pass # TODO
280
+ return ret
281
+ # parse_ncsc_keywords()
282
+
283
+ def apply_shift_for_ncsops(ncsops, shift):
284
+ new_ops = []
285
+ s = gemmi.Vec3(*shift)
286
+ for op in ncsops:
287
+ newt = op.tr.vec + s - op.tr.mat.multiply(s)
288
+ newop = gemmi.NcsOp(gemmi.Transform(op.tr.mat, newt), op.id, op.given)
289
+ new_ops.append(newop)
290
+
291
+ return new_ops
292
+ # apply_shift_for_ncsops()
293
+
294
+ def write_symmetry_expanded_model(st, prefix, pdb=False, cif=False, cif_ref=None):
295
+ st_new = st.clone()
296
+ model.expand_ncs(st_new)
297
+ fileio.write_model(st_new, prefix=prefix, pdb=pdb, cif=cif, cif_ref=cif_ref)
298
+ # write_symmetry_expanded_model()
File without changes
@@ -0,0 +1,262 @@
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 argparse
10
+ import gemmi
11
+ import numpy
12
+ import pandas
13
+ import time
14
+ import os
15
+ import scipy.special
16
+ import scipy.optimize
17
+ from servalcat.utils import logger
18
+ from servalcat.xtal import sigmaa
19
+ from servalcat import utils
20
+ from servalcat import ext
21
+
22
+ #import line_profiler
23
+ #profile = line_profiler.LineProfiler()
24
+ #import atexit
25
+ #atexit.register(profile.print_stats)
26
+
27
+ integr = sigmaa.integr
28
+
29
+ def add_arguments(parser):
30
+ parser.description = 'Convert intensity to amplitude'
31
+ parser.add_argument('--hklin', required=True,
32
+ help='Input MTZ file')
33
+ parser.add_argument('--hklin_index', type=int, default=0,
34
+ help='block index if hklin is mmcif file (default: %(default)d)')
35
+ parser.add_argument('--labin',
36
+ help='MTZ column for I,SIGI')
37
+ parser.add_argument('--d_min', type=float)
38
+ parser.add_argument('--d_max', type=float)
39
+ parser.add_argument('--nbins', type=int,
40
+ help="Number of bins (default: auto)")
41
+ parser.add_argument('-o','--output_prefix',
42
+ help='output file name prefix')
43
+ # add_arguments()
44
+
45
+ def parse_args(arg_list):
46
+ parser = argparse.ArgumentParser()
47
+ add_arguments(parser)
48
+ return parser.parse_args(arg_list)
49
+ # parse_args()
50
+
51
+ def determine_Sigma_and_aniso(hkldata):
52
+ # initial estimate
53
+ hkldata.binned_df["ml"]["S"] = 1.
54
+ I_over_eps = hkldata.df.I.to_numpy() / hkldata.df.epsilon.to_numpy()
55
+ x = []
56
+ for i_bin, idxes in hkldata.binned("ml"):
57
+ #S = max(numpy.nanmean(I_over_eps[idxes]), 1e-3)
58
+ # var(I) = var_signal + var_noise, so this overestimates S,
59
+ # but it should be better than having negative values (in noisy shell)
60
+ S = numpy.nanstd(I_over_eps[idxes])
61
+ hkldata.binned_df["ml"].loc[i_bin, "S"] = S
62
+ x.append(S)
63
+ logger.writeln("Initial estimates:")
64
+ logger.writeln(hkldata.binned_df["ml"].to_string())
65
+
66
+ B = gemmi.SMat33d(0,0,0,0,0,0)
67
+ SMattolist = lambda B: [B.u11, B.u22, B.u33, B.u12, B.u13, B.u23]
68
+ adpdirs = utils.model.adp_constraints(hkldata.sg.operations(), hkldata.cell, tr0=True)
69
+ logger.writeln("ADP free parameters = {}".format(adpdirs.shape[0]))
70
+ ssqmat = hkldata.ssq_mat()
71
+ cycle_data = [[0] + SMattolist(B) + list(hkldata.binned_df["ml"].S)]
72
+ for icyc in range(100):
73
+ #logger.writeln("Refine B")
74
+ B_converged = False
75
+ t0 = time.time()
76
+ args=(ssqmat, hkldata, adpdirs)
77
+ for j in range(10):
78
+ x = numpy.dot(SMattolist(B), numpy.linalg.pinv(adpdirs))
79
+ f0 = ll_all_B(x, *args)
80
+ shift = ll_shift_B(x, *args)
81
+ for i in range(3):
82
+ ss = shift / 2**i
83
+ f1 = ll_all_B(x + ss, *args)
84
+ #logger.writeln("f0 = {:.3e} shift = {} df = {:.3e}".format(f0, ss, f1 - f0))
85
+ if f1 < f0:
86
+ B = gemmi.SMat33d(*numpy.dot(x+ss, adpdirs))
87
+ if numpy.max(numpy.abs(ss)) < 1e-4: B_converged = True
88
+ break
89
+ else:
90
+ B_converged = True
91
+ if B_converged: break
92
+
93
+ #logger.writeln("time= {}".format(time.time() - t0))
94
+ #logger.writeln("B_aniso= {}".format(B))
95
+ #logger.writeln("Refine S")
96
+ S_converged = [False for _ in hkldata.binned("ml")]
97
+ k_ani = hkldata.debye_waller_factors(b_cart=B)
98
+ for i, (i_bin, idxes) in enumerate(hkldata.binned("ml")):
99
+ #logger.writeln("Bin {}".format(i_bin))
100
+ for j in range(10):
101
+ S = hkldata.binned_df["ml"].loc[i_bin, "S"]
102
+ f0 = numpy.nansum(integr.ll_int(hkldata.df.I.to_numpy()[idxes], hkldata.df.SIGI.to_numpy()[idxes], k_ani[idxes],
103
+ S * hkldata.df.epsilon.to_numpy()[idxes],
104
+ numpy.zeros(len(idxes)), hkldata.df.centric.to_numpy()[idxes]+1,
105
+ hkldata.df.llweight.to_numpy()[idxes]))
106
+ shift = numpy.exp(ll_shift_bin_S(hkldata.df.I.to_numpy()[idxes], hkldata.df.SIGI.to_numpy()[idxes], k_ani[idxes],
107
+ S, hkldata.df.centric.to_numpy()[idxes]+1, hkldata.df.epsilon.to_numpy()[idxes],
108
+ hkldata.df.llweight.to_numpy()[idxes]))
109
+ for k in range(3):
110
+ ss = shift**(1. / 2**k)
111
+ f1 = numpy.nansum(integr.ll_int(hkldata.df.I.to_numpy()[idxes], hkldata.df.SIGI.to_numpy()[idxes], k_ani[idxes],
112
+ S * ss * hkldata.df.epsilon.to_numpy()[idxes],
113
+ numpy.zeros(len(idxes)), hkldata.df.centric.to_numpy()[idxes]+1,
114
+ hkldata.df.llweight.to_numpy()[idxes]))
115
+ #logger.writeln("bin {:3d} f0 = {:.3e} shift = {:.3e} df = {:.3e}".format(i_bin, f0, ss, f1 - f0))
116
+ if f1 < f0:
117
+ hkldata.binned_df["ml"].loc[i_bin, "S"] = S * ss
118
+ if ss > 0.9999: S_converged[i] = True
119
+ break
120
+ else:
121
+ S_converged[i] = True
122
+ if S_converged[i]: break
123
+
124
+ #logger.writeln("Refined estimates in cycle {}:".format(icyc))
125
+ #logger.writeln(hkldata.binned_df["ml"].to_string())
126
+ #logger.writeln("B_aniso= {}".format(B))
127
+ cycle_data.append([icyc] + SMattolist(B) + list(hkldata.binned_df["ml"].S))
128
+ if B_converged and all(S_converged):
129
+ logger.writeln("Converged in cycle {}".format(icyc))
130
+ logger.writeln("Refined estimates:")
131
+ logger.writeln(hkldata.binned_df["ml"].to_string())
132
+ logger.writeln("B_aniso= {}".format(B))
133
+ break
134
+
135
+ #with open("fw_cycles.dat", "w") as ofs:
136
+ # ofs.write("cycle B11 B22 B33 B12 B13 B23 " + " ".join("S{}".format(i) for i in hkldata.binned_df["ml"].index) + "\n")
137
+ # for data in cycle_data:
138
+ # ofs.write("{:2d} ".format(data[0]+1))
139
+ # ofs.write(" ".join("{:.4e}".format(x) for x in data[1:]))
140
+ # ofs.write("\n")
141
+
142
+ return B
143
+
144
+ def ll_all_B(x, ssqmat, hkldata, adpdirs):
145
+ B = gemmi.SMat33d(*numpy.dot(x, adpdirs))
146
+ k_ani = hkldata.debye_waller_factors(b_cart=B)
147
+ ret = 0.
148
+ for i_bin, idxes in hkldata.binned("ml"):
149
+ ret += numpy.nansum(integr.ll_int(hkldata.df.I.to_numpy()[idxes], hkldata.df.SIGI.to_numpy()[idxes], k_ani[idxes],
150
+ hkldata.binned_df["ml"].S[i_bin] * hkldata.df.epsilon.to_numpy()[idxes],
151
+ numpy.zeros(len(idxes)), hkldata.df.centric.to_numpy()[idxes]+1,
152
+ hkldata.df.llweight.to_numpy()[idxes]))
153
+ return ret
154
+
155
+ def ll_shift_bin_S(Io, sigIo, k_ani, S, c, eps, llw, exp_trans=True):
156
+ tmp = integr.ll_int_fw_der1_S(Io, sigIo, k_ani, S, c, eps, llw)
157
+ g = numpy.nansum(tmp)
158
+ H = numpy.nansum(tmp**2)
159
+ if exp_trans:
160
+ return -g / (H * S + g)
161
+ else:
162
+ return -g / H
163
+
164
+ def ll_shift_B(x, ssqmat, hkldata, adpdirs):
165
+ b_aniso = gemmi.SMat33d(*numpy.dot(x, adpdirs))
166
+ k_ani = hkldata.debye_waller_factors(b_cart=b_aniso)
167
+ Io = hkldata.df.I.to_numpy()
168
+ sigIo = hkldata.df.SIGI.to_numpy()
169
+ c = hkldata.df.centric.to_numpy() + 1
170
+ epsilon = hkldata.df.epsilon.to_numpy()
171
+ llw = hkldata.df.llweight.to_numpy()
172
+ r = numpy.empty(len(Io)) * numpy.nan
173
+ for i_bin, idxes in hkldata.binned("ml"):
174
+ r[idxes] = integr.ll_int_fw_der1_ani(Io[idxes], sigIo[idxes],
175
+ k_ani[idxes], hkldata.binned_df["ml"].S[i_bin],
176
+ c[idxes], epsilon[idxes], llw[idxes])
177
+ g = -numpy.nansum(ssqmat * r, axis=1)
178
+ H = numpy.nansum(numpy.matmul(ssqmat[None,:].T, ssqmat.T[:,None]) * (r**2)[:,None,None], axis=0)
179
+ g, H = numpy.dot(g, adpdirs.T), numpy.dot(adpdirs, numpy.dot(H, adpdirs.T))
180
+ return -numpy.dot(g, numpy.linalg.pinv(H))
181
+
182
+ def expected_F_from_int(Io, sigo, k_ani, eps, c, S):
183
+ to = Io / sigo - sigo / c / k_ani**2 / S / eps
184
+ tf = numpy.zeros(Io.size)
185
+ sig1 = numpy.ones(Io.size)
186
+ k_num = numpy.where(c == 1, 0.5, 0.)
187
+ F = numpy.sqrt(sigo) * ext.integ_J_ratio(k_num, k_num - 0.5, False, to, tf, sig1, c,
188
+ integr.exp2_threshold, integr.h, integr.N, integr.ewmax)
189
+ Fsq = sigo * ext.integ_J_ratio(k_num + 0.5, k_num - 0.5, False, to, tf, sig1, c,
190
+ integr.exp2_threshold, integr.h, integr.N, integr.ewmax)
191
+ varF = Fsq - F**2
192
+ return F, numpy.sqrt(varF)
193
+
194
+ def french_wilson(hkldata, B_aniso, labout=None):
195
+ if labout is None: labout = ["F", "SIGF"]
196
+ k_ani = hkldata.debye_waller_factors(b_cart=B_aniso)
197
+ has_ano = "I(+)" in hkldata.df and "I(-)" in hkldata.df
198
+ if has_ano:
199
+ ano_data = hkldata.df[["I(+)", "SIGI(+)", "I(-)", "SIGI(-)"]].to_numpy()
200
+ if len(labout) == 2:
201
+ labout += [f"{labout[0]}(+)", f"{labout[1]}(+)", f"{labout[0]}(-)", f"{labout[1]}(-)"]
202
+ hkldata.df[labout] = numpy.nan
203
+ for i_bin, idxes in hkldata.binned("ml"):
204
+ S = hkldata.binned_df["ml"].S[i_bin]
205
+ c = hkldata.df.centric.to_numpy()[idxes] + 1 # 1 for acentric, 2 for centric
206
+ Io = hkldata.df.I.to_numpy()[idxes]
207
+ sigo = hkldata.df.SIGI.to_numpy()[idxes]
208
+ eps = hkldata.df.epsilon.to_numpy()[idxes]
209
+ F, sigF = expected_F_from_int(Io, sigo, k_ani[idxes], eps, c, S)
210
+ hkldata.df.loc[idxes, labout[0]] = F
211
+ hkldata.df.loc[idxes, labout[1]] = sigF
212
+ if has_ano:
213
+ Fp, sigFp = expected_F_from_int(ano_data[idxes,0], ano_data[idxes,1], k_ani[idxes], eps, c, S)
214
+ Fm, sigFm = expected_F_from_int(ano_data[idxes,2], ano_data[idxes,3], k_ani[idxes], eps, c, S)
215
+ hkldata.df.loc[idxes, labout[2]] = Fp
216
+ hkldata.df.loc[idxes, labout[3]] = sigFp
217
+ hkldata.df.loc[idxes, labout[4]] = Fm
218
+ hkldata.df.loc[idxes, labout[5]] = sigFm
219
+
220
+ def main(args):
221
+ if not args.output_prefix:
222
+ args.output_prefix = utils.fileio.splitext(os.path.basename(args.hklin))[0] + "_fw"
223
+ try:
224
+ mtz = utils.fileio.read_mmhkl(args.hklin, cif_index=args.hklin_index)
225
+ except RuntimeError as e:
226
+ raise SystemExit("Error: {}".format(e))
227
+ if not args.labin:
228
+ labin = sigmaa.decide_mtz_labels(mtz, require=("K", "J"))
229
+ else:
230
+ labin = args.labin.split(",")
231
+ try:
232
+ hkldata, _, _, _, _ = sigmaa.process_input(hklin=mtz,
233
+ labin=labin,
234
+ n_bins_ml=args.nbins,
235
+ free=None,
236
+ xyzins=[],
237
+ d_min=args.d_min,
238
+ n_per_mlbin=500,
239
+ max_mlbins=30,
240
+ cif_index=args.hklin_index)
241
+ except RuntimeError as e:
242
+ raise SystemExit("Error: {}".format(e))
243
+
244
+ B_aniso = determine_Sigma_and_aniso(hkldata)
245
+ french_wilson(hkldata, B_aniso)
246
+ mtz_out = args.output_prefix+".mtz"
247
+ lab_out = ["F", "SIGF", "I", "SIGI"]
248
+ labo_types = {"F":"F", "SIGF":"Q", "I":"J", "SIGI":"Q"}
249
+ if "I(+)" in hkldata.df and "I(-)" in hkldata.df:
250
+ lab_out += ["F(+)", "SIGF(+)", "F(-)", "SIGF(-)"]
251
+ labo_types.update({"F(+)":"G", "SIGF(+)":"L", "F(-)":"G", "SIGF(-)":"L"})
252
+ if len(labin) == 3:
253
+ lab_out.append("FREE")
254
+ labo_types[lab_out[-1]] = "I"
255
+ hkldata.write_mtz(mtz_out, lab_out, types=labo_types)
256
+ return B_aniso, hkldata
257
+ # main()
258
+
259
+ if __name__ == "__main__":
260
+ import sys
261
+ args = parse_args(sys.argv[1:])
262
+ main(args)