Repository: bcliang/gamry-parser

Issue:
Change: Impl class properties
Replace class getter methods with `@property` decorators. 

- get_curve_count
- get_curve_indices
- get_curve_numbers
- get_curves
- get_header
- get_experiment_type
- get_ocv_curve
- get_ocv_value

Note: this will be a breaking change.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 78424da..ecd0b0d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - 
 
 ### Changed
-- 
+- [#46](https://github.com/bcliang/gamry-parser/pull/46) Change: class property methods
 
 ### Added
 - 
diff --git a/README.md b/README.md
index 0d7563c..8ecea3a 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ $ python setup.py install
 
 ### Usage
 
-The provided Usage example loads a CV DTA file two ways, and demonstrates the utility of custom functions within the CyclicVoltammetry subclass (`get_v_range`, `get_scan_rate`)
+The provided Usage example loads a CV DTA file two ways, and demonstrates the utility of class properties within the CyclicVoltammetry subclass (`v_range`, `scan_rate`)
 
 ```bash
 $ python usage.py
@@ -51,12 +51,12 @@ file = '/enter/the/file/path.dta'
 gp = parser.GamryParser()
 gp.load(filename=file)
 
-print("experiment type: {}".format(gp.get_experiment_type()))
-print("loaded curves: {}".format(gp.get_curve_count()))
+print("experiment type: {}".format(gp.experiment_type))
+print("loaded curves: {}".format(gp.curve_count))
 
-curve_index = random.randint(1,gp.get_curve_count())
+curve_index = random.randint(1,gp.curve_count)
 print("showing curve #{}".format(curve_index))
-print(gp.get_curve_data(curve_index))
+print(gp.curve(curve_index))
 ```
 
 #### ChronoAmperometry Example
@@ -72,7 +72,7 @@ import random
 file = '/enter/the/file/path.dta'
 ca = parser.ChronoAmperometry(to_timestamp=True)
 ca.load(filename=file)
-print(ca.get_curve_data())
+print(ca.curve())
 ```
 
 #### Demos
@@ -90,7 +90,7 @@ ipython notebook demonstration scripts are included in the `demo` folder.
 
 #### Additional Examples
 
-Similar procedure should be followed for using the `gamry_parser.CyclicVoltammetry()`, `gamry_parser.Impedance()`, `gamry_parser.OpenCircuitPotential()`, and `gamry_parser.VFP600()` parser subclasses. Take a look at `usage.py` and in `tests/` for some additional usage examples.
+Similar procedure should be followed for using the `gamry_parser.CyclicVoltammetry()`, `gamry_parser.Impedance()`, `gamry_parser.OpenCircuitPotential()`, `gamry_parser.SquareWaveVoltammetry()` and `gamry_parser.VFP600()` parser subclasses. Take a look at `usage.py` and in `tests/` for some additional usage examples.
 
 ## Development
 
@@ -104,6 +104,7 @@ Similar procedure should be followed for using the `gamry_parser.CyclicVoltammet
   │   ├── eispot.py             # Impedance() experiment parser
   |   ├── gamryparser.py        # GamryParser: generic DTA file parser
   │   ├── ocp.py                # OpenCircuitPotential() experiment parser
+  │   ├── squarewave.py         # SquareWaveVoltammetry() experiment parser
   |   └── vfp600.py             # VFP600() parses experiment data generated by the Gamry VFP600 LabView Frontend. 
   ├── tests                     # unit tests and test data
   |   └── ...
diff --git a/demo/notebook_cyclicvoltammetry.ipynb b/demo/notebook_cyclicvoltammetry.ipynb
index ac4f4b6..7a82e7d 100644
--- a/demo/notebook_cyclicvoltammetry.ipynb
+++ b/demo/notebook_cyclicvoltammetry.ipynb
@@ -1,6 +1,6 @@
 {
   "nbformat": 4,
-  "nbformat_minor": 0,
+  "nbformat_minor": 2,
   "metadata": {
     "colab": {
       "name": "Gamry-Parser CyclicVoltammetry Example",
@@ -16,13 +16,8 @@
   },
   "cells": [
     {
-      "metadata": {
-        "id": "WF08aBjO8Lvh",
-        "colab_type": "code",
-        "cellView": "form",
-        "colab": {}
-      },
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title Imports, initial setup (Ctrl+F9 to run all)\n",
         "from google.colab import files \n",
@@ -43,17 +38,17 @@
         "  \n",
         "print('Done.')"
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "qPt8TDQgA0h6",
+        "id": "WF08aBjO8Lvh",
         "colab_type": "code",
-        "cellView": "code",
+        "cellView": "form",
         "colab": {}
-      },
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "\"\"\"\n",
         "### SCRIPT CONFIGURATION SETTINGS ###\n",
@@ -74,17 +69,17 @@
         "\n",
         "print('Done.')"
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "3cK_P2Clmksm",
+        "id": "qPt8TDQgA0h6",
         "colab_type": "code",
-        "cellView": "form",
+        "cellView": "code",
         "colab": {}
-      },
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title DTA File\n",
         "if upload_files:\n",
@@ -94,68 +89,65 @@
         "  experiment = [\"cv_data.dta\"]\n",
         "\n"
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "mxwEyYWICCYs",
+        "id": "3cK_P2Clmksm",
         "colab_type": "code",
         "cellView": "form",
         "colab": {}
-      },
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title Load and Plot Curve Data\n",
         "\n",
         "for f in experiment:\n",
-        "  p.load(f)\n",
-        "  \n",
-        "  # generate a plot based on the first curve listed in the file.\n",
-        "  curve_count = p.get_curve_count()\n",
-        "  data = p.get_curve_data(curve_count) \n",
-        "  \n",
-        "  # print to screen\n",
-        "  print('Data Preview: Last Curve')\n",
-        "  print(data.iloc[:5])\n",
-        "  \n",
-        "  # matplotlib fig\n",
-        "  if show_plots:\n",
-        "    fig, ax = plt.subplots(figsize=(18,8))\n",
-        "    for i in range(curve_count):\n",
-        "      data = p.get_curve_data(i)\n",
-        "      trace = ax.plot(data['Vf'], data['Im']*1e6, label=\"curve {}\".format(i))\n",
-        "      \n",
-        "      \n",
-        "    ax.set_title(\"{}, {} curves\".format(f, curve_count), fontsize=18)\n",
-        "    ax.set_xlabel('Potential (V)')\n",
-        "    ax.set_ylabel('Current (A)', fontsize=14)\n",
-        "    plt.show()"
+        "    p.load(f)\n",
+        "    \n",
+        "    # generate a plot based on the first curve listed in the file.\n",
+        "    data = p.curve(p.curve_count) \n",
+        "    \n",
+        "    # print to screen\n",
+        "    print('Data Preview: Last Curve')\n",
+        "    print(data.iloc[:5])\n",
+        "    \n",
+        "    # matplotlib fig\n",
+        "    if show_plots:\n",
+        "        fig, ax = plt.subplots(figsize=(18,8))\n",
+        "        for i in range(p.curve_count):\n",
+        "            data = p.curve(i)\n",
+        "            trace = ax.plot(data['Vf'], data['Im']*1e6, label=\"curve {}\".format(i))\n",
+        "            \n",
+        "        ax.set_title(\"{}, {} curves\".format(f, p.curve_count), fontsize=18)\n",
+        "        ax.set_xlabel('Potential (V)')\n",
+        "        ax.set_ylabel('Current (A)', fontsize=14)\n",
+        "        plt.show()"
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "AQc4jnhlURDV",
+        "id": "mxwEyYWICCYs",
         "colab_type": "code",
-        "colab": {},
-        "cellView": "form"
-      },
+        "cellView": "form",
+        "colab": {}
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title Compare a specific curve across loaded files\n",
         "\n",
         "fig, ax = plt.subplots(figsize=(18,8))\n",
         "    \n",
         "for f in experiment:\n",
-        "  p.load(f)\n",
-        "  \n",
-        "  # generate a plot based on the first curve listed in the file.\n",
-        "  curve_count = p.get_curve_count()\n",
-        "  if curve_count > compare_curves:\n",
-        "    data = p.get_curve_data(compare_curves) \n",
-        "    trace = ax.plot(data['Vf'], data['Im']*1e6, label=\"file {}\".format(f))\n",
+        "    p.load(f)\n",
+        "    \n",
+        "    # generate a plot based on the first curve listed in the file.\n",
+        "    if p.curve_count > compare_curves:\n",
+        "        data = p.curve(compare_curves) \n",
+        "        trace = ax.plot(data['Vf'], data['Im']*1e6, label=\"file {}\".format(f))\n",
         "   \n",
         "ax.set_title(\"CyclicVoltammetry Test, Compare Curve #{}\".format(compare_curves), fontsize=18)\n",
         "ax.set_xlabel('Potential (V)')\n",
@@ -163,17 +155,17 @@
         "ax.legend()\n",
         "plt.show()"
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "ZPXwezuvmgZ0",
+        "id": "AQc4jnhlURDV",
         "colab_type": "code",
-        "cellView": "form",
-        "colab": {}
-      },
+        "colab": {},
+        "cellView": "form"
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title Download All Curves, All Experiments\n",
         "\n",
