Commit 50afa926 authored by Ferdinand Fischer's avatar Ferdinand Fischer
Browse files

First version of the quantity.domain object.

parent 532c63d3
......@@ -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)
......
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
......@@ -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,122 @@ 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;
%% 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
% -> use default grid
assert(iscell(valueOriginal), 'If no grid is specified, valueOriginal must be a cell-array')
valueOriginalGridSize = size(valueOriginal{1});
myDomain = quantity.Domain.defaultGrid(valueOriginalGridSize);
elseif domainParser.isDefault('domain') && ...
domainParser.isDefault('grid')
% case 2: the gridNames are specified
% -> use default grid with specified names
assert(iscell(valueOriginal), 'If no grid is specified, valueOriginal must be a cell-array')
valueOriginalGridSize = size(valueOriginal{1});
myDomain = quantity.Domain.defaultGrid(valueOriginalGridSize, ...
misc.ensureIsCell(domainParser.Results.gridName));
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(size(myGrid), size(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('grid', myGrid{k}, ...
'name', myGridName{k});
end
else
myGridName = {myParser.Results.gridName};
% else case: the domains are specified as domain
% objects.
myDomain = domainParser.Results.domain;
end
%% allow initialization of empty objects:
% #TODO check uniqueness of gridNames
%% 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
% 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( numGridElements(myDomain) == numel(valueOriginal{1}) || ...
misc.alln( cellfun(@isempty, valueOriginal ) ), ...
'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
% 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
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}(:);
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)
gridName = {obj.domain.name};
end
function grid = get.grid(obj)
grid = {obj.domain.grid};
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.
......@@ -178,7 +208,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
end
obj.gridName = name;
end
function set.grid(obj, grid)
if ~iscell(grid)
grid = {grid};
......@@ -225,7 +255,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 +279,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);
......@@ -307,13 +337,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,15 +393,15 @@ 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})
continue;
......@@ -411,9 +441,9 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
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')
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;
......@@ -425,10 +455,10 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
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
......@@ -442,23 +472,23 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
% this is the default case for ascending alphabetical
% order
[sortedNames, I] = sort(gridNames);
% if descending: flip the order of the entries
if descend
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
for k = 1:numel(obj)
obj(k).valueDiscrete = permute(obj(k).valueDiscrete, I);
end
end
end
end% sort()
function c = horzcat(a, varargin)
......@@ -488,7 +518,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 +565,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 +595,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 +619,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 +713,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 +739,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
......@@ -732,7 +762,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
myParser.parse(varargin{:});
variableGrid = myParser.Results.variableGrid;
myGridSize = [numel(variableGrid), ...
myGridSize = [numel(variableGrid), ...
numel(myParser.Results.initialValueGrid)];
% the time (s) vector has to start at 0, to ensure the IC. If
......@@ -753,14 +783,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,7 +812,7 @@ 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)
if nargin == 1 || isempty(gridName2Replace)
% if gridName2Replace is empty, then nothing must be done.
......@@ -803,8 +833,8 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
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}).
......@@ -846,10 +876,10 @@ 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
......@@ -904,7 +934,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
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 +943,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 +971,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)
......@@ -1079,7 +1109,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 +1122,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 +1179,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 +1189,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi