changeset 920:386ef449df51 feature/d1_staggered

Merge with default.
author Martin Almquist <malmquist@stanford.edu>
date Wed, 28 Nov 2018 17:35:19 -0800
parents c70131daaa6e (current diff) 306f5b3cd7bc (diff)
children 9c74d96fc9e2
files +multiblock/DiffOp.m +scheme/Elastic2dVariable.m +scheme/Wave.m +scheme/bcSetup.m .hgtags
diffstat 21 files changed, 469 insertions(+), 301 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/+grid/Nodes.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,47 @@
+classdef Nodes < grid.Grid
+    properties
+        coords
+    end
+
+    methods
+        % Creates a grid with one point for each row in coords.
+        % The dimension equals the number of columns in coords.
+        function obj = Nodes(coords)
+            obj.coords = coords;
+        end
+
+        function o = N(obj)
+            o = size(obj.coords, 1);
+        end
+
+        % d returns the spatial dimension of the grid
+        function o = D(obj)
+            o = size(obj.coords, 2);
+        end
+
+        % points returns a n x d matrix containing the coordinates for all points.
+        function X = points(obj)
+            X = obj.coords;
+        end
+
+        % Restricts the grid function gf on obj to the subgrid g.
+        function gf = restrictFunc(obj, gf, g)
+            error('Not implemented');
+        end
+
+        % Projects the grid function gf on obj to the grid g.
+        function gf = projectFunc(obj, gf, g)
+            error('Not implemented');
+        end
+
+        % Return the grid.boundaryIdentifiers of all boundaries in a cell array.
+        function bs = getBoundaryNames(obj)
+            error('Not implemented');
+        end
+
+        % Return coordinates for the given boundary
+        function b = getBoundary(obj, name)
+            error('Not implemented');
+        end
+    end
+end
--- a/+multiblock/+domain/Circle.m	Wed Nov 21 18:29:29 2018 -0800
+++ b/+multiblock/+domain/Circle.m	Wed Nov 28 17:35:19 2018 -0800
@@ -65,10 +65,10 @@
             conn{5,2} = {'n','s'};
 
             boundaryGroups = struct();
-            boundaryGroups.E = multiblock.BoundaryGroup({2,'e'});
-            boundaryGroups.N = multiblock.BoundaryGroup({3,'n'});
-            boundaryGroups.W = multiblock.BoundaryGroup({4,'n'});
-            boundaryGroups.S = multiblock.BoundaryGroup({5,'e'});
+            boundaryGroups.E = multiblock.BoundaryGroup({{2,'e'}});
+            boundaryGroups.N = multiblock.BoundaryGroup({{3,'n'}});
+            boundaryGroups.W = multiblock.BoundaryGroup({{4,'n'}});
+            boundaryGroups.S = multiblock.BoundaryGroup({{5,'e'}});
             boundaryGroups.all = multiblock.BoundaryGroup({{2,'e'},{3,'n'},{4,'n'},{5,'e'}});
 
             obj = obj@multiblock.DefCurvilinear(blocks, conn, boundaryGroups, blocksNames);
--- a/+multiblock/DiffOp.m	Wed Nov 21 18:29:29 2018 -0800
+++ b/+multiblock/DiffOp.m	Wed Nov 28 17:35:19 2018 -0800
@@ -223,33 +223,8 @@
             [blockClosure, blockPenalty] = obj.diffOps{I}.boundary_condition(name, type);
 
             % Expand to matrix for full domain.
-            div = obj.blockmatrixDiv;
-            if ~iscell(blockClosure)
-                temp = blockmatrix.zero(div);
-                temp{I,I} = blockClosure;
-                closure = blockmatrix.toMatrix(temp);
-            else
-                for i = 1:length(blockClosure)
-                    temp = blockmatrix.zero(div);
-                    temp{I,I} = blockClosure{i};
-                    closure{i} = blockmatrix.toMatrix(temp);
-                end
-            end
-
-            if ~iscell(blockPenalty)
-                div{2} = size(blockPenalty, 2); % Penalty is a column vector
-                p = blockmatrix.zero(div);
-                p{I} = blockPenalty;
-                penalty = blockmatrix.toMatrix(p);
-            else
-                % TODO: used by beam equation, should be eliminated. SHould only set one BC per call
-                for i = 1:length(blockPenalty)
-                    div{2} = size(blockPenalty{i}, 2); % Penalty is a column vector
-                    p = blockmatrix.zero(div);
-                    p{I} = blockPenalty{i};
-                    penalty{i} = blockmatrix.toMatrix(p);
-                end
-            end
+            closure = multiblock.local2globalClosure(blockClosure, obj.blockmatrixDiv, I);
+            penalty = multiblock.local2globalPenalty(blockPenalty, obj.blockmatrixDiv, I);
         end
 
         function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/+multiblock/local2globalClosure.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,10 @@
