From e5a653a37bd83d769f859d6ece8bb24fd921dff9 Mon Sep 17 00:00:00 2001
From: "Mads M. Pedersen" <mmpe@dtu.dk>
Date: Wed, 21 Dec 2016 11:11:02 +0100
Subject: [PATCH] New stuff

---
 wetb/utils/euler.py               | 115 ++++++++++++++
 wetb/utils/rotation.py            | 242 ++++++++++++++++++++++++++++++
 wetb/utils/tests/test_rotation.py | 126 ++++++++++++++++
 wetb/wind/air_density.py          | 221 +++++++++++++++++++++++++++
 wetb/wind/masks.py                |  13 ++
 wetb/wind/weibull.py              | 109 ++++++++++++++
 6 files changed, 826 insertions(+)
 create mode 100644 wetb/utils/euler.py
 create mode 100644 wetb/utils/rotation.py
 create mode 100644 wetb/utils/tests/test_rotation.py
 create mode 100644 wetb/wind/air_density.py
 create mode 100644 wetb/wind/masks.py
 create mode 100644 wetb/wind/weibull.py

diff --git a/wetb/utils/euler.py b/wetb/utils/euler.py
new file mode 100644
index 0000000..6c2f924
--- /dev/null
+++ b/wetb/utils/euler.py
@@ -0,0 +1,115 @@
+'''
+Created on 15/01/2014
+
+@author: MMPE
+'''
+
+import numpy as np
+from wetb.utils.geometry import deg
+
+
+def Ax(angle):
+    cos = np.cos(angle)
+    sin = np.sin(angle)
+    return np.array([[1, 0, 0],
+                   [0, cos, -sin],
+                   [0, sin, cos]])
+
+def Ay(angle):
+    cos = np.cos(angle)
+    sin = np.sin(angle)
+    return np.array([[cos, 0, sin],
+                   [0, 1, 0       ],
+                   [-sin, 0, cos  ]])
+
+def Az(angle):
+    cos = np.cos(angle)
+    sin = np.sin(angle)
+    return np.array([[cos, -sin, 0],
+                   [sin, cos, 0],
+                   [0, 0, 1]])
+
+
+
+
+def euler2A(euler_param):
+    assert len(euler_param) == 4
+    e = euler_param
+    return np.array([[e[0] ** 2 + e[1] ** 2 - e[2] ** 2 - e[3] ** 2, 2 * (e[1] * e[2] + e[0] * e[3]) , 2 * (e[1] * e[3] - e[0] * e[2]) ],
+                         [2 * (e[1] * e[2] - e[0] * e[3]), e[0] ** 2 - e[1] ** 2 + e[2] ** 2 - e[3] ** 2, 2 * (e[2] * e[3] + e[0] * e[1]) ],
+                         [2 * (e[1] * e[3] + e[0] * e[2]), 2 * (e[2] * e[3] - e[0] * e[1]), e[0] ** 2 - e[1] ** 2 - e[2] ** 2 + e[3] ** 2]]).T
+
+def A2euler(A):
+    # method from http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+    sqrt = np.sqrt
+    (m00, m01, m02), (m10, m11, m12), (m20, m21, m22) = A
+    tr = m00 + m11 + m22
+
+    if (tr > 0):
+        S = sqrt(tr + 1.0) * 2  # // S=4*qw
+        qw = 0.25 * S
+        qx = (m21 - m12) / S
+        qy = (m02 - m20) / S
+        qz = (m10 - m01) / S
+    elif ((m00 > m11) and (m00 > m22)):
+        S = sqrt(1.0 + m00 - m11 - m22) * 2  # // S=4*qx
+        qw = (m21 - m12) / S
+        qx = 0.25 * S
+        qy = (m01 + m10) / S
+        qz = (m02 + m20) / S
+    elif (m11 > m22):
+        S = sqrt(1.0 + m11 - m00 - m22) * 2  # // S=4*qy
+        qw = (m02 - m20) / S
+        qx = (m01 + m10) / S
+        qy = 0.25 * S
+        qz = (m12 + m21) / S
+    else:
+        S = sqrt(1.0 + m22 - m00 - m11) * 2  # // S=4*qz
+        qw = (m10 - m01) / S
+        qx = (m02 + m20) / S
+        qy = (m12 + m21) / S
+        qz = 0.25 * S
+    return np.array([qw, qx, qy, qz])
+
+
+
+#def A2xyz(A):
+#    if abs(A[2, 0]) != 1:
+#        y = -np.arcsin(A[2, 0])
+#        x = np.arctan2(A[2, 1] / np.cos(y), A[2, 2] / np.cos(y))
+#        z = np.arctan2(A[1, 0] / np.cos(y), A[0, 0] / np.cos(y))
+#    else:
+#        z = 0
+#        if A[2, 0] == -1:
+#            y = np.pi / 2
+#            x = z + np.arctan(A[0, 1], A[0, 2])
+#        else:
+#            y = -np.pi / 2
+#            x = -z + np.arctan(-A[0, 1], -A[0, 2])
+#    return np.array([x, y, z])
+#
+#def zxz2euler(z1, x, z2):
+#    return np.array([np.cos(.5 * (z1 + z2)) * np.cos(.5 * x),
+#                     np.cos(.5 * (z1 - z2)) * np.sin(.5 * x),
+#                     np.sin(.5 * (z1 - z2)) * np.sin(.5 * x),
+#                     np.sin(.5 * (z1 + z2)) * np.cos(.5 * x)])
+#
+#def xyz2A(x, y, z):
+#    return np.dot(Ax(x), np.dot(Ay(y), Az(z)))
+
+#def euler2xyz(euler):
+#    return A2xyz(euler2A(euler))
+
+#def A2euler(A):
+#    return xyz2euler(*A2xyz(A))
+
+def euler2angle(euler):
+    if euler[0] > 1:
+        euler[0] = 1
+    if euler[0] < -1:
+        euler[0] = -1
+
+    return np.arccos(euler[0]) * 2
+
+def euler2gl(euler):
+    return np.r_[deg(euler2angle(euler)), euler[1:]]
diff --git a/wetb/utils/rotation.py b/wetb/utils/rotation.py
new file mode 100644
index 0000000..2254630
--- /dev/null
+++ b/wetb/utils/rotation.py
@@ -0,0 +1,242 @@
+'''
+Created on 21/07/2014
+
+@author: mmpe
+'''
+import numpy as np
+
+
+def transformation_matrix(angles, xyz):
+    """Create Transformation matrix(es)
+    !!!Note that the columns of the returned matrix(es) is original(unrotate) xyz-axes in rotated coordinates\n
+    !!!Multiplying a this matrix by a vector rotates the vector -angle radians (in right handed terminology).
+
+    Parameters
+    ----------
+    angles : int, float or array_like
+        Angle(s) for rotation matrix(es). \n
+        One rotation matrix will be returned for each angle
+    xyz : {0,1,2}
+        - 0: rotation around x(first) axis\n
+        - 1: rotation around y(second) axis\n
+        - 2: rotation around z(third) axis\n
+
+    Returns
+    -------
+    transformation_matrix : array_like, shape = (no_angles, 3,3)
+        Rotation matrix(es)
+    """
+    indexes = [(0, 4, 8, 5, 7), (4, 0, 8, 6, 2), (8, 0, 4, 1, 3)]  # 1, cos,cos,sin,-sin for x,y and z rotation matrix
+    if isinstance(angles, (int, float)):
+        n = 1
+    else:
+        n = len(angles)
+    m = np.zeros(n * 9, np.float)
+    cosx = np.cos(angles)
+    sinx = np.sin(angles)
+    m[indexes[xyz][0]::9] = 1
+    m[indexes[xyz][1]::9] = cosx
+    m[indexes[xyz][2]::9] = cosx
+    m[indexes[xyz][3]::9] = sinx
+    m[indexes[xyz][4]::9] = -sinx
+    return m.reshape(n, 3, 3)
+
+def rotmat(angles, xyz):
+    """Create rotation matrix(es)
+    !!!Note that the columns of the returned matrix(es) is rotated xyz-axes in original(unrotated) coordinates\n
+    Multiplying a this matrix by a vector rotates the vector +angle radians (in right handed terminology).
+    Parameters
+    ----------
+    angles : int, float or array_like
+        Angle(s) for rotation matrix(es). \n
+        Note that
+        One rotation matrix will be returned for each angle
+    xyz : {0,1,2}
+        - 0: rotation around x(first) axis\n
+        - 1: rotation around y(second) axis\n
+        - 2: rotation around z(third) axis\n
+
+    Returns
+    -------
+    transformation_matrix : array_like, shape = (no_angles, 3,3)
+        Rotation matrix(es)
+    """
+    indexes = [(0, 4, 8, 5, 7), (4, 0, 8, 6, 2), (8, 0, 4, 1, 3)]  # 1, cos,cos,sin,-sin for x,y and z rotation matrix
+    if isinstance(angles, (int, float)):
+        n = 1
+    else:
+        n = len(angles)
+    m = np.zeros(n * 9, np.float)
+    cosx = np.cos(angles)
+    sinx = np.sin(angles)
+    m[indexes[xyz][0]::9] = 1
+    m[indexes[xyz][1]::9] = cosx
+    m[indexes[xyz][2]::9] = cosx
+    m[indexes[xyz][3]::9] = -sinx
+    m[indexes[xyz][4]::9] = sinx
+    return m.reshape(n, 3, 3)
+
+def mdot(m1, m2):
+    """Multiplication of matrix pairs
+
+    Parameters
+    ----------
+    m1 : array_like shape = (i, n, m)
+        First matrix set, i.e. i matrixes, size n x m\n
+        i must be equal to j or 1
+    m2 : array_like, shape = (j,m,p)
+        Second matrix set, i.e. j matrixes, size m x p\n
+        j must be equal to i or 1
+
+    Returns
+    -------
+    mdot : array_like, shape(max(i,j),n,p)
+        Matrix products
+    """
+    if m1.shape[0] == 1 and m2.shape[0] == 1:
+        return np.array([np.dot(m1[0], m2[0])])
+    elif m1.shape[0] > 1 and m2.shape[0] == 1:
+        mprod = np.empty_like(m1)
+        for i in range(m1.shape[0]):
+            mprod[i, :] = np.dot(m1[i, :], m2[0])
+    elif m1.shape[0] == 1 and m2.shape[0] > 1:
+        mprod = np.empty_like(m2)
+        for i in range(m2.shape[0]):
+            mprod[i, :] = np.dot(m1[0], m2[i, :])
+    elif m1.shape[0] > 1 and m2.shape[0] > 1 and m1.shape[0] == m2.shape[0]:
+        mprod = np.empty_like(m1)
+        for i in range(m1.shape[0]):
+            mprod[i, :] = np.dot(m1[i, :], m2[i, :])
+    else:
+        raise Exception("m1 and m2 must have same first dimension or one of the matrices must have first dimension 1")
+    return mprod
+
+
+def dots(rotmats, v):
+    """Rotate vector(s) v by rotation matrix(es)
+
+    Parameters
+    ----------
+    rotmats : array_like, shape=(i,3,3) or list of array_like, shape=(i,3,3)
+        if list of array_like, rotation matrixes are recursively reduced by multiplication\n
+        i must be 1 or equal to j
+    vector : array_like, shape=(j,3)
+        vectors to rotate
+
+    Returns
+    -------
+    dots : array_like, shape=(j,3)
+    """
+    if isinstance(rotmats, list):
+        if len(rotmats) > 1:
+            rotmats_red = [mdot(rotmats[0], rotmats[1])]
+            rotmats_red.extend(rotmats[2:])
+            return dots(rotmats_red, v)
+        else:
+            m = rotmats[0]
+    else:
+        m = rotmats
+    if len(v.shape) == 1:
+        v = np.array([v]).T
+
+    if m.shape[0] == 1:
+        return np.dot(m[0], v)
+    else:
+        if m.shape[0] != v.shape[1]:
+            raise Exception("m must have same dimension as v has number of columns")
+        res = np.zeros_like(v, dtype=np.float)
+        for i in range(v.shape[1]):
+            res[:, i] = np.dot(m[i], v[:, i])
+        return res
+
+
+def rotate(rotmats, v):
+    global x, y, z
+    v = np.array(v)
+    if isinstance(rotmats, (list, tuple)):
+        if len(rotmats) > 1:
+            rotmats_red = [mdot(rotmats[0], rotmats[1])]
+            rotmats_red.extend(rotmats[2:])
+            return rotate(rotmats_red, v)
+        else:
+            rotmats = rotmats[0]
+    assert rotmats.shape[-1] == rotmats.shape[-2] == v.shape[-1] == 3, "rotmats is %d x %x and v is i x %d, but must be 3" % (rotmats.shape[-1], rotmats.shape[-2], v.shape[-1])
+
+#    if isinstance(v, tuple):
+#        v = np.array([v])
+    n = (1, v.shape[0])[len(v.shape) == 2]
+
+    if rotmats.shape[0] == 1:
+        return np.dot(rotmats[0], v.T).T
+    else:
+        if v.shape[0] != rotmats.shape[0]:
+            raise Exception("V and rotmats must have same first dimension")
+        v = v.T
+        v_rot = np.zeros_like(v, dtype=np.float)
+        for i in range(n):
+            v_rot[:, i] = np.dot(rotmats[i], v[:, i])
+        return v_rot.T
+
+def rotate_x(v, angle):
+    """Rotate vector(s) around x axis
+
+    Parameters
+    ---------
+    v : array_like, shape=(3,) or shape=(1-N,3)
+        Vector(s) to rotate
+    angle : int, float or array_like shape=(1,) or shape=(N,)
+        Angle(s) [rad] to rotate
+
+    Returns
+    -------
+    y : array_like
+        Rotated vector(s)
+    """
+    return _rotate(v, angle, lambda x, y, z, cos, sin : [x, cos * y - sin * z, sin * y + cos * z])
+
+def rotate_y(v, angle):
+    """Rotate vector(s) around y axis
+
+    Parameters
+    ---------
+    v : array_like, shape=(3,) or shape=(1-N,3)
+        Vector(s) to rotate
+    angle : int, float or array_like shape=(1,) or shape=(N,)
+        Angle(s) [rad] to rotate
+
+    Returns
+    -------
+    y : array_like
+        Rotated vector(s)
+    """
+    return _rotate(v, angle, lambda x, y, z, cos, sin : [cos * x + sin * z, y, -sin * x + cos * z])
+
+def rotate_z(v, angle):
+    """Rotate vector(s) around z axis
+
+    Parameters
+    ---------
+    v : array_like, shape=(3,) or shape=(1-N,3)
+        Vector(s) to rotate
+    angle : int, float or array_like shape=(1,) or shape=(N,)
+        Angle(s) [rad] to rotate
+
+    Returns
+    -------
+    y : array_like
+        Rotated vector(s)
+    """
+    return _rotate(v, angle, lambda x, y, z, cos, sin : [cos * x - sin * y, sin * x + cos * y, z])
+
+def _rotate(v, angle, rotfunc):
+    angle = np.atleast_1d(angle)
+    cos, sin = np.cos(angle), np.sin(angle)
+    v = np.array(v)
+    if len(v.shape) == 1:
+        assert angle.shape[0] == 1
+        assert v.shape[0] == 3
+        return np.array(rotfunc(v[0], v[1], v[2], cos, sin), dtype=np.float).T
+    else:
+        assert angle.shape[0] == 1 or angle.shape[0] == v.shape[0]
+        assert v.shape[1] == 3
+        return np.array(rotfunc(v[:, 0], v[:, 1], v[:, 2], cos, sin), dtype=np.float).T
diff --git a/wetb/utils/tests/test_rotation.py b/wetb/utils/tests/test_rotation.py
new file mode 100644
index 0000000..07f21bc
--- /dev/null
+++ b/wetb/utils/tests/test_rotation.py
@@ -0,0 +1,126 @@
+'''
+Created on 15/01/2014
+
+@author: MMPE
+'''
+import unittest
+
+import numpy as np
+from wetb.utils.geometry import rad
+from wetb.utils.rotation import transformation_matrix, mdot, dots, rotate, rotate_x, \
+    rotmat, rotate_y, rotate_z
+
+x, y, z = 0, 1, 2
+class Test(unittest.TestCase):
+
+
+    def test_transformation_matrix(self):
+        np.testing.assert_array_almost_equal(transformation_matrix(rad(30), x), [[[ 1., 0. , 0.],
+                                                                                  [ 0., 0.8660254, 0.5],
+                                                                                  [ 0., -0.5 , 0.8660254]]])
+        np.testing.assert_array_almost_equal(transformation_matrix(rad(30), y), [[[ 0.8660254, 0. , -0.5],
+                                                                     [ 0., 1., 0 ],
+                                                                     [ 0.5, 0        , 0.8660254]]])
+        np.testing.assert_array_almost_equal(transformation_matrix(rad(30), z), [[[ 0.8660254, 0.5 , 0.],
+                                                                                  [ -0.5, 0.8660254, 0],
+                                                                                  [ 0., 0 , 1]]])
+
+    def test_rotation_matrix(self):
+        np.testing.assert_array_almost_equal(rotmat(rad(30), x), [[[ 1., 0. , 0.],
+                                                                   [ 0., 0.8660254, -0.5],
+                                                                   [ 0., 0.5 , 0.8660254]]])
+        np.testing.assert_array_almost_equal(rotmat(rad(30), y), [[[ 0.8660254, 0. , 0.5],
+                                                                   [ 0., 1., 0 ],
+                                                                   [ -0.5, 0        , 0.8660254]]])
+        np.testing.assert_array_almost_equal(rotmat(rad(30), z), [[[ 0.8660254, -0.5 , 0.],
+                                                                   [ 0.5, 0.8660254, 0],
+                                                                   [ 0., 0 , 1]]])
+    def test_rotation_matrixes(self):
+        np.testing.assert_array_almost_equal(rotmat([rad(30), rad(60)], 0), [[[ 1., 0. , 0.],
+                                                                     [ 0., 0.8660254, -0.5],
+                                                                     [ 0., 0.5 , 0.8660254]],
+                                                                             [[ 1., 0. , 0.],
+                                                                     [ 0., 0.5, -0.8660254, ],
+                                                                     [ 0., +0.8660254, 0.5 ]]])
+
+    def test_mdot(self):
+        x, y, z = 0, 1, 2
+        m1 = transformation_matrix(np.pi, x)
+        m2 = transformation_matrix([np.pi, np.pi / 2], y)
+
+        np.testing.assert_array_almost_equal(m1, [[[1, 0, 0], [0, -1, 0], [0, 0, -1]]])
+        np.testing.assert_array_almost_equal(mdot(m1, m1), [[[1, 0, 0], [0, 1, 0], [0, 0, 1]]])
+        np.testing.assert_array_almost_equal(mdot(m1, m2), [[[-1, 0, 0], [0, -1, 0], [0, 0, 1]], [[0, 0, -1], [0, -1, 0], [-1, 0, 0]]])
+        np.testing.assert_array_almost_equal(mdot(m2, m2), [[[1, 0, 0], [0, 1, 0], [0, 0, 1]], [[-1, 0, 0], [0, 1, 0], [0, 0, -1]]])
+        np.testing.assert_array_almost_equal(mdot(m2, m1), [[[-1, 0, 0], [0, -1, 0], [0, 0, 1]], [[0, 0, 1], [0, -1, 0], [1, 0, 0]]])
+
+
+    def test_dots(self):
+        x, y, z = 0, 1, 2
+        v1 = np.array([1, 2, 3])
+        v2 = np.array([1, 2, 4])
+
+        np.testing.assert_array_almost_equal(dots(transformation_matrix(-np.pi / 2, x), v1).T, [[1, -3, 2]])
+        np.testing.assert_array_almost_equal(dots(transformation_matrix(-np.pi , x), v1).T, [[1, -2, -3]])
+
+        np.testing.assert_array_almost_equal(dots(transformation_matrix(-np.pi / 2, y), v1).T, [[3, 2, -1]])
+        np.testing.assert_array_almost_equal(dots(transformation_matrix(-np.pi , y), v1).T, [[-1, 2, -3]])
+
+        np.testing.assert_array_almost_equal(dots(transformation_matrix(-np.pi / 2, z), v1).T, [[-2, 1, 3]])
+        np.testing.assert_array_almost_equal(dots(transformation_matrix(-np.pi , z), v1).T, [[-1, -2, 3]])
+
+        v = np.array([v1, v2]).T
+
+        np.testing.assert_array_almost_equal(dots(transformation_matrix([-np.pi / 2, np.pi], x), v).T, [[1, -3, 2], [1, -2, -4]])
+        np.testing.assert_array_almost_equal(dots(transformation_matrix([-np.pi / 2, np.pi], y), v).T, [[3, 2, -1], [-1, 2, -4]])
+        np.testing.assert_array_almost_equal(dots(transformation_matrix([-np.pi / 2, np.pi], z), v).T, [[-2, 1, 3], [-1, -2, 4]])
+        np.testing.assert_array_almost_equal(dots([transformation_matrix(np.pi / 2, z), transformation_matrix(np.pi / 2, y), transformation_matrix(np.pi / 2, x)], v1).T, [[3, -2, 1]])
+
+
+    def test_rotate(self):
+        x, y, z = 0, 1, 2
+        v = np.array([1, 2, 3])
+
+        np.testing.assert_array_almost_equal(rotate(rotmat(np.pi / 2, x), v), [1, -3, 2])
+        np.testing.assert_array_almost_equal(rotate(rotmat(np.pi, x), v), [1, -2, -3])
+
+        np.testing.assert_array_almost_equal(rotate(rotmat(np.pi / 2, y), v), [3, 2, -1])
+        np.testing.assert_array_almost_equal(rotate(rotmat(np.pi, y), v), [-1, 2, -3])
+
+        np.testing.assert_array_almost_equal(rotate(rotmat(np.pi / 2, z), v), [-2, 1, 3])
+        np.testing.assert_array_almost_equal(rotate(rotmat(np.pi, z), v), [-1, -2, 3])
+
+        v = np.array([[1, 2, 3], [1, 2, 4]])
+
+        np.testing.assert_array_almost_equal(rotate(rotmat([np.pi / 2, -np.pi], x), v)[0], [1, -3, 2])
+        np.testing.assert_array_almost_equal(rotate(rotmat([np.pi / 2, -np.pi], x), v)[1], [1, -2, -4])
+
+        np.testing.assert_array_almost_equal(rotate(rotmat([np.pi / 2, np.pi], y), v)[0], [3, 2, -1])
+        np.testing.assert_array_almost_equal(rotate(rotmat([np.pi / 2, np.pi], y), v)[1], [-1, 2, -4])
+
+        np.testing.assert_array_almost_equal(rotate(rotmat([np.pi / 2, np.pi], z), v)[0], [-2, 1, 3])
+        np.testing.assert_array_almost_equal(rotate(rotmat([np.pi / 2, np.pi], z), v)[1], [-1, -2, 4])
+
+
+    def test_rotate_xyz(self):
+        x, y, z = 0, 1, 2
+        v = np.array([1, 2, 3])
+
+        np.testing.assert_array_almost_equal(rotate_x(v, np.pi / 2), [1, -3, 2])
+        np.testing.assert_array_almost_equal(rotate_x(v, -np.pi), [1, -2, -3])
+
+        np.testing.assert_array_almost_equal(rotate_y(v, np.pi / 2), [3, 2, -1])
+        np.testing.assert_array_almost_equal(rotate_y(v, np.pi), [-1, 2, -3])
+
+        np.testing.assert_array_almost_equal(rotate_z(v, np.pi / 2), [-2, 1, 3])
+        np.testing.assert_array_almost_equal(rotate_z(v, np.pi), [-1, -2, 3])
+
+        v = np.array([[1, 2, 3], [4, 5, 6]])
+        np.testing.assert_array_almost_equal(rotate_z(v, np.pi / 2), [[-2, 1, 3], [-5, 4, 6]])
+        np.testing.assert_array_almost_equal(rotate_z(v, [np.pi / 2]), [[-2, 1, 3], [-5, 4, 6]])
+        np.testing.assert_array_almost_equal(rotate_z(v, [-np.pi / 2, np.pi]), [[2, -1, 3], [-4, -5, 6]])
+
+
+if __name__ == "__main__":
+    #import sys;sys.argv = ['', 'Test.test_rad']
+    unittest.main()
diff --git a/wetb/wind/air_density.py b/wetb/wind/air_density.py
new file mode 100644
index 0000000..0af9724
--- /dev/null
+++ b/wetb/wind/air_density.py
@@ -0,0 +1,221 @@
+'''
+Created on 08/02/2016
+
+@author: mmpe
+'''
+
+import numpy as np
+
+
+
+
+def saturated_vapor_pressure(T):
+    """Calculate pressure of saturated water vapor at specified temperature as described at
+    http://wahiduddin.net/calc/density_altitude.htm
+    Parameters
+    ---------
+    t : float
+        Temperature [C]
+
+    Returns
+    -------
+    float
+        Pressure [mb]
+    """
+
+    eso = 6.1078
+    c0 = 0.99999683
+    c1 = -0.90826951 * 10 ** -2
+    c2 = 0.78736169 * 10 ** -4
+    c3 = -0.61117958 * 10 ** -6
+    c4 = 0.43884187 * 10 ** -8
+    c5 = -0.29883885 * 10 ** -10
+    c6 = 0.21874425 * 10 ** -12
+    c7 = -0.17892321 * 10 ** -14
+    c8 = 0.11112018 * 10 ** -16
+    c9 = -0.30994571 * 10 ** -19
+    p = (c0 + T * (c1 + T * (c2 + T * (c3 + T * (c4 + T * (c5 + T * (c6 + T * (c7 + T * (c8 + T * (c9))))))))))
+    return eso / p ** 8
+
+def saturated_vapor_pressure2(t):
+    """Calculate pressure of saturated water vapor at specified temperature as described at
+    http://wahiduddin.net/calc/density_altitude.htm
+    Parameters
+    ---------
+    t : float
+        Temperature [C]
+
+    Returns
+    -------
+    float
+        Pressure [mb]
+    """
+    c0 = 6.1078
+    c1 = 7.5
+    c2 = 237.3
+    return c0 * 10 ** ((c1 * t) / (c2 + t))
+
+def saturated_vapor_pressure3(t):
+    """Calculate pressure of saturated water vapor at specified temperature according to
+
+    The IAPWS Formulation 1995 for the Thermodynamic Properties of Ordinary Water Substance for General and Scientific Use
+    W. Wagner and A. Pruss
+    J. Phys. Chem. Ref. Data 31, 387 (2002); http://dx.doi.org/10.1063/1.1461829
+
+    Parameters
+    ---------
+    t : float
+        Temperature [C]
+
+    Returns
+    -------
+    float
+        Pressure [mb]
+    """
+
+    T = t + 273.15
+    Tc = 647.096
+    Pc = 220640
+    v = 1 - T / Tc
+    C1 = -7.85951783
+    C2 = 1.84408259
+    C3 = -11.7866497
+    C4 = 22.6807411
+    C5 = -15.9618719
+    C6 = 1.80122502
+    return Pc * np.exp(Tc / T * (C1 * v + C2 * v ** 1.5 + C3 * v ** 3 + C4 * v ** 3.5 + C5 * v ** 4 + C6 * v ** 7.5))
+
+def saturated_vapor_pressure4(t):
+
+    """Calculate the saturated vapor pressure as described in
+    http://www.vaisala.com/Vaisala%20Documents/Application%20notes/Humidity_Conversion_Formulas_B210973EN-F.pdf
+
+    Parameters
+    ---------
+    t : float
+        Temperature [C]
+
+    Returns
+    -------
+    Saturated vapor pressure [mBar]
+    """
+    A = 6.116441
+    m = 7.591386
+    Tn = 240.7263
+    return A * 10 ** ((m * t) / (t + Tn))
+
+def saturated_vapor_pressure_IEC(t):
+    """Calculate the saturated vapor pressure according to IEC 61400-12-1
+
+    Parameters
+    ---------
+    t : float
+        Temperature [C]
+
+    Returns
+    -------
+    Saturated vapor pressure [mBar]
+    """
+
+    T = t + 273.15
+    return 0.000000205 * np.exp(0.0631846 * T)
+
+
+
+def drew_point(t, RH):
+    A = 6.116441
+    m = 7.591386
+    Tn = 240.7263
+    Pw = saturated_vapor_pressure4(t) * RH / 100
+    return Tn / (m / np.log10(Pw / A) - 1)
+
+def air_density(P, t, rh=0, saturated_vapor_pressure_function=saturated_vapor_pressure):
+    """Calculate the density of atmospheric air at specified pressure, temperature and humidity
+    source: http://wahiduddin.net/calc/density_altitude.htm
+    Equivalent to formulation in IEC61400-12-1 if used with the saturated_vapor_pressure_IEC function
+
+
+    Parameters
+    ---------
+    P : float
+        Atmospheric pressure [mb]=[hPa]
+    t : float
+        Temperature [C]
+    rh : float
+        Relative humidity [%]
+    saturated_vapor_pressure_function : function
+        Function, f(t)->P, that takes the temperature in celcius as input and
+        returns the saturated vapor pressure in mbar
+
+    Returns
+    -------
+    float
+        Density of atmospheric air
+    """
+    Pv = saturated_vapor_pressure_function(t) * rh / 100
+    Pd = P - Pv
+    Rv = 461.4964
+    Rd = 287.0531
+    Tk = t + 273.15
+    return (Pd * 100 / (Rd * Tk)) + (Pv * 100 / (Rv * Tk))
+
+def R(rh=0, t=15, P=1014):
+    """Specific gas constant ~287.058 J/(kg K) for dry air
+    
+    Parameters
+    ---------
+    rh : float
+        Relative humidity [%]
+    t : float
+        Temperature [C]
+    P : float
+        pressure [hPa]
+    
+    Returns
+    -------
+    Specific gas constant
+    """
+    assert np.all((900<P)&(P<1100)), "Pressure outside range 900 to 1100"
+    assert np.all((-50<t)&(t<100)), "Temperature outside range -50 to 100"
+    Tk = t + 273.15
+    return P * 100 / (air_density(P, t, rh) * Tk)
+
+if __name__ == "__main__":
+    pass
+    
+#     #print (air_density(1013, 65, 50))
+#     #print (drew_point(40, 50))
+#     import matplotlib.pyplot as plt
+#     if 0:
+#         t = np.arange(-20, 50)
+#         plt.plot(t, saturated_vapor_pressure(t))
+#         plt.plot(t, saturated_vapor_pressure2(t), "--")
+#         plt.plot(t, saturated_vapor_pressure3(t), ":")
+#         plt.show()
+# 
+#     if 0:
+#         t = np.arange(5, 25)
+#         plt.xlabel("Temperature [C]")
+#         plt.ylabel("Density [kg/m3]")
+#         plt.plot(t, air_density(990, t, 50), label='P: 990 hPa')
+#         plt.plot(t, air_density(1018, t, 50), label='P: 1018 hPa')
+#         plt.legend()
+#         plt.show()
+# 
+#     p = 1013
+#     t = 20
+#     rh = 70
+#     print (R(p, t, rh) * (t + 273.15))
+#     print ((p * 100) / air_density(p, t, rh))
+#     
+#     print (R(p,10,80))
+#     print (R(p,100,80))
+# 
+#     if 0:
+#         rh = np.arange(0, 100)
+#         plt.xlabel("Rh [%]")
+#         plt.ylabel("R")
+#         plt.plot(rh, R(990, 20, rh), label='P: 990 hPa, 20 t')
+# 
+#         plt.legend()
+#         plt.show()
diff --git a/wetb/wind/masks.py b/wetb/wind/masks.py
new file mode 100644
index 0000000..a0c8bec
--- /dev/null
+++ b/wetb/wind/masks.py
@@ -0,0 +1,13 @@
+'''
+Created on 06/11/2015
+
+@author: MMPE
+'''
+
+
+def wsp_mask(x, wsp, pm_tolerance):
+    return (wsp - pm_tolerance <= x) & (x < wsp + pm_tolerance)
+
+def wdir_mask(x, wdir, pm_tolerance):
+    return ((x - wdir + pm_tolerance) % 360 >= 0) & ((x - wdir + pm_tolerance) % 360 < 2 * pm_tolerance)
+
diff --git a/wetb/wind/weibull.py b/wetb/wind/weibull.py
new file mode 100644
index 0000000..43ef018
--- /dev/null
+++ b/wetb/wind/weibull.py
@@ -0,0 +1,109 @@
+"""
+Contents
+--------
+- `fit <#wetb.wind.weibull.fit>`_: Fit a weibull distribution, in terms of the parameters k and A, to the provided wind speeds
+- `pdf <#wetb.wind.weibull.pdf>`_: Create Weibull pdf function
+- `random <#wetb.wind.weibull.random>`_: Create a list of n random Weibull distributed values
+"""
+import numpy as np
+import math
+gamma = math.gamma
+def pdf(A, k):
+    """Create Weibull pdf function
+
+    Parameters
+    ----------
+    A : float
+        Scale parameter
+    k : float
+        Shape parameter
+
+    Returns
+    -------
+    pdf-function
+
+    Examples
+    --------
+    >>> from wetb.wind import weibull
+    >>> pdf_func = weibull.pdf(3,4)
+    >>> pdf_func(5)
+    0.131007116969
+    >>> from pylab import arange, plot, show
+    >>> wsp = arange(20)
+    >>> plot(wsp, pdf_func(wsp))
+    >>> show()
+
+    """
+
+    return lambda x: k * x ** (k - 1) / A ** k * np.exp(-(x / A) ** k)
+
+def cdf(A,k):
+    return lambda x: 1-np.exp(-(x/A)**k)
+
+def random(A, k, n):
+    """Create a list of n random Weibull distributed values
+
+    Parameters
+    ----------
+    A : float
+        Scale parameter
+    k : float
+        Shape parameter
+    n : int
+        Number of values
+
+    Returns
+    -------
+    x : array_like, shape (n,)
+        n random Weibull distributed values
+
+    Examples
+    --------
+    >>> from wetb.wind import weibull
+    >>> from pylab import hist, show
+    >>> hist(weibull.random(4,2,1000), 20)
+    >>> show()
+    """
+    return A * np.random.weibull(k, n)
+
+def fit(wsp):
+    """Fit a weibull distribution, in terms of the parameters k and A, to the provided wind speeds
+
+    Parameters
+    ----------
+    wsp : array_like
+        Wind speeds
+
+    Returns
+    -------
+    A : float
+        Scale parameter
+    k : float
+        Shape parameter
+
+    Examples
+    --------
+    >>> from wetb.wind import weibull
+    >>> A,k = weibull.fit(wsp_lst)
+    """
+    res_pr_ms = 2  # number of wind speed bins pr m/s
+
+    pdf, x = np.histogram(wsp, bins=np.arange(0, np.ceil(np.nanmax(wsp)), 1 / res_pr_ms))
+    x = (x[1:] + x[:-1]) / 2
+    N = np.sum(~np.isnan(wsp))
+    pdf = pdf / N * res_pr_ms
+
+    m = lambda n : np.sum(pdf * x ** n / res_pr_ms)
+    from scipy.optimize import newton
+    func = lambda k : gamma(1 / k + 1) ** 2 / gamma(2 / k + 1) - m(1) ** 2 / m(2)
+    k = newton(func, 1)
+    A = m(1) / gamma(1 / k + 1)
+    return A, k
+
+if __name__ == "__main__":
+    from wetb.wind import weibull
+    from pylab import hist, show, plot
+    hist(weibull.random(10, 2, 10000), 20, normed=True)
+    wsp = np.arange(0, 20, .5)
+    plot(wsp, weibull.pdf(10, 2)(wsp))
+    show()
-- 
GitLab