Skip to content

Commit

Permalink
Move some PyAna submodules to PyAnaMisc pkg with empty __init__.py
Browse files Browse the repository at this point in the history
  • Loading branch information
tkittel committed Aug 8, 2024
1 parent db7f64e commit 5d0c469
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 160 deletions.
2 changes: 1 addition & 1 deletion src/simplebuild_dgcode/data/pkgs/PyAnalysis/PyAna/pkg.info
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package( USEPKG Units USEEXT Numpy )
package( USEPKG Units PyAnaMisc USEEXT Numpy )

######################################################################

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def kh(evt):

#3) disable FPE's certain matplotlib function calls (we don't care too
# much about FPE's in other peoples gui/plotting code):
from PyAna.fpe import standardMPLFixes as _standardMPLFixes
from PyAnaMisc.fpe import standardMPLFixes as _standardMPLFixes
_standardMPLFixes()

#4) Make all legends draggable by default (unless new kw notdraggable=True) and set default numpoints=1 :
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1 @@
#To avoid confusing users with useless(?) warnings such as the following:
#
#/usr/lib64/python2.7/site-packages/matplotlib/backends/backend_gtk.py:250: Warning: Source ID 2 was not found when attempting to remove it
# gobject.source_remove(self._idle_event_id)
#
#we monkey-patch FigureCanvasGTK.destroy and block stderr while it is invoked.
#
#Hopefully in some future version of matplotlib this warning will disappear by itself.
import sys,os
try:
from matplotlib.backends.backend_gtk import FigureCanvasGTK
if hasattr(FigureCanvasGTK,'destroy'):
_FigureCanvasGTK_destroy = FigureCanvasGTK.destroy
def patched_FigureCanvasGTK_destroy(self):
_stderr = sys.stderr
null = open(os.devnull,'wb')
try:
sys.stderr.flush()
sys.stderr = null
_FigureCanvasGTK_destroy(self)
finally:
sys.stderr.flush()
sys.stderr = _stderr
null.close()
FigureCanvasGTK.destroy=patched_FigureCanvasGTK_destroy
except ImportError:
#Something unexpected, but let us not break stuff for users because of it!
pass
import PyAnaMisc._fix_backend_gtk
119 changes: 1 addition & 118 deletions src/simplebuild_dgcode/data/pkgs/PyAnalysis/PyAna/python/dyncmap.py
Original file line number Diff line number Diff line change
@@ -1,118 +1 @@
from __future__ import division
__all__=['dynamic_2d_colormap']

import numpy as np
from matplotlib.colors import LinearSegmentedColormap

def dynamic_2d_colormap(data,imgobj=None,ncols=65536):
#attempt to use high-precision floating point for intermediate calculations:
try:
import decimal
_=decimal.Context(prec=999)
hpfloat,hpabs,hpmin,hpmax=_.create_decimal,_.abs,_.min,_.max
hpceil = lambda x : x.to_integral_value(rounding=decimal.ROUND_CEILING)
except ImportError:
import math
hpfloat,hpabs,hpmin,hpmax,hpceil=float,abs,min,max,math.ceil

hpncols=hpfloat(ncols)
zero,one=hpfloat(0.0),hpfloat(1.0)
#figure out limits (remember that all values in data might be masked):
rawmin = np.min(data)
rawmax = np.max(data)
if rawmin is np.ma.masked and rawmax is np.ma.masked:
rawmin,rawmax = -one, one
datamin = hpfloat(rawmin)
datamax = hpfloat(rawmax)
ddata=datamax-datamin
if ddata:
datamin -= hpfloat(0.005)*hpabs(ddata)
datamax += hpfloat(0.005)*hpabs(ddata)
else:
datamin -= hpfloat(0.5);
datamax += hpfloat(0.5);

#NB: In principle there is a bug here, ispos+isneg should be based on
#rawmin/rawmax not datamin/datamax. However, at least the code in
#NCSABVal/scripts/sabstudio is relying on the present behaviour, so we can't
#just fix it right away:
ispos=datamin>=zero
isneg=datamax<=zero
#ispos=rawmin>=zero
#isneg=rawmax<=zero

