Index: sasmodels/sesans.py
===================================================================
--- sasmodels/sesans.py	(revision a86e249e1edc9902d8497f521e4085aa171ee02e)
+++ sasmodels/sesans.py	(revision 7c1cce33768834960ded2d38104d915782069119)
@@ -20,5 +20,5 @@
     Spin-Echo SANS transform calculator.  Similar to a resolution function,
     the SesansTransform object takes I(q) for the set of *q_calc* values and
-    produces a transformed dataset
+    produces a transformed dataset.
 
     *SElength* (A) is the set of spin-echo lengths in the measured data.
@@ -45,23 +45,12 @@
         #import logging; logging.info("creating SESANS transform")
         self.q = z
-        # isoriented flag determines whether data is from an oriented sample or not, should be a selection variable upon entering SESANS data.
-        if self.isoriented==False:
-            self._set_hankel(SElength, zaccept, Rmax)
-        if self.isoriented==True:
-            self._set_cosmat(SElength, zaccept, Rmax)
+        # isoriented flag determines whether data is from an oriented sample or
+        # not, should be a selection variable upon entering SESANS data.
+        self._set_hankel(SElength, zaccept, Rmax)
 
     def apply(self, Iq):
-        try:
-            G0 = np.dot(self._H0, Iq)
-            G = np.dot(self._H.T, Iq)
-            P = G - G0
-        except:
-            try:
-                dq = self.q_calc[0]
-                G0 = sum(np.dot(self._cos0, Iq) * dq)
-                G = sum(np.dot(self._cosmat.T, Iq) * dq)
-                P = G - G0
-            except:
-                raise ValueError('Sesanstransform.apply cannot generate either a Hankel transform or a cosine transform of you SESANS data')
+        G0 = np.dot(self._H0, Iq)
+        G = np.dot(self._H.T, Iq)
+        P = G - G0
         return P
 
@@ -86,4 +75,46 @@
         self._H, self._H0 = H, H0
 
+class OrientedSesansTransform(object):
+    """
+    Oriented Spin-Echo SANS transform calculator.  Similar to a resolution
+    function, the OrientedSesansTransform object takes I(q) for the set
+    of *q_calc* values and produces a transformed dataset.
+
+    *SElength* (A) is the set of spin-echo lengths in the measured data.
+
+    *zaccept* (1/A) is the maximum acceptance of scattering vector in the spin
+    echo encoding dimension (for ToF: Q of min(R) and max(lam)).
+
+    *Rmax* (A) is the maximum size sensitivity; larger radius requires more
+    computation time.
+    """
+    #: SElength from the data in the original data units; not used by transform
+    #: but the GUI uses it, so make sure that it is present.
+    q = None  # type: np.ndarray
+
+    #: q values to calculate when computing transform
+    q_calc = None  # type: np.ndarray
+
+    # transform arrays
+    _cosmat = None  # type: np.ndarray
+    _cos0 = None # type: np.ndarray
+    _Iq_shape = None # type: Tuple[int, int]
+
+    def __init__(self, z, SElength, zaccept, Rmax):
+        # type: (np.ndarray, float, float) -> None
+        #import logging; logging.info("creating SESANS transform")
+        self.q = z
+        # isoriented flag determines whether data is from an oriented sample or
+        # not, should be a selection variable upon entering SESANS data.
+        self._set_cosmat(SElength, zaccept, Rmax)
+
+    def apply(self, Iq):
+        dq = self.q_calc[0][0]
+        Iq = np.reshape(Iq, self._Iq_shape)
+        G0 = self._cos0 * np.sum(Iq) * dq
+        G = np.sum(np.dot(Iq, self._cosmat.T), axis=1) * dq
+        P = G - G0
+        return P
+
     def _set_cosmat(self, SElength, zaccept, Rmax):
         # type: (np.ndarray, float, float) -> None
@@ -99,9 +130,8 @@
 
         cos0 = np.float32(dq / (2 * pi))
+        cosmat = np.float32(dq / (2 * pi)) * np.cos(q[:, None] * SE[None, :])
 
-        repq = np.tile(q, (SElength.size, 1)).T
-        repSE = np.tile(SElength, (q.size, 1))
-        cosmat = np.float32(dq / (2 * pi)) * np.cos(repSE * repq)
-
-        self.q_calc = q
+        qx, qy = np.meshgrid(q, q)
+        self._Iq_shape = qx.shape
+        self.q_calc = qx.flatten(), qy.flatten()
         self._cosmat, self._cos0 = cosmat, cos0
