diff --git a/nifty6/operand.py b/nifty6/operand.py
new file mode 100644
index 0000000000000000000000000000000000000000..c553947cf07ec305ba961af13fb15087d30fcc31
--- /dev/null
+++ b/nifty6/operand.py
@@ -0,0 +1,118 @@
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Copyright(C) 2020 Max-Planck-Society
+# Author: Martin Reinecke
+#
+# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik.
+
+from .utilities import NiftyMeta
+from . import pointwise
+
+
+class Operand(metaclass=NiftyMeta):
+    """Transforms values defined on one domain into values defined on another
+    domain, and can also provide the Jacobian.
+    """
+
+    @property
+    def target(self):
+        """The domain on which the Operand's value is defined.
+
+        Returns
+        -------
+        target : DomainTuple or MultiDomain
+        """
+        return self._target
+
+    @property
+    def fld(self):
+        """Returns an object identical to `self`, except that all information
+        about the Jacobian and metric is stripped away.
+        Linearizations return the contained Field or MultiField object,
+        Fields and MultiFields just return themselves.
+
+        Returns
+        -------
+        Field or MultiField : the field object
+        """
+        raise NotImplementedError
+
+    @property
+    def val(self):
+        """The numerical value associated with this object
+        Depending on the `target` this is either  a `numpy.ndarray` or a
+        dictionary of `numpy.ndarray`s matching the object's `target`.
+
+        Returns
+        -------
+        numpy.ndarray or dictionary of np.ndarrays : the numerical value
+        """
+        raise NotImplementedError
+
+    def val_rw(self):
+        """Like `val`, but returns a writeable copy of the data.
+
+        Returns
+        -------
+        numpy.ndarray or dictionary of np.ndarrays :
+            a writeable copy of the numerical value
+        """
+        return None
+
+    @property
+    def jac(self):
+        """The Jacobian associated with this object
+        This can be `None` (in which case the object is a constant), or it can
+        be a `LinearOperator` with a `target` matching the object's.
+
+        Returns
+        -------
+        None or LinearOperator : the Jacobian
+        """
+        return None
+
+    @property
+    def want_metric(self):
+        """Whether a metric should be computed for the full expression.
+        This is `False` whenever `jac` is `None`. In other cases it signals
+        that operators processing this object should compute the metric.
+
+        Returns
+        -------
+        bool : whether the metric should be computed
+        """
+        return False
+
+    @property
+    def metric(self):
+        """The metric associated with the object.
+        This is `None`, except when all the following conditions hold:
+        - `want_metric` is `True`
+        - `target` is the scalar domain
+        - the operator chain generating this object contained an operator which
+          could compute the metric
+
+        Returns
+        -------
+        None or LinearOperator : the metric
+        """
+        raise NotImplementedError
+
+
+for f in pointwise.ptw_dict.keys():
+    def func(f):
+        def func2(self, *args, **kwargs):
+            return self.ptw(f, *args, **kwargs)
+        return func2
+    setattr(Operand, f, func(f))