+% Takes the block-local closures and turns it into a global closure
+%   local -- The local closure
+%   div   -- block matrix division for the diffOp
+%   I     -- Index of blockmatrix block
+function closure = local2globalClosure(local, div, I)
+    closure_bm = blockmatrix.zero(div);
+    closure_bm{I,I} = local;
+
+    closure = blockmatrix.toMatrix(closure_bm);
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/+multiblock/local2globalPenalty.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,11 @@
+% Takes the block-local penalty and turns it into a global penalty
+%   local -- The local penalty
+%   div   -- block matrix division for the diffOp
+%   I     -- Index of blockmatrix block
+function penalty = local2globalPenalty(local, div, I)
+    penaltyDiv = {div{1}, size(local,2)};
+    penalty_bm = blockmatrix.zero(penaltyDiv);
+    penalty_bm{I,1} = local;
+
+    penalty = blockmatrix.toMatrix(penalty_bm);
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/+scheme/+bc/closureSetup.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,25 @@
+% Setup closure and penalty matrices for several boundary conditions at once.
+% Each bc is a struct with the fields
+%  * type     -- Type of boundary condition
+%  * boundary -- Boundary identifier
+%  * data     -- A function_handle for a function which provides boundary data.(see below)
+% Also takes S_sign which modifies the sign of the penalty function, [-1,1]
+% Returns a closure matrix and a penalty matrices for each boundary condition.
+%
+% The boundary data function can either be a function of time or a function of time and space coordinates.
+% In the case where it only depends on time it should return the data as grid function for the boundary.
+% In the case where it also takes space coordinates the number of space coordinates should match the number of dimensions of the problem domain.
+% For example in the 2D case: f(t,x,y).
+function [closure, penalties] = closureSetup(diffOp, bcs)
+    scheme.bc.verifyFormat(bcs, diffOp);
+
+    % Setup storage arrays
+    closure = spzeros(size(diffOp));
+    penalties = cell(1, length(bcs));
+
+    % Collect closures and penalties
+    for i = 1:length(bcs)
+        [localClosure, penalties{i}] = diffOp.boundary_condition(bcs{i}.boundary, bcs{i}.type);
+        closure = closure + localClosure;
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/+scheme/+bc/forcingSetup.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,86 @@
+% Setup the forcing function for the given boundary conditions and data.
+% Each bc is a struct with the fields
+%  * type     -- Type of boundary condition
+%  * boundary -- Boundary identifier
+%  * data     -- A function_handle for a function which provides boundary data.(see below)
+% S_sign allows changing the sign of the function to put on different sides in the system of ODEs.
+%   default is 1, which the same side as the diffOp.
+% Returns a forcing function S.
+%
+% The boundary data function can either be a function of time or a function of time and space coordinates.
+% In the case where it only depends on time it should return the data as grid function for the boundary.
+% In the case where it also takes space coordinates the number of space coordinates should match the number of dimensions of the problem domain.
+% For example in the 2D case: f(t,x,y).
+
+function S = forcingSetup(diffOp, penalties, bcs, S_sign)
+    default_arg('S_sign', 1);
+
+    assertType(bcs, 'cell');
+    assertIsMember(S_sign, [1, -1]);
+
+    scheme.bc.verifyFormat(bcs, diffOp);
+
+    [gridData, symbolicData] = parseAndSortData(bcs, penalties, diffOp);
+
+    % Setup penalty function
+    O = spzeros(size(diffOp),1);
+    function v = S_fun(t)
+        v = O;
+        for i = 1:length(gridData)
+            v = v + gridData{i}.penalty*gridData{i}.func(t);
+        end
+
+        for i = 1:length(symbolicData)
+            v = v + symbolicData{i}.penalty*symbolicData{i}.func(t, symbolicData{i}.coords{:});
+        end
+
+        v = S_sign * v;
+    end
+    S = @S_fun;
+end
+
+% Go through a cell array of boundary condition specifications and return cell arrays
+% of structs for grid and symbolic data.
+function [gridData, symbolicData] = parseAndSortData(bcs, penalties, diffOp)
+    gridData = {};
+    symbolicData = {};
+    for i = 1:length(bcs)
+        [ok, isSymbolic, data] = parseData(bcs{i}, penalties{i}, diffOp.grid);
+
+        if ~ok
+            continue % There was no data
+        end
+
+        if isSymbolic
+            symbolicData{end+1} = data;
+        else
+            gridData{end+1} = data;
+        end
+    end
+end
+
+function [ok, isSymbolic, dataStruct] = parseData(bc, penalty, grid)
+    if ~isfield(bc,'data') || isempty(bc.data)
+        isSymbolic = [];
+        dataStruct = struct();
+        ok = false;
+        return
+    end
+    ok = true;
+
+    nArg = nargin(bc.data);
+
+    if nArg > 1
+        % Symbolic data
+        isSymbolic = true;
+        coord = grid.getBoundary(bc.boundary);
+        dataStruct.penalty = penalty;
+        dataStruct.func = bc.data;
+        dataStruct.coords = num2cell(coord, 1);
+    else
+        % Grid data
+        isSymbolic = false;
+        dataStruct.penalty = penalty;
+        dataStruct.func = bc.data;
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/+scheme/+bc/verifyFormat.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,31 @@
+% Errors with a more or less detailed error message if there is a problem with the bc specification
+function verifyBcFormat(bcs, diffOp)
+    assertType(bcs, 'cell');
+    for i = 1:length(bcs)
+        assertType(bcs{i}, 'struct');
+        assertStructFields(bcs{i}, {'type', 'boundary'});
+
+        if ~isfield(bcs{i}, 'data') || isempty(bcs{i}.data)
+            continue
+        end
+
+        if ~isa(bcs{i}.data, 'function_handle')
+            error('bcs{%d}.data should be a function of time or a function of time and space',i);
+        end
+
+        % Find dimension of boundary
+        b = diffOp.grid.getBoundary(bcs{i}.boundary);
+        dim = size(b,2);
+
+        % Assert that the data function has a valid number of input arguments
+        if ~(nargin(bcs{i}.data) == 1 || nargin(bcs{i}.data) == 1 + dim)
+            error('sbplib:scheme:bcSetup:DataWrongNumberOfArguments', 'bcs{%d}.data has the wrong number of input arguments. Must be either only time or time and space.', i);
+        end
+
+        if nargin(bcs{i}.data) == 1
+            % Grid data (only function of time)
+            % Assert that the data has the correct dimension
+            assertSize(bcs{i}.data(0), 1, size(b,1));
+        end
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/+scheme/Laplace1D.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,146 @@
+classdef Laplace1D < scheme.Scheme
+    properties
+        grid
+        order % Order accuracy for the approximation
+
+        D % non-stabalized scheme operator
+        H % Discrete norm
+        M % Derivative norm
+        a
+
+        D2
+        Hi
+        e_l
+        e_r
+        d_l
+        d_r
+        gamm
+    end
+
+    methods
+        function obj = Laplace1D(grid, order, a)
+            default_arg('a', 1);
+
+            assertType(grid, 'grid.Cartesian');
+
+            ops = sbp.D2Standard(grid.size(), grid.lim{1}, order);
+
+            obj.D2 = sparse(ops.D2);
+            obj.H =  sparse(ops.H);
+            obj.Hi = sparse(ops.HI);
+            obj.M =  sparse(ops.M);
+            obj.e_l = sparse(ops.e_l);
+            obj.e_r = sparse(ops.e_r);
+            obj.d_l = -sparse(ops.d1_l);
+            obj.d_r = sparse(ops.d1_r);
+
+
+            obj.grid = grid;
+            obj.order = order;
+
+            obj.a = a;
+            obj.D = a*obj.D2;
+
+            obj.gamm = grid.h*ops.borrowing.M.S;
+        end
+
+
+        % Closure functions return the opertors applied to the own doamin to close the boundary
+        % Penalty functions return the opertors to force the solution. In the case of an interface it returns the operator applied to the other doamin.
+        %       boundary            is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'.
+        %       type                is a string specifying the type of boundary condition if there are several.
+        %       data                is a function returning the data that should be applied at the boundary.
+        %       neighbour_scheme    is an instance of Scheme that should be interfaced to.
+        %       neighbour_boundary  is a string specifying which boundary to interface to.
+        function [closure, penalty] = boundary_condition(obj,boundary,type,data)
+            default_arg('type','neumann');
+            default_arg('data',0);
+
+            [e,d,s] = obj.get_boundary_ops(boundary);
+
+            switch type
+                % Dirichlet boundary condition
+                case {'D','dirichlet'}
+                    tuning = 1.1;
+                    tau1 = -tuning/obj.gamm;
+                    tau2 =  1;
+
+                    tau = tau1*e + tau2*d;
+
+                    closure = obj.a*obj.Hi*tau*e';
+                    penalty = obj.a*obj.Hi*tau;
+
+                % Neumann boundary condition
+                case {'N','neumann'}
+                    tau = -e;
+
+                    closure = obj.a*obj.Hi*tau*d';
+                    penalty = -obj.a*obj.Hi*tau;
+
+                % Unknown, boundary condition
+                otherwise
+                    error('No such boundary condition: type = %s',type);
+            end
+        end
+
+        function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary)
+            % u denotes the solution in the own domain
+            % v denotes the solution in the neighbour domain
+
+            [e_u,d_u,s_u] = obj.get_boundary_ops(boundary);
+            [e_v,d_v,s_v] = neighbour_scheme.get_boundary_ops(neighbour_boundary);
+
+
+            a_u = obj.a;
+            a_v = neighbour_scheme.a;
+
+            gamm_u = obj.gamm;
+            gamm_v = neighbour_scheme.gamm;
+
+            tuning = 1.1;
+            
+            tau1 = -(a_u/gamm_u + a_v/gamm_v) * tuning;
+            tau2 = 1/2*a_u;
+            sig1 = -1/2;
+            sig2 = 0;
+
+            tau = tau1*e_u + tau2*d_u;
+            sig = sig1*e_u + sig2*d_u;
+
+            closure = obj.Hi*( tau*e_u' + sig*a_u*d_u');
+            penalty = obj.Hi*(-tau*e_v' + sig*a_v*d_v');
+        end
+
+        % Ruturns the boundary ops and sign for the boundary specified by the string boundary.
+        % The right boundary is considered the positive boundary
+        function [e,d,s] = get_boundary_ops(obj,boundary)
+            switch boundary
+                case 'l'
+                    e = obj.e_l;
+                    d = obj.d_l;
+                    s = -1;
+                case 'r'
+                    e = obj.e_r;
+                    d = obj.d_r;
+                    s = 1;
+                otherwise
+                    error('No such boundary: boundary = %s',boundary);
+            end
+        end
+
+        function N = size(obj)
+            N = obj.grid.size();
+        end
+
+    end
+
+    methods(Static)
+        % Calculates the matrcis need for the inteface coupling between boundary bound_u of scheme schm_u
+        % and bound_v of scheme schm_v.
+        %   [uu, uv, vv, vu] = inteface_couplong(A,'r',B,'l')
+        function [uu, uv, vv, vu] = interface_coupling(schm_u,bound_u,schm_v,bound_v)
+            [uu,uv] = schm_u.interface(bound_u,schm_v,bound_v);
+            [vv,vu] = schm_v.interface(bound_v,schm_u,bound_u);
+        end
+    end
+end
\ No newline at end of file
--- a/+scheme/Wave.m	Wed Nov 21 18:29:29 2018 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-classdef Wave < scheme.Scheme
-    properties
-        m % Number of points in each direction, possibly a vector
-        h % Grid spacing
-        x % Grid
-        order % Order accuracy for the approximation
-
-        D % non-stabalized scheme operator
-        H % Discrete norm
-        M % Derivative norm
-        alpha
-
-        D2
-        Hi
-        e_l
-        e_r
-        d1_l
-        d1_r
-        gamm
-    end
-
-    methods
-        function obj = Wave(m,xlim,order,alpha)
-            default_arg('a',1);
-            [x, h] = util.get_grid(xlim{:},m);
-
-            ops = sbp.Ordinary(m,h,order);
-
-            obj.D2 = sparse(ops.derivatives.D2);
-            obj.H =  sparse(ops.norms.H);
-            obj.Hi = sparse(ops.norms.HI);
-            obj.M =  sparse(ops.norms.M);
-            obj.e_l = sparse(ops.boundary.e_1);
-            obj.e_r = sparse(ops.boundary.e_m);
-            obj.d1_l = sparse(ops.boundary.S_1);
-            obj.d1_r = sparse(ops.boundary.S_m);
-
-
-            obj.m = m;
-            obj.h = h;
-            obj.order = order;
-
-            obj.alpha = alpha;
-            obj.D = alpha*obj.D2;
-            obj.x = x;
-
-            obj.gamm = h*ops.borrowing.M.S;
-
-        end
-
-
-        % Closure functions return the opertors applied to the own doamin to close the boundary
-        % Penalty functions return the opertors to force the solution. In the case of an interface it returns the operator applied to the other doamin.
-        %       boundary            is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'.
-        %       type                is a string specifying the type of boundary condition if there are several.
-        %       data                is a function returning the data that should be applied at the boundary.
-        %       neighbour_scheme    is an instance of Scheme that should be interfaced to.
-        %       neighbour_boundary  is a string specifying which boundary to interface to.
-        function [closure, penalty] = boundary_condition(obj,boundary,type,data)
-            default_arg('type','neumann');
-            default_arg('data',0);
-
-            [e,d,s] = obj.get_boundary_ops(boundary);
-
-            switch type
-                % Dirichlet boundary condition
-                case {'D','dirichlet'}
-                    alpha = obj.alpha;
-
-                    % tau1 < -alpha^2/gamma
-                    tuning = 1.1;
-                    tau1 = -tuning*alpha/obj.gamm;
-                    tau2 =  s*alpha;
-
-                    p = tau1*e + tau2*d;
-
-                    closure = obj.Hi*p*e';
-
-                    pp = obj.Hi*p;
-                    switch class(data)
-                        case 'double'
-                            penalty = pp*data;
-                        case 'function_handle'
-                            penalty = @(t)pp*data(t);
-                        otherwise
-                            error('Wierd data argument!')
-                    end
-
-
-                % Neumann boundary condition
-                case {'N','neumann'}
-                    alpha = obj.alpha;
-                    tau1 = -s*alpha;
-                    tau2 = 0;
-                    tau = tau1*e + tau2*d;
-
-                    closure = obj.Hi*tau*d';
-
-                    pp = obj.Hi*tau;
-                    switch class(data)
-                        case 'double'
-                            penalty = pp*data;
-                        case 'function_handle'
-                            penalty = @(t)pp*data(t);
-                        otherwise
-                            error('Wierd data argument!')
-                    end
-
-                % Unknown, boundary condition
-                otherwise
-                    error('No such boundary condition: type = %s',type);
-            end
-        end
-
-        function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary)
-            % u denotes the solution in the own domain
-            % v denotes the solution in the neighbour domain
-            [e_u,d_u,s_u] = obj.get_boundary_ops(boundary);
-            [e_v,d_v,s_v] = neighbour_scheme.get_boundary_ops(neighbour_boundary);
-
-            tuning = 1.1;
-
-            alpha_u = obj.alpha;
-            alpha_v = neighbour_scheme.alpha;
-
-            gamm_u = obj.gamm;
-            gamm_v = neighbour_scheme.gamm;
-
-            % tau1 < -(alpha_u/gamm_u + alpha_v/gamm_v)
-
-            tau1 = -(alpha_u/gamm_u + alpha_v/gamm_v) * tuning;
-            tau2 = s_u*1/2*alpha_u;
-            sig1 = s_u*(-1/2);
-            sig2 = 0;
-
-            tau = tau1*e_u + tau2*d_u;
-            sig = sig1*e_u + sig2*d_u;
-
-            closure = obj.Hi*( tau*e_u' + sig*alpha_u*d_u');
-            penalty = obj.Hi*(-tau*e_v' - sig*alpha_v*d_v');
-        end
-
-        % Ruturns the boundary ops and sign for the boundary specified by the string boundary.
-        % The right boundary is considered the positive boundary
-        function [e,d,s] = get_boundary_ops(obj,boundary)
-            switch boundary
-                case 'l'
-                    e = obj.e_l;
-                    d = obj.d1_l;
-                    s = -1;
-                case 'r'
-                    e = obj.e_r;
-                    d = obj.d1_r;
-                    s = 1;
-                otherwise
-                    error('No such boundary: boundary = %s',boundary);
-            end
-        end
-
-        function N = size(obj)
-            N = obj.m;
-        end
-
-    end
-
-    methods(Static)
-        % Calculates the matrcis need for the inteface coupling between boundary bound_u of scheme schm_u
-        % and bound_v of scheme schm_v.
-        %   [uu, uv, vv, vu] = inteface_couplong(A,'r',B,'l')
-        function [uu, uv, vv, vu] = interface_coupling(schm_u,bound_u,schm_v,bound_v)
-            [uu,uv] = schm_u.interface(bound_u,schm_v,bound_v);
-            [vv,vu] = schm_v.interface(bound_v,schm_u,bound_u);
-        end
-    end
-end
\ No newline at end of file
--- a/+scheme/bcSetup.m	Wed Nov 21 18:29:29 2018 -0800
+++ b/+scheme/bcSetup.m	Wed Nov 28 17:35:19 2018 -0800
@@ -1,10 +1,9 @@
-% function [closure, S] = bcSetup(diffOp, bc)
 % Takes a diffOp and a cell array of boundary condition definitions.
 % Each bc is a struct with the fields
 %  * type     -- Type of boundary condition
 %  * boundary -- Boundary identifier
 %  * data     -- A function_handle for a function which provides boundary data.(see below)
-% Also takes S_sign which modifies the sign of S, [-1,1]
+% Also takes S_sign which modifies the sign of the penalty function, [-1,1]
 % Returns a closure matrix and a forcing function S.
 %
 % The boundary data function can either be a function of time or a function of time and space coordinates.
@@ -16,97 +15,6 @@
     assertType(bcs, 'cell');
     assert(S_sign == 1 || S_sign == -1, 'S_sign must be either 1 or -1');
 
-    verifyBcFormat(bcs, diffOp);
-
-    % Setup storage arrays
-    closure = spzeros(size(diffOp));
-    gridData = {};
-    symbolicData = {};
-
-    % Collect closures, penalties and data
-    for i = 1:length(bcs)
-        [localClosure, penalty] = diffOp.boundary_condition(bcs{i}.boundary, bcs{i}.type);
-        closure = closure + localClosure;
-
-        [ok, isSymbolic, data] = parseData(bcs{i}, penalty, diffOp.grid);
-
-        if ~ok
-            % There was no data
-            continue
-        end
-
-        if isSymbolic
-            symbolicData{end+1} = data;
-        else
-            gridData{end+1} = data;
-        end
-    end
-
-    % Setup penalty function
-    O = spzeros(size(diffOp),1);
-    function v = S_fun(t)
-        v = O;
-        for i = 1:length(gridData)
-            v = v + gridData{i}.penalty*gridData{i}.func(t);
-        end
-
-        for i = 1:length(symbolicData)
-            v = v + symbolicData{i}.penalty*symbolicData{i}.func(t, symbolicData{i}.coords{:});
-        end
-
-        v = S_sign * v;
-    end
-    S = @S_fun;
+    [closure, penalties] = scheme.bc.closureSetup(diffOp, bcs);
+    S = scheme.bc.forcingSetup(diffOp, penalties, bcs, S_sign);
 end
-
-function verifyBcFormat(bcs, diffOp)
-    for i = 1:length(bcs)
-        assertType(bcs{i}, 'struct');
-        assertStructFields(bcs{i}, {'type', 'boundary'});
-
-        if ~isfield(bcs{i}, 'data') || isempty(bcs{i}.data)
-            continue
-        end
-
-        if ~isa(bcs{i}.data, 'function_handle')
-            error('bcs{%d}.data should be a function of time or a function of time and space',i);
-        end
-
-        b = diffOp.grid.getBoundary(bcs{i}.boundary);
-
-        dim = size(b,2);
-
-        if nargin(bcs{i}.data) == 1
-            % Grid data (only function of time)
-            assertSize(bcs{i}.data(0), 1, size(b));
-        elseif nargin(bcs{i}.data) ~= 1+dim
-           error('sbplib:scheme:bcSetup:DataWrongNumberOfArguments', 'bcs{%d}.data has the wrong number of input arguments. Must be either only time or time and space.', i);
-        end
-    end
-end
-
-function [ok, isSymbolic, dataStruct] = parseData(bc, penalty, grid)
-    if ~isfield(bc,'data') || isempty(bc.data)
-        isSymbolic = [];
-        dataStruct = struct();
-        ok = false;
-        return
-    end
-    ok = true;
-
-    nArg = nargin(bc.data);
-
-    if nArg > 1
-        % Symbolic data
-        isSymbolic = true;
-        coord = grid.getBoundary(bc.boundary);
-        dataStruct.penalty = penalty;
-        dataStruct.func = bc.data;
-        dataStruct.coords = num2cell(coord, 1);
-    else
-        % Grid data
-        isSymbolic = false;
-        dataStruct.penalty = penalty;
-        dataStruct.func = bcs{i}.data;
-    end
-end
--- a/+util/ReplaceableString.m	Wed Nov 21 18:29:29 2018 -0800
+++ b/+util/ReplaceableString.m	Wed Nov 28 17:35:19 2018 -0800
@@ -58,3 +58,5 @@
 function b = padStr(a, n)
     b = sprintf('%-*s', n, a);
 end
+
+% TODO: Add a debug mode which prints without replacing?
--- a/.hgtags	Wed Nov 21 18:29:29 2018 -0800
+++ b/.hgtags	Wed Nov 28 17:35:19 2018 -0800
@@ -1,4 +1,5 @@
 18c023aaf3f79cbe2b9b1cf547d80babdaa1637d v0.1
 0776fa4754ff0c1918f6e1278c66f48c62d05736 grids0.1
+b723495cdb2f96314d7b3f0aa79723a7dc088c7d v0.2
 08f3ffe63f484d02abce8df4df61e826f568193f elastic1.0
 08f3ffe63f484d02abce8df4df61e826f568193f Heimisson2018
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE.txt	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,25 @@
+MIT License
+
+Copyright (c)
+2015-2018 Jonatan Werpers
+2015-2018 Martin Almquist
+2016-2018 Ylva Rydin
+2018 Vidar Stiernström
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,2 @@
+# SBPLIB
+sbplib is a library of primitives and help functions for working with summation-by-parts finite differences in Matlab. To use sbplib download the code and add the sbplib folder to the matlab path.
--- a/TextTable.m	Wed Nov 21 18:29:29 2018 -0800
+++ b/TextTable.m	Wed Nov 28 17:35:19 2018 -0800
@@ -41,6 +41,14 @@
             obj.fmtArray{i,j} = fmt;
         end
 
+        function formatGrid(obj, I, J, fmt)
+            for i = I
+                for j = J
+                    obj.fmtArray{i,j} = fmt;
+                end
+            end
+        end
+
         function formatRow(obj, i, fmt)
             obj.fmtArray(i,:) = {fmt};
         end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dealStruct.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,18 @@
+function varargout = dealStruct(s, fields)
+    default_arg('fields', []);
+
+    if isempty(fields)
+        out = dealFields(s, fieldnames(s));
+        varargout = out(1:nargout);
+    else
+        assert(nargout == length(fields), 'Number of output arguements must match the number of fieldnames provided');
+        varargout = dealFields(s, fields);
+    end
+end
+
+function out = dealFields(s, fields)
+    out = cell(1, length(fields));
+    for i = 1:length(fields)
+        out{i} = s.(fields{i});
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgRevision.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,8 @@
+% Returns the short mercurial revision Id.
+%  ok is false if there are uncommited changes.
+function [revId, ok] = hgRevision()
+    [~, s] = system('hg id -i');
+    revId = strtrim(s);
+
+    ok = s(end) ~= '+';
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/structArray.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,16 @@
+% % Usage example:
+% c = structArray({'a','b'}, {
+%     1, 2;
+%     3, 4;
+% });
+
+function c = structArray(fields, values)
+    assert(length(fields) == size(values, 2), 'Number of fields and number of colums of ''values'' must be equal');
+    c = struct();
+
+    for i = 1:size(values, 1)
+        for j = 1:length(fields)
+            c(i).(fields{j}) = values{i,j};
+        end
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/structCellArray.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,16 @@
+% % Usage example:
+% c = structCellArray({'a','b'}, {
+%     1, 2;
+%     3, 4;
+% });
+
+function c = structCellArray(fields, values)
+    assert(length(fields) == size(values, 2), 'Number of fields and number of colums of ''values'' must be equal');
+    c = cell(1, size(values, 1));
+
+    for i = 1:size(values, 1)
+        for j = 1:length(fields)
+            c{i}.(fields{j}) = values{i,j};
+        end
+    end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stuffStruct.m	Wed Nov 28 17:35:19 2018 -0800
@@ -0,0 +1,8 @@
+function s = stuffStruct(varargin)
+    s = struct();
+
+    for i = 1:nargin
+        assert(~isempty(inputname(i)), 'All inputs must be variables.');
+        s.(inputname(i)) = varargin{i};
+    end
+end