Repository: oemof/tespy
Issue: Absolute path specification

The network load, network export and design_path as well as init_path do not work with absolute path specification. This requires a check of
- [ ] how the path is specified and
- [ ] which operating system TESPy is executed on.

Changed files:
tespy/tools/helpers.py, tespy/network_reader.py, tespy/connections.py, tespy/networks.py

diff --git a/tespy/connections.py b/tespy/connections.py
index 4629e6d5..67083e73 100644
--- a/tespy/connections.py
+++ b/tespy/connections.py
@@ -274,6 +274,8 @@ class connection:
                 elif key == 'state':
                     if kwargs[key] in ['l', 'g']:
                         self.state.set_attr(val=kwargs[key], val_set=True)
+                    elif isinstance(kwargs[key], dc_simple):
+                        self.state = kwargs[key]
                     else:
                         if (isinstance(kwargs[key], float) or
                                 isinstance(kwargs[key], np.float64) or
diff --git a/tespy/network_reader.py b/tespy/network_reader.py
index b9d0287d..9736ed82 100644
--- a/tespy/network_reader.py
+++ b/tespy/network_reader.py
@@ -8,6 +8,7 @@
 """
 
 import pandas as pd
+import numpy as np
 from tespy import cmp, con, nwk, hlp, cmp_char
 import os
 import ast
@@ -118,11 +119,17 @@ def load_nwk(path):
     9.367
     >>> shutil.rmtree('./tmp', ignore_errors=True)
     """
+    if path[-1] != '/' and path[-1] != '\\\\':
+        path += '/'
+
+    path = hlp.modify_path_os(path)
+    path_comps = hlp.modify_path_os(path + 'comps/')
+
     msg = 'Reading network data from base path ' + path + '.'
     logging.info(msg)
 
     # load characteristics
-    fn = path + '/comps/char.csv'
+    fn = path_comps + 'char.csv'
     chars = pd.read_csv(fn, sep=';', decimal='.',
                         converters={'x': ast.literal_eval,
                                     'y': ast.literal_eval})
@@ -130,7 +137,7 @@ def load_nwk(path):
     logging.debug(msg)
 
     # load characteristic maps
-    fn = path + '/comps/char_map.csv'
+    fn = path_comps + 'char_map.csv'
     char_maps = pd.read_csv(fn, sep=';', decimal='.',
                             converters={'x': ast.literal_eval,
                                         'y': ast.literal_eval,
@@ -143,10 +150,10 @@ def load_nwk(path):
     comps = pd.DataFrame()
     inter = pd.DataFrame()
 
-    files = os.listdir(path + '/comps/')
+    files = os.listdir(path_comps)
     for f in files:
         if f != 'bus.csv' and f != 'char.csv' and f != 'char_map.csv':
-            fn = path + '/comps/' + f
+            fn = path_comps + f
             df = pd.read_csv(fn, sep=';', decimal='.',
                              converters={'design': ast.literal_eval,
                                          'offdesign': ast.literal_eval,
@@ -180,7 +187,7 @@ def load_nwk(path):
     nw.inter = inter.set_index('label').to_dict()['instance']
 
     # load connections
-    fn = path + '/conn.csv'
+    fn = path + 'conn.csv'
     conns = pd.read_csv(fn, sep=';', decimal='.',
                         converters={'design': ast.literal_eval,
                                     'offdesign': ast.literal_eval})
@@ -203,7 +210,7 @@ def load_nwk(path):
     logging.info(msg)
 
     # load busses
-    fn = path + '/comps/bus.csv'
+    fn = path_comps + 'bus.csv'
     busses = pd.read_csv(fn, sep=';', decimal='.')
 
     msg = 'Reading bus data from ' + fn + '.'
@@ -371,7 +378,7 @@ def construct_network(path):
         TESPy network object.
     """
     # read network .csv-file
-    netw = pd.read_csv(path + '/netw.csv', sep=';', decimal='.',
+    netw = pd.read_csv(path + 'netw.csv', sep=';', decimal='.',
                        converters={'fluids': ast.literal_eval})
     f_list = netw['fluids'][0]
 
@@ -421,13 +428,16 @@ def construct_conns(c, *args):
     # read fluid properties
     for key in ['m', 'p', 'h', 'T', 'x', 'v', 'Td_bp']:
         if key in c:
-            dc = hlp.dc_prop(val=c[key], val0=c[key + '0'], val_set=c[key + '_set'],
-                             unit=c[key + '_unit'], unit_set=c[key + '_unit_set'],
-                             ref=None, ref_set=c[key + '_ref_set'])
-            kwargs[key] = dc
+            if key in c:
+                dc = hlp.dc_prop(val=c[key], val0=c[key + '0'], val_set=c[key + '_set'],
+                                 unit=c[key + '_unit'], unit_set=c[key + '_unit_set'],
+                                 ref=None, ref_set=c[key + '_ref_set'])
+                kwargs[key] = dc
 
     key = 'state'
-    dc = hlp.dc_simple(val=c[key], val_set=c[key + '_set'])
+    if key in c:
+        dc = hlp.dc_simple(val=c[key], val_set=c[key + '_set'])
+        kwargs[key] = dc
 
     # read fluid vector
     val = {}
diff --git a/tespy/networks.py b/tespy/networks.py
index 481a078e..1e8c6d96 100644
--- a/tespy/networks.py
+++ b/tespy/networks.py
@@ -701,10 +701,7 @@ class network:
         cp_sort.set_index('label', inplace=True)
         for c in cp_sort.cp.unique():
             if c not in not_required:
-                if self.path_abs:
-                    path = self.design_path + '/comps/' + c + '.csv'
-                else:
-                    path = './' + self.design_path + '/comps/' + c + '.csv'
+                path = hlp.modify_path_os(self.design_path + '/comps/' + c + '.csv')
 
                 msg = 'Reading design point information for components of type ' +  c + ' from path ' + path + '.'
                 logging.debug(msg)
@@ -718,10 +715,7 @@ class network:
                         i += 1
 
         # connections
-        if self.path_abs:
-            path = self.design_path + '/conn.csv'
-        else:
-            path = './' + self.design_path + '/conn.csv'
+        path = hlp.modify_path_os(self.design_path + '/conn.csv')
         df = pd.read_csv(path, index_col=0, delimiter=';', decimal='.')
         msg = 'Reading design point information for connections from path ' + path + '.'
         logging.debug(msg)
@@ -1111,10 +1105,9 @@ class network:
         """
         # match connection (source, source_id, target, target_id) on
         # connection objects of design file
-        if self.path_abs:
-            path = self.init_path + '/conn.csv'
-        else:
-            path = './' + self.init_path + '/conn.csv'
+
+        path = hlp.modify_path_os(self.init_path + '/conn.csv')
+
         df = pd.read_csv(path, index_col=0, delimiter=';', decimal='.')
         for c in self.conns.index:
             conn = (df.loc[df['s'].isin([c.s.label]) & df['t'].isin([c.t.label]) &
@@ -2336,18 +2329,18 @@ class network:
         filename : str
             Path for the results.
 
-        path_abs : boolean
-            Absolute path specified?
-
         Note
         ----
-        File results will be saved to ./filename/results.csv. If you provide :code:`save(structure=True)`,
-        all network information will be saved to path ./filename/.
+        Results will be saved to path. The results contain:
+
+        - netw.csv (network information)
+        - conn.csv (connection information)
+        - folder comps containing .csv files (bus.csv, char.csv, char_map.csv)
+          as well as .csv files for all types of components within your network.
         """
-        if kwargs.get('path_abs', False):
-            path = path + '/'
-        else:
-            path = './' + path + '/'
+        if path[-1] != '/' and path[-1] != '\\\\':
+            path += '/'
+        path = hlp.modify_path_os(path)
 
         logging.debug('Saving network to path ' + path + '.')
         # creat path, if non existent
@@ -2355,15 +2348,16 @@ class network:
             os.makedirs(path)
 
         # create path for component folder if non existent
-        if not os.path.exists(path + 'comps/'):
-            os.makedirs(path + 'comps/')
+        path_comps = hlp.modify_path_os(path + 'comps/')
+        if not os.path.exists(path_comps):
+            os.makedirs(path_comps)
 
         # save all network information
         self.save_network(path + 'netw.csv')
-        self.save_connections(path + 'conn.csv', structure=True)
-        self.save_components(path + 'comps/')
-        self.save_busses(path + 'comps/bus.csv')
-        self.save_characteristics(path + 'comps/')
+        self.save_connections(path + 'conn.csv')
+        self.save_components(path_comps)
+        self.save_busses(path_comps + 'bus.csv')
+        self.save_characteristics(path_comps)
 
     def save_network(self, fn):
         r"""
@@ -2392,7 +2386,7 @@ class network:
         df.to_csv(fn, sep=';', decimal='.', index=False, na_rep='nan')
         logging.debug('Network information saved to ' + fn + '.')
 
-    def save_connections(self, fn, structure=False):
+    def save_connections(self, fn):
         r"""
         Saves connections to fn, saves network structure data if structure is True.
 
@@ -2417,10 +2411,9 @@ class network:
         df['t'] = self.conns.apply(network.get_props, axis=1, args=('t', 'label'))
         df['t_id'] = self.conns.apply(network.get_props, axis=1, args=('t_id',))
 
-        if structure:
-            # design and offdesign parameters
-            df['design'] = self.conns.apply(network.get_props, axis=1, args=('design',))
-            df['offdesign'] = self.conns.apply(network.get_props, axis=1, args=('offdesign',))
+        # design and offdesign parameters
+        df['design'] = self.conns.apply(network.get_props, axis=1, args=('design',))
+        df['offdesign'] = self.conns.apply(network.get_props, axis=1, args=('offdesign',))
 
         cols = ['m', 'p', 'h', 'T', 'x', 'v', 'Td_bp']
         for key in cols:
@@ -2428,16 +2421,15 @@ class network:
             df[key] = self.conns.apply(network.get_props, axis=1, args=(key, 'val'))
             df[key + '_unit'] = self.conns.apply(network.get_props, axis=1, args=(key, 'unit'))
 
-            if structure:
-                # connection parametrisation
-                df[key + '_unit_set'] = self.conns.apply(network.get_props, axis=1, args=(key, 'unit_set'))
-                df[key + '0'] = self.conns.apply(network.get_props, axis=1, args=(key, 'val0'))
-                df[key + '_set'] = self.conns.apply(network.get_props, axis=1, args=(key, 'val_set'))
-                df[key + '_ref'] = self.conns.apply(network.get_props, axis=1, args=(key, 'ref', 'obj',)).astype(str)
-                df[key + '_ref'] = df[key + '_ref'].str.extract(r' at (.*?)>', expand=False)
-                df[key + '_ref_f'] = self.conns.apply(network.get_props, axis=1, args=(key, 'ref', 'f',))
-                df[key + '_ref_d'] = self.conns.apply(network.get_props, axis=1, args=(key, 'ref', 'd',))
-                df[key + '_ref_set'] = self.conns.apply(network.get_props, axis=1, args=(key, 'ref_set',))
+            # connection parametrisation
+            df[key + '_unit_set'] = self.conns.apply(network.get_props, axis=1, args=(key, 'unit_set'))
+            df[key + '0'] = self.conns.apply(network.get_props, axis=1, args=(key, 'val0'))
+            df[key + '_set'] = self.conns.apply(network.get_props, axis=1, args=(key, 'val_set'))
+            df[key + '_ref'] = self.conns.apply(network.get_props, axis=1, args=(key, 'ref', 'obj',)).astype(str)
+            df[key + '_ref'] = df[key + '_ref'].str.extract(r' at (.*?)>', expand=False)
+            df[key + '_ref_f'] = self.conns.apply(network.get_props, axis=1, args=(key, 'ref', 'f',))
+            df[key + '_ref_d'] = self.conns.apply(network.get_props, axis=1, args=(key, 'ref', 'd',))
+            df[key + '_ref_set'] = self.conns.apply(network.get_props, axis=1, args=(key, 'ref_set',))
 
         key = 'state'
         df[key] = self.conns.apply(network.get_props, axis=1, args=(key, 'val'))
@@ -2447,14 +2439,12 @@ class network:
             # fluid mass fraction
             df[val] = self.conns.apply(network.get_props, axis=1, args=('fluid', 'val', val))
 
-            if structure:
-                # fluid mass fraction parametrisation
-                df[val + '0'] = self.conns.apply(network.get_props, axis=1, args=('fluid', 'val0', val))
-                df[val + '_set'] = self.conns.apply(network.get_props, axis=1, args=('fluid', 'val_set', val))
+            # fluid mass fraction parametrisation
+            df[val + '0'] = self.conns.apply(network.get_props, axis=1, args=('fluid', 'val0', val))
+            df[val + '_set'] = self.conns.apply(network.get_props, axis=1, args=('fluid', 'val_set', val))
 
-        if structure:
-            # fluid balance parametrisation
-            df['balance'] = self.conns.apply(network.get_props, axis=1, args=('fluid', 'balance'))
+        # fluid balance parametrisation
+        df['balance'] = self.conns.apply(network.get_props, axis=1, args=('fluid', 'balance'))
 
         df.to_csv(fn, sep=';', decimal='.', index=False, na_rep='nan')
         logging.debug('Connection information saved to ' + fn + '.')
diff --git a/tespy/tools/helpers.py b/tespy/tools/helpers.py
index ad5d3eb0..b20709ea 100644
--- a/tespy/tools/helpers.py
+++ b/tespy/tools/helpers.py
@@ -2390,3 +2390,38 @@ def lamb_trans(params, l):
 def dlamb_trans_dl(params, l):
     d = 0.001
     return (lamb_trans(params, l + d) - lamb_trans(params, l - d)) / (2 * d)
+
+
+def modify_path_os(path):
+    """
+    Modify the path according the os. Also detects weather the path
+    specification is absolute or relative and adjusts the path respectively.
+
+    Parameters
+    ----------
+    path : str
+        Path to modify.
+
+    Returns
+    -------
+    path : str
+        Modified path.
+    """
+
+    if os.name == 'nt':
+        # windows
+        path = path.replace('/', '\\\\')
+        if path[0] != '\\\\' and path[1:2] != ':\\\\':
+            # relative path
+            path = '.\\\\' + path
+    elif os.name == 'posix':
+        # linux, max
+        if path[0] != '/':
+            # absolute path
+            path = './' + path
+    else:
+        # unkown os
+        msg = 'Unknown operating system, using posix pathing logic.'
+        logging.warning(msg)
+
+    return path