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.
- servalcat/__init__.py +10 -0
- servalcat/__main__.py +120 -0
- servalcat/ext.cpython-314t-x86_64-linux-gnu.so +0 -0
- servalcat/refine/__init__.py +0 -0
- servalcat/refine/cgsolve.py +100 -0
- servalcat/refine/refine.py +1162 -0
- servalcat/refine/refine_geom.py +245 -0
- servalcat/refine/refine_spa.py +400 -0
- servalcat/refine/refine_xtal.py +339 -0
- servalcat/refine/spa.py +151 -0
- servalcat/refine/xtal.py +312 -0
- servalcat/refmac/__init__.py +0 -0
- servalcat/refmac/exte.py +191 -0
- servalcat/refmac/refmac_keywords.py +660 -0
- servalcat/refmac/refmac_wrapper.py +423 -0
- servalcat/spa/__init__.py +0 -0
- servalcat/spa/fofc.py +488 -0
- servalcat/spa/fsc.py +391 -0
- servalcat/spa/localcc.py +197 -0
- servalcat/spa/realspcc_from_var.py +128 -0
- servalcat/spa/run_refmac.py +979 -0
- servalcat/spa/shift_maps.py +293 -0
- servalcat/spa/shiftback.py +137 -0
- servalcat/spa/translate.py +129 -0
- servalcat/utils/__init__.py +35 -0
- servalcat/utils/commands.py +1629 -0
- servalcat/utils/fileio.py +836 -0
- servalcat/utils/generate_operators.py +296 -0
- servalcat/utils/hkl.py +811 -0
- servalcat/utils/logger.py +140 -0
- servalcat/utils/maps.py +345 -0
- servalcat/utils/model.py +933 -0
- servalcat/utils/refmac.py +759 -0
- servalcat/utils/restraints.py +888 -0
- servalcat/utils/symmetry.py +298 -0
- servalcat/xtal/__init__.py +0 -0
- servalcat/xtal/french_wilson.py +262 -0
- servalcat/xtal/run_refmac_small.py +240 -0
- servalcat/xtal/sigmaa.py +1954 -0
- servalcat/xtal/twin.py +316 -0
- servalcat-0.4.131.dist-info/METADATA +60 -0
- servalcat-0.4.131.dist-info/RECORD +45 -0
- servalcat-0.4.131.dist-info/WHEEL +6 -0
- servalcat-0.4.131.dist-info/entry_points.txt +4 -0
- servalcat-0.4.131.dist-info/licenses/LICENSE +373 -0
|
@@ -0,0 +1,339 @@
|
|
|
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 pandas
|
|
12
|
+
import os
|
|
13
|
+
import shutil
|
|
14
|
+
import argparse
|
|
15
|
+
from servalcat.utils import logger
|
|
16
|
+
from servalcat import utils
|
|
17
|
+
from servalcat.xtal.sigmaa import decide_mtz_labels, process_input, calculate_maps, calculate_maps_int, calculate_maps_twin
|
|
18
|
+
from servalcat.refine.xtal import LL_Xtal
|
|
19
|
+
from servalcat.refine.refine import Geom, Refine, RefineParams, update_meta, print_h_options, load_config
|
|
20
|
+
from servalcat.refmac import refmac_keywords
|
|
21
|
+
from servalcat import ext
|
|
22
|
+
b_to_u = utils.model.b_to_u
|
|
23
|
+
|
|
24
|
+
def add_arguments(parser):
|
|
25
|
+
parser.description = "program to refine crystallographic structures"
|
|
26
|
+
parser.add_argument("--hklin", required=True)
|
|
27
|
+
parser.add_argument('--hklin_free',
|
|
28
|
+
help='Input MTZ file for test flags')
|
|
29
|
+
parser.add_argument("-d", '--d_min', type=float)
|
|
30
|
+
parser.add_argument('--d_max', type=float)
|
|
31
|
+
parser.add_argument('--nbins', type=int,
|
|
32
|
+
help="Number of bins for statistics (default: auto)")
|
|
33
|
+
parser.add_argument('--nbins_ml', type=int,
|
|
34
|
+
help="Number of bins for ML parameters (default: auto)")
|
|
35
|
+
parser.add_argument("--labin", help="F,SIGF,FREE input")
|
|
36
|
+
parser.add_argument('--labin_free',
|
|
37
|
+
help='MTZ column of --hklin_free')
|
|
38
|
+
parser.add_argument('--labin_llweight',
|
|
39
|
+
help=argparse.SUPPRESS) # testing
|
|
40
|
+
parser.add_argument('--free', type=int,
|
|
41
|
+
help='flag number for test set')
|
|
42
|
+
parser.add_argument('--model', required=True,
|
|
43
|
+
help='Input atomic model file')
|
|
44
|
+
parser.add_argument("--monlib",
|
|
45
|
+
help="Monomer library path. Default: $CLIBD_MON")
|
|
46
|
+
parser.add_argument('--ligand', nargs="*", action="append",
|
|
47
|
+
help="restraint dictionary cif file(s)")
|
|
48
|
+
parser.add_argument('--newligand_continue', action='store_true',
|
|
49
|
+
help="Make ad-hoc restraints for unknown ligands (not recommended)")
|
|
50
|
+
parser.add_argument('--hydrogen', default="all", choices=["all", "yes", "no"],
|
|
51
|
+
help="all: add riding hydrogen atoms, yes: use hydrogen atoms if present, no: remove hydrogen atoms in input. "
|
|
52
|
+
"Default: %(default)s")
|
|
53
|
+
parser.add_argument('--hout', action='store_true', help="write hydrogen atoms in the output model")
|
|
54
|
+
parser.add_argument('--jellybody', action='store_true',
|
|
55
|
+
help="Use jelly body restraints")
|
|
56
|
+
parser.add_argument('--jellybody_params', nargs=2, type=float,
|
|
57
|
+
metavar=("sigma", "dmax"), default=[0.01, 4.2],
|
|
58
|
+
help="Jelly body sigma and dmax (default: %(default)s)")
|
|
59
|
+
parser.add_argument('--jellyonly', action='store_true',
|
|
60
|
+
help="Jelly body only (experimental, may not be useful)")
|
|
61
|
+
parser.add_argument('--find_links', action='store_true',
|
|
62
|
+
help='Automatically add links')
|
|
63
|
+
parser.add_argument('--keywords', nargs='+', action="append",
|
|
64
|
+
help="refmac keyword(s)")
|
|
65
|
+
parser.add_argument('--keyword_file', nargs='+', action="append",
|
|
66
|
+
help="refmac keyword file(s)")
|
|
67
|
+
parser.add_argument('--randomize', type=float, default=0,
|
|
68
|
+
help='Shake coordinates with specified rmsd')
|
|
69
|
+
parser.add_argument('--ncycle', type=int, default=10,
|
|
70
|
+
help="number of CG cycles (default: %(default)d)")
|
|
71
|
+
parser.add_argument('--weight', type=float,
|
|
72
|
+
help="refinement weight (default: auto)")
|
|
73
|
+
parser.add_argument('--no_weight_adjust', action='store_true',
|
|
74
|
+
help='Do not adjust weight during refinement')
|
|
75
|
+
parser.add_argument('--target_bond_rmsz_range', nargs=2, type=float, default=[0.5, 1.],
|
|
76
|
+
help='Bond rmsz range for weight adjustment (default: %(default)s)')
|
|
77
|
+
parser.add_argument('--ncsr', action='store_true',
|
|
78
|
+
help='Use local NCS restraints')
|
|
79
|
+
parser.add_argument('--adpr_weight', type=float, default=1.,
|
|
80
|
+
help="ADP restraint weight (default: %(default)f)")
|
|
81
|
+
parser.add_argument('--occr_weight', type=float, default=0.,
|
|
82
|
+
help="Occupancy restraint weight (default: %(default)f)")
|
|
83
|
+
parser.add_argument('--bfactor', type=float,
|
|
84
|
+
help="reset all atomic B values to specified value")
|
|
85
|
+
parser.add_argument('--fix_xyz', action="store_true")
|
|
86
|
+
parser.add_argument('--adp', choices=["fix", "iso", "aniso"], default="iso")
|
|
87
|
+
parser.add_argument('--refine_all_occ', action="store_true")
|
|
88
|
+
parser.add_argument('--max_dist_for_adp_restraint', type=float, default=4.)
|
|
89
|
+
parser.add_argument('--adp_restraint_power', type=float)
|
|
90
|
+
parser.add_argument('--adp_restraint_exp_fac', type=float)
|
|
91
|
+
parser.add_argument('--adp_restraint_no_long_range', action='store_true')
|
|
92
|
+
parser.add_argument('--adp_restraint_mode', choices=["diff", "kldiv"], default="kldiv")
|
|
93
|
+
parser.add_argument('--unrestrained', action='store_true', help="No positional restraints")
|
|
94
|
+
parser.add_argument('--refine_h', action="store_true", help="Refine hydrogen (default: restraints only)")
|
|
95
|
+
parser.add_argument('--refine_dfrac', action="store_true", help="Refine deuterium fraction (neutron only)")
|
|
96
|
+
parser.add_argument('--twin', action="store_true", help="Turn on twin refinement")
|
|
97
|
+
parser.add_argument('--twin_mlalpha', action="store_true", help="Use ML optimisation for twin fractions")
|
|
98
|
+
parser.add_argument("-s", "--source", choices=["electron", "xray", "neutron", "custom"], required=True)
|
|
99
|
+
parser.add_argument("--wavelength", type=float, help="For f_prime")
|
|
100
|
+
parser.add_argument('--no_solvent', action='store_true',
|
|
101
|
+
help="Do not consider bulk solvent contribution")
|
|
102
|
+
parser.add_argument("--non_binary_solvent_mask", action='store_true',
|
|
103
|
+
help=argparse.SUPPRESS) # experimental
|
|
104
|
+
parser.add_argument('--use_in_est', choices=["all", "work", "test"],
|
|
105
|
+
help="Which set of reflections to use for the ML parameter estimation. Default: 'work' if --twin is set; otherwise 'test'.")
|
|
106
|
+
parser.add_argument('--keep_charges', action='store_true',
|
|
107
|
+
help="Use scattering factor for charged atoms. Use it with care.")
|
|
108
|
+
parser.add_argument("--keep_entities", action='store_true',
|
|
109
|
+
help="Do not override entities")
|
|
110
|
+
parser.add_argument('--allow_unusual_occupancies', action="store_true", help="Allow negative or more than one occupancies")
|
|
111
|
+
parser.add_argument('-o','--output_prefix')
|
|
112
|
+
parser.add_argument("--write_trajectory", action='store_true',
|
|
113
|
+
help="Write all output from cycles")
|
|
114
|
+
parser.add_argument("--vonmises", action='store_true',
|
|
115
|
+
help="Experimental: von Mises type restraint for angles")
|
|
116
|
+
parser.add_argument("--prefer_intensity", action='store_true')
|
|
117
|
+
parser.add_argument("--use_fw", action='store_true',
|
|
118
|
+
help="For debugging purpose; use F&W-converted amplitudes but use intensity for stats")
|
|
119
|
+
parser.add_argument("--config",
|
|
120
|
+
help="Config file (.yaml)")
|
|
121
|
+
# add_arguments()
|
|
122
|
+
|
|
123
|
+
def parse_args(arg_list):
|
|
124
|
+
parser = argparse.ArgumentParser()
|
|
125
|
+
add_arguments(parser)
|
|
126
|
+
return parser.parse_args(arg_list)
|
|
127
|
+
# parse_args()
|
|
128
|
+
|
|
129
|
+
def main(args):
|
|
130
|
+
if args.refine_dfrac and args.source != "neutron": # TODO should check params.is_refined()
|
|
131
|
+
raise SystemExit("--refine_dfrac can only be used for the neutron source")
|
|
132
|
+
if args.labin_llweight and args.twin:
|
|
133
|
+
raise SystemExit("--labin_llweight not supported for twin refinement")
|
|
134
|
+
if args.wavelength is not None and args.source != "xray":
|
|
135
|
+
raise SystemExit("Error: Wavelength is only available for X-ray source")
|
|
136
|
+
if args.ligand: args.ligand = sum(args.ligand, [])
|
|
137
|
+
if not args.output_prefix:
|
|
138
|
+
args.output_prefix = utils.fileio.splitext(os.path.basename(args.model))[0] + "_refined"
|
|
139
|
+
# This could be confusing. Twinning may not be detected.
|
|
140
|
+
if not args.use_in_est:
|
|
141
|
+
args.use_in_est = "work" if args.twin else "test"
|
|
142
|
+
keywords = []
|
|
143
|
+
if args.keywords or args.keyword_file:
|
|
144
|
+
if args.keywords: keywords = sum(args.keywords, [])
|
|
145
|
+
if args.keyword_file: keywords.extend(l for f in sum(args.keyword_file, []) for l in open(f))
|
|
146
|
+
params = refmac_keywords.parse_keywords(keywords)
|
|
147
|
+
refine_cfg = load_config(args.config, args, params)
|
|
148
|
+
hklin = args.hklin
|
|
149
|
+
labin = args.labin
|
|
150
|
+
if labin is not None:
|
|
151
|
+
labin = labin.split(",")
|
|
152
|
+
elif utils.fileio.is_mmhkl_file(hklin):
|
|
153
|
+
hklin = utils.fileio.read_mmhkl(hklin)
|
|
154
|
+
labin = decide_mtz_labels(hklin, prefer_intensity=args.prefer_intensity)
|
|
155
|
+
software_items = utils.fileio.software_items_from_mtz(hklin)
|
|
156
|
+
try:
|
|
157
|
+
hkldata, sts, fc_labs, args.free, use_in_est = process_input(
|
|
158
|
+
hklin=hklin,
|
|
159
|
+
labin=labin,
|
|
160
|
+
n_bins_ml=args.nbins_ml,
|
|
161
|
+
n_bins_stat=args.nbins,
|
|
162
|
+
free=args.free,
|
|
163
|
+
xyzins=[args.model],
|
|
164
|
+
d_max=args.d_max,
|
|
165
|
+
d_min=args.d_min,
|
|
166
|
+
use=args.use_in_est,
|
|
167
|
+
max_mlbins=30,
|
|
168
|
+
keep_charges=args.keep_charges,
|
|
169
|
+
allow_unusual_occupancies=args.allow_unusual_occupancies,
|
|
170
|
+
hklin_free=args.hklin_free,
|
|
171
|
+
labin_free=args.labin_free,
|
|
172
|
+
labin_llweight=args.labin_llweight)
|
|
173
|
+
except RuntimeError as e:
|
|
174
|
+
raise SystemExit("Error: {}".format(e))
|
|
175
|
+
|
|
176
|
+
if "FREE" in hkldata.df:
|
|
177
|
+
use_in_target = "work"
|
|
178
|
+
else:
|
|
179
|
+
use_in_target = "all"
|
|
180
|
+
|
|
181
|
+
is_int = "I" in hkldata.df
|
|
182
|
+
ccu = utils.model.CustomCoefUtil()
|
|
183
|
+
if args.source == "custom":
|
|
184
|
+
ccu.read_from_cif(sts[0], args.model)
|
|
185
|
+
ccu.show_info()
|
|
186
|
+
addends, addends2 = None, None
|
|
187
|
+
else:
|
|
188
|
+
addends, addends2 = utils.model.check_atomsf(sts, args.source, mott_bethe=(args.source=="electron"), wavelength=args.wavelength)
|
|
189
|
+
if args.use_fw:
|
|
190
|
+
if not is_int:
|
|
191
|
+
raise SystemExit("Error: need intensity input when -use_fw")
|
|
192
|
+
logger.writeln("Converting intensities to amplitudes with the French & Wilson algorithm")
|
|
193
|
+
from servalcat.xtal import french_wilson
|
|
194
|
+
B_aniso = french_wilson.determine_Sigma_and_aniso(hkldata)
|
|
195
|
+
french_wilson.french_wilson(hkldata, B_aniso, labout=["FP", "SIGFP"]) # TODO when anomalous
|
|
196
|
+
del hkldata.binned_df["ml"]["S"]
|
|
197
|
+
is_int = False
|
|
198
|
+
|
|
199
|
+
st = sts[0]
|
|
200
|
+
utils.model.fix_deuterium_residues(st)
|
|
201
|
+
if args.unrestrained:
|
|
202
|
+
monlib = gemmi.MonLib()
|
|
203
|
+
topo = None
|
|
204
|
+
if args.hydrogen == "yes":
|
|
205
|
+
h_change = gemmi.HydrogenChange.NoChange
|
|
206
|
+
else:
|
|
207
|
+
h_change = gemmi.HydrogenChange.Remove
|
|
208
|
+
st.remove_hydrogens()
|
|
209
|
+
if args.hydrogen == "all":
|
|
210
|
+
logger.writeln("\nWARNING: in unrestrained refinement hydrogen atoms are not generated.\n")
|
|
211
|
+
for i, cra in enumerate(st[0].all()):
|
|
212
|
+
cra.atom.serial = i + 1
|
|
213
|
+
else:
|
|
214
|
+
try:
|
|
215
|
+
monlib = utils.restraints.load_monomer_library(st, monomer_dir=args.monlib, cif_files=args.ligand,
|
|
216
|
+
stop_for_unknowns=not args.newligand_continue,
|
|
217
|
+
params=params)
|
|
218
|
+
except RuntimeError as e:
|
|
219
|
+
raise SystemExit("Error: {}".format(e))
|
|
220
|
+
if not args.keep_entities:
|
|
221
|
+
utils.model.setup_entities(st, clear=True, force_subchain_names=True, overwrite_entity_type=True,
|
|
222
|
+
fix_sequences=True)
|
|
223
|
+
utils.restraints.find_and_fix_links(st, monlib, find_metal_links=args.find_links,
|
|
224
|
+
add_found=args.find_links)
|
|
225
|
+
h_change = {"all":gemmi.HydrogenChange.ReAddKnown,
|
|
226
|
+
"yes":gemmi.HydrogenChange.NoChange,
|
|
227
|
+
"no":gemmi.HydrogenChange.Remove}[args.hydrogen]
|
|
228
|
+
try:
|
|
229
|
+
topo, _ = utils.restraints.prepare_topology(st, monlib, h_change=h_change,
|
|
230
|
+
check_hydrogen=(args.hydrogen=="yes"),
|
|
231
|
+
params=params)
|
|
232
|
+
except RuntimeError as e:
|
|
233
|
+
raise SystemExit("Error: {}".format(e))
|
|
234
|
+
|
|
235
|
+
if h_change == gemmi.HydrogenChange.ReAddKnown and args.source == "neutron":
|
|
236
|
+
topo.adjust_hydrogen_distances(gemmi.Restraints.DistanceOf.Nucleus,
|
|
237
|
+
default_scale=utils.restraints.default_proton_scale)
|
|
238
|
+
|
|
239
|
+
print_h_options(h_change, st[0].has_hydrogen(), args.refine_h, args.hout, geom_only=False)
|
|
240
|
+
|
|
241
|
+
# initialize values
|
|
242
|
+
utils.model.reset_adp(st[0], args.bfactor, args.adp)
|
|
243
|
+
utils.model.initialize_values(st[0], refine_cfg.initialisation)
|
|
244
|
+
|
|
245
|
+
# auto weight
|
|
246
|
+
if args.weight is None:
|
|
247
|
+
logger.writeln("Estimating weight using resolution")
|
|
248
|
+
reso = hkldata.d_min_max()[0]
|
|
249
|
+
args.weight = numpy.exp(reso * 0.9104 + 0.2162)
|
|
250
|
+
logger.writeln(" Will use weight= {:.2f}".format(args.weight))
|
|
251
|
+
|
|
252
|
+
if args.ncsr:
|
|
253
|
+
ncslist = utils.restraints.prepare_ncs_restraints(st)
|
|
254
|
+
else:
|
|
255
|
+
ncslist = False
|
|
256
|
+
if args.refine_dfrac:
|
|
257
|
+
# needed to make Fc calculation expand H/D (ugh)
|
|
258
|
+
st.has_d_fraction = True
|
|
259
|
+
refine_params = RefineParams(st, refine_xyz=not args.fix_xyz,
|
|
260
|
+
adp_mode=dict(fix=0, iso=1, aniso=2)[args.adp],
|
|
261
|
+
refine_occ=args.refine_all_occ,
|
|
262
|
+
refine_dfrac=args.refine_dfrac, cfg=refine_cfg,
|
|
263
|
+
exclude_h_ll=not args.refine_h)
|
|
264
|
+
geom = Geom(st, topo, monlib, refine_params,
|
|
265
|
+
shake_rms=args.randomize, adpr_w=args.adpr_weight, occr_w=args.occr_weight, params=params,
|
|
266
|
+
unrestrained=args.unrestrained or args.jellyonly, use_nucleus=(args.source=="neutron"),
|
|
267
|
+
ncslist=ncslist)
|
|
268
|
+
geom.geom.angle_von_mises = args.vonmises
|
|
269
|
+
geom.geom.adpr_max_dist = args.max_dist_for_adp_restraint
|
|
270
|
+
if args.adp_restraint_power is not None: geom.geom.adpr_d_power = args.adp_restraint_power
|
|
271
|
+
if args.adp_restraint_exp_fac is not None: geom.geom.adpr_exp_fac = args.adp_restraint_exp_fac
|
|
272
|
+
if args.adp_restraint_no_long_range: geom.geom.adpr_long_range = False
|
|
273
|
+
geom.geom.adpr_mode = args.adp_restraint_mode
|
|
274
|
+
if args.jellybody or args.jellyonly:
|
|
275
|
+
geom.geom.ridge_sigma, geom.geom.ridge_dmax = args.jellybody_params
|
|
276
|
+
if args.jellyonly: geom.geom.ridge_exclude_short_dist = False
|
|
277
|
+
if args.source == "custom":
|
|
278
|
+
ccu.set_coeffs(st)
|
|
279
|
+
ll = LL_Xtal(hkldata, args.free, st, monlib, source=args.source,
|
|
280
|
+
use_solvent=0 if args.no_solvent else 2 if args.non_binary_solvent_mask else 1,
|
|
281
|
+
use_in_est=use_in_est, use_in_target=use_in_target,
|
|
282
|
+
twin=args.twin, twin_mlalpha=args.twin_mlalpha,
|
|
283
|
+
addends=addends, addends2=addends2, is_int=is_int)
|
|
284
|
+
refiner = Refine(st, geom, refine_cfg, refine_params, ll=ll,
|
|
285
|
+
unrestrained=args.unrestrained)
|
|
286
|
+
|
|
287
|
+
stats = refiner.run_cycles(args.ncycle, weight=args.weight,
|
|
288
|
+
weight_adjust=not args.no_weight_adjust,
|
|
289
|
+
weight_adjust_bond_rmsz_range=args.target_bond_rmsz_range,
|
|
290
|
+
stats_json_out=args.output_prefix + "_stats.json")
|
|
291
|
+
update_meta(st, stats[-1], ll)
|
|
292
|
+
st.meta.software = software_items + st.meta.software
|
|
293
|
+
refiner.st.name = args.output_prefix
|
|
294
|
+
utils.fileio.write_model(refiner.st, args.output_prefix, pdb=True, cif=True, hout=args.hout)
|
|
295
|
+
if st.has_d_fraction: # XXX make hout when neutron
|
|
296
|
+
st_hd_expand = refiner.st.clone()
|
|
297
|
+
st_hd_expand.store_deuterium_as_fraction(False)
|
|
298
|
+
utils.fileio.write_model(st_hd_expand, args.output_prefix + "_hd_expand", pdb=True, cif=True, hout=True)
|
|
299
|
+
if refine_cfg.write_trajectory:
|
|
300
|
+
utils.fileio.write_model(refiner.st_traj, args.output_prefix + "_traj", cif=True)
|
|
301
|
+
|
|
302
|
+
if ll.twin_data:
|
|
303
|
+
# replace hkldata
|
|
304
|
+
hkldata = calculate_maps_twin(ll.hkldata, ll.b_aniso, ll.fc_labs, ll.D_labs, ll.twin_data,
|
|
305
|
+
use=use_in_target)
|
|
306
|
+
elif is_int:
|
|
307
|
+
calculate_maps_int(ll.hkldata, ll.b_aniso, ll.fc_labs, ll.D_labs, use=use_in_target)
|
|
308
|
+
else:
|
|
309
|
+
calculate_maps(ll.hkldata, ll.b_aniso, ll.fc_labs, ll.D_labs, args.output_prefix + "_stats.log",
|
|
310
|
+
use=use_in_target)
|
|
311
|
+
|
|
312
|
+
# Write mtz file
|
|
313
|
+
if ll.twin_data:
|
|
314
|
+
labs = ["F_est", "F_exp"]
|
|
315
|
+
elif is_int:
|
|
316
|
+
labs = ["I", "SIGI", "F_est"]
|
|
317
|
+
else:
|
|
318
|
+
labs = ["FP", "SIGFP"]
|
|
319
|
+
labs.extend(["FOM", "FWT", "DELFWT", "FC"])
|
|
320
|
+
if "FAN" in hkldata.df:
|
|
321
|
+
labs.append("FAN")
|
|
322
|
+
if "DELFAN" in hkldata.df:
|
|
323
|
+
labs.append("DELFAN")
|
|
324
|
+
if not args.no_solvent:
|
|
325
|
+
labs.append("FCbulk")
|
|
326
|
+
if "FREE" in hkldata.df:
|
|
327
|
+
labs.append("FREE")
|
|
328
|
+
if args.labin_llweight:
|
|
329
|
+
labs.append("llweight")
|
|
330
|
+
labs += ll.D_labs + ["S"] # for debugging, for now
|
|
331
|
+
mtz_out = args.output_prefix+".mtz"
|
|
332
|
+
hkldata.write_mtz(mtz_out, labs=labs, types={"FOM": "W", "FP":"F", "SIGFP":"Q", "I":"J", "SIGI":"Q", "F_est": "F", "F_exp": "F", "llweight": "R"})
|
|
333
|
+
|
|
334
|
+
# main()
|
|
335
|
+
|
|
336
|
+
if __name__ == "__main__":
|
|
337
|
+
import sys
|
|
338
|
+
args = parse_args(sys.argv[1:])
|
|
339
|
+
main(args)
|
servalcat/refine/spa.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
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 import utils
|
|
15
|
+
from servalcat.spa import fofc
|
|
16
|
+
from servalcat.spa import fsc
|
|
17
|
+
from servalcat import ext
|
|
18
|
+
b_to_u = utils.model.b_to_u
|
|
19
|
+
u_to_b = utils.model.u_to_b
|
|
20
|
+
|
|
21
|
+
def calc_D_and_S(hkldata, lab_obs): # simplified version of fofc.calc_D_and_S()
|
|
22
|
+
bdf = hkldata.binned_df["ml"]
|
|
23
|
+
bdf["D"] = 0.
|
|
24
|
+
bdf["S"] = 0.
|
|
25
|
+
for i_bin, idxes in hkldata.binned("ml"):
|
|
26
|
+
Fo = hkldata.df[lab_obs].to_numpy()[idxes]
|
|
27
|
+
Fc = hkldata.df.FC.to_numpy()[idxes]
|
|
28
|
+
bdf.loc[i_bin, "D"] = numpy.nansum(numpy.real(Fo * numpy.conj(Fc))) / numpy.sum(numpy.abs(Fc)**2)
|
|
29
|
+
bdf.loc[i_bin, "S"] = numpy.nanmean(numpy.abs(Fo - bdf.D[i_bin] * Fc)**2)
|
|
30
|
+
# calc_D_and_S()
|
|
31
|
+
|
|
32
|
+
class LL_SPA:
|
|
33
|
+
def __init__(self, hkldata, st, monlib, lab_obs, source="electron", mott_bethe=True):
|
|
34
|
+
assert source in ("electron", "xray", "custom")
|
|
35
|
+
self.source = source
|
|
36
|
+
self.mott_bethe = False if source != "electron" else mott_bethe
|
|
37
|
+
self.hkldata = hkldata
|
|
38
|
+
self.lab_obs = lab_obs
|
|
39
|
+
self.st = st
|
|
40
|
+
self.monlib = monlib
|
|
41
|
+
self.d_min_max = hkldata.d_min_max()
|
|
42
|
+
self.ll = None
|
|
43
|
+
self.b_aniso = None
|
|
44
|
+
|
|
45
|
+
def refine_id(self):
|
|
46
|
+
if self.source in ("electron", "custom"):
|
|
47
|
+
# XXX when custom, it's actually unknown..
|
|
48
|
+
return "ELECTRON MICROSCOPY"
|
|
49
|
+
return "NON-EM SPA" # does not happen, I guess
|
|
50
|
+
|
|
51
|
+
def update_ml_params(self):
|
|
52
|
+
# FIXME make sure D > 0
|
|
53
|
+
calc_D_and_S(self.hkldata, self.lab_obs)
|
|
54
|
+
|
|
55
|
+
def update_fc(self):
|
|
56
|
+
if self.st.ncs:
|
|
57
|
+
st = self.st.clone()
|
|
58
|
+
st.expand_ncs(gemmi.HowToNameCopiedChain.Short, merge_dist=0)
|
|
59
|
+
else:
|
|
60
|
+
st = self.st
|
|
61
|
+
|
|
62
|
+
self.hkldata.df["FC"] = utils.model.calc_fc_fft(st, self.d_min_max[0] - 1e-6,
|
|
63
|
+
monlib=self.monlib,
|
|
64
|
+
source=self.source,
|
|
65
|
+
mott_bethe=self.mott_bethe,
|
|
66
|
+
miller_array=self.hkldata.miller_array())
|
|
67
|
+
|
|
68
|
+
def prepare_target(self):
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
def overall_scale(self, min_b=0.5):
|
|
72
|
+
k, b = self.hkldata.scale_k_and_b(lab_ref=self.lab_obs, lab_scaled="FC")
|
|
73
|
+
min_b_iso = self.st[0].calculate_b_aniso_range()[0] # actually min of aniso too
|
|
74
|
+
tmp = min_b_iso + b
|
|
75
|
+
if tmp < min_b: # perhaps better only adjust b_iso that went too small, but we need to recalculate Fc
|
|
76
|
+
logger.writeln("Adjusting overall B to avoid too small value")
|
|
77
|
+
b += min_b - tmp
|
|
78
|
+
logger.writeln("Applying overall B to model: {:.2f}".format(b))
|
|
79
|
+
utils.model.shift_b(self.st[0], b)
|
|
80
|
+
# adjust Fc
|
|
81
|
+
k_iso = self.hkldata.debye_waller_factors(b_iso=b)
|
|
82
|
+
self.hkldata.df["FC"] *= k_iso
|
|
83
|
+
# adjust Fo
|
|
84
|
+
self.hkldata.df[self.lab_obs] /= k
|
|
85
|
+
# overall_scale()
|
|
86
|
+
|
|
87
|
+
def calc_target(self): # -LL target for SPA
|
|
88
|
+
ret = 0
|
|
89
|
+
for i_bin, idxes in self.hkldata.binned("ml"):
|
|
90
|
+
Fo = self.hkldata.df[self.lab_obs].to_numpy()[idxes]
|
|
91
|
+
DFc = self.hkldata.df.FC.to_numpy()[idxes] * self.hkldata.binned_df["ml"].D[i_bin]
|
|
92
|
+
S = self.hkldata.binned_df["ml"].S[i_bin]
|
|
93
|
+
ret += numpy.nansum(numpy.abs(Fo - DFc)**2) / S + numpy.log(S) * len(idxes)
|
|
94
|
+
return ret * 2 # friedel mates
|
|
95
|
+
# calc_target()
|
|
96
|
+
|
|
97
|
+
def calc_stats(self, bin_stats=False):
|
|
98
|
+
# ignore bin_stats for now. better stats are calculated after refinement
|
|
99
|
+
stats = fsc.calc_fsc_all(self.hkldata, labs_fc=["FC"], lab_f=self.lab_obs)
|
|
100
|
+
fsca = fsc.fsc_average(stats.ncoeffs, stats.fsc_FC_full)
|
|
101
|
+
logger.writeln("FSCaverage = {:.4f}".format(fsca))
|
|
102
|
+
ret = {"summary": {"FSCaverage": fsca, "-LL": self.calc_target()}}
|
|
103
|
+
# XXX in fsc object, _full is misleading - it's not full in cross validation mode
|
|
104
|
+
ret["bin_stats"] = stats
|
|
105
|
+
ret["ml"] = self.hkldata.binned_df["ml"].copy()
|
|
106
|
+
return ret
|
|
107
|
+
|
|
108
|
+
def calc_grad(self, refine_params, specs):
|
|
109
|
+
dll_dab = numpy.empty_like(self.hkldata.df[self.lab_obs])
|
|
110
|
+
d2ll_dab2 = numpy.zeros(len(self.hkldata.df.index))
|
|
111
|
+
blur = utils.model.determine_blur_for_dencalc(self.st, self.d_min_max[0] / 3) # TODO need more work
|
|
112
|
+
logger.writeln("blur for deriv= {:.2f}".format(blur))
|
|
113
|
+
for i_bin, idxes in self.hkldata.binned("ml"):
|
|
114
|
+
D = self.hkldata.binned_df["ml"].D[i_bin]
|
|
115
|
+
S = self.hkldata.binned_df["ml"].S[i_bin]
|
|
116
|
+
Fc = self.hkldata.df.FC.to_numpy()[idxes]
|
|
117
|
+
Fo = self.hkldata.df[self.lab_obs].to_numpy()[idxes]
|
|
118
|
+
dll_dab[idxes] = -2 * D / S * (Fo - D * Fc)#.conj()
|
|
119
|
+
d2ll_dab2[idxes] = 2 * D**2 / S
|
|
120
|
+
|
|
121
|
+
if self.mott_bethe:
|
|
122
|
+
dll_dab *= self.hkldata.d_spacings()**2 * gemmi.mott_bethe_const()
|
|
123
|
+
d2ll_dab2 *= gemmi.mott_bethe_const()**2
|
|
124
|
+
|
|
125
|
+
# we need V for Hessian and V**2/n for gradient.
|
|
126
|
+
d2ll_dab2 *= self.hkldata.cell.volume
|
|
127
|
+
dll_dab_den = self.hkldata.fft_map(data=dll_dab * self.hkldata.debye_waller_factors(b_iso=-blur))
|
|
128
|
+
dll_dab_den.array[:] *= self.hkldata.cell.volume**2 / dll_dab_den.point_count
|
|
129
|
+
self.ll = ext.LL(self.st, refine_params, self.mott_bethe)
|
|
130
|
+
self.ll.set_ncs([x.tr for x in self.st.ncs if not x.given])
|
|
131
|
+
if self.source == "custom":
|
|
132
|
+
self.ll.calc_grad_custom(dll_dab_den, blur)
|
|
133
|
+
else:
|
|
134
|
+
self.ll.calc_grad_it92(dll_dab_den, blur)
|
|
135
|
+
|
|
136
|
+
# second derivative
|
|
137
|
+
d2dfw_table = ext.TableS3(*self.hkldata.d_min_max())
|
|
138
|
+
d2dfw_table.make_table(1./self.hkldata.d_spacings(), d2ll_dab2)
|
|
139
|
+
if self.source == "custom":
|
|
140
|
+
self.ll.make_fisher_table_diag_fast_custom(d2dfw_table, 1.)
|
|
141
|
+
self.ll.fisher_diag_from_table_custom()
|
|
142
|
+
else:
|
|
143
|
+
self.ll.make_fisher_table_diag_fast_it92(d2dfw_table)
|
|
144
|
+
self.ll.fisher_diag_from_table_it92()
|
|
145
|
+
#json.dump(dict(b=self.ll.table_bs, pp1=self.ll.pp1, bb=self.ll.bb),
|
|
146
|
+
# open("ll_fisher.json", "w"), indent=True)
|
|
147
|
+
#a, (b,c) = ll.fisher_for_coo()
|
|
148
|
+
#json.dump(([float(x) for x in a], ([int(x) for x in b], [int(x) for x in c])), open("fisher.json", "w"))
|
|
149
|
+
#logger.writeln("disabling spec_correction in spa target")
|
|
150
|
+
if specs is not None:
|
|
151
|
+
self.ll.spec_correction(specs, use_rr=False)
|