diff --git a/+misc/Parser.m b/+misc/Parser.m
index a5545a45e98306278e4c43f6e1f538e5c4bcc15d..4df1f6a5ee236e7703eec28cfcb93e6fb38fd337 100644
--- a/+misc/Parser.m
+++ b/+misc/Parser.m
@@ -6,13 +6,15 @@ classdef Parser < inputParser
 		
         
     methods
-        function obj = Parser()
+        function obj = Parser(varargin)
             obj = obj@inputParser();
             
             % set default properties
             obj.KeepUnmatched = true;
             obj.CaseSensitive = true;
             obj.PartialMatching = false;
+			
+			
 		end % Parser()
         
         function i = isDefault(obj, name)
diff --git a/+misc/ensureIsCell.m b/+misc/ensureIsCell.m
new file mode 100644
index 0000000000000000000000000000000000000000..b93e156a35a6bc0ec047105af10ddd3ab10ea612
--- /dev/null
+++ b/+misc/ensureIsCell.m
@@ -0,0 +1,11 @@
+function value = ensureIsCell(value)
+%ENSUREISCELL ensures that the value is a cell.
+% c = ensureIsCell(value) checks if value is a cell. If it is not a cell,
+% it is converted as a cell.
+
+if ~iscell(value)
+	value = {value};
+end	
+	
+end
+
diff --git a/+quantity/BasicVariable.m b/+quantity/BasicVariable.m
index 53f5bfaf70206700c83024c7582ae6cd8cd0dc0a..b46a9dbdbaf7dda4f7dbd1b9fd624dea08afb2ac 100644
--- a/+quantity/BasicVariable.m
+++ b/+quantity/BasicVariable.m
@@ -135,10 +135,9 @@ function obj = BasicVariable(valueContinuous, varargin)
 					Di(j) = obj_j.derivatives(k+1);
 				end
 			end
-			Di = reshape(Di, [ 1 size(obj)]);
 			D(i,:) = Di;
 		end
-		
+		D = reshape(D, [numel(K) size(obj)]);
 	end
 
 	%% PROPERTIES
diff --git a/+quantity/Discrete.m b/+quantity/Discrete.m
index d30e3e6489dcbbabf66d4ef52101d6392657c292..aa02851964d404afc2f720d52537343f418684bc 100644
--- a/+quantity/Discrete.m
+++ b/+quantity/Discrete.m
@@ -2,9 +2,6 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 	properties (SetAccess = protected)
 		% Discrete evaluation of the continuous quantity
 		valueDiscrete double;
-		
-		% Name of the domains that generate the grid.
-		gridName {mustBe.unique};
 	end
 	
 	properties (Hidden, Access = protected, Dependent)
@@ -12,6 +9,21 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 	end
 	
 	properties
+		% ID of the figure handle in which the handle is plotted
+		figureID double = 1;
+		
+		% Name of this object
+		name char;
+		
+		% domain
+		domain;
+	end
+	
+	properties ( Dependent )
+		
+		% Name of the domains that generate the grid.
+		gridName {mustBe.unique};
+		
 		% Grid for the evaluation of the continuous quantity. For the
 		% example with the function f(x,t), the grid would be
 		%   {[<spatial domain>], [<temporal domain>]}
@@ -19,12 +31,6 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		% spatial domain and <temporal domain> the discrete description of
 		% the temporal domain.
 		grid; % in set.grid it is ensured that, grid is a (1,:)-cell-array
-		
-		% ID of the figure handle in which the handle is plotted
-		figureID double = 1;
-		
-		% Name of this object
-		name char;
 	end
 	
 	methods
@@ -32,13 +38,21 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		% --- Constructor ---
 		%--------------------
 		function obj = Discrete(valueOriginal, varargin)
-			% The constructor requires valueOriginal to be
+			% DISCRETE a quantity, represented by discrete values.
+			%	obj = Discrete(valueOriginal, varargin) initializes a
+			%	quantity. The parameters to be set are:
+			% 'valueOrigin' must be
 			% 1) a cell-array of double arrays with
 			%	size(valueOriginal) == size(obj) and
 			%	size(valueOriginal{it}) == gridSize
+			%	Example: valueOrigin = { f(Z, T), g(Z, T) } is a cell array
+			%	wich contains the functions f(z,t) and g(z,t) evaluated on
+			%	the discrete domain (Z x T). Then, the name-value-pair
+			%	parameter 'domain' must be set with quantity.Domain
+			%	objects, according to the domains Z and T.
 			% OR
 			% 2) adouble-array with
-			%	size(valueOriginal) == [gridSize, size(quantity)] 
+			%	size(valueOriginal) == [gridSize, size(quantity)]
 			% Furthermore, 'gridName' must be part of the name-value-pairs
 			% in varargin. Additional parameters can be specified using
 			% name-value-pair-syntax in varargin.
@@ -56,7 +70,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					obj = valueOriginal;
 				else
 					% empty object. this is needed for instance, to create
-					% quantity.Discrete([]), which is useful for creating default 
+					% quantity.Discrete([]), which is useful for creating default
 					% values.
 					obj = quantity.Discrete.empty(size(valueOriginal));
 				end
@@ -64,106 +78,83 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				
 				%% input parser
 				myParser = misc.Parser();
-				myParser.addParameter('gridName', [], @(g) ischar(g) || iscell(g));
-				myParser.addParameter('grid', [], @(g) isnumeric(g) || iscell(g));
 				myParser.addParameter('name', string(), @isstr);
 				myParser.addParameter('figureID', 1, @isnumeric);
 				myParser.parse(varargin{:});
-				assert(all(~contains(myParser.UsingDefaults, 'gridName')), ...
-					'gridName is a mandatory input for quantity');
 				
-				if iscell(myParser.Results.gridName)
-					myGridName = myParser.Results.gridName;
-				else
-					myGridName = {myParser.Results.gridName};
+				%% domain parser
+				myDomain = quantity.Domain.parser(varargin{:});
+												
+				%% get the sizes of obj and grid
+				gridLength = myDomain.gridLength;
+
+				% convert double valued valueOriginal to cell-valued value
+				% original
+				if ~iscell(valueOriginal)
+					valueOriginal = quantity.Discrete.value2cell(valueOriginal, gridLength);
 				end
 				
-				%% allow initialization of empty objects:
+				% Check if the grid fits to the values. In addition, catch
+				% the case if all values are empty. This is required for
+				% the initialization of quantity.Function and
+				% quantity.Symbolic objects
+				assert( misc.alln( cellfun(@isempty, valueOriginal ) ) || ...
+					numGridElements(myDomain) == numel(valueOriginal{1}), ...
+					'grids do not fit to valueOriginal');				
+				
+				% allow initialization of empty objects
 				valueOriginalSize = size(valueOriginal);
 				if any(valueOriginalSize == 0)
 					% If the size is specified in the arguements, it should
 					% be chosen instead of the default size from the
 					% valueOriginal.
 					myParser = misc.Parser();
-					myParser.addParameter('size', valueOriginalSize((1+numel(myGridName)):end));
+					myParser.addParameter('size', valueOriginalSize((1+ndims(myDomain)):end));
 					myParser.parse(varargin{:});
 					obj = quantity.Discrete.empty(myParser.Results.size);
 					return;
 				end
 				
-				%% get the sizes of obj and grid
-				if iscell(valueOriginal)
-					if isempty(valueOriginal{1})
-						% if valueOriginal is a cell-array with empty
-						% cells, then grid must be specified as an input
-						% parameter. This case is important for
-						% constructing Symbolic or Function quantities
-						% without discrete values.
-						assert(all(~contains(myParser.UsingDefaults, 'grid')), ...
-							['grid is a mandatory input for quantity, ', ...
-							'if no discrete values are specified']);
-						if ~iscell(myParser.Results.grid)
-							gridSize = numel(myParser.Results.grid);
-						else
-							gridSize = cellfun(@(v) numel(v), myParser.Results.grid);
-						end
-					else
-						gridSize = size(valueOriginal{1});
-					end
-					objSize = size(valueOriginal);
-				elseif isnumeric(valueOriginal)
-					gridSize = valueOriginalSize(1 : numel(myGridName));
-					objSize = [valueOriginalSize(numel(myGridName)+1 : end), 1, 1];
-				end
-				
-				%% get grid and check size
-				if any(contains(myParser.UsingDefaults, 'grid'))
-					myGrid = quantity.Discrete.defaultGrid(gridSize);
-				else
-					myGrid = myParser.Results.grid;
-				end
-				if ~iscell(myGrid)
-					myGrid = {myGrid};
-				end
-				if isempty(myGridName) || isempty(myGrid)
-					if ~(isempty(myGridName) && isempty(myGrid))
-						error(['If one of grid and gridName is empty, ', ...
-							'then both must be empty.']);
-					end
-				else
-					assert(isequal(size(myGrid), size(myGridName)), ...
-						'number of grids and gridNames must be equal');
-					myGridSize = cellfun(@(v) numel(v), myGrid);
-					assert(isequal(gridSize(gridSize>1), myGridSize(myGridSize>1)), ...
-						'grids do not fit to valueOriginal');
-				end
-				
-				%% set valueDiscrete
-				if ~iscell(valueOriginal)
-					valueOriginal = quantity.Discrete.value2cell(valueOriginal, gridSize, objSize);
-				end
-				for k = 1:prod(objSize)
-					if numel(myGrid) == 1
-						obj(k).valueDiscrete = valueOriginal{k}(:);
+				% set valueDiscrete
+				for k = 1:numel(valueOriginal)
+					if numel(myDomain) == 1
+						% TODO: Which case is this? Why does it need extra
+						% treatment?
+						obj(k).valueDiscrete = valueOriginal{k}(:); %#ok<AGROW>
 					else
-						obj(k).valueDiscrete = valueOriginal{k};
+						obj(k).valueDiscrete = valueOriginal{k}; %#ok<AGROW>
 					end
 				end
 				
 				%% set further properties
-				[obj.grid] = deal(myGrid);
-				[obj.gridName] = deal(myGridName);
+				[obj.domain] = deal(myDomain);
 				[obj.name] = deal(myParser.Results.name);
 				[obj.figureID] = deal(myParser.Results.figureID);
 				
 				%% reshape object from vector to matrix
-				obj = reshape(obj, objSize);
+				obj = reshape(obj, size(valueOriginal));
 			end
 		end% Discrete() constructor
-
+		
 		%---------------------------
 		% --- getter and setters ---
- 		%---------------------------
+		%---------------------------
+		function gridName = get.gridName(obj)
+			if isempty(obj.domain)
+				gridName = {};
+			else
+				gridName = {obj.domain.name};
+			end
+		end
+		
+		function grid = get.grid(obj)
+			if isempty(obj.domain)
+				grid = {};
+			else
+				grid = {obj.domain.grid};
+			end
+		end
+		
 		function itIs = isConstant(obj)
 			% the quantity is interpreted as constant if it has no grid or
 			% it has a grid that is only one point.
@@ -172,29 +163,12 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		function doNotCopy = get.doNotCopy(obj)
 			doNotCopy = obj.doNotCopyPropertiesName();
 		end
-		function set.gridName(obj, name)
-			if ~iscell(name)
-				name = {name};
-			end
-			obj.gridName = name;
-		end
-	
-		function set.grid(obj, grid)
-			if ~iscell(grid)
-				grid = {grid};
-			end
-			
-			isV = cellfun(@(v) (sum(size(v)>1) == 1) || (numel(v) == 1), grid); % also allow 1x1x1x41 grids
-			assert(all(isV(:)), 'Please use vectors for the grid entries!');
-			
-			[obj.grid] = deal(grid);
-		end
 		function valueDiscrete = get.valueDiscrete(obj)
 			% check if the value discrete for this object
 			% has already been computed.
 			empty = isempty(obj.valueDiscrete);
 			if any(empty(:))
-				obj.valueDiscrete = obj.on(obj.grid);
+				obj.valueDiscrete = obj.obj2value(obj.domain, true);
 			end
 			valueDiscrete = obj.valueDiscrete;
 		end
@@ -225,7 +199,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		end
 		function o = quantity.Function(obj)
 			props = nameValuePair( obj(1) );
-		
+			
 			for k = 1:numel(obj)
 				F = griddedInterpolant(obj(k).grid{:}', obj(k).on());
 				o(k) = quantity.Function(@(varargin) F(varargin{:}), ...
@@ -249,7 +223,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				error('Not yet implemented')
 			end
 		end
-			
+		
 		function obj = setName(obj, newName)
 			% Function to set all names of all elements of the quantity obj to newName.
 			[obj.name] = deal(newName);
@@ -257,46 +231,188 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 	end
 	
 	methods (Access = public)
-		function value = on(obj, myGrid, myGridName)
-			% TODO es sieht so aus als w�rde die Interpolation bei
-			% konstanten werten ziemlichen Quatsch machen!
-			%	Da muss man nochmal ordentlich drauf schauen!
+		function [d, I, d_size] = compositionDomain(obj, domainName)
+			
+			assert(isscalar(obj));
+					
+			d = obj.on();
+			
+			% the evaluation of obj.on( compositionDomain ) is done by:
+			d_size = size(d);
+			
+			% 1) vectorization of the n-d-grid: compositionDomain	
+			d = d(:);
+
+			% 2) then it is sorted in ascending order
+			[d, I] = sort(d);			
+			
+			% verify the domain to be monotonical increasing
+			deltaCOD = diff(d);
+			assert(misc.alln(deltaCOD >= 0), 'The domain for the composition f(g(.)) must be monotonically increasing');
+
+			d = quantity.Domain(domainName, d);
+		end
+		
+		function obj_hat = compose(obj, g, optionalArgs)
+			% COMPOSE compose two functions
+			%	OBJ_hat = compose(obj, G, varargin) composes the function
+			%	defined by OBJ with the function given by G. In particular,
+			%		f_hat(z,t) = f( z, g(z,t) )
+			%	if f(t) = obj, g is G and f_hat is OBJ_hat.
+			arguments
+				obj
+				g quantity.Discrete;
+				optionalArgs.domain quantity.Domain = obj(1).domain;
+			end
+			myCompositionDomain = optionalArgs.domain;
+			originalDomain = obj(1).domain;
+			
+			assert( length( myCompositionDomain ) == 1 );
+			[idx, logOfDomain] = originalDomain.gridIndex(myCompositionDomain);
+			assert( isequal( originalDomain(idx), myCompositionDomain ), ...
+				'Composition of functions: The domains for the composition must be equal. A grid join is not implemented yet.');
+			assert( any( logOfDomain )  )
+			
+			% 2) get the composition domain:
+			%	For the argument y of a function f(y) which should be
+			%	composed by y = g(z,t) a new dommain will be created on the
+			%	basis of evaluation of g(z,t).
+			[composeOnDomain, I, domainSize] = ...
+				g.compositionDomain(myCompositionDomain.name);
+			
+			% check if the composition domain is in the range of definition
+			% of obj.
+			if ~composeOnDomain.isSubDomainOf( myCompositionDomain )
+				warning('quantity:Discrete:compose', ....
+					'The composition domain is not a subset of obj.domain! The missing values will be extrapolated.');
+			end			
+			
+			% 3) evaluation on the new grid:
+			%	the order of the domains is important. At first, the
+			%	domains which will not be replaced are taken. The last
+			%	domain must be the composed domain. For example: a function
+			%	f(x, y, z, t), where y should be composed with g(z, t) will
+			%	be resorted to f_(x, z, t, y) and then evaluated with y =
+			%	g(z,t)
+			evaluationDomain = [originalDomain( ~logOfDomain ), composeOnDomain ];
+			newValues = obj.on( evaluationDomain );
+			
+			% reshape the new values into a 2-d array so that the first
+			% dimension is any domain but the composition domain and the
+			% last dimension is the composition domain
+			sizeOldDomain = prod( [originalDomain( ~logOfDomain ).n] );
+			sizeComposeDomain = composeOnDomain.gridLength;
+			newValues = reshape(newValues, [sizeOldDomain, sizeComposeDomain]);
+			
+			% 4) reorder the computed values, dependent on the sort
+			% position fo the new domain
+			newValues(:,I) = newValues(:,:);
+			
+			% 5) rearrange the computed values, to have the same dimension
+			% as the required domain
+			% *) consider the domain 
+			%		f( z, g(z,t) ) = f(z, g(zeta,t) )|_{zeta = z}
+			tmpDomain = [originalDomain( ~logOfDomain ), g(1).domain ];
+			% newValues will be reshaped into the form
+			%	f(z, t, zeta)
+			newValues = reshape( newValues, [tmpDomain.gridLength, 1] );
+			% *) now the common domains, i.e., zeta = z must be merged:
+			%	for this, find the index of the common domain in list of
+			%	temporary combined domain
+			
+			intersectDomain = intersect( {originalDomain( ~logOfDomain ).name}, ...
+				{g(1).domain.name} );
+			
+			if ~isempty(intersectDomain)
+				
+				idx = 1:length(tmpDomain);
+				idxCommon = idx(strcmp({tmpDomain.name}, intersectDomain));
+
+				% take the diagonal values of the common domain, i.e., z = zeta
+				newValues = misc.diagNd( newValues, idxCommon );
+			end
+			
+			% *) build a new valueDiscrete on the correct grid.		
+			obj_hat = quantity.Discrete( newValues, ...
+				'name', [obj.name '°' g.name], ...
+				'size', size(obj), ...
+				'domain', tmpDomain.join);
+			
+		end
+		
+		function value = on(obj, myDomain, gridNames)
+			% ON evaluation of the quantity on a certain domain.
+			%	value = on(obj) or value = obj.on(); evaluates the quantity
+			%	on its standard grid. 
+			%	value = obj.on( myDomain ) evalutes the quantity on the
+			%	grid specified by myDomain. The order of the domains in
+			%	domain, will be the same as from myDomain. 
+			%	value = obj.on( grid ) evaluates the quantity specified by
+			%	grid. Grid must be a cell-array with the grids as elements.
+			%	value = obj.on( grid, gridName ) evaluates the quantity
+			%	specified by grid. Grid must be a cell-aary with the grids
+			%	as elements. By the gridName parameter the order of the
+			%	grid can be specified.
+
 			if isempty(obj)
 				value = zeros(size(obj));
 			else
-				if nargin == 1
-					myGrid = obj(1).grid;
-					myGridName = obj(1).gridName;
-				elseif nargin >= 2 && ~iscell(myGrid)
-					myGrid = {myGrid};
-				end
-				gridPermuteIdx = 1:obj(1).nargin;
-				if nargin == 3
-					if ~iscell(myGridName)
-						myGridName = {myGridName};
+				if nargin == 2
+					% case 1: a domain is specified by myDomain or by
+					% myDomain as a cell-array with grid entries
+					if iscell(myDomain) || isnumeric(myDomain)
+						myDomain = misc.ensureIsCell(myDomain);
+% 						assert(all(cellfun(@(v)isvector(v), myDomain)), 'The cell entries for a new grid have to be vectors')
+						newGrid = myDomain;
+						myDomain = quantity.Domain.empty();
+						
+						if obj(1).isConstant()
+							gridNames = repmat({''}, length(newGrid));
+						else
+							gridNames = {obj(1).domain.name};
+						end
+						
+						for k = 1:length(newGrid)
+							myDomain(k) = quantity.Domain(gridNames{k}, newGrid{k});
+						end
 					end
-					assert(numel(myGrid) == numel(myGridName), ...
-						['If on() is called by using gridNames as third input', ...
-						', then the cell-array of grid and gridName must have ', ...
-						'equal number of elements.']);
-					assert(numel(myGridName) == obj(1).nargin, ...
-						'All (or none) gridName must be specified');
-					gridPermuteIdx = cellfun(@(v) obj(1).gridIndex(v), myGridName);
-					myGrid = myGrid(gridPermuteIdx);
-				end
-				
-				value = obj.obj2value();
-				
-				if nargin >= 2 && (prod(obj(1).gridSize) > 1)
-					indexGrid = arrayfun(@(s)linspace(1,s,s), size(obj), 'UniformOutput', false);
-					tempInterpolant = numeric.interpolant(...
-						[obj(1).grid, indexGrid{:}], value);
-					value = tempInterpolant.evaluate(myGrid{:}, indexGrid{:});
-				elseif obj.isConstant
-					value = repmat(value, [cellfun(@(v) numel(v), myGrid), ones(1, length(size(obj)))]);
-					gridPermuteIdx = 1:numel(myGrid);
+				elseif nargin == 3
+					% case 2: a domain is specified by a grid and a grid
+					% name. Then, the first input parameter is the grid,
+					% i.e., myGrid = myDomain and the second is the grid
+					% name.
+					myDomain = misc.ensureIsCell(myDomain);
+					gridNames = misc.ensureIsCell(gridNames);
+
+					assert(all(cellfun(@(v)isvector(v), myDomain)), 'The cell entries for a new grid have to be vectors')
+					assert(iscell(gridNames), 'The gridNames parameter must be cell array')
+					assert(all(cellfun(@ischar, gridNames)), 'The gridNames must be strings')
+					
+					newGrid = myDomain;
+					myDomain = quantity.Domain.empty();
+					for k = 1:length(newGrid)
+						myDomain(k) = quantity.Domain(gridNames{k}, newGrid{k});
+					end
+				else
+					myDomain = obj(1).domain;
 				end
-				value = permute(reshape(value, [cellfun(@(v) numel(v), myGrid), size(obj)]), ...
+ 				
+				% verify the domain
+				if obj(1).isConstant
+					gridPermuteIdx = 1:length(myDomain);
+				else
+					assert(numel(myDomain) == numel(obj(1).domain), ['Wrong grid for the evaluation of the object']);
+					% compute the permutation index, in order to bring the
+					% new domain in the same order as the original one.
+					gridPermuteIdx = obj(1).domain.getPermutationIdx(myDomain);
+				end			
+				% get the valueDiscrete data for this object. Apply the
+				% permuted myDomain. Then the obj2value will be evaluated
+				% in the order of the original domain. The permuatation to
+				% the new order will be done in the next step.
+				originalOrderedDomain(gridPermuteIdx) = myDomain;
+				value = obj.obj2value(originalOrderedDomain);
+				value = permute(reshape(value, [originalOrderedDomain.gridLength, size(obj)]), ...
 					[gridPermuteIdx, numel(gridPermuteIdx)+(1:ndims(obj))]);
 			end
 		end
@@ -307,13 +423,13 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				value = zeros(size(obj));
 				indexGrid = arrayfun(@(s)linspace(1,s,s), size(obj), 'UniformOutput', false);
 				interpolant = numeric.interpolant(...
-						[indexGrid{:}], value);
+					[indexGrid{:}], value);
 			else
-				myGrid = obj(1).grid;			
+				myGrid = obj(1).grid;
 				value = obj.obj2value();
 				indexGrid = arrayfun(@(s)linspace(1,s,s), size(obj), 'UniformOutput', false);
 				interpolant = numeric.interpolant(...
-						[myGrid, indexGrid{:}], value);
+					[myGrid, indexGrid{:}], value);
 			end
 		end
 		
@@ -363,17 +479,17 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					[referenceGrid, referenceGridName] = varargin{1}.getFinestGrid(varargin{2:end});
 				else
 					referenceGrid = {};
-				referenceGridName = '';
+					referenceGridName = '';
 				end
 				return;
 			else
 				referenceGridName = a(1).gridName;
 				referenceGrid = a(1).grid;
-				referenceGridSize = a(1).gridSize(referenceGridName);				
+				referenceGridSize = a(1).gridSize(referenceGridName);
 			end
-
+			
 			for it = 1 : numel(varargin)
-				if isempty(varargin{it})
+				if isempty(varargin{it}) || isempty(varargin{it}(1).domain)
 					continue;
 				end
 				assert(numel(referenceGridName) == numel(varargin{it}(1).gridName), ...
@@ -392,75 +508,24 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			end
 		end
 		
-		function [gridJoined, gridNameJoined] = gridJoin(obj1, obj2)
-			%% gridJoin combines the grid and gridName of two objects (obj1,
-			% obj2), such that every gridName only occurs once and that the
-			% finer grid of both is used.
-			
-			gridNameJoined = unique([obj1(1).gridName, obj2(1).gridName]);
-			gridJoined = cell(1, numel(gridNameJoined));
-			for it = 1 : numel(gridNameJoined)
-				currentGridName = gridNameJoined{it};
-				[index1, lolo1] = obj1.gridIndex(currentGridName);
-				[index2, lolo2] = obj2.gridIndex(currentGridName);
-				if ~any(lolo1)
-					gridJoined{it} = obj2(1).grid{index2};
-				elseif ~any(lolo2)
-					gridJoined{it} = obj1(1).grid{index1};
-				else
-					tempGrid1 = obj1(1).grid{index1};
-					tempGrid2 = obj2(1).grid{index2};
-					
-					if ~obj1.isConstant && ~obj2.isConstant				
-					assert(tempGrid1(1) == tempGrid2(1), 'Grids must have same domain for gridJoin')
-					assert(tempGrid1(end) == tempGrid2(end), 'Grids must have same domain for gridJoin')
-					end
-					if numel(tempGrid1) > numel(tempGrid2)
-						gridJoined{it} = tempGrid1;
-					else
-						gridJoined{it} = tempGrid2;
-					end
-				end
-			end
-		end % gridJoin()
 		function obj = sort(obj, varargin)
 			%SORT sorts the grid of the object in a desired order
-			% obj = sortGrid(obj) sorts the grid in alphabetical order. 
+			% obj = sortGrid(obj) sorts the grid in alphabetical order.
 			% obj = sort(obj, 'descend') sorts the grid in descending
 			% alphabetical order.
-				
-			if nargin == 2 && strcmp(varargin{1}, 'descend')
-				descend = 1;
-			else
-				descend = 0;
-			end
-			
+						
 			% only sort the grids if there is something to sort
 			if obj(1).nargin > 1
-				gridNames = obj(1).gridName;
 				
-				% this is the default case for ascending alphabetical
-				% order
-				[sortedNames, I] = sort(gridNames);
-
-				% if descending: flip the order of the entries
-				if descend 
-					sortedNames = flip(sortedNames);
-					I = flip(I);
-				end
-
-				% sort the grid entries
-				[obj.grid] = deal(obj(1).grid(I));
-
-				% assign the new grid names
-				[obj.gridName] = deal(sortedNames);
-
-				% permute the value discrete
+				[sortedDomain, I] = obj(1).domain.sort(varargin{:});
+				[obj.domain] = deal(sortedDomain);
+				
 				for k = 1:numel(obj)
 					obj(k).valueDiscrete = permute(obj(k).valueDiscrete, I);
-				end	
+				end
 			end
 		end% sort()
+		
 		function c = horzcat(a, varargin)
 			%HORZCAT Horizontal concatenation.
 			%   [A B] is the horizontal concatenation of objects A and B
@@ -488,7 +553,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			%   X3 ...]' when any of X1, X2, X3, etc. is an object.
 			%
 			%	See also HORZCAT, CAT.
-			 c = cat(2, a, varargin{:});
+			c = cat(2, a, varargin{:});
 		end
 		function c = vertcat(a, varargin)
 			%VERTCAT Vertical concatenation.
@@ -535,18 +600,18 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			%   single matrix.
 			%
 			%   Examples:
-			%     a = magic(3); b = pascal(3); 
+			%     a = magic(3); b = pascal(3);
 			%     c = cat(4,a,b)
 			%   produces a 3-by-3-by-1-by-2 result and
 			%     s = {a b};
-			%     for i=1:length(s), 
+			%     for i=1:length(s),
 			%       siz{i} = size(s{i});
 			%     end
 			%     sizes = cat(1,siz{:})
 			%   produces a 2-by-2 array of size vectors.
-
+			
 			if nargin == 1