if ispos and datamin<zero:
datamin=zero
if isneg and datamax>zero:
datamax=zero
if not ispos and not isneg:
#make sure that 0.0 falls on a "bin-edge" in the colormap, so as to
#not let small positive values accidentally acquire blue values or
#vice versa:
assert datamax>zero and datamin<zero
d0=(datamax-datamin)/hpncols
m=hpmin(datamax,-datamin)
k=hpmax(hpceil(m/d0),4)#at least 4 bins on the smallest side
assert k<hpncols
eps=(k*(datamax-datamin)-hpncols*m)/(hpncols-k)
if datamax > -datamin: datamin -= eps
else: datamax += eps
# d=(datamax-datamin)/hpncols

#relative root-mean-square clipped to [0.0001,0.3]:
npmean=np.mean(np.square(data))
if npmean is np.ma.masked:
rms = hpfloat(0.1)
else:
rms=hpmax(hpfloat(0.0001),
hpmin(hpfloat(0.3),
hpfloat(np.sqrt(npmean))/(datamax-datamin)))
if datamin>=0.0:
cdict = {'red': [(0.0, 0.10, 0.10),
(float(rms), 0.7, 0.7),
(float(3*rms), 1.0, 1.0),
(1.0, 1.0, 1.0)],
'green': [(0.0, 0.0, 0.0),
(1.0, 1.0, 1.0)],
'blue': [(0.0, 0.0, 0.0),
(1.0, 0.1, 0.1)]}
elif datamax<=0.0:
cdict = {'red': [(0.0, 0.1, 0.1),
(1.0, 0.0, 0.0)],
'green': [(0.0, 1.0, 1.0),
(1.0, 0.0, 0.0)],
'blue': [(0.0, 1.0, 1.0),
(float(one-3*rms), 1.0, 1.0),
(float(one-rms), 0.7, 0.7),
(1.0, 0.1, 0.1)]}
else:
rneg = - datamin / ( datamax - datamin )
dbin = one/hpncols
#In rneg-dbin to rneg+dbin, we keep equal mix of red and blue, so
#numerical errors won't show e.g. very small positive values as blue or
#vice versa:
cdict = {'red': [(0.0, 0.1, 0.1),
(float(rneg-dbin), 0.0, 0.1),
(float(rneg+dbin), 0.1, 0.1),
(float(rneg+hpmax(hpfloat(2)*dbin,(one-rneg)*rms)), 0.7, 0.7),
(float(rneg+hpmax(hpfloat(3)*dbin,(one-rneg)*rms*hpfloat(3))), 1.0, 1.0),
(1.0, 1.0, 1.0)],
'green': [(0.0, 1.0, 1.0),
(float(rneg-dbin), 0.0, 0.0),
(float(rneg+dbin), 0.0, 0.0),
(1.0, 1.0, 1.0)],
'blue': [(0.0, 1.0, 1.0),
(float(rneg-hpmax(hpfloat(3)*dbin,rneg*hpfloat(3)*rms)), 1.0, 1.0),
(float(rneg-hpmax(hpfloat(2)*dbin,rneg*rms)), 0.7, 0.7),
(float(rneg-dbin), 0.1, 0.1),
(float(rneg+dbin), 0.1, 0.0),
(1.0, 0.1, 0.1)]}
#for k,v in cdict.items():
# print k,v
import matplotlib.colors
cmap=LinearSegmentedColormap('dynamic_rbcmap', cdict, N=ncols, gamma=1.0)
cmap.set_bad('black')#color of masked values (if any)
if imgobj:
imgobj.set_clim(float(datamin),float(datamax))
imgobj.set_cmap(cmap)
return float(datamin),float(datamax),cmap
from PyAnaMisc.dyncmap import dynamic_2d_colormap
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package()

######################################################################

Package providing standalone modules (empty __init__.py!) related to PyAna.

