Skip to content

Commit

Permalink
Bug fixes: Allow binning unmerged datasets, UI fixes when user enters…
Browse files Browse the repository at this point in the history
… explicit bin thresholds, rectifying broken I/SigI preset button
  • Loading branch information
Oeffner committed Nov 1, 2022
1 parent 30c0470 commit b6b8e3b
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 42 deletions.
5 changes: 3 additions & 2 deletions crys3d/hklviewer/HKLviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ def __init__(self, thisapp, isembedded=False): #, cctbxpython=None):
self.matching_arrays = []
self.bin_infotpls = None
self.bin_opacities= None
self.nbins = 1
self.lowerbinvals = []
self.upperbinvals = []
self.html_url = None
Expand Down Expand Up @@ -984,9 +985,9 @@ def ProcessMessages(self):
# needed for determining if expansion checkbox for P1 and friedel are enabled or disabled
self.ano_spg_tpls = self.infodict.get("ano_spg_tpls",[])

if self.infodict.get("current_scene_id", None) is not None:
if self.infodict.get("current_labels", None) is not None:
# needed in case we run xtriage on this miller array
self.current_labels = self.array_infotpls[self.infodict.get("current_scene_id")][1][0]
self.current_labels = self.infodict.get("current_labels")

if self.infodict.get("colnames_select_lst"):
self.colnames_select_lst = self.infodict.get("colnames_select_lst",[])
Expand Down
96 changes: 57 additions & 39 deletions crys3d/hklviewer/jsview_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,14 +333,15 @@ def update_settings(self, diff_phil, curphilparam) :
msg = ""
if self.params.viewer.scene_id is not None and \
has_phil_path(diff_phil,
"scene_id",
#"scene_id",
"show_missing",
"show_only_missing",
"show_systematic_absences",
"binner_idx",
"binlabel",
"nbins",
) and not has_phil_path(diff_phil, "scene_bin_thresholds") :
) and not has_phil_path(diff_phil, "scene_bin_thresholds"):
self.binvalsboundaries = []
self.binvals, self.nuniqueval = self.calc_bin_thresholds(curphilparam.binning.binner_idx,
curphilparam.binning.nbins)
self.sceneisdirty = True
Expand Down Expand Up @@ -900,6 +901,8 @@ def get_scene_id_from_label_or_type(self, datalabel, datatype=None):


def get_label_type_from_scene_id(self, sceneid):
if sceneid is None:
return None,None
# Find data label and type for a particular sceneid
assert sceneid < len(self.hkl_scenes_infos)
datalabel = self.hkl_scenes_infos[sceneid][3]
Expand Down Expand Up @@ -960,12 +963,21 @@ def calc_bin_thresholds(self, binner_idx, nbins):
bindata, dummy = self.get_matched_binarray(binner_idx)
selection = flex.sort_permutation( bindata )
bindata_sorted = bindata.select(selection)
# get binvals by dividing bindata_sorted with nbins
# Get binvals by dividing bindata_sorted with nbins
# This yields approximately the same number of reflections in each bin
binvals = [bindata_sorted[0]] * (nbins+1) #
for i,e in enumerate(bindata_sorted):
idiv = int( (nbins+1)*float(i)/len(bindata_sorted))
binvals[idiv] = e
# If this didn't yield enough bins with different binvalues, say a multiplicity dataset
# with values between [1;6] but 95% reflections having multiplcity=2 then assign
# binvalues equidistantly between [1;6] even if some bins are empty
nuniquevalues = len(set(list(bindata)))
if len(set(binvals)) < nbins:
binincr = (max(bindata) - min(bindata))/nbins
for i in range(nbins):
binvals[i] = i*binincr + min(bindata)