-				objCell = {a};			
+				objCell = {a};
 			else
 				objCell = [{a}, varargin(:)'];
 				
@@ -565,7 +630,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					% concatenated, so a new empty object is initialized.
 					s = cellfun(@(o) size(o), objCell, 'UniformOutput', false);
 					if dim == 1
-					S = sum(cat(3, s{:}), 3);
+						S = sum(cat(3, s{:}), 3);
 					elseif dim == 2
 						S = s{1};
 					else
@@ -589,7 +654,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 						if isempty(value)
 							M = zeros([prod(obj(1).gridSize), size(value(l))]);
 						end
-							M = reshape(M, [obj(1).gridSize, size(value)]);
+						M = reshape(M, [obj(1).gridSize, size(value)]);
 						o = quantity.Discrete( M, ...
 							'size', size(value), ...
 							'gridName', obj(1).gridName, ...
@@ -683,13 +748,13 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			end
 			
 			solution = objInverseTemp.on(rhs);
-
-% 			solution = zeros(numel(obj), 1);
-% 			for it = 1 : numel(obj)
-% 				objInverseTemp = obj(it).invert(gridName);
-% 				solution(it) = objInverseTemp.on(rhs(it));				
-% 			end
-% 			solution = reshape(solution, size(obj));
+			
+			% 			solution = zeros(numel(obj), 1);
+			% 			for it = 1 : numel(obj)
+			% 				objInverseTemp = obj(it).invert(gridName);
+			% 				solution(it) = objInverseTemp.on(rhs(it));
+			% 			end
+			% 			solution = reshape(solution, size(obj));
 		end
 		
 		function inverse = invert(obj, gridName)
@@ -709,7 +774,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			assert(isequal(size(obj), [1, 1]));
 			inverse = quantity.Discrete(repmat(obj(1).grid{obj.gridIndex(gridName)}(:), [1, size(obj)]), ...
 				'size', size(obj), 'grid', obj.on(), 'gridName', {[obj(1).name]}, ...
-				'name', gridName); 
+				'name', gridName);
 			
 		end
 		
@@ -731,15 +796,15 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			myParser.addParameter('AbsTol', 1e-6);
 			myParser.parse(varargin{:});
 			
-			variableGrid = myParser.Results.variableGrid;
-			myGridSize = [numel(variableGrid), ... 
+			variableGrid = myParser.Results.variableGrid(:);
+			myGridSize = [numel(variableGrid), ...
 				numel(myParser.Results.initialValueGrid)];
 			
 			% the time (s) vector has to start at 0, to ensure the IC. If
 			% variableGrid does not start with 0, it is separated in
 			% negative and positive parts and later combined again.
-			positiveVariableGrid = [0, variableGrid(variableGrid > 0)];
-			negativeVariableGrid = [0, flip(variableGrid(variableGrid < 0))];
+			positiveVariableGrid = [0; variableGrid(variableGrid > 0)];
+			negativeVariableGrid = [0; flip(variableGrid(variableGrid < 0))];
 			
 			% solve ode for every entry in obj and for every initial value
 			options = odeset('RelTol', myParser.Results.RelTol, 'AbsTol', myParser.Results.AbsTol);
@@ -753,14 +818,14 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					if numel(positiveVariableGrid) > 1
 						[resultGridPositive, odeSolutionPositive] = ...
 							ode45(@(y, z) obj(it).on(z), ...
-								positiveVariableGrid, ...
-								myParser.Results.initialValueGrid(icIdx), options);
+							positiveVariableGrid, ...
+							myParser.Results.initialValueGrid(icIdx), options);
 					end
 					if numel(negativeVariableGrid) >1
 						[resultGridNegative, odeSolutionNegative] = ...
 							ode45(@(y, z) obj(it).on(z), ...
-								negativeVariableGrid, ...
-								myParser.Results.initialValueGrid(icIdx), options);
+							negativeVariableGrid, ...
+							myParser.Results.initialValueGrid(icIdx), options);
 					end
 					if any(variableGrid == 0)
 						resultGrid = [flip(resultGridNegative(2:end)); 0 ; resultGridPositive(2:end)];
@@ -782,8 +847,29 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				{variableGrid, myParser.Results.initialValueGrid}, ...
 				'size', size(obj), 'name', ['solve(', obj(1).name, ')']);
 		end
-			
+		
 		function solution = subs(obj, gridName2Replace, values)
+			% SUBS substitute variables of a quantity
+			%	solution = SUBS(obj, NEWDOMAIN), replaces the original
+			%	domain of the object with the new domain specified by
+			%	NEWDOMAIN. NEWDOMAIN must have the same grid name as the
+			%	original domain.
+			%
+			%	solution = SUBS(obj, GRIDNAMES2REPLACE, VALUES) replaces
+			%	the domains which are specified by GRIDNAMES2REPLACE by
+			%	VALUES. GRIDNAMES2REPLACE must be a cell-array with the
+			%	names of the domains or an object-array with
+			%	quantity.Domain objects which should be replaced by VALUES.
+			%	VALUES must be a cell-array of the new values or new grid
+			%	names.
+			%
+			%	Example: 
+			%		q = q.subs('z', 't')
+			%		will replace the domain with the name 'z' by a domain
+			%		with the name 't' but with the same discretization.
+			%		q = q.subs('z', linspace(0,1)')
+			%		will replace the grid of domain with the name 'z' by
+			%		the new grid specified by linspace.
 			if nargin == 1 || isempty(gridName2Replace)
 				% if gridName2Replace is empty, then nothing must be done.
 				solution = obj;
@@ -792,19 +878,23 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				solution = obj;
 			else
 				% input checks
-				assert(nargin == 3, ['Wrong number of input arguments. ', ...
-					'gridName2Replace and values must be cell-arrays!']);
-				if ~iscell(gridName2Replace)
-					gridName2Replace = {gridName2Replace};
-				end
-				if ~iscell(values)
-					values = {values};
+				if nargin == 2
+					assert(isa(gridName2Replace, 'quantity.Domain'), 'If only two parameters are specified, the second parameter must be a quantiy.Domain');
+
+					values = {gridName2Replace.grid};
+					gridName2Replace = {gridName2Replace.name};
+					
+				elseif nargin == 3
+					
+					gridName2Replace = misc.ensureIsCell(gridName2Replace);
+					values = misc.ensureIsCell(values);
 				end
+				
 				assert(numel(values) == numel(gridName2Replace), ...
 					'gridName2Replace and values must be of same size');
 				
-				% here substitution starts: 
-				% The first (gridName2Replace{1}, values{1})-pair is 
+				% here substitution starts:
+				% The first (gridName2Replace{1}, values{1})-pair is
 				% replaced. If there are more cell-elements in those inputs
 				% then subs() is called again for the remaining pairs
 				% (gridName2Replace{2:end}, values{2:end}).
@@ -835,8 +925,8 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 						% called with subs(f, zeta, z), then g(z) = f(z, z)
 						% results, hence the dimensions z and zeta are
 						% merged.
-						gridIndices = [obj(1).gridIndex(gridName2Replace{1}), ...
-							obj(1).gridIndex(values{1})];
+						gridIndices = [obj(1).domain.gridIndex(gridName2Replace{1}), ...
+							obj(1).domain.gridIndex(values{1})];
 						newGridForOn = obj(1).grid;
 						if numel(obj(1).grid{gridIndices(1)}) > numel(obj(1).grid{gridIndices(2)})
 							newGridForOn{gridIndices(2)} = newGridForOn{gridIndices(1)};
@@ -846,17 +936,16 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 						newValue = misc.diagNd(obj.on(newGridForOn), gridIndices);
 						newGrid = {newGridForOn{gridIndices(1)}, ...
 							newGridForOn{1:1:numel(newGridForOn) ~= gridIndices(1) ...
-							 & 1:1:numel(newGridForOn) ~= gridIndices(2)}};
+							& 1:1:numel(newGridForOn) ~= gridIndices(2)}};
 						newGridName = {values{1}, ...
 							obj(1).gridName{1:1:numel(obj(1).gridName) ~= gridIndices(1) ...
-							 & 1:1:numel(obj(1).gridName) ~= gridIndices(2)}};
-						
+							& 1:1:numel(obj(1).gridName) ~= gridIndices(2)}};
 					else
 						% this is the default case. just grid name is
 						% changed.
 						newGrid = obj(1).grid;
 						newGridName = obj(1).gridName;
-						newGridName{obj(1).gridIndex(gridName2Replace{1})} ...
+						newGridName{obj(1).domain.gridIndex(gridName2Replace{1})} ...
 							= values{1};
 						newValue = obj.on();
 					end
@@ -870,7 +959,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					% newGrid is the similar to the original grid, but the
 					% grid of gridName2Replace is removed.
 					newGrid = obj(1).grid;
-					newGrid = newGrid((1:1:numel(newGrid)) ~= obj.gridIndex(gridName2Replace{1}));
+					newGrid = newGrid((1:1:numel(newGrid)) ~= obj(1).domain.gridIndex(gridName2Replace{1}));
 					newGridSize = cellfun(@(v) numel(v), newGrid);
 					% newGridForOn is the similar to the original grid, but
 					% the grid of gridName2Replace is set to values{1} for
@@ -900,11 +989,14 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					solution = solution.subs(gridName2Replace(2:end), values(2:end));
 				end
 			end
-			
 		end
 		
+		function [idx, logicalIdx] = gridIndex(obj, varargin)
+			[idx, logicalIdx] = obj(1).domain.gridIndex(varargin{:});
+		end 
+		
 		function value = at(obj, point)
-			% at() evaluates the object at one point and returns it as array 
+			% at() evaluates the object at one point and returns it as array
 			% with the same size as size(obj).
 			value = reshape(obj.on(point), size(obj));
 		end
@@ -913,11 +1005,11 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% ATINDEX returns the valueDiscrete at the requested index.
 			% value = atIndex(obj, varargin) returns the
 			% quantity.Discrete.valueDiscrete at the index defined by
-			% varargin.  
+			% varargin.
 			%	value = atIndex(obj, 1) returns the first element of
 			%	"valueDiscrete"
 			%	value = atIndex(obj, ':') returns all elements of
-			%	obj.valueDiscrete in vectorized form. 
+			%	obj.valueDiscrete in vectorized form.
 			%	value = atIndex(obj, 1, end) returns the obj.valueDiscrete
 			%	at the index (1, end).
 			%	If a range of index is requested, the result is returned
@@ -941,15 +1033,15 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					'UniformOutput', false);
 				
 				valueSize = size(value{1});
-								
+				
 				if all(cellfun(@numel, varargin) == 1) && all(cellfun(@isnumeric, varargin))
 					outputSize = [];
 				else
 					outputSize = valueSize(1:obj(1).nargin);
-			end
-
+				end
+				
 				value = reshape([value{:}], [outputSize, size(obj)]);
-		end
+			end
 		end
 		
 		function n = nargin(obj)
@@ -976,7 +1068,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		function s = gridSize(obj, myGridName)
 			% GRIDSIZE returns the size of all grid entries.
 			% todo: this should be called gridLength
-			if isempty(obj(1).grid)
+			if isempty(obj(1).domain)
 				s = [];
 			else
 				if nargin == 1
@@ -987,20 +1079,6 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			end
 		end
 		
-		function matGrid = ndgrid(obj, grid)
-			% ndgrid calles ndgrid for the default grid, if no other grid
-			% is specified. Empty grid as input returns empty cell as
-			% result.
-			if nargin == 1
-				grid = obj.grid;
-			end
-			if isempty(grid)
-				matGrid = {};
-			else
-				[matGrid{1:obj.nargin}] = ndgrid(grid{:});
-			end
-		end % ndgrid()
-		
 		function H = plot(obj, varargin)
 			H = [];
 			p = misc.Parser();
@@ -1079,7 +1157,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 						xlabel(labelHelper(1), 'Interpreter','latex');
 						
 						if p.Results.showTitle
-						title(titleHelper(), 'Interpreter','latex');
+							title(titleHelper(), 'Interpreter','latex');
 						end
 						a = gca();
 						a.TickLabelInterpreter = 'latex';
@@ -1092,10 +1170,10 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			if p.Results.export
 				matlab2tikz(p.Results.exportOptions{:});
 			end
-		
+			
 			function myLabel = labelHelper(gridNumber)
 				if ~isempty(obj(rowIdx, columnIdx, figureIdx).gridName)
-				myLabel = ['$$', greek2tex(obj(rowIdx, columnIdx, figureIdx).gridName{gridNumber}), '$$'];
+					myLabel = ['$$', greek2tex(obj(rowIdx, columnIdx, figureIdx).gridName{gridNumber}), '$$'];
 				else
 					myLabel = '';
 				end
@@ -1149,9 +1227,9 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				si = num2cell( size(obj) );
 				s(si{:}) = struct();
 				for l = 1:numel(obj)
-
+					
 					doNotCopyProperties = obj(l).doNotCopy;
-
+					
 					for k = 1:length(properties)
 						if ~any(strcmp(doNotCopyProperties, properties{k}))
 							s(l).(properties{k}) = obj(1).(properties{k});
@@ -1159,7 +1237,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					end
 				end
 			else
-				% Without this else-case 
+				% Without this else-case
 				% this method is in conflict with the default matlab built-in
 				% calls struct(). When calling struct('myFieldName', myQuantity) this
 				% quantity-method is called (and errors) and not the
@@ -1192,28 +1270,57 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		end % flipGrid()
 		
 		function newObj = changeGrid(obj, gridNew, gridNameNew)
+			% CHANGEGRID change the grid of the quantity.
+			%	newObj = CHANGEGRID(obj, gridNew, gridNameNew)
 			% change the grid of the obj quantity. The order of grid and
 			% gridName in the obj properties remains unchanged, only the
 			% data points are exchanged.
+			%
+			%	newObj = CHANGEGRID(obj, domain) changes the domain of the
+			%	object specified by the name of DOMAIN into the
+			%	corresponding domain from DOMAIN.
+			%
+			%	example:
+			%	q.changeGrid( linspace(0,1)', 't')
+			%	will change the grid with the name 't' to the new grid linspace(0,1)'
 			if isempty(obj)
 				newObj = obj.copy();
 				return;
 			end
-			gridIndexNew = obj(1).gridIndex(gridNameNew);
-			myGrid = cell(1, numel(obj(1).grid));
-			myGridName = cell(1, numel(obj(1).grid));
-			for it = 1 : numel(myGrid)
-				myGrid{gridIndexNew(it)} = gridNew{it};
-				myGridName{gridIndexNew(it)} = gridNameNew{it};
+			
+			if isa(gridNew, 'quantity.Domain')
+				gridNameNew = {gridNew.name};
+				gridNew = {gridNew.grid};
+			else
+				gridNew  = misc.ensureIsCell(gridNew);
+				gridNameNew = misc.ensureIsCell(gridNameNew);
+			end
+			
+			if obj(1).isConstant
+				
+				newDomain(1:length( gridNew )) = quantity.Domain();
+				for i = 1 : length(gridNew)
+					newDomain(i) = ...
+						quantity.Domain(gridNameNew{i}, gridNew{i});
+				end
+			else
+				gridIndexNew = obj(1).domain.gridIndex(gridNameNew);
+				newDomain = obj(1).domain;
+
+				for i = 1 : length(gridIndexNew)
+					newDomain(gridIndexNew(i)) = ...
+						quantity.Domain(gridNameNew{i}, gridNew{i});
+				end
+				assert(isequal({newDomain.name}, obj(1).gridName), ...
+					'rearranging grids failed');
 			end
-			assert(isequal(myGridName(:), obj(1).gridName(:)), 'rearranging grids failed');
+			
 			
 			newObj = obj.copy();
-			for it = 1 : numel(obj)
-				newObj(it).valueDiscrete = obj(it).on(myGrid);
+			[newObj.domain] = deal(newDomain);
+			for i = 1 : numel(obj)
+				newObj(i).valueDiscrete = obj(i).on(newDomain);
 			end
-			%[newObj.derivatives] = deal({});
-			[newObj.grid] = deal(myGrid);
 		end
 		
 		function [lowerBounds, upperBounds] = getBoundaryValues(obj, varargin)
@@ -1247,7 +1354,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% state-transition matrix for a ordinary differential equation
 			%	d / dt x(t) = A(t) x(t)
 			% with A as this object. The system matrix can be constant or
-			% variable. It is the solution of 
+			% variable. It is the solution of
 			%	d / dt F(t,t0) = A(t) F(t,t0)
 			%		   F(t,t) = I.
 			% Optional parameters for the computation can be defined as
@@ -1267,7 +1374,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			ip = misc.Parser();
 			ip.addParameter('grid', []);
 			ip.addParameter('gridName1', '');
-			ip.addParameter('gridName2', '');	
+			ip.addParameter('gridName2', '');
 			ip.parse(varargin{:});
 			myGrid = ip.Results.grid;
 			gridName1 = ip.Results.gridName1;
@@ -1282,38 +1389,39 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				assert(numel(obj(1).gridName) == 1, 'No gridName is defined. Use name-value-pairs property "gridName1" to define a grid name');
 				gridName1 = obj(1).gridName{1};
 			end
-						
+			
 			if ip.isDefault('gridName2')
 				gridName2 = [gridName1 '0'];
 			end
 			
 			
 			assert(numel(myGrid) > 1, 'If the state transition matrix is computed for constant values, a spatial domain has to be defined!')
-				
+			
+			myDomain = [quantity.Domain(gridName1, myGrid), ...
+				        quantity.Domain(gridName2, myGrid)];
+			
 			if obj.isConstant
 				% for a constant system matrix, the matrix exponential
 				% function can be used.
 				z = sym(gridName1, 'real');
 				zeta = sym(gridName2, 'real');
 				f0 = expm(obj.atIndex(1)*(z - zeta));
-				F = quantity.Symbolic(f0, 'grid', {myGrid, myGrid});
-									
+				F = quantity.Symbolic(f0, 'domain', myDomain);
+				
 			elseif isa(obj, 'quantity.Symbolic')
 				f0 = misc.fundamentalMatrix.odeSolver_par(obj.function_handle, myGrid);
 				F = quantity.Discrete(f0, ...
 					'size', [size(obj, 1), size(obj, 2)], ...
-					'gridName', {gridName1, gridName2}, ...
-					'grid', {myGrid, myGrid});
+					'domain', myDomain);
 			else
 				f0 = misc.fundamentalMatrix.odeSolver_par( ...
 					obj.on(myGrid), ...
 					myGrid );
 				F = quantity.Discrete(f0, ...
 					'size', [size(obj, 1), size(obj, 2)], ...
-					'gridName', {gridName1, gridName2}, ...
-					'grid', {myGrid, myGrid});
+					'domain', myDomain);
 			end
-		end				
+		end
 		
 		function s = sum(obj, dim)
 			s = quantity.Discrete(sum(obj.on(), obj.nargin + dim), ...
@@ -1367,7 +1475,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		function P = mtimes(a, b)
 			% TODO rewrite the selection of the special cases! the
 			% if-then-cosntruct is pretty ugly!
-		
+			
 			% numeric computation of the matrix product
 			if isempty(b) || isempty(a)
 				% TODO: actually this only holds for multiplication of
@@ -1388,7 +1496,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					return
 				else
 					b = quantity.Discrete(b, 'size', size(b), 'grid', {}, ...
-					'gridName', {}, 'name', '');
+						'gridName', {}, 'name', '');
 				end
 			end
 			
@@ -1396,8 +1504,13 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				P = (b.' * a.').';
 				% this recursion is safe, because isa(b, 'double') is considered in
 				% the if above.
-				P.setName(['c ', b(1).name]);
-				return
+				
+				if isnumeric(P)
+					return
+				else
+					P.setName(['c ', b(1).name]);
+					return
+				end
 			end
 			if a.isConstant() && ~b.isConstant()
 				% If the first argument a is constant value, then bad
@@ -1408,11 +1521,14 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				P = (b' * a')';
 				P.setName([a(1).name, ' ', b(1).name]);
 				return
+			elseif a.isConstant() && b.isConstant()
+				P = a.on() * b.on();
+				return 
 			end
 			if isscalar(b)
 				% do the scalar multiplication in a loop
 				for k = 1:numel(a)
-					P(k) = innerMTimes(a(k), b);				
+					P(k) = innerMTimes(a(k), b);
 				end
 				P = reshape(P, size(a));
 				return
@@ -1421,13 +1537,13 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			if isscalar(a)
 				% do the scalar multiplication in a loop
 				for k = 1:numel(b)
-					P(k) = innerMTimes(a, b(k));				
+					P(k) = innerMTimes(a, b(k)); %#ok<AGROW>
 				end
 				P = reshape(P, size(b));
 				return
 			end
-
-			P = innerMTimes(a, b);			
+			
+			P = innerMTimes(a, b);
 		end
 		
 		function P = innerMTimes(a, b)
@@ -1438,40 +1554,52 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% misc.multArray is very efficient, but requires that the
 			% multiple dimensions of the input are correctly arranged.
 			[idx, permuteGrid] = computePermutationVectors(a, b);
-						
-			parameters = struct(a(1));
+			
 			parameters.name = [a(1).name, ' ', b(1).name];
-			parameters.grid = [...
-				a(1).grid(idx.A.common), ...
-				b(1).grid(~idx.B.common)];
-			parameters.gridName = [...
-				a(1).gridName(idx.A.grid), ...
-				b(1).gridName(~idx.B.common)];
-			
-			% select finest grid from a and b
-			parameters.grid = cell(1, numel(parameters.gridName));
-			[gridJoined, gridNameJoined] = gridJoin(a, b);
-			% gridJoin combines the grid of a and b while using the finer
-			% of both.
-			aGrid = cell(1, a.nargin); % for later call of a.on() with fine grid
-			bGrid = cell(1, b.nargin); % for later call of b.on() with fine grid
-			for it = 1 : numel(parameters.gridName)
-				parameters.grid{it} = gridJoined{...
-					strcmp(parameters.gridName{it}, gridNameJoined)};
-				if a.gridIndex(parameters.gridName{it})
-					aGrid{a.gridIndex(parameters.gridName{it})} = ...
-						parameters.grid{it};
+			parameters.figureID = a(1).figureID;
+			
+			domainA = a(1).domain;
+			domainB = b(1).domain;
+			
+			% The joined domain of quantity A and B is a mixture from both
+			% domains. If the domains have the same name, it must be
+			% checked if they have the same discretization. This is done by
+			% the function "gridJoin". It returns the finer grid.
+			% If the domain names do not coincide, they are just
+			% appended. At first, the domains of qunatity A, then the
+			% domains of quantity B. 
+			joinedDomain = [ gridJoin(domainA(idx.A.common), domainB(idx.B.common)), ...
+				domainA(~idx.A.common), domainB(~idx.B.common) ];
+			
+			%% generate the new grids
+			newDomainA = quantity.Domain.empty(); % for later call of a.on() with fine grid
+			newDomainB = quantity.Domain.empty(); % for later call of b.on() with fine grid
+			for i = 1 : numel(joinedDomain)
+
+				parameters.domain(i) = joinedDomain(i);
+				
+				% if there is a component of the original domain in the
+				% joined domain, it can happen the length of a domain has
+				% changed. Thus, it must be updated for a later evaluation.
+				idxA = domainA.gridIndex(joinedDomain(i).name);
+				idxB = domainB.gridIndex(joinedDomain(i).name);
+				
+				if idxA
+					newDomainA = [newDomainA, joinedDomain(i)]; %#ok<AGROW>
+				end
+				
+				if idxB
+					newDomainB = [newDomainB, joinedDomain(i)]; %#ok<AGROW>
 				end
-				if b.gridIndex(parameters.gridName{it})
-					bGrid{b.gridIndex(parameters.gridName{it})} = ...
-						parameters.grid{it};
-				end		
 			end
 			parameters = misc.struct2namevaluepair(parameters);
 			
-			valueA = permute(a.on(aGrid), idx.A.permute);
-			valueB = permute(b.on(bGrid), idx.B.permute);
+			% evaluation of the quantities on their "maybe" new domain.
+			valueA = a.on(newDomainA);
+			valueB = b.on(newDomainB);
 			
+			% do the multidimensional tensor multiplication and permute the
+			% values to the right order 
 			C = misc.multArray(valueA, valueB, idx.A.value(end), idx.B.value(1), idx.common);
 			C = permute(C, permuteGrid);
 			P = quantity.Discrete(C, parameters{:});
@@ -1494,11 +1622,11 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				objDiscreteReshaped = permute(reshape(objDiscreteOriginal, ...
 					[prod(obj(1).gridSize), size(obj)]), [2, 3, 1]);
 				invDiscrete = zeros([prod(obj(1).gridSize), size(obj)]);
-
+				
 				parfor it = 1 : size(invDiscrete, 1)
 					invDiscrete(it, :, :) = inv(objDiscreteReshaped(:, :, it));
 				end
-
+				
 				y = quantity.Discrete(reshape(invDiscrete, size(objDiscreteOriginal)),...
 					'size', size(obj), ...
 					'name', ['{(', obj(1).name, ')}^{-1}'], ...
@@ -1515,9 +1643,9 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			objT = obj.';
 			objCtMat = conj(objT.on());
 			objCt = quantity.Discrete(objCtMat, 'grid', obj(1).grid, ...
-			'gridName', obj(1).gridName, 'name', ['{', obj(1).name, '}^{H}']);
+				'gridName', obj(1).gridName, 'name', ['{', obj(1).name, '}^{H}']);
 		end % ctranspose(obj)
-
+		
 		function y = exp(obj)
 			% exp() is the exponential function using obj as the exponent.
 			y = quantity.Discrete(exp(obj.on()), ...
@@ -1534,7 +1662,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% The integral domain is specified by integralGrid.
 			
 			if obj.nargin > 1
-			assert(ischar(integralGridName), 'integralGrid must specify a gridName as a char');
+				assert(ischar(integralGridName), 'integralGrid must specify a gridName as a char');
 			else
 				integralGridName = obj(1).gridName;
 			end
@@ -1555,7 +1683,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			%	xNorm = sqrt(x.' * x).
 			% Optionally, a weight can be defined, such that instead
 			%	xNorm = sqrt(x.' * weight * x).
-						
+			
 			myParser = misc.Parser();
 			myParser.addParameter('weight', eye(size(obj, 1)));
 			myParser.parse(varargin{:});
@@ -1592,7 +1720,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% number of rows."
 			x = inv(A) * B;
 		end
- 		
+		
 		function x = mrdivide(B, A)
 			% mRdivide see doc mrdivide of matlab: "x = B/A solves the
 			% system of linear equations x*A = B for x. The matrices A and
@@ -1636,11 +1764,13 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% Check if there is any dimension which is zero
 			empty = any(size(obj) == 0);
 			
-			% If the constructor is called without arguments, a
-			% quantity.Discrete is created without an initialization of
-			% the grid. Thus, the quantity is not initialized and empty if
-			% the grid is not a cell.
-			empty = empty || ~iscell(obj(1).grid);
+			% If the constructor is called without any arguments, an object
+			% is still created ( this is required to allow object-arrays),
+			% but the object should be recognized as an empty object. Thus,
+			% we can test if the domain has been set with a domain object.
+			% This happens only in the part of the constructor which is
+			% entered if a valid quantity is initialized.
+			empty = empty || ~isa(obj(1).domain, 'quantity.Domain');
 			
 		end % isempty()
 		
@@ -1682,7 +1812,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		
 		function myGrid = gridOf(obj, myGridName)
 			if ~iscell(myGridName)
-				gridIdx = obj(1).gridIndex(myGridName);
+				gridIdx = obj(1).domain.gridIndex(myGridName);
 				if gridIdx > 0
 					myGrid = obj(1).grid{gridIdx};
 				else
@@ -1691,7 +1821,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			else
 				myGrid = cell(size(myGridName));
 				for it = 1 : numel(myGrid)
-					gridIdx = obj(1).gridIndex(myGridName{it});
+					gridIdx = obj(1).domain.gridIndex(myGridName{it});
 					if gridIdx > 0
 						myGrid{it} = obj(1).grid{gridIdx};
 					else
@@ -1701,37 +1831,6 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			end
 		end
 		
-		function [idx, logicalIdx] = gridIndex(obj, names)
-			%% GRIDINDEX returns the index of the grid
-			% [idx, log] = gridIndex(obj, names) searches in the gridName
-			% properties of obj for the "names" and returns its index as
-			% "idx" and its logical index as "log"
-			
-			if nargin == 1
-				names = obj(1).gridName;
-			end
-			
-			if ~iscell(names)
-				names = {names};
-			end
-			
-			idx = zeros(1, length(names));
-			nArgIdx = 1:obj.nargin();
-			
-			logicalIdx = false(1, obj.nargin());
-			
-			for k = 1:length(names)
-				log = strcmp(obj(1).gridName, names{k});
-				logicalIdx = logicalIdx | log;
-				if any(log)
-					idx(k) = nArgIdx(log);
-				else
-					idx(k) = 0;
-				end
-			end
-			
-		end
-		
 		function result = diff(obj, k, diffGridName)
 			% DIFF computation of the derivative
 			%	result = DIFF(obj, k, diffGridName) applies the
@@ -1752,11 +1851,21 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				return
 			end
 			
-			if nargin < 3 % if no diffGridName is specified, then the derivative 
+			if nargin < 3 % if no diffGridName is specified, then the derivative
 				% w.r.t. to all gridNames is applied
 				diffGridName = obj(1).gridName;
 			end
-				
+			
+			if isa(diffGridName, 'quantity.Domain')
+				% a quantity.Domain is used instead of a grid name 
+				%	-> get the grid name from the domain object
+				%	
+				% #todo@domain: make the default case to call with a
+				% quantity.Domain instead of a grid name. Then, the
+				% section about the grid selection and so can be simplified
+				%
+				diffGridName = {diffGridName.name};
+			end
 			
 			% diff for each element of diffGridName (this is rather
 			% inefficient, but an easy implementation of the specification)
@@ -1770,37 +1879,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					end
 				end
 			else
-			gridSelector = strcmp(obj(1).gridName, diffGridName);
-			gridSelectionIndex = find(gridSelector);
-			
-			spacing = gradient(obj(1).grid{gridSelectionIndex}, 1);
-			assert(numeric.near(spacing, spacing(1)), ...
-				'diff is currently only implemented for equidistant grid');
-			permutationVector = 1 : (numel(obj(1).grid)+ndims(obj));
-			
-			objDiscrete = permute(obj.on(), ...
-				[permutationVector(gridSelectionIndex), ...
-				permutationVector(permutationVector ~= gridSelectionIndex)]);
-			
-			if size(objDiscrete, 2) == 1
-				derivativeDiscrete = gradient(objDiscrete, spacing(1));
-			else
-				[~, derivativeDiscrete] = gradient(objDiscrete, spacing(1));
-			end
-			
-			rePermutationVector = [2:(gridSelectionIndex), ...
-				1, (gridSelectionIndex+1):ndims(derivativeDiscrete)];
-			result = quantity.Discrete(...
-				permute(derivativeDiscrete, rePermutationVector), ...
-				'size', size(obj), 'grid', obj(1).grid, ...
-				'gridName', obj(1).gridName, ...
-					'name', ['(d_{', diffGridName, '}', obj(1).name, ')']);
-				
-				if k > 1
-% 			% if a higher order derivative is requested, call the function
-% 			% recursivly until the first-order derivative is reached
-					result = result.diff(k-1, diffGridName);
-				end
+				result = obj.diff_inner(k, diffGridName);
 			end
 		end
 		
@@ -1836,15 +1915,15 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			if iscell(intGridName) && numel(intGridName) > 1
 				obj = obj.int(intGridName{1}, a(1), b(1));
 				I = obj.int(intGridName{2:end}, a(2:end), b(2:end));
-				return				
+				return
 			end
 			[idxGrid, isGrid] = obj.gridIndex(intGridName);
 			if nargin == 4
 				I = cumInt( obj, varargin{:});
 				return;
-% 				
-% 				assert(all((varargin{2} == a(idxGrid)) & (varargin{3} == b(idxGrid))), ...
-% 					'only integration from beginning to end of domain is implemented');
+				%
+				% 				assert(all((varargin{2} == a(idxGrid)) & (varargin{3} == b(idxGrid))), ...
+				% 					'only integration from beginning to end of domain is implemented');
 			end
 			
 			%% do the integration over one dimension
@@ -1855,12 +1934,12 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			[domainIdx{1:obj(1).nargin}] = deal(':');
 			currentGrid = obj(1).grid{ idxGrid };
 			domainIdx{idxGrid} = find(currentGrid >= a(idxGrid) & currentGrid <= b(idxGrid));
-
-			myGrid = currentGrid(domainIdx{idxGrid});
 			
+			myGrid = currentGrid(domainIdx{idxGrid});
+
 			for kObj = 1:numel(obj)
 				J = numeric.trapz_fast_nDim(myGrid, obj(kObj).atIndex(domainIdx{:}), idxGrid);
-
+				
 				% If the quantity is integrated, so that the first
 				% dimension collapses, this dimension has to be shifted
 				% away, so that the valueDiscrete can be set correctly.
@@ -1868,21 +1947,20 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 					J = shiftdim(J);
 				end
 				
-				if all(isGrid)
-					grdName = '';
-					newGrid = {};
-				else
-					grdName = obj(kObj).gridName{~isGrid};
-					newGrid = obj(kObj).grid{~isGrid};
-				end
-				
 				% create result:
 				I(kObj).valueDiscrete = J;
-				I(kObj).gridName = grdName;
-				I(kObj).grid = newGrid;
 				I(kObj).name = ['int(' obj(kObj).name ')'];
 			end
-				
+			
+			if all(isGrid)
+				newDomain = quantity.Domain();
+			else
+				newDomain = quantity.Domain(obj(kObj).gridName{~isGrid}, ...
+					obj(kObj).grid{~isGrid});
+			end
+			
+			[I.domain] = deal(newDomain);
+			
 		end
 		
 		function result = cumInt(obj, domain, lowerBound, upperBound)
@@ -1898,7 +1976,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			
 			% input parser since some default values depend on intGridName.
 			myParser = misc.Parser;
-			myParser.addRequired('domain', @(d) obj.gridIndex(d) ~= 0);
+			myParser.addRequired('domain', @(d) obj(1).domain.gridIndex(d) ~= 0);
 			myParser.addRequired('lowerBound', ...
 				@(l) isnumeric(l) || ischar(l) );
 			myParser.addRequired('upperBound', ...
@@ -1907,7 +1985,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			
 			% get grid
 			myGrid = obj(1).grid;
-			intGridIdx = obj.gridIndex(domain);
+			intGridIdx = obj(1).domain.gridIndex(domain);
 			
 			% integrate
 			F = numeric.cumtrapz_fast_nDim(myGrid{intGridIdx}, ...
@@ -1915,7 +1993,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			result = quantity.Discrete(F, 'grid', myGrid, ...
 				'gridName', obj(1).gridName);
 			
-			% int_lowerBound^upperBound f(.) = 
+			% int_lowerBound^upperBound f(.) =
 			%	F(upperBound) - F(lowerBound)
 			result = result.subs({domain}, upperBound) ...
 				- result.subs({domain}, lowerBound);
@@ -1923,7 +2001,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				result.setName(deal(['int(', obj(1).name, ')']));
 			end
 		end
-				
+		
 		function C = plus(A, B)
 			%% PLUS is the sum of two quantities.
 			assert(isequal(size(A), size(B)), 'plus() not supports mismatching sizes')
@@ -1936,58 +2014,31 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% for support of numeric inputs:
 			if ~isa(A, 'quantity.Discrete')
 				if isnumeric(A)
-					A = quantity.Discrete(A, 'name', 'c', 'gridName', {}, 'grid', {});
+					A = quantity.Discrete(A, 'name', 'c', 'domain', quantity.Domain.empty());
 				else
 					error('Not yet implemented')
 				end
 			elseif ~isa(B, 'quantity.Discrete')
 				if isnumeric(B)
-					B = quantity.Discrete(B, 'name', 'c', 'gridName', {});
+					B = quantity.Discrete(B, 'name', 'c', 'domain', quantity.Domain.empty());
 				else
 					B = quantity.Discrete(B, 'name', 'c', 'gridName', A(1).gridName, 'grid', A.grid);
 				end
 			end
 			
 			% combine both domains with finest grid
-			[gridJoined, gridNameJoined] = gridJoin(A, B);
-			gridJoinedLength = cellfun(@(v) numel(v), gridJoined);
+			joinedDomain = gridJoin(A(1).domain, B(1).domain);
 			
-			[aDiscrete] = A.expandValueDiscrete(gridJoined, gridNameJoined, gridJoinedLength);
-			[bDiscrete] = B.expandValueDiscrete(gridJoined, gridNameJoined, gridJoinedLength);
-
-			% create result object
-			C = quantity.Discrete(aDiscrete + bDiscrete, ...
-				'grid', gridJoined, 'gridName', gridNameJoined, ...
-				'size', size(A), 'name', [A(1).name, '+', B(1).name]);
-		end
-		
-		function [valDiscrete] = expandValueDiscrete(obj, gridJoined, gridNameJoined, gridJoinedLength)
-			% EXPANDVALUEDISCRETE
-			%	[valDiscrete, gridNameSorted] = ...
-			%       expandValueDiscrete(obj, gridIndex, valDiscrete)
-			%	Expanses the value of obj, so that 
-			%	todo
 			
-			% get the index of obj.grid in the joined grid
-			gridLogical = logical( obj.gridIndex(gridNameJoined) );
-			
-			valDiscrete = obj.on(gridJoined(gridLogical > 0), gridNameJoined(gridLogical > 0));
-			oldDim = ndims(valDiscrete);
-			valDiscrete = permute(valDiscrete, [(1:sum(~gridLogical)) + oldDim, 1:oldDim] );
-			valDiscrete = repmat(valDiscrete, [gridJoinedLength(~gridLogical), ones(1, ndims(valDiscrete))]);
-% 			
-   			valDiscrete = reshape(valDiscrete, ...
-  				[gridJoinedLength(~gridLogical), gridJoinedLength(gridLogical), size(obj)]);
- 			
-% 			% permute valDiscrete such that grids are in the order specified
-% 			% by gridNameJoined.
- 			gridIndex = 1:numel(gridLogical);		
-			gridOrder = [gridIndex(~gridLogical), gridIndex(gridLogical)];
-  			valDiscrete = permute(valDiscrete, [gridOrder, numel(gridLogical)+(1:ndims(obj))]);
+			[aDiscrete] = A.expandValueDiscrete(joinedDomain);
+			[bDiscrete] = B.expandValueDiscrete(joinedDomain);
 			
+			% create result object
+			C = quantity.Discrete(aDiscrete + bDiscrete, ...
+				'domain', joinedDomain, ...
+				'name', [A(1).name, '+', B(1).name]);
 		end
 		
-		
 		function C = minus(A, B)
 			% minus uses plus()
 			C = A + (-B);
@@ -1997,7 +2048,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			elseif isnumeric(B)
 				[C.name] = deal([A(1).name, '-c']);
 			else
-			[C.name] = deal([A(1).name, '-', B(1).name]);
+				[C.name] = deal([A(1).name, '-', B(1).name]);
 			end
 		end
 		
@@ -2034,7 +2085,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		function maxValue = max(obj)
 			% max returns the maximal value of all elements of A over all
 			% variables as a double array.
-			maxValue = reshape(max(obj.on(), [], 1:obj(1).nargin), [size(obj)]);			
+			maxValue = reshape(max(obj.on(), [], 1:obj(1).nargin), [size(obj)]);
 		end
 		
 		function [i, m, s] = near(obj, B, varargin)
@@ -2078,7 +2129,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		function minValue = min(obj)
 			% max returns the minimum value of all elements of A over all
 			% variables as a double array.
-			minValue = reshape(min(obj.on(), [], 1:obj(1).nargin), [size(obj)]);			
+			minValue = reshape(min(obj.on(), [], 1:obj(1).nargin), [size(obj)]);
 		end % min()
 		
 		function absQuantity = abs(obj)
@@ -2086,9 +2137,9 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			if isempty(obj)
 				absQuantity = quantity.Discrete.empty(size(obj));
 			else
-			absQuantity = quantity.Discrete(abs(obj.on()), ...
-				'grid', obj(1).grid, 'gridName', obj(1).gridName, ...
-				'size', size(obj), 'name', ['|', obj(1).name, '|']);	
+				absQuantity = quantity.Discrete(abs(obj.on()), ...
+					'grid', obj(1).grid, 'gridName', obj(1).gridName, ...
+					'size', size(obj), 'name', ['|', obj(1).name, '|']);
 			end
 		end % abs()
 		
@@ -2111,7 +2162,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 		function meanValue = mean(obj)
 			% mean returns the mean value of all elements of A over all
 			% variables as a double array.
-			meanValue = reshape(mean(obj.on(), 1:obj(1).nargin), size(obj));			
+			meanValue = reshape(mean(obj.on(), 1:obj(1).nargin), size(obj));
 		end
 		
 		function medianValue = median(obj)
@@ -2120,18 +2171,40 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% ATTENTION:
 			% median over multiple dimensions is only possible from MATLAB 2019a and above!
 			% TODO Update Matlab!
-			medianValue = reshape(median(obj.on(), 1:obj(1).nargin), size(obj));			
+			medianValue = reshape(median(obj.on(), 1:obj(1).nargin), size(obj));
 		end
-						
-		function q = obj2value(obj)
-			% for a case with a 3-dimensional grid, the common cell2mat
-			% with reshape did not work. So it has to be done manually:
+		
+		function value = obj2value(obj, myDomain)
+			% OBJ2VALUE make the stored data in valueDiscrete available for
+			% in the output format
+			%	value = obj2value(obj) returns the valueDiscrete in the
+			%	form size(value) = [gridLength, objSize]
+			%	obj2value(obj, myDomain) returns the valueDiscrete in the
+			%	form size(value) = [myDomain.gridLength objSize]
 			
 			v = zeros([numel(obj(1).valueDiscrete), numel(obj)]);
 			for k = 1:numel(obj)
 				v(:,k) = obj(k).valueDiscrete(:);
 			end
-			q = reshape(v, [obj(1).gridSize(), size(obj)]);
+			
+			value = reshape(v, [obj(1).gridSize(), size(obj)]);
+				
+			if nargin == 2 && ~isequal( myDomain, obj(1).domain )
+				% if a new domain is specified for the evaluation of
+				% the quantity, ...
+				if obj.isConstant
+					% ... duplicate the constant value on the desired
+					% domain
+					value = repmat(v, [cellfun(@(v) numel(v), {myDomain.grid}), ones(1, length(size(obj)))]);
+				else
+					%... do an interpolation based on the old data.
+				indexGrid = arrayfun(@(s) 1:s, size(obj), 'UniformOutput', false);
+				tempInterpolant = numeric.interpolant(...
+					[obj(1).grid, indexGrid{:}], value);
+				tempGrid = {myDomain.grid};
+				value = tempInterpolant.evaluate(tempGrid{:}, indexGrid{:});
+				end
+			end
 			
 		end
 		
@@ -2166,48 +2239,43 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 	%%
 	methods (Static)
 		
-		function P = zeros(valueSize, grid, varargin)
+		function P = zeros(valueSize, domain, varargin)
 			%ZEROS initializes an zero quantity.Discrete object
-			%   P = zeros(gridSize, valueSize, grid) returns a
-			%   quantity.Discrete object that has only zero entries on a
-			%   grid with defined by the cell GRID and the value size
-			%   defined by the size-vector VALUESIZE
-			%       P = quantity.Discrete([n,m], {linspace(0,1)',
-			%                                       linspace(0,10)});
-			%       creates an (n times m) zero quantity.Discrete on the
-			%       grid (0,1) x (0,10)
-			%
-			if ~iscell(grid)
-				grid = {grid};
+			%	P = zeros(VALUESIZE, DOMAIN) creates a matrix of size
+			%	VALUESIZE on the DOMAIN with zero entries.
+			
+			myParser = misc.Parser();
+			myParser.addParameter('gridName', '');
+			myParser.parse(varargin{:});
+			
+			if ~isa(domain, 'quantity.Domain')
+				% if the input parameter DOMAIN is not a quantity.Domain
+				% object. It is assumed that it is a grid.
+				grids = misc.ensureIsCell(domain);
+				gridNames = misc.ensureIsCell( myParser.Results.gridName );
+				domain = quantity.Domain.gridCells2domain(grids, gridNames);
 			end
-			gridSize = cellfun('length', grid);
-			O = zeros([gridSize(:); valueSize(:)]');
-			P = quantity.Discrete(O, 'size', valueSize, 'grid', grid, varargin{:});
+			
+			O = zeros([domain.gridLength, valueSize(:)']);
+			P = quantity.Discrete(O, 'size', valueSize, 'domain', domain, varargin{:});
 		end
 		
 		function q = value2cell(value, gridSize, valueSize)
 			% VALUE2CELL
-			gs = num2cell(gridSize(:));
-			vs =  arrayfun(@(n) ones(n, 1), valueSize, 'UniformOutput', false);
-			s = [gs(:); vs(:)]; %{gs{:}, vs{:}};%
-			q = reshape(mat2cell(value, s{:}), valueSize);
-		end
-		
-		function g = defaultGrid(gridSize)
 			
-			g = cell(1, length(gridSize));
-			for k = 1:length(gridSize)
-				
-				o = ones(1, length(gridSize) + 1); % + 1 is required to deal with one dimensional grids
-				o(k) = gridSize(k);
-				O = ones(o);
-				O(:) = linspace(0, 1, gridSize(k));
-				
-				g{k} = O;
+			fullSize = size(value);
+			
+			myGridSize = num2cell( fullSize(1:length(gridSize)) );
+			
+			if nargin == 2			
+				valueSize = [fullSize(length(myGridSize)+1:length(fullSize)), 1, 1];
 			end
+			
+			myValueSize =  arrayfun(@(n) ones(n, 1), valueSize, 'UniformOutput', false);
+			s = [myGridSize(:); myValueSize(:)]; %{gs{:}, vs{:}};%
+			q = reshape(mat2cell(value, s{:}), valueSize);
 		end
 		
-		
 		function [p, q, parameters, gridSize] = inputNormalizer(a, b)
 			
 			parameters = [];
@@ -2239,10 +2307,75 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 	end %% (Static)
 	methods(Access = protected)
 		
+		function [valDiscrete] = expandValueDiscrete(obj, newDomain)
+			% EXPANDVALUEDISCRETE expand the discrete value on the
+			% newDomain
+			%	[valDiscrete] = ...
+			%       expandValueDiscrete(obj, newDomain) expands the
+			%       discrete values on a new domain. So that a function
+			%			f(z,t) = f(z) + f(t)
+			%	can be computed.
+		
+			gridJoinedLength = newDomain.gridLength;
+			
+			% get the index of obj.grid in the joined grid
+			[idx, logicalIdx] = newDomain.gridIndex({obj(1).domain.name});
+			% evaluate the 
+			valDiscrete = obj.on( newDomain(logicalIdx) );
+			oldDim = ndims(valDiscrete);
+			valDiscrete = permute(valDiscrete, [(1:sum(~logicalIdx)) + oldDim, 1:oldDim] );
+			valDiscrete = repmat(valDiscrete, [gridJoinedLength(~logicalIdx), ones(1, ndims(valDiscrete))]);
+			%
+			valDiscrete = reshape(valDiscrete, ...
+				[gridJoinedLength(~logicalIdx), gridJoinedLength(logicalIdx), size(obj)]);
+			
+			% permute valDiscrete such that grids are in the order specified
+			% by gridNameJoined.
+			gridIndex = 1:numel(logicalIdx);
+			gridOrder = [gridIndex(~logicalIdx), gridIndex(logicalIdx)];
+			gridIndex(gridOrder) = 1:numel(logicalIdx);
+			
+			valDiscrete = permute(valDiscrete, [gridIndex, numel(logicalIdx)+(1:ndims(obj))]);
+		end
+		
+		function result = diff_inner(obj, k, diffGridName)
+			gridSelector = strcmp(obj(1).gridName, diffGridName);
+			gridSelectionIndex = find(gridSelector);
+
+			spacing = gradient(obj(1).grid{gridSelectionIndex}, 1);
+			assert(numeric.near(spacing, spacing(1)), ...
+				'diff is currently only implemented for equidistant grid');
+			permutationVector = 1 : (numel(obj(1).grid)+ndims(obj));
+
+			objDiscrete = permute(obj.on(), ...
+				[permutationVector(gridSelectionIndex), ...
+				permutationVector(permutationVector ~= gridSelectionIndex)]);
+
+			if size(objDiscrete, 2) == 1
+				derivativeDiscrete = gradient(objDiscrete, spacing(1));
+			else
+				[~, derivativeDiscrete] = gradient(objDiscrete, spacing(1));
+			end
+
+			rePermutationVector = [2:(gridSelectionIndex), ...
+				1, (gridSelectionIndex+1):ndims(derivativeDiscrete)];
+			result = quantity.Discrete(...
+				permute(derivativeDiscrete, rePermutationVector), ...
+				'size', size(obj), 'grid', obj(1).grid, ...
+				'gridName', obj(1).gridName, ...
+				'name', ['(d_{', diffGridName, '}', obj(1).name, ')']);
+
+			if k > 1
+				% 			% if a higher order derivative is requested, call the function
+				% 			% recursivly until the first-order derivative is reached
+				result = result.diff(k-1, diffGridName);
+			end
+		end
+		
 		function [idx, permuteGrid] = computePermutationVectors(a, b)
 			% Computes the required permutation vectors to use
 			% misc.multArray for multiplication of matrices
-						
+			
 			uA = unique(a(1).gridName, 'stable');
 			assert(numel(uA) == numel(a(1).gridName), 'Gridnames have to be unique!');
 			uB = unique(b(1).gridName, 'stable');
@@ -2313,7 +2446,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% quantity.Discrete class.
 			permuteGrid = [idxGrid(lGridA), idxGrid(lGridB), idxGrid(lGridVA), idxGrid(lGridVB)];
 		end
-
+		
 		
 		function f = setValueContinuous(obj, f)
 		end
@@ -2328,10 +2461,6 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 			% copied.
 		end
 		
-		% 		function s = getHeader(obj)
-		% 			s = 'TEST'
-		% 		end
-		
 		function s = getPropertyGroups(obj)
 			% Function to display the correct values
 			
@@ -2339,14 +2468,14 @@ classdef  (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
 				s = getPropertyGroups@matlab.mixin.CustomDisplay(obj);
 				return;
 			else
-				s = getPropertyGroups@matlab.mixin.CustomDisplay(obj(1));	
+				s = getPropertyGroups@matlab.mixin.CustomDisplay(obj(1));
 			end
 			
 			if numel(obj) ~= 1
 				s.PropertyList.valueDiscrete = ...
 					[sprintf('%ix', gridSize(obj), size(obj)) sprintf('\b')];
 			end
-		end
+		end % getPropertyGroups
 	end % methods (Access = protected)
 	
 	methods ( Static, Access = protected )
diff --git a/+quantity/Domain.m b/+quantity/Domain.m
new file mode 100644
index 0000000000000000000000000000000000000000..f9fcfbd62632982b5e632aef26a2ee109f01ee44
--- /dev/null
+++ b/+quantity/Domain.m
@@ -0,0 +1,405 @@
+classdef Domain < handle & matlab.mixin.CustomDisplay 
+	%DOMAIN class to describes a range of values on which a function can be defined.
+	
+	% todo:
+	%	* EquidistantDomain
+	%	* multi dimensional
+	
+	properties (SetAccess = protected)
+		% The discrete points of the grid for the evaluation of a
+		% continuous quantity. For an example, the function f(x) should be
+		% considered on the domain x \in X = [0, 1]. Then, a grid can be
+		% generated by X_grid = linspace(0, 1).
+		grid double {mustBeReal, mustBeFinite, mustBe.ascending};
+		% TODO add mustBeVector
+		
+		% a speaking name for this domain; Should be unique, so that the
+		% domain can be identified by the name.
+		name char;
+	end
+	
+	properties (Dependent)
+		n;			% number of discretization points
+		lower;		% lower bound of the domain
+		upper;		% upper bound of the domain
+	end
+
+	properties (Dependent, Hidden)
+		ones;
+	end
+	
+	methods
+		function obj = Domain(name, grid)
+			%DOMAIN initialize the domain
+			%
+			if nargin >= 1
+				obj.grid = grid(:);
+				obj.name = name;
+			end
+		end
+		
+		function i = get.ones(obj)
+			i = ones(size(obj.grid));
+		end
+		
+		function n = get.n(obj)
+			if isempty(obj.grid)
+				n = [];
+			else
+				n = length(obj.grid);
+			end
+		end
+		
+		function lower = get.lower(obj)
+			lower = min( obj.grid );
+		end
+		
+		function upper = get.upper(obj)
+			upper = max( obj.grid );
+		end
+		
+		function nd = ndims(obj)
+			%NDIMS number of dimensions of the domain specified by the
+			%object-array.
+			nd = size(obj(:), 1);
+		end
+		
+		function n = numGridElements(obj)
+			% NUMGRIDLEMENTS returns the number of the elements of the grid
+			n = prod([obj.n]);
+		end
+		
+		function s = gridLength(obj)
+			%GRIDLENGTH number of discretization points for each grid in the
+			%object-array.
+			s = [obj.n];
+		end
+		
+% 		function s = eq(obj, obj2)
+% 			
+% 			if isempty(obj) && isempty(obj2)
+% 				% if both are empty -> they are equal
+% 				s = true;
+% 			elseif isempty(obj) || isempty(obj2)
+% 				% if only one domain is empty -> they are not equal
+% 				s = false;
+% 			else
+% 				% if both are not empty: check if the grids and the
+% 				% gridNames are equal
+% 				s = all(obj.gridLength == obj2.gridLength);
+% 			
+% 			for k = 1:numel(obj)	
+% 				s = s && all(obj(k).grid == obj2(k).grid);
+% 				s = s && strcmp(obj(k).name, obj2(k).name);
+% 			end
+% 			end
+% 		end
+% 		
+		function l = ne(obj, obj2)
+			l = ~(obj == obj2);
+		end
+				
+		function [joinedDomain, index] = join(domain1, domain2)
+			%% gridJoin combines the grid and gridName of two objects (obj1,
+			% obj2), such that every gridName only occurs once and that the
+			% finer grid of both is used.
+			arguments
+				domain1 quantity.Domain = quantity.Domain.empty
+				domain2 quantity.Domain = quantity.Domain.empty
+			end
+			
+			if nargin == 1 && length(domain1) == 1
+				joinedDomain = domain1;
+				index = 1;
+				return;
+			end
+			
+			[joinedGrid, index] = unique({domain1.name, domain2.name}, 'stable');
+			
+			joinedDomain = quantity.Domain.empty;
+			
+			% #todo@domain: the gird comparison seems to be very
+			% complicated
+			
+			% check for each grid if it is in the domain of obj1 or obj2 or
+			% both
+			for i = 1 : numel(joinedGrid)
+				currentGridName = joinedGrid{i};
+				[index1, logicalIndex1] = domain1.gridIndex(currentGridName);
+				[index2, logicalIndex2] = domain2.gridIndex(currentGridName);
+				
+				%
+				% 				if ~any(logicalIndex1)
+				% 					joinedDomain(i) = obj2(index2);
+				% 				elseif ~any(logicalIndex2)
+				% 					joinedDomain(i) = obj1(index1);
+				% 				else
+				%
+				% Check if a domain is in both domains:
+				%	-> then take the finer one of both
+				if any(logicalIndex1) && any(logicalIndex2)
+					tempDomain1 = domain1(index1);
+					tempDomain2 = domain2(index2);
+					
+					assert(tempDomain1.lower == tempDomain2.lower, 'Grids must have same domain for gridJoin')
+					assert(tempDomain1.upper == tempDomain2.upper, 'Grids must have same domain for gridJoin')
+
+					if tempDomain1.gridLength > tempDomain2.gridLength
+						joinedDomain(i) = tempDomain1;
+					else
+						joinedDomain(i) = tempDomain2;
+					end
+				% If it is not in both, -> just take the normal grid
+				elseif any(logicalIndex1)
+					joinedDomain(i) = domain1(index1);
+				elseif any(logicalIndex2)
+					joinedDomain(i) = domain2(index2);
+				end
+				
+			end
+		end
+		
+		function [joinedDomain, index] = gridJoin(obj1, obj2)
+			[joinedDomain, index] = obj1.join(obj2);
+		end % gridJoin()
+				
+		function [idx, logicalIdx] = gridIndex(obj, names)
+			%% GRIDINDEX returns the index of the grid
+			% [idx, log] = gridIndex(obj, names) searches in the name
+			% properties of obj for the "names" and returns its index as
+			% "idx" and its logical index as "log"
+			arguments
+				obj
+				names = {obj.name};
+			end
+			
+			if isa(names, 'quantity.Domain')
+				names = {names.name};
+			end
+			
+			names = misc.ensureIsCell(names);
+			
+			idx = zeros(1, length(names));
+			nArgIdx = 1:obj.ndims();
+			
+			logicalIdx = false(1, obj.ndims());
+			
+			for k = 1:length(names)
+				log = strcmp({obj.name}, names{k});
+				logicalIdx = logicalIdx | log;
+				if any(log)
+					% #todo@domain: if the index for a grid name is searched in a
+					% list with multiple grids but same names, only the first
+					% occurrence is returned. this should be changed...
+					tmpFirst = nArgIdx(log);
+					idx(k) = tmpFirst(1);
+				else
+					idx(k) = 0;
+				end
+			end
+		end % gridIndex
+		
+		function i = isempty(obj)
+			i = any(size(obj) == 0);
+			i = i || any( cellfun(@isempty, {obj.grid} ) );
+		end
+		
+		function i = isSubDomainOf(obj, d)
+			% ISSUBDOMAIN 
+			%	i = isSubDomainOf(OBJ, D) checks if the domain OBJ is a
+			%	sub-domain of D.
+			
+			i = obj.lower >= d.lower && ...
+				obj.upper <= d.upper;	
+		end % isSubDomainOf
+		
+		function matGrid = ndgrid(obj)
+			% ndgrid calles ndgrid for the default grid, if no other grid
+			% is specified. Empty grid as input returns empty cell as
+			% result.
+			if isempty(obj)
+				matGrid = {};
+			else
+				grids = {obj.grid};
+				[matGrid{1:obj.ndims}] = ndgrid(grids{:});
+			end
+		end % ndgrid()
+		
+		function [idx, newDomain] = getPermutationIdx(obj, order)
+			
+			if isa(order, 'quantity.Domain')
+				names = {order.name};
+			elseif ischar([order{:}])
+				names = order;
+			else
+				error('the input parameter order must be a array of quantity.Domain objects or a cell-array with string')
+			end
+						
+			idx = cellfun(@(v) obj.gridIndex(v), names);
+			
+			if isa(order, 'quantity.Domain')
+				newDomain = order;
+			else			
+				newDomain = obj(idx);
+			end
+		end
+		
+		
+		function [newObj, I] = sort(obj, varargin)
+			%SORT sorts the grid of the object in a desired order
+			% obj = sortGrid(obj) sorts the grid in alphabetical order.
+			% obj = sort(obj, 'descend') sorts the grid in descending
+			% alphabetical order.
+			
+			if nargin == 2 && strcmp(varargin{1}, 'descend')
+				descend = 1;
+			elseif nargin == 1 || strcmp(varargin{1}, 'ascend')
+				descend = 0;
+			else
+				error(['Unknown sorting order' char( varargin{1} )])
+			end
+			
+			% only sort the grids if there is something to sort
+			if obj.ndims > 1
+				gridNames = {obj.name};
+				
+				% this is the default case for ascending alphabetical
+				% order
+				[sortedNames, I] = sort(gridNames);
+				
+				% if descending: flip the order of the entries
+				if descend
+					sortedNames = flip(sortedNames);
+					I = flip(I);
+				end
+				
+				% sort the grid entries
+				myGrids = {obj.grid};
+				myGrids = myGrids(I);
+				
+				newObj(1:length(myGrids)) = quantity.Domain();
+				for k = 1:length(myGrids)
+					newObj(k) = quantity.Domain(sortedNames{k}, myGrids{k});
+				end
+			else
+				newObj = obj;
+			end
+		end% sort()			
+	end
+	
+	methods (Access = protected)
+		
+		function s = getPropertyGroups(obj)
+			% Function to display the correct values
+			
+			if isempty(obj)
+				s = getPropertyGroups@matlab.mixin.CustomDisplay(obj);
+				return;
+			else
+				s = getPropertyGroups@matlab.mixin.CustomDisplay(obj(1));
+			end
+			
+			if numel(obj) ~= 1
+				
+				grids = {obj.grid};
+				
+				sizes = cellfun(@(g)size(g), grids, 'UniformOutput', false);							
+								
+				s.PropertyList.grid = ...
+					[sprintf('%ix%i,  ', cell2mat(sizes)) sprintf('\b\b\b')];
+				s.PropertyList.name = ...
+					[sprintf('%s,  ', obj.name) sprintf('\b\b\b')];
+				s.PropertyList.lower = ... 
+					[sprintf('%d,  ', obj.lower) sprintf('\b\b\b')];
+				s.PropertyList.upper = ... 
+					[sprintf('%.3f,  ', obj.upper) sprintf('\b\b\b')];
+				s.PropertyList.n = ...
+					[sprintf('%i,  ', obj.n) sprintf('\b\b\b')];
+			end
+		end % getPropertyGroups
+		
+	end % Access = protected
+	
+	methods (Static)
+		
+		function d = gridCells2domain(grids, gridNames)
+			grids = misc.ensureIsCell(grids);
+			gridNames = misc.ensureIsCell(gridNames);
+			
+			assert(length( grids ) == length(gridNames))
+
+			d = quantity.Domain.empty();
+			
+			for k = 1:length(grids)
+				d = [d quantity.Domain(gridNames{k}, grids{k})];
+			end
+		end
+		
+		function g = defaultGrid(gridSize, name)
+			
+			if nargin == 1
+				% If no names are specified, chose x_1, x_2, ... as default
+				% names.
+				name = cell(1, length(gridSize));
+				for k = 1:length(gridSize)
+					name{k} = ['x_' num2str(k)];
+				end
+			end
+			
+			% generate a default gird with given sizes
+			g = quantity.Domain.empty();
+			for k = 1:length(gridSize)
+				
+				o = ones(1, length(gridSize) + 1); % + 1 is required to deal with one dimensional grids
+				o(k) = gridSize(k);
+				O = ones(o);
+				O(:) = linspace(0, 1, gridSize(k));
+				
+				g(k) = quantity.Domain(name{k}, O);
+			end
+		end
+		
+		function [myDomain, unmatched] = parser(varargin)
+			%% domain parser
+			domainParser = misc.Parser();
+			domainParser.addParameter('domain', {}, @(g) isa(g, 'quantity.Domain'));
+			domainParser.addParameter('gridName', '', @(g) ischar(g) || iscell(g));
+			domainParser.addParameter('grid', [], @(g) isnumeric(g) || iscell(g));
+			domainParser.parse(varargin{:});
+
+			if domainParser.isDefault('domain') && ...
+					( domainParser.isDefault('grid') || ...
+					  domainParser.isDefault('gridName') )
+				% case 1: nothing about the grid is defined
+					error('No domain is specified! A domain is obligatory for the initialization of a quantity.')
+			elseif domainParser.isDefault('domain')
+				% case 3: the gridNames and the gridValues are defined:
+				%	-> initialize quantity.Domain objects with the
+				%	specified values
+
+				myGridName = misc.ensureIsCell(domainParser.Results.gridName);
+				myGrid = misc.ensureIsCell(domainParser.Results.grid);
+
+				assert(isequal(numel(myGrid), numel(myGridName)), ...
+					'number of grids and gridNames must be equal');
+
+				% initialize the domain objects
+				myDomain = quantity.Domain.empty();
+				for k = 1:numel(myGrid)
+					myDomain(k) = quantity.Domain(myGridName{k}, myGrid{k});
+				end
+			else
+				% else case: the domains are specified as domain
+				% objects.
+				myDomain = domainParser.Results.domain;
+			end	
+			
+			assert(numel(myDomain) == numel(unique({myDomain.name})), ...
+				'The names of the domain must be unique');
+			
+			unmatched = domainParser.UnmatchedNameValuePair;
+		end
+		
+	end
+end
+
diff --git a/+quantity/EquidistantDomain.m b/+quantity/EquidistantDomain.m
new file mode 100644
index 0000000000000000000000000000000000000000..ff763973dfff6ae55c52e884a40144faf8264875
--- /dev/null
+++ b/+quantity/EquidistantDomain.m
@@ -0,0 +1,24 @@
+classdef EquidistantDomain < quantity.Domain
+	%EQUIDISTANTDOMAIN class to handle the discretization of the range of
+	%definition of a function. The discretization points are equally
+	%distributed over the domain.
+	
+	properties
+		Property1
+	end
+	
+	methods
+		function obj = untitled(inputArg1,inputArg2)
+			%UNTITLED Construct an instance of this class
+			%   Detailed explanation goes here
+			obj.Property1 = inputArg1 + inputArg2;
+		end
+		
+		function outputArg = method1(obj,inputArg)
+			%METHOD1 Summary of this method goes here
+			%   Detailed explanation goes here
+			outputArg = obj.Property1 + inputArg;
+		end
+	end
+end
+
diff --git a/+quantity/Function.m b/+quantity/Function.m
index 4b090acdc57b53388e2b9c58fb142af9540ba511..dc15eb148e7cbe49544e23c5928fbe061294f343 100644
--- a/+quantity/Function.m
+++ b/+quantity/Function.m
@@ -74,57 +74,26 @@ classdef Function < quantity.Discrete
 		%-----------------------------
 		% --- overloaded functions ---
 		%-----------------------------
-		function value = on(obj, myGrid, myGridName)
-			% evaluates obj.valueContinuous function and returns an array
-			% with the dimensions (n, m, ..., z_disc). n, m, ... are the
-			% array dimensions of obj.valueContinuous and z_disc is the
-			% dimensions of the grid.
-			if nargin == 1
-				gridSize = obj(1).gridSize;
-				if gridSize > 2
-					value = cat(length(gridSize),obj.valueDiscrete);
-				else % important to include this case for empty grids!
-					value = [obj.valueDiscrete]; % = cat(2,obj.valueDiscrete)
-				end
-				value = reshape(value, [gridSize, size(obj)]);
-				return
-			elseif nargin >= 2 && ~iscell(myGrid)
-				myGrid = {myGrid};
-			end
-			gridPermuteIdx = 1:obj(1).nargin;
-			if isempty(gridPermuteIdx)
-				gridPermuteIdx = 1;
-			end
-			if nargin == 3
-				if ~iscell(myGridName)
-					myGridName = {myGridName};
-				end
-				assert(numel(myGrid) == numel(myGridName), ...
-					['If on() is called by using gridNames as third input', ...
-					', then the cell-array of grid and gridName must have ', ...
-					'equal number of elements.']);
-				assert(numel(myGridName) == obj(1).nargin, ...
-					'All (or none) gridName must be specified');
-				gridPermuteIdx = cellfun(@(v) obj(1).gridIndex(v), myGridName);
-				myGrid = myGrid(gridPermuteIdx);
-			end
-			% at first the value has to be initialized by different
-			% dimensions, to simplify the evaluation of each entry:
-
-			gridSize = cellfun('length', myGrid);
-			if isempty(gridSize), gridSize = 1; end
+		function value = obj2value(obj, myDomain, recalculate)
 
-			value = nan([numel(obj), gridSize]);
-			for k = 1:numel(obj)
-				ndGrd = obj.ndgrid( myGrid );
-				tmp = obj(k).evaluateFunction( ndGrd{:} );
-				% Replaced
-				%   double(subs(obj(k).valueSymbolic, obj.variable, grid));
-				% because this is very slow!
-				value(k,:) = tmp(:);
+			% check if the domain for the evaluation has changed. If not
+			% we can use the stored values in valueDiscrete:
+			if nargin < 3
+				recalculate = false;
+			end
+			if ~recalculate && isequal(myDomain, obj(1).domain)
+				value = obj2value@quantity.Discrete(obj);
+			else
+				% otherwise the function has to be evaluated on the new
+				% domain
+				value = cell(size(obj));
+				for k = 1:numel(obj)
+					ndGrd = myDomain.ndgrid;
+					tmp = obj(k).evaluateFunction( ndGrd{:} );
+					value{k} = tmp(:);
+				end					
+				value = reshape( cell2mat(value), [ gridLength(myDomain), size(obj)]);
 			end
-			value = reshape(permute(value, [1+(gridPermuteIdx), 1]), ...
-				[gridSize(gridPermuteIdx), size(obj)]);
 		end
 		
 		function n = nargin(obj)
@@ -134,8 +103,6 @@ classdef Function < quantity.Discrete
 		end
 	end
 	
-	
-	
 	% -------------
 	% --- Mathe ---
 	%--------------
diff --git a/+quantity/Operator.m b/+quantity/Operator.m
index d2ad64f5f0055cbfb8676a29f9483198cab0f4b3..197681e8f2d6b730f0553c391c2df005efc1c92d 100644
--- a/+quantity/Operator.m
+++ b/+quantity/Operator.m
@@ -19,28 +19,63 @@ classdef Operator < handle & matlab.mixin.Copyable
 	end
 	methods
 		function obj = Operator(A, varargin)
-			
+			% OPERATOR initialization of an operator object
+			%	 obj = Operator(A, varargin) initializes an operator of the
+			%	 form 
+			%		A[x(t)](z) = A0(z) x(z,t) + A1(z) dz x(z,t) + ...
+			%			A2(z) dz^2 x(z,t) + Ak dz^k x(z,t)
+			%	The input parameter A can be:
+			%		cell-array of doubles 
+			%			this is good to initialize operators with constant 
+			%			coefficients
+			%			A[x](t) = a_0 x(t) + a_1 dt x(t) + ...
+			%
+			%		cell-array of quantities
+			%			this helps to initialize operators with variable
+			%			coefficients:
+			%		A[x(t)](z) = A0(z) x(z,t) + A1(z) dz x(z,t) + ...
+			%			A2(z) dz^2 x(z,t) + Ak dz^k x(z,t)
+			%
+			%
+			%	
 			if nargin > 0
 				
 				prsr = misc.Parser();
 				prsr.addOptional('s', sym('s'));
 				prsr.addOptional('name', '');
+				prsr.addOptional('domain', quantity.Domain.empty());
 				prsr.parse(varargin{:});
 				
 				s = prsr.Results.s;
 				
 				%TODO assert that A has entries with the same size
+				
+				if isa(A, 'sym') && symvar(A) == prsr.Results.s
+					% if the matrix A is a symbolic expression which
+					% contains the algebraic representation of the
+					% operator, then the object can be generated if the
+					% symbolic expression is converted into the right form
+
+					% TODO: this works only for constant coefficients:
+					% catch the case if the coefficients are variable:
+					assert(length(symvar(A)) == 1, 'Not yet implemented')
+					
+					A = num2cell( double( polyMatrix.polynomial2coefficients(A, prsr.Results.s) ), ...
+						[1, 2]);
+					
+				end
+				
 				if iscell(A)
 					for k = 1 : numel(A)
 						if isnumeric(A{k})
 							obj(k).coefficient = ...
-								quantity.Discrete(A{k}, 'gridName', {}, 'grid', {});
+								quantity.Discrete(A{k}, 'domain', prsr.Results.domain);
 						else
 							obj(k).coefficient = A{k}; %#ok<AGROW>
 						end
 					end
 				elseif isnumeric(A)
-					obj(1).coefficient = quantity.Discrete(A, 'gridName', {}, 'grid', {});
+					obj(1).coefficient = quantity.Discrete(A, 'domain', prsr.Results.domain);
 				end
 					
 				[obj.s] = deal(s);
@@ -70,6 +105,29 @@ classdef Operator < handle & matlab.mixin.Copyable
 			I = quantity.Operator(II);
 		end
 		
+		function value = applyTo(obj, y, varargin)
+			% APPLYON evaluation of the differential parameterization
+			%	VALUE = applyOn(OBJ, Y, varargin) applies the operator OBJ
+			%	on the function Y, i.e., 
+			%		value = A0(t) y(t) + A1(t) dt y(t) + A2(t) dz^2 y(t)...
+			%	is evaluated.
+			
+			myParser = misc.Parser();
+			myParser.addParameter('domain', y.domain);
+			myParser.addParameter('n', length(obj));
+			myParser.parse(varargin{:});
+			
+			myDomain = myParser.Results.domain;
+			n = myParser.Results.n;
+			
+			value = zeros(size(obj(1).coefficient));
+			
+			for k = 1:n
+				value = value + obj(k).coefficient * y.diff(k - 1, myDomain );
+			end
+			
+		end
+		
 		function C = mtimes(A, B)
 			
 % 			if isnumeric(A) && numel(A) == 1
@@ -105,6 +163,7 @@ classdef Operator < handle & matlab.mixin.Copyable
 			end
 			C = quantity.Operator(C);
 		end
+		
 		function C = plus(A, B)
 			
 			if ~isa(A, 'quantity.Operator')
@@ -130,9 +189,11 @@ classdef Operator < handle & matlab.mixin.Copyable
 			end
 			C = quantity.Operator(C);			
 		end
+		
 		function C = minus(A, B)
 			C = A + (-1)*B;
 		end
+		
 		function C = adj(A)
 			assert(A(1).coefficient.isConstant())
 			
@@ -145,6 +206,7 @@ classdef Operator < handle & matlab.mixin.Copyable
 			
 			C = quantity.Operator(c);
 		end
+		
 		function C = det(A)
 			assert(A(1).coefficient.isConstant())
 			
@@ -158,13 +220,16 @@ classdef Operator < handle & matlab.mixin.Copyable
 			
 			C = quantity.Operator(c);
 		end
+		
 		function C = uminus(A)
 			C = (-1)*A;
 		end
+		
 		function s = size(obj, varargin)
 			s = [length(obj), size(obj(1).coefficient)];
 			s = s(varargin{:});
 		end
+		
 		function l = length(obj)
 			l = numel(obj);
 		end
@@ -415,8 +480,15 @@ classdef Operator < handle & matlab.mixin.Copyable
 		function newOperator = changeGrid(obj, gridNew, gridNameNew)
 			newOperator = quantity.Operator(obj.M.changeGrid(gridNew, gridNameNew));
 		end
+		
 		function newOperator = subs(obj, varargin)
 			newOperator = obj.M.subs(varargin{:});
+			
+			if isnumeric(newOperator)
+				newOperator = squeeze( num2cell(newOperator, [1 2]) );
+				newOperator = quantity.Operator(newOperator);
+			end
+			
 		end	
 	end
 	
diff --git a/+quantity/Symbolic.m b/+quantity/Symbolic.m
index f65d413b5fa1393f0a1e10f99719deb5f60a8e09..e49270ab4594fa01bf9d5a6e9d3250296765374e 100644
--- a/+quantity/Symbolic.m
+++ b/+quantity/Symbolic.m
@@ -15,6 +15,12 @@ classdef Symbolic < quantity.Function
 		
 		function obj = Symbolic(valueContinuous, varargin)
 			
+			%TODO Contruktor überarbeiten:
+			% # bei matlabFunction( symbolischer ausdruck ) muss auf die
+			%   Reihenfolge der variablen geachtet werden!
+			% # Manchmal werden mehrere grid variablen an den
+			% untergeordneten Konstruktor durchgereicht.
+			
 			parentVarargin = {};
 			
 			if nargin > 0
@@ -26,21 +32,22 @@ classdef Symbolic < quantity.Function
 				variableParser = misc.Parser();
 				variableParser.addParameter('variable', quantity.Symbolic.getVariable(valueContinuous));
 				variableParser.addParameter('symbolicEvaluation', false);
+				variableParser.addParameter('gridName', '');
+				variableParser.addParameter('domain', []);
 				variableParser.parse(varargin{:});
-				variable = variableParser.Results.variable;
-				numVar = numel(variable);
-				defaultGrid = cell(1, numVar);
-				for it = 1:numVar
-					if it==1
-						defaultGrid{it} = linspace(0, 1, 101).';
-					else
-						defaultGrid{it} = reshape(linspace(0, 1, 101), [ones(1, it-1), numel(linspace(0, 1, 101))]);
-					end
+				
+				if ~variableParser.isDefault('variable')
+					warning('Do not set the variable property. It will be ignored.')
 				end
-				gridParser = misc.Parser();
-				gridParser.addParameter('grid', defaultGrid);
-				gridParser.parse(varargin{:});
 				
+				if variableParser.isDefault('gridName') && variableParser.isDefault('domain')
+					warning('Grid names are generated from the symbolic variable. Please set the grid names in a quantity.Domain object explicitly')
+					variableNames = cellstr(string(variableParser.Results.variable));
+					varargin = [varargin(:)', {'gridName'}, {variableNames}];
+				end
+				
+				[myDomain, varargin] = quantity.Domain.parser(varargin{:});
+
 				s = size(valueContinuous);
 				S = num2cell(s);
 				
@@ -63,10 +70,10 @@ classdef Symbolic < quantity.Function
 					% variable
 					if isa(fun{k}, 'sym')
 						symb{k} = fun{k};
-						fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, variable);
+						fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, {myDomain.name});
 					elseif isa(fun{k}, 'double')
 						symb{k} = sym(fun{k});
-						fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, variable);						
+						fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, {myDomain.name});						
 					elseif isa(fun{k}, 'function_handle')
 						symb{k} = sym(fun{k});
 					else
@@ -74,16 +81,14 @@ classdef Symbolic < quantity.Function
 					end
 				end
 				
-				gridName = cellstr(string(variable));
-				% check if the reuqired gridName fits to the current
-				% variables:
-				if isfield(gridParser.Unmatched, 'gridName') ...
-						&& ~all(strcmp(gridParser.Unmatched.gridName, gridName))
-					error('If the gridName(s) are set explicitly, they have to be the same as the variable names!')
-				end
+% 				
+% 				% check if the reuqired gridName fits to the current
+% 				% variables:
+% 				if ~all(strcmp(sort({myDomain.name}), sort(variableName)))
+% 					error('If the gridName(s) are set explicitly, they have to be the same as the variable names!')
+% 				end
 				
-				parentVarargin = [{fun}, varargin(:)', {'grid'}, {gridParser.Results.grid}, ...
-					{'gridName'}, {gridName}];
+				parentVarargin = [{fun}, varargin(:)', {'domain'}, {myDomain}];
 			end
 			% call parent class
 			obj@quantity.Function(parentVarargin{:});
@@ -96,7 +101,8 @@ classdef Symbolic < quantity.Function
 				
 				% special properties
 				for k = 1:numel(valueContinuous)
-					obj(k).variable = variable; %#ok<AGROW>
+					obj(k).variable = ...
+						quantity.Symbolic.getVariable(valueContinuous); %#ok<AGROW>
 					obj(k).valueSymbolic = symb{k}; %#ok<AGROW>
 					obj(k).symbolicEvaluation = ...
 						variableParser.Results.symbolicEvaluation;  %#ok<AGROW>
@@ -243,11 +249,9 @@ classdef Symbolic < quantity.Function
 				obj = objCell{objIdx};
 				
 				if ~isempty(obj)
-					myVariable = obj(1).variable;
-					myGrid = obj(1).grid;
+					myDomain = obj(1).domain;
 				else
-					myVariable = '';
-					myGrid = {};
+					myDomain = quantity.Domain.empty();
 				end
 				
 				for k = 1:numel(objCell)
@@ -256,8 +260,7 @@ classdef Symbolic < quantity.Function
 						o = objCell{k};
 					elseif isa(objCell{k}, 'double') || isa(objCell{k}, 'sym')
 						o = quantity.Symbolic( objCell{k}, ...
-							'variable', myVariable, ...
-							'grid', myGrid);
+							'domain', myDomain);
 					end
 					objCell{k} = o;
 				end
@@ -267,23 +270,53 @@ classdef Symbolic < quantity.Function
 		
 		function solution = subs(obj, gridName, values)
 			%% SUBS substitues gridName and values
-			%   solution = subs(obj, gridName, values) TODO
-			% This function substitutes the variables specified with
-			% gradName with values. It can be used to rename grids or to
-			% evaluate (maybe just some) grids.
-			% GridName is cell-array of char-arrays
-			% chosen from obj.gridName-property. Values is a cell-array of
-			% the size of gridName. Each cell can contain arrays themself,
-			% but those arrays must be of same size. Values can be
-			% char-arrays standing for new gridName or or numerics.
-			% In contrast to on() or at(), only some, but not necessarily
-			% all variables are evaluated.
-			if ~iscell(gridName)
-				gridName = {gridName};
+			%   solution = subs(obj, DOMAIN) can be used to change the grid
+			%   of this quantity. The domains with the same domain name as
+			%   DOMAIN will be replaced with the corresponding grid in
+			%   DOMAIN.
+			%
+			%	solution = subs(obj, DOMAINNAME, GRID) can be used to
+			%	change the grid of this quantity. The grid of the domains with the
+			%	DOMAINNAME will be replaced by the values specified in
+			%	GRID. DOMAINNAME must be a cell-array containing the names
+			%	and GRID must be a cell-array containing the new grids.
+			%
+			%	solution = sub(obj, DOMAINNAME, NEWDOMAIN) can be used to
+			%	change the grid and/or the name of a domain. DOMAINNAME
+			%	must be a cell-array of domain names, or a oboject-array of
+			%	quantity.Domain objects. The NEWDOMAIN must be an
+			%	object-array of quantity.Domain.
+			
+			if isa(gridName, 'quantity.Domain')
+				if nargin == 2
+					gridName = {gridName.name};
+					values = {gridName.grid};
+				else
+					gridName = {gridName.name};
+				end
+			else
+				gridName = misc.ensureIsCell(gridName);
 			end
-			if ~iscell(values)
-				values = {values};
+			
+			if nargin == 3 && isa(values, 'quantity.Domain')
+				% replacement of the grid AND the gridName
+				% 1) replace the grid
+				solution = obj.changeGrid( {values.grid}, gridName );
+				% 2) replace the name
+				solution = solution.subs( gridName, {values.name} );
+				return
+			else
+				values = misc.ensureIsCell(values);
 			end
+			
+% 			% ensure that domains which should be replaced are known by the
+% 			% object
+% 			for k = 1:length(values)
+% 				if ischar(values{k})
+% 					assert( any( strcmp( obj(1).gridName, values{k} ) ), 'The domain name to be replaced must be a domain name of the object' );
+% 				end
+% 			end
+			
 			isNumericValue = cellfun(@isnumeric, values);
 			if any((cellfun(@(v) numel(v(:)), values)>1) & isNumericValue)
 				error('only implemented for one value per grid');
@@ -342,7 +375,7 @@ classdef Symbolic < quantity.Function
 						end
 					end
 					solution = quantity.Symbolic(double(symbolicSolution), ...
-						'grid', newGrid, 'variable', newGridName, 'name', obj(1).name);
+						'grid', newGrid, 'gridName', newGridName, 'name', obj(1).name);
 				else
 					% before creating a new quantity, it is checked that
 					% newGridName is unique. If there are non-unique
@@ -351,7 +384,7 @@ classdef Symbolic < quantity.Function
 					uniqueGridName = unique(newGridName, 'stable');
 					if numel(newGridName) == numel(uniqueGridName)
 						solution = quantity.Symbolic(symbolicSolution, ...
-							'grid', newGrid, 'variable', newGridName, 'name', obj(1).name);
+							'grid', newGrid, 'gridName', newGridName, 'name', obj(1).name);
 					else
 						uniqueGrid = cell(1, numel(uniqueGridName));
 						for it = 1 : numel(uniqueGrid)
@@ -370,10 +403,9 @@ classdef Symbolic < quantity.Function
 							end
 						end
 						solution = quantity.Symbolic(symbolicSolution, ...
-							'grid', uniqueGrid, 'variable', uniqueGridName, 'name', obj(1).name);
+							'grid', uniqueGrid, 'gridName', uniqueGridName, 'name', obj(1).name);
 					end
 				end
-				
 			end
 		end % subs()
 		
@@ -469,7 +501,7 @@ classdef Symbolic < quantity.Function
 					obj(it).variable, varNew(s)), varNew(0)==ic);
 			end
 			solution = quantity.Symbolic(symbolicSolution, ...
-				'gridName', {myParser.Results.newGridName, 'ic'}, 'variable', {s, ic}, ...
+				'gridName', {myParser.Results.newGridName, 'ic'}, ...
 				'grid', {myParser.Results.variableGrid, myParser.Results.initialValueGrid}, ...
 				'name', ['solve(', obj(1).name, ')']);
 		end % solveDVariableEqualQuantity()
@@ -488,45 +520,17 @@ classdef Symbolic < quantity.Function
 		function y = sqrt(x)
 			% quadratic root for scalar and diagonal symbolic quantities
 			y = quantity.Symbolic(sqrt(x.sym()), ...
-				'grid', x(1).grid, 'variable', x(1).variable, ...
+				'domain', x(1).domain, ...
 				'name', ['sqrt(', x(1).name, ')']);
 		end % sqrt()
 		
 		function y = sqrtm(x)
 			% quadratic root for matrices of symbolic quantities
 			y = quantity.Symbolic(sqrtm(x.sym()), ...
-				'grid', x(1).grid, 'variable', x(1).variable, ...
+				'domain', x(1).domain, ...
 				'name', ['sqrtm(', x(1).name, ')']);
 		end % sqrtm()
 		
-		function result = diff(obj, k, diffGridName)
-			% diff applies the kth-derivative for the variable specified with
-			% the input gridName to the obj. If no gridName is specified, then diff
-			% applies the derivative w.r.t. to all gridNames / variables.
-			if nargin == 1 || isempty(k)
-				k = 1; % by default, only one derivatve per diffGridName is applied
-			end
-			if nargin <= 2 % if no diffGridName is specified, then the derivative 
-				% w.r.t. to all gridNames is applied
-				diffGridName = obj(1).gridName;
-			end
-			
-			result = obj.copy();
-			if iscell(diffGridName)
-				for it = 1 : numel(diffGridName)
-					result = result.diff(k, diffGridName{it});
-				end
-			else
-				diffVariable = obj.gridName2variable(diffGridName);
-				[result.name] = deal(['(d_{' char(diffVariable) '} ' result(1).name, ')']);
-				[result.valueDiscrete] = deal([]);
-				for l = 1:numel(obj)
-					result(l).valueSymbolic = diff(obj(l).valueSymbolic, diffVariable, k);
-					result(l).valueContinuous = obj.setValueContinuous(result(l).valueSymbolic, obj(1).variable);
-				end
-			end
-		end % diff()
-		
 		function b = flipGrid(a, myGridName)
 			if ~iscell(myGridName)
 				myGridName = {myGridName};