primary author: [email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#To avoid confusing users with useless(?) warnings such as the following:
#
#/usr/lib64/python2.7/site-packages/matplotlib/backends/backend_gtk.py:250: Warning: Source ID 2 was not found when attempting to remove it
# gobject.source_remove(self._idle_event_id)
#
#we monkey-patch FigureCanvasGTK.destroy and block stderr while it is invoked.
#
#Hopefully in some future version of matplotlib this warning will disappear by itself.
import sys
import os
try:
from matplotlib.backends.backend_gtk import FigureCanvasGTK
if hasattr(FigureCanvasGTK,'destroy'):
_FigureCanvasGTK_destroy = FigureCanvasGTK.destroy
def patched_FigureCanvasGTK_destroy(self):
_stderr = sys.stderr
null = open(os.devnull,'wb')
try:
sys.stderr.flush()
sys.stderr = null
_FigureCanvasGTK_destroy(self)
finally:
sys.stderr.flush()
sys.stderr = _stderr
null.close()
FigureCanvasGTK.destroy=patched_FigureCanvasGTK_destroy
except ImportError:
#Something unexpected, but let us not break stuff for users because of it!
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
__all__=['dynamic_2d_colormap']

import numpy as np
from matplotlib.colors import LinearSegmentedColormap

def dynamic_2d_colormap(data,imgobj=None,ncols=65536):
#attempt to use high-precision floating point for intermediate calculations:
try:
import decimal
_=decimal.Context(prec=999)
hpfloat,hpabs,hpmin,hpmax=_.create_decimal,_.abs,_.min,_.max
hpceil = lambda x : x.to_integral_value(rounding=decimal.ROUND_CEILING)
except ImportError:
import math
hpfloat,hpabs,hpmin,hpmax,hpceil=float,abs,min,max,math.ceil

hpncols=hpfloat(ncols)
zero,one=hpfloat(0.0),hpfloat(1.0)
#figure out limits (remember that all values in data might be masked):
rawmin = np.min(data)
rawmax = np.max(data)
if rawmin is np.ma.masked and rawmax is np.ma.masked:
rawmin,rawmax = -one, one
datamin = hpfloat(rawmin)
datamax = hpfloat(rawmax)
ddata=datamax-datamin
if ddata:
datamin -= hpfloat(0.005)*hpabs(ddata)
datamax += hpfloat(0.005)*hpabs(ddata)
else:
datamin -= hpfloat(0.5);
datamax += hpfloat(0.5);

#NB: In principle there is a bug here, ispos+isneg should be based on
#rawmin/rawmax not datamin/datamax. However, at least the code in
#NCSABVal/scripts/sabstudio is relying on the present behaviour, so we can't
#just fix it right away:
ispos=datamin>=zero
isneg=datamax<=zero
#ispos=rawmin>=zero
#isneg=rawmax<=zero

if ispos and datamin<zero:
datamin=zero
if isneg and datamax>zero:
datamax=zero
if not ispos and not isneg:
#make sure that 0.0 falls on a "bin-edge" in the colormap, so as to
#not let small positive values accidentally acquire blue values or
#vice versa:
assert datamax>zero and datamin<zero
d0=(datamax-datamin)/hpncols
m=hpmin(datamax,-datamin)
k=hpmax(hpceil(m/d0),4)#at least 4 bins on the smallest side
assert k<hpncols
eps=(k*(datamax-datamin)-hpncols*m)/(hpncols-k)
if datamax > -datamin: datamin -= eps
else: datamax += eps
# d=(datamax-datamin)/hpncols

#relative root-mean-square clipped to [0.0001,0.3]:
npmean=np.mean(np.square(data))
if npmean is np.ma.masked:
rms = hpfloat(0.1)
else:
rms=hpmax(hpfloat(0.0001),
hpmin(hpfloat(0.3),
hpfloat(np.sqrt(npmean))/(datamax-datamin)))
if datamin>=0.0:
cdict = {'red': [(0.0, 0.10, 0.10),
(float(rms), 0.7, 0.7),
(float(3*rms), 1.0, 1.0),
(1.0, 1.0, 1.0)],
'green': [(0.0, 0.0, 0.0),
(1.0, 1.0, 1.0)],
'blue': [(0.0, 0.0, 0.0),
(1.0, 0.1, 0.1)]}
elif datamax<=0.0:
cdict = {'red': [(0.0, 0.1, 0.1),
(1.0, 0.0, 0.0)],
'green': [(0.0, 1.0, 1.0),
(1.0, 0.0, 0.0)],
'blue': [(0.0, 1.0, 1.0),
(float(one-3*rms), 1.0, 1.0),
(float(one-rms), 0.7, 0.7),
(1.0, 0.1, 0.1)]}
else:
rneg = - datamin / ( datamax - datamin )
dbin = one/hpncols
#In rneg-dbin to rneg+dbin, we keep equal mix of red and blue, so
#numerical errors won't show e.g. very small positive values as blue or
#vice versa:
cdict = {'red': [(0.0, 0.1, 0.1),
(float(rneg-dbin), 0.0, 0.1),
(float(rneg+dbin), 0.1, 0.1),
(float(rneg+hpmax(hpfloat(2)*dbin,(one-rneg)*rms)), 0.7, 0.7),
(float(rneg+hpmax(hpfloat(3)*dbin,(one-rneg)*rms*hpfloat(3))), 1.0, 1.0),
(1.0, 1.0, 1.0)],
'green': [(0.0, 1.0, 1.0),
(float(rneg-dbin), 0.0, 0.0),
(float(rneg+dbin), 0.0, 0.0),
(1.0, 1.0, 1.0)],
'blue': [(0.0, 1.0, 1.0),
(float(rneg-hpmax(hpfloat(3)*dbin,rneg*hpfloat(3)*rms)), 1.0, 1.0),
(float(rneg-hpmax(hpfloat(2)*dbin,rneg*rms)), 0.7, 0.7),
(float(rneg-dbin), 0.1, 0.1),
(float(rneg+dbin), 0.1, 0.0),
(1.0, 0.1, 0.1)]}
#for k,v in cdict.items():
# print k,v
import matplotlib.colors
cmap=LinearSegmentedColormap('dynamic_rbcmap', cdict, N=ncols, gamma=1.0)
cmap.set_bad('black')#color of masked values (if any)
if imgobj:
imgobj.set_clim(float(datamin),float(datamax))
imgobj.set_cmap(cmap)
return float(datamin),float(datamax),cmap
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
def disableFPEDuringCall(obj,fctname):
import Core.misc
if not Core.misc.is_debug_build():
return
orig = getattr(obj,fctname)
import Core.FPE
nofpectxmgr = Core.FPE.DisableFPEContextManager
def safefct(*args,**kwargs):
with nofpectxmgr():
return orig(*args,**kwargs)
setattr(obj,fctname,safefct)

__standardFixesDone = [False]
def standardMPLFixes():
global __standardFixesDone
if __standardFixesDone[0]:
return
__standardFixesDone[0] = True
import Core.misc
if not Core.misc.is_debug_build():
return

try:
import matplotlib.pyplot
except ImportError:
return#do nothing

import matplotlib.figure
import matplotlib.scale
try:
import matplotlib.backends.backend_pdf
except ImportError:
pass
disableFPEDuringCall(matplotlib.pyplot,'tight_layout')
disableFPEDuringCall(matplotlib.figure.Figure,'tight_layout')
disableFPEDuringCall(matplotlib.figure.Figure,'savefig')
disableFPEDuringCall(matplotlib.pyplot,'show')
disableFPEDuringCall(matplotlib.pyplot,'plot')
disableFPEDuringCall(matplotlib.pyplot,'savefig')
if hasattr(matplotlib.scale,'LogTransformBase'):
disableFPEDuringCall(matplotlib.scale.LogTransformBase,'transform_non_affine')
if hasattr(matplotlib.scale,'LogTransform'):
disableFPEDuringCall(matplotlib.scale.LogTransform,'transform_non_affine')
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package(USEPKG Utils USEEXT ZLib)
package(USEPKG Utils PyAnaMisc USEEXT ZLib)

######################################################################

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
if _backend_ok and _backend=='gtkagg':
#attempt to silence some useless/confusing warnings
try:
import PyAna._fix_backend_gtk # noqa F401
import PyAnaMisc._fix_backend_gtk # noqa F401
except ImportError:
#perhaps PyAna package was not built, no biggie, users might get warnings.
#perhaps PyAnaMisc package was not built, no biggie, users might get warnings.
pass

if _backend_ok and _backend=='tkagg':
Expand All @@ -21,11 +21,7 @@
message=('Treat the new Tool classes introduced in v1.5 as experimental'+
' for now, the API will likely change in version 2.1'))

try:
from PyAna.fpe import standardMPLFixes as _standardMPLFixes # noqa E402
except ModuleNotFoundError:
#fail silently, the PyAna pkg might be disabled
_standardMPLFixes = lambda : None
from PyAnaMisc.fpe import standardMPLFixes as _standardMPLFixes # noqa E402
_standardMPLFixes()

def _ensure_backend_ok():
Expand Down
Loading

0 comments on commit 5d0c469

Please sign in to comment.