binvals.sort()
self.mprint("Bin thresholds are:\n" + str(binvals), verbose=1)
return binvals, nuniquevalues
Expand Down Expand Up @@ -1018,22 +1030,16 @@ def MatchBinArrayToSceneArray(self):
binarraydata, dummy = self.get_matched_binarray(self.params.binning.binner_idx)
scenearraydata = self.HKLscene_from_dict().data
ibinarray = self.bin_labels_type_idxs[self.params.binning.binner_idx][2]
matchindices = miller.match_indices(self.HKLscene_from_dict().indices,
self.HKLscene_from_dict(ibinarray).indices )
matched_binarray = binarraydata.select( matchindices.pairs().column(1) )
#valarray.sort(by_value="packed_indices")
#import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
#missing = scenearraydata.lone_set( valarray )
# insert NAN values for reflections in self.miller_array not found in binarray
#valarray = display.ExtendMillerArray(valarray, missing.size(), missing.indices() )
#match_valindices = miller.match_indices(scenearray.indices(), valarray.indices() )
#match_valarray = valarray.select( match_valindices.pairs().column(1) )
#match_valarray.sort(by_value="packed_indices")
#match_valarray.set_info(binarraydata.info() )
if len(set(self.HKLscene_from_dict(ibinarray).indices)) < self.HKLscene_from_dict(ibinarray).indices.size():
raise Sorry("The indices in the array chosen for binning is not unique")

matchindices = miller.match_multi_indices(self.HKLscene_from_dict(ibinarray).indices,
self.HKLscene_from_dict().indices )
matched_binarray = binarraydata.select( matchindices.pairs().column(0) )
# patch the bin array so its sequence matches the scene array
patched_binarraydata = []
c = 0
for b in matchindices.pair_selection(0):
for b in matchindices.pair_selection(1):
if b:
patched_binarraydata.append(matched_binarray[c])
c +=1
Expand Down Expand Up @@ -1258,7 +1264,6 @@ def DrawNGLJavaScript(self, blankscene=False):
self.radii2 = []
self.spbufttips = []

self.binvalsboundaries = []
if not blankscene:
if bin_labels_type_idx[0] =="Resolution":
self.binvalsboundaries = self.binvals
Expand All @@ -1268,24 +1273,28 @@ def DrawNGLJavaScript(self, blankscene=False):
self.bindata = self.scene.singletonsiness
else:
# get upper and lower bounds for the dataset used for binning
dummy, self.binvalsboundaries = self.get_matched_binarray(self.params.binning.binner_idx)
# binvals derived from scene_bin_thresholds must be sorted
self.binvals.sort()
# if minimum or maximum of binvals are smaller or bigger than lower or
# upper bounds then use those values instead
vals = self.binvals[:]
if self.binvals[0] > self.binvalsboundaries[0]:
vals[0] = self.binvalsboundaries[0]
if self.binvals[-1] < self.binvalsboundaries[1]:
vals[-1] = self.binvalsboundaries[1]
self.binvalsboundaries = vals
self.binvalsboundaries.sort()
self.bindata = self.MatchBinArrayToSceneArray()
if len(self.binvalsboundaries)==0 or len(self.params.binning.scene_bin_thresholds) > 0:
dummy, self.binvalsboundaries = self.get_matched_binarray(self.params.binning.binner_idx)
# binvals derived from scene_bin_thresholds must be sorted
# if minimum or maximum of binvals are smaller or bigger than lower or
# upper bounds then use those values instead
self.binvals.sort()
vals = self.binvals[:]
# ignoring nan values add binvalsboundaries if these are smaller or bigger than values in binvals
nonanbinvals = [e for e in self.binvals if not math.isnan(e)]
if nonanbinvals[0] > self.binvalsboundaries[0]:
vals[0] = self.binvalsboundaries[0]
if nonanbinvals[-1] < self.binvalsboundaries[1]:
vals[-1] = self.binvalsboundaries[-1]
# if nan values are present then sort with nan being the last value
vals = list(set( vals)) # no duplicates
self.binvalsboundaries = sorted(vals, key= lambda e: sys.maxsize if math.isnan(e) else e)