@@ -538,7 +542,7 @@ classdef Symbolic < quantity.Function
 				variableNew{it} = 1-variableOld{it};
 			end
 			b = quantity.Symbolic(subs(a.sym, variableOld, variableNew), ...
-				'grid', a(1).grid, 'variable', a(1).variable, ...
+				'domain', a(1).domain, ...
 				'name', ['flip(', a(1).name, ')']);
 		end % flipGrid()
 		
@@ -569,35 +573,13 @@ classdef Symbolic < quantity.Function
 		function mObj = uminus(obj)
 			% unitary minus: C = -A
 			mObj = quantity.Symbolic(-obj.sym, 'grid', obj(1).grid, ...
-				'variable', obj(1).variable, 'name', ['-', obj(1).name]);
+				'domain', obj(1).domain, 'name', ['-', obj(1).name]);
 		end % uminus()
 		function mObj = uplus(obj)
 			% unitary plus: C = +A
 			mObj = copy(obj);
 		end % uplus()
-		
-		function parameters = combineGridGridNameVariable(A, B, parameters)
-			assert(isa(A, 'quantity.Symbolic') && isa(B, 'quantity.Symbolic'), ...
-				'A and B must be quantity.Symbolic');
-			
-			if nargin < 3
-				parameters = struct(A(1));
-			end
-			% combine grids of two quantities. This is often used in mathematical
-			% operations to obtain the parameters of the resulting quantity.
-			idx = computePermutationVectors(A, B);
-			parameters.grid = ...
-				[A(1).grid(idx.A.common), ...
-				A(1).grid(~idx.A.common), ...
-				B(1).grid(~idx.B.common)];
-			parameters.gridName = [A(1).gridName(idx.A.common), ...
-				A(1).gridName(~idx.A.common), ...
-				B(1).gridName(~idx.B.common)];
-			parameters.variable = [A(1).variable(idx.A.common), ...
-				A(1).variable(~idx.A.common), ...
-				B(1).variable(~idx.B.common)];
-		end % combineGridGridNameVariable()
-		
+ 		
 		function C = mtimes(A, B)
 			% if one input is ordinary matrix, this is very simple.
 			if isempty(B) ||  isempty(A)
@@ -609,13 +591,13 @@ classdef Symbolic < quantity.Function
 			end		
 			if isnumeric(B)
 				C = quantity.Symbolic(A.sym() * B, ...
-					'grid', A(1).grid, 'variable', A(1).variable, ...
+					'domain', A(1).domain, ...
 					'name', [A(1).name, ' c']);
 				return
 			end
 			if isnumeric(A)
 				C = quantity.Symbolic(A * B.sym(), ...
-					'grid', B(1).grid, 'variable', B(1).variable, ...
+					'domain', B(1).domain, ...
 					'name', ['c ', B(1).name]);
 				return
 			end
@@ -624,7 +606,7 @@ classdef Symbolic < quantity.Function
 				return;
 			end
 			
-			parameters = combineGridGridNameVariable(A, B);
+			parameters.domain = gridJoin(A(1).domain, B(1).domain);
 			parameters.name = [A(1).name, ' ', B(1).name];
 			parameters = misc.struct2namevaluepair(parameters);
 			
@@ -635,16 +617,32 @@ classdef Symbolic < quantity.Function
 			% minus() see quantity.Discrete.
 			assert(isequal(size(a), size(b)), 'terms must be of same size');
 			if ~isa(a, 'quantity.Discrete')
-				a = quantity.Symbolic(a, 'name', 'c');
+				if isnumeric(a)
+					a = quantity.Symbolic(a, 'name', 'c', 'domain', quantity.Domain.empty());
+				else
+					error('Not yet implemented')
+				end					
 				parameters = struct(b(1));
 			else
 				if ~isa(b, 'quantity.Discrete')
-					b = quantity.Symbolic(b, 'name', 'c');
+					if isnumeric(b)
+						b = quantity.Symbolic(b, 'name', 'c', 'domain', quantity.Domain.empty());
+					else
+						error('Not yet implemented')
+					end					
 				end
 				parameters = struct(a(1));
 			end
 			
-			parameters = combineGridGridNameVariable(a, b, parameters);
+			% remove the not required parameters:
+			for k = {'domain', 'grid', 'gridName', 'variable'}
+				if isfield(parameters, k)
+					parameters = rmfield(parameters, k);
+				end
+			end
+			
+			% set new parameter values:
+			parameters.domain = gridJoin(a(1).domain, b(1).domain);
 			parameters.name = [a(1).name, '+' , b(1).name];
 			parameters = misc.struct2namevaluepair(parameters);
 			
@@ -654,56 +652,50 @@ classdef Symbolic < quantity.Function
 		function absQuantity = abs(obj)
 			% abs returns the absolut value of the quantity as a quantity
 			absQuantity = quantity.Symbolic(abs(obj.sym()), ...
-				'grid', obj(1).grid, 'variable', obj(1).variable, ...
-				'name', ['|', obj(1).name, '|']);	
+				'name', ['|', obj(1).name, '|'], ...
+				'domain', obj(1).domain);	
 		end % abs()
 		
 		function y = real(obj)
 			% real() returns the real part of the obj.
 			y = quantity.Symbolic(real(obj.sym()), ...
 				'name', ['real(', obj(1).name, ')'], ...
-				'grid', obj(1).grid, ...
-				'variable', obj(1).variable);
+				'domain', obj(1).domain);
 		end % real()
 		
 		function y = imag(obj)
 			% imag() returns the real part of the obj.
 			y = quantity.Symbolic(imag(obj.sym()), ...
 				'name', ['real(', obj(1).name, ')'], ...
-				'grid', obj(1).grid, ...
-				'variable', obj(1).variable);
+				'domain', obj(1).domain);
 		end % imag()
 		
 		function y = inv(obj)
 			% inv inverts the matrix obj.
 			y = quantity.Symbolic(inv(obj.sym()), ...
 				'name', ['(', obj(1).name, ')^{-1}'], ...
-				'grid', obj(1).grid, ...
-				'variable', obj(1).variable);
+				'domain', obj(1).domain);
 		end % inv()
 		
 		function y = exp(obj)
 			% exp() is the exponential function using obj as the exponent.
 			y = quantity.Symbolic(exp(obj.sym()), ...
 				'name', ['exp(', obj(1).name, ')'], ...
-				'grid', obj(1).grid, ...
-				'variable', obj(1).variable);
+				'domain', obj(1).domain);
 		end % exp()
 		
 		function y = expm(obj)
 			% exp() is the matrix-exponential function using obj as the exponent.
 			y = quantity.Symbolic(expm(obj.sym()), ...
 				'name', ['expm(', obj(1).name, ')'], ...
-				'grid', obj(1).grid, ...
-				'variable', obj(1).variable);
+				'domain', obj(1).domain);
 		end % expm()
 		
 		function y = ctranspose(obj)
 			% ctranspose() or ' is the complex conjugate tranpose
 			y = quantity.Symbolic(conj(obj.sym().'), ...
 				'name', ['{', obj(1).name, '}^{H}'], ...
-				'grid', obj(1).grid, ...
-				'variable', obj(1).variable);
+				'domain', obj(1).domain);
 		end % expm()
 		
 		function C = int(obj, z, a, b)
@@ -780,7 +772,7 @@ classdef Symbolic < quantity.Function
 					% with numeric integral bounds. Therefore, this try-catch block
 					% is needed.
 					C = quantity.Symbolic(I, ...
-						'grid', gridI, 'variable', variableI, ...
+						'grid', gridI, 'gridName', gridNameI, ...
 						'name', ['int[', obj(1).name, ']']);
 				catch
 					C = cumInt(quantity.Discrete(obj), z, a, b);					
@@ -872,6 +864,16 @@ classdef Symbolic < quantity.Function
 			end
 		end
 		
+		function result = diff_inner(obj, k, diffGridName)
+			result = obj.copy();
+			[result.name] = deal(['(d_{' diffGridName '} ' result(1).name, ')']);
+			[result.valueDiscrete] = deal([]);
+			for l = 1:numel(obj)
+				result(l).valueSymbolic = diff(obj(l).valueSymbolic, diffGridName, k);
+				result(l).valueContinuous = obj.setValueContinuous(result(l).valueSymbolic, obj(1).variable);
+			end
+		end		
+		
 	end
 	
 	methods (Static)
@@ -937,11 +939,7 @@ classdef Symbolic < quantity.Function
 		end
 		function f = setValueContinuous(f, symVar)
 			% converts symVar into a matlabFunction.
-			if isa(f, 'sym')
-				if nargin == 1
-					symVar = quantity.Symbolic.getVariable(f);
-				end
-				
+			if isa(f, 'sym')			
 				if isempty(symvar(f))
 					f = @(varargin) double(f) + quantity.Function.zero(varargin{:});
 				else
diff --git a/+quantity/TimeDelayOperator.m b/+quantity/TimeDelayOperator.m
new file mode 100644
index 0000000000000000000000000000000000000000..4ad46845500091f78be38a0fda10a561284af49e
--- /dev/null
+++ b/+quantity/TimeDelayOperator.m
@@ -0,0 +1,130 @@
+classdef TimeDelayOperator < handle & matlab.mixin.Copyable
+	%TIMEDELAYOPERATOR class to describe an operator of the form
+	%	D[h](z,t) = h( t + coefficient(z) )
+	% This comes from the inverse Laplace transfromed value of
+	%	exp(-s coefficient(z) ) * H(s)   <->   h(t + coefficient(z))
+	%
+	
+	properties
+		coefficient {mustBe.quantity};
+	end
+	
+	properties (Access = protected)
+		spatialDomain_inner quantity.Domain;
+		timeDomain_inner quantity.Domain;
+		isZero logical = false;
+	end
+	
+	methods
+		function obj = TimeDelayOperator(coefficient, timeDomain, optionalArgs)
+			%TIMEDELAYOPERATOR Construct an instance of a time delay
+			%operator
+			%   obj = TimeDelayOperator(COEFFICIENT, TIMEDOMAIN, VARARGIN)
+			%   will construct a time delay operator of the form
+			%		OBJ[h](z,t) = h( t + COEFFICIENT(z) )
+			%	with the time delay described by COEFFICIENT on a temporal
+			%	domain TIMEDOMAIN. Using the name-value-pairs optional
+			%	parameters, a spatial domain can be specified by the name
+			%	'spatialDomain'.
+			arguments
+				coefficient;
+				timeDomain;
+				optionalArgs.spatialDomain =  coefficient.domain;
+				optionalArgs.isZero logical = false;
+			end
+			
+			obj.coefficient = coefficient;
+			obj.timeDomain_inner = timeDomain;
+			obj.spatialDomain_inner = optionalArgs.spatialDomain;
+			obj.isZero = optionalArgs.isZero;
+		end
+		
+		function d = applyTo(obj, h, optionalArgs)
+			%APPLYON apply the operator on a function
+			%   D = applyOn(OBJ, H) computes the application of the
+			%   operator OBJ to the function H. 
+			%		d = OBJ[h]
+			arguments
+				obj
+				h quantity.Discrete;
+ 				optionalArgs.domain quantity.Domain = h(1).domain;
+			end
+			
+			assert( length(optionalArgs.domain) == 1);
+			
+			n = size(obj, 1);
+			m = size(obj, 2); assert(m == size(h, 1));
+			l = size(h, 2);
+			
+			d = quantity.Discrete.zeros([n, l], [obj.spatialDomain, obj.timeDomain]);
+			
+			for i = 1 : n
+				for j = 1 : m
+					for k = 1 : l
+						if ~obj(i, j).isZero
+							d(i, k) = d(i, k) + h(j, k).compose( obj.t + obj(i, j).coefficient, 'domain', optionalArgs.domain );
+						end
+					end
+				end
+			end
+		end		
+		
+		function d = compositionDomain( obj, h)
+			
+			% todo assert all coefficients have the same domain
+			
+			d = h(1).compositionDomain( obj.t + obj(1).coefficient );
+		end
+		
+		function t = t(obj)
+			t = quantity.Symbolic(sym(obj.timeDomain.name), 'domain', obj.timeDomain);
+		end
+		
+		function d = diag(obj)
+			
+			n = length(obj);
+			idx = 1:n;
+			
+			d(1:(n^2)) = obj.zero(obj.timeDomain);
+			d = reshape(d, n, n);
+			d(idx == idx') = obj(:);
+						
+		end
+		
+		function C = coefficients(obj)		
+			C = reshape([obj.coefficient], size(obj));
+		end
+		
+		function sd = spatialDomain(obj)
+			sd = join( [obj.spatialDomain_inner] );
+		end
+		
+		function td = timeDomain(obj)
+			td = join( [obj.timeDomain_inner] );
+		end
+		
+
+	end
+	
+	methods (Static)
+		function o = zero(timeDomain, namedArgs)
+			
+			arguments
+				timeDomain quantity.Domain;
+				namedArgs.spatialDomain quantity.Domain = quantity.Domain.empty();
+			end
+			
+			if isempty( namedArgs.spatialDomain )			
+				infValue = inf;
+			else
+				infValue = inf(obj.spatialDomain.gridLength, 1);
+			end
+			
+			qinf = quantity.Discrete(infValue, ...
+				'domain', namedArgs.spatialDomain);
+			o = quantity.TimeDelayOperator(qinf, timeDomain, 'isZero', true);			
+		end		
+	end
+	
+end
+
diff --git a/+signals/+faultmodels/TaylorPolynomial.m b/+signals/+faultmodels/TaylorPolynomial.m
index c5363b2bab45fc6de420e35b07d6b4728923659c..d451e9b9e72eb5b671f1eb77a1955beb8431c005 100644
--- a/+signals/+faultmodels/TaylorPolynomial.m
+++ b/+signals/+faultmodels/TaylorPolynomial.m
@@ -128,12 +128,11 @@ classdef TaylorPolynomial < quantity.Discrete
 		end
 		function monomials = getMonomials(degree, t, tStar, myLocalGrid, T)
 			monomials = quantity.Symbolic.zeros([degree, 1], ...
-				'variable', {t, tStar}, ...
-					'grid', {myLocalGrid, T}, ...
+					'domain', quantity.Domain.empty(), ...
 					'name', 'varphi_k');
 			for k = 1:degree
 				monomials(k, 1) = quantity.Symbolic((t-tStar)^(k-1), ...
-					'variable', {t, tStar}, ...
+					'gridName', cellfun(@char, {t, tStar}, 'UniformOutput', false), ...
 					'grid', {myLocalGrid, T}, ...
 					'name', 'varphi_k');
 			end
diff --git a/+unittests/+quantity/testDiscrete.m b/+unittests/+quantity/testDiscrete.m
index fc20f9e5dab6eb9c45f2b8ae6089972a00c088bc..65fb584378377f8cb3e71efe162818e8a7a73a8b 100644
--- a/+unittests/+quantity/testDiscrete.m
+++ b/+unittests/+quantity/testDiscrete.m
@@ -4,6 +4,50 @@ function [tests] = testDiscrete()
 tests = functiontests(localfunctions);
 end
 
+function testCompose(testCase)
+
+t = quantity.Domain('t', linspace(0, 2));
+f = quantity.Discrete(sin(t.grid * 2 * pi) , 'domain', t, 'name', 'f' );
+g = quantity.Discrete( 0.5 * t.grid, 'domain', t, 'name', 'g');
+h = quantity.Discrete( 2 * t.grid, 'domain', t, 'name', 'h');
+
+% verify simple evaluation of f( g(t) )
+testCase.verifyEqual( on( f.compose(g) ), sin(t.grid * pi), 'AbsTol', 3e-3 )
+
+% verify that a warning is thrown, if the domain of definition is exceeded
+testCase.verifyWarning(@()f.compose(h), 'quantity:Discrete:compose')
+
+% verify f(tau) = sin( tau * 2pi ); g(z, t) = t + z^2
+% f( g(z, t ) ) = sin( (t + z^2 ) * 2pi )
+tau = quantity.Domain('tau', linspace(-1, 3));
+z = quantity.Domain('z', linspace(0, 1, 51));
+
+f = quantity.Discrete( sin( tau.grid * 2 * pi ), 'domain', tau );
+g = quantity.Discrete( ( z.grid.^2 ) + t.grid', 'domain', [z, t] );
+
+fg = f.compose(g);
+F = quantity.Function(@(z,t) sin( ( t + z.^2 ) * 2 * pi ), ...
+	'domain', [z, t], 'name', 'f(g) as function handle');
+testCase.verifyEqual(fg.on, F.on, 'AbsTol', 1e-2)
+
+% verify f(z, tau) °_tau g(z, t) -> f(z, g(z,t))
+%	f(z, tau) = z + tau;
+%	g(z, t) = z + t
+%	f(z, g(z,t) ) = 2*z + t
+
+z = quantity.Domain('z', linspace(0, 1, 11));
+t = quantity.Domain('t', linspace(0, 1, 21));
+tau = quantity.Domain('tau', linspace(0, 2, 31)); 
+
+f = quantity.Discrete( z.grid + tau.grid', 'domain', [z, tau]);
+g = quantity.Discrete( z.grid + t.grid', 'domain', [z, t]);
+
+fog = f.compose(g, 'domain', tau);
+FoG = quantity.Discrete( 2 * z.grid + t.grid', 'domain', [z, t]);
+testCase.verifyEqual( fog.on, FoG.on, 'AbsTol', 5e-16)
+
+end
+
 function testCastDiscrete2Function(testCase)
 
 z = linspace(0, 2*pi)';
@@ -24,12 +68,37 @@ tc.verifyEqual(blubQuadraticNorm.on(), 2*ones(11,1));
 tc.verifyEqual(blubQuadraticWeightedNorm.on(), 4*ones(11,1));
 end % testQuadraticNorm()
 
+function testSort(tc)
+
+t = linspace(0, pi, 7)';
+domA = quantity.Domain('a', t);
+domB = quantity.Domain('b', t);
+q = quantity.Discrete(sin(t) .* cos(t'), ...
+	'domain', [domA, domB]);
+
+q1V = q.valueDiscrete();
+
+q.sort('descend');
+
+q2V = q.valueDiscrete();
+
+tc.verifyEqual(q1V , q2V')
+
+
+end % testSort
+
 function testBlkdiag(tc)
 % init some data
 syms z zeta
-A = quantity.Symbolic(...
-	[1+z*zeta, -zeta; -z, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 'q');
+
+z = quantity.Domain('z', linspace(0, 1, 21));
+zeta = quantity.Domain('zeta', linspace(0, 1, 41));
+
+A = quantity.Discrete(cat(4, ...
+	cat(3, 1 + z.grid .* zeta.grid', - z.ones .* zeta.grid'), ...
+	cat(3, -z.grid .* zeta.ones', z.grid .^2 .* zeta.ones') ), ...
+	'domain', [z, zeta], 'name', 'q');
+
 B = 2*A(:, 1);
 C = 3*A(1);
 
@@ -66,7 +135,7 @@ function testCtranspose(tc)
 syms z zeta
 qSymbolic = quantity.Symbolic(...
 	[1+z*zeta, -zeta; -z, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 'q');
+	'gridName', {'z', 'zeta'}, 'name', 'q');
 qDiscrete = quantity.Discrete(qSymbolic);
 qDiscreteCtransp = qDiscrete';
 tc.verifyEqual(qDiscrete(1,1).on(), qDiscreteCtransp(1,1).on());
@@ -82,12 +151,14 @@ tc.verifyEqual(qDiscrete2.imag.on(), -qDiscrete2Ctransp.imag.on());
 end % testCtranspose
 
 function testTranspose(tc)
+
 syms z zeta
 qSymbolic = quantity.Symbolic(...
 	[1+z*zeta, -zeta; -z, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 'q');
+	'gridName', {'z', 'zeta'}, 'name', 'q');
 qDiscrete = quantity.Discrete(qSymbolic);
 qDiscreteTransp = qDiscrete.';
+
 tc.verifyEqual(qDiscrete(1,1).on(), qDiscreteTransp(1,1).on());
 tc.verifyEqual(qDiscrete(2,2).on(), qDiscreteTransp(2,2).on());
 tc.verifyEqual(qDiscrete(1,2).on(), qDiscreteTransp(2,1).on());
@@ -98,16 +169,16 @@ function testFlipGrid(tc)
 syms z zeta
 myGrid = linspace(0, 1, 11);
 f = quantity.Discrete(quantity.Symbolic([1+z+zeta; 2*zeta+sin(z)] + zeros(2, 1)*z*zeta, ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid}));
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid}));
 % % flip one grid
 fReference = quantity.Discrete(quantity.Symbolic([1+z+(1-zeta); 2*(1-zeta)+sin(z)] + zeros(2, 1)*z*zeta, ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid}));
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid}));
 fFlipped = f.flipGrid('zeta');
 tc.verifyEqual(fReference.on(), fFlipped.on(), 'AbsTol', 10*eps);
 
 % flip both grids
 fReference2 = quantity.Discrete(quantity.Symbolic([1+(1-z)+(1-zeta); 2*(1-zeta)+sin(1-z)] + zeros(2, 1)*z*zeta, ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid}));
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid}));
 fFlipped2 = f.flipGrid({'z', 'zeta'});
 fFlipped3 = f.flipGrid({'zeta', 'z'});
 tc.verifyEqual(fReference2.on(), fFlipped2.on(), 'AbsTol', 10*eps);
@@ -115,19 +186,22 @@ tc.verifyEqual(fReference2.on(), fFlipped3.on(), 'AbsTol', 10*eps);
 end % testFlipGrid();
 
 function testScalarPlusMinusQuantity(testCase)
