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,140 @@
|
|
|
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 sys
|
|
10
|
+
import datetime
|
|
11
|
+
import platform
|
|
12
|
+
import getpass
|
|
13
|
+
import traceback
|
|
14
|
+
import shlex
|
|
15
|
+
import servalcat
|
|
16
|
+
|
|
17
|
+
class Logger(object):
|
|
18
|
+
def __init__(self, file_out=None, append=True):
|
|
19
|
+
self.ofs = None
|
|
20
|
+
self.stopped = False
|
|
21
|
+
self.prefix = ""
|
|
22
|
+
if file_out:
|
|
23
|
+
self.set_file(file_out, append)
|
|
24
|
+
# __init__()
|
|
25
|
+
def stop_logging(self): self.stopped = True
|
|
26
|
+
def start_logging(self): self.stopped = False
|
|
27
|
+
def set_prefix(self, p): self.prefix = p
|
|
28
|
+
def clear_prefix(self): self.prefix = ""
|
|
29
|
+
|
|
30
|
+
def set_file(self, file_out, append=True):
|
|
31
|
+
try:
|
|
32
|
+
self.ofs = open(file_out, "a" if append else "w")
|
|
33
|
+
except:
|
|
34
|
+
print("Error: Cannot open log file to write")
|
|
35
|
+
# set_file()
|
|
36
|
+
|
|
37
|
+
def write(self, l, end="", flush=True, fs=None, print_fs=sys.stdout):
|
|
38
|
+
if self.stopped: return
|
|
39
|
+
if self.prefix:
|
|
40
|
+
l = "".join(self.prefix + x for x in l.splitlines(keepends=True))
|
|
41
|
+
print(l, end=end, file=print_fs, flush=flush)
|
|
42
|
+
for f in (self.ofs, fs):
|
|
43
|
+
if f is not None:
|
|
44
|
+
f.write(l)
|
|
45
|
+
f.write(end)
|
|
46
|
+
if flush: f.flush()
|
|
47
|
+
# write()
|
|
48
|
+
|
|
49
|
+
def writeln(self, l, flush=True, fs=None, print_fs=sys.stdout):
|
|
50
|
+
self.write(l, end="\n", flush=flush, fs=fs, print_fs=print_fs)
|
|
51
|
+
# writeln()
|
|
52
|
+
|
|
53
|
+
def error(self, l, end="\n", flush=True, fs=None):
|
|
54
|
+
self.write(l, end, flush, fs, print_fs=sys.stderr)
|
|
55
|
+
# error()
|
|
56
|
+
|
|
57
|
+
def close(self):
|
|
58
|
+
if self.ofs is not None:
|
|
59
|
+
self.ofs.close()
|
|
60
|
+
self.ofs = None
|
|
61
|
+
# close()
|
|
62
|
+
|
|
63
|
+
def flush(self): # to act as a file object
|
|
64
|
+
if self.ofs:
|
|
65
|
+
self.ofs.flush()
|
|
66
|
+
# class Logger
|
|
67
|
+
|
|
68
|
+
_logger = Logger() # singleton
|
|
69
|
+
set_file = _logger.set_file
|
|
70
|
+
write = _logger.write
|
|
71
|
+
writeln = _logger.writeln
|
|
72
|
+
error = _logger.error
|
|
73
|
+
close = _logger.close
|
|
74
|
+
flush = _logger.flush
|
|
75
|
+
stop = _logger.stop_logging
|
|
76
|
+
start = _logger.start_logging
|
|
77
|
+
set_prefix = _logger.set_prefix
|
|
78
|
+
clear_prefix = _logger.clear_prefix
|
|
79
|
+
|
|
80
|
+
def with_prefix(prefix):
|
|
81
|
+
class WithPrefix(object): # should keep original prefix and restore?
|
|
82
|
+
def __enter__(self):
|
|
83
|
+
_logger.set_prefix(prefix)
|
|
84
|
+
return _logger
|
|
85
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
86
|
+
_logger.clear_prefix()
|
|
87
|
+
return WithPrefix()
|
|
88
|
+
|
|
89
|
+
def silent():
|
|
90
|
+
class Silent(object):
|
|
91
|
+
def write(self, *args, **kwargs):
|
|
92
|
+
pass
|
|
93
|
+
def flush(self):
|
|
94
|
+
pass
|
|
95
|
+
return Silent()
|
|
96
|
+
|
|
97
|
+
def dependency_versions():
|
|
98
|
+
import gemmi
|
|
99
|
+
import scipy
|
|
100
|
+
import numpy
|
|
101
|
+
import pandas
|
|
102
|
+
return dict(gemmi=gemmi.__version__,
|
|
103
|
+
scipy=scipy.version.full_version,
|
|
104
|
+
numpy=numpy.version.full_version,
|
|
105
|
+
pandas=pandas.__version__)
|
|
106
|
+
# dependency_versions()
|
|
107
|
+
|
|
108
|
+
def versions_str():
|
|
109
|
+
tmpl = "Servalcat {servalcat} with Python {python} ({deps})"
|
|
110
|
+
return tmpl.format(servalcat=servalcat.__version__,
|
|
111
|
+
python=platform.python_version(),
|
|
112
|
+
deps=", ".join([x[0]+" "+x[1] for x in dependency_versions().items()]))
|
|
113
|
+
# versions_str()
|
|
114
|
+
|
|
115
|
+
def write_header(command="servalcat"):
|
|
116
|
+
writeln("# Servalcat ver. {} (Python {})".format(servalcat.__version__, platform.python_version()))
|
|
117
|
+
writeln("# Library vers. {}".format(", ".join([x[0]+" "+x[1] for x in dependency_versions().items()])))
|
|
118
|
+
writeln("# Started on {}".format(datetime.datetime.now()))
|
|
119
|
+
writeln("# Host: {} User: {}".format(platform.node(), getpass.getuser()))
|
|
120
|
+
writeln("# Command-line:")
|
|
121
|
+
writeln("# {} {}".format(command, " ".join(map(lambda x: shlex.quote(x), sys.argv[1:]))))
|
|
122
|
+
# write_header()
|
|
123
|
+
|
|
124
|
+
def exit_success():
|
|
125
|
+
_logger.writeln("\n# Finished on {}\n".format(datetime.datetime.now()))
|
|
126
|
+
|
|
127
|
+
def handle_exception(exc_type, exc_value, exc_traceback):
|
|
128
|
+
if issubclass(exc_type, KeyboardInterrupt):
|
|
129
|
+
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
name = type(exc_value).__name__ if hasattr(type(exc_value), "__name__") else "(unknown)"
|
|
133
|
+
#_logger.writeln("Uncaught exception: {}: {}".format(name, exc_value))
|
|
134
|
+
_logger.error("".join(traceback.format_exception(exc_type, exc_value, exc_traceback)))
|
|
135
|
+
_logger.writeln("# Abnormally finished on {}\n".format(datetime.datetime.now()))
|
|
136
|
+
_logger.close()
|
|
137
|
+
|
|
138
|
+
# handle_exception()
|
|
139
|
+
|
|
140
|
+
sys.excepthook = handle_exception
|
servalcat/utils/maps.py
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
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 copy
|
|
12
|
+
import scipy.optimize
|
|
13
|
+
import scipy.signal
|
|
14
|
+
from servalcat.utils import logger
|
|
15
|
+
from servalcat.utils import hkl
|
|
16
|
+
from servalcat import ext
|
|
17
|
+
|
|
18
|
+
def new_grid_like(gr):
|
|
19
|
+
return type(gr)(gr.array*0, gr.unit_cell, gr.spacegroup)
|
|
20
|
+
# new_grid_like()
|
|
21
|
+
|
|
22
|
+
def copy_maps(maps):
|
|
23
|
+
return [[type(m[0])(m[0].array, m[0].unit_cell, m[0].spacegroup)]+copy.deepcopy(m[1:]) for m in maps]
|
|
24
|
+
|
|
25
|
+
def mask_from_model(st, radius, soft_edge=0, grid=None, unit_cell=None, spacegroup=None, grid_shape=None,
|
|
26
|
+
ignore_hydrogen=True):
|
|
27
|
+
if grid is not None:
|
|
28
|
+
mask = new_grid_like(grid)
|
|
29
|
+
else:
|
|
30
|
+
mask = gemmi.FloatGrid(grid_shape)
|
|
31
|
+
mask.set_unit_cell(unit_cell)
|
|
32
|
+
mask.spacegroup = spacegroup
|
|
33
|
+
|
|
34
|
+
if ignore_hydrogen and st[0].has_hydrogen():
|
|
35
|
+
st = st.clone()
|
|
36
|
+
st.remove_hydrogens()
|
|
37
|
+
|
|
38
|
+
if soft_edge > 0:
|
|
39
|
+
ext.soft_mask_from_model(mask, st[0], radius, soft_edge)
|
|
40
|
+
else:
|
|
41
|
+
mask.mask_points_in_constant_radius(st[0], radius, 1.)
|
|
42
|
+
return mask
|
|
43
|
+
# mask_from_model()
|
|
44
|
+
|
|
45
|
+
def test_mask_with_model(mask, st, mask_threshold=.5, inclusion_cutoff=.8):
|
|
46
|
+
logger.writeln("Testing mask with model..")
|
|
47
|
+
n_all = 0
|
|
48
|
+
n_out = 0
|
|
49
|
+
for cra in st[0].all():
|
|
50
|
+
v = mask.interpolate_value(cra.atom.pos)
|
|
51
|
+
n_all += 1
|
|
52
|
+
if v < mask_threshold:
|
|
53
|
+
logger.writeln(" WARNING: mask value at {} = {:.1f}".format(cra, v))
|
|
54
|
+
n_out += 1
|
|
55
|
+
|
|
56
|
+
logger.writeln(" n_atoms= {} n_out_of_mask= {} ({:.2%})".format(n_all, n_out, n_out/n_all if n_all>0 else 0))
|
|
57
|
+
return 1 - n_out/n_all > inclusion_cutoff
|
|
58
|
+
# test_mask_with_model()
|
|
59
|
+
|
|
60
|
+
def check_symmetry_related_map_values(st, grid, cc_cutoff=0.9):
|
|
61
|
+
logger.writeln("Checking if model and map symmetry match.")
|
|
62
|
+
mapvals = numpy.array([grid.interpolate_value(cra.atom.pos) for cra in st[0].all()])
|
|
63
|
+
ret = False # return True if bad
|
|
64
|
+
for op in st.ncs:
|
|
65
|
+
if op.given: continue
|
|
66
|
+
st2 = st.clone()
|
|
67
|
+
st2[0].transform_pos_and_adp(op.tr)
|
|
68
|
+
mapvals2 = numpy.array([grid.interpolate_value(cra.atom.pos) for cra in st2[0].all()])
|
|
69
|
+
cc = numpy.corrcoef(mapvals, mapvals2)[0,1]
|
|
70
|
+
logger.writeln(" CC_map(pos_model, pos_model_ncs{})= {:.4f}".format(op.id, cc))
|
|
71
|
+
if cc < cc_cutoff: ret = True
|
|
72
|
+
return ret
|
|
73
|
+
# check_symmetry_related_map_values()
|
|
74
|
+
|
|
75
|
+
def half2full(map_h1, map_h2):
|
|
76
|
+
assert map_h1.shape == map_h2.shape
|
|
77
|
+
assert map_h1.unit_cell == map_h2.unit_cell
|
|
78
|
+
tmp = (map_h1.array + map_h2.array)/2.
|
|
79
|
+
gr = gemmi.FloatGrid(tmp, map_h1.unit_cell, map_h1.spacegroup)
|
|
80
|
+
return gr
|
|
81
|
+
# half2full()
|
|
82
|
+
|
|
83
|
+
def nyquist_resolution(map_grid):
|
|
84
|
+
grid_shape = map_grid.shape
|
|
85
|
+
rec_cell = map_grid.unit_cell.reciprocal().parameters
|
|
86
|
+
resolutions = [2./rec_cell[i]/grid_shape[i] for i in (0,1,2)]
|
|
87
|
+
return max(resolutions)
|
|
88
|
+
# nyquist_resolution()
|
|
89
|
+
|
|
90
|
+
def sharpen_mask_unsharpen(maps, mask, d_min, b=None):
|
|
91
|
+
assert len(maps) < 3
|
|
92
|
+
if b is None and len(maps) != 2:
|
|
93
|
+
raise RuntimeError("Cannot determine sharpening")
|
|
94
|
+
|
|
95
|
+
hkldata = mask_and_fft_maps(maps, d_min)
|
|
96
|
+
normalizer = numpy.ones(len(hkldata.df.index))
|
|
97
|
+
if len(maps) == 2:
|
|
98
|
+
labs = ["F_map1", "F_map2"]
|
|
99
|
+
else:
|
|
100
|
+
labs = ["FP"]
|
|
101
|
+
|
|
102
|
+
# 1. Sharpen
|
|
103
|
+
if b is None:
|
|
104
|
+
hkldata.setup_relion_binning("ml")
|
|
105
|
+
calc_noise_var_from_halfmaps(hkldata)
|
|
106
|
+
logger.writeln("""$TABLE: Normalizing before masking:
|
|
107
|
+
$GRAPHS: ln(Mn(|F|)) :A:1,2:
|
|
108
|
+
: Normalizer :A:1,3:
|
|
109
|
+
: FSC(full) :A:1,4:
|
|
110
|
+
$$ 1/resol^2 ln(Mn(|F|)) normalizer FSC $$
|
|
111
|
+
$$""")
|
|
112
|
+
for i_bin, idxes in hkldata.binned("ml"):
|
|
113
|
+
bin_d_min = hkldata.binned_df["ml"].d_min[i_bin]
|
|
114
|
+
Fo = hkldata.df.FP.to_numpy()[idxes]
|
|
115
|
+
FSCfull = hkldata.binned_df["ml"].FSCfull[i_bin]
|
|
116
|
+
sig_fo = numpy.std(Fo)
|
|
117
|
+
if FSCfull > 0:
|
|
118
|
+
n_fo = sig_fo * numpy.sqrt(FSCfull)
|
|
119
|
+
else:
|
|
120
|
+
n_fo = sig_fo # XXX not a right way
|
|
121
|
+
|
|
122
|
+
normalizer[idxes] = n_fo
|
|
123
|
+
for lab in labs: hkldata.df.loc[idxes, lab] /= n_fo
|
|
124
|
+
logger.writeln("{:.4f} {:.2f} {:.3f} {:.4f}".format(1/bin_d_min**2,
|
|
125
|
+
numpy.log(numpy.average(numpy.abs(Fo))),
|
|
126
|
+
n_fo, FSCfull))
|
|
127
|
+
|
|
128
|
+
logger.writeln("$$")
|
|
129
|
+
|
|
130
|
+
else:
|
|
131
|
+
logger.writeln("Sharpening B before masking= {}".format(b))
|
|
132
|
+
normalizer[:] = hkldata.debye_waller_factors(b_iso=b)
|
|
133
|
+
for lab in labs: hkldata.df[lab] /= normalizer
|
|
134
|
+
|
|
135
|
+
# 2. Mask, FFT, and unsharpen
|
|
136
|
+
for lab in labs:
|
|
137
|
+
m = hkldata.fft_map(lab, grid_size=mask.shape)
|
|
138
|
+
m.array[:] *= mask
|
|
139
|
+
#write_ccp4_map("debug_{}.ccp4".format(lab), new_maps[-1][0])
|
|
140
|
+
rg = gemmi.transform_map_to_f_phi(m)
|
|
141
|
+
hkldata.df[lab] = rg.get_value_by_hkl(hkldata.miller_array()) * normalizer
|
|
142
|
+
|
|
143
|
+
# TODO can return here for most use cases?
|
|
144
|
+
|
|
145
|
+
new_maps = []
|
|
146
|
+
for i, lab in enumerate(labs):
|
|
147
|
+
m = hkldata.fft_map(lab, grid_size=mask.shape)
|
|
148
|
+
new_maps.append([m]+maps[i][1:])
|
|
149
|
+
|
|
150
|
+
return new_maps
|
|
151
|
+
# sharpen_mask_unsharpen()
|
|
152
|
+
|
|
153
|
+
def mask_and_fft_maps(maps, d_min, mask=None, with_000=True):
|
|
154
|
+
assert len(maps) <= 2
|
|
155
|
+
hkldata = None
|
|
156
|
+
for i, m in enumerate(maps):
|
|
157
|
+
if len(maps) == 2:
|
|
158
|
+
lab = "F_map{}".format(i+1)
|
|
159
|
+
else:
|
|
160
|
+
lab = "FP"
|
|
161
|
+
g = m[0]
|
|
162
|
+
if mask is not None:
|
|
163
|
+
g.array[:] *= mask
|
|
164
|
+
f_grid = gemmi.transform_map_to_f_phi(g)
|
|
165
|
+
if hkldata is None:
|
|
166
|
+
asudata = f_grid.prepare_asu_data(dmin=d_min, with_000=with_000)
|
|
167
|
+
hkldata = hkl.hkldata_from_asu_data(asudata, lab)
|
|
168
|
+
else:
|
|
169
|
+
hkldata.df[lab] = f_grid.get_value_by_hkl(hkldata.miller_array())
|
|
170
|
+
|
|
171
|
+
if len(maps) == 2:
|
|
172
|
+
hkldata.df["FP"] = (hkldata.df.F_map1 + hkldata.df.F_map2)/2.
|
|
173
|
+
|
|
174
|
+
return hkldata
|
|
175
|
+
# mask_and_fft_maps()
|
|
176
|
+
|
|
177
|
+
def calc_noise_var_from_halfmaps(hkldata):
|
|
178
|
+
hkldata.binned_df["ml"]["var_noise"] = 0.
|
|
179
|
+
hkldata.binned_df["ml"]["var_signal"] = 0.
|
|
180
|
+
hkldata.binned_df["ml"]["FSCfull"] = 0.
|
|
181
|
+
|
|
182
|
+
logger.writeln("Bin Ncoeffs d_max d_min FSChalf var.noise")
|
|
183
|
+
for i_bin, idxes in hkldata.binned("ml"):
|
|
184
|
+
bin_d_min = hkldata.binned_df["ml"].d_min[i_bin]
|
|
185
|
+
bin_d_max = hkldata.binned_df["ml"].d_max[i_bin]
|
|
186
|
+
|
|
187
|
+
sel1 = hkldata.df.F_map1.to_numpy()[idxes]
|
|
188
|
+
sel2 = hkldata.df.F_map2.to_numpy()[idxes]
|
|
189
|
+
|
|
190
|
+
if sel1.size < 3:
|
|
191
|
+
logger.writeln("WARNING: skipping bin {} with size= {}".format(i_bin, sel1.size))
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
fsc = numpy.real(numpy.corrcoef(sel1, sel2)[1,0])
|
|
195
|
+
varn = numpy.var(sel1-sel2)/4
|
|
196
|
+
vart = numpy.var(sel1+sel2)/4
|
|
197
|
+
logger.writeln("{:3d} {:7d} {:7.3f} {:7.3f} {:.4f} {:e}".format(i_bin, sel1.size, bin_d_max, bin_d_min,
|
|
198
|
+
fsc, varn))
|
|
199
|
+
hkldata.binned_df["ml"].loc[i_bin, "var_noise"] = varn
|
|
200
|
+
hkldata.binned_df["ml"].loc[i_bin, "var_signal"] = vart-varn
|
|
201
|
+
hkldata.binned_df["ml"].loc[i_bin, "FSCfull"] = 2*fsc/(1+fsc)
|
|
202
|
+
# calc_noise_var_from_halfmaps()
|
|
203
|
+
|
|
204
|
+
def write_ccp4_map(filename, array, cell=None, sg=None, mask_for_extent=None, mask_threshold=0.5, mask_padding=5,
|
|
205
|
+
grid_start=None, grid_shape=None, update_cell=False):
|
|
206
|
+
"""
|
|
207
|
+
- If mask_for_extent is set: grid_shape is ignored
|
|
208
|
+
- grid_shape must be specified together with grid_start.
|
|
209
|
+
- mask_padding unit: px
|
|
210
|
+
"""
|
|
211
|
+
logger.writeln("Writing map file: {}".format(filename))
|
|
212
|
+
ccp4 = gemmi.Ccp4Map()
|
|
213
|
+
|
|
214
|
+
if type(array) == numpy.ndarray:
|
|
215
|
+
# TODO check dtype
|
|
216
|
+
if sg is None: sg = gemmi.SpaceGroup(1)
|
|
217
|
+
ccp4.grid = gemmi.FloatGrid(array, cell, sg)
|
|
218
|
+
else:
|
|
219
|
+
# TODO check type
|
|
220
|
+
ccp4.grid = array
|
|
221
|
+
if cell is not None: ccp4.grid.set_unit_cell(cell)
|
|
222
|
+
if sg is not None: ccp4.grid.spacegroup = sg
|
|
223
|
+
|
|
224
|
+
ccp4.update_ccp4_header(2, True) # float, update stats
|
|
225
|
+
|
|
226
|
+
if mask_for_extent is not None: # want to crop part of map using mask
|
|
227
|
+
tmp = numpy.where(mask_for_extent.array > mask_threshold)
|
|
228
|
+
if grid_start is not None:
|
|
229
|
+
grid_start = numpy.array(grid_start)[:,None]
|
|
230
|
+
shape = numpy.array(ccp4.grid.shape)[:,None]
|
|
231
|
+
tmp -= grid_start
|
|
232
|
+
tmp += (shape*numpy.floor(1-tmp/shape)).astype(int) + grid_start
|
|
233
|
+
|
|
234
|
+
l = [(min(x)-mask_padding, max(x)+mask_padding) for x in tmp]
|
|
235
|
+
grid_start = [l[i][0] for i in range(3)]
|
|
236
|
+
grid_shape = [l[i][1]-l[i][0]+1 for i in range(3)]
|
|
237
|
+
|
|
238
|
+
if grid_start is not None: # want to change origin
|
|
239
|
+
new_shape = ccp4.grid.shape if grid_shape is None else grid_shape
|
|
240
|
+
logger.writeln(" setting starting grid: {} {} {}".format(*grid_start))
|
|
241
|
+
logger.writeln(" setting new shape: {} {} {}".format(*new_shape))
|
|
242
|
+
|
|
243
|
+
new_cell = ccp4.grid.unit_cell
|
|
244
|
+
if update_cell:
|
|
245
|
+
abc = [new_cell.parameters[i]*new_shape[i]/ccp4.grid.shape[i] for i in range(3)]
|
|
246
|
+
new_cell = gemmi.UnitCell(abc[0], abc[1], abc[2],
|
|
247
|
+
new_cell.alpha, new_cell.beta, new_cell.gamma)
|
|
248
|
+
logger.writeln(" setting new cell: {:6.2f} {:6.2f} {:6.2f} {:5.1f} {:5.1f} {:5.1f}".format(*new_cell.parameters))
|
|
249
|
+
cell_grid = new_shape
|
|
250
|
+
else:
|
|
251
|
+
cell_grid = ccp4.grid.shape
|
|
252
|
+
|
|
253
|
+
new_grid = gemmi.FloatGrid(ccp4.grid.get_subarray(grid_start, new_shape),
|
|
254
|
+
new_cell,
|
|
255
|
+
ccp4.grid.spacegroup)
|
|
256
|
+
ccp4 = gemmi.Ccp4Map()
|
|
257
|
+
ccp4.grid = new_grid
|
|
258
|
+
ccp4.update_ccp4_header(2, True) # float, update stats
|
|
259
|
+
for i in range(3):
|
|
260
|
+
ccp4.set_header_i32(5+i, grid_start[i])
|
|
261
|
+
ccp4.set_header_i32(1+i, new_shape[i])
|
|
262
|
+
ccp4.set_header_i32(8+i, cell_grid[i])
|
|
263
|
+
|
|
264
|
+
ccp4.write_ccp4_map(filename)
|
|
265
|
+
# write_ccp4_map()
|
|
266
|
+
|
|
267
|
+
def optimize_peak(grid, ini_pos):
|
|
268
|
+
logger.writeln("Finding peak using interpolation..")
|
|
269
|
+
x = grid.unit_cell.fractionalize(ini_pos)
|
|
270
|
+
logger.writeln(" x0: [{}, {}, {}]".format(*x.tolist()))
|
|
271
|
+
logger.writeln(" f0: {}".format(-grid.interpolate_value(x, order=3)))
|
|
272
|
+
|
|
273
|
+
res = scipy.optimize.minimize(fun=lambda x:-grid.interpolate_value(gemmi.Fractional(*x), order=3),
|
|
274
|
+
x0=x.tolist(),
|
|
275
|
+
jac=lambda x:-numpy.array(grid.tricubic_interpolation_der(gemmi.Fractional(*x))[1:])
|
|
276
|
+
)
|
|
277
|
+
logger.writeln(str(res))
|
|
278
|
+
final_pos = grid.unit_cell.orthogonalize(gemmi.Fractional(*res.x))
|
|
279
|
+
logger.writeln(" Move from initial: [{:.3f}, {:.3f}, {:.3f}] A".format(*(final_pos-ini_pos).tolist()))
|
|
280
|
+
return final_pos
|
|
281
|
+
# optimize_peak()
|
|
282
|
+
|
|
283
|
+
def raised_cosine_kernel(r1, dr=2):
|
|
284
|
+
assert r1 > 2
|
|
285
|
+
assert dr >= 0
|
|
286
|
+
assert r1 > dr
|
|
287
|
+
|
|
288
|
+
boxsize = 2 * r1 + 1
|
|
289
|
+
x, y, z = numpy.meshgrid(range(boxsize), range(boxsize), range(boxsize))
|
|
290
|
+
cen = boxsize // 2
|
|
291
|
+
r0 = r1 - dr
|
|
292
|
+
d = numpy.sqrt((x-cen)**2+(y-cen)**2+(z-cen)**2)
|
|
293
|
+
kern = 0.5 + 0.5 * numpy.cos(numpy.pi * (d - r0) / (r1 - r0))
|
|
294
|
+
kern[d<=r0] = 1
|
|
295
|
+
kern[d>=r1] = 0
|
|
296
|
+
kern /= numpy.sum(kern)
|
|
297
|
+
return kern
|
|
298
|
+
# raised_cosine_kernel()
|
|
299
|
+
|
|
300
|
+
def fft_convolve_simple(in1, in2):
|
|
301
|
+
if numpy.iscomplexobj(in1):
|
|
302
|
+
return numpy.fft.ifftn(numpy.fft.fftn(in1) * numpy.fft.fftn(in2))
|
|
303
|
+
else:
|
|
304
|
+
return numpy.fft.irfftn(numpy.fft.rfftn(in1) * numpy.fft.rfftn(in2))
|
|
305
|
+
|
|
306
|
+
def local_var(grid, kernel, method="scipy"):
|
|
307
|
+
if method == "scipy":
|
|
308
|
+
convolve = lambda x, y: scipy.signal.fftconvolve(x, y, "same")
|
|
309
|
+
else:
|
|
310
|
+
convolve = fft_convolve_simple
|
|
311
|
+
mean_x2 = convolve(numpy.abs(grid.array)**2, kernel)
|
|
312
|
+
mean_x = convolve(grid.array, kernel)
|
|
313
|
+
var_x = new_grid_like(grid)
|
|
314
|
+
var_x.array[:] = mean_x2 - numpy.abs(mean_x)**2
|
|
315
|
+
var_x.array[var_x.array<0] = 0 # due to loss of significance
|
|
316
|
+
return var_x
|
|
317
|
+
# local_var()
|
|
318
|
+
|
|
319
|
+
def local_cc(map1, map2, kernel, method="scipy"):
|
|
320
|
+
# maps may be real space grid or reciprocal space grid
|
|
321
|
+
if method == "scipy":
|
|
322
|
+
convolve = lambda x, y: scipy.signal.fftconvolve(x, y, "same")
|
|
323
|
+
else:
|
|
324
|
+
convolve = fft_convolve_simple
|
|
325
|
+
|
|
326
|
+
localcc = new_grid_like(map1)
|
|
327
|
+
mean_1_sqr = convolve(numpy.abs(map1.array)**2, kernel)
|
|
328
|
+
mean_2_sqr = convolve(numpy.abs(map2.array)**2, kernel)
|
|
329
|
+
mean_1 = convolve(map1.array, kernel)
|
|
330
|
+
mean_2 = convolve(map2.array, kernel)
|
|
331
|
+
mean_12 = convolve(map1.array * numpy.conj(map2.array), kernel)
|
|
332
|
+
var_1 = mean_1_sqr - numpy.abs(mean_1)**2
|
|
333
|
+
var_2 = mean_2_sqr - numpy.abs(mean_2)**2
|
|
334
|
+
bad_sel = (var_1 <= 0) | (var_2 <= 0)
|
|
335
|
+
var_1_2 = var_1 * var_2
|
|
336
|
+
var_1_2[bad_sel] = 1 # to avoid division by zero
|
|
337
|
+
covar_12 = mean_12 - mean_1 * numpy.conj(mean_2)
|
|
338
|
+
localcc.array[:] = covar_12 / numpy.sqrt(var_1_2)
|
|
339
|
+
localcc.array[bad_sel] = 0.
|
|
340
|
+
if numpy.iscomplexobj(localcc.array):
|
|
341
|
+
localcc.array[:] = localcc.array.real
|
|
342
|
+
localcc.array[localcc.array > 1] = 1.
|
|
343
|
+
localcc.array[localcc.array < -1] = -1.
|
|
344
|
+
return localcc
|
|
345
|
+
# local_cc()
|