diff --git a/tests/run_pytest.py b/tests/run_pytest.py
index a39f5c56dd1c9e0494eca31aedab907ffc1998ef..909b146579ce85b29b566579c88a81a43d656c10 100644
--- a/tests/run_pytest.py
+++ b/tests/run_pytest.py
@@ -5,4 +5,5 @@ Created on 29/01/2016
 '''
 import pytest
 import os
-pytest.main(os.path.dirname(__file__) + "./../")
+p = os.path.abspath(os.path.join(os.path.dirname(__file__) + "./../")).replace("\\", "/")
+pytest.main(p)
diff --git a/wetb/dlc/high_level.py b/wetb/dlc/high_level.py
index 26b976f7fa6b2fddcf3fb3fd9995d646669e158f..a67327e4dc591d43a5372aee4c5cd96603afdfe9 100644
--- a/wetb/dlc/high_level.py
+++ b/wetb/dlc/high_level.py
@@ -125,7 +125,7 @@ class DLCHighLevel(object):
 
     def sensor_info(self, sensors=[]):
         if sensors != []:
-            return self.sensor_df[functools.reduce(np.logical_or, [((self.sensor_df.get(f, np.array([""] * len(self.sensor_df.name))).values != "") | (self.sensor_df.name == f)) for f in np.atleast_1d(sensors)])]
+            return self.sensor_df[functools.reduce(np.logical_or, [((self.sensor_df.get(f, pd.DataFrame([""] * len(self.sensor_df.name))[0]).values != "") | (self.sensor_df.name == f)) for f in np.atleast_1d(sensors)])]
         else:
             return self.sensor_df
 
@@ -149,7 +149,10 @@ class DLCHighLevel(object):
             start, step, stop = [float(eval(v, globals(), self.__dict__)) for v in values.lower().split(":")]
             values = np.arange(start, stop + step, step)
         else:
-            values = [(eval(v, globals(), self.__dict__)) for v in str(values).lower().replace("/", ",").split(",")]
+            try:
+                values = [(eval(v, globals(), self.__dict__)) for v in str(values).lower().replace("/", ",").split(",")]
+            except SyntaxError:
+                values = [(eval(v.lstrip('0'), globals(), self.__dict__)) for v in str(values).lower().replace("/", ",").split(",")]
 
         dist = self.dlc_df[dist_key][row]
         if str(dist).lower() == "weibull" or str(dist).lower() == "rayleigh":
@@ -287,10 +290,10 @@ class DLCHighLevel(object):
 
 
 if __name__ == "__main__":
-    dlc_hl = DLCHighLevel(r'X:\NREL5MW\dlc.xlsx')
+    dlc_hl = DLCHighLevel(r'X:\DTU10MW\Q0010\DLC_post_betas1.xlsx')
     #print (DLCHighLevelInputFile(r'C:\mmpe\Projects\DLC.xlsx').sensor_info(0, 0, 1)['Name'])
     #print (dlc_dict()['64'])
     #print (dlc_hl.fatigue_distribution()['64'])
-    print (dlc_hl.file_hour_lst(r"X:\NREL5MW/C0008/res/"))
+    print (dlc_hl.file_hour_lst(r"X:\DTU10MW/Q0010/res/"))
 
 
diff --git a/wetb/dlc/tests/test_files/DLC_test.xlsx b/wetb/dlc/tests/test_files/DLC_test.xlsx
index a5ce9710e36966c718266794b0689f631ec6a9c2..8c6c9a112a2c323f220dd424c392b56ea4b101c4 100644
Binary files a/wetb/dlc/tests/test_files/DLC_test.xlsx and b/wetb/dlc/tests/test_files/DLC_test.xlsx differ
diff --git a/wetb/hawc2/hawc2_pbsjob.py b/wetb/hawc2/hawc2_pbsjob.py
new file mode 100755
index 0000000000000000000000000000000000000000..1091786a8f939406c2fac23384111f3da3dd0460
--- /dev/null
+++ b/wetb/hawc2/hawc2_pbsjob.py
@@ -0,0 +1,63 @@
+
+
+from wetb.hawc2.htc_file import HTCFile
+from wetb.utils.cluster_tools.pbsjob import PBSJob
+from wetb.hawc2.log_file import LogInterpreter
+import os
+import time
+from wetb.utils.cluster_tools import pbsjob
+class HAWC2PBSJob(PBSJob):
+
+    def __init__(self, host, username, password):
+        PBSJob.__init__(self, host, username, password)
+
+
+    def submit(self, job, cwd, pbs_out_file):
+        with open (cwd + job) as fid:
+            htcfilename = [l for l in fid if l.startswith('wine')][0].rsplit(" ", 1)[1].strip()
+        print (htcfilename)
+        htcfile = HTCFile(cwd + htcfilename)
+
+        self.log_filename = htcfile.simulation.logfile[0]
+        self.loginterpreter = LogInterpreter(htcfile.simulation.time_stop[0])
+        PBSJob.submit(self, job, cwd, pbs_out_file)
+
+    def status_monitor(self, update=5):
+        i = 0
+        self.loglinenumber = 0
+        while self.in_queue():
+            i += 1
+            print (i, self.status, self.get_nodeid())
+            if self.status is pbsjob.RUNNING:
+                #self.test()
+                scratch_log_filename = "/scratch/%s/%s.g-000.risoe.dk/%s" % (self.client.username, self.jobid, self.log_filename)
+                try:
+                    n, out, err = self.client.execute('tail --lines=+%d %s' % (self.loglinenumber, scratch_log_filename))
+                    self.loginterpreter.update_status(out)
+                    print (self.loginterpreter.status, self.loginterpreter.pct, self.loginterpreter.remaining_time, self.loginterpreter.lastline)
+                    with open("status" + self.jobid, 'w') as fid:
+                        fid.write(";".join([self.loginterpreter.status, str(self.loginterpreter.pct), str(self.loginterpreter.remaining_time), self.loginterpreter.lastline]))
+                    #print (out)
+                    self.loglinenumber += out.count ("\n")
+                    #print (err)
+
+                except Warning as e:
+                    if not "tail: cannot open" in str(e):
+                        print (str(e))
+
+            time.sleep(update)
+        print (i, self.status, self.get_nodeid())
+
+    def test(self):
+        self.log_filename = "logfiles/short.log"
+        scratch_log_filename = "/scratch/%s/%s.g-000.risoe.dk/%s" % (self.client.username, self.jobid, self.log_filename)
+        print (scratch_log_filename)
+        try:
+            n, out, err = self.client.execute('tail --lines=+%d %s' % (self.loglinenumber, scratch_log_filename))
+            print (n)
+            print (out)
+            self.loglinenumber += out.count ("\n")
+            print (err)
+
+        except Warning as e:
+            print (str(e))
diff --git a/wetb/hawc2/htc_contents.py b/wetb/hawc2/htc_contents.py
index bc6bba5e0cea3e2419feae926e4651e697cfe2e5..ee1f06d3834f2df2d04d7c843d22180d6bb212f2 100644
--- a/wetb/hawc2/htc_contents.py
+++ b/wetb/hawc2/htc_contents.py
@@ -155,6 +155,8 @@ class HTCLine(HTCContents):
     values = None
     comments = ""
     def __init__(self, name, values, comments):
+        if "__" in name:
+            name = name[:name.index("__")]
         self.name_ = name
         self.values = values
         self.comments = comments
diff --git a/wetb/hawc2/htc_file.py b/wetb/hawc2/htc_file.py
index 2406ac54da2bfeeaceca48bc7db06cedd4ed2dbd..d539794e32d0f8107757e1e8b4e30b5f40d6f4c8 100644
--- a/wetb/hawc2/htc_file.py
+++ b/wetb/hawc2/htc_file.py
@@ -13,6 +13,7 @@ from __future__ import absolute_import
 from io import open
 from builtins import str
 from future import standard_library
+from wetb.utils.process_exec import pexec
 standard_library.install_aliases()
 from collections import OrderedDict
 
@@ -39,6 +40,7 @@ class HTCFile(HTCContents, HTCDefaults):
             self.lines = self.empty_htc.split("\n")
         else:
             self.filename = filename
+            self.modelpath = os.path.realpath(os.path.join(os.path.dirname(self.filename), modelpath))
             self.lines = self.readlines(filename)
 #            with open(filename) as fid:
 #                self.lines = fid.readlines()
@@ -55,7 +57,7 @@ class HTCFile(HTCContents, HTCDefaults):
                 self._add_contents(line)
                 if line.name_ == "exit":
                     break
-        assert 'simulation' in self.contents, "%s could not be loaded. 'simulation' section missing" % filename
+        #assert 'simulation' in self.contents, "%s could not be loaded. 'simulation' section missing" % filename
 
     def readlines(self, filename):
         self.htc_inputfiles.append(filename)
@@ -65,7 +67,7 @@ class HTCFile(HTCContents, HTCDefaults):
         for l in lines:
             if l.lower().lstrip().startswith('continue_in_file'):
                 filename = l.lstrip().split(";")[0][len("continue_in_file"):].strip()
-                filename = os.path.join(os.path.dirname(self.filename), self.modelpath, filename)
+                filename = os.path.join(self.modelpath, filename)
 
                 for line in self.readlines(filename):
                     if line.lstrip().lower().startswith('exit'):
@@ -95,7 +97,10 @@ class HTCFile(HTCContents, HTCDefaults):
 
     def set_name(self, name, folder="htc"):
         self.filename = os.path.join(self.modelpath, folder, "%s.htc" % name).replace("\\", "/")
-        self.simulation.logfile = "./log/%s.log" % name
+        if 'simulation' in self and 'logfile' in self.simulation:
+            self.simulation.logfile = "./log/%s.log" % name
+        elif 'test_structure' in self and 'logfile' in self.test_structure:
+            self.test_structure.logfile = "./log/%s.log" % name
         self.output.filename = "./res/%s" % name
 
     def input_files(self):
@@ -126,6 +131,7 @@ class HTCFile(HTCContents, HTCDefaults):
         if 'hydro' in self:
             if 'water_properties' in self.hydro:
                 files.append(self.hydro.water_properties.get('water_kinematics_dll', [None])[0])
+                files.append(self.hydro.water_properties.get('water_kinematics_dll', [None, None])[1])
         if 'soil' in self:
             if 'soil_element' in self.soil:
                 files.append(self.soil.soil_element.get('datafile', [None])[0])
@@ -163,7 +169,12 @@ class HTCFile(HTCContents, HTCDefaults):
         return [f for f in files if f]
 
     def turbulence_files(self):
-        files = [self.get('wind.%s.filename_%s' % (type, comp), [None])[0] for type in ['mann', 'flex'] for comp in ['u', 'v', 'w']]
+        if self.wind.turb_format[0] == 0:
+            return []
+        elif self.wind.turb_format[0] == 1:
+            files = [self.get('wind.mann.filename_%s' % comp, [None])[0] for comp in ['u', 'v', 'w']]
+        elif self.wind.turb_format[0] == 2:
+            files = [self.get('wind.flex.filename_%s' % comp, [None])[0] for comp in ['u', 'v', 'w']]
         return [f for f in files if f]
 
 
@@ -172,17 +183,24 @@ class HTCFile(HTCContents, HTCDefaults):
             return []
         dataformat = self.output.get('data_format', 'hawc_ascii')
         res_filename = self.output.filename[0]
-        if dataformat == "gtsdf" or dataformat == "gtsdf64":
+        if dataformat[0] == "gtsdf" or dataformat[0] == "gtsdf64":
             return [res_filename + ".hdf5"]
-        elif dataformat == "flex_int":
+        elif dataformat[0] == "flex_int":
             return [res_filename + ".int", os.path.join(os.path.dirname(res_filename), 'sensor')]
         else:
             return [res_filename + ".sel", res_filename + ".dat"]
 
 
+    def simulate(self, exe):
+        self.save()
+        htcfile = os.path.relpath(self.filename, self.modelpath)
+        hawc2exe = exe
+        errorcode, stdout, stderr, cmd = pexec([hawc2exe, htcfile], self.modelpath)
+
+        if errorcode or 'Elapsed time' not in stderr:
+            raise Exception (str(stdout) + str(stderr))
 
 if "__main__" == __name__:
-    f = HTCFile(r"C:\mmpe\HAWC2\Hawc2_model\htc\NREL_5MW_reference_wind_turbine_launcher_test.htc")
-    print ("\n".join(f.output_files()))
+    f = HTCFile(r"C:\mmpe\HAWC2\models\PhaseIJacketv30\htc", "../../")
 
 
diff --git a/wetb/hawc2/log_file.py b/wetb/hawc2/log_file.py
index e6ba440670e524ab1cd13cee945837ffb34880f8..622bb6fc009df5834ecf6ab9f2ee3bfe26be5748 100644
--- a/wetb/hawc2/log_file.py
+++ b/wetb/hawc2/log_file.py
@@ -23,35 +23,13 @@ INITIALIZATION = 'Initializing simulation'
 SIMULATING = "Simulating"
 DONE = "Simulation succeded"
 
-#def is_file_open(filename):
-#    try:
-#        os.rename(filename, filename + "_")
-#        os.rename(filename + "_", filename)
-#        return False
-#    except OSError as e:
-#        if "The process cannot access the file because it is being used by another process" not in str(e):
-#            raise
-#
-#        if os.path.isfile(filename + "_"):
-#            os.remove(filename + "_")
-#        return True
-
-class LogFile(object):
-    def __init__(self, log_filename, time_stop):
-        self.filename = log_filename
+class LogInterpreter(object):
+    def __init__(self, time_stop):
         self.time_stop = time_stop
         self.hawc2version = "Unknown"
         self.reset()
         self.update_status()
 
-
-    @staticmethod
-    def from_htcfile(htcfile, modelpath):
-        logfilename = htcfile.simulation.logfile[0]
-        if not os.path.isabs(logfilename):
-            logfilename = os.path.join(modelpath, logfilename)
-        return LogFile(logfilename, htcfile.simulation.time_stop[0])
-
     def reset(self):
         self.position = 0
         self.lastline = ""
@@ -66,12 +44,8 @@ class LogFile(object):
 
     def __str__(self):
         return self.txt
+
     def clear(self):
-        # exist_ok does not exist in Python27
-        if not os.path.exists(os.path.dirname(self.filename)):
-            os.makedirs(os.path.dirname(self.filename))#, exist_ok=True)
-        with open(self.filename, 'w', encoding='utf-8'):
-            pass
         self.reset()
 
     def extract_time(self, txt):
@@ -89,17 +63,13 @@ class LogFile(object):
             print ("Cannot extract time from #" + time_line + "#")
             pass
 
-    def update_status(self):
-        if not os.path.isfile(self.filename):
+    def update_status(self, new_lines=""):
+        if self.txt == "" and new_lines == "":
             self.status = MISSING
         else:
             if self.status == UNKNOWN or self.status == MISSING:
                 self.status = PENDING
-            with open(self.filename, 'rb') as fid:
-                fid.seek(self.position)
-                txt = fid.read()
-            self.position += len(txt)
-            txt = txt.decode(encoding='utf_8', errors='strict')
+            txt = new_lines
             self.txt += txt
             if self.status == PENDING and self.position > 0:
                 self.status = INITIALIZATION
@@ -162,6 +132,42 @@ class LogFile(object):
             return "--:--"
 
 
+class LogFile(LogInterpreter):
+
+    def __init__(self, log_filename, time_stop):
+        self.filename = log_filename
+        LogInterpreter.__init__(self, time_stop)
+
+
+    @staticmethod
+    def from_htcfile(htcfile, modelpath):
+        logfilename = htcfile.simulation.logfile[0]
+        if not os.path.isabs(logfilename):
+            logfilename = os.path.join(modelpath, logfilename)
+        return LogFile(logfilename, htcfile.simulation.time_stop[0])
+
+    def clear(self):
+        # exist_ok does not exist in Python27
+        if not os.path.exists(os.path.dirname(self.filename)):
+            os.makedirs(os.path.dirname(self.filename))  #, exist_ok=True)
+        with open(self.filename, 'w', encoding='utf-8'):
+            pass
+        LogInterpreter.clear(self)
+
+    def update_status(self):
+        if not os.path.isfile(self.filename):
+            self.status = MISSING
+        else:
+            if self.status == UNKNOWN or self.status == MISSING:
+                self.status = PENDING
+            s = self.status
+            with open(self.filename, 'rb') as fid:
+                fid.seek(self.position)
+                txt = fid.read()
+            self.position += len(txt)
+            txt = txt.decode(encoding='utf_8', errors='strict')
+            if txt != "":
+                LogInterpreter.update_status(self, txt)
 
 
 
diff --git a/wetb/hawc2/pc_file.py b/wetb/hawc2/pc_file.py
index 4383d78331fbe3f6f69d8094bb7353c39e460e6c..f42cc1076518a42f14b38cb307b65c6847a28a39 100644
--- a/wetb/hawc2/pc_file.py
+++ b/wetb/hawc2/pc_file.py
@@ -76,12 +76,43 @@ class PCFile(AEFile):
         return Cx0 + (Cx1 - Cx0) * (thickness - th0) / (th1 - th0)
 
     def CL(self, radius, alpha, ae_set_nr=1):
+        """Lift coefficient
+
+        Parameters
+        ---------
+        radius : float
+            radius [m]
+        alpha : float
+            Angle of attack [deg]
+        ae_set_nr : int optional
+            Aerdynamic set number, default is 1
+
+        Returns
+        -------
+        Lift coefficient : float
+        """
         return self._Cxxx(radius, alpha, 1, ae_set_nr)
 
     def CD(self, radius, alpha, ae_set_nr=1):
+        """Drag coefficient
+
+        Parameters
+        ---------
+        radius : float
+            radius [m]
+        alpha : float
+            Angle of attack [deg]
+        ae_set_nr : int optional
+            Aerdynamic set number, default is 1
+
+        Returns
+        -------
+        Drag coefficient : float
+        """
         return self._Cxxx(radius, alpha, 2, ae_set_nr)
 
     def CM(self, radius, alpha, ae_set_nr=1):
+
         return self._Cxxx(radius, alpha, 3, ae_set_nr)
 
 if __name__ == "__main__":
diff --git a/wetb/hawc2/simulation.py b/wetb/hawc2/simulation.py
index 2f9243016bb18c2ba5adf58c13468a5293a05b89..d1d20e698568c0f91786c47a954c9cdd942955c3 100644
--- a/wetb/hawc2/simulation.py
+++ b/wetb/hawc2/simulation.py
@@ -297,7 +297,7 @@ class SimulationThread(Thread):
         htcfile = os.path.relpath(self.sim.htcFile.filename, self.sim.modelpath)
         hawc2exe = self.sim.hawc2exe
         stdout = self.sim.stdout_filename
-        self.process = subprocess.Popen("%s %s 1> %s 2>&1" % (hawc2exe, htcfile, stdout), stdout=None, stderr=None, shell=True, cwd=modelpath, creationflags=CREATE_NO_WINDOW)
+        self.process = subprocess.Popen('"%s" %s 1> %s 2>&1' % (hawc2exe, htcfile, stdout), stdout=None, stderr=None, shell=True, cwd=modelpath, creationflags=CREATE_NO_WINDOW)
 
         Thread.start(self)
 
diff --git a/wetb/hawc2/tests/test_files/htcfiles/dlc14_wsp10_wdir000_s0000.htc b/wetb/hawc2/tests/test_files/htcfiles/dlc14_wsp10_wdir000_s0000.htc
index 4a994f38434a8a15f6915ce1a9a92420f9a2e265..6928a04fa4c672557ae03fb0309418b398cb8e2a 100644
--- a/wetb/hawc2/tests/test_files/htcfiles/dlc14_wsp10_wdir000_s0000.htc
+++ b/wetb/hawc2/tests/test_files/htcfiles/dlc14_wsp10_wdir000_s0000.htc
@@ -286,7 +286,7 @@ begin wind ;
   windfield_rotations     0.0 0.0 0.0 ;    yaw, tilt, rotation
   center_pos0             0.0 0.0 -90 ; hub heigth
   shear_format            3 0.2 ; 
-  turb_format             0.0     ;  0=none, 1=mann,2=flex
+  turb_format             1     ;  0=none, 1=mann,2=flex
   tower_shadow_method     3     ;  0=none, 1=potential flow, 2=jet
   scale_time_start       100.0 ; 
   wind_ramp_factor   0.0 100.0 0.8 1.0 ;
diff --git a/wetb/hawc2/tests/test_htc_file.py b/wetb/hawc2/tests/test_htc_file.py
index 51da8f5cc7b3aa8e9f5b164f0383b58b3a5ef8d4..ba878df683153fe88a28c756065865d75faae290 100644
--- a/wetb/hawc2/tests/test_htc_file.py
+++ b/wetb/hawc2/tests/test_htc_file.py
@@ -200,6 +200,12 @@ class TestHtcFile(unittest.TestCase):
                 raise ValueError(f + " is not in list")
         self.assertFalse(input_files)
 
+    def test_input_files2(self):
+        htcfile = HTCFile(self.testfilepath + "ansi.htc")
+        input_files = htcfile.input_files()
+        self.assertTrue('./htc_hydro/ireg_airy_h6_t10.inp' in input_files)
+        #
+
     def test_continue_in_files(self):
         htcfile = HTCFile(self.testfilepath + "continue_in_file.htc", ".")
         self.assertIn('main_body__31', htcfile.new_htc_structure.keys())
diff --git a/wetb/utils/cluster_tools/__init__.py b/wetb/utils/cluster_tools/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/wetb/utils/cluster_tools/pbsjob.py b/wetb/utils/cluster_tools/pbsjob.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae18eaf8ccd00bf8c0129c65d5743900b0ba0dad
--- /dev/null
+++ b/wetb/utils/cluster_tools/pbsjob.py
@@ -0,0 +1,89 @@
+'''
+Created on 04/12/2015
+
+@author: mmpe
+'''
+
+#import x
+import time
+from wetb.utils.cluster_tools.ssh_client import SSHClient
+import os
+import paramiko
+import subprocess
+
+
+
+NOT_SUBMITTED = "Job not submitted"
+PENDING = "Pending"
+RUNNING = "Running"
+DONE = "Done"
+class PBSJob(object):
+    _status = NOT_SUBMITTED
+    nodeid = None
+    def __init__(self, host, username, password):
+        self.client = SSHClient(host, username, password, port=22)
+
+
+    def execute(self, cmd, cwd="./"):
+        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cwd)
+        stdout, stderr = proc.communicate()
+        errorcode = proc.returncode
+        return errorcode, stdout.decode(), stderr.decode()
+
+    def submit(self, job, cwd, pbs_out_file):
+        self.cwd = cwd
+        self.pbs_out_file = os.path.relpath(cwd + pbs_out_file).replace("\\", "/")
+        self.nodeid = None
+        try:
+            os.remove (self.pbs_out_file)
+        except FileNotFoundError:
+            pass
+        _, out, _ = self.execute("qsub %s" % job, cwd)
+        self.jobid = out.split(".")[0]
+        self._status = PENDING
+
+    @property
+    def status(self):
+        if self._status in [NOT_SUBMITTED, DONE]:
+            return self._status
+
+        if self.nodeid is None:
+            self.nodeid = self.get_nodeid()
+            if self.nodeid is not None:
+                self._status = RUNNING
+
+        if self.in_queue() and self.nodeid is None:
+            self._status = PENDING
+        elif os.path.isfile(self.pbs_out_file):
+            self._status = DONE
+        return self._status
+
+    def get_nodeid(self):
+            errorcode, out, err = self.execute("qstat -f %s | grep exec_host" % self.jobid)
+            if errorcode == 0:
+                return out.strip().replace("exec_host = ", "").split(".")[0]
+            elif errorcode == 1 and out == "":
+                return None
+            elif errorcode == 153 and 'qstat: Unknown Job Id' in err:
+                return None
+            else:
+                raise Exception(str(errorcode) + out + err)
+
+    def stop(self):
+        try:
+            self.execute("qdel %s" % self.jobid)
+        except Warning as e:
+            if 'qdel: Unknown Job Id' in str(e):
+                return
+            raise e
+
+
+    def in_queue(self):
+        errorcode, out, err = self.execute("qstat %s" % self.jobid)
+        if errorcode == 0:
+            return True
+        elif 'qstat: Unknown Job Id' in str(err):
+            return False
+        else:
+            raise Exception(str(errorcode) + out + err)
+
diff --git a/wetb/utils/cluster_tools/ssh_client.py b/wetb/utils/cluster_tools/ssh_client.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d70e472b1aed1705b436038bed2d5ff42e4b564
--- /dev/null
+++ b/wetb/utils/cluster_tools/ssh_client.py
@@ -0,0 +1,142 @@
+'''
+Created on 27/11/2015
+
+@author: MMPE
+'''
+
+from io import StringIO
+import paramiko
+import os
+import sys
+
+class SSHClient(object):
+    "A wrapper of paramiko.SSHClient"
+    TIMEOUT = 4
+
+    def __init__(self, host, username, password, port=22, key=None, passphrase=None):
+        self.host = host
+        self.username = username
+        self.password = password
+        self.port = port
+        self.key = key
+        if key is not None:
+            self.key = paramiko.RSAKey.from_private_key(StringIO(key), password=passphrase)
+
+    def info(self):
+        return self.host, self.username, self.password, self.port
+
+    def __enter__(self):
+        self.client = paramiko.SSHClient()
+        self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+        self.client.connect(self.host, self.port, username=self.username, password=self.password, pkey=self.key, timeout=self.TIMEOUT)
+        assert self.client is not None
+        self.transport = paramiko.Transport((self.host, self.port))
+        self.transport.connect(username=self.username, password=self.password)
+        self.sftp = paramiko.SFTPClient.from_transport(self.transport)
+        return self
+
+    def __exit__(self, *args):
+        self.close()
+
+
+    def download(self, remotefilepath, localfile, verbose=False):
+        if verbose:
+            print ("Download %s > %s" % (remotefilepath, str(localfile)))
+        with self:
+            if isinstance(localfile, (str, bytes, int)):
+                ret = self.sftp.get(remotefilepath, localfile)
+            elif hasattr(localfile, 'write'):
+                ret = self.sftp.putfo(remotefilepath, localfile)
+        if verbose:
+            print (ret)
+
+
+    def upload(self, localfile, filepath, verbose=False):
+        if verbose:
+            print ("Upload %s > %s" % (localfile, filepath))
+        with self:
+            if isinstance(localfile, (str, bytes, int)):
+                ret = self.sftp.put(localfile, filepath)
+            elif hasattr(localfile, 'read'):
+                ret = self.sftp.putfo(localfile, filepath)
+        if verbose:
+            print (ret)
+
+    def close(self):
+        if self.client is not None:
+            self.client.close()
+            self.client = None
+        self.sftp.close()
+        self.transport.close()
+
+    def file_exists(self, filename):
+        _, out, _ = (self.execute('[ -f %s ] && echo "File exists" || echo "File does not exists"' % filename.replace("\\", "/")))
+        return out.strip() == "File exists"
+
+    def execute(self, command, sudo=False, verbose=False):
+
+        feed_password = False
+        if sudo and self.username != "root":
+            command = "sudo -S -p '' %s" % command
+            feed_password = self.password is not None and len(self.password) > 0
+        if isinstance(command, (list, tuple)):
+            command = "\n".join(command)
+
+        if verbose:
+            print (">>> " + command)
+        with self:
+            stdin, stdout, stderr = self.client.exec_command(command)
+            if feed_password:
+                stdin.write(self.password + "\n")
+                stdin.flush()
+
+            v, out, err = stdout.channel.recv_exit_status(), stdout.read().decode(), stderr.read().decode()
+
+
+        if v:
+            raise Warning ("out:\n%s\n----------\nerr:\n%s" % (out, err))
+        elif verbose:
+            if out:
+                sys.stdout.write(out)
+            if err:
+                sys.stderr.write(err)
+        return v, out, err
+
+    def append_wine_path(self, path):
+        ret = self.execute('wine regedit /E tmp.reg "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment"')
+        self.download('tmp.reg', 'tmp.reg')
+        with open('tmp.reg') as fid:
+            lines = fid.readlines()
+
+        path_line = [l for l in lines if l.startswith('"PATH"=')][0]
+        for p in path_line[8:-1].split(";"):
+            if os.path.abspath(p) == os.path.abspath(p):
+                return
+        if path not in path_line:
+            path_line = path_line.strip()[:-1] + ";" + path + '"'
+
+            with open('tmp.reg', 'w') as fid:
+                fid.write("".join(lines[:3] + [path_line]))
+            self.upload('tmp.reg', 'tmp.reg')
+            ret = self.execute('wine regedit tmp.reg')
+
+    def glob(self, filepattern, cwd=""):
+        cwd = os.path.join(cwd, os.path.split(filepattern)[0]).replace("\\", "/")
+        filepattern = os.path.split(filepattern)[1]
+        _, out, _ = self.execute(r'find %s -maxdepth 1 -type f -name "%s"' % (cwd, filepattern))
+        files = []
+        for file in out.strip().split("\n"):
+            files.append(file.strip())
+        return files
+
+
+if __name__ == "__main__":
+    from mmpe.ui.qt_ui import QtInputUI
+    q = QtInputUI(None)
+    import x
+    username, password = "mmpe", x.password  #q.get_login("mmpe")
+
+
+    client = SSHClient(host='gorm', port=22, username=username, password=password)
+    print (client.glob("*.*", ".hawc2launcher/medium1__1__"))
+    #    ssh.upload('../News.txt', 'news.txt')
diff --git a/wetb/utils/cython_compile/cython_compile.py b/wetb/utils/cython_compile/cython_compile.py
index af9146883099b7a27cb4e348b47eaf5c669a7a0e..2d09be147d2454350552da9f3ea63a3c055b3ba3 100644
--- a/wetb/utils/cython_compile/cython_compile.py
+++ b/wetb/utils/cython_compile/cython_compile.py
@@ -381,7 +381,10 @@ def py2pyx(pylines):
             indent = l[:len(l) - len(l.lstrip())]
             cdef = l[l.index("#c") + 1:]
             l = indent + cdef
-        pyxlines.append(l)
+        if "__future__" in l:
+            pyxlines.insert(0, l)  # from __future__ import ... must be first
+        else:
+            pyxlines.append(l)
     return pyxlines
 
 
diff --git a/wetb/utils/geometry.py b/wetb/utils/geometry.py
index 01a4daf6e0cbac576dbc95711ab7205957b9a8eb..79086245e3494faf8d1f2ac41836da08436f65f9 100644
--- a/wetb/utils/geometry.py
+++ b/wetb/utils/geometry.py
@@ -168,11 +168,11 @@ def abvrel2xyz(alpha, beta, vrel):
     Parameters
     ----------
     alpha : array_like
-        Pitot tube angle of attack
+        Pitot tube angle of attack. Zero: Parallel to pitot tube. Positive: Flow from wind side (pressure side)
     beta : array_like
-        Pitot tube side slip angle
+        Pitot tube side slip angle. Zero: Parallel to pitot tube. Positive: Flow from root side
     vrel : array_like
-        Pitot tube relative velocity
+        Pitot tube relative velocity. Positive: flow towards pitot tube
 
     Returns
     -------
@@ -181,13 +181,13 @@ def abvrel2xyz(alpha, beta, vrel):
     y : array_like
         Wind component in alpha plane (positive for positive alpha)
     z : array_like
-        Wind component in beta plane (positive for positive beta)
+        Wind component in beta plane (positive for negative 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_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
 
@@ -196,10 +196,10 @@ def abvrel2xyz(alpha, beta, vrel):
 
     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))
+    y[m] = sign_vsy[m] * 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))
+    z[m] = sign_vsz[m] * 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
index 04dfa697dc683f49c31b862a50df8cdf828b1f7e..6626cee2dfa4b8b2f488dd457863430fd708df00 100644
--- a/wetb/utils/process_exec.py
+++ b/wetb/utils/process_exec.py
@@ -13,7 +13,7 @@ from future import standard_library
 standard_library.install_aliases()
 
 import os
-import psutil
+
 
 DEBUG = False
 def pexec(args, cwd=None):
diff --git a/wetb/wind/shear.py b/wetb/wind/shear.py
index 77e70135fcd42753b2b0f304e9ef85d1ef86224d..3d03110aff5b304807cd546c52e47153b73ffc80 100644
--- a/wetb/wind/shear.py
+++ b/wetb/wind/shear.py
@@ -169,7 +169,7 @@ def fit_log_shear(z_u_lst, include_R=False):
 
     Example
     --------
-    >>> fit_log_shear([(85, 8.88131), (21, 4.41832)],  87.13333)
+    >>> fit_log_shear([(85, 8.88131), (21, 4.41832)])
     [ 0.49938238  8.99192568]
     """
 #    def shear_error(x, z_u_lst):
@@ -183,3 +183,13 @@ def fit_log_shear(z_u_lst, include_R=False):
         return a * kappa, np.exp(-b / a), sum((U - (a * np.log(z) + b)) ** 2)
     return a * kappa, np.exp(-b / a)
 
+if __name__ == '__main__':
+    from matplotlib.pyplot import plot, show
+    z = np.arange(0, 211)
+    for alpha, c in zip([0.00001, 1, 2], ['r', 'b', 'g']):
+        u = power_shear(alpha, 120, 10, z)
+        plot(u, z, c)
+        plot(u.mean(), 120, c + '.')
+
+    plot([8.5, 11], [120, 120])
+    show()