-syms z
-myGrid = linspace(0, 1, 7);
-f = quantity.Discrete(quantity.Symbolic([1; 2] + zeros(2, 1)*z, ...
-	'variable', {z}, 'grid', {myGrid}));
+
+z = quantity.Domain('z', linspace(0, 1, 7));
+f = quantity.Discrete( ( [2; 1] + zeros(2, 1) .* z.grid' )', ...
+	'domain', z);
+
 testCase.verifyError(@() 1-f-1, '');
 testCase.verifyError(@() 1+f+1, '');
+
 end % testScalarPlusMinusQuantity
 
 function testNumericVectorPlusMinusQuantity(testCase)
-syms z
-myGrid = linspace(0, 1, 7);
-f = quantity.Discrete(quantity.Symbolic([1+z; 2+sin(z)] + zeros(2, 1)*z, ...
-	'variable', {z}, 'grid', {myGrid}));
+
+z = quantity.Domain('z', linspace(0, 1, 7));
+f = quantity.Discrete( [1+z.grid, 2+sin(z.grid)] , ...
+	'domain', z);
+
 a = ones(size(f));
 testCase.verifyEqual(on(a-f), 1-f.on());
 testCase.verifyEqual(on(f-a), f.on()-1);
@@ -136,19 +210,18 @@ testCase.verifyEqual(on(f+a), f.on()+1);
 end % testNumericVectorPlusMinusQuantity
 
 function testUnitaryPluasAndMinus(testCase)
-syms z zeta
-qSymbolic = quantity.Symbolic(...
-	[1+z*zeta, -zeta; -z, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 'q');
-qDiscrete = quantity.Discrete(qSymbolic);
-qDoubleArray = qSymbolic.on();
+z = quantity.Domain('z', linspace(0, 1, 7));
+zeta = quantity.Domain('zeta', linspace(0, 1, 7));
+
+q = quantity.Discrete(cat(4, ...
+	cat(3, 1 + z.grid .* zeta.grid', - z.grid .* zeta.ones'), ...
+	cat(3, - z.ones .* zeta.grid', z.grid.^2 .* zeta.ones') ), ...
+	'domain', [z, zeta]) ;
 
-testCase.verifyEqual(on(-qSymbolic), -qDoubleArray);
-testCase.verifyEqual(on(-qDiscrete), -qDoubleArray);
-testCase.verifyEqual(on(+qSymbolic), +qDoubleArray);
-testCase.verifyEqual(on(+qDiscrete), +qDoubleArray);
-testCase.verifyEqual(on(+qDiscrete), on(+qSymbolic));
-testCase.verifyEqual(on(-qDiscrete), on(-qSymbolic));
+qDoubleArray = q.on();
+	
+testCase.verifyEqual(on(-q), -qDoubleArray);
+testCase.verifyEqual(on(+q), +qDoubleArray);
 
 end
 
@@ -194,31 +267,51 @@ testCase.verifyEqual(DE.on(), compareDE.on())
 ED = [E, D];
 compareED = quantity.Discrete({tan(t*z), sin(t * z); cos(t * z), cos(t*z)}, 'grid', {t, z}, 'gridName', {'t', 'z'});
 testCase.verifyEqual(ED.on(), compareED.on())
+
+% concatenation of a constant and a function:
+C = [5; 2];
+const = quantity.Discrete(C, 'domain', quantity.Domain.empty);
+% 
+constA = [const, A];
+testCase.verifyTrue(all(constA(1,1).on() == C(1)));
+testCase.verifyTrue(all(constA(2,1).on() == C(2)));
+
+Aconst = [A, const];
+testCase.verifyTrue(all(Aconst(1,2).on() == C(1)));
+testCase.verifyTrue(all(Aconst(2,2).on() == C(2)));
+
+
 end
 
 function testExp(testCase)
 % 1 spatial variable
-syms z zeta
-s1d = quantity.Discrete(quantity.Symbolic(...
-	1+z*z, 'grid', {linspace(0, 1, 21)}, 'variable', {z}, 'name', 's1d'));
+z = quantity.Domain('z', linspace(0, 1, 3));
+s1d = quantity.Discrete(1 + z.grid .* z.grid.', 'domain', z);
 testCase.verifyEqual(s1d.exp.on(), exp(s1d.on()));
 
 % diagonal matrix
-s2dDiag = quantity.Discrete(quantity.Symbolic(...
-	[1+z*zeta, 0; 0, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 's2dDiag'));
+zeta = quantity.Domain('zeta', linspace(0, 1, 4));
+s2dDiag = quantity.Discrete(cat(4, ...
+	cat(3, 1 + z.grid .* zeta.grid', zeros(z.n, zeta.n)), ....
+	cat(3, zeros(z.n, zeta.n), z.grid.^2 .* zeta.ones')), 'domain', [z, zeta]);
+
 testCase.verifyEqual(s2dDiag.exp.on(), exp(s2dDiag.on()));
 end
 
 function testExpm(testCase)
-syms z zeta
-mat2d = quantity.Discrete(quantity.Symbolic(...
-	ones(2, 2) + [1+z*zeta, 3*zeta; 2+5*z+zeta, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 's2d'));
+
+z = quantity.Domain('z', linspace(0, 1, 3));
+zeta = quantity.Domain('zeta', linspace(0, 1, 4));
+
+mat2d = quantity.Discrete(...
+	{1 + z.grid .* zeta.grid', 3 * z.ones .* zeta.grid'; ...
+	 2 + 5 * z.grid + zeta.grid', z.grid .^2 .* zeta.ones'}, ...
+	 'domain', [z, zeta]);
+
 mat2dMat = mat2d.on();
 mat2dExpm = 0 * mat2d.on();
-for zIdx = 1 : 21
-	for zetaIdx = 1 : 41
+for zIdx = 1 : z.n
+	for zetaIdx = 1 : zeta.n
 		mat2dExpm(zIdx, zetaIdx, :, :) = expm(reshape(mat2dMat(zIdx, zetaIdx, :, :), [2, 2]));
 	end
 end
@@ -289,7 +382,8 @@ end
 function testDiag2Vec(testCase)
 % quantity.Symbolic
 syms z
-myMatrixSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1, 0; 0, 0.9-z/2]);
+do = quantity.Domain('z', linspace(0,1,3));
+myMatrixSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1, 0; 0, 0.9-z/2], 'domain', do);
 myVectorSymbolic = diag2vec(myMatrixSymbolic);
 testCase.verifyEqual(myMatrixSymbolic(1,1).valueContinuous, myVectorSymbolic(1,1).valueContinuous);
 testCase.verifyEqual(myMatrixSymbolic(2,2).valueContinuous, myVectorSymbolic(2,1).valueContinuous);
@@ -302,6 +396,7 @@ testCase.verifyEqual(myMatrixDiscrete(1,1).on(), myVectorDiscrete(1,1).on());
 testCase.verifyEqual(myMatrixDiscrete(2,2).on(), myVectorDiscrete(2,1).on());
 testCase.verifyEqual(numel(myVectorDiscrete), size(myMatrixDiscrete, 1));
 end
+
 function testVec2Diag(testCase)
 % quantity.Discrete
 n = 7;
@@ -343,39 +438,38 @@ end
 
 function testSolveDVariableEqualQuantityConstant(testCase)
 %% simple constant case
-quan = quantity.Discrete(ones(51, 1), 'grid', linspace(0, 1, 51), ...
+quan = quantity.Discrete(ones(3, 1), 'grid', linspace(0, 1, 3), ...
 	'gridName', 'z', 'size', [1, 1], 'name', 'a');
 solution = quan.solveDVariableEqualQuantity();
-[referenceResult1, referenceResult2] = ndgrid(linspace(0, 1, 51), linspace(0, 1, 51));
+[referenceResult1, referenceResult2] = ndgrid(linspace(0, 1, 3), linspace(0, 1, 3));
 testCase.verifyEqual(solution.on(), referenceResult1 + referenceResult2, 'AbsTol', 10*eps);
 end
 
 function testSolveDVariableEqualQuantityNegative(testCase)
 syms z zeta
 assume(z>0 & z<1); assume(zeta>0 & zeta<1);
-myParameterGrid = linspace(0, 1, 51);
+myParameterGrid = linspace(0, 0.1, 5);
 Lambda = quantity.Symbolic(-0.1-z^2, ...%, -1.2+z^2]),...1+z*sin(z)
-	'variable', z, ...
 	'grid', myParameterGrid, 'gridName', 'z', 'name', '\Lambda');
 
 %%
-myGrid = linspace(-2, 2, 101);
-sEval = -0.9;
+myGrid = linspace(-.2, .2, 11);
+sEval = -0.09;
 solveThisOdeDiscrete = solveDVariableEqualQuantity(...
 	diag2vec(quantity.Discrete(Lambda)), 'variableGrid', myGrid);
 solveThisOdeSymbolic = solveDVariableEqualQuantity(...
 	diag2vec(Lambda), 'variableGrid', myGrid);
 
-testCase.verifyEqual(solveThisOdeSymbolic.on({sEval, 0.5}), solveThisOdeDiscrete.on({sEval, 0.5}), 'RelTol', 5e-4);
+testCase.verifyEqual(solveThisOdeSymbolic.on({sEval, 0.05}), solveThisOdeDiscrete.on({sEval, 0.05}), 'RelTol', 5e-3);
 end
 
 function testSolveDVariableEqualQuantityComparedToSym(testCase)
 %% compare with symbolic implementation
 syms z
 assume(z>0 & z<1);
-quanBSym = quantity.Symbolic(1+z, 'grid', {linspace(0, 1, 21)}, ...
-	'gridName', 'z', 'name', 'bSym', 'variable', {z});
-quanBDiscrete = quantity.Discrete(quanBSym.on(), 'grid', {linspace(0, 1, 21)}, ...
+quanBSym = quantity.Symbolic(1+z, 'grid', {linspace(0, 1, 5)}, ...
+	'gridName', 'z', 'name', 'bSym', 'gridName', {'z'});
+quanBDiscrete = quantity.Discrete(quanBSym.on(), 'grid', {linspace(0, 1, 5)}, ...
 	'gridName', 'z', 'name', 'bDiscrete', 'size', size(quanBSym));
 solutionBSym = quanBSym.solveDVariableEqualQuantity();
 solutionBDiscrete = quanBDiscrete.solveDVariableEqualQuantity();
@@ -387,10 +481,11 @@ function testSolveDVariableEqualQuantityAbsolut(testCase)
 %% compare with symbolic implementation
 syms z
 assume(z>0 & z<1);
-quanBSym = quantity.Symbolic(1+z, 'grid', {linspace(0, 1, 51)}, ...
-	'gridName', 'z', 'name', 'bSym', 'variable', {z});
-quanBDiscrete = quantity.Discrete(quanBSym.on(), 'grid', {linspace(0, 1, 51)}, ...
+quanBSym = quantity.Symbolic(1+z, 'grid', {linspace(0, 1, 11)}, ...
+	'gridName', 'z', 'name', 'bSym', 'gridName', {'z'});
+quanBDiscrete = quantity.Discrete(quanBSym.on(), 'grid', {linspace(0, 1, 11)}, ...
 	'gridName', 'z', 'name', 'bDiscrete', 'size', size(quanBSym));
+
 solutionBDiscrete = quanBDiscrete.solveDVariableEqualQuantity();
 myGrid = solutionBDiscrete.grid{1};
 solutionBDiscreteDiff = solutionBDiscrete.diff(1, 's');
@@ -399,9 +494,10 @@ for icIdx = 1 : length(myGrid)
 	quanOfSolutionOfS(:, icIdx, :) = quanBSym.on(solutionBDiscrete.on({myGrid, myGrid(icIdx)}));
 end
 %%
-testCase.verifyEqual(solutionBDiscreteDiff.on({myGrid(2:end-1), myGrid}), quanOfSolutionOfS(2:end-1, :), 'AbsTol', 1e-3);
+testCase.verifyEqual(solutionBDiscreteDiff.on({myGrid(2:end-1), myGrid}), quanOfSolutionOfS(2:end-1, :), 'AbsTol', 1e-2);
 
 end
+
 function testMtimesDifferentGridLength(testCase)
 %%
 a = quantity.Discrete(ones(11, 1), 'grid', linspace(0, 1, 11), 'gridName', 'z', ...
@@ -411,10 +507,9 @@ b = quantity.Discrete(ones(5, 1), 'grid', linspace(0, 1, 5), 'gridName', 'z', ..
 ab  = a*b;
 %
 syms z
-c = quantity.Symbolic(1, 'grid', linspace(0, 1, 21), 'variable', z, 'name', 'c');
+c = quantity.Symbolic(1, 'grid', linspace(0, 1, 21), 'gridName', 'z', 'name', 'c');
 ac = a*c;
 
-%%
 %%
 testCase.verifyEqual(ab.on(), ones(11, 1));
 testCase.verifyEqual(ac.on(), ones(21, 1));
@@ -472,23 +567,28 @@ testCase.verifyEqual(myQuantityDZzeta(3).on(), zeros(11, 21), 'AbsTol', 10*eps);
 end
 
 function testOn(testCase)
-%% init data
-gridVecA = linspace(0, 1, 27);
-gridVecB = linspace(0, 1, 41);
-[value] = createTestData(gridVecA, gridVecB);
+% init data
+domainVecA = quantity.Domain('z', linspace(0, 1, 27));
+domainVecB = quantity.Domain('zeta', linspace(0, 1, 41));
+[value] = createTestData(domainVecA.grid, domainVecB.grid);
 a = quantity.Discrete(value, 'size', [2, 3], ...
-	'grid', {gridVecA, gridVecB}, 'gridName', {'z', 'zeta'}, 'name', 'A');
+	'domain', [domainVecA, domainVecB], 'name', 'A');
 
-%%
+%
 testCase.verifyEqual(value, a.on());
 testCase.verifyEqual(permute(value, [2, 1, 3, 4]), ...
-	a.on({gridVecB, gridVecA}, {'zeta', 'z'}));
+	a.on([domainVecB, domainVecA]));
 testCase.verifyEqual(createTestData(linspace(0, 1, 100), linspace(0, 1, 21)), ...
 	a.on({linspace(0, 1, 100), linspace(0, 1, 21)}), 'AbsTol', 100*eps);
+
+d24z = quantity.Domain('z', linspace(0, 1, 24));
+d21zeta = quantity.Domain('zeta', linspace(0, 1, 21));
+
 testCase.verifyEqual(createTestData(linspace(0, 1, 24), linspace(0, 1, 21)), ...
-	a.on({linspace(0, 1, 24), linspace(0, 1, 21)}, {'z', 'zeta'}), 'AbsTol', 24*eps);
+	a.on( [d24z, d21zeta] ), 'AbsTol', 24*eps);
+
 testCase.verifyEqual(permute(createTestData(linspace(0, 1, 21), linspace(0, 1, 24)), [2, 1, 3, 4]), ...
-	a.on({linspace(0, 1, 24), linspace(0, 1, 21)}, {'zeta', 'z'}), 'AbsTol', 2e-4);
+	a.on( {linspace(0, 1, 24), linspace(0, 1, 21)}, {'zeta', 'z'}), 'AbsTol', 2e-4);
 
 	function [value] = createTestData(gridVecA, gridVecB)
 		[zGridA , zetaGridA] = ndgrid(gridVecA, gridVecB);
@@ -499,6 +599,21 @@ testCase.verifyEqual(permute(createTestData(linspace(0, 1, 21), linspace(0, 1, 2
 		value(:,:,1,2) = 1+zGridA + zetaGridA.^2;
 		value(:,:,2,3) = 1+zeros(numel(gridVecA), numel(gridVecB));
 	end
+
+%% test on on 3-dim domain
+z = quantity.Domain('z', 0:3);
+t = quantity.Domain('t', 0:4);
+x = quantity.Domain('x', 0:5);
+
+Z = quantity.Discrete( z.grid, 'domain', z);
+T = quantity.Discrete( t.grid, 'domain', t);
+X = quantity.Discrete( x.grid, 'domain', x);
+
+ZTX = Z+T+X;
+XTZ = X+T+Z;
+
+testCase.verifyEqual( ZTX.on([t z x]), XTZ.on([t z x]), 'AbsTol', 1e-12);
+
 end
 
 function testOn2(testCase)
@@ -518,30 +633,37 @@ testCase.verifyEqual([a(1).on({zeta(2), eta(3), z(4)}); a(2).on({zeta(2), eta(3)
 end
 
 function testMtimesZZeta2x2(testCase)
-gridVecA = linspace(0, 1, 101);
-[zGridA , ~] = ndgrid(gridVecA, gridVecA);
 
-a = quantity.Discrete(ones([size(zGridA), 2, 2]), 'size', [2, 2], ...
-	'grid', {gridVecA, gridVecA}, 'gridName', {'z', 'zeta'}, 'name', 'A');
+
+
+%%
+z11_ = linspace(0, 1, 11);
+z11 = quantity.Domain('z', z11_);
+zeta11 = quantity.Domain('zeta', z11_);
+
+a = quantity.Discrete(ones([z11.n, z11.n, 2, 2]), 'size', [2, 2], ...
+	'domain', [z11, zeta11], 'name', 'A');
 
 syms zeta
-assume(zeta>=0 & zeta>=1)
-gridVecB = linspace(0, 1, 100);
-b = quantity.Symbolic((eye(2, 2)), 'variable', zeta, ...
-	'grid', gridVecB);
+z10_ = linspace(0, 1, 10);
+
+b = quantity.Symbolic((eye(2, 2)), 'gridName', 'zeta', ...
+	'grid', z10_);
+
 c = a*b;
 
-%%
+%
 testCase.verifyEqual(c.on(), a.on());
 end
 
 function testMTimesPointWise(tc)
 
 syms z zeta
-Z = linspace(0, 1, 501)';
-ZETA = Z;
-P = quantity.Symbolic([z, z^2; z^3, z^4] * zeta, 'grid', {Z, ZETA});
-B = quantity.Symbolic([sin(zeta); cos(zeta)], 'grid', ZETA);
+Z = linspace(0, pi, 51)';
+ZETA = linspace(0, pi / 2, 71)';
+
+P = quantity.Symbolic( sin(zeta) * z, 'grid', {Z, ZETA}, 'gridName', {'z', 'zeta'});
+B = quantity.Symbolic( sin(zeta), 'grid', ZETA, 'gridName', 'zeta');
 
 PB = P*B;
 
@@ -549,6 +671,7 @@ p = quantity.Discrete(P);
 b = quantity.Discrete(B);
 
 pb = p*b;
+
 tc.verifyEqual(MAX(abs(PB-pb)), 0, 'AbsTol', 10*eps);
 end
 
@@ -575,7 +698,10 @@ KLdKR = KL \ KR;
 
 %%
 testCase.verifyEqual(vLdVR.on(), 2 .\ (linspace(1, 2, 21).').^2);
-testCase.verifyEqual(KLdKR.on(), permute(repmat(2*eye(2), [1, 1, size(T)]), [4, 3, 1, 2]));
+
+% TODO #discussion: for the following case the operation KL \ KR seems to change the order of the grid in the old version. As this is very unexpected. I would like to change them to not change the order! 
+testCase.verifyEqual(KLdKR.on(), permute(repmat(2*eye(2), [1, 1, size(T)]), [3, 4, 1, 2]));
+
 end
 
 function testMrdivide(testCase)
@@ -601,7 +727,9 @@ KLdKR = KL / KR;
 
 %%
 testCase.verifyEqual(vLdVR.on(), 2 ./ (linspace(1, 2, 21).').^2);
-testCase.verifyEqual(KLdKR.on(),  permute(repmat(0.5*eye(2), [1, 1, size(T)]), [4, 3, 1, 2]));
+
+% TODO #discussion: for the following case the operation KL \ KR seems to change the order of the grid in the old version. As this is very unexpected. I would like to change them to not change the order! 
+testCase.verifyEqual(KLdKR.on(),  permute(repmat(0.5*eye(2), [1, 1, size(T)]), [3, 4, 1, 2]));
 end
 
 function testInv(testCase)
@@ -665,7 +793,7 @@ testCase.verifyEqual(V.on(), f.on(), 'AbsTol', 1e-5);
 
 %% int_s_t a(t,s) * b(s) ds
 v = int(a*b, sy, sy, t);
-V = quantity.Symbolic(subs(v, {t, sy}, {'t', 's'}), 'grid', {tGrid, s});
+V = quantity.Symbolic(subs(v, {t, sy}, {'t', 's'}), 'grid', {tGrid, s}, 'gridName', {'t', 's'});
 
 f = cumInt(k * x, 's', 's', 't');
 
@@ -680,7 +808,7 @@ end
 y = quantity.Discrete(C, 'size', size(c), 'grid', {tGrid s}, 'gridName', {'t' 's'});
 
 v = int(a*c, sy, sy, t);
-V = quantity.Symbolic(subs(v, {t, sy}, {'t', 's'}), 'grid', {tGrid, s});
+V = quantity.Symbolic(subs(v, {t, sy}, {'t', 's'}), 'grid', {tGrid, s}, 'gridName', {'t', 's'});
 f = cumInt( k * y, 's', 's', 't');
 
 testCase.verifyEqual( f.on(f(1).grid, f(1).gridName), V.on(f(1).grid, f(1).gridName), 'AbsTol', 1e-5 );
@@ -714,12 +842,12 @@ tGrid = linspace(0, pi, 51)';
 a = [ 1, sin(t); t, 1];
 
 % compute the numeric version of the volterra integral
-f = quantity.Symbolic(a, 'grid', {tGrid}, 'variable', {'t'});
+f = quantity.Symbolic(a, 'grid', {tGrid}, 'gridName', {'t'});
 f = quantity.Discrete(f);
 
 intK = cumInt(f, 't', 0, 't');
 
-K = quantity.Symbolic( int(a, 0, t), 'grid', {tGrid}, 'variable', {'t'});
+K = quantity.Symbolic( int(a, 0, t), 'grid', {tGrid}, 'gridName', {'t'});
 
 testCase.verifyEqual(intK.on(), K.on(), 'AbsTol', 1e-3);
 
@@ -753,61 +881,6 @@ testCase.verifyTrue(all(B_z_1(:,:,2) == 1));
 
 end
 
-function testGridJoin(testCase)
-s = linspace(0, 1);
-z = linspace(0, 1, 71)';
-t = linspace(0, 1, 51);
-
-[Z, T, S] = ndgrid(z, t, s);
-
-a = quantity.Discrete(cat(4, sin(Z.*Z.*S), cos(Z.*T.*S)), ...
-	'size', [2 1], 'grid', {z, t, s}, 'gridName', {'z', 't', 's'});
-b = quantity.Discrete(ones(numel(s), numel(t)), ...
-	'size', [1 1], 'grid', {s, t}, 'gridName', {'p', 's'});
-c = quantity.Discrete(ones(numel(t), 2, 2), ...
-	'size', [2 2], 'grid', {t}, 'gridName', {'p'});
-
-[gridJoinedAB, gridNameJoinedAB] = gridJoin(a, b);
-[gridJoinedCC, gridNameJoinedCC] = gridJoin(c, c);
-[gridJoinedBC, gridNameJoinedBC] = gridJoin(b, c);
-
-testCase.verifyEqual(gridNameJoinedAB, {'p', 's', 't', 'z'});
-testCase.verifyEqual(gridJoinedAB, {s, s, t, z});
-testCase.verifyEqual(gridNameJoinedCC, {'p'});
-testCase.verifyEqual(gridJoinedCC, {t});
-testCase.verifyEqual(gridNameJoinedBC, {'p', 's'});
-testCase.verifyEqual(gridJoinedBC, {s, t});
-end
-
-function testGridIndex(testCase)
-
-
-z = linspace(0, 2*pi, 71)';
-t = linspace(0, 3*pi, 51);
-s = linspace(0, 1);
-
-[Z, T, S] = ndgrid(z, t, s);
-
-a = quantity.Discrete(cat(4, sin(Z.*Z.*S), cos(Z.*T.*S)), ...
-	'size', [2 1], 'grid', {z, t, s}, 'gridName', {'z', 't', 's'});
-
-idx = a.gridIndex('z');
-
-testCase.verifyEqual(idx, 1);
-
-
-idx = a.gridIndex({'z', 't'});
-testCase.verifyEqual(idx, [1 2]);
-
-idx = a.gridIndex({'z', 's'});
-testCase.verifyEqual(idx, [1 3]);
-
-idx = a.gridIndex('t');
-testCase.verifyEqual(idx, 2);
-
-end
-
-
 function testMTimes(testCase)
 % multiplication of a(z) * a(z)'
 % a(z) \in (3, 2), z \in (100)
@@ -836,7 +909,7 @@ testCase.verifyTrue(numeric.near(b1b1', B1B1.on()));
 % vector multiplication of b(z, t) * b(v, t)
 b2 = cat(3, z .* t, z*0 +1);
 b2b2 = misc.multArray(b2, permute(b2, [1 2 4 3]), 4, 3, [1, 2]);
-b2b2 = permute(b2b2, [2 1 3 4]);
+b2b2 = permute(b2b2, [1 2 3 4]);
 B2 = quantity.Discrete(b2, 'size', [ 2, 1 ], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)});
 B2B2 = B2*B2';
 testCase.verifyTrue(numeric.near(b2b2, B2B2.on()));
@@ -845,12 +918,12 @@ c2 = cat(4, sin(z), sin(t), cos(z));
 b2c2 = misc.multArray(b2, c2, 4, 3, [1 2]);
 C2 = quantity.Discrete(c2, 'size', [1 3],  'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)});
 B2C2 = B2 * C2;
-testCase.verifyTrue(numeric.near(permute(b2c2, [2 1 3 4]), B2C2.on()));
+testCase.verifyTrue(numeric.near(permute(b2c2, [1 2 3 4]), B2C2.on()));
 
 % matrix b(z,t) * b(z,t)
 b = cat(4, cat(3, z .* t, z*0 +1), cat(3, sin( z ), cos( z .* t )));
 bb = misc.multArray(b, b, 4, 3, [1, 2]);
-bb = permute(bb, [2 1 3 4]);
+bb = permute(bb, [1 2 3 4]);
 B = quantity.Discrete(b, 'size', [ 2, 2 ], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)});
 BB = B*B;
 testCase.verifyTrue(numeric.near(bb, BB.on));
@@ -859,8 +932,8 @@ testCase.verifyTrue(numeric.near(bb, BB.on));
 c = cat(4, cat(3, v .* t2, v*0 +1), cat(3, sin( v ), cos( v .* t2 )), cat(3, cos( t2 ), tan( v .* t2 )));
 bc = misc.multArray(b, c, 4, 3, 2);
 bc = permute(bc, [ 1 2 4 3 5 ]);
-B = quantity.Discrete(b, 'size', [ 2, 2 ], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)});
-C = quantity.Discrete(c, 'size', [2, 3], 'gridName', {'v', 't'}, 'grid',{v(:,1),  t2(1,:)});
+B = quantity.Discrete(b, 'size', [2, 2], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)});
+C = quantity.Discrete(c, 'size', [2, 3], 'gridName', {'v', 't'}, 'grid', {v(:,1),  t2(1,:)});
 BC = B*C;
 testCase.verifyTrue(numeric.near(bc, BC.on));
 
@@ -876,7 +949,7 @@ testCase.verifyTrue( numeric.near( aa(2,2).on() , cos(z * pi) .* cos(z * pi)));
 
 %% test multiplicatin with constants:
 C = [3 0; 0 5];
-c = quantity.Discrete(C, 'gridName', {}, 'grid', {}, 'name', 'c');
+c = quantity.Discrete(C, 'domain', quantity.Domain.empty(), 'name', 'c');
 testCase.verifyTrue( numeric.near( squeeze(double(a*c)), [sin(z * pi) * 3, cos(z * pi)  * 5]));
 testCase.verifyTrue( numeric.near( squeeze(double(a*[3 0; 0 5])), [sin(z * pi) * 3, cos(z * pi)  * 5]));
 testCase.verifyTrue( numeric.near( double(c*c), C*C));
@@ -888,16 +961,23 @@ testCase.verifyTrue( numeric.near( squeeze(double(a * 42)), [sin(z * pi) * 42, c
 
 testCase.verifyTrue( numeric.near( squeeze(double(s * a')), [sin(z * pi) * 42, cos(z * pi) * 42]));
 testCase.verifyTrue( numeric.near( squeeze(double(a * s)), [sin(z * pi) * 42, cos(z * pi) * 42]));
+end
 
-%% test 
+function testOnConstant(testCase)
 
+A = [0 1; 2 3];
+const = quantity.Discrete(A, 'domain', quantity.Domain.empty);
+C = const.on([1:5]);
 
+for k = 1:numel(A)
+	testCase.verifyTrue( all( A(k) == C(:,k) ));
+end
 
 end
 
 function testIsConstant(testCase)
 
-C = quantity.Discrete(rand(3,7), 'gridName', {});
+C = quantity.Discrete(rand(3,7), 'domain', quantity.Domain.empty());
 testCase.verifyTrue(C.isConstant());
 
 z = linspace(0, pi)';
@@ -913,7 +993,7 @@ a = cat(3, [myGrid*0, myGrid, sin(myGrid)], [myGrid.^2, myGrid.^3, cos(myGrid)])
 c = rand(2, 3);  % dim = (2, 3)
 
 A = quantity.Discrete(a, 'size', [3, 2], 'grid', myGrid, 'gridName', {'z'});
-C = quantity.Discrete(c, 'gridName', {});
+C = quantity.Discrete(c, 'domain', quantity.Domain.empty());
 
 ac = misc.multArray(a, c, 3, 1);    % dim = (100, 3, 3)
 AC = A*C;
@@ -993,7 +1073,6 @@ eMatReference = zGrid + zetaGrid;
 testCase.verifyEqual(numel(eMat), numel(eMatReference));
 testCase.verifyEqual(eMat(:), eMatReference(:));
 
-
 %% addition with constant values
 testCase.verifyEqual(permute([a b], [1 3 2]), AB.on());
 
@@ -1009,6 +1088,26 @@ cAB2 = [1 2; 3 4] + AB2;
 testCase.verifyEqual(AB2c.on(), tst)
 testCase.verifyEqual(cAB2.on(), tst)
 
+%% test plus on different domains
+z = quantity.Domain('z', 0:3);
+t = quantity.Domain('t', 0:4);
+x = quantity.Domain('x', 0:5);
+
+T = quantity.Discrete( t.grid, 'domain', t);
+Z = quantity.Discrete( z.grid, 'domain', z);
+X = quantity.Discrete( x.grid, 'domain', x);
+
+TZX = T+Z+X;
+
+TZ1 = T + Z + 1;
+TZX1 = subs(TZX, 'x', 1);
+
+testCase.verifyEqual( TZX1.on(), TZ1.on(), 'AbsTol', 1e-12 );
+testCase.verifyEqual( on( T + 0.5 + X  ), on( subs( TZX, 'z', 0.5) ), 'AbsTol', 1e-12 );
+
+XTZ = X+T+Z;
+testCase.verifyEqual( on( TZX + XTZ ), on( 2 * TZX ), 'AbsTol', eps )
+
 end
 
 function testInit(testCase)
@@ -1019,7 +1118,11 @@ v = {sin(z * t * pi); cos(z * t * pi)};
 V = cat(3, v{:});
 b = quantity.Discrete(v, 'grid', {z, t}, 'gridName', {'z', 't'});
 c = quantity.Discrete(V, 'grid', {z, t}, 'gridName', {'z', 't'});
-d = quantity.Discrete(V, 'gridName', {'z', 't'});
+
+Z = quantity.Domain('z', z);
+T = quantity.Domain('t', t);
+
+d = quantity.Discrete(V, 'domain', [Z, T]);
 
 %%
 verifyTrue(testCase, misc.alln(b.on() == c.on()));
@@ -1071,23 +1174,12 @@ verifyTrue(testCase, numeric.near(A, Anum, 1e-2));
 
 end
 
-% function testNDGrid(testCase)
-% %%
-% z = linspace(0,1).';
-% t = linspace(0,1,101);
-% b = quantity.Discrete({sin(z * t * pi); cos(z * t * pi)}, 'grid', {z, t}, 'gridName', {'z', 't'});
-% % #TODO
-% end
-
-function testDefaultGrid(testCase)
-g = quantity.Discrete.defaultGrid([100, 42]);
-testCase.verifyEqual(g{1}, linspace(0, 1, 100).');
-testCase.verifyEqual(g{2}, linspace(0, 1, 42));
-end
-
 function testValue2Cell(testCase)
 v = rand([100, 42, 2, 3]);
-V = quantity.Discrete( quantity.Discrete.value2cell(v, [100, 42], [2, 3]), 'gridName', {'z', 't'} );
+Z = quantity.Domain('z', linspace(0,1,100));
+T = quantity.Domain('t', linspace(0,1,42));
+V = quantity.Discrete( quantity.Discrete.value2cell(v, [100, 42], [2, 3]), ...
+	'domain', [Z, T]);
 verifyTrue(testCase, misc.alln(v == V.on()));
 end
 
@@ -1139,6 +1231,7 @@ t = linspace(0, 1, 31);
 quan = quantity.Discrete({sin(z * t * pi); cos(z * t * pi)}, 'grid', {z, t}, 'gridName', {'z', 't'});
 gridSampled = {linspace(0, 1, 11), linspace(0, 1, 21)};
 quanCopy = copy(quan);
+
 quanSampled = quanCopy.changeGrid(gridSampled, {'z', 't'});
 testCase.verifyEqual(quanSampled.on(), quan.on(gridSampled))
 
@@ -1147,14 +1240,16 @@ quanSampled2 = quanCopy2.changeGrid(gridSampled, {'t', 'z'});
 testCase.verifyEqual(quanSampled2.on(), permute(quan.on(gridSampled, {'t', 'z'}), [2, 1, 3]));
 end
 
-
-
 function testSubs(testCase)
 %%
 myTestArray = ones(21, 41, 2, 3);
 myTestArray(:,1,1,1) = linspace(0, 1, 21);
 myTestArray(:,:,2,:) = 2;
-quan = quantity.Discrete(myTestArray, 'gridName', {'z', 'zeta'}, 'size', [2, 3]);
+z = quantity.Domain('z', linspace(0, 1, 21));
+zeta = quantity.Domain('zeta', linspace(0, 1, 41));
+
+quan = quantity.Discrete(myTestArray, 'domain', [z, zeta]);
+
 quanZetaZ = quan.subs({'z', 'zeta'}, {'zeta', 'z'});
 quan10 = quan.subs({'z', 'zeta'}, {1, 0});
 quan01 = quan.subs({'z', 'zeta'}, {0, 1});
@@ -1162,7 +1257,7 @@ quant1 = quan.subs({'z', 'zeta'}, {'t', 1});
 quanZetaZeta = quan.subs({'z'}, 'zeta');
 quanPt = quan.subs({'z'}, {1});
 quanEta = quan.subs({'z'}, {'eta'});
-%%
+%
 testCase.verifyEqual(quan10, shiftdim(quan.on({1, 0})));
 testCase.verifyEqual(quan01, shiftdim(quan.on({0, 1})));
 testCase.verifyEqual(quanZetaZ.on(), quan.on());
@@ -1175,9 +1270,17 @@ testCase.verifyEqual(quanZetaZeta.on(), quanZetaZetaReference);
 testCase.verifyEqual(quanEta(1).gridName, {'eta', 'zeta'})
 testCase.verifyEqual(quanPt.on(), shiftdim(quan.on({1, quan(1).grid{2}})));
 
-%% 4d-case
+testCase.verifyEqual( gridSize( quanZetaZeta.subs( ...
+	quantity.Domain('zeta', linspace(0,1,3))) ), 3);
+
+% 4d-case
 myTestArray4d = rand(11, 21, 31, 41, 1, 2);
-quan4d = quantity.Discrete(myTestArray4d, 'gridName', {'z', 'zeta', 'eta', 'beta'}, 'name', 'fun5d');
+x1 = quantity.Domain('z', linspace(0, 1, 11));
+x2 = quantity.Domain('zeta', linspace(0, 1, 21));
+x3 = quantity.Domain('eta', linspace(0, 1, 31));
+x4 = quantity.Domain('beta', linspace(0, 1, 41));
+
+quan4d = quantity.Discrete(myTestArray4d, 'domain', [x1, x2, x3, x4], 'name', 'fun5d');
 quan4dAllEta = quan4d.subs({'z', 'zeta', 'eta', 'beta'}, {'eta', 'eta', 'eta', 'eta'});
 testCase.verifyEqual(reshape(quan4d.on({1, 1, 1, 1}), size(quan4d)), ...
 	reshape(quan4dAllEta.on({1}), size(quan4dAllEta)));
@@ -1185,6 +1288,8 @@ testCase.verifyEqual(reshape(quan4d.on({1, 1, 1, 1}), size(quan4d)), ...
 quan4dArbitrary = quan4d.subs({'z', 'zeta', 'eta', 'beta'}, {'zeta', 'beta', 'z', 1});
 testCase.verifyEqual(reshape(quan4d.on({1, 1, 1, 1}), size(quan4d)), ...
 	reshape(quan4dArbitrary.on({1, 1, 1}), size(quan4dAllEta)));
+
+
 end
 
 function testZTTimes(testCase)
diff --git a/+unittests/+quantity/testDomain.m b/+unittests/+quantity/testDomain.m
new file mode 100644
index 0000000000000000000000000000000000000000..fae2acc0d35e4fdcdf159f778c6dcc56d31de900
--- /dev/null
+++ b/+unittests/+quantity/testDomain.m
@@ -0,0 +1,180 @@
+function [tests] = testDomain()
+	tests = functiontests(localfunctions);
+end
+
+function setupOnce(testCase)
+end
+
+function testEquality(testCase)
+
+d = quantity.Domain('d', 0);
+e = quantity.Domain.empty();
+
+dd = d;
+ee = e;
+
+testCase.verifyNotEqual( d, e );
+testCase.verifyEqual( dd, d );
+testCase.verifyEqual( ee, e );
+testCase.verifyNotEqual( e, d );
+
+end
+
+function testDomainInit(testCase)
+
+Z = linspace(0,pi, 3);
+d = quantity.Domain('z', Z);
+D = [d d];
+
+testCase.verifyEqual( ndims(D), 2);
+testCase.verifyEqual( ndims(d), 1);
+end
+
+function testSort(testCase)
+
+z = linspace(0, pi, 3);
+a = quantity.Domain('a', z);
+b = quantity.Domain('b', z);
+c = quantity.Domain('c', z);
+
+d = [a b c];
+
+[d_, I_] = d.sort('descend');
+[d__, I__] = d.sort('ascend');
+d___ = d.sort();
+
+testCase.verifyEqual({d_.name}, {'c', 'b', 'a'});
+testCase.verifyEqual({d__.name}, {d.name});
+testCase.verifyEqual({d__.name}, {d___.name});
+testCase.verifyEqual( I_, flip(I__) );
+
+
+end
+
+function testDomainGridLength(testCase)
+
+Z = linspace(0,pi, 3);
+d = quantity.Domain('z', Z);
+D = [d d];
+
+testCase.verifyEqual( cellfun(@(v) numel(v), {Z, Z}), D.gridLength)
+
+end
+
+function testDomainNumGridElements(testCase)
+
+Z = linspace(0,pi, 3);
+d = quantity.Domain('z', Z);
+D = [d d];
+
+testCase.verifyEqual( D.numGridElements, prod([length(Z), length(Z)]));
+
+end
+
+function testEmpty(testCase)
+
+d = quantity.Domain();
+e = quantity.Domain.empty();
+o = quantity.Domain('', 1);
+
+testCase.verifyTrue( isempty(d) )
+testCase.verifyTrue( isempty(e) )
+testCase.verifyTrue( isempty([d, e]))
+testCase.verifyTrue( isempty([d, d]))
+testCase.verifyFalse( isempty(o) )
+testCase.verifyFalse( isempty([o e]) )
+testCase.verifyTrue( isempty([o d]) )
+
+end
+
+
+function testDefaultGrid(testCase)
+	g = quantity.Domain.defaultGrid([100, 42]);
+	testCase.verifyEqual(g(1).grid, linspace(0, 1, 100).');
+	testCase.verifyEqual(g(2).grid, linspace(0, 1, 42).');
+end
+
+function testGridJoin(testCase)
+s = linspace(0, 1);
+z = linspace(0, 1, 71)';
+t = linspace(0, 1, 51);
+
+S = quantity.Domain('s', s);
+Z = quantity.Domain('z', z);
+T = quantity.Domain('t', t);
+Ps = quantity.Domain('p', s);
+St = quantity.Domain('s', t);
+Pt = quantity.Domain('p', t);
+
+a = [Z, T, S];
+b = [Ps, St];
+c = Pt;
+% 
+% 
+% a = quantity.Discrete(cat(4, sin(Z.*Z.*S), cos(Z.*T.*S)), ...
+% 	'size', [2 1], 'grid', {z, t, s}, 'gridName', {'z', 't', 's'});
+% b = quantity.Discrete(ones(numel(s), numel(t)), ...
+% 	'size', [1 1], 'grid', {s, t}, 'gridName', {'p', 's'});
+% c = quantity.Discrete(ones(numel(t), 2, 2), ...
+% 	'size', [2 2], 'grid', {t}, 'gridName', {'p'});
+
+joinedDomainAB = sort( gridJoin(a, b) );
+joinedDomainCC = sort( gridJoin(c, c) );
+joinedDomainBC = sort( gridJoin(b, c) );
+joinedDomain = sort( gridJoin(Z, [Z, T]) );
+
+testCase.verifyEqual({joinedDomainAB.name}, {'p', 's', 't', 'z'});
+testCase.verifyEqual({joinedDomainAB.grid}, {s(:), s(:), t(:), z(:)});
+testCase.verifyEqual({joinedDomainCC.name}, {'p'});
+testCase.verifyEqual({joinedDomainCC.grid}, {t(:)});
+testCase.verifyEqual({joinedDomainBC.name}, {'p', 's'});
+testCase.verifyEqual({joinedDomainBC.grid}, {s(:), t(:)});
+testCase.verifyEqual({joinedDomain.grid}, {t(:), z(:)});
+end
+
+function testGridIndex(testCase)
+z = linspace(0, 2*pi, 71)';
+t = linspace(0, 3*pi, 51);
+s = linspace(0, 1);
+
+S = quantity.Domain('s', s);
+Z = quantity.Domain('z', z);
+T = quantity.Domain('t', t);
+
+a = [Z, T, S];
+
+idx = a.gridIndex('z');
+
+testCase.verifyEqual(idx, 1);
+
+
+idx = a.gridIndex({'z', 't'});
+testCase.verifyEqual(idx, [1 2]);
+
+idx = a.gridIndex({'z', 's'});
+testCase.verifyEqual(idx, [1 3]);
+
+idx = a.gridIndex('t');
+testCase.verifyEqual(idx, 2);
+
+end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/+unittests/+quantity/testOperator.m b/+unittests/+quantity/testOperator.m
index 8e853d2226aef03e5e817925914111e0eb4cc749..81e8acfcab8fbeca9fa04ff95391d313cf169a10 100644
--- a/+unittests/+quantity/testOperator.m
+++ b/+unittests/+quantity/testOperator.m
@@ -27,7 +27,7 @@ A = cat(3, [ 0, 0, 0, 0; ...
 B = cat(3, [1, 0, 0, 0]', zeros(4, 1), zeros(4, 1));
 		 
 for k = 1:3
-	a{k} = quantity.Symbolic(A(:,:,k), 'grid', Z, 'variable', z);
+	a{k} = quantity.Symbolic(A(:,:,k), 'grid', Z, 'gridName', 'z');
 end
 		 
 A = quantity.Operator(a, 's', s);
@@ -37,6 +37,31 @@ testCase.TestData.a = a;
 
 end
 
+function testApplyTo(testCase)
+
+	A = [0 1 0; 0 0 1; - (1:3)];
+	B = [0 0 1]';
+	C = [1 0 0];
+	
+	sys = ss(A, B, C, []);
+	detsIA = quantity.Operator(num2cell(flip(charpoly(A))));
+	adjB = quantity.Operator( adjoint(sym('s')*eye(size(A)) - A) * B );
+	
+	t = sym('t', 'real');
+	sol.y = quantity.Symbolic( t.^3 .* (1 - t).^3, ...
+		'grid', linspace(0, 1, 1e2), 'gridName', 't');
+	
+	sol.u = detsIA.applyTo(sol.y);
+	sol.x = adjB.applyTo(sol.y);
+	
+	[sim.y, sim.t, sim.x] = lsim(sys, sol.u.on(), sol.y.domain.grid, 'foh');
+		
+	% deviation of final values:
+	dev.y = sim.y(end)
+	dev.x = sim.x(end, :)
+	
+end
+
 function testCTranspose(testCase)
 	At = testCase.TestData.A';
 	testCase.verifyTrue(At(3).coefficient == testCase.TestData.A(3).coefficient');	
@@ -47,9 +72,9 @@ function testFundamentalMatrixSpaceDependent(testCase)
 %
 	a = 20;
 	z = sym('z', 'real');
-	Z = linspace(0, 1, 32)';
+	Z = linspace(0, 1, 5)';
 	A = quantity.Operator(...
-		{quantity.Symbolic([1, z; 0, a], 'variable', 'z', 'grid', Z)});
+		{quantity.Symbolic([1, z; 0, a], 'gridName', 'z', 'grid', Z)});
 	
 	F = A.stateTransitionMatrix();
 		
@@ -60,7 +85,7 @@ function testFundamentalMatrixSpaceDependent(testCase)
 		f = (exp(z) - exp(a * z) - (1 - a) * z * exp( a * z ) ) / (1 - a)^2; 
 	end   
 
-	PhiExact = quantity.Symbolic([exp(z), f; 0, exp(a*z)], 'grid', Z, 'name', 'Phi_A');
+	PhiExact = quantity.Symbolic([exp(z), f; 0, exp(a*z)], 'grid', Z, 'gridName', 'z',  'name', 'Phi_A');
 		
 	testCase.verifyEqual(double(F), PhiExact.on(), 'RelTol', 1e-6);
 	
@@ -69,10 +94,10 @@ end
 function testStateTransitionMatrix(testCase)
 N = 2;
 Z = linspace(0,1,11)';
-A0 = quantity.Symbolic([0 1; 1 0], 'grid', Z, 'variable', 'z');
-A1 = quantity.Symbolic([0 1; 0 0], 'grid', Z, 'variable', 'z');
+A0 = quantity.Symbolic([0 1; 1 0], 'grid', Z, 'gridName', 'z');
+A1 = quantity.Symbolic([0 1; 0 0], 'grid', Z, 'gridName', 'z');
 A = quantity.Operator({A0, A1});
-B = quantity.Operator(quantity.Symbolic([-1 -1; 0 0], 'grid', Z, 'variable', 'z'));
+B = quantity.Operator(quantity.Symbolic([-1 -1; 0 0], 'grid', Z, 'gridName', 'z'));
 
 [Phi1, Psi1] = A.stateTransitionMatrix(N, B);
 [Phi2, Psi2] = A.stateTransitionMatrixByOdeSystem(N, B);
@@ -97,7 +122,7 @@ end
 	
 function testMTimes(testCase)
 
-	z = linspace(0, pi, 15)';
+	z = linspace(0, pi, 3)';
 	A0 = quantity.Discrete(reshape((repmat([1 2; 3 4], length(z), 1)), length(z), 2, 2),...
 		'gridName', 'z', 'grid', z, 'name', 'A');
 	A1 = quantity.Discrete(reshape((repmat([5 6; 7 8], length(z), 1)), length(z), 2, 2), ...
@@ -115,7 +140,7 @@ function testMTimes(testCase)
 	As = A0.at(0) + A1.at(0)*s + A2.at(0)*s^2;
 	Bs = A0.at(0);
 	
-	Cs = misc.polynomial2coefficients(As*Bs, s);
+	Cs = polyMatrix.polynomial2coefficients(As*Bs, s);
 	
 	for k = 1:3
 		testCase.verifyTrue( numeric.near(Cs(:,:,k), C(k).coefficient.at(0)) );
@@ -133,7 +158,7 @@ function testMTimes(testCase)
 end
 
 function testSum(testCase)
-z = linspace(0, pi, 15)';
+z = linspace(0, pi, 5)';
 	A0 = quantity.Discrete(reshape((repmat([1 2; 3 4], length(z), 1)), length(z), 2, 2),...
 		'gridName', 'z', 'grid', z, 'name', 'A');
 	A1 = quantity.Discrete(reshape((repmat([5 6; 7 8], length(z), 1)), length(z), 2, 2), ...
diff --git a/+unittests/+quantity/testSymbolic.m b/+unittests/+quantity/testSymbolic.m
index 5634c445b328a6105b88f0d24e20ed1299677aa2..9d58191833f10fc15862123def7719d015923cfb 100644
--- a/+unittests/+quantity/testSymbolic.m
+++ b/+unittests/+quantity/testSymbolic.m
@@ -7,8 +7,11 @@ end
 function testVec2Diag(testCase)
 % quantity.Symbolic
 syms z
-myMatrixSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1, 0; 0, 0.9-z/2]);
-myVectorSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1; 0.9-z/2]);
+Z = quantity.Domain('z', linspace(0, 1, 3));
+myMatrixSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1, 0; 0, 0.9-z/2], ...
+	'domain', Z);
+myVectorSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1; 0.9-z/2], ...
+	'domain', Z);
 
 testCase.verifyTrue( myVectorSymbolic.vec2diag.near(myMatrixSymbolic) );
 end
@@ -17,9 +20,9 @@ function testSymbolicEvaluation(tc)
 syms z
 myGrid = linspace(0, 1, 7);
 fS = quantity.Symbolic(z * sinh(z * 1e5) / cosh(z * 1e5), ...
-	'variable', {z}, 'grid', {myGrid}, 'symbolicEvaluation', true);
+	'gridName', {'z'}, 'grid', {myGrid}, 'symbolicEvaluation', true);
 fF = quantity.Symbolic(z * sinh(z * 1e5) / cosh(z * 1e5), ...
-	'variable', {z}, 'grid', {myGrid}, 'symbolicEvaluation', false);
+	'gridName', {'z'}, 'grid', {myGrid}, 'symbolicEvaluation', false);
 
 tc.verifyTrue(any(isnan(fF.on)))
 tc.verifyFalse(any(isnan(fS.on)))
@@ -29,7 +32,7 @@ function testCtranspose(tc)
 syms z zeta
 qSymbolic = quantity.Symbolic(...
 	[1+zeta, -zeta; -z, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 'q');
+	'gridName', {'z', 'zeta'}, 'name', 'q');
 qSymbolicCtransp = qSymbolic';
 tc.verifyEqual(qSymbolic(1,1).on(), qSymbolicCtransp(1,1).on());
 tc.verifyEqual(qSymbolic(2,2).on(), qSymbolicCtransp(2,2).on());
@@ -37,7 +40,7 @@ tc.verifyEqual(qSymbolic(1,2).on(), qSymbolicCtransp(2,1).on());
 tc.verifyEqual(qSymbolic(2,1).on(), qSymbolicCtransp(1,2).on());
 
 qSymbolic2 = quantity.Symbolic(sym('z') * 2i + sym('zeta'), ...
-	'grid', {linspace(0, 1, 21), linspace(0, 1, 11)}, 'variable', sym({'z', 'zeta'}), 'name', 'im');
+	'grid', {linspace(0, 1, 21), linspace(0, 1, 11)}, 'gridName', {'z', 'zeta'}, 'name', 'im');
 qSymolic2Ctransp = qSymbolic2';
 tc.verifyEqual(qSymbolic2.real.on(), qSymolic2Ctransp.real.on());
 tc.verifyEqual(qSymbolic2.imag.on(), -qSymolic2Ctransp.imag.on());
@@ -47,7 +50,7 @@ function testTranspose(tc)
 syms z zeta
 qSymbolic = quantity.Symbolic(...
 	[1+z*zeta, -zeta; -z, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 'q');
+	'gridName', {'z', 'zeta'}, 'name', 'q');
 qSymbolicTransp = qSymbolic.';
 tc.verifyEqual(qSymbolic(1,1).on(), qSymbolicTransp(1,1).on());
 tc.verifyEqual(qSymbolic(2,2).on(), qSymbolicTransp(2,2).on());
@@ -59,16 +62,16 @@ function testFlipGrid(tc)
 syms z zeta
 myGrid = linspace(0, 1, 11);
 f = quantity.Symbolic([1+z+zeta; 2*zeta+sin(z)] + zeros(2, 1)*z*zeta, ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid});
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid});
 % flip zeta
 fReference = quantity.Symbolic([1+z+(1-zeta); 2*(1-zeta)+sin(z)] + zeros(2, 1)*z*zeta, ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid});
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid});
 fFlipped = f.flipGrid('zeta');
 tc.verifyEqual(fReference.on, fFlipped.on, 'AbsTol', 10*eps);
 
 % flip z and zeta
 fReference2 = quantity.Symbolic([1+(1-z)+(1-zeta); 2*(1-zeta)+sin(1-z)] + zeros(2, 1)*z*zeta, ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid});
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid});
 fFlipped2 = f.flipGrid({'z', 'zeta'});
 tc.verifyEqual(fReference2.on, fFlipped2.on, 'AbsTol', 10*eps);
 end % testFlipGrid();
@@ -76,14 +79,15 @@ end % testFlipGrid();
 function testCumInt(testCase)
 tGrid = linspace(pi, 1.1*pi, 51)';
 sGrid = tGrid;
-syms s t
+s = sym('s');
+t = sym('t');
 
 a = [ 1, s; t, 1];
 b = [ s; 2*s];
 
 %% int_0_t a(t,s) * b(s) ds
 % compute symbolic version of the volterra integral
-integrandSymbolic = quantity.Symbolic(a*b, 'grid', {tGrid, sGrid}, 'variable', {t, s});
+integrandSymbolic = quantity.Symbolic(a*b, 'grid', {tGrid, sGrid}, 'gridName', {'t', 's'});
 integrandDiscrete = quantity.Discrete(integrandSymbolic);
 V = cumInt(integrandSymbolic, 's', tGrid(1), 't');
 f = cumInt(integrandDiscrete, 's', sGrid(1), 't');
@@ -100,6 +104,7 @@ testCase.verifyEqual( f.on(f(1).grid, f(1).gridName), V.on(f(1).grid, f(1).gridN
 fCumInt2Bcs = cumInt(integrandSymbolic, 's', 'zeta', 't');
 fCumInt2Cum = cumInt(integrandSymbolic, 's', tGrid(1), 't') ...
 	- cumInt(integrandSymbolic, 's', tGrid(1), 'zeta');
+
 testCase.verifyEqual(fCumInt2Bcs.on(fCumInt2Bcs(1).grid, fCumInt2Bcs(1).gridName), ...
 	fCumInt2Cum.on(fCumInt2Bcs(1).grid, fCumInt2Bcs(1).gridName), 'AbsTol', 100*eps);
 end
@@ -108,7 +113,7 @@ function testScalarPlusMinusQuantity(testCase)
 syms z
 myGrid = linspace(0, 1, 7);
 f = quantity.Symbolic([1; 2] + zeros(2, 1)*z, ...
-	'variable', {z}, 'grid', {myGrid});
+	'gridName', {'z'}, 'grid', {myGrid});
 testCase.verifyError(@() 1-f-1, '');
 testCase.verifyError(@() 1+f+1, '');
 end
@@ -117,7 +122,7 @@ function testNumericVectorPlusMinusQuantity(testCase)
 syms z
 myGrid = linspace(0, 1, 7);
 f = quantity.Symbolic([1+z; 2+sin(z)] + zeros(2, 1)*z, ...
-	'variable', {z}, 'grid', {myGrid});
+	'gridName', {'z'}, 'grid', {myGrid});
 a = ones(size(f));
 testCase.verifyEqual(on(a-f), 1-f.on(), 'RelTol', 10*eps);
 testCase.verifyEqual(on(f-a), f.on()-1, 'RelTol', 10*eps);
@@ -129,11 +134,11 @@ function testDiffWith2Variables(testCase)
 syms z zeta
 myGrid = linspace(0, 1, 7);
 f = quantity.Symbolic([z*zeta, z; zeta, 1], ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid});
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid});
 fdz = quantity.Symbolic([zeta, 1; 0, 0], ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid});
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid});
 fdzeta = quantity.Symbolic([z, 0; 1, 0], ...
-	'variable', {z, zeta}, 'grid', {myGrid, myGrid});
+	'gridName', {'z', 'zeta'}, 'grid', {myGrid, myGrid});
 
 fRefTotal = 0*f.on();
 fRefTotal(:,:,1,1) = 1;