@@ -182,18 +174,21 @@
         "for f in experiment:\n",
         "  p.load(f)\n",
         "  \n",
-        "  # use get_curves() to retrieve all curves\n",
-        "  data = p.get_curves()\n",
-        "  \n",
-        "  for df in data:\n",
+        "  # use the curves @property to retrieve all curve data\n",
+        "  for df in p.curves:\n",
         "    aggreg = aggreg.append(df)\n",
         "    \n",
         "  \n",
         "aggreg.to_csv('results.csv')\n",
         "files.download('results.csv')"
       ],
-      "execution_count": 0,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "ZPXwezuvmgZ0",
+        "colab_type": "code",
+        "cellView": "form",
+        "colab": {}
+      }
     }
   ]
 }
\ No newline at end of file
diff --git a/demo/notebook_cyclicvoltammetry_peakdetect.ipynb b/demo/notebook_cyclicvoltammetry_peakdetect.ipynb
index 0f037cb..87f3309 100644
--- a/demo/notebook_cyclicvoltammetry_peakdetect.ipynb
+++ b/demo/notebook_cyclicvoltammetry_peakdetect.ipynb
@@ -1,6 +1,6 @@
 {
   "nbformat": 4,
-  "nbformat_minor": 0,
+  "nbformat_minor": 2,
   "metadata": {
     "colab": {
       "name": "Gamry-Parser CyclicVoltammetry Peak Detection Example",
@@ -15,10 +15,7 @@
   "cells": [
     {
       "cell_type": "code",
-      "metadata": {
-        "id": "YyeVlSYkhahF",
-        "cellView": "form"
-      },
+      "execution_count": null,
       "source": [
         "#@title Imports, initial setup (Ctrl+F9 to run all)\n",
         "import os\n",
@@ -42,15 +39,15 @@
         "\n",
         "print('Done.')"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "YyeVlSYkhahF",
+        "cellView": "form"
+      }
     },
     {
       "cell_type": "code",
-      "metadata": {
-        "id": "ZGoqracvk9q2",
-        "cellView": "form"
-      },
+      "execution_count": null,
       "source": [
         "\"\"\"\n",
         "### SCRIPT CONFIGURATION SETTINGS ###\n",
@@ -70,9 +67,9 @@
         "file_label_xform = \"[51:]\" #@param {type:\"string\"}\n",
         "\n",
         "# create a \"results\" dataframe to contain the values we care about\n",
-        "data_df = pandas.DataFrame()\n",
-        "settings_df = pandas.DataFrame()\n",
-        "peaks_df = pandas.DataFrame()\n",
+        "data_df = pd.DataFrame()\n",
+        "settings_df = pd.DataFrame()\n",
+        "peaks_df = pd.DataFrame()\n",
         "\n",
         "# identify files to process\n",
         "files = [f for f in os.listdir(data_path) if \n",
@@ -80,15 +77,15 @@
         "    len(re.findall(file_pattern.upper(), f.upper())) > 0\n",
         "    ]\n"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "ZGoqracvk9q2",
+        "cellView": "form"
+      }
     },
     {
       "cell_type": "code",
-      "metadata": {
-        "cellView": "form",
-        "id": "8MFNF2Qz6lef"
-      },
+      "execution_count": null,
       "source": [
         "#@markdown **Process Data and Detect Peaks**\n",
         "\n",
@@ -116,7 +113,7 @@
         "    # load the dta file using gamry parser\n",
         "    gp.load(filename=os.path.join(data_path, file))\n",
         "\n",
-        "    is_cv = gp.get_header().get(\"TAG\") == \"CV\"\n",
+        "    is_cv = gp.experiment_type == \"CV\"\n",
         "    if not is_cv:\n",
         "        # if the DTA file is a different experiment type, skip it and move to the next file.\n",
         "        print(\"File `{}` is not a CV experiment. Skipping\".format(file))\n",
@@ -128,17 +125,17 @@
         "    cv.load()\n",
         "    for curve_num in curves_to_sample:\n",
         "        print(\"\\tProcessing Curve #{}\".format(curve_num))\n",
-        "        v1, v2 = cv.get_v_range()\n",
-        "        settings = pandas.DataFrame({\n",
+        "        v1, v2 = cv.v_range\n",
+        "        settings = pd.DataFrame({\n",
         "            \"label\": my_label,\n",
-        "            \"curves\": cv.get_curve_count(),\n",
+        "            \"curves\": cv.curve_count,\n",
         "            \"v1_mV\": v1*1000,\n",
         "            \"v2_mV\": v2*1000,\n",
-        "            \"rate_mV\": cv.get_scan_rate(),\n",
+        "            \"rate_mV\": cv.scan_rate,\n",
         "        }, index=[0])\n",
         "        settings_df = settings_df.append(settings)\n",
         "\n",
-        "        data = copy.deepcopy(cv.get_curve_data(curve=curve_num))\n",
+        "        data = copy.deepcopy(cv.curve(curve=curve_num))\n",
         "        data.Im = data.Im*1e9\n",
         "        data.Vf = data.Vf*1e3\n",
         "        data[\"label\"] = my_label #\"{:03d}-{}\".format(index, curve_num)\n",
@@ -146,7 +143,7 @@
         "        data_df = data_df.append(data)\n",
         "\n",
         "        # find peaks in the data\n",
-        "        dV = cv.get_scan_rate()  # in mV\n",
+        "        dV = cv.scan_rate  # in mV\n",
         "        peak_width = int(peak_width_mV/dV)\n",
         "        peaks_pos, props_pos = find_peaks(\n",
         "            data.Im, \n",
@@ -180,15 +177,15 @@
         "print(\"\\nPeaks Detected\")\n",
         "print(peaks_df.to_string(index=False))"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "cellView": "form",
+        "id": "8MFNF2Qz6lef"
+      }
     },
     {
       "cell_type": "code",
-      "metadata": {
-        "id": "Ulne80RrpBrW",
-        "cellView": "form"
-      },
+      "execution_count": null,
       "source": [
         "#@markdown **I-V plot**: Overlay the loaded CyclicVoltammetry Curves\n",
         "\n",
@@ -246,8 +243,11 @@
         "}\n",
         "fig.show(config=config)"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "Ulne80RrpBrW",
+        "cellView": "form"
+      }
     }
   ]
 }
\ No newline at end of file
diff --git a/demo/notebook_gamry_parser.ipynb b/demo/notebook_gamry_parser.ipynb
index 6c4130d..1da0304 100644
--- a/demo/notebook_gamry_parser.ipynb
+++ b/demo/notebook_gamry_parser.ipynb
@@ -1,6 +1,6 @@
 {
   "nbformat": 4,
-  "nbformat_minor": 0,
+  "nbformat_minor": 2,
   "metadata": {
     "colab": {
       "name": "Gamry-Parser GamryParser Example",
@@ -16,13 +16,8 @@
   },
   "cells": [
     {
-      "metadata": {
-        "id": "WF08aBjO8Lvh",
-        "colab_type": "code",
-        "cellView": "form",
-        "colab": {}
-      },
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title Imports, initial setup (Ctrl+F9 to run all)\n",
         "from google.colab import files \n",
@@ -43,17 +38,17 @@
         "  \n",
         "print('Done.')"
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "qPt8TDQgA0h6",
+        "id": "WF08aBjO8Lvh",
         "colab_type": "code",
-        "cellView": "code",
+        "cellView": "form",
         "colab": {}
-      },
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "\"\"\"\n",
         "### SCRIPT CONFIGURATION SETTINGS ###\n",
@@ -74,17 +69,17 @@
         "\n",
         "print('Done.')"
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "3cK_P2Clmksm",
+        "id": "qPt8TDQgA0h6",
         "colab_type": "code",
-        "cellView": "form",
+        "cellView": "code",
         "colab": {}
-      },
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title DTA File\n",
         "if upload_files:\n",
@@ -94,17 +89,17 @@
         "  experiment = [\"chronoa_data.dta\"]\n",
         "\n"
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "mxwEyYWICCYs",
+        "id": "3cK_P2Clmksm",
         "colab_type": "code",
         "cellView": "form",
         "colab": {}
-      },
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title Load and Plot Final Curve\n",
         "\n",
@@ -112,8 +107,8 @@
         "  p.load(f)\n",
         "  \n",
         "  # generate a plot based on the first curve listed in the file.\n",
-        "  curve_count = p.get_curve_count()\n",
-        "  data = p.get_curve_data(curve_count) \n",
+        "  curve_count = p.curve_count\n",
+        "  data = p.curve(curve_count) \n",
         "  \n",
         "  # print to screen\n",
         "  print('Display Curve #{} first 5 rows...'.format(curve_count))\n",
@@ -137,17 +132,17 @@
         "    plt.show()\n",
         "    "
       ],
-      "execution_count": 0,
-      "outputs": []
-    },
-    {
+      "outputs": [],
       "metadata": {
-        "id": "ZPXwezuvmgZ0",
+        "id": "mxwEyYWICCYs",
         "colab_type": "code",
         "cellView": "form",
         "colab": {}
-      },
+      }
+    },
+    {
       "cell_type": "code",
+      "execution_count": null,
       "source": [
         "#@title Download All Curves, All Experiments\n",
         "\n",
@@ -155,16 +150,20 @@
         "\n",
         "for f in experiment:\n",
         "  p.load(f)\n",
-        "  # use get_curves() to retrieve all curves\n",
-        "  data = p.get_curves()\n",
-        "  for df in data:\n",
+        "  # use the curves @property to retrieve all curves\n",
+        "  for df in p.curves:\n",
         "    aggreg.append(df)\n",
         "\n",
         "aggreg.to_csv('results.csv')\n",
         "files.download('results.csv')"
       ],
-      "execution_count": 0,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "ZPXwezuvmgZ0",
+        "colab_type": "code",
+        "cellView": "form",
+        "colab": {}
+      }
     }
   ]
 }
\ No newline at end of file
diff --git a/demo/notebook_potentiostatic_eis.ipynb b/demo/notebook_potentiostatic_eis.ipynb
index 77a5562..648957a 100644
--- a/demo/notebook_potentiostatic_eis.ipynb
+++ b/demo/notebook_potentiostatic_eis.ipynb
@@ -1,6 +1,6 @@
 {
   "nbformat": 4,
-  "nbformat_minor": 0,
+  "nbformat_minor": 2,
   "metadata": {
     "colab": {
       "name": "Gamry-Parser Potentiostatic EIS Example",
@@ -19,8 +19,6 @@
     {
       "cell_type": "code",
       "execution_count": null,
-      "metadata": {},
-      "outputs": [],
       "source": [
         "#@title Imports, initial setup (Ctrl+F9 to run all)\n",
         "try:\n",
@@ -41,14 +39,13 @@
         "z = gamry_parser.Impedance()\n",
         "\n",
         "print('Done.')"
-      ]
+      ],
+      "outputs": [],
+      "metadata": {}
     },
     {
       "cell_type": "code",
-      "metadata": {
-        "id": "7f1iTOecIISA",
-        "cellView": "form"
-      },
+      "execution_count": null,
       "source": [
         "\"\"\"\n",
         "### SCRIPT CONFIGURATION SETTINGS ###\n",
@@ -58,6 +55,9 @@
         "#@markdown Where are the Gamry DTA files located?\n",
         "file_path = \"/path/to/gamry/files/\" #@param {type:\"string\"}\n",
         "\n",
+        "#@markdown What's the title of this experiment?\n",
+        "experiment_name = \"EIS Experimentt 1\" #@param {type:\"string\"}\n",
+        "\n",
         "#@markdown Which of the DTA files do we want to compare? (Regular expression matching)\n",
         "file_pattern = \"EIS\" #@param {type:\"string\"}\n",
         "\n",
@@ -98,12 +98,11 @@
         "    z.load(f)\n",
         "\n",
         "    # process data header metadata\n",
-        "    header = z.get_header()\n",
-        "    start_time = pd.Timestamp(\"{} {}\".format(header.get(\"DATE\"), header.get(\"TIME\")))\n",
-        "    print('{} [{}] ocp={}'.format(start_time, name, z.get_ocv_value()))\n",
+        "    start_time = pd.Timestamp(\"{} {}\".format(z.header.get(\"DATE\"), z.header.get(\"TIME\")))\n",
+        "    print('{} [{}] ocp={}'.format(start_time, name, z.ocv))\n",
         "    \n",
         "    # extract EIS curve\n",
-        "    res = z.get_curve_data()\n",
+        "    res = z.curve()\n",
         "    \n",
         "    start_times.append(start_time)\n",
         "    df_mag[name] = res['Zmod']\n",
@@ -139,15 +138,15 @@
         "    print(\"\\n    Showing Z_mag @ {} Hz [actual={:0.2f} Hz]\".format(freq, df_mag.index[row_index]))\n",
         "    print(df_mag.iloc[row_index])\n"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "7f1iTOecIISA",
+        "cellView": "form"
+      }
     },
     {
       "cell_type": "code",
-      "metadata": {
-        "id": "t850lx00MlBL",
-        "cellView": "form"
-      },
+      "execution_count": null,
       "source": [
         "#@markdown **Bode Plot**: Display Zmag, Zphase vs. Freq\n",
         "from plotly.subplots import make_subplots\n",
@@ -222,15 +221,15 @@
         "}\n",
         "fig.show(config=config)"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "t850lx00MlBL",
+        "cellView": "form"
+      }
     },
     {
       "cell_type": "code",
-      "metadata": {
-        "id": "V6LKqpiECgb5",
-        "cellView": "form"
-      },
+      "execution_count": null,
       "source": [
         "#@markdown **Polar Coordinate Plot**: Display -Zimag vs. Zreal\n",
         "\n",
@@ -286,15 +285,15 @@
         "}\n",
         "fig.show(config=config)"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "V6LKqpiECgb5",
+        "cellView": "form"
+      }
     },
     {
       "cell_type": "code",
-      "metadata": {
-        "id": "JVPwUpRcSdXM",
-        "cellView": "form"
-      },
+      "execution_count": null,
       "source": [
         "#@markdown **Time-series Plot (Z_mag)**\n",
         "from plotly.subplots import make_subplots\n",
@@ -356,15 +355,7 @@
         "        'anchor': 'x',\n",
         "        # 'type': 'log'\n",
         "    },\n",
-        "    # 'xaxis2': {\n",
-        "    #     'title': 'Frequency, Hz',\n",
-        "    #     # 'type': 'log',\n",
-        "    #     'matches': 'x'\n",
-        "    # },\n",
         "    'yaxis': source_yaxis_config,\n",
-        "    # 'yaxis2': {\n",
-        "    #     'title': 'Phase, deg',\n",
-        "    # },\n",
         "    'legend': {'x': 0.85, 'y': 0.97},\n",
         "    'margin': dict(l=30, r=20, t=60, b=20),\n",
         "    'width': 1200,\n",
@@ -378,15 +369,15 @@
         "}\n",
         "fig.show(config=config)"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "JVPwUpRcSdXM",
+        "cellView": "form"
+      }
     },
     {
       "cell_type": "code",
-      "metadata": {
-        "id": "V17rqu3-A74s",
-        "cellView": "form"
-      },
+      "execution_count": null,
       "source": [
         "#@markdown WORK IN PROGRESS **Simulation**: Fit collected data to an electrochemical model\n",
         "equivalent_model = \"Body Impedance Model\" #@param [\"Randles\", \"Randles + Diffusion\", \"Randles + Corrosion\", \"Body Impedance Model\"]\n",
@@ -568,8 +559,11 @@
         "    fig.update_layout(layout)\n",
         "    fig.show()"
       ],
-      "execution_count": null,
-      "outputs": []
+      "outputs": [],
+      "metadata": {
+        "id": "V17rqu3-A74s",
+        "cellView": "form"
+      }
     }
   ]
 }
