Index: sasmodels/direct_model.py
===================================================================
--- sasmodels/direct_model.py	(revision 745b7bb20cb177250d7a71a24058de0dcca609e4)
+++ sasmodels/direct_model.py	(revision 40a87fa5096610e24c3ead10299f609e99f093f2)
@@ -30,5 +30,5 @@
 from . import resolution
 from . import resolution2d
-from .details import build_details
+from .details import build_details, dispersion_mesh
 
 try:
@@ -73,4 +73,53 @@
     #print("values:", values)
     return calculator(call_details, values, cutoff, is_magnetic)
+
+
+def call_ER(model_info, pars):
+    # type: (ModelInfo, ParameterSet) -> float
+    """
+    Call the model ER function using *values*.
+
+    *model_info* is either *model.info* if you have a loaded model,
+    or *kernel.info* if you have a model kernel prepared for evaluation.
+    """
+    if model_info.ER is None:
+        return 1.0
+    else:
+        value, weight = _vol_pars(model_info, pars)
+        individual_radii = model_info.ER(*value)
+        return np.sum(weight*individual_radii) / np.sum(weight)
+
+
+def call_VR(model_info, pars):
+    # type: (ModelInfo, ParameterSet) -> float
+    """
+    Call the model VR function using *pars*.
+
+    *model_info* is either *model.info* if you have a loaded model,
+    or *kernel.info* if you have a model kernel prepared for evaluation.
+    """
+    if model_info.VR is None:
+        return 1.0
+    else:
+        value, weight = _vol_pars(model_info, pars)
+        whole, part = model_info.VR(*value)
+        return np.sum(weight*part)/np.sum(weight*whole)
+
+
+def call_profile(model_info, **pars):
+    # type: (ModelInfo, ...) -> Tuple[np.ndarray, np.ndarray, Tuple[str, str]]
+    """
+    Returns the profile *x, y, (xlabel, ylabel)* representing the model.
+    """
+    args = {}
+    for p in model_info.parameters.kernel_parameters:
+        if p.length > 1:
+            value = np.array([pars.get(p.id+str(j), p.default)
+                              for j in range(1, p.length+1)])
+        else:
+            value = pars.get(p.id, p.default)
+        args[p.id] = value
+    x, y = model_info.profile(**args)
+    return x, y, model_info.profile_axes
 
 
@@ -98,15 +147,11 @@
 
 
-def call_profile(model_info, **pars):
-    args = {}
-    for p in model_info.parameters.kernel_parameters:
-        if p.length > 1:
-            value = np.array([pars.get(p.id+str(j), p.default)
-                              for j in range(1, p.length+1)])
-        else:
-            value = pars.get(p.id, p.default)
-        args[p.id] = value
-    x, y = model_info.profile(**args)
-    return x, y, model_info.profile_axes
+def _vol_pars(model_info, pars):
+    # type: (ModelInfo, ParameterSet) -> Tuple[np.ndarray, np.ndarray]
+    vol_pars = [get_weights(p, pars)
+                for p in model_info.parameters.call_parameters
+                if p.type == 'volume']
+    value, weight = dispersion_mesh(model_info, vol_pars)
+    return value, weight
 
 
@@ -158,5 +203,5 @@
                 Iq, dIq = None, None
             #self._theory = np.zeros_like(q)
-            q_vectors = [q]            
+            q_vectors = [q]
             q_mono = sesans.make_all_q(data)
         elif self.data_type == 'Iqxy':
@@ -213,5 +258,5 @@
                 Iq, dIq = None, None
             if (getattr(data, 'dxl', None) is None
-                or getattr(data, 'dxw', None) is None):
+                    or getattr(data, 'dxw', None) is None):
                 raise ValueError("oriented sample with 1D data needs slit resolution")
 
@@ -254,9 +299,13 @@
         if self._kernel is None:
             self._kernel = self._model.make_kernel(self._kernel_inputs)
-            self._kernel_mono = (self._model.make_kernel(self._kernel_mono_inputs)
-                                 if self._kernel_mono_inputs else None)
+            self._kernel_mono = (
+                self._model.make_kernel(self._kernel_mono_inputs)
+                if self._kernel_mono_inputs else None)
 
         Iq_calc = call_kernel(self._kernel, pars, cutoff=cutoff)
-        # TODO: may want to plot the raw Iq for other than oriented usans
+        # Storing the calculated Iq values so that they can be plotted.
+        # Only applies to oriented USANS data for now.
+        # TODO: extend plotting of calculate Iq to other measurement types
+        # TODO: refactor so we don't store the result in the model
         self.Iq_calc = None
         if self.data_type == 'sesans':
@@ -264,6 +313,6 @@
                        if self._kernel_mono_inputs else None)
             result = sesans.transform(self._data,
-                                   self._kernel_inputs[0], Iq_calc, 
-                                   self._kernel_mono_inputs, Iq_mono)
+                                      self._kernel_inputs[0], Iq_calc,
+                                      self._kernel_mono_inputs, Iq_mono)
         else:
             result = self.resolution.apply(Iq_calc)
@@ -273,5 +322,5 @@
                     np.reshape(Iq_calc, (self.resolution.ny, self.resolution.nx))
                 )
-        return result        
+        return result
 
 
@@ -350,5 +399,7 @@
                 for k, v in [pair.split('=')])
     if call == "ER_VR":
-        print(calculator.ER_VR(**pars))
+        ER = call_ER(model_info, pars)
+        VR = call_VR(model_info, pars)
+        print(ER, VR)
     else:
         Iq = calculator(**pars)
