From f895a78e98cdc3fc7a23a8e1340ea0caf5c16e35 Mon Sep 17 00:00:00 2001
From: madsmpedersen <m@madsp.dk>
Date: Thu, 10 Dec 2015 13:45:06 +0100
Subject: [PATCH] functions - utils

---
 .../fatigue_tools/rainflowcounting/compile.py |   5 +-
 .../rainflowcounting/pair_range.pyd           | Bin 39936 -> 40448 bytes
 .../rainflowcounting/peak_trough.pyd          | Bin 37888 -> 37888 bytes
 .../rainflowcounting/rainflowcount.py         |  14 +-
 .../rainflowcounting/rainflowcount_astm.pyd   | Bin 45056 -> 45056 bytes
 wetb/gtsdf/tests/test_unix_time.py            |  24 +++
 wetb/utils/__init__.py                        |   0
 wetb/utils/caching.py                         |  80 +++++++
 wetb/utils/cython_compile/__init__.py         |   5 +
 .../utils/cython_compile/examples/__init__.py |   0
 wetb/utils/cython_compile/examples/cycheck.py |  33 +++
 .../utils/cython_compile/examples/examples.py |  54 +++++
 wetb/utils/geometry.py                        | 198 ++++++++++++++++++
 wetb/utils/process_exec.py                    |  54 +++++
 wetb/utils/tests/__init__.py                  |   0
 wetb/utils/tests/test_caching.py              |  71 +++++++
 wetb/utils/tests/test_geometry.py             | 113 ++++++++++
 wetb/utils/timing.py                          | 116 ++++++++++
 18 files changed, 761 insertions(+), 6 deletions(-)
 create mode 100644 wetb/gtsdf/tests/test_unix_time.py
 create mode 100644 wetb/utils/__init__.py
 create mode 100644 wetb/utils/caching.py
 create mode 100644 wetb/utils/cython_compile/__init__.py
 create mode 100644 wetb/utils/cython_compile/examples/__init__.py
 create mode 100644 wetb/utils/cython_compile/examples/cycheck.py
 create mode 100644 wetb/utils/cython_compile/examples/examples.py
 create mode 100644 wetb/utils/geometry.py
 create mode 100644 wetb/utils/process_exec.py
 create mode 100644 wetb/utils/tests/__init__.py
 create mode 100644 wetb/utils/tests/test_caching.py
 create mode 100644 wetb/utils/tests/test_geometry.py
 create mode 100644 wetb/utils/timing.py

diff --git a/wetb/fatigue_tools/rainflowcounting/compile.py b/wetb/fatigue_tools/rainflowcounting/compile.py
index 2666310..989b753 100644
--- a/wetb/fatigue_tools/rainflowcounting/compile.py
+++ b/wetb/fatigue_tools/rainflowcounting/compile.py
@@ -1,6 +1,5 @@
-import sys
-sys.path.append("../../../../MMPE/")
-from mmpe.cython_compile.cython_compile import cython_import
+from wetb.utils.cython_compile.cython_compile import cython_import
+
 
 cython_import('pair_range')
 cython_import('peak_trough')
diff --git a/wetb/fatigue_tools/rainflowcounting/pair_range.pyd b/wetb/fatigue_tools/rainflowcounting/pair_range.pyd
index 30f8baed7a441394607bf0e5759a496a08e83a6d..7f84c44c60aecdc881934f4457bb926fd8d719e9 100644
GIT binary patch
delta 5793
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

delta 5515
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

