servalcat 0.4.60__cp312-cp312-win_amd64.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.

Potentially problematic release.


This version of servalcat might be problematic. Click here for more details.

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