\ No newline at end of file
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 96a2698..ab2fa79 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,2 +1,4 @@
 setuptools
-pytest
\ No newline at end of file
+pytest
+tox
+coverage 
\ No newline at end of file
diff --git a/gamry_parser/chronoa.py b/gamry_parser/chronoa.py
index f5e86e0..3580531 100644
--- a/gamry_parser/chronoa.py
+++ b/gamry_parser/chronoa.py
@@ -6,7 +6,7 @@ import re
 class ChronoAmperometry(parser.GamryParser):
     """Load a ChronoAmperometry experiment generated in Gamry EXPLAIN format."""
 
-    def get_curve_data(self, curve: int = 0):
+    def curve(self, curve: int = 0):
         """retrieve chronoamperometry experiment data
 
         Args:
@@ -20,10 +20,11 @@ class ChronoAmperometry(parser.GamryParser):
         """
 
         assert self.loaded, "DTA file not loaded. Run ChronoAmperometry.load()"
-        df = self.curves[curve]
+        df = self._curves[curve]
         return df[["T", "Vf", "Im"]]
 
-    def get_sample_time(self):
+    @property
+    def sample_time(self):
         """retrieve the programmed sample period
 
         Args:
@@ -33,11 +34,10 @@ class ChronoAmperometry(parser.GamryParser):
             float: sample period of the potentiostat (in seconds)
 
         """
+        return self._header.get("SAMPLETIME", None)
 
-        assert self.loaded, "DTA file not loaded. Run ChronoAmperometry.load()"
-        return self.header["SAMPLETIME"]
-
-    def get_sample_count(self, curve: int = 0):
+    @property
+    def sample_count(self, curve: int = 0):
         """compute the number of samples collected for the loaded chronoamperometry experiment
 
         Args:
@@ -48,5 +48,4 @@ class ChronoAmperometry(parser.GamryParser):
 
         """
 
-        assert self.loaded, "DTA file not loaded. Run ChronoAmperometry.load()"
-        return len(self.curves[curve - 1].index)
+        return len(self._curves[curve - 1].index) if len(self.curves) > 0 else 0
diff --git a/gamry_parser/cv.py b/gamry_parser/cv.py
index 127b40e..6199319 100644
--- a/gamry_parser/cv.py
+++ b/gamry_parser/cv.py
@@ -4,7 +4,8 @@ import gamry_parser as parser
 class CyclicVoltammetry(parser.GamryParser):
     """Load a Cyclic Voltammetry experiment generated in Gamry EXPLAIN format."""
 
-    def get_v_range(self):
+    @property
+    def v_range(self):
         """retrieve the programmed voltage scan ranges
 
         Args:
@@ -18,31 +19,28 @@ class CyclicVoltammetry(parser.GamryParser):
         """
         assert self.loaded, "DTA file not loaded. Run CyclicVoltammetry.load()"
         assert (
-            "VLIMIT1" in self.header.keys()
+            "VLIMIT1" in self._header.keys()
         ), "DTA header file missing VLIMIT1 specification"
         assert (
-            "VLIMIT2" in self.header.keys()
+            "VLIMIT2" in self._header.keys()
         ), "DTA header file missing VLIMIT2 specification"
 
-        return self.header["VLIMIT1"], self.header["VLIMIT2"]
+        return self._header.get("VLIMIT1", None), self._header.get("VLIMIT2", None)
 
-    def get_scan_rate(self):
+    @property
+    def scan_rate(self):
         """retrieve the programmed scan rate
 
         Args:
             None
 
         Returns:
-            float: the scan rate, in mV/s
+            float: the scan rate, in mV/s (returns None for unknown scan rates)
 
         """
-        assert self.loaded, "DTA file not loaded. Run CyclicVoltammetry.load()"
-        assert (
-            "SCANRATE" in self.header.keys()
-        ), "DTA header file missing SCANRATE specification"
-        return self.header["SCANRATE"]
+        return self._header.get("SCANRATE", None)
 
-    def get_curve_data(self, curve: int = 0):
+    def curve(self, curve: int = 0):
         """retrieve relevant cyclic voltammetry experimental data
 
         Args:
@@ -61,6 +59,6 @@ class CyclicVoltammetry(parser.GamryParser):
         ), "Invalid curve ({}). File contains {} total curves.".format(
             curve, self.curve_count
         )
-        df = self.curves[curve]
+        df = self._curves[curve]
 
         return df[["Vf", "Im"]]
diff --git a/gamry_parser/eispot.py b/gamry_parser/eispot.py
index 5fba0f2..27cd4e0 100644
--- a/gamry_parser/eispot.py
+++ b/gamry_parser/eispot.py
@@ -4,7 +4,7 @@ import gamry_parser as parser
 class Impedance(parser.GamryParser):
     """Load a Potentiostatic EIS experiment generated in Gamry EXPLAIN format."""
 
-    def get_curve_data(self, curve: int = 0):
+    def curve(self, curve: int = 0):
         """retrieve potentiostatic eis-relevant data
 
         Args:
@@ -21,5 +21,5 @@ class Impedance(parser.GamryParser):
         """
 
         assert self.loaded, "DTA file not loaded. Run Impedance.load()"
-        df = self.curves[curve]
+        df = self._curves[curve]
         return df[["Freq", "Zreal", "Zimag", "Zmod", "Zphz"]]
diff --git a/gamry_parser/gamryparser.py b/gamry_parser/gamryparser.py
index 90004e6..62d2ef6 100644
--- a/gamry_parser/gamryparser.py
+++ b/gamry_parser/gamryparser.py
@@ -12,16 +12,13 @@ class GamryParser:
     fname: str = None
     to_timestamp: bool = False
     loaded: bool = False
-
-    header_length: int = 0
-    header: dict = None
-
     curve_count: int = 0
-    curves: list = []
-    curve_units: dict = dict()
+    header_length: int = 0
 
-    ocv_exists: bool = False
-    ocv: pd.DataFrame = None
+    _header: dict = None
+    _curves: list = []
+    _curve_units: dict = dict()
+    _ocv: pd.DataFrame = None
 
     REQUIRED_UNITS: dict = dict(CV=dict(Vf="V vs. Ref.", Im="A"))
 
@@ -36,26 +33,25 @@ class GamryParser:
             None
 
         """
+        self._reset_props()
         self.fname = filename if filename is not None else self.fname
         self.to_timestamp = (
             to_timestamp if to_timestamp is not None else self.to_timestamp
         )
-        self._reset_props()
 
     def _reset_props(self):
         "re-initialize parser properties"
 
+        self.fname = None
+        self.to_timestamp = False
         self.loaded = False
-
-        self.header = dict()
-        self.header_length = 0
-
-        self.curves = []
         self.curve_count = 0
-        self.curve_units = dict()
+        self.header_length = 0
 
-        self.ocv_exists = False
-        self.ocv = None
+        self._header = dict()
+        self._curves = []
+        self._curve_units = dict()
+        self._ocv = None
 
     def load(self, filename: str = None, to_timestamp: bool = None):
         """save experiment information to \"header\", then save curve data to \"curves\"
@@ -67,7 +63,10 @@ class GamryParser:
 
         """
 