diff --git a/wetb/fatigue_tools/rainflowcounting/peak_trough.pyd b/wetb/fatigue_tools/rainflowcounting/peak_trough.pyd
index 250f85f0e8de0b2c2fe387e0175e92e9c8415cf7..0c51f1e87fd5fbe6715cdc259504e21cea284308 100644
GIT binary patch
delta 5791
zcmai230PBSx<2RVQ9_A;CJ>M%LJ&}d2reLNSPvLv5fpVOqAa4a6att?C5llqG1mHx
zGj(h&YHiu7t*Lf#XmPoQwrXAK*j9VBcdTM<r*u(i?Va53Kj$Dk_s-Ls=kfgK|K9ig
zmVY}x8ao(c2eWg~`-l3fMU?m(qym&rG0qfKgoZe9rX%QEFptiW^-<LFF~cluFH)2d
zPBOFL65Y(WDItrQPWM3*6EN-$VO_NCV>;>9tNSSGT^joixnTuGac1`5OW2tD8oli7
z&-i)`F+2Ma(#qT^>YfvFIg31DM3y4R#-c@(^`h)LF9S5z64@o}qHHcAVl)?C*2)%-
zgxi9zH2Sx*e?Xmq=>96`j3c_MrRd4bo)u*tb~XoJ{jqX88-y|w%I;TStaC3uiM3zQ
zTt*8?5jB%h6Ns8DJNpB^?j)i}6t$0tss)js7-*WEy^G*nJ`kyJFKRdoyJI7t&)YA;
zbL?E^{q44Xb}r5IT7VuugSlV<_4w@8`I{)}G-qX9Ekf%lw*F%KoY?La+n2?5(q@4_
z+<#599}!yV2iT(9er!yaIoGt47({wXFkTrkCZqMLV7)V9Md1MUNM3_tJ0!N$cEK*g
z+6Yc<w<S2Opy|KCeu*!WV1_=)5R<wM-g7?7v}}b<PhDVowm`niP3B`0L`nBCleWSY
zX`pPydWzCo<^8*Hblo@$c`pIy3BHVJ3xrPSWb(JbrxRkCv@JGwS0{$v4BqaW@%gg*
zo6K4x<WEed+hF&^O+Gk$4td8y^b%`~r5Y!57U?eRaaMUsn=NY6QHHtJ0(a$88PAR2
z>M@-;)dF!IYnXVWt<xirb~9pGoLLi%|I}Db9^CNM2Rk;P(IRokD;DsBc=wNl6w8^{
zJ8|X|<fahro6w?IJo&%tc*?g((ORW<2-7v=6o%hi5C2q5VXPa#-OESis3t&KN)n2b
z?gye=iqt5_sMt3k&nty~0d{-EGCpRw>=j850P8)Ed1)JHyyKYXp0%}l|0m7dTyHD!
zsiT=g2Ds$=G*fPXnSQCvT!XFI&zX*&ug9Tq&bFMy5IOj6x~w%9s4!po3|ujoVlo=F
zuBrW*Z9_D2K3PK&OC=er_3*xb8Z$+2W0gNL%#0@ZB_P1Nv<V%4fy1gyL7`={Y8%;i
zR(WU>%nnRuW;cO8a1EX_Uk7ev4sU=}sz`=uh8I<zGRk_W4+>&pDqv60k_jIfP-ae5
zg<#MM!_lclwAWiC*Cm`KLz!lJ*chJ~d!{K?Yh~qncqTZR(d*$@@Z-t1iO%HEMOfsQ
z$gAa(flP<P5zC%7z{epujKKgtp<xrXh}qc$d?sOjc2;eGrJ=rz+yMGej`>6nr$Yl>
zyD*?-b~`@tnAop}2chXqo*v?-EtPz^42@Rl`%Tb3ZGohsnM9I)&<u~JoswQBJ<~&7
zn8mlh8O}~$z);OLkFfuseP%pO#+03k4QJM>l&D!0O&rOa8{pB5xlBa^sKa;CJ#aGI
zhlwbHo8g|4^akGf%?5CUYni+T$c|V}m%!nO3{TI+6lE>)()4Dsc*32Z$P**$!9^WP
zKY}^xRK`$mYf;zG@_-78N{i~_m+6IBWM+a~3&An-4S8|-2qF=XtODDa$Rt|kR5k*=
z70HVpfMV7$dE&|u;QmY=2+Ozq*Q`cbb_~zD;XtKo9y$PP<{XuMzkCGP8^HrNVCLL)
zvX!F%R{_Yi_0CPEWzv!n$-6Un$*(Xe<`sD%4&ZPQS^$!>;atqyGIQ|=GCPcyG{Ily
zt(8ee0mr8Ez(bp7>^xd_0;h7=S>-ey>45UMHb#{N-^8WSUxGT`Lvc2fqE28aO4F+v
zhl5=;o@X9`r{m{jtRo<f9fCl>@d8*nVt$^O#|q{wVvge3CSv{$A2=UTnQ%Qmh;hjT
zCLu`iHHRcD09{H6NhgplKw^T2;%x#>;lbC)%r6zdcBoG9m2FxwQcrjY@46QDC3s{M
z5f$!AbU6z%x%U{Q7*|8g_z%q+i21f)o<__Wym<{VpBK!NICz|pMxO&sV!WgaE5XJ4
zb`fk%^vkG3_K9R(;Y}3If+Clg3wX1;6PkY-%ZH62W=s{sE+gg}f;k`qzD=A>--m#t
zsq`7h#OF&;m*g8UNr=^fOP*iF!BcrRtJ2X8p5W-_7ZUUlepRukJosBuC=;9xca!ET
zny?g1bIh=GQxMNN4OyC*^z+c72~ZrsY>@I7yd3e~Jp{^PIIf9gRB7;yW{TpFz?nA8
z=~VG<zJb8x07ZiU4i<_IY6b8OC{7Ob+>%Q6X}PBNJgIa#Lpbs`OW>tse>xX>liR#L
z#TA`tIp>fs3n1Vixm?Jxj<;Y*$~oC{xDhzZ#{)M%cQ_E3h`^^1lRCqH0*))6Zz0M~
zRPe)a6JsF3f-?adedX}i)TK;xGCWKTp^t-q+C91qB<Y3pVJJ!GXcKg#htvOpYw21>
zk_15+oTQZ;ZI<}u%b+PEvNbiAc(e0C-24$S``%lMjp;hM{OrQlYZ%qeu0mBFx%(`|
zL+uEf2fn!E!0#1%nrHJ@r80cI&9mnrJD;$_kbRP{djz(x$X46gIr!@Dh0|$gU68&|
zxFmg<KyqNmErld5#${6)y3;jrR^L5oVB%V1-Ec*bu~XRDP}ILhV&vLc;`UYYp875h
zK3)OZeRwxsBHhK#{tAhio}tI_E==^(?d&zgI)$L^@g$2oh|#FV@b)5T&D6AN(SCxM
zde0*9-k!1ejsDOf|86o*Y7%|PILqt+WLk^pUOVAEgFO6roW%f9mmTs&0{yj7^q=Ac
zdOx8j3-l7)y6-)}@o>n`2nyzE5ooeJrJF}7@#xA@wYG`++eCjEHMq7v!JWcc`W^Cg
zqQ)9$YAx&vBDKn|@Yu%=c>rRjKD`H~oQOVz;_eVH+j%e250UAR|KyD_Gy5CaQRs<O
z=q85z2Fa>>C4_B}juqS=LXp<e=a9dJI??@C5H{a0@l@<^kU<i}6=iMlx6m!6n~mu`
z=J%TP6%xlPFXUq{9`Ugm%=7)|a_E@v*P8ys)33x6;5?&5fx7^Q`WIX$X;B|)-ce(K
zfxTbwD^oivdj+bkMUvie_zk_c7`4bo`5~M(l<!MKgH=;BP%0=_i%K#~f1igH?4=-0
z>)QGPhC%uvLMPEUXN(poXl9Mlyg)RE$hvVN@xH_*guzw|>e?r{Vj2Tq<Ce#f{udbt
z9BC49DN%P4wS=f&MzxS_t48pUOgvaMxdWYo_@hx`KO#O##Lh&#QV6XOgkHm;`;aiO
zMo|B0l=@Z-s*7<9d!HieAfmQNFXJhsY3p}De)dG)4y0I%m|hP;c!YlNs5`?SQ_|u>
z*qE)Jvqq#xT^?BecdX7bG`xm1yq4_^AljXRwph?UguB^s-uI$~eG!I)`?2WxGXdRS
z0PzcaGHnQR4%bF!yd*l|)z6Wu=Gx+4K@O$Mz=<JWKdt3Ls^zrh>cGPrB(`)b@!*g&
zj^`{ICnB)3bp>!>fqLQ}k<FibpL>!QHnISMeL`*0950%AKOaJJ1DK=vpv?`QK;!Ds
zTD7VqoJCU%Y|KrdPeNa=r<)YNJb2;Tf`g9Gcn<F7&QM(>p9+hF+-}Jq2E_y<H(ewa
z3dG|X5S^DiYu9WdlTJdT<{ey0)W9H4NmSoG6F)TZ-uZ}|9F1OB#ugdj${lbxFIt*4
z7dsYCfrcv?@Kauuq&*KUX7>3!D9^8u)7Ufhx$X$W`E&HATD~{4|ICG3`621I@wjjN
z$Nh1dUW6zMdk*_;LyXS#T|E1#ft4&y%zk0kKST&m0yi?Gw{QksH~*PWOWKhO3l?&&
z6+FHZaZE2S7Y;3q@Kd4<)0l#dIerX2(@fvFn*Kp_TjL+Yj|-FB-^<}MV8sLu=0Hk8
z0JA;^DhpJqLIiPFP^Rz59NW3AYz|tunE2(`=%(OcEzW@>1&Z<U*mtlFxKNPia!iYY
z%Z(Fb<OdhP^ujyL2lHXlqB76oY%FF7-6bvL+P+nCmd`QKx@_37C`^`&m_xo+O43bL
zMI#WB4Hp*0$R40G&Vo~oXC;ra*R{6Ei;vS&o+1~NwlbBP;y#ExaC62Uyk&B+cAX3N
z=pxqqGY3Bwt##ReD<bpJ6|LE~g@e^gN*R{3onPW98-xEdW4l{z9lILKql~*KBbk7d
zc@fpsb>$It^|h7trPb9{HI)%{dflqpnux_!HDzfv<@J^Ng4)`(D{9w8G?(jEL{yaO
zswx}HOLS;#h^Q~Es;O98+q|;2u||jDKh%|%t|`&g*EUwJ3SS93>iuTnKv6n<U3qxj
zx-t?Pmj-o93_mEg6bR|8t*xmf9Qdj}af~AbAfRCroezf^g6S1-9-n3KprMkkfMQ)R
zQ%S)lU6yA_Ni`->QbKe>V2WCYF22_VxtG_JQ6(i+)pfP?I>HEspvH`G0t;3*`nmt_
z{KG-rtneeQ@HcQ+$cj25wu0IAa$_dFt2s=Le>YM8)<FmYHI3DEdTfJ9O~DiugzSGy
zo-m4BRilHFW?wx2C|hf@JMF99BZO{WCbR@0KjojX5BuprxVSFb*C?_C3>TL8OCol{
zawikO>%>w%gC%-@$<XV9*Ee9J-rv1XEO$tpi~uOrd%6#b9Oa(`t_rORH_;6z8iNQ`
zQYSj7gDD1o=iyQN86eBxC%O0pe8%7}`AqEP!byYDN$f1gj=%dVv8UWE1e8FY!Bb)s
ztwwNP@9!QW#@{J&WQ5~BoK^G_<;)g^n<%72<2d+gy<#}xZ}291OA;wVVl`txv)*6g
z^hEH3;fWWwCq|Og@B$ftycU1wcF~PuHyB#d<NnWNkJ=h!Dt-@#8!btode#l%p4FuO
z9#Nd}s+uaD^7nYK|Jhq$-r#_~);j2DRlv4ZC8&*cU^L3Xx^WWr<#5b++Sb0&haMkP
z79NCq)plYNJJ#p-+}h4<=@_g0f2;Y;5(hgiO^~qtqoX@yW7n=b;C4`PP<gQKpwp{v
zuO@YJokgARbYASFx}3T~y3}2{T}55%x{O`zT^(KhT^G9syN0?H-OBEyZmxS(cU||+
z?iae>>F(>k)jiPd)Z^Bp?uqUx>RH}n?Ag}S(ep;n#h%Z4hI**im9K}qZkxH(NdFI+
C0wW;+

delta 5583
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
Hv(Wzn8qL*t

diff --git a/wetb/fatigue_tools/rainflowcounting/rainflowcount.py b/wetb/fatigue_tools/rainflowcounting/rainflowcount.py
index f05a1a0..93c980b 100644
--- a/wetb/fatigue_tools/rainflowcounting/rainflowcount.py
+++ b/wetb/fatigue_tools/rainflowcounting/rainflowcount.py
@@ -1,4 +1,7 @@
 import numpy as np
+from wetb.utils.cython_compile.cython_compile import cython_import
+
+
 def check_signal(signal):
     # check input data validity
     if not type(signal).__name__ == 'ndarray':
@@ -61,13 +64,18 @@ def rainflow_windap(signal, levels=255., thresshold=(255 / 50)):
         signal = signal / gain
         signal = np.round(signal).astype(np.int)
 
+
+        # If possible the module is compiled using cython otherwise the python implementation is used
+        cython_import('wetb.fatigue_tools.rainflowcounting.peak_trough')
+        cython_import('wetb.fatigue_tools.rainflowcounting.pair_range')
+
         from wetb.fatigue_tools.rainflowcounting.peak_trough import peak_trough
+        from wetb.fatigue_tools.rainflowcounting.pair_range import pair_range_amplitude_mean
 
 
         #Convert to list of local minima/maxima where difference > thresshold
         sig_ext = peak_trough(signal, thresshold)
 
-        from wetb.fatigue_tools.rainflowcounting.pair_range import pair_range_amplitude_mean
 
         #rainflow count
         ampl_mean = pair_range_amplitude_mean(sig_ext)
@@ -114,7 +122,7 @@ def rainflow_astm(signal):
 
     # Import find extremes and rainflow.
     # If possible the module is compiled using cython otherwise the python implementation is used
-    #cython_import('fatigue_tools.rainflowcount_astm')
+    cython_import('wetb.fatigue_tools.rainflowcounting.rainflowcount_astm')
     from wetb.fatigue_tools.rainflowcounting.rainflowcount_astm import find_extremes, rainflowcount
 
     # Remove points which is not local minimum/maximum
@@ -123,4 +131,4 @@ def rainflow_astm(signal):
     # rainflow count
     ampl_mean = np.array(rainflowcount(sig_ext))
 
-    return np.array(ampl_mean).T
\ No newline at end of file
+    return np.array(ampl_mean).T
diff --git a/wetb/fatigue_tools/rainflowcounting/rainflowcount_astm.pyd b/wetb/fatigue_tools/rainflowcounting/rainflowcount_astm.pyd
index e985824ec83011c3b893870352cdef19cba7a582..7b90534e6695dad59e19a543b6284ed5cc5ee381 100644
GIT binary patch
delta 5634
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

delta 5475
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
Cr~B;y

diff --git a/wetb/gtsdf/tests/test_unix_time.py b/wetb/gtsdf/tests/test_unix_time.py
new file mode 100644
index 0000000..e69c48b
--- /dev/null
+++ b/wetb/gtsdf/tests/test_unix_time.py
@@ -0,0 +1,24 @@
+'''
+Created on 03/12/2015
+
+@author: mmpe
+'''
+import unittest
+import numpy as np
+import datetime
+from wetb.utils.timing import print_time
+from wetb.gtsdf.unix_time import to_unix
+
+class Test(unittest.TestCase):
+
+    @print_time
+    def r(self, dt):
+        return [to_unix(dt) for dt in dt]
+
+    def test_to_unix(self):
+        dt = [datetime.datetime(2000, 1, 1, 12, s % 60) for s in np.arange(1000000)]
+        self.r(dt)
+
+if __name__ == "__main__":
+    #import sys;sys.argv = ['', 'Test.testName']
+    unittest.main()
diff --git a/wetb/utils/__init__.py b/wetb/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/wetb/utils/caching.py b/wetb/utils/caching.py
new file mode 100644
index 0000000..184825e
--- /dev/null
+++ b/wetb/utils/caching.py
@@ -0,0 +1,80 @@
+'''
+Created on 07/02/2014
+
+@author: MMPE
+'''
+import inspect
+
+def set_cache_property(obj, name, get_func, set_func=None):
+    """Create a cached property
+
+    Parameters
+    ----------
+    obj : object
+        Class to add property to
+    name : str
+        Name of property
+    get_func : func
+        Getter function
+    set_func : func, optional
+        Setter function
+
+    Examples
+    --------
+    >>> class Example(object):
+    >>>     def __init__(self):
+    >>>        set_cache_property(self, "test", self.slow_function)
+    >>>
+    >>> e = Example()
+    >>> e.test # Call, store and return result of e.slow_function
+    >>> e.test # Return stored result of e.slow_function
+    """
+    _name = "_" + name
+    setattr(obj, _name, None)
+    def get(self):
+        if getattr(obj, _name) is None:
+            setattr(obj, _name, get_func())
+        return getattr(obj, _name)
+
+    p = property(lambda self:get(self), set_func)
+    return setattr(obj.__class__, name, p)
+
+def cache_function(f):
+    """Cache function decorator
+
+    Example:
+    >>> class Example(object):
+    >>>    @cache_function
+    >>>    def slow_function(self):
+    >>>        # calculate slow result
+    >>>        return 1
+    >>>
+    >>> e = Example()
+    >>> e.slow_function() # Call, store and return result of e.slow_function
+    >>> e.slow_function() # Return stored result of e.slow_function
+    """
+    def wrap(*args, **kwargs):
+        self = args[0]
+        name = "_" + f.__name__
+        if not hasattr(self, name) or getattr(self, name) is None or kwargs.get("reload", False):
+            try:
+                del kwargs['reload']
+            except KeyError:
+                pass
+            # ======HERE============
+            setattr(self, name, f(*args, **kwargs))
+            # ======================
+            if not hasattr(self, "cache_attr_lst"):
+                self.cache_attr_lst = set()
+                def clear_cache():
+                    for attr in self.cache_attr_lst:
+                        delattr(self, attr)
+                    self.cache_attr_lst = set()
+                self.clear_cache = clear_cache
+            self.cache_attr_lst.add(name)
+
+        return getattr(self, name)
+    if 'reload' in inspect.getargspec(f)[0]:
+        raise AttributeError("Functions decorated with cache_function are not allowed to take a parameter called 'reload'")
+    return wrap
+
diff --git a/wetb/utils/cython_compile/__init__.py b/wetb/utils/cython_compile/__init__.py
new file mode 100644
index 0000000..8d7b1d7
--- /dev/null
+++ b/wetb/utils/cython_compile/__init__.py
@@ -0,0 +1,5 @@
+d = None;d = dir()
+
+from .cython_compile import cython_compile, cython_import, cython_compile_autodeclare, is_compiled
+
+__all__ = [m for m in set(dir()) - set(d)]
\ No newline at end of file
diff --git a/wetb/utils/cython_compile/examples/__init__.py b/wetb/utils/cython_compile/examples/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/wetb/utils/cython_compile/examples/cycheck.py b/wetb/utils/cython_compile/examples/cycheck.py
new file mode 100644
index 0000000..b3979a9
--- /dev/null
+++ b/wetb/utils/cython_compile/examples/cycheck.py
@@ -0,0 +1,33 @@
+'''
+Created on 29/03/2013
+
+@author: Mads
+'''
+import cython
+import math
+
+
+def cycheck(p):
+    for i in range(10):
+        for y in range(2, int(math.sqrt(p)) + 1):
+            if p % y == 0:
+                return False
+    return True
+
+@cython.ccall
+@cython.locals(y=cython.int, p=cython.ulonglong)
+def cycheck_pure(p):
+    for i in range(10):
+        for y in range(2, int(math.sqrt(p)) + 1):
+            if p % y == 0:
+                return False
+    return True
+
+
+def cycheck_cdef(p):  #cpdef cycheck_cdef(unsigned long long p):
+    #cdef int y
+    for i in range(10):
+        for y in range(2, int(math.sqrt(p)) + 1):
+            if p % y == 0:
+                return False
+    return True
diff --git a/wetb/utils/cython_compile/examples/examples.py b/wetb/utils/cython_compile/examples/examples.py
new file mode 100644
index 0000000..53fbe7a
--- /dev/null
+++ b/wetb/utils/cython_compile/examples/examples.py
@@ -0,0 +1,54 @@
+'''
+Created on 11/07/2013
+
+@author: Mads M. Pedersen (mmpe@dtu.dk)
+'''
+import math
+
+from wetb.utils.cython_compile.cython_compile import cython_compile, \
+    cython_compile_autodeclare, cython_import
+from wetb.utils.cython_compile.examples import cycheck
+
+
+def pycheck(p):
+    for i in range(10):
+        for y in range(2, int(math.sqrt(p)) + 1):
+            if p % y == 0:
+                return False
+    return True
+
+
+@cython_compile
+def cycheck_compile(p):
+    import math
+    for i in range(10):
+        for y in range(2, int(math.sqrt(p)) + 1):
+            if p % y == 0:
+                return False
+    return True
+
+
+@cython_compile_autodeclare
+def cycheck_compile_autodeclare(p):
+    import math
+    for i in range(10):
+        for y in range(2, int(math.sqrt(p)) + 1):
+            if p % y == 0:
+                return False
+    return True
+
+
+p = 17
+
+print (pycheck(p))
+
+cython_import('cycheck')
+print (cycheck.cycheck(p))
+print (cycheck.cycheck_pure(p))
+print (cycheck.cycheck_cdef(p))
+
+print (cycheck_compile(p))
+
+print (cycheck_compile_autodeclare(p))
+
+
diff --git a/wetb/utils/geometry.py b/wetb/utils/geometry.py
new file mode 100644
index 0000000..586ad40
--- /dev/null
+++ b/wetb/utils/geometry.py
@@ -0,0 +1,198 @@
+import numpy as np
+
+def rad(deg):
+    return deg * np.pi / 180
+
+def deg(rad):
+    return rad / np.pi * 180
+
+def sind(dir_deg):
+    return np.sin(rad(dir_deg))
+
+def cosd(dir_deg):
+    return np.cos(rad(dir_deg))
+
+def tand(dir_deg):
+    return np.tan(rad(dir_deg))
+
+def mean_deg(dir, axis=0):
+    """Mean of angles in degrees
+
+    Parameters
+    ----------
+    dir : array_like
+        Angles in degrees
+
+    Returns
+    -------
+    mean_deg : float
+        Mean angle
+    """
+    return deg(np.arctan2(np.mean(sind(dir[:]), axis), np.mean(cosd(dir[:]), axis)))
+
+def std_deg(dir):
+    """Standard deviation of angles in degrees
+
+    Parameters
+    ----------
+    dir : array_like
+        Angles in degrees
+
+    Returns
+    -------
+    std_deg : float
+        standard deviation
+    """
+    return deg(np.sqrt(1 - (np.mean(sind(dir)) ** 2 + np.mean(cosd(dir)) ** 2)))
+
+
+def wsp_dir2uv(wsp, dir, dir_ref=None):
+    """Convert horizontal wind speed and direction to u,v
+
+    Parameters
+    ----------
+    wsp : array_like
+        Horizontal wind speed
+    dir : array_like
+        Wind direction
+    dir_ref : int or float, optional
+        Reference direction\n
+        If None, default, the mean direction is used as reference
+
+    Returns
+    -------
+    u : array_like
+        u wind component
+    v : array_like
+        v wind component
+    """
+    if dir_ref is None:
+        dir = dir[:] - mean_deg(dir[:])
+    else:
+        dir = dir[:] - dir_ref
+    u = np.cos(rad(dir)) * wsp[:]
+    v = -np.sin(rad(dir)) * wsp[:]
+    return np.array([u, v])
+
+def wsp_dir_tilt2uvw(wsp, dir, tilt, wsp_horizontal, dir_ref=None):
+    """Convert horizontal wind speed and direction to u,v,w
+
+    Parameters
+    ----------
+    wsp : array_like
+        - if wsp_horizontal is True: Horizontal wind speed, $\sqrt{u^2+v^2}\n
+        - if wsp_horizontal is False: Wind speed, $\sqrt{u^2+v^2+w^2}
+    dir : array_like
+        Wind direction
+    tilt : array_like
+        Wind tilt
+    wsp_horizontal : bool
+        See wsp
+    dir_ref : int or float, optional
+        Reference direction\n
+        If None, default, the mean direction is used as reference
+
+
+    Returns
+    -------
+    u : array_like
+        u wind component
+    v : array_like
+        v wind component
+    w : array_like
+        v wind component
+    """
+    wsp, dir, tilt = wsp[:], dir[:], tilt[:]
+    if wsp_horizontal:
+        w = tand(tilt) * wsp
+        u, v = wsp_dir2uv(wsp, dir, dir_ref)
+    else:
+        w = sind(tilt) * wsp
+        u, v = wsp_dir2uv(np.sqrt(wsp ** 2 - w ** 2), dir, dir_ref)
+    return np.array([u, v, w])
+
+
+
+def xyz2uvw(x, y, z, left_handed=True):
+    """Convert sonic x,y,z measurements to u,v,w wind components
+
+    Parameters
+    ----------
+    x : array_like
+        Sonic x component
+    y : array_like
+        Sonic x component
+    z : array_like
+        Sonic x component
+    left_handed : boolean
+        if true (default), xyz are defined in left handed coodinate system (default for some sonics)
+        if false, xyz are defined in normal right handed coordinate system
+
+    Returns
+    -------
+    u : array_like
+        u wind component
+    v : array_like
+        v wind component
+    w : array_like
+        w wind component
+    """
+    x, y, z = map(np.array, [x, y, z])
+    if left_handed:
+        y *= -1
+    theta = deg(np.arctan2(np.mean(y), np.mean(x)))
+    SV = cosd(theta) * y - sind(theta) * x
+    SUW = cosd(theta) * x + sind(theta) * y
+
+    #% rotation around y of tilt
+    tilt = deg(np.arctan2(np.mean(z), np.mean(SUW)))
+    SU = SUW * cosd(tilt) + z * sind(tilt);
+    SW = z * cosd(tilt) - SUW * sind(tilt);
+
+    return np.array([SU, SV, SW])
+
+def rpm2rads(rpm):
+    return rpm * 2 * np.pi / 60
+
+
+def abvrel2xyz(alpha, beta, vrel):
+    """Convert pitot tube alpha, beta and relative velocity to local Cartesian wind speed velocities
+
+    Parameters
+    ----------
+    alpha : array_like
+        Pitot tube angle of attack
+    beta : array_like
+        Pitot tube side slip angle
+    vrel : array_like
+        Pitot tube relative velocity
+
+    Returns
+    -------
+    x : array_like
+        Wind component towards pitot tube (positive for postive vrel and -90<beta<90)
+    y : array_like
+        Wind component in alpha plane (positive for positive alpha)
+    z : array_like
+        Wind component in beta plane (positive for positive beta)
+    """
+    alpha = np.array(alpha, dtype=np.float)
+    beta = np.array(beta, dtype=np.float)
+    vrel = np.array(vrel, dtype=np.float)
+
+    sign_vsx = -((np.abs(beta) > np.pi / 2) * 2 - 1)  # +1 for |beta| <= 90, -1 for |beta|>90
+    sign_vsy = np.sign(alpha)  #+ for alpha > 0
+    sign_vsz = -np.sign(beta)  #- for beta>0
+
+
+    x = sign_vsx * np.sqrt(vrel ** 2 / (1 + np.tan(alpha) ** 2 + np.tan(beta) ** 2))
+
+    m = alpha != 0
+    y = np.zeros_like(alpha)
+    y[m] = sign_vsy * np.sqrt(vrel[m] ** 2 / ((1 / np.tan(alpha[m])) ** 2 + 1 + (np.tan(beta[m]) / np.tan(alpha[m])) ** 2))
+
+    m = beta != 0
+    z = np.zeros_like(alpha)
+    z[m] = sign_vsz * np.sqrt(vrel[m] ** 2 / ((1 / np.tan(beta[m])) ** 2 + 1 + (np.tan(alpha[m]) / np.tan(beta[m])) ** 2))
+
+    return x, y, z
diff --git a/wetb/utils/process_exec.py b/wetb/utils/process_exec.py
new file mode 100644
index 0000000..caeb497
--- /dev/null
+++ b/wetb/utils/process_exec.py
@@ -0,0 +1,54 @@
+'''
+Created on 10/03/2014
+
+@author: MMPE
+'''
+
+import os
+import psutil
+
+DEBUG = False
+def pexec(args, cwd=None):
+    """
+    usage: errorcode, stdout, stderr, cmd = pexec("MyProgram.exe arg1, arg2", r"c:\tmp\")
+
+    """
+    import subprocess
+    if not isinstance(args, (list, tuple)):
+        args = [args]
+    args = [str(arg) for arg in args]
+    for i in range(len(args)):
+        if os.path.exists(args[i]):
+            args[i] = str(args[i]).replace('/', os.path.sep).replace('\\', os.path.sep).replace('"', '')
+
+    cmd = "%s" % '{} /c "{}"'.format (os.environ.get("COMSPEC", "cmd.exe"), subprocess.list2cmdline(args))
+    if os.path.isfile(cwd):
+        cwd = os.path.dirname(cwd)
+    proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cwd)
+    stdout, stderr = proc.communicate()
+    errorcode = proc.returncode
+
+    return errorcode, stdout.decode(), stderr.decode(), cmd
+
+
+def process(args, cwd=None):
+    import subprocess
+    if not isinstance(args, (list, tuple)):
+        args = [args]
+    args = [str(arg) for arg in args]
+    for i in range(len(args)):
+        if os.path.exists(args[i]):
+            args[i] = str(args[i]).replace('/', os.path.sep).replace('\\', os.path.sep).replace('"', '')
+
+    cmd = "%s" % '{} /c "{}"'.format (os.environ.get("COMSPEC", "cmd.exe"), subprocess.list2cmdline(args))
+    if cwd is not None and os.path.isfile(cwd):
+        cwd = os.path.dirname(cwd)
+    return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, cwd=cwd)
+
+def exec_process(process):
+    stdout, stderr = process.communicate()
+    errorcode = process.returncode
+
+    return errorcode, stdout.decode(), stderr.decode()
+
+
diff --git a/wetb/utils/tests/__init__.py b/wetb/utils/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/wetb/utils/tests/test_caching.py b/wetb/utils/tests/test_caching.py
new file mode 100644
index 0000000..4d42a6e
--- /dev/null
+++ b/wetb/utils/tests/test_caching.py
@@ -0,0 +1,71 @@
+'''
+Created on 08/11/2013
+
+@author: mmpe
+'''
+import multiprocessing
+import time
+import unittest
+
+
+from wetb.utils.timing import get_time
+from wetb.utils.caching import cache_function, set_cache_property
+
+
+class Example(object):
+    def __init__(self, *args, **kwargs):
+        object.__init__(self, *args, **kwargs)
+
+        set_cache_property(self, "test", self.slow_function)
+        set_cache_property(self, 'pool', lambda : multiprocessing.Pool(20))
+
+    def slow_function(self):
+        time.sleep(1)
+        return 1
+
+    @cache_function
+    def test_cache_function(self):
+        return self.slow_function()
+
+    @get_time
+    def prop(self, prop):
+        return getattr(self, prop)
+
+
+
+
+def f(x):
+    return x ** 2
+
+class TestCacheProperty(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def testcache_property_test(self):
+        e = Example()
+        self.assertAlmostEqual(e.prop("test")[1], 1, 2)
+        self.assertAlmostEqual(e.prop("test")[1], 0, 2)
+
+    def testcache_property_pool(self):
+        e = Example()
+        print (e.prop("pool"))
+        self.assertAlmostEqual(e.prop("pool")[1], 0, places=4)
+        print (get_time(e.pool.map)(f, range(10)))
+
+
+    def test_cache_function(self):
+        e = Example()
+        self.assertAlmostEqual(get_time(e.test_cache_function)()[1], 1, places=2)
+        self.assertAlmostEqual(get_time(e.test_cache_function)()[1], 0, places=2)
+        self.assertAlmostEqual(get_time(e.test_cache_function)(reload=True)[1], 1, places=2)
+        self.assertAlmostEqual(get_time(e.test_cache_function)()[1], 0, places=2)
+        e.clear_cache()
+        self.assertAlmostEqual(get_time(e.test_cache_function)()[1], 1, places=2)
+
+
+
+
+
+if __name__ == "__main__":
+    #import sys;sys.argv = ['', 'Test.testName']
+    unittest.main()
diff --git a/wetb/utils/tests/test_geometry.py b/wetb/utils/tests/test_geometry.py
new file mode 100644
index 0000000..7c20104
--- /dev/null
+++ b/wetb/utils/tests/test_geometry.py
@@ -0,0 +1,113 @@
+'''
+Created on 15/01/2014
+
+@author: MMPE
+'''
+import unittest
+
+import wetb.gtsdf
+import numpy as np
+from wetb.utils.geometry import rad, deg, mean_deg, sind, cosd, std_deg, xyz2uvw, \
+    wsp_dir2uv, wsp_dir_tilt2uvw, tand
+
+
+class Test(unittest.TestCase):
+
+
+    def test_rad(self):
+        self.assertEqual(rad(45), np.pi / 4)
+        self.assertEqual(rad(135), np.pi * 3 / 4)
+
+
+    def test_deg(self):
+        self.assertEqual(45, deg(np.pi / 4))
+        self.assertEqual(135, deg(np.pi * 3 / 4))
+
+    def test_rad_deg(self):
+        for i in [15, 0.5, 355, 400]:
+            self.assertEqual(i, deg(rad(i)), i)
+
+    def test_sind(self):
+        self.assertAlmostEqual(sind(30), .5)
+
+    def test_cosd(self):
+        self.assertAlmostEqual(cosd(60), .5)
+
+    def test_tand(self):
+        self.assertAlmostEqual(tand(30), 0.5773, 3)
+
+
+    def test_mean_deg(self):
+        self.assertEqual(mean_deg(np.array([0, 90])), 45)
+        self.assertAlmostEqual(mean_deg(np.array([350, 10])), 0)
+
+
+    def test_mean_deg_array(self):
+        a = np.array([[0, 90], [350, 10], [0, -90]])
+        np.testing.assert_array_almost_equal(mean_deg(a, 1), [45, 0, -45])
+        np.testing.assert_array_almost_equal(mean_deg(a.T, 0), [45, 0, -45])
+
+
+    def test_std_deg(self):
+        self.assertEqual(std_deg(np.array([0, 0, 0])), 0)
+        self.assertAlmostEqual(std_deg(np.array([0, 90, 180, 270])), 57.296, 2)
+
+    def test_wspdir2uv(self):
+        u, v = wsp_dir2uv(np.array([1, 1, 1]), np.array([30, 0, 330]))
+        np.testing.assert_array_almost_equal(u, [0.8660, 1, 0.8660], 3)
+        np.testing.assert_array_almost_equal(v, [-0.5, 0, 0.5], 3)
+
+    def test_wspdir2uv_dir_ref(self):
+        u, v = wsp_dir2uv(np.array([1, 1, 1]), np.array([30, 0, 330]), 30)
+        np.testing.assert_array_almost_equal(u, [1, 0.8660, .5], 3)
+        np.testing.assert_array_almost_equal(v, [0, 0.5, .8660], 3)
+
+    def test_xyz2uvw(self):
+        u, v, w = xyz2uvw([1, 1, 0], [0, 1, 1], 0, left_handed=False)
+        np.testing.assert_almost_equal(u, [np.sqrt(1 / 2), np.sqrt(2), np.sqrt(1 / 2)])
+        np.testing.assert_almost_equal(v, [-np.sqrt(1 / 2), 0, np.sqrt(1 / 2)])
+
+
+        u, v, w = xyz2uvw([1, 1, 0], [0, 1, 1], 0, left_handed=True)
+        np.testing.assert_almost_equal(u, [np.sqrt(1 / 2), np.sqrt(2), np.sqrt(1 / 2)])
+        np.testing.assert_almost_equal(v, [np.sqrt(1 / 2), 0, -np.sqrt(1 / 2)])
+
+        u, v, w = xyz2uvw(np.array([-1, -1, -1]), np.array([-0.5, 0, .5]), np.array([0, 0, 0]), left_handed=False)
+        np.testing.assert_array_almost_equal(u, np.array([1, 1, 1]))
+        np.testing.assert_array_almost_equal(v, np.array([.5, 0, -.5]))
+        np.testing.assert_array_almost_equal(w, np.array([0, 0, 0]))
+
+        u, v, w = xyz2uvw(np.array([.5, cosd(30), 1]), np.array([sind(60), sind(30), 0]), np.array([0, 0, 0]), left_handed=False)
+        np.testing.assert_array_almost_equal(u, np.array([sind(60), 1, sind(60)]))
+        np.testing.assert_array_almost_equal(v, np.array([.5, 0, -.5]))
+        np.testing.assert_array_almost_equal(w, np.array([0, 0, 0]))
+
+        u, v, w = xyz2uvw(np.array([.5, cosd(30), 1]), np.array([0, 0, 0]), np.array([sind(60), sind(30), 0]), left_handed=False)
+        np.testing.assert_array_almost_equal(u, np.array([sind(60), 1, sind(60)]))
+        np.testing.assert_array_almost_equal(v, np.array([0, 0, 0]))
+        np.testing.assert_array_almost_equal(w, np.array([.5, 0, -.5]))
+
+
+    def test_wspdir2uv2(self):
+        time, data, info = wetb.gtsdf.load("test_files/SonicDataset.hdf5")
+        stat, x, y, z, temp, wsp, dir, tilt = data[2:3].T  #xyz is left handed
+        np.testing.assert_array_almost_equal(xyz2uvw(*wsp_dir2uv(wsp, dir), z=0), xyz2uvw(x, y, 0))
+
+    def test_wspdirtil2uvw(self):
+        time, data, info = wetb.gtsdf.load("test_files/SonicDataset.hdf5")
+        stat, x, y, z, temp, wsp, dir, tilt = data[3:6].T  #xyz is left handed
+        wsp = np.sqrt(wsp ** 2 + z ** 2)
+        np.testing.assert_array_almost_equal(xyz2uvw(*wsp_dir_tilt2uvw(wsp, dir, tilt, wsp_horizontal=False), left_handed=False), xyz2uvw(x, y, z))
+
+    def test_wspdirtil2uvw_horizontal_wsp(self):
+        time, data, info = wetb.gtsdf.load("test_files/SonicDataset.hdf5")
+        stat, x, y, z, temp, wsp, dir, tilt = data[:].T  #xyz is left handed
+        np.testing.assert_array_almost_equal(xyz2uvw(*wsp_dir_tilt2uvw(wsp, dir, tilt, wsp_horizontal=True), left_handed=False), xyz2uvw(x, y, z))
+
+        np.testing.assert_array_almost_equal(wsp_dir_tilt2uvw(wsp, dir, tilt, wsp_horizontal=True, dir_ref=180), np.array([x, -y, z]), 5)
+        np.testing.assert_array_almost_equal(xyz2uvw(*wsp_dir_tilt2uvw(wsp, dir, tilt, wsp_horizontal=True), left_handed=False), xyz2uvw(x, y, z))
+
+
+if __name__ == "__main__":
+    #import sys;sys.argv = ['', 'Test.test_rad']
+    unittest.main()
diff --git a/wetb/utils/timing.py b/wetb/utils/timing.py
new file mode 100644
index 0000000..f3aea9a
--- /dev/null
+++ b/wetb/utils/timing.py
@@ -0,0 +1,116 @@
+from six import exec_
+import time
+import inspect
+def get_time(f):
+    """Get time decorator
+    returns (return_values, time_of_execution)
+
+    >>> @get_time
+    >>> def test():
+    >>>    time.sleep(1)
+    >>>    return "end"
+    >>>
+    >>> test()
+    ('end', 0.999833492421551)
+    """
+    def wrap(*args, **kwargs):
+        t = time.clock()
+        res = f(*args, **kwargs)
+        return res, time.clock() - t
+    w = wrap
+    w.__name__ = f.__name__
+    return w
+
+
+def print_time(f):
+    """Print time decorator
+    prints name of method and time of execution
+
+    >>> @print_time
+    >>> def test():
+    >>>    time.sleep(1)
+    >>>
+    >>> test()
+    test            1.000s
+    """
+    def wrap(*args, **kwargs):
+        t = time.time()
+        res = f(*args, **kwargs)
+        print ("%-12s\t%.3fs" % (f.__name__, time.time() - t))
+        return res
+    w = wrap
+    w.__name__ = f.__name__
+    return w
+
+
+cum_time = {}
+def print_cum_time(f):
+    """Print cumulated time decorator
+    prints name of method and cumulated time of execution
+
+    >>> @print_cum_time
+    >>> def test():
+    >>>    time.sleep(1)
+    >>>
+    >>> test()
+    test            0001 calls, 1.000000s, 1.000000s pr. call'
+    >>> test()
+    test            0002 calls, 2.000000s, 1.000000s pr. call'
+    """
+    if f not in cum_time:
+        cum_time[f] = (0, 0)
+
+    def wrap(*args, **kwargs):
+        t = time.time()
+        res = f(*args, **kwargs)
+        ct = cum_time[f][1] + time.time() - t
+        n = cum_time[f][0] + 1
+        cum_time[f] = (n, ct)
+        print ("%-12s\t%.4d calls, %03fs, %fs pr. call'" % (f.__name__, n, ct, ct / n))
+        return res
+    w = wrap
+    w.__name__ = f.__name__
+    return w
+
+def print_line_time(f):
+    """Execute one line at the time and prints the time of execution.
+    Only for non-branching and non-looping code
+
+    prints: time_of_line, cumulated_time, code_line
+
+
+    >>> @print_line_time
+    >>> def test():
+    >>>    time.sleep(.3)
+    >>>    time.sleep(.5)
+    >>>
+    >>> test()
+    0.300s    0.300s    time.sleep(.3)
+    0.510s    0.810s    time.sleep(.51)
+
+    """
+    def wrap(*args, **kwargs):
+        arg_names, varargs, varkw, defaults = inspect.getargspec(f)
+        kwargs[varargs] = args[len(arg_names):]
+        kwargs[varkw] = {}
+        for k, v in kwargs.items():
+            if k not in tuple(arg_names) + (varargs, varkw):
+                kwargs.pop(k)
+                kwargs[varkw][k] = v
+        if defaults:
+            kwargs.update(dict(zip(arg_names[::-1], defaults[::-1])))
+        kwargs.update(dict(zip(arg_names, args)))
+
+
+        lines = inspect.getsourcelines(f)[0][2:]
+        tcum = time.clock()
+        locals = kwargs
+        gl = f.__globals__
+
+        for l in lines:
+            tline = time.clock()
+            exec(l.strip(), locals, gl)  #res = f(*args, **kwargs)
+            print ("%.3fs\t%.3fs\t%s" % (time.clock() - tline, time.clock() - tcum, l.strip()))
+    w = wrap
+    w.__name__ = f.__name__
+    return w
-- 
GitLab