From 3a4720c657be15523ec45dca0f2e5d581cbe9e4f Mon Sep 17 00:00:00 2001
From: Niclas Esser <nesser@mpifr-bonn.mpg.de>
Date: Fri, 2 Aug 2024 11:52:46 +0200
Subject: [PATCH] Pylint changes

---
 pafsim/chain.py                 | 41 +++++++++++-----------
 pafsim/connector.py             | 23 ++++++-------
 pafsim/executor.py              | 37 +++++++++++++-------
 pafsim/processor/__init__.py    | 61 ++++++++++++++++++++++++++-------
 pafsim/processor/_processor.py  | 13 ++++---
 pafsim/processor/beamformer.py  | 10 +++---
 pafsim/processor/calibrator.py  | 15 ++++----
 pafsim/processor/channelizer.py | 15 ++++----
 pafsim/processor/correlator.py  | 12 +++----
 pafsim/processor/generator.py   | 24 ++++++-------
 pafsim/processor/waveform.py    | 16 ++++-----
 pafsim/vector.py                |  8 +++--
 12 files changed, 163 insertions(+), 112 deletions(-)

diff --git a/pafsim/chain.py b/pafsim/chain.py
index 576ef48..18eb7f9 100644
--- a/pafsim/chain.py
+++ b/pafsim/chain.py
@@ -1,6 +1,6 @@
 import os
 import shutil
-from typing  import List, Dict
+from typing  import Dict
 import networkx as nx
 import matplotlib.pyplot as plt
 
@@ -20,11 +20,12 @@ class ProcessingChain():
             name (str, optional): When creating multiple ProcessingChain objects with dependencies,
                 a name can be provided to refer to dependend ProcessingChains. Defaults to "".
         """
-        self.name: str=name
-        self.conf: dict=conf
-        self.setup_flag: bool=False
-        self.processed: bool=False
-        self.dag: nx.DiGraph=nx.DiGraph()
+        self.name: str = name
+        self.conf: dict = conf
+        self.setup_flag: bool = False
+        self.processed: bool = False
+        self.dag: nx.DiGraph = nx.DiGraph()
+        self.dag_list: list = []
         self.processors: Dict[str,processing.Processor] = {}
 
     def setup(self):
@@ -81,11 +82,11 @@ class ProcessingChain():
                 return proc
         return None
 
-    def plot(self, dir: str="", figsize: tuple=(8,4)):
+    def plot(self, path: str="", figsize: tuple=(8,4)):
         """Plots the DAG
 
         Args:
-            dir (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
+            path (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
             figsize (tuple, optional): Size of the plotted figure. Defaults to (8,4).
         """
         fig = plt.figure(num = 1, figsize=figsize)
@@ -98,8 +99,8 @@ class ProcessingChain():
                          linewidths=2,
                          font_size=9)
         fig.suptitle("Directed acyclic graph (DAG)")
-        if dir:
-            fig.savefig(dir + 'dag.png')
+        if path:
+            fig.savefig(path + 'dag.png')
             plt.close()
         else:
             plt.show()
@@ -117,11 +118,11 @@ class ProcessingChain():
             self.processors[pname].process()
         self.processed = True
 
-    def plotAll(self, dir: str="", figsize: tuple=(8,4)):
+    def plotAll(self, path: str="", figsize: tuple=(8,4)):
         """Calls all plot methods of the processors
 
         Args:
-            dir (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
+            path (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
             figsize (tuple, optional): Size of the plotted figure. Defaults to (8,4).
 
         Raises:
@@ -131,7 +132,7 @@ class ProcessingChain():
         if self.processed is False:
             raise Exception("Chain not processed, use process() before plot()")
         for pname in self.dag_list:
-            self.processors[pname].plot(dir, figsize)
+            self.processors[pname].plot(path, figsize)
 
     def save(self, plot: bool=True, overwrite:bool=True):
         """Stores the configurations and marked vectors of the procesing chains in dedicated folders
@@ -145,18 +146,18 @@ class ProcessingChain():
             return
         directory = os.path.abspath(self.conf["location"])
 
-        if overwrite == True and os.path.exists(directory):
+        if overwrite is True and os.path.exists(directory):
             shutil.rmtree(directory)
         if not os.path.exists(directory):
             os.makedirs(directory)
-        if directory[-1]!="/": directory+="/"
-        with open(directory + "/conf.json", "w") as o:
+
+        with open(os.path.join(directory, "conf.json"), "w", encoding="utf-8") as o:
             o.write(str(self.conf))
-        os.mkdir(directory + '/data/')
+        os.mkdir(os.path.join(directory, 'data/'))
         if plot:
-            os.mkdir(directory + '/plot/')
-            self.plotAll(directory + '/plot/')
+            os.mkdir(os.path.join(directory, 'plot/'))
+            self.plotAll(os.path.join(directory, 'plot/'))
 
         for tv_name in self.conf["store"]:
             tv = Vector(self.getProcessor(tv_name))
-            tv.store(directory + '/data/')
\ No newline at end of file
+            tv.store(os.path.join(directory, 'data/'))
diff --git a/pafsim/connector.py b/pafsim/connector.py
index 8240f9a..89fb4dc 100644
--- a/pafsim/connector.py
+++ b/pafsim/connector.py
@@ -1,10 +1,9 @@
 # import numpy as np
 from pafsim.processor import Processor
 
-class ConnectionError(Exception):
+class EdgeConnectionError(Exception):
     """Exception class used for invalid connections
     """
-    pass
 
 class Connector:
     """The Connector class is used as edges in the ProcessingChain. It transposes output
@@ -20,31 +19,31 @@ class Connector:
         """Connects to Processors and checks if the connection is valid
 
         Raises:
-            ConnectionError: If a connection is invalid
+            EdgeConnectionError: If a connection is invalid
         """
         if self.a_node.O_FORMAT in self.b_node.I_FORMAT:
             print("Connection without transpose")
-            self._assign()
+            self.assign()
         elif set(self.a_node.O_FORMAT) in [set(x) for x in self.b_node.I_FORMAT]:
             print("Connection needs transpose")
-            self._transpose()
-            self._assign()
+            self.transpose()
+            self.assign()
         else:
-            raise ConnectionError("Output format of node A does not match input formats of node B")
+            raise EdgeConnectionError("Output format of node A does not match input formats of node B")
 
-    def _assign(self):
+    def assign(self):
         """Assigns the Processors
 
         Raises:
-            ConnectionError: Mismatch of inputs
+            EdgeConnectionError: Mismatch of inputs
         """
         if self.b_node.N_INPUTS > len(self.b_node.pp):
-            raise ConnectionError(f"Can not assign more than {self.b_node.N_INPUTS} connections")
+            raise EdgeConnectionError(f"Can not assign more than {self.b_node.N_INPUTS} connections")
         self.b_node.pp.append(self.a_node)
 
-    def _transpose(self):
+    def transpose(self):
         """Transpose the output of the pre processor to the expected input of the post processor
 
         ToDo: Not implemented yet
         """
-        pass
\ No newline at end of file
+
diff --git a/pafsim/executor.py b/pafsim/executor.py
index e7f0fa0..687755d 100644
--- a/pafsim/executor.py
+++ b/pafsim/executor.py
@@ -1,6 +1,8 @@
-import networkx as nx
+import typing as ty
 from collections import OrderedDict
+import networkx as nx
 from pafsim.chain import ProcessingChain
+from pafsim.vector import Vector
 
 class Executor():
     """The Executor enables the simulation of comprehensive simulation configuration with nested
@@ -12,10 +14,12 @@ class Executor():
         Args:
             conf (dict, optional): The discrition of the simulation. Defaults to None.
         """
-        self.test_vectors = []
-        self.chains = []
-        self.dag = nx.DiGraph()
-        self.conf = conf
+        self.test_vectors: ty.List[Vector] = []
+        self.chains: ty.List[ProcessingChain] = []
+        self.dag: nx.DiGraph = nx.DiGraph()
+        self.conf: dict = conf
+        self.input_vectors: ty.List[Vector] = []
+        self.output_vectors: ty.List[Vector] = []
 
     def update(self, conf: dict):
         """Updates the current configuration
@@ -29,7 +33,7 @@ class Executor():
         try:
             self.conf.update(conf)
         except Exception as e:
-            raise Exception("Failed to update configuration file with {}".format(e))
+            raise Exception(f"Failed to update configuration file with {e}")
 
     def _sort_chains(self):
         """Used to sort the processing chains according to the dependencies speciefied in
@@ -74,27 +78,34 @@ class Executor():
         self.update(chain.conf)
         self.chains.append(chain)
 
-    def getVectorById(self, id: str):
+    def getVectorById(self, name: str):
         """Get a test vector by its ID
 
         Args:
-            id (str): The ID of a test vector
+            name (str): The ID of a test vector
 
         Returns:
             Vector: The Vector
         """
         for i in self.input_vectors:
-            if id == i.id:
+            if name == i.name:
                 return i
         for i in self.output_vectors:
-            if id == i.id:
+            if name == i.name:
                 return i
-        print("No Vector with id={} loaded".format(id))
+        print(f"No Vector with name '{name}' found")
         return None
 
-    def getChain(self, name):
+    def getChain(self, name: str) -> ProcessingChain:
+        """Get a registered ProcessingChain object by its name
+
+        Args:
+            name (str): The name of the ProcessingChain
+
+        Returns:
+            ProcessingChain: The ProcessingChain object if it exists, otherwise None
+        """
         for chain in self.chains:
             if chain.name == name:
                 return chain
         return None
-
diff --git a/pafsim/processor/__init__.py b/pafsim/processor/__init__.py
index af1a20e..7fbdc0c 100644
--- a/pafsim/processor/__init__.py
+++ b/pafsim/processor/__init__.py
@@ -1,31 +1,68 @@
 import numpy as np
+from pafsim.processor._processor import Processor
+from pafsim.processor.beamformer import Beamformer
+from pafsim.processor.calibrator import WeightGenerator
+from pafsim.processor.channelizer import Channelizer
+from pafsim.processor.correlator import Correlator
+from pafsim.processor.waveform import WaveformGenerator
+from pafsim.processor.generator import Generator
+
+def createProcessor(class_name: str, name: str, kwargs: dict) -> Processor:
+    """Factory function to create a Processor by and name and configuration options
+
+    Args:
+        class_name (str): The class name of the Processor (e.g. Channelizer)
+        name (str): The unique name of the processor
+        kwargs (dict): The configuration options of the processor
+
+    Returns:
+        Processor: The created processor
+    """
+    return globals()[class_name](name, **kwargs)
+
+def scale(data: np.ndarray, bits: int) -> np.ndarray:
+    """Scales a numpy array to passed bit size
 
-def createProcessor(class_name, id, kwargs):
-    return globals()[class_name](id, **kwargs)
+    Args:
+        data (np.ndarray): The array to scale
+        bits (int): The number of bits
 
-def scale(data, bits):
+    Returns:
+        np.ndarray: The rescaled array
+    """
     return (data / np.max(np.abs(data)) * 2**(bits - 1))
 
-def float2int(data, dtype):
+def rescalefloat2int(data: np.ndarray, dtype: np.dtype) -> np.ndarray:
+    """Rescales a float array to an int array
+
+    Args:
+        data (np.ndarray): The float array to rescale
+        dtype (np.dtype): The desired integer data type (e.g. np.int32)
+
+    Returns:
+        np.ndarray: The converted integer array
+    """
     bits = np.dtype(dtype).itemsize * 8
     if np.max(data) > 2**(bits-1):
         data = scale(data, bits)
     return data.astype(dtype)
 
-def complex2int2(data, dtype):
+def complex2int2(data: np.ndarray, dtype: np.dtype) -> np.ndarray:
+    """Rescales a float array to an int array
+
+    Args:
+        data (np.ndarray): The float array to rescale
+        dtype (np.dtype): The desired integer data type (e.g. np.int32)
+
+    Returns:
+        np.ndarray: The converted integer array
+    """
     bits = np.dtype(dtype).itemsize * 8
     dtype = np.dtype([('real', dtype), ('imag', dtype)])
     if np.max(data) > 2**(bits-1):
         data = scale(data, bits)
     return data.astype(dtype)
 
-from pafsim.processor._processor import Processor
-from pafsim.processor.beamformer import Beamformer
-from pafsim.processor.calibrator import WeightGenerator
-from pafsim.processor.channelizer import Channelizer
-from pafsim.processor.correlator import Correlator
-from pafsim.processor.waveform import WaveformGenerator
-from pafsim.processor.generator import Generator
 
 __all__ = [
     "Processor",
diff --git a/pafsim/processor/_processor.py b/pafsim/processor/_processor.py
index f3c86e3..1b90762 100644
--- a/pafsim/processor/_processor.py
+++ b/pafsim/processor/_processor.py
@@ -1,7 +1,7 @@
+from abc import ABC, abstractmethod
+import os
 import pickle
 import numpy as np
-import os
-from abc import ABC, abstractmethod
 
 
 class Processor(ABC):
@@ -16,7 +16,7 @@ class Processor(ABC):
         """Base constructor of the Processor. Can not be initiatied as this class is abstract
 
         Args:
-            name (str): The ID of the Processor
+            name (str): The unique name of theProcessor
         """
         self.pp: list[Processor] = [None for __ in range(self.N_INPUT)]
         self.name: str = name
@@ -66,15 +66,14 @@ class Processor(ABC):
         pass
 
     @abstractmethod
-    def plot(self, dir: str="", figsize: tuple=(8,4)):
+    def plot(self, path: str="", figsize: tuple=(8,4)):
         """Abstract method to plot the output array.
             Needs to be implemented by inherited classes
 
         Args:
-            dir (str, optional): If set stores the plot in the directory. Defaults to "".
+            path (str, optional): If set stores the plot in the directory. Defaults to "".
             figsize (tuple, optional): Te size of the figure to plot. Defaults to (8,4).
         """
-        pass
 
     def dim(self, label: str) -> int:
         """Returns the size of the requested dimension
@@ -154,4 +153,4 @@ def getDim(label: str, processor: Processor) -> int:
         idx = processor.O_FORMAT.index(label)
     except:
         raise ValueError(f"Label {label} does not exists in {processor.O_FORMAT}")
-    return processor.shape[idx]
\ No newline at end of file
+    return processor.shape[idx]
diff --git a/pafsim/processor/beamformer.py b/pafsim/processor/beamformer.py
index 4b1d641..6d83270 100644
--- a/pafsim/processor/beamformer.py
+++ b/pafsim/processor/beamformer.py
@@ -21,7 +21,7 @@ class Beamformer(Processor):
         """Construct a Beamformer object
 
         Args:
-            name (str): The ID of the Beamformer
+            name (str): The unique name of the Beamformer
         """
         super().__init__(name, **kwargs)
 
@@ -76,11 +76,11 @@ class Beamformer(Processor):
             return np.matmul(i, w.conj()).transpose(0,3,1,2)
         return np.dot(self.pp[0].output, self.pp[1].output.T)
 
-    def plot(self, dir="", figsize=(8,4)):
+    def plot(self, path="", figsize=(8,4)):
         """Plotting function to plot the beamformed time series
 
         Args:
-            dir (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
+            path (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
             figsize (tuple, optional): Size of the plotted figure. Defaults to (8,4).
         """
         sub = []
@@ -93,8 +93,8 @@ class Beamformer(Processor):
                 sub[b*self.dim('P')+p].set_ylabel("Channel")
                 plt.imshow(np.log(np.abs(self.output[:, b, p, :])), interpolation='nearest', aspect='auto')
         fig.tight_layout()
-        if dir:
-            fig.savefig(dir + self.name + '.png')
+        if path:
+            fig.savefig(path + self.name + '.png')
             plt.close()
         else:
             plt.show()
diff --git a/pafsim/processor/calibrator.py b/pafsim/processor/calibrator.py
index d832d5b..8a1914a 100644
--- a/pafsim/processor/calibrator.py
+++ b/pafsim/processor/calibrator.py
@@ -16,10 +16,11 @@ class WeightGenerator(Processor):
         """Construct a WeightGenerator object
 
         Args:
-            name (str): The ID of the WeightGenerator
+            name (str): The unique name of theWeightGenerator
         kwargs:
-            beams: The number of beams to produce. This parameter is implicitly determined
+            beams (int): The number of beams to produce. This parameter is implicitly determined
                 when using the maxsnr processing
+            mode (str): maxsnr -> maximum signal-to-noise algorithm (default)
         """
         super().__init__(name, **kwargs)
         self.beam = kwargs.get('beams', 2)
@@ -103,11 +104,11 @@ class WeightGenerator(Processor):
         return data
 
 
-    def plot(self, dir="", figsize=(8,4)):
+    def plot(self, path="", figsize=(8,4)):
         """Plotting function to plot the amplitude of the beamweight
 
         Args:
-            dir (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
+            path (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
             figsize (tuple, optional): Size of the plotted figure. Defaults to (8,4).
         """
         sub = []
@@ -120,8 +121,8 @@ class WeightGenerator(Processor):
                 sub[b*self.dim('P')+p].set_ylabel("Channel")
                 sub[b*self.dim('P')+p].imshow(np.log(np.abs(self.output[b, :, p])),interpolation='nearest', aspect='auto')
         fig.tight_layout()
-        if dir:
-            fig.savefig(dir + self.name + '.png')
+        if path:
+            fig.savefig(path + self.name + '.png')
             plt.close()
         else:
-            plt.show()
\ No newline at end of file
+            plt.show()
diff --git a/pafsim/processor/channelizer.py b/pafsim/processor/channelizer.py
index fef9c37..4af9e00 100644
--- a/pafsim/processor/channelizer.py
+++ b/pafsim/processor/channelizer.py
@@ -17,7 +17,7 @@ class Channelizer(Processor):
         """Construct a Generator object
 
         Args:
-            name (str): The ID of the Generator
+            name (str): The unique name of theGenerator
         kwargs:
             taps (int): Number of taps used for the filter. Defaults to 2
             channels (int): Number of channels to produce. Defaults to 32
@@ -35,6 +35,7 @@ class Channelizer(Processor):
         self.pd = kwargs.get('pd', 1)
 
         # self.stop_min = kwargs.get('stop_min', 0.00001)
+        self.coeff = np.array()
         self.os_factor = self.pn / self.pd
         self.tap_length = int(self.channels * 2)
         self.channel_bw = 1/self.channels
@@ -97,8 +98,8 @@ class Channelizer(Processor):
             if end > x.size:
                 break
             x_p = x[start:end].reshape(self.taps, self.tap_length).T
-            sum = (x_p * self.coeff).sum(axis=1)
-            y.append(sum)
+            summed = (x_p * self.coeff).sum(axis=1)
+            y.append(summed)
             offset+=1
         return np.asarray(y)
 
@@ -138,11 +139,11 @@ class Channelizer(Processor):
         reshaped = self.pp[0].output.reshape(self.dim('A'), self.dim('P'), -1, self.channels*2)
         return np.fft.rfft(reshaped * self.coeff, axis=-1).transpose(3,0,1,2)[1:]
 
-    def plot(self, dir="", figsize=(8,4)):
+    def plot(self, path="", figsize=(8,4)):
         """Plotting function to plot the channeles vs time
 
         Args:
-            dir (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
+            path (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
             figsize (tuple, optional): Size of the plotted figure. Defaults to (8,4).
         """
         sub = []
@@ -163,8 +164,8 @@ class Channelizer(Processor):
         sub[self.dim('A') * self.dim('P')].plot(np.arange(0,len(self.coeff)),
                                                 self.coeff.flatten())
         fig.tight_layout()
-        if dir:
-            fig.savefig(dir + self.name + '.png')
+        if path:
+            fig.savefig(path + self.name + '.png')
             plt.close()
         else:
             plt.show()
diff --git a/pafsim/processor/correlator.py b/pafsim/processor/correlator.py
index 34f8a10..5f4b4de 100644
--- a/pafsim/processor/correlator.py
+++ b/pafsim/processor/correlator.py
@@ -16,7 +16,7 @@ class Correlator(Processor):
         """Construct a Correlator object
 
         Args:
-            name (str):  The ID of the Correlator
+            name (str):  The unique name of theCorrelator
         kwargs:
             acc (int): Number of ACMs to create.
             mode (str): 'acm' -> correlate and build covariance matrices (default)
@@ -58,12 +58,12 @@ class Correlator(Processor):
             o[n] = np.matmul(x[..., n*nacc:(n+1)*nacc], np.conj(x[..., n*nacc:(n+1)*nacc]).transpose(0,1,3,2))
         return o
 
-    def plot(self, dir="", figsize=(8,4)):
+    def plot(self, path="", figsize=(8,4)):
         """Plotting function to plot the amplitude of covariance matrices. Just a sub-set is
         randomly chosen
 
         Args:
-            dir (str, optional): If not set to "" it stores the plot in the given directory.
+            path (str, optional): If not set to "" it stores the plot in the given directory.
                 Defaults to "".
             figsize (tuple, optional): Size of the plotted figure. Defaults to (8,4).
         """
@@ -75,8 +75,8 @@ class Correlator(Processor):
             sub[i].set_title("Channel " + str(f))
             sub[i].imshow(np.log(np.abs(self.output[0, f, 0])),interpolation='nearest', aspect='auto')
         fig.tight_layout()
-        if dir:
-            fig.savefig(dir + self.name + '.png')
+        if path:
+            fig.savefig(path + self.name + '.png')
             plt.close()
         else:
-            plt.show()
\ No newline at end of file
+            plt.show()
diff --git a/pafsim/processor/generator.py b/pafsim/processor/generator.py
index b86bf61..819ef0c 100644
--- a/pafsim/processor/generator.py
+++ b/pafsim/processor/generator.py
@@ -1,11 +1,10 @@
-import numpy as np
-from matplotlib import pyplot as plt
-from pafsim.processor._processor import Processor
 import os
+import typing as ty
+from matplotlib import pyplot as plt
 import h5py as h5
 import numpy as np
-import typing as ty
 
+from pafsim.processor._processor import Processor
 class FrontendSimFile(h5.File):
     """
     The FrontendSimFile is class to read ACMs and properties from an HDF5 file produced
@@ -95,9 +94,8 @@ class FrontendSimFile(h5.File):
         """
         if self.exists(path):
             return self[path]
-        else:
-            print(f"Path {path} does not exists in dataset")
-            return None
+        print(f"Path {path} does not exists in dataset")
+        return None
 
     def datasets(self, base_path: str, names: set, suffix: str="acm") -> ty.List[np.ndarray]:
         """Returns multiple datasets with matching names and suffix within the base path.
@@ -170,7 +168,7 @@ class Generator(Processor):
         """Construct a Generator object
 
         Args:
-            name (str): The ID of the Generator
+            name (str): The unique name of theGenerator
         kwargs:
             dataset (str): Path to an HDF5 file containing the output of the PAF Frontend simulator
             noise (str): Names of the noise datasets to use for the time series generation
@@ -185,6 +183,7 @@ class Generator(Processor):
         self.rfi = self.conf.get("rfi", [0])
         self.width = self.conf.get("width", 10000)
         self.fs = 0
+        self.duration = 0
         self.reader = FrontendSimFile(self.dataset)
         self.antennas = self.reader.nelements
         self.pol = self.reader.npol
@@ -242,11 +241,12 @@ class Generator(Processor):
             out[:, 0, t*nchan:(t+1)*nchan] = np.fft.ifft(channelized_array[:,:,t], axis=0).T
         return out
 
-    def plot(self, dir="", figsize=(8,4)):
+#pylint: disable=R0801
+    def plot(self, path="", figsize=(8,4)):
         """Plotting function to plot the time series
 
         Args:
-            dir (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
+            path (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
             figsize (tuple, optional): Size of the plotted figure. Defaults to (8,4).
         """
         sub = []
@@ -260,8 +260,8 @@ class Generator(Processor):
                 sub[a*self.pol+p].set_xlabel("Time [s]")
                 sub[a*self.pol+p].plot(taxis, self.output[a])
         fig.tight_layout()
-        if dir:
-            fig.savefig(dir + self.name + '.png')
+        if path:
+            fig.savefig(path + self.name + '.png')
             plt.close()
         else:
             plt.show()
diff --git a/pafsim/processor/waveform.py b/pafsim/processor/waveform.py
index 45cbbfa..9578394 100644
--- a/pafsim/processor/waveform.py
+++ b/pafsim/processor/waveform.py
@@ -1,5 +1,5 @@
+from scipy import signal
 import numpy as np
-import scipy.signal as signal
 import matplotlib.pyplot as plt
 
 
@@ -15,7 +15,7 @@ class WaveformGenerator(Processor):
     def __init__(self, name:str, **kwargs):
         """Construct a WaveformGenerator object
         Args:
-            name (str): The ID of the Generator
+            name (str): The unique name of theGenerator
         kwargs:
             fs (float): The sampling rate at which to generate the time series [Hz]. Defaults to 1000 Hz
             duration (float): The duration of the signal [seconds]. Defaults to 1s
@@ -83,7 +83,7 @@ class WaveformGenerator(Processor):
         o = np.zeros(self.shape)
         for a in range(self.antennas):
             for p in range(self.pol):
-                if type(self.freq) == list:
+                if isinstance(self.freq, list):
                     for f in self.freq:
                         o[a,p] += self.gain * np.sin(2 * np.pi * f * self.t + self.phase) + self.offset
                 else:
@@ -125,11 +125,11 @@ class WaveformGenerator(Processor):
     # def sawtooth(self):
     #     return self.gain * signal.sawtooth(2 * np.pi * self.period * self.t)
 
-    def plot(self, dir="", figsize=(8,4)):
+    def plot(self, path="", figsize=(8,4)):
         """Plotting function to plot the time series
 
         Args:
-            dir (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
+            path (str, optional): If not set to "" it stores the plot in the given directory. Defaults to "".
             figsize (tuple, optional): Size of the plotted figure. Defaults to (8,4).
         """
         sub = []
@@ -142,8 +142,8 @@ class WaveformGenerator(Processor):
                 sub[a*self.pol+p].set_xlabel("Time [s]")
                 sub[a*self.pol+p].plot(self.t, self.output[a,p])
         fig.tight_layout()
-        if dir:
-            fig.savefig(dir + self.name + '.png')
+        if path:
+            fig.savefig(path + self.name + '.png')
             plt.close()
         else:
-            plt.show()
\ No newline at end of file
+            plt.show()
diff --git a/pafsim/vector.py b/pafsim/vector.py
index 326b941..ce308d6 100644
--- a/pafsim/vector.py
+++ b/pafsim/vector.py
@@ -45,8 +45,8 @@ class Vector():
         Args:
             path (str): The location where to store the Vector
         """
-        self.path = path + self.name + '.pk1'
-        with open(self.path, 'wb') as f:
+        path = path + self.name + '.pk1'
+        with open(path, 'wb') as f:
             pickle.dump(self, f, pickle.HIGHEST_PROTOCOL)
 
     def load(self, path: str):
@@ -56,4 +56,6 @@ class Vector():
             path (str): The location where the vector was stored
         """
         with open(path, 'rb') as f:
-            self = pickle.load(f)
\ No newline at end of file
+            loaded = pickle.load(f)
+            self.__dict__.update(loaded.__dict__)
+
-- 
GitLab