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
servalcat/spa/fofc.py ADDED
@@ -0,0 +1,488 @@
1
+ # TODO shift map first using mask!
2
+ """
3
+ Author: "Keitaro Yamashita, Garib N. Murshudov"
4
+ MRC Laboratory of Molecular Biology
5
+
6
+ This software is released under the
7
+ Mozilla Public License, version 2.0; see LICENSE.
8
+ """
9
+ from __future__ import absolute_import, division, print_function, generators
10
+ import gemmi
11
+ import numpy
12
+ import time
13
+ from servalcat.utils import logger
14
+ from servalcat import utils
15
+ from servalcat.spa import shift_maps
16
+ import argparse
17
+
18
+ def add_arguments(parser):
19
+ parser.description = 'Fo-Fc map calculation based on model and data errors'
20
+ group = parser.add_mutually_exclusive_group()
21
+ group.add_argument("--halfmaps", nargs=2)
22
+ group.add_argument("--map", help="Use only if you really do not have half maps.")
23
+ parser.add_argument('--pixel_size', type=float,
24
+ help='Override pixel size (A)')
25
+ parser.add_argument('--model', required=True,
26
+ help='Input atomic model file')
27
+ parser.add_argument("-d", '--resolution', type=float, required=True)
28
+ parser.add_argument('-m', '--mask', help="mask file")
29
+ parser.add_argument('-r', '--mask_radius', type=float, help="mask radius (not used if --mask is given)")
30
+ parser.add_argument('--no_check_mask_with_model', action='store_true',
31
+ help='Disable mask test using model')
32
+ parser.add_argument("-B", type=float, help="Estimated blurring")
33
+ parser.add_argument("--half1_only", action='store_true', help="Only use half 1 for map calculation (use half 2 only for noise estimation)")
34
+ parser.add_argument("--normalized_map", action='store_true',
35
+ help="Write normalized map in the masked region. Now this is on by default.")
36
+ parser.add_argument("--no_fsc_weights", action='store_true',
37
+ help="Just for debugging purpose: turn off FSC-based weighting")
38
+ parser.add_argument("--sharpening_b", type=float,
39
+ help="Use B value (negative value for sharpening) instead of standard deviation of the signal")
40
+ parser.add_argument("--trim", action='store_true',
41
+ help="Write trimmed maps")
42
+ parser.add_argument("--trim_mtz", action='store_true',
43
+ help="Write trimmed mtz")
44
+ parser.add_argument("--monlib",
45
+ help="Monomer library path. Default: $CLIBD_MON")
46
+ parser.add_argument("--omit_proton", action='store_true',
47
+ #help="Omit hydrogen proton (leaving electrons) from model in map calculation")
48
+ help=argparse.SUPPRESS)
49
+ parser.add_argument("--omit_h_electron", action='store_true',
50
+ #help="Omit hydrogen electrons (leaving protons) from model in map calculation")
51
+ help=argparse.SUPPRESS)
52
+ parser.add_argument("-s", "--source", choices=["electron", "xray", "neutron", "custom"], default="electron")
53
+ parser.add_argument('-o','--output_prefix', default="diffmap",
54
+ help='output file name prefix (default: %(default)s)')
55
+ parser.add_argument('--keep_charges', action='store_true',
56
+ help="Use scattering factor for charged atoms. Use it with care.")
57
+ # add_arguments()
58
+
59
+ def parse_args(arg_list):
60
+ parser = argparse.ArgumentParser()
61
+ add_arguments(parser)
62
+ return parser.parse_args(arg_list)
63
+ # parse_args()
64
+
65
+ def calc_D_and_S(hkldata, has_halfmaps=True, half1_only=False):#fo_asu, fc_asu, varn, bins, bin_idxes):
66
+ bdf = hkldata.binned_df["ml"]
67
+ bdf["D"] = 0.
68
+ bdf["S"] = 0.
69
+ stats_str = """$TABLE: Statistics :
70
+ $GRAPHS
71
+ : log(Mn(|F|^2)) and variances :A:1,6,7,8,13,14:
72
+ : FSC :A:1,9,10,11:
73
+ : weights :A:1,12,15,16:
74
+ : map weights :A:1,17:
75
+ $$
76
+ 1/resol^2 bin n d_max d_min log(var(Fo)) log(var(Fc)) log(var(DFc)) FSC.model FSC.full sqrt(FSC.full) D log(var_U,T) log(var_noise) wFo wFc wFo.sharpen
77
+ $$
78
+ $$
79
+ """
80
+ tmpl = "{:.4f} {:3d} {:7d} {:7.3f} {:7.3f} {:.4e} {:.4e} {:4e} {: .4f} {: .4f} {: .4f} {: .4e} {:.4e} {:.4e} {:.4f} {:.4f} {:.4e}\n"
81
+
82
+ var_noise = None
83
+ FP = hkldata.df.FP.to_numpy()
84
+ if half1_only:
85
+ FP = hkldata.df.F_map1.to_numpy()
86
+ var_noise = hkldata.binned_df["ml"].var_noise * 2
87
+ elif has_halfmaps:
88
+ var_noise = hkldata.binned_df["ml"].var_noise
89
+
90
+ for i_bin, idxes in hkldata.binned("ml"):
91
+ bin_d_min = hkldata.binned_df["ml"].d_min[i_bin]
92
+ bin_d_max = hkldata.binned_df["ml"].d_max[i_bin]
93
+ Fo = FP[idxes]
94
+ Fc = hkldata.df.FC.to_numpy()[idxes]
95
+ fsc = numpy.real(numpy.corrcoef(Fo, Fc)[1,0])
96
+ bdf.loc[i_bin, "D"] = numpy.sum(numpy.real(Fo * numpy.conj(Fc)))/numpy.sum(numpy.abs(Fc)**2)
97
+ if has_halfmaps:
98
+ varn = var_noise[i_bin]
99
+ fsc_full = hkldata.binned_df["ml"].FSCfull[i_bin]
100
+ S = max(0, numpy.average(numpy.abs(Fo-bdf.D[i_bin]*Fc)**2)-varn)
101
+ bdf.loc[i_bin, "S"] = S
102
+ w = S/(S+varn)
103
+ if fsc_full < 0: # this should be fixed actually. needs smoothing to zero.
104
+ w_sharpen = 0
105
+ else:
106
+ w_sharpen = w / numpy.sqrt(fsc_full) / numpy.std(Fo)
107
+ else:
108
+ varn = fsc_full = 0
109
+ w = 1
110
+ w_sharpen = 1
111
+
112
+ with numpy.errstate(divide="ignore", invalid="ignore"):
113
+ stats_str += tmpl.format(1/bin_d_min**2, i_bin, Fo.size, bin_d_max, bin_d_min,
114
+ numpy.log(numpy.average(numpy.abs(Fo)**2)),
115
+ numpy.log(numpy.average(numpy.abs(Fc)**2)),
116
+ numpy.log(bdf.D[i_bin]**2*numpy.average(numpy.abs(Fc)**2)),
117
+ fsc, fsc_full, numpy.sqrt(fsc_full), bdf.D[i_bin],
118
+ numpy.log(bdf.S[i_bin]), numpy.log(varn),
119
+ w, 1-w, w_sharpen)
120
+ return stats_str
121
+ # calc_D_and_S()
122
+
123
+ #import line_profiler
124
+ #profile = line_profiler.LineProfiler()
125
+ #import atexit
126
+ #atexit.register(profile.print_stats)
127
+ #@profile
128
+ def calc_maps(hkldata, B=None, has_halfmaps=True, half1_only=False, no_fsc_weights=False, sharpening_b=None):
129
+ has_fc = "FC" in hkldata.df
130
+
131
+ if not has_fc:
132
+ labs = ["FWT"]
133
+ if B is not None: labs.append("FWT_b0")
134
+ elif has_halfmaps:
135
+ labs = ["Fupdate", "DELFWT", "FWT", "DELFWT_noscale", "Fupdate_noscale"]
136
+ if B is not None: labs.extend(["Fupdate_b0", "DELFWT_b0", "FWT_b0"])
137
+ else:
138
+ labs = ["DELFWT"]
139
+
140
+ tmp = {}
141
+ for l in labs:
142
+ tmp[l] = numpy.zeros(len(hkldata.df.index), numpy.complex128)
143
+
144
+ logger.writeln("Calculating maps..")
145
+ logger.write(" sharpening method: ")
146
+ if sharpening_b is None:
147
+ logger.writeln("1/sqrt(FSC * Mn(Fo)) for Fo and 1/sigma_U,T for Fo-Fc")
148
+ else:
149
+ logger.writeln("1/exp(-B*s^2/4) with B= {:.2f}".format(sharpening_b))
150
+
151
+ time_t = time.time()
152
+
153
+ if half1_only:
154
+ FP = hkldata.df.F_map1.to_numpy()
155
+ else:
156
+ FP = hkldata.df.FP.to_numpy()
157
+
158
+ s2 = 1./hkldata.d_spacings().to_numpy()**2
159
+
160
+ fsc_became_negative = False
161
+
162
+ for i_bin, idxes in hkldata.binned("ml"):
163
+ if has_halfmaps:
164
+ fsc = hkldata.binned_df["ml"].FSCfull[i_bin] # FSCfull
165
+ if half1_only:
166
+ varn = hkldata.binned_df["ml"].var_noise[i_bin] * 2
167
+ fsc = fsc/(2-fsc) # to FSChalf
168
+ else:
169
+ varn = hkldata.binned_df["ml"].var_noise[i_bin]
170
+ else:
171
+ fsc, varn = 1., 0.
172
+
173
+ w_nomodel = 1. if no_fsc_weights else fsc
174
+ Fo = FP[idxes]
175
+ sig_fo = numpy.std(Fo)
176
+ s2_bin = s2[idxes]
177
+
178
+ if has_fc:
179
+ Fc = hkldata.df.FC.to_numpy()[idxes]
180
+ D = hkldata.binned_df["ml"].D[i_bin]
181
+ S = hkldata.binned_df["ml"].S[i_bin] # variance of unexplained signal
182
+ w = 1. if no_fsc_weights or not has_halfmaps else S/(S+varn)
183
+ delfwt = w * (Fo-D*Fc)
184
+ fup = 2 * w * Fo + (1 - 2*w) * D*Fc # <F> + delfwt
185
+ if has_halfmaps: # no point making this map when half maps not given
186
+ tmp["DELFWT_noscale"][idxes] = delfwt
187
+ tmp["Fupdate_noscale"][idxes] = fup
188
+
189
+ if not fsc_became_negative and fsc <= 0:
190
+ logger.writeln(" WARNING: cutting resolution at {:.2f} A because fsc < 0".format(hkldata.binned_df["ml"].d_max[i_bin]))
191
+ fsc_became_negative = True
192
+ if fsc_became_negative:
193
+ continue
194
+
195
+ if sharpening_b is None:
196
+ k = sig_fo * numpy.sqrt(fsc)
197
+ k_fofc = numpy.sqrt(S) if has_fc and S > 0 else 1. # to avoid zero-division. if S=0 then w=0.
198
+ else:
199
+ k = k_fofc = numpy.exp(-sharpening_b*s2_bin/4)
200
+
201
+ lab_suf = "" if B is None else "_b0"
202
+ if has_halfmaps:
203
+ tmp["FWT"+lab_suf][idxes] = w_nomodel / k * Fo
204
+ if has_fc:
205
+ tmp["DELFWT"+lab_suf][idxes] = delfwt / k_fofc
206
+ tmp["Fupdate"+lab_suf][idxes] = fup / k
207
+ elif has_fc:
208
+ tmp["DELFWT"+lab_suf][idxes] = delfwt
209
+
210
+ if B is not None and has_halfmaps: # local B based map
211
+ k_l = numpy.exp(-B*s2_bin/4.)
212
+ k2_l = numpy.exp(-B*s2_bin/2.)
213
+ fsc_l = k2_l*fsc/(1+(k2_l-1)*fsc)
214
+ w_nomodel = 1. if no_fsc_weights else fsc_l
215
+ tmp["FWT"][idxes] = Fo*w_nomodel/k/k_l
216
+ if has_fc:
217
+ S_l = S * k2_l
218
+ w = 1. if no_fsc_weights or not has_halfmaps else S_l/(S_l+varn)
219
+ delfwt = (Fo-D*Fc)*w/k_fofc/k_l
220
+ fup = (w*Fo+(1.-w)*D*Fc)/k/k_l
221
+ logger.writeln("{:4d} {:.4e} {:.4e} {:.4e} {:.4e} {:.4e} {:.4e}".format(i_bin,
222
+ numpy.average(k),
223
+ numpy.average(sig_fo),
224
+ numpy.average(fsc_l),
225
+ numpy.average(k_l),
226
+ numpy.average(abs(fup)),
227
+ numpy.average(abs(delfwt))))
228
+ tmp["DELFWT"][idxes] = delfwt
229
+ tmp["Fupdate"][idxes] = fup
230
+
231
+ for l in labs:
232
+ hkldata.df[l] = tmp[l]
233
+
234
+ logger.writeln(" finished in {:.3f} sec.".format(time.time()-time_t))
235
+ return labs
236
+ # calc_maps()
237
+
238
+ def dump_to_mtz(hkldata, map_labs, mtz_out):
239
+ extra_labs = list(filter(lambda x: x in hkldata.df, ["FP", "FC"]))
240
+ map_labs = map_labs + extra_labs
241
+ hkldata.write_mtz(mtz_out, map_labs)
242
+ # dump_to_mtz()
243
+
244
+ def calc_fofc(st, d_min, maps, mask=None, monlib=None, B=None, half1_only=False,
245
+ no_fsc_weights=False, sharpening_b=None, omit_proton=False, omit_h_electron=False,
246
+ source="electron"):
247
+ if no_fsc_weights:
248
+ logger.writeln("WARNING: --no_fsc_weights is requested.")
249
+ if sharpening_b is not None:
250
+ logger.writeln("WARNING: --sharpening_b={} is given".format(sharpening_b))
251
+
252
+ hkldata = utils.maps.mask_and_fft_maps(maps, d_min, mask)
253
+ hkldata.df["FC"] = utils.model.calc_fc_fft(st, d_min - 1e-6, monlib=monlib, source=source,
254
+ miller_array=hkldata.miller_array())
255
+ if mask is not None:
256
+ fc_map = hkldata.fft_map("FC", grid_size=mask.shape)
257
+ fc_map.array[:] *= mask
258
+ hkldata.df["FC"] = gemmi.transform_map_to_f_phi(fc_map).get_value_by_hkl(hkldata.miller_array())
259
+
260
+ hkldata.setup_relion_binning("ml")
261
+
262
+ has_halfmaps = (len(maps) == 2)
263
+ if has_halfmaps:
264
+ utils.maps.calc_noise_var_from_halfmaps(hkldata)
265
+
266
+ stats_str = calc_D_and_S(hkldata, has_halfmaps=has_halfmaps, half1_only=half1_only)
267
+
268
+ if omit_proton or omit_h_electron:
269
+ hkldata.df["FC"] = utils.model.calc_fc_fft(st, d_min - 1e-6, monlib=monlib, source=source,
270
+ omit_proton=omit_proton, omit_h_electron=omit_h_electron,
271
+ miller_array=hkldata.miller_array())
272
+
273
+ map_labs = calc_maps(hkldata, B=B, has_halfmaps=has_halfmaps, half1_only=half1_only,
274
+ no_fsc_weights=no_fsc_weights, sharpening_b=sharpening_b)
275
+ return hkldata, map_labs, stats_str
276
+ # calc_fofc()
277
+
278
+ def write_files(hkldata, map_labs, grid_start, stats_str,
279
+ mask=None, output_prefix="diffmap", trim_map=False, trim_mtz=False,
280
+ normalize_map=True, omit_h_electron=False):
281
+ # this function may modify the overall scale of FWT/DELFWT.
282
+
283
+ if mask is not None and (trim_map or trim_mtz):
284
+ new_cell, new_shape, new_grid_start, shifts = shift_maps.determine_shape_and_shift(mask=gemmi.FloatGrid(mask.array,
285
+ hkldata.cell,
286
+ hkldata.sg),
287
+ grid_start=grid_start,
288
+ padding=5,
289
+ mask_cutoff=0.5,
290
+ noncentered=True,
291
+ noncubic=True,
292
+ json_out=None)
293
+ else:
294
+ new_cell, new_shape, new_grid_start, shifts = None, None, None, None
295
+
296
+ if trim_map:
297
+ grid_start_for_map = new_grid_start
298
+ shape_for_map = new_shape
299
+ else:
300
+ grid_start_for_map = grid_start
301
+ shape_for_map = None
302
+
303
+ if normalize_map and mask is not None:
304
+ cutoff = 0.5
305
+ if "DELFWT" in hkldata.df:
306
+ logger.writeln("Normalized Fo-Fc map requested.")
307
+ delfwt_map = hkldata.fft_map("DELFWT", grid_size=mask.shape)
308
+ masked = delfwt_map.array[mask.array>cutoff]
309
+ logger.writeln(" Whole volume: {} voxels".format(delfwt_map.point_count))
310
+ logger.writeln(" Masked volume: {} voxels (>{})".format(masked.size, cutoff))
311
+ global_mean = numpy.average(delfwt_map)
312
+ global_std = numpy.std(delfwt_map)
313
+ logger.writeln(" Global mean: {:.3e}".format(global_mean))
314
+ logger.writeln(" Global std: {:.3e}".format(global_std))
315
+ masked_mean = numpy.average(masked)
316
+ masked_std = numpy.std(masked)
317
+ logger.writeln(" Masked mean: {:.3e}".format(masked_mean))
318
+ logger.writeln(" Masked std: {:.3e}".format(masked_std))
319
+ #logger.writeln(" If you want to scale manually: {}".format())
320
+ scaled = (delfwt_map - masked_mean)/masked_std
321
+ hkldata.df["DELFWT"] /= masked_std # it would work if masked_mean~0
322
+ if omit_h_electron:
323
+ scaled *= -1
324
+ filename = "{}_normalized_fofc_flipsign.mrc".format(output_prefix)
325
+ else:
326
+ filename = "{}_normalized_fofc.mrc".format(output_prefix)
327
+ logger.writeln(" Writing {}".format(filename))
328
+ utils.maps.write_ccp4_map(filename, scaled, cell=hkldata.cell,
329
+ grid_start=grid_start_for_map, grid_shape=shape_for_map)
330
+
331
+ # Write Fo map as well
332
+ if "FWT" in hkldata.df:
333
+ fwt_map = hkldata.fft_map("FWT", grid_size=mask.shape)
334
+ masked = fwt_map.array[mask.array>cutoff]
335
+ masked_mean = numpy.average(masked)
336
+ masked_std = numpy.std(masked)
337
+ scaled = (fwt_map - masked_mean)/masked_std # does not make much sense for Fo map though
338
+ hkldata.df["FWT"] /= masked_std # it would work if masked_mean~0
339
+ filename = "{}_normalized_fo.mrc".format(output_prefix)
340
+ logger.writeln(" Writing {}".format(filename))
341
+ utils.maps.write_ccp4_map(filename, scaled, cell=hkldata.cell,
342
+ grid_start=grid_start_for_map, grid_shape=shape_for_map)
343
+
344
+ if trim_mtz and shifts is not None:
345
+ hkldata2 = utils.hkl.HklData(new_cell, hkldata.sg, df=None)
346
+ d_min = hkldata.d_min_max()[0]
347
+ for lab in map_labs + ["FP", "FC"]:
348
+ if lab not in hkldata.df: continue
349
+ gr = hkldata.fft_map(lab, grid_size=mask.shape)
350
+ gr = gemmi.FloatGrid(gr.get_subarray(new_grid_start, new_shape),
351
+ new_cell, hkldata.sg)
352
+ if hkldata2.df is None:
353
+ ad = gemmi.transform_map_to_f_phi(gr).prepare_asu_data(dmin=d_min)
354
+ hkldata2.merge_asu_data(ad, lab)
355
+ else:
356
+ hkldata2.df[lab] = gemmi.transform_map_to_f_phi(gr).get_value_by_hkl(hkldata2.miller_array())
357
+ hkldata2.translate(lab, -shifts)
358
+ hkldata = hkldata2
359
+
360
+ dump_to_mtz(hkldata, map_labs, "{}_maps.mtz".format(output_prefix))
361
+ if stats_str:
362
+ with open("{}_Fstats.log".format(output_prefix), "w") as f:
363
+ f.write(stats_str)
364
+ # write_files()
365
+
366
+ def write_coot_script(py_out, model_file, mtz_file, contour_fo=1.2, contour_fofc=3.0, ncs_ops=None):
367
+ with open(py_out, "w") as ofs:
368
+ ofs.write('imol = read_pdb("{}")\n'.format(model_file)) # TODO safer
369
+ ofs.write('imol_fo = make_and_draw_map("{}", "FWT", "PHWT", "", 0, 0)\n'.format(mtz_file))
370
+ ofs.write('imol_fofc = make_and_draw_map("{}", "DELFWT", "PHDELWT", "", 0, 1)\n'.format(mtz_file))
371
+ if contour_fo is not None:
372
+ ofs.write('set_contour_level_absolute(imol_fo, {:.1f})\n'.format(contour_fo))
373
+ if contour_fofc is not None:
374
+ ofs.write('set_contour_level_absolute(imol_fofc, {:.1f})\n'.format(contour_fofc))
375
+ if ncs_ops is not None:
376
+ for op in ncs_ops:
377
+ if op.given: continue
378
+ c, resid = utils.symmetry.find_center_of_origin(op.tr.mat, op.tr.vec)
379
+ if resid.length() > 1e-6: continue # coot does not support translation..
380
+ v = [y for x in op.tr.mat.tolist() for y in x] + c.tolist()
381
+ ofs.write("add_molecular_symmetry(imol, {})\n".format(",".join(str(x) for x in v)))
382
+ # write_coot_script()
383
+
384
+ def write_chimerax_script(cxc_out, model_file, fo_mrc_file, fofc_mrc_file):
385
+ with open(cxc_out, "w") as ofs:
386
+ ofs.write('open {}\n'.format(model_file))
387
+ ofs.write('open {}\n'.format(fo_mrc_file))
388
+ ofs.write('open {}\n'.format(fofc_mrc_file))
389
+ ofs.write('volume #3 level 4 level -4 color #00FF00 color #FF0000 squaremesh false cap false style mesh meshlighting false\n')
390
+ ofs.write('isolde start\n')
391
+ ofs.write('clipper associate #2 toModel #1\n')
392
+ ofs.write('clipper associate #3 toModel #1\n')
393
+ # write_chimerax_script()
394
+
395
+ def main(args):
396
+ if not args.halfmaps and not args.map:
397
+ raise SystemExit("Error: give --halfmaps or --map")
398
+
399
+ if not args.halfmaps and args.B is not None:
400
+ raise SystemExit("Error: -B only works with half maps")
401
+
402
+ if args.half1_only:
403
+ if not args.halfmaps:
404
+ raise SystemExit("--half1_only requires half maps")
405
+ logger.error("--half1_only specified. Half map 2 is used only for noise estimation")
406
+
407
+ if args.normalized_map:
408
+ logger.writeln("DeprecationWarning: --normalized_map is now on by default. This option will be removed in the future.")
409
+
410
+ if not args.halfmaps:
411
+ logger.error("Warning: using --halfmaps is strongly recommended!")
412
+
413
+ st = utils.fileio.read_structure(args.model)
414
+ ccu = utils.model.CustomCoefUtil()
415
+ if not args.keep_charges:
416
+ utils.model.remove_charge([st])
417
+ if args.source == "custom":
418
+ ccu.read_from_cif(st, args.model)
419
+ ccu.show_info()
420
+ ccu.set_coeffs(st)
421
+ else:
422
+ utils.model.check_atomsf([st], args.source)
423
+ ncs_org = gemmi.NcsOpList(st.ncs)
424
+ utils.model.expand_ncs(st)
425
+
426
+ if (args.omit_proton or args.omit_h_electron) and not st[0].has_hydrogen():
427
+ raise SystemExit("ERROR! --omit_proton/--omit_h_electron requested, but no hydrogen atoms were found.")
428
+
429
+ if args.halfmaps:
430
+ maps = utils.fileio.read_halfmaps(args.halfmaps, pixel_size=args.pixel_size)
431
+ has_halfmaps = True
432
+ else:
433
+ maps = [utils.fileio.read_ccp4_map(args.map, pixel_size=args.pixel_size)]
434
+ has_halfmaps = False
435
+
436
+ grid_start = maps[0][1]
437
+ g = maps[0][0]
438
+ st.spacegroup_hm = "P1"
439
+ st.cell = g.unit_cell
440
+
441
+ if st[0].has_hydrogen():
442
+ monlib = utils.restraints.load_monomer_library(st, monomer_dir=args.monlib)
443
+ else:
444
+ monlib = None
445
+
446
+ if args.mask:
447
+ mask = utils.fileio.read_ccp4_map(args.mask)[0]
448
+ if not args.no_check_mask_with_model:
449
+ if not utils.maps.test_mask_with_model(mask, st):
450
+ raise SystemExit("\nError: Model is out of mask.\n"
451
+ "Please check your --model and --mask. You can disable this test with --no_check_mask_with_model.")
452
+ elif args.mask_radius:
453
+ mask = utils.maps.mask_from_model(st, args.mask_radius, grid=g)
454
+ utils.maps.write_ccp4_map("mask_from_model.ccp4", mask)
455
+ else:
456
+ mask = None
457
+ logger.writeln("Warning: Mask is needed for map normalization. Use --mask or --mask_radius if you want normalized map.")
458
+
459
+ hkldata, map_labs, stats_str = calc_fofc(st, args.resolution, maps, mask=mask, monlib=monlib, B=args.B,
460
+ half1_only=args.half1_only, no_fsc_weights=args.no_fsc_weights,
461
+ sharpening_b=args.sharpening_b, omit_proton=args.omit_proton,
462
+ omit_h_electron=args.omit_h_electron,
463
+ source=args.source)
464
+ write_files(hkldata, map_labs, grid_start, stats_str,
465
+ mask=mask, output_prefix=args.output_prefix,
466
+ trim_map=args.trim, trim_mtz=args.trim_mtz, omit_h_electron=args.omit_h_electron)
467
+
468
+ py_out = "{}_coot.py".format(args.output_prefix)
469
+ write_coot_script(py_out, model_file=args.model,
470
+ mtz_file=args.output_prefix+"_maps.mtz",
471
+ contour_fo=None if mask is None else 1.2,
472
+ contour_fofc=None if mask is None else 3.0,
473
+ ncs_ops=ncs_org)
474
+ logger.writeln("\nOpen model and diffmap mtz with COOT:")
475
+ logger.writeln("coot --script " + py_out)
476
+ if mask is not None:
477
+ logger.writeln("\nWant to list Fo-Fc map peaks? Try:")
478
+ if args.omit_h_electron:
479
+ logger.writeln("servalcat util map_peaks --map {}_normalized_fofc_flipsign.mrc --model {} --abs_level 4.0".format(args.output_prefix, args.model))
480
+ else:
481
+ logger.writeln("servalcat util map_peaks --map {}_normalized_fofc.mrc --model {} --abs_level 4.0".format(args.output_prefix, args.model))
482
+
483
+ # main()
484
+
485
+ if __name__ == "__main__":
486
+ import sys
487
+ args = parse_args(sys.argv[1:])
488
+ main(args)