diff --git a/gext/descriptors.py b/gext/descriptors.py
index 87fbd99355bb15e1b9b4acb7aed353e3ce6128fe..7a31240c2296214eb28ce7661e753f33561bcec9 100644
--- a/gext/descriptors.py
+++ b/gext/descriptors.py
@@ -3,10 +3,27 @@
 import numpy as np
 from scipy.spatial.distance import pdist
 
-def distance(coords: np.ndarray) -> np.ndarray:
-    """Compute the distance matric as a descriptor."""
-    return pdist(coords, metric="euclidean")
+class Distance():
 
-def coulomb(coords: np.ndarray) -> np.ndarray:
-    """Compute the Coulomb matrix as a descriptor."""
-    return 1.0/distance(coords)
+    """Distance matrix descriptors."""
+
+    def __init__(self, **kwargs):
+        self.set_options(**kwargs)
+
+    def set_options(self, kwargs):
+        """Given an option dictionary set the valid options and
+        raise an error if there are invalid ones."""
+        if len(kwargs) > 0:
+            raise ValueError("Invalid arguments given to the descriptor class.")
+
+    def compute(self, coords: np.ndarray) -> np.ndarray:
+        """Compute the distance matric as a descriptor."""
+        return pdist(coords, metric="euclidean")
+
+class Coulomb(Distance):
+
+    """Coulomb matrix descriptors."""
+
+    def compute(self, coords: np.ndarray) -> np.ndarray:
+        """Compute the Coulomb matrix as a descriptor."""
+        return 1.0/super().compute(coords)
diff --git a/gext/main.py b/gext/main.py
index 397bb69ef1591963bd6dab650dee9dc5b71cc710..86ad8178eb5380d2f73c559b68d1111e1d9a196e 100644
--- a/gext/main.py
+++ b/gext/main.py
@@ -14,24 +14,56 @@ class Extrapolator:
     it requires the number of electrons, the number of basis functions
     and the number of atoms of the molecule. The number of previous
     steps used by the extrapolator is an optional argument with default
-    value of 10."""
+    value of 6."""
 
-    def __init__(self, nelectrons: int, nbasis: int, natoms: int,
-            nsteps: int = 6, **kwargs):
+    def _update_docstring(self):
+        options_str = "\n".join(f"  - '{key}': {value}" for key, value in self.supported_options.items())
+        self.__doc__ = self.__doc__.format(options=options_str)
+
+    def __init__(self, nelectrons: int, nbasis: int, natoms: int, **kwargs):
+
+        self.supported_options = {
+            "verbose": False,
+            "nsteps": 6
+        }
+
+        self._update_docstring()
 
         self.nelectrons = nelectrons
         self.nbasis = nbasis
         self.natoms = natoms
-        self.nsteps = nsteps
 
-        self.gammas = CircularBuffer(self.nsteps, (self.nelectrons//2, self.nbasis))
-        self.overlaps = CircularBuffer(self.nsteps, (self.nbasis, self.nbasis))
-        self.descriptors = CircularBuffer(self.nsteps,
+        self.gammas = CircularBuffer(self.options["nsteps"], (self.nelectrons//2, self.nbasis))
+        self.overlaps = CircularBuffer(self.options["nsteps"], (self.nbasis, self.nbasis))
+        self.descriptors = CircularBuffer(self.options["nsteps"],
             ((self.natoms - 1)*self.natoms//2, ))
 
         self.tangent: Optional[np.ndarray] = None
 
-        self._set_options(**kwargs)
+        self.set_options(**kwargs)
+
+    def set_options(self, **kwargs):
+        """Given an arbitrary amount of keyword arguments, parse them if
+        specified, set default values if not specified and raise an error
+        if invalid arguments are passed."""
+
+        self.options = {}
+        descriptor_options = {}
+        fitting_options = {}
+
+        for key, value in kwargs.items():
+            if key in self.supported_options:
+                self.options[key] = value
+            elif key.startswith("descriptor_"):
+                descriptor_options[key[11:]] = value
+            elif key.startswith("fitting_"):
+                fitting_options[key[8:]] = value
+            else:
+                raise ValueError(f"Unsupported option: {key}")
+
+        for option, default_value in self.supported_options.items():
+            if not hasattr(self.options, option):
+                setattr(self.options, option, default_value)
 
     def load_data(self, coords: np.ndarray, coeff: np.ndarray,
             overlap: np.ndarray):
@@ -73,13 +105,6 @@ class Extrapolator:
 
         return c_guess @ c_guess.T
 
-    def _set_options(self, **kwargs):
-        """Parse additional options from the additional keyword arguments."""
-        self.options = {}
-        if "verbose" in kwargs:
-            self.options["verbose"] = kwargs["verbose"]
-        else:
-            self.options["verbose"] = False
 
     def _get_tangent(self) -> np.ndarray:
         """Get the tangent point."""