@@ -149,7 +154,7 @@ function testGridName2variable(testCase)
 syms z zeta eta e a
 myGrid = linspace(0, 1, 7);
 obj = quantity.Symbolic([z*zeta, eta*z; e*a, a], ...
-	'variable', {z zeta eta e a}, ...
+	'gridName', {'z' 'zeta' 'eta' 'e' 'a'}, ...
 	'grid', {myGrid, myGrid, myGrid, myGrid, myGrid});
 
 thisGridName = {'eta', 'e', 'z'};
@@ -168,8 +173,8 @@ end
 
 function testCat(testCase)
 syms z zeta
-f1 = quantity.Symbolic(1+z*z, 'grid', {linspace(0, 1, 21)}, 'variable', {z}, 'name', 'f1');
-f2 = quantity.Symbolic(sin(z), 'grid', {linspace(0, 1, 21)}, 'variable', {z}, 'name', 'f2');
+f1 = quantity.Symbolic(1+z*z, 'grid', {linspace(0, 1, 21)}, 'gridName', {'z'}, 'name', 'f1');
+f2 = quantity.Symbolic(sin(z), 'grid', {linspace(0, 1, 21)}, 'gridName', {'z'}, 'name', 'f2');
 
 % vertical concatenation
 F = [f1; f2];
@@ -189,7 +194,7 @@ testCase.verifyTrue(F(2,1) == f1);
 testCase.verifyTrue(F(2,2) == f2);
 
 % concatenation on different grids
-f3 = quantity.Symbolic(cos(z), 'grid', {linspace(0, 1, 13)}, 'variable', {z}, 'name', 'f1');
+f3 = quantity.Symbolic(cos(z), 'grid', {linspace(0, 1, 13)}, 'gridName', {'z'}, 'name', 'f1');
 
 F = [f1, f3];
 testCase.verifyEqual(F(2).gridSize, f1.gridSize)
@@ -208,19 +213,19 @@ end
 function testExp(testCase)
 % 1 spatial variable
 syms z zeta
-s1d = quantity.Symbolic(1+z*z, 'grid', {linspace(0, 1, 21)}, 'variable', {z}, 'name', 's1d');
+s1d = quantity.Symbolic(1+z*z, 'grid', {linspace(0, 1, 21)}, 'gridName', {'z'}, 'name', 's1d');
 testCase.verifyEqual(s1d.exp.on(), exp(s1d.on()));
 
 % diagonal matrix
 s2dDiag = quantity.Symbolic([1+z*zeta, 0; 0, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 's2dDiag');
+	'gridName', {'z', 'zeta'}, 'name', 's2dDiag');
 testCase.verifyEqual(s2dDiag.exp.on(), exp(s2dDiag.on()));
 end
 
 function testExpm(testCase)
 syms z zeta
 mat2d = quantity.Symbolic(ones(2, 2) + [1+z*zeta, 3*zeta; 2+5*z+zeta, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 's2d');
+	'gridName', {'z', 'zeta'}, 'name', 's2d');
 mat2dMat = mat2d.on();
 mat2dExpm = 0 * mat2d.on();
 for zIdx = 1 : 21
@@ -272,7 +277,7 @@ function testOn(testCase)
 syms z zeta
 gridZ = linspace(0, 1, 40);
 gridZeta = linspace(0, 1, 21);
-A = quantity.Symbolic(2+[z, z^2, z+zeta; -sin(zeta), z*zeta, 1], 'variable', {z, zeta}, ...
+A = quantity.Symbolic(2+[z, z^2, z+zeta; -sin(zeta), z*zeta, 1], 'gridName', {'z', 'zeta'}, ...
 	'grid', {gridZ, gridZeta}, 'name', 'A');
 
 %%
@@ -305,17 +310,17 @@ end
 function testSqrt(testCase)
 % 1 spatial variable
 syms z zeta
-s1d = quantity.Symbolic(1+z*z, 'grid', {linspace(0, 1, 21)}, 'variable', {z}, 'name', 's1d');
+s1d = quantity.Symbolic(1+z*z, 'grid', {linspace(0, 1, 21)}, 'gridName', {'z'}, 'name', 's1d');
 testCase.verifyEqual(s1d.sqrt.on(), sqrt(s1d.on()));
 
 % 2 spatial variables
 s2d = quantity.Symbolic(1+z*zeta, 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 's2d');
+	'gridName', {'z', 'zeta'}, 'name', 's2d');
 testCase.verifyEqual(s2d.sqrt.on(), sqrt(s2d.on()));
 
 % diagonal matrix
 s2dDiag = quantity.Symbolic([1+z*zeta, 0; 0, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 's2dDiag');
+	'gridName', {'z', 'zeta'}, 'name', 's2dDiag');
 testCase.verifyEqual(s2dDiag.sqrt.on(), sqrt(s2dDiag.on()));
 
 end
@@ -323,7 +328,7 @@ end
 function testSqrtm(testCase)
 syms z zeta
 mat2d = quantity.Symbolic(ones(2, 2) + [1+z*zeta, 3*zeta; 2+5*z+zeta, z^2], 'grid', {linspace(0, 1, 21), linspace(0, 1, 41)},...
-	'variable', {z, zeta}, 'name', 's2d');
+	'gridName', {'z', 'zeta'}, 'name', 's2d');
 mat2dMat = mat2d.on();
 mat2dSqrtm = 0 * mat2d.on();
 for zIdx = 1 : 21
@@ -342,7 +347,7 @@ function testInv(testCase)
 syms z
 zGrid = linspace(0, 1, 21);
 v = quantity.Symbolic(1+z*z, ...
-	'grid', {zGrid}, 'variable', {z}, 'name', 's1d');
+	'grid', {zGrid}, 'gridName', {'z'}, 'name', 's1d');
 vInvReference = 1 ./ (1 + zGrid.^2);
 vInv = v.inv();
 
@@ -365,7 +370,7 @@ end
 
 function testZero(testCase)
 
-x = quantity.Symbolic(0);
+x = quantity.Symbolic(0, 'domain', quantity.Domain.empty());
 
 verifyEqual(testCase, x.on(), 0);
 verifyTrue(testCase, misc.alln(on(x) * magic(5) == zeros(5)));
@@ -375,7 +380,10 @@ end
 function testCast2Quantity(testCase)
 
 syms z t
-x = quantity.Symbolic(sin(z * t * pi), 'grid', {linspace(0, 1).', linspace(0, 2, 200)});
+d = [quantity.Domain('z', linspace(0, 1, 5)), ...
+	 quantity.Domain('t', linspace(0, 2, 2))];
+
+x = quantity.Symbolic(sin(z * t * pi), 'domain', d);
 
 X = quantity.Discrete(x);
 
@@ -384,7 +392,9 @@ end
 
 function testMrdivide(testCase)
 syms z t
-x = quantity.Symbolic(sin(z * t * pi), 'grid', {linspace(0, 1).', linspace(0, 2, 200)});
+d = [quantity.Domain('z', linspace(0, 1, 5)), ...
+	 quantity.Domain('t', linspace(0, 2, 2))];
+x = quantity.Symbolic(sin(z * t * pi), 'domain', d);
 o = x / x;
 
 verifyTrue(testCase, misc.alln(o.on == 1));
@@ -398,7 +408,9 @@ end
 
 function testMTimesConstants(testCase)
 syms z t
-x = quantity.Symbolic(sin(z * t * pi), 'grid', {linspace(0, 1).', linspace(0, 2, 200)});
+d = [quantity.Domain('z', linspace(0, 1, 5)), ...
+	 quantity.Domain('t', linspace(0, 2, 2))];
+x = quantity.Symbolic(sin(z * t * pi), 'domain', d);
 
 % test multiplicaiton iwth a constant scalar
 x5 = x * 5;
@@ -424,9 +436,9 @@ function testMLRdivide2ConstantSymbolic(testCase)
 
 syms z
 assume(z>0 & z<1);
-Lambda = quantity.Symbolic(eye(2), 'variable', z, ...
+Lambda = quantity.Symbolic(eye(2), ...
 	'grid', linspace(0, 1), 'gridName', 'z', 'name', 'Lambda');
-A = quantity.Symbolic(eye(2), 'variable', z, ...
+A = quantity.Symbolic(eye(2), ...
 	'grid', linspace(0, 1), 'gridName', 'z', 'name', 'A');
 C1 = A / Lambda;
 C2 = A \ Lambda;
@@ -451,7 +463,9 @@ end
 function testCastToQuantity(testCase)
 
 syms z t
-x = quantity.Symbolic(sin(z * t * pi), 'grid', {linspace(0, 1).', linspace(0, 2, 200)});
+x = quantity.Symbolic(sin(z * t * pi), ...
+	'grid', {linspace(0, 1).', linspace(0, 2, 200)}, ...
+	'gridName', {'z', 't'});
 
 X1 = quantity.Discrete(x);
 
@@ -460,25 +474,14 @@ verifyEqual(testCase, X1.on, x.on);
 
 end
 
-% function testmTimesFunctionHandleSymbolic(testCase)
-%
-% TODO repair this kind of multiplication!
-%
-% %
-% f = quantity.Function(@(z) z, 'name', 'f');
-% s = quantity.Symbolic(sym('z'), 'name', 'f');
-% verifyTrue(testCase, isa(f*s, 'quantity.Function'));
-%
-% end
-
-
 function testDiff(testCase)
 %%
 z = sym('z', 'real');
 f1 = sinh(z * pi);
 f2 = cosh(z * pi);
 
-f = quantity.Symbolic([f1 ; f2 ], 'name', 'f');
+f = quantity.Symbolic([f1 ; f2 ], 'name', 'f', 'domain', ...
+	quantity.Domain('z', linspace(0,1,11)));
 
 d2f = f.diff(2);
 d1f = f.diff(1);
@@ -502,23 +505,37 @@ verifyTrue(testCase, numeric.near(double([R3{:}]), f.diff(2).on(), 1e-12));
 
 end
 
-
 function testInit(testCase)
 
-syms z
-A = [0 1; 1 0];
+z = linspace(0,1).';
+t = linspace(0,1,3);
+
+syms x y
 
-%% compute comparative solution
-P1 = expm(A * z);
+v = [sin(x * y * pi); cos(x * y * pi)];
+
+try
+	b = quantity.Symbolic(v, 'grid', {z, t}, 'gridName', {'z', 't'});
+	testCase.verifTrue(false);
+end
+	
+b = quantity.Symbolic(v, 'grid', {z, t}, 'gridName', {'x', 'y'});
+c = quantity.Symbolic(v, 'grid', {z, t}, 'gridName', {'x', 'y'});
 
+%%
+verifyTrue(testCase, misc.alln(b.on() == c.on()));
 end
 
 function testPlus(testCase)
 syms z zeta
 
+Z = quantity.Domain('z', linspace(0, 1, 3));
+Zeta = quantity.Domain('zeta', linspace(0, 1, 4));
+
 %% 1 + 2 variables
-fSymVec3 = quantity.Symbolic([sinh(pi * z)], 'name', 'sinh');
-fSymVec4 = quantity.Symbolic([cosh(zeta * pi * z)], 'name', 'cosh');
+fSymVec3 = quantity.Symbolic([sinh(pi * z)], 'name', 'sinh', 'domain', Z);
+fSymVec4 = quantity.Symbolic([cosh(zeta * pi * z)], 'name', 'cosh', ...
+	'domain', [Z Zeta]);
 
 result = fSymVec3 - fSymVec4;
 resultDiscrete = quantity.Discrete(fSymVec3) - quantity.Discrete(fSymVec4);
@@ -527,7 +544,9 @@ resultDiscrete = quantity.Discrete(fSymVec3) - quantity.Discrete(fSymVec4);
 testCase.verifyEqual(result.on(), resultDiscrete.on(), 'AbsTol', 10*eps);
 
 %% quantity + constant
-myQuan = quantity.Symbolic([sinh(pi * z), cosh(zeta * pi * z); 1, z^2], 'name', 'myQuan');
+
+myQuan = quantity.Symbolic([sinh(pi * z), cosh(zeta * pi * z); 1, z^2], ...
+	'name', 'myQuan', 'domain', [Z Zeta]);
 myQuanPlus1 = myQuan + ones(size(myQuan));
 my1PlusQuan =  ones(size(myQuan)) + myQuan;
 
@@ -537,7 +556,8 @@ testCase.verifyEqual(myQuan.on()+1, my1PlusQuan.on(), 'AbsTol', 10*eps);
 
 
 %% 1 variable
-fSymVec = quantity.Symbolic([sinh(z * pi) ; cosh(z * pi)], 'name', 'sinhcosh');
+fSymVec = quantity.Symbolic([sinh(z * pi) ; cosh(z * pi)], ...
+	'domain', Z, 'name', 'sinhcosh');
 
 F = fSymVec + fSymVec;
 val = F.on();
@@ -556,7 +576,8 @@ syms zeta
 f1fun = @(z, zeta) sinh(z * pi);
 f2fun = @(z, zeta) cosh(zeta * pi) + 0*z;
 
-fSymVec = quantity.Symbolic([sinh(z * pi); cosh(zeta * pi)], 'name', 'sinhcosh');
+fSymVec = quantity.Symbolic([sinh(z * pi); cosh(zeta * pi)], ...
+	'domain', [Z Zeta], 'name', 'sinhcosh');
 fSymVec2 = [fSymVec(2); fSymVec(1)];
 
 F = fSymVec + fSymVec;
@@ -579,7 +600,8 @@ syms z
 f1 = sinh(z * pi);
 f2 = cosh(z * pi);
 
-f = quantity.Symbolic([f1 ; f2 ], 'name', 'sinhcosh');
+f = quantity.Symbolic([f1 ; f2 ], 'name', 'sinhcosh', 'domain', ...
+	quantity.Domain('z', linspace(0,1,3)));
 
 F = f * f.';
 val = F.on();
@@ -605,36 +627,42 @@ end
 
 function testVariable(testCase)
 syms z
-q = quantity.Symbolic(z);
+q = quantity.Symbolic(z, 'domain', quantity.Domain('z', 1));
 
 verifyEqual(testCase, q.variable, z);
 
 end
 
-function testUMinus(testCase)
+function testUMinusAndUPlus(testCase)
 %%
 syms z;
 f1 = (sin(z .* pi) );
 f2 =  z;
 f3 = (cos(z .* pi) );
-zz = linspace(0, 1, 42).';
-f = quantity.Symbolic([f1, f2; 0, f3], ...
-	'grid', {zz});
+d = quantity.Domain('z', linspace(0, 1, 3).');
 
-mf = -f;
+f = quantity.Symbolic([f1, f2; 0, f3], ...
+	'domain', d);
+fDisc = quantity.Discrete(f);
+values = f.on();
 
 %%
-verifyEqual(testCase, [f.valueDiscrete], -[mf.valueDiscrete]);
+testCase.verifyEqual(on( -f ), -values);
+testCase.verifyEqual(on( + ( - f ) ), -values );
+testCase.verifyEqual(on( + fDisc), on(+f));
+testCase.verifyEqual(on( - fDisc), on(-f));
+
+
 end
 
 function testConstantValues(testCase)
 %%
 syms z
-q = quantity.Symbolic(magic(7), 'grid', {linspace(0, 1)'}, 'variable', z);
+q = quantity.Symbolic(magic(3), 'grid', {linspace(0, 1, 4)'}, 'gridName', 'z');
 
 %%
-magic7 = magic(7);
-magic700 = zeros(100, 7, 7);
+magic7 = magic(3);
+magic700 = zeros(4, 3, 3);
 for k = 1:q.gridSize()
 	magic700(k, :,:) = magic7;
 end
@@ -643,7 +671,7 @@ end
 testCase.verifyEqual(magic700, q.on());
 testCase.verifyTrue(q.isConstant());
 
-p = quantity.Symbolic([0 0 0 0 z], 'grid', linspace(0,1)', 'variable', z);
+p = quantity.Symbolic([0 0 0 0 z], 'grid', linspace(0, 1, 4)', 'gridName', 'z');
 testCase.verifyFalse(p.isConstant());
 
 end
@@ -651,8 +679,8 @@ end
 function testIsConstant(testCase)
 %%
 syms z
-Q_constant = quantity.Symbolic(1);
-Q_var = quantity.Symbolic(z);
+Q_constant = quantity.Symbolic(1, 'domain', quantity.Domain.empty());
+Q_var = quantity.Symbolic(z, 'domain', quantity.Domain.empty());
 
 %%
 verifyTrue(testCase, Q_constant.isConstant);
@@ -662,22 +690,23 @@ end
 function testEqual(testCase)
 
 %%
-Q = quantity.Symbolic(0, 'name', 'P');
-P = quantity.Symbolic(0, 'name', 'Q');
+Q = quantity.Symbolic(0, 'name', 'P', 'domain', quantity.Domain.empty());
+P = quantity.Symbolic(0, 'name', 'Q', 'domain', quantity.Domain.empty());
 
 syms Z;
 f1 = (sin(Z .* pi) );
 f2 =  Z;
 f3 = (cos(Z .* pi) );
-z = linspace(0, 1, 42).';
+z = quantity.Domain('Z', linspace(0, 1, 4));
+
 R1 = quantity.Symbolic([f1, f2; 0, f3], ...
-	'grid', {z});
+	'domain', z);
 
 R2 = quantity.Symbolic([f2, f1; 0, f3], ...
-	'grid', {z});
+	'domain', z);
 
 R3 = quantity.Symbolic([f1, f2; 0, f3], ...
-	'grid', {z});
+	'domain', z);
 
 %%
 verifyTrue(testCase, Q == P);
@@ -690,16 +719,18 @@ end
 function testEvaluateConstant(testCase)
 %%
 syms z zeta
-Q = quantity.Symbolic(magic(7), 'grid', {1}, 'variable', z);
-Q2D = quantity.Symbolic(magic(7), 'grid', {1, 1}, 'variable', {z, zeta});
+Z = quantity.Domain('z', linspace(0,1,1)');
+Zeta = quantity.Domain('zeta', linspace(0,1,1)');
+Q = quantity.Symbolic(magic(2), 'domain', Z);
+Q2D = quantity.Symbolic(magic(2), 'domain', [Z, Zeta]);
 
 %%
-verifyEqual(testCase, shiftdim(Q.on(), 1), magic(7))
-verifyEqual(testCase, shiftdim(Q2D.on(), 2), magic(7))
+verifyEqual(testCase, shiftdim(Q.on(), 1), magic(2))
+verifyEqual(testCase, shiftdim(Q2D.on(), 2), magic(2))
 end
 
 function testSize(testCase)
-Q = quantity.Symbolic(ones(1,2,3,4));
+Q = quantity.Symbolic(ones(1,2,3,4), 'domain', quantity.Domain.empty());
 verifyEqual(testCase, size(Q), [1, 2, 3, 4]);
 end
 
@@ -709,9 +740,10 @@ syms Z;
 f1 = (sin(Z .* pi) );
 f2 =  Z;
 f3 = (cos(Z .* pi) );
-z = linspace(0, 1, 42).';
+z = linspace(0, 1, 4).';
+zDomain = quantity.Domain('Z', z);
 f = quantity.Symbolic([f1, f2; 0, f3], ...
-	'grid', {z});
+	'domain', zDomain);
 
 value = f.on();
 
@@ -725,13 +757,15 @@ end
 function testSolveAlgebraic(testCase)
 syms x
 assume(x>0 & x<1);
+
+X = quantity.Domain('x', linspace(0,1,11));
 % scalar
-fScalar = quantity.Symbolic([(1+x)^2]);
+fScalar = quantity.Symbolic([(1+x)^2], 'domain', X);
 solutionScalar = fScalar.solveAlgebraic(2, fScalar.gridName{1});
 testCase.verifyEqual(solutionScalar, sqrt(2)-1, 'AbsTol', 1e-12);
 
 % array
-f = quantity.Symbolic([2*x, 1]);
+f = quantity.Symbolic([2*x, 1], 'domain', X);
 solution = f.solveAlgebraic([1, 1], f(1).gridName{1});
 testCase.verifyEqual(solution, 0.5);
 end
@@ -739,17 +773,29 @@ end
 function testSubs(testCase)
 %%
 % init
-syms x y z bla
-f = quantity.Symbolic([x, y^2; y+x, 1]);
+syms x y z w
+X = quantity.Domain('x', linspace(0, 1, 11));
+Y = quantity.Domain('y', linspace(0, 1, 7));
+Z = quantity.Domain('z', linspace(0, 1, 5));
+W = quantity.Domain('w', linspace(0, 1, 3));
+
+f = quantity.Symbolic([x, y^2; y+x, 1], 'domain', [X Y]);
 
 % subs on variable
 Ff1 = [1, 1^2; 1+1, 1];
-Ffz = quantity.Symbolic([z, z^2; z+z, 1]);
-Fyx = quantity.Symbolic([bla, x^2; x+bla, 1]);
+Ffz = quantity.Symbolic([z, z^2; z+z, 1], 'domain', Z);
+Fyx = quantity.Symbolic([w, x^2; x+w, 1], 'domain', [X W]);
 
+% test 1: replace all domains by numerical values
 fOf1 = f.subs({'x', 'y'}, {1, 1});
-fOfz = f.subs({'x', 'y'}, {'z', 'z'});
-fOfyx = f.subs({'x', 'y'}, {'bla', 'x'});
+
+% test 2: replace one domain by a numerical value
+fOf2 = f.subs({'x'}, {1});
+
+Z0 = quantity.Domain('z', 0);
+
+fOfz = f.subs([X, Y], [Z Z]);
+fOfyx = f.subs({'x', 'y'}, {'w', 'x'});
 
 testCase.verifyEqual(Ff1, fOf1);
 testCase.verifyEqual(Ffz.sym(), fOfz.sym());
@@ -758,20 +804,20 @@ testCase.verifyEqual(Fyx.sym(), fOfyx.sym());
 %%
 syms z zeta
 assume(z>0 & z<1); assume(zeta>0 & zeta<1);
-F = quantity.Symbolic(zeros(1), 'grid', {linspace(0,1), linspace(0,1)}, 'variable', {z, zeta});
+F = quantity.Symbolic(zeros(1), 'grid', ...
+	{linspace(0, 1, 3), linspace(0, 1, 3)}, 'gridName', {'z', 'zeta'});
 Feta = F.subs('z', 'eta');
 testCase.verifyEqual(Feta(1).gridName, {'eta', 'zeta'});
 
 %%
-fMessy = quantity.Symbolic(x^1*y^2*z^4*bla^5, 'variables', {x, y, z, bla}, ...
-	'grid', {linspace(0, 1, 5), linspace(0, 1, 11), linspace(0, 1, 15), linspace(0, 1, 21)});
-fMessyA = fMessy.subs({'x', 'y', 'z', 'bla'}, {'a', 'a', 'a', 'a'});
-fMessyYY = fMessy.subs({'bla', 'x', 'z'}, {'bli', 'y', 'y'});
+fMessy = quantity.Symbolic(x^1*y^2*z^4*w^5, 'domain', [X, Y, Z, W]);
+fMessyA = fMessy.subs({'x', 'y', 'z', 'w'}, {'a', 'a', 'a', 'a'});
+fMessyYY = fMessy.subs({'w', 'x', 'z'}, {'xi', 'y', 'y'});
 
 testCase.verifyEqual(fMessyA.gridName, {'a'});
-testCase.verifyEqual(fMessyA.grid, {linspace(0, 1, 21)});
-testCase.verifyEqual(fMessyYY.gridName, {'y', 'bli'});
-testCase.verifyEqual(fMessyYY.grid, {linspace(0, 1, 15), linspace(0, 1, 21)});
+testCase.verifyEqual(fMessyA.grid, {linspace(0, 1, 11)'});
+testCase.verifyEqual(fMessyYY.gridName, {'y', 'xi'});
+testCase.verifyEqual(fMessyYY.grid, {linspace(0, 1, 11)', linspace(0, 1, 3)'});
 % % sub multiple numerics -> not implemented yet
 % f11 = f.subs('x', [1; 2]);
 % f1 = f.subs('x', 1);
diff --git a/+unittests/+quantity/testTimeDelayOperator.m b/+unittests/+quantity/testTimeDelayOperator.m
new file mode 100644
index 0000000000000000000000000000000000000000..0e6dcf9e8ad745aa6b16b1401472ea254ac16a1f
--- /dev/null
+++ b/+unittests/+quantity/testTimeDelayOperator.m
@@ -0,0 +1,117 @@
+function [tests] = testTimeDelayOperator()
+%TESTGEVREY Summary of this function goes here
+%   Detailed explanation goes here
+tests = functiontests(localfunctions);
+end
+
+function setupOnce(testCase)
+
+z = quantity.Domain('z', linspace(0,1));
+zeta = quantity.Domain('zeta', linspace(0,1));
+t = quantity.Domain('t', linspace(0, 2, 31));
+v = quantity.Discrete( z.grid, 'domain', z );
+c_zeta = quantity.Discrete( zeta.grid * 0, 'domain', zeta );
+c = quantity.Discrete( 1, 'domain', quantity.Domain.empty);
+
+testCase.TestData.z = z;
+testCase.TestData.t = t;
+testCase.TestData.zeta = zeta;
+testCase.TestData.coefficient.fun_zeta = c_zeta;
+testCase.TestData.coefficient.variable = v;
+testCase.TestData.coefficient.constant = c;
+	
+testCase.TestData.delay.variable = quantity.TimeDelayOperator(v, t);
+testCase.TestData.delay.constant = quantity.TimeDelayOperator(c, t);
+testCase.TestData.delay.zero = quantity.TimeDelayOperator.zero(t);
+testCase.TestData.delay.spatialDomain2 = quantity.TimeDelayOperator( ...
+	v - c_zeta, t);
+
+testCase.TestData.delay.diagonal = diag( ...
+	[testCase.TestData.delay.constant, ...
+	 testCase.TestData.delay.variable]);
+end
+
+function testInit(testCase)
+
+td = testCase.TestData;
+
+testCase.verifyEqual(td.delay.constant.coefficient, ...
+	td.coefficient.constant);
+testCase.verifyEqual( td.delay.variable.coefficient, td.coefficient.variable);
+	
+end
+
+
+function testDiag(testCase)
+
+	D = testCase.TestData.delay.diagonal;
+	v = testCase.TestData.delay.variable;
+	c = testCase.TestData.delay.constant;
+	o = testCase.TestData.delay.zero;
+
+	testCase.verifyEqual( D(1), c );
+	testCase.verifyEqual( D(2), o );
+	testCase.verifyEqual( D(3), o );
+	testCase.verifyEqual( D(4), v );
+	
+end
+
+function testApplyTo(testCase)
+
+	z = testCase.TestData.z;
+	t = testCase.TestData.t;
+	tau = quantity.Domain('tau', linspace(-1, 3, 201));
+	v = testCase.TestData.delay.variable;
+	
+	h = quantity.Discrete( sin(tau.grid * 2 * pi), 'domain', tau );
+	H = quantity.Discrete( sin(z.grid * 2 * pi + t.grid' * 2 * pi), ...
+		'domain', [z, t]);
+	H_ = v.applyTo(h);
+	
+	%% test a scalar operator
+	testCase.verifyEqual(H.on(), H_.on(), 'AbsTol', 5e-3);
+	
+	d2 = testCase.TestData.delay.spatialDomain2;
+	H2 = d2.applyTo(h);
+	H2 = subs(H2, 'zeta', 0);
+	testCase.verifyEqual(H.on(), H2.on(), 'AbsTol', 5e-3);
+	
+	%% test a diagonal operator
+	h = quantity.Discrete( {sin(tau.grid * 2 * pi); ...
+		sin(tau.grid * 2 * pi)}, 'domain', tau );
+	H = quantity.Discrete( {sin(z.grid * 0 + 2 * pi + t.grid' * 2 * pi); ...
+		sin(z.grid * 2 * pi + t.grid' * 2 * pi)}, 'domain', [z, t]);
+	D = testCase.TestData.delay.diagonal;
+	H_ = D.applyTo(h);
+	
+	testCase.verifyEqual( H.on(), H_.on(), 'AbsTol', 5e-3)
+	
+	%% test application of the operator to a function h(z,t)
+	% h(z,tau) = z + sin( tau * 2 * pi)
+	%	D[h] = z + sin( ( t + g(z) ) * 2* pi )
+	%		g(z) = z
+	%	D[h] = z + sin( ( t + z ) * 2 * pi )
+	
+	h = quantity.Discrete( z.grid + sin(tau.grid' * 2 * pi), ...
+		'domain', [z, tau]);
+	
+	Dh = quantity.Discrete(	z.grid + sin( ( z.grid + t.grid') * 2 * pi ), ...
+		'domain', [z, t]);
+	
+	D = testCase.TestData.delay.variable;
+	
+	Dh2 = D.applyTo( h, 'domain', tau );
+
+	testCase.verifyEqual( Dh.on, Dh2.on, 'AbsTol', 2e-3)
+	
+end
+
+
+
+
+
+
+
+
+
+