self.nbinvalsboundaries = len(self.binvalsboundaries)
# avoid resetting opacities of bins unless we change the number of bins
if self.oldnbinvalsboundaries != self.nbinvalsboundaries and not self.executing_preset_btn:
self.params.binning.bin_opacity = [ [1.0, e] for e in range(self.nbinvalsboundaries + 1) ]
self.params.binning.bin_opacity = [ [1.0, e] for e in range(self.nbinvalsboundaries ) ]
self.oldnbinvalsboundaries = self.nbinvalsboundaries
# Un-binnable data are scene data values where there are no matching reflections in the bin data
# Put these in a separate bin and be diligent with the book keeping!
Expand All @@ -1298,7 +1307,7 @@ def DrawNGLJavaScript(self, blankscene=False):
def data2bin(d, binvalsboundaries, nbinvalsboundaries):
for ibin, binval in enumerate(binvalsboundaries):
if math.isnan(d): # NaN values are un-binnable. Tag them for an additional last bin
return nbinvalsboundaries
return nbinvalsboundaries-1
if (ibin+1) == nbinvalsboundaries:
return ibin
if d > binval and d <= binvalsboundaries[ibin+1]:
Expand Down Expand Up @@ -1333,8 +1342,13 @@ def getprecision(v1,v2):
self.bin_infotpls = []
if self.nuniqueval < self.params.binning.nbins:
self.mprint("%d bins was requested but %s data has only %d unique value(s)!" %(self.params.binning.nbins, colstr, self.nuniqueval), 0)
for ibin in range(self.nbinvalsboundaries):
for ibin in range(self.nbinvalsboundaries+1):
mstr =""
nreflsinbin = len(self.radii2[ibin])
bin2 = float("nan"); bin1= float("nan") # indicates un-binned data
if ibin == self.nbinvalsboundaries:
mstr= "bin[%d] has %d reflections with no %s values (assigned to %2.3f)" %(cntbin, nreflsinbin, \
colstr, bin1)
precision = 3
if ibin < (self.nbinvalsboundaries-1):
bin1 = self.binvalsboundaries[ibin]
Expand All @@ -1357,10 +1371,16 @@ def getprecision(v1,v2):
binformatstr = "]%2." + str(precision) + "f; %2." + str(precision) + "f]"
mstr= "bin[%d] has %d reflections with %s in " %(cntbin, nreflsinbin, colstr)
mstr += binformatstr %(bin1, bin2)
self.bin_infotpls.append( roundoff((nreflsinbin, bin1, bin2 ), precision) )
self.binstrs.append(mstr)
self.mprint(mstr, verbose=1)
cntbin += 1
if len(self.bin_infotpls) > 0 \
and math.isnan(self.bin_infotpls[-1][1]) \
and math.isnan(self.bin_infotpls[-1][2]) \
and nreflsinbin == 0:
continue

self.bin_infotpls.append( roundoff((nreflsinbin, bin1, bin2 ), precision) )
self.binstrs.append(mstr)
self.mprint(mstr, verbose=1)
cntbin += 1

if self.params.binning.bin_opacity != None:
opqlist = self.params.binning.bin_opacity
Expand Down Expand Up @@ -1407,8 +1427,6 @@ def getprecision(v1,v2):
self.RemoveStageObjects()
for ibin in range(self.nbinvalsboundaries+1):
nreflsinbin = len(self.radii2[ibin])
if nreflsinbin == 0:
continue
if self.debug:
self.SetBrowserDebug("true")
self.DefineHKL_Axes(str(Hstararrowstart), str(Hstararrowend),
Expand All @@ -1429,7 +1447,7 @@ def getprecision(v1,v2):
self.sceneisdirty = False
self.lastscene_id = self.params.viewer.scene_id
self.SendInfoToGUI( { "CurrentDatatype": self.get_current_datatype(),
"current_scene_id": self.params.viewer.scene_id } )
"current_labels": self.get_label_type_from_scene_id( self.params.viewer.scene_id)[0] } )
self.mprint("\nDone rendering reflections ")


Expand Down
2 changes: 1 addition & 1 deletion crys3d/hklviewer/preset_buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@

("IoverSigI", "I/SigI >= 2 ( from miller_array.data()/miller_array.sigmas() )",
"""
miller_array_operation = "('newarray._data=array1.data()/array1.sigmas()', 'IoverSigI', ['I<<FSQ,SIGI<<FSQ', 'Intensity'], ['', ''])"
miller_array_operation = "('newarray._data=array1.data()/array1.sigmas()\\nnewarray._sigmas = None', 'IoverSigI', ['I<<FSQ,SIGI<<FSQ', 'Intensity'], ['', ''])"
binning {
scene_bin_thresholds = -10000 1 2 3 4 5 460 793.55 2750
binlabel = "IoverSigI"
Expand Down

0 comments on commit b6b8e3b

Please sign in to comment.