-        self.__init__(filename=filename, to_timestamp=to_timestamp)
+        self.__init__(
+            filename=filename if filename else self.fname,
+            to_timestamp=to_timestamp if to_timestamp else self.to_timestamp,
+        )
         self.loaded = False
         assert self.fname is not None, "GamryParser needs to know what file to parse."
         assert os.path.exists(self.fname), "The file '{}' was not found.".format(
@@ -91,30 +90,25 @@ class GamryParser:
         """
 
         start_time = pd.to_datetime(
-            self.header["DATE"] + " " + self.header["TIME"],
+            self._header["DATE"] + " " + self._header["TIME"],
             dayfirst=bool(
-                re.search(r"[0-9]+\-[0-9]+\-[0-2]{1}[0-9]{3}", self.header["DATE"])
+                re.search(r"[0-9]+\-[0-9]+\-[0-2]{1}[0-9]{3}", self._header["DATE"])
             ),
         )
-        for curve in self.curves:
+        for curve in self._curves:
             curve["T"] = start_time + pd.to_timedelta(curve["T"], "s")
 
-    def get_curve_count(self) -> int:
-        """return the number of loaded curves"""
-        assert self.loaded, "DTA file not loaded. Run GamryParser.load()"
-        return self.curve_count
-
-    def get_curve_indices(self) -> tuple:
+    @property
+    def curve_indices(self) -> tuple:
         """return indices of curves (zero-based indexing)"""
-        assert self.loaded, "DTA file not loaded. Run GamryParser.load()"
-        return tuple(range(self.curve_count))
+        return tuple(range(self.curve_count)) if self.curve_count else None
 
-    def get_curve_numbers(self) -> tuple:
+    @property
+    def curve_numbers(self) -> tuple:
         """return Gamry curve numbers (one-based indexing, as in Gamry software)"""
-        assert self.loaded, "DTA file not loaded. Run GamryParser.load()"
-        return tuple(range(1, self.curve_count + 1))
+        return tuple(range(1, self.curve_count + 1)) if self.curve_count else None
 
-    def get_curve_data(self, curve: int = 0) -> pd.DataFrame:
+    def curve(self, curve: int = 0) -> pd.DataFrame:
         """retrieve relevant experimental data
 
         Args:
@@ -130,19 +124,26 @@ class GamryParser:
         ), "Invalid curve ({}). File contains {} total curves.".format(
             curve, self.curve_count
         )
-        return self.curves[curve]
+        return self._curves[curve]
 
-    def get_curves(self) -> list:
+    @property
+    def curves(self) -> list:
         """return all loaded curves as a list of pandas DataFrames"""
-        assert self.loaded, "DTA file not loaded. Run GamryParser.load()"
-        return self.curves
+        # assert self.loaded, "DTA file not loaded. Run GamryParser.load()"
+        return self._curves
+
+    @curves.setter
+    def curves(self, val: list):
+        self._curves = val
 
-    def get_header(self) -> list:
+    @property
+    def header(self) -> list:
         """return the experiment configuration dictionary"""
-        assert self.loaded, "DTA file not loaded. Run GamryParser.load()"
-        return self.header
+        # assert self.loaded, "DTA file not loaded. Run GamryParser.load()"
+        return self._header
 
-    def get_experiment_type(self) -> str:
+    @property
+    def experiment_type(self) -> str:
         """retrieve the type of experiment that was loaded (TAG)
 
         Args:
@@ -151,22 +152,17 @@ class GamryParser:
             str: Experiment Type (EXPLAIN-TAG)
 
         """
-        assert self.loaded, "DTA file not loaded. Run GamryParser.load()"
-        return self.header["TAG"]
-
-    def get_ocv_curve(self) -> pd.DataFrame:
-        """return the contents of OCVCURVE (if it exists). Deprecated in Framework version 7"""
-        if self.ocv_exists:
-            return self.ocv
-        else:
-            return None
+        return self._header.get("TAG", None)
 
-    def get_ocv_value(self):
+    @property
+    def ocv(self):
         """return the final OCV measurement of the experiment (if it exists)"""
-        if "EOC" in self.header.keys():
-            return self.header["EOC"]
-        else:
-            return None
+        return self._header.get("EOC", None)
+
+    @property
+    def ocv_curve(self) -> pd.DataFrame:
+        """return the contents of OCVCURVE (if it exists). Deprecated in Framework version 7"""
+        return self._ocv
 
     def read_header(self) -> list:
         """helper function to grab data from the EXPLAIN file header, which contains the loaded experiment's configuration
@@ -194,16 +190,16 @@ class GamryParser:
                 if len(cur_line) > 1:
                     # data format: key, type, value
                     if cur_line[1] in ["LABEL", "PSTAT"]:
-                        self.header[cur_line[0]] = cur_line[2]
+                        self._header[cur_line[0]] = cur_line[2]
                     elif cur_line[1] in ["QUANT", "IQUANT", "POTEN"]:
                         # locale-friendly alternative to float
-                        self.header[cur_line[0]] = locale.atof(cur_line[2])
+                        self._header[cur_line[0]] = locale.atof(cur_line[2])
                     elif cur_line[1] in ["IQUANT", "SELECTOR"]:
-                        self.header[cur_line[0]] = int(cur_line[2])
+                        self._header[cur_line[0]] = int(cur_line[2])
                     elif cur_line[1] in ["TOGGLE"]:
-                        self.header[cur_line[0]] = cur_line[2] == "T"
+                        self._header[cur_line[0]] = cur_line[2] == "T"
                     elif cur_line[1] == "TWOPARAM":
-                        self.header[cur_line[0]] = {
+                        self._header[cur_line[0]] = {
                             "enable": cur_line[2] == "T",
                             # locale-friendly alternative to float
                             "start": locale.atof(cur_line[3]),
@@ -211,13 +207,13 @@ class GamryParser:
                             "finish": locale.atof(cur_line[4]),
                         }
                     elif cur_line[0] == "TAG":
-                        self.header["TAG"] = cur_line[1]
+                        self._header["TAG"] = cur_line[1]
                     elif cur_line[0] == "NOTES":
                         n_notes = int(cur_line[2])
                         note = ""
                         for _ in range(n_notes):
                             note += f.readline().strip() + "\n"
-                        self.header[cur_line[0]] = note
+                        self._header[cur_line[0]] = note
                     elif cur_line[0] == "OCVCURVE":
                         n_points = int(cur_line[2])
                         ocv = f.readline().strip() + "\n"  # grab header data
@@ -227,14 +223,13 @@ class GamryParser:
                         ocv = pd.read_csv(
                             StringIO(ocv), delimiter="\t", header=0, index_col=0
                         )
-                        self.ocv = ocv
-                        self.ocv_exists = True
+                        self._ocv = ocv
 
             self.header_length = f.tell()
 
-        return self.header, self.header_length
+        return self._header, self.header_length
 
-    def read_curve_data(self, fid: int) -> tuple:
+    def _read_curve_data(self, fid: int) -> tuple:
         """helper function to process an EXPLAIN Table
 
         Args:
@@ -276,16 +271,16 @@ class GamryParser:
         """
 
         assert (
-            len(self.header) > 0
+            len(self._header) > 0
         ), "Must read file header before curves can be extracted."
-        self.curves = []
+        self._curves = []
         self.curve_count = 0
 
         with open(file=self.fname, mode="r", encoding="utf8", errors="ignore") as f:
             f.seek(self.header_length)  # skip to end of header
 
             while True:
-                curve_keys, curve_units, curve = self.read_curve_data(f)
+                curve_keys, curve_units, curve = self._read_curve_data(f)
                 if curve.empty:
                     break
 
@@ -302,8 +297,8 @@ class GamryParser:
                         if not is_numeric_dtype(curve[key]):
                             curve[key] = curve[key].map(locale.atof)
 
-                if not bool(self.curve_units.items()):
-                    exp_type = self.header["TAG"]
+                if not bool(self._curve_units.items()):
+                    exp_type = self._header["TAG"]
                     for key, unit in zip(curve_keys, curve_units):
                         if exp_type in self.REQUIRED_UNITS.keys():
                             if key in self.REQUIRED_UNITS[exp_type].keys():
@@ -312,12 +307,12 @@ class GamryParser:
                                 ), "Unit error for '{}': Expected '{}', found '{}'!".format(
                                     key, self.REQUIRED_UNITS[exp_type][key], unit
                                 )
-                        self.curve_units[key] = unit
+                        self._curve_units[key] = unit
                 else:
                     for key, unit in zip(curve_keys, curve_units):
-                        assert self.curve_units[key] == unit, "Unit mismatch found!"
+                        assert self._curve_units[key] == unit, "Unit mismatch found!"
 
-                self.curves.append(curve)
+                self._curves.append(curve)
                 self.curve_count += 1
 
-        return self.curves
+        return self._curves
diff --git a/gamry_parser/ocp.py b/gamry_parser/ocp.py
index 662a2e9..1950439 100644
--- a/gamry_parser/ocp.py
+++ b/gamry_parser/ocp.py
@@ -7,7 +7,7 @@ import re
 class OpenCircuitPotential(parser.GamryParser):
     """Load an Open Circuit Potential (CORPOT) experiment generated in Gamry EXPLAIN format."""
 
-    def get_curve_data(self):
+    def curve(self):
         """retrieve OCP data
 
         Args:
@@ -21,7 +21,7 @@ class OpenCircuitPotential(parser.GamryParser):
         """
 
         assert self.loaded, "DTA file not loaded. Run OpenCircuitPotential.load()"
-        df = self.curves[0]
+        df = self._curves[0]
         return df[["T", "Vf"]]
 
     def load(self, filename: str = None, to_timestamp: bool = None):
@@ -37,18 +37,10 @@ class OpenCircuitPotential(parser.GamryParser):
         super().load(filename=filename, to_timestamp=to_timestamp)
 
         assert (
-            self.header["TAG"] == "CORPOT"
+            self._header.get("TAG", "NOTFOUND") == "CORPOT"
         ), "This does not appear to be an Open Circuit Potential \
             Experiment file (looking for CORPOT, received {})".format(
-            self.header["TAG"]
+            self.header.get("TAG", None)
         )
-        self.ocv_exists = True
-        self.ocv = self.curves[0]
+        self._ocv = self._curves[0]
         self.loaded = True
-
-    def get_ocv_curve(self):
-        """return the contents of OCVCURVE (if it exists). Deprecated in Framework version 7"""
-        if self.ocv_exists:
-            return self.ocv
-        else:
-            return None
diff --git a/gamry_parser/squarewave.py b/gamry_parser/squarewave.py
index 03e0cc2..da46063 100644
--- a/gamry_parser/squarewave.py
+++ b/gamry_parser/squarewave.py
@@ -23,29 +23,35 @@ class SquareWaveVoltammetry(parser.GamryParser):
 
     @property
     def step_size(self):
+        """returns the experimental step size, in mV"""
         return self.header.get("STEPSIZE", None)
 
     @property
     def pulse_size(self):
+        """returns the experimental pulse size, in mV"""
         return self.header.get("PULSESIZE", None)
 
     @property
     def pulse_width(self):
+        """returns the experimental pulse "on" time, in seconds"""
         return self.header.get("PULSEON", None)
 
     @property
     def frequency(self):
+        """returns the experimental step frequency, in Hz"""
         return self.header.get("FREQUENCY", None)
 
     @property
     def v_range(self):
+        """returns the potential sweep range of a single voltammetry cycle (tuple, in V)"""
         return (self.header.get("VINIT", 0), self.header.get("VFINAL", 0))
 
     @property
     def cycles(self):
+        """returns the number of voltammetry cycles (#)"""
         return self.header.get("CYCLES", 0)
 
-    def get_curve_data(self, curve: int = 0):
+    def curve(self, curve: int = 0):
         """retrieve relevant SWV experimental data
 
         Args:
@@ -69,6 +75,6 @@ class SquareWaveVoltammetry(parser.GamryParser):
         ), "Invalid curve ({}). File contains {} total curves.".format(
             curve, self.curve_count
         )
-        df = self.curves[curve]
+        df = self._curves[curve]
 
         return df[["T", "Vfwd", "Vrev", "Vstep", "Ifwd", "Irev", "Idif"]]
diff --git a/gamry_parser/vfp600.py b/gamry_parser/vfp600.py
index a6cf6a8..43a43e5 100644
--- a/gamry_parser/vfp600.py
+++ b/gamry_parser/vfp600.py
@@ -33,7 +33,7 @@ class VFP600(parser.GamryParser):
         # No information about experiment time is available with VFP600 files, so no conversion of samples to pd.Timestamp is possible.
         super().load(filename=filename, to_timestamp=False)
 
-    def get_curve_data(self, curve=0):
+    def curve(self, curve=0):
         """retrieve chronoamperometry experiment data
 
         Args:
@@ -47,25 +47,27 @@ class VFP600(parser.GamryParser):
         """
 
         assert self.loaded, "DTA file not loaded. Run ChronoAmperometry.load()"
-        df = self.curves[curve]
-        df["T"] = df.index * self.get_sample_time()
+        df = self._curves[curve]
+        df["T"] = df.index * self.sample_time
         return df[["T", "Voltage", "Current"]]
 
-    def get_sample_time(self):
+    @property
+    def sample_time(self):
         """retrieve the programmed sample period
 
         Args:
             None.
 
         Returns:
-            float: sample period of the potentiostat (in seconds)
+            float: sample period of the potentiostat (in seconds), or 0 if no sample time recorded
 
         """
+        freq = self._header.get("FREQ", None)
 
-        assert self.loaded, "DTA file not loaded. Run ChronoAmperometry.load()"
-        return 1 / self.header["FREQ"]
+        return 1 / freq if freq else 0
 
-    def get_sample_count(self, curve: int = 0):
+    @property
+    def sample_count(self, curve: int = 0):
         """compute the number of samples collected for the loaded chronoamperometry experiment
 
         Args:
@@ -76,10 +78,9 @@ class VFP600(parser.GamryParser):
 
         """
 
-        assert self.loaded, "DTA file not loaded. Run ChronoAmperometry.load()"
-        return len(self.curves[curve - 1].index)
+        return len(self._curves[curve - 1].index) if len(self._curves) > 0 else 0
 
-    def read_curve_data(self, fid: int):
+    def _read_curve_data(self, fid: int):
         """helper function to process an EXPLAIN Table
 
         Args:
diff --git a/usage.py b/usage.py
index 5722bf2..e1ccad1 100644
--- a/usage.py
+++ b/usage.py
@@ -5,24 +5,24 @@ file = "tests/cv_data.dta"
 gp = parser.GamryParser()
 gp.load(filename=file)
 print("GamryParser() usage:")
-print("experiment type: {}".format(gp.get_experiment_type()))
-print("loaded curves: {}".format(gp.get_curve_count()))
+print("experiment type: {}".format(gp.experiment_type))
+print("loaded curves: {}".format(gp.curve_count))
 
-curve_index = random.randint(1, gp.get_curve_count())
+curve_index = random.randint(1, gp.curve_count)
 print("showing curve #{}".format(curve_index))
-print(gp.get_curve_data(curve_index))
+print(gp.curve(curve_index))
 
 cv = parser.CyclicVoltammetry(filename=file)
 cv.load()
-vrange = cv.get_v_range()
+vrange = cv.v_range
 print("\nCyclic Voltammetry class")
-print("Programmed Scan Rate: {} mV/s".format(cv.get_scan_rate()))
+print("Programmed Scan Rate: {} mV/s".format(cv.scan_rate))
 print("Programmed V range: [{}, {}] V".format(vrange[0], vrange[1]))
 print(
     "\tnote: range will not match with below; the raw file has been modified for faster test execution"
 )
 
-curve = cv.get_curve_data(curve_index)
+curve = cv.curve(curve_index)
 print("showing curve #{}".format(curve_index))
 print("Acheived V range: [{}, {}]".format(min(curve["Vf"]), max(curve["Vf"])))
 print(curve)


Test patch:
diff --git a/tests/test_chronoamperometry.py b/tests/test_chronoamperometry.py
index 5deb1f6..fd61560 100644
--- a/tests/test_chronoamperometry.py
+++ b/tests/test_chronoamperometry.py
@@ -9,12 +9,12 @@ class TestChronoAmperometry(unittest.TestCase):
 
     def test_getters(self):
         gp = parser.ChronoAmperometry(filename="tests/chronoa_data.dta")
-        self.assertRaises(AssertionError, gp.get_sample_time)
-        self.assertRaises(AssertionError, gp.get_sample_count)
+        self.assertIsNone(gp.sample_time)
+        self.assertEqual(gp.sample_count, 0)
 
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertTrue((curve.columns == ["T", "Vf", "Im"]).all())
 
-        self.assertEqual(gp.get_sample_time(), 30)
-        self.assertEqual(gp.get_sample_count(), 10)
+        self.assertEqual(gp.sample_time, 30)
+        self.assertEqual(gp.sample_count, 10)
diff --git a/tests/test_cyclicvoltammetry.py b/tests/test_cyclicvoltammetry.py
index a878952..a91dbeb 100644
--- a/tests/test_cyclicvoltammetry.py
+++ b/tests/test_cyclicvoltammetry.py
@@ -9,20 +9,19 @@ class TestCyclicVoltammetry(unittest.TestCase):
     def test_is_loaded(self):
         gp = parser.CyclicVoltammetry()
         # should raise exception if file not loaded
-        self.assertRaises(AssertionError, gp.get_curve_count)
-        self.assertRaises(AssertionError, gp.get_curve_data, 2)
-        self.assertRaises(AssertionError, gp.get_curves)
-        self.assertRaises(AssertionError, gp.get_scan_rate)
-        self.assertRaises(AssertionError, gp.get_experiment_type)
+        self.assertEqual(gp.curve_count, 0)
+        self.assertRaises(AssertionError, gp.curve, 2)
+        self.assertListEqual(gp.curves, [])
+        self.assertIsNone(gp.scan_rate)
+        self.assertIsNone(gp.experiment_type)
 
     def test_getters(self):
         gp = parser.CyclicVoltammetry(filename="tests/cv_data.dta")
         gp.load()
-        vrange = gp.get_v_range()
+        vrange = gp.v_range
         self.assertEqual(vrange[0], 0.1)
         self.assertEqual(vrange[1], 0.9)
 
-        scanrate = gp.get_scan_rate()
-        self.assertEqual(scanrate, 1.23456)
-        curve = gp.get_curve_data(1)
+        self.assertEqual(gp.scan_rate, 1.23456)
+        curve = gp.curve(curve=1)
         self.assertTrue((curve.columns == ["Vf", "Im"]).all())
diff --git a/tests/test_gamryparser.py b/tests/test_gamryparser.py
index c27778d..7546456 100644
--- a/tests/test_gamryparser.py
+++ b/tests/test_gamryparser.py
@@ -12,10 +12,10 @@ class TestGamryParser(unittest.TestCase):
     def test_is_loaded(self):
         gp = parser.GamryParser()
         # should raise exception if file not loaded
-        self.assertRaises(AssertionError, gp.get_curve_count)
-        self.assertRaises(AssertionError, gp.get_curve_data, 2)
-        self.assertRaises(AssertionError, gp.get_curves)
-        self.assertRaises(AssertionError, gp.get_experiment_type)
+        self.assertEqual(gp.curve_count, 0)
+        self.assertRaises(AssertionError, gp.curve, 2)
+        self.assertListEqual(gp.curves, [])
+        self.assertIsNone(gp.experiment_type)
 
     def test_read_header(self):
         gp = parser.GamryParser(filename="tests/cv_data.dta")
@@ -53,13 +53,13 @@ class TestGamryParser(unittest.TestCase):
         gp = parser.GamryParser(filename="tests/cv_data.dta")
         gp.load()
         self.assertEqual(gp.curve_count, 5)
-        curve1 = gp.curves[0]
+        curve1 = gp._curves[0]
         self.assertEqual(curve1["T"].iloc[0], 0.1)
         self.assertEqual(curve1["T"].iloc[-1], 1.0)
         self.assertEqual(curve1["Vf"].iloc[-1], 0.5)
         self.assertEqual(curve1.index.dtype, np.int64)
 
-        curve5 = gp.curves[-1]
+        curve5 = gp._curves[-1]
         self.assertEqual(curve5.index[-1], 49)
         self.assertEqual(curve5["T"].iloc[-1], 601.1)
         self.assertEqual(curve5["Vf"].iloc[-1], 0.889001)
@@ -70,14 +70,14 @@ class TestGamryParser(unittest.TestCase):
     def test_use_datetime(self):
         gp = parser.GamryParser(filename="tests/chronoa_data.dta", to_timestamp=False)
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         # 'T' should return elapsed time in seconds
         self.assertEqual(curve["T"][0], 0)
         self.assertEqual(curve["T"].iloc[-1], 270)
 
         gp = parser.GamryParser(filename="tests/chronoa_data.dta", to_timestamp=True)
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         # 'T' should return datetime objects
         self.assertEqual(curve["T"][0], pd.to_datetime("3/10/2019 12:00:00"))
         self.assertEqual(curve["T"].iloc[-1], pd.to_datetime("3/10/2019 12:04:30"))
@@ -86,18 +86,17 @@ class TestGamryParser(unittest.TestCase):
         gp = parser.GamryParser(filename="tests/eispot_data_curveaborted.dta")
         gp.load()
         self.assertEqual(gp.curve_count, 1)
-        self.assertEqual(gp.curves[0].shape, (5, 10))
+        self.assertEqual(gp._curves[0].shape, (5, 10))
 
     def test_getters(self):
         locale.setlocale(locale.LC_ALL, "")
         gp = parser.GamryParser(filename="tests/cv_data.dta")
         gp.load()
-        self.assertEqual(gp.get_curve_count(), 5)
-        self.assertTrue(isinstance(gp.get_header(), dict))
-        self.assertEqual(len(gp.get_header()), len(gp.header))
-        self.assertEqual(gp.get_experiment_type(), gp.header["TAG"])
-        self.assertTrue(isinstance(gp.get_curve_data(1), pd.DataFrame))
-        curves = gp.get_curves()
+        self.assertEqual(gp.curve_count, 5)
+        self.assertTrue(isinstance(gp.header, dict))
+        self.assertEqual(gp.experiment_type, gp.header["TAG"])
+        self.assertTrue(isinstance(gp.curve(1), pd.DataFrame))
+        curves = gp._curves
         self.assertTrue(isinstance(curves, list))
         self.assertTrue(isinstance(curves[-1], pd.DataFrame))
 
@@ -105,25 +104,21 @@ class TestGamryParser(unittest.TestCase):
         locale.setlocale(locale.LC_ALL, "")
         gp = parser.GamryParser(filename="tests/cv_data.dta")
         gp.load()
-        indices = gp.get_curve_indices()
-        self.assertEqual(indices, (0, 1, 2, 3, 4))
-        numbers = gp.get_curve_numbers()
-        self.assertEqual(numbers, (1, 2, 3, 4, 5))
+        self.assertEqual(gp.curve_indices, (0, 1, 2, 3, 4))
+        self.assertEqual(gp.curve_numbers, (1, 2, 3, 4, 5))
 
     def test_ocvcurve_self(self):
         locale.setlocale(locale.LC_ALL, "")
         gp = parser.GamryParser(filename="tests/cv_data.dta")
         gp.load()
-        self.assertFalse(gp.ocv_exists)
-        self.assertEqual(gp.get_ocv_curve(), None)
-        self.assertEqual(gp.get_ocv_value(), None)
+        self.assertEqual(gp.ocv_curve, None)
+        self.assertEqual(gp.ocv, None)
         gp = parser.GamryParser(filename="tests/ocvcurve_data.dta")
         gp.load()
-        self.assertTrue(gp.ocv_exists)
-        self.assertEqual(gp.get_ocv_curve().iloc[0]["T"], 0.258333)
-        self.assertEqual(gp.get_ocv_curve().iloc[-1]["T"], 10.3333)
-        self.assertEqual(gp.get_ocv_curve().iloc[-1]["Vf"], 0.283437)
-        self.assertEqual(gp.get_ocv_value(), 0.2834373)
+        self.assertEqual(gp.ocv_curve.iloc[0]["T"], 0.258333)
+        self.assertEqual(gp.ocv_curve.iloc[-1]["T"], 10.3333)
+        self.assertEqual(gp.ocv_curve.iloc[-1]["Vf"], 0.283437)
+        self.assertEqual(gp.ocv, 0.2834373)
 
     def test_locale(self):
         # confirm that files will load properly with non-US locales
@@ -131,7 +126,7 @@ class TestGamryParser(unittest.TestCase):
 
         gp = parser.GamryParser(filename="tests/chronoa_de_data.dta")
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertEqual(curve["T"].iloc[-1], 270)
         self.assertEqual(curve["Vf"].iloc[0], -5e-004)
         self.assertEqual(curve["Vf"].iloc[-1], 4e-001)
@@ -142,7 +137,7 @@ class TestGamryParser(unittest.TestCase):
         # pandas.read_csv() will handle 1,234,567.890 notation even in the wrong locale.
         gp = parser.GamryParser(filename="tests/chronoa_data.dta")
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertEqual(curve["T"].iloc[-1], 270)
         self.assertEqual(curve["Vf"].iloc[0], -5.4e-004)
         self.assertEqual(curve["Vf"].iloc[-1], 4e-001)
@@ -154,7 +149,7 @@ class TestGamryParser(unittest.TestCase):
         locale.setlocale(locale.LC_ALL, "en_US.utf8")
         gp = parser.GamryParser(filename="tests/chronoa_de_data.dta")
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertEqual(curve["Vf"].iloc[0], -50)
         self.assertEqual(curve["Vf"].iloc[-1], 40000)
         self.assertEqual(curve["Im"].iloc[0], -0.002)
diff --git a/tests/test_impedance.py b/tests/test_impedance.py
index cdbe166..e2ef656 100644
--- a/tests/test_impedance.py
+++ b/tests/test_impedance.py
@@ -11,13 +11,13 @@ class TestImpedance(unittest.TestCase):
         gp.load()
         self.assertEqual(len(gp.curves), 1)
 
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertTrue(isinstance(curve["Freq"].iloc[-1], float))
 
     def test_getters(self):
         gp = parser.Impedance(filename="tests/eispot_data.dta")
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertEqual(len(curve), 10)
         self.assertTrue(
             (curve.columns == ["Freq", "Zreal", "Zimag", "Zmod", "Zphz"]).all()
diff --git a/tests/test_ocp.py b/tests/test_ocp.py
index fd7bb20..863840c 100644
--- a/tests/test_ocp.py
+++ b/tests/test_ocp.py
@@ -13,7 +13,7 @@ class TestOpenCircuit(unittest.TestCase):
             filename="tests/ocp_data.dta", to_timestamp=False
         )
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         # 'T' should return elapsed time in seconds
         self.assertEqual(curve["T"][0], 5.00833)
         self.assertEqual(curve["T"].iloc[-1], 105.175)
@@ -22,7 +22,7 @@ class TestOpenCircuit(unittest.TestCase):
             filename="tests/ocp_data.dta", to_timestamp=True
         )
         gp.load()
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         # 'T' should return datetime objects
         self.assertEqual(curve["T"][0], pd.to_datetime("2020-02-10 17:18:05.008330"))
         self.assertEqual(
@@ -36,7 +36,7 @@ class TestOpenCircuit(unittest.TestCase):
         gp.load()
         self.assertEqual(len(gp.curves), 1)
 
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         for key in curve.keys():
             self.assertTrue(key in ["T", "Vf"])
         self.assertEqual(curve.shape, (21, 2))
@@ -48,7 +48,7 @@ class TestOpenCircuit(unittest.TestCase):
         gp.load(filename="tests/ocp_data.dta")
         self.assertEqual(len(gp.curves), 1)
 
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         for key in curve.keys():
             self.assertTrue(key in ["T", "Vf"])
         self.assertEqual(curve.shape, (21, 2))
@@ -59,12 +59,12 @@ class TestOpenCircuit(unittest.TestCase):
         gp = parser.OpenCircuitPotential(
             filename="tests/ocp_data.dta", to_timestamp=False
         )
-        self.assertIsNone(gp.get_ocv_curve())
+        self.assertIsNone(gp.ocv_curve)
         gp.load()
-        curve = gp.get_curve_data()
-        self.assertEqual(gp.get_curve_count(), 1)
+        curve = gp.curve()
+        self.assertEqual(gp.curve_count, 1)
         self.assertEqual(len(curve), 21)
         self.assertTrue((curve.columns == ["T", "Vf"]).all())
 
         # pandas equivalence check
-        assert_frame_equal(gp.get_ocv_curve(), gp.curves[0])
+        assert_frame_equal(gp.ocv_curve, gp.curves[0])
diff --git a/tests/test_squarewave.py b/tests/test_squarewave.py
index ed39fc0..f989de4 100644
--- a/tests/test_squarewave.py
+++ b/tests/test_squarewave.py
@@ -3,7 +3,7 @@ from pandas import Timestamp
 import unittest
 
 
-class TestCyclicVoltammetry(unittest.TestCase):
+class TestSquareWaveVoltammetry(unittest.TestCase):
     def setUp(self):
         pass
 
@@ -36,9 +36,9 @@ class TestCyclicVoltammetry(unittest.TestCase):
         self.assertEqual(gp.pulse_size, 25)
         self.assertEqual(gp.pulse_width, 0.01)
         self.assertEqual(gp.cycles, 251)
-        self.assertEqual(gp.get_curve_count(), 1)
+        self.assertEqual(gp.curve_count, 1)
 
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertTrue(
             (
                 curve.columns == ["T", "Vfwd", "Vrev", "Vstep", "Ifwd", "Irev", "Idif"]
@@ -48,7 +48,7 @@ class TestCyclicVoltammetry(unittest.TestCase):
         self.assertEqual(curve["T"].iloc[0], 0.01)
 
         gp.load(to_timestamp=True)
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertTrue(
             (
                 curve.columns == ["T", "Vfwd", "Vrev", "Vstep", "Ifwd", "Irev", "Idif"]
diff --git a/tests/test_vfp600.py b/tests/test_vfp600.py
index 528bb5f..f97bcfe 100644
--- a/tests/test_vfp600.py
+++ b/tests/test_vfp600.py
@@ -9,21 +9,25 @@ class TestVFP600(unittest.TestCase):
     def test_load(self):
         gp = parser.VFP600()
         self.assertFalse(gp.loaded)
+        self.assertEqual(gp.sample_time, 0)
+        self.assertEqual(gp.sample_count, 0)
+        self.assertEqual(gp.curve_count, 0)
 
         gp.load("tests/vfp600_data.dta")
         self.assertEqual(gp.fname, "tests/vfp600_data.dta")
-        self.assertTrue("VFP600" in gp.get_header()["TAG"])
+        self.assertEqual(gp.experiment_type, "VFP600")
+
         # data file acq frequency = 15hz
-        self.assertEqual(gp.get_sample_time(), 1 / 15)
-        self.assertEqual(gp.get_curve_count(), 1)
-        self.assertEqual(gp.get_sample_count(), 20)
+        self.assertEqual(gp.sample_time, 1 / 15)
+        self.assertEqual(gp.curve_count, 1)
+        self.assertEqual(gp.sample_count, 20)
         self.assertTrue(gp.loaded)
 
     def test_getters(self):
         gp = parser.VFP600()
         gp.load("tests/vfp600_data.dta")
 
-        curve = gp.get_curve_data()
+        curve = gp.curve()
         self.assertTrue((curve.columns == ["T", "Voltage", "Current"]).all())
 
         self.assertEqual(curve["T"][0], 0)