diff --git a/functions/optimal_bound_2d.m b/functions/optimal_bound_2d.m
new file mode 100644
index 0000000000000000000000000000000000000000..a7e85f434012856af9e505f4fdf590749af5dab3
--- /dev/null
+++ b/functions/optimal_bound_2d.m
@@ -0,0 +1,155 @@
+function V = optimal_bound_2d(A,q11,q22,J,x1vec,x2vec,h,plot,print)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Written by: Bo Bernhardsson and Marcus Thelander Andrén, 2017
+%
+% This function numerically computes the value function V,
+% that determines the optimal reset bound. The idea is to use the
+% simularity to the diffusion-convection equation, and simulate it to
+% stationarity while at each time-step enforcing an inequality on V.
+%
+% The partial-differential-equation we simulate is:
+%
+%   (x'Qx - J) + x'A'grad(V) + 1/2laplace(V) = dV/dt
+%   "Production" "Convection"   "Diffusion"
+%
+% while at each time step we enforce the inequality:
+%
+% 0 >= V(x)   (assumes V(0) = rho)
+%
+% Without loss of generality, the system is assumed to be of the form
+% Q = diag(q11, q22) and R = I (the system state can always be transformed
+% to achieve this).
+%
+% The integration is performed numerically using the Finite Difference
+% Method. We use the implicit scheme, with a first-order upwind in time
+% approximation, and a central difference in space approximation.
+%
+% For more information, see the related article: 
+%
+%   M. Thelander Andrén, B. Bernhardsson, A. Cervin and K. Soltesz,
+%   "On Event-Based Sampling for LQG-Optimal Control" in Proc. 56th IEEE
+%   Conf. on Decision and Control, Melbourne, Australia, 2017
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%A-matrix
+a11 = A(1,1);
+a12 = A(1,2);
+a21 = A(2,1);
+a22 = A(2,2);
+
+%Gridding (NOTE: Assuming equidistant grid!)
+dx1 = abs(x1vec(2)-x1vec(1));
+dx2 = abs(x1vec(2)-x1vec(1));
+nx1 = length(x1vec);
+nx2 = length(x2vec);
+
+%Grid up relative value function V
+V = zeros(nx1,nx2);
+
+%Make grid of state space
+[X1,X2] = meshgrid(x1vec, x2vec);
+X1 = X1';
+X2 = X2'; %To get it in the right direction
+X1cut = X1(2:end-1,2:end-1);
+X2cut = X2(2:end-1,2:end-1);
+
+%"Production" term at each (i,j)-location for duration h
+prod = h*(q11*X1.^2 + q22*X2.^2 - J);
+prod = prod(2:end-1,:);
+prod = prod(:,2:end-1);
+prodvec = reshape(prod,[],1);
+
+%Dirichlet Boundary condition vector (V(x) = 0 at boundary)
+bc = zeros(nx2-2,nx1-2);
+
+% Coefficient matrices for the implicit scheme
+Ex1 = sparse(2:nx1-2,1:nx1-3,1,nx1-2,nx1-2);
+Ax1 = Ex1+Ex1'-2*speye(nx1-2);
+Ex2 = sparse(2:nx2-2,1:nx2-3,1,nx2-2,nx2-2);
+Ax2 = Ex2+Ex2' - 2*speye(nx2-2);
+Amat = kron(Ax2/dx2^2,speye(nx1-2)) + kron(speye(nx2-2),Ax1/dx1^2);
+D = speye((nx1-2)*(nx2-2))-0.5*h*Amat;
+
+diffx1 = Ex1'-Ex1;
+diffx2 = Ex2'-Ex2;
+Dx1 = kron(speye(nx2-2),diffx1/(2*dx1));
+Dx2 = kron(diffx2/(2*dx2),speye(nx1-2));
+
+fx1 = reshape(a11*X1cut + a12*X2cut,[],1);
+fx2 = reshape(a21*X1cut + a22*X2cut,[],1);
+
+%Setup simulation parameters
+maxiter = 5e4;
+rel_stop_crit = 1e-4*h;
+
+%Setup plot
+if plot
+    figure()
+    colormap('jet')
+    %Optimal bound
+    subplot(1,2,2)
+    %Suppress warning of constant Z-data
+    warning('off','MATLAB:contour:ConstantData')
+    [~, h1] =contour(X1,X2,V,[0 0],'b','ZDataSource','V');
+    axis([x1vec(1) x1vec(end) x2vec(1) x2vec(end)])
+    xlabel('x_1')
+    ylabel('x_2')
+    title('Optimal Reset Bound')
+    grid on
+    axis equal
+    
+    %Surface plot of V
+    subplot(1,2,1)
+    h2 = surf(X1,X2,V,'EdgeColor','none','ZDataSource','V');
+    shading interp
+    xlabel('x_1')
+    ylabel('x_2')
+    zlabel('V')
+    title('Value Function')
+    axis tight
+end
+
+%Main loop
+for it=1:maxiter
+    Vn = V;
+    
+    %Time-step
+    v = Vn;
+    v = v(2:end-1,2:end-1);
+    v = reshape(v+bc,[],1);
+    v = D\(v + prodvec + h*fx1.*(Dx1*v) + h*fx2.*(Dx2*v));
+    v = reshape(v,nx2 - 2, nx1 - 2);
+    V(2:nx2-1,2:nx1-1) = v;
+    
+    %Enforce boundary conditions and V <= 0
+    V(1,:) = 0;
+    V(nx2,:) = 0;
+    V(:,1) = 0;
+    V(:,nx1) = 0;
+    V = min(V,0);
+    
+    %Check for steady-state
+    Vdiff = abs(V - Vn);
+    maxVdiff = max(max(Vdiff));
+    if maxVdiff < rel_stop_crit
+         disp('Stopping criteria met')
+         return
+    end
+    
+    %Print and plot
+    if mod(it,10) == 0
+        if print
+             disp(['Iterations : ' num2str(it),' out of ',num2str(maxiter), ' | Maximum Vdiff : ', num2str(maxVdiff),' V(0) = ', num2str(min(min(V)))])
+        end
+        if plot
+            refreshdata(h1, 'caller')
+            refreshdata(h2, 'caller')    
+            drawnow
+        end
+    end
+end
+disp('Maximum number of iterations reached')
+end
diff --git a/optimal_bound_2d.m b/optimal_bound_2d.m
deleted file mode 100644
index 785956d31f141fe3df973e3b7ce6e9f7480c20cb..0000000000000000000000000000000000000000
--- a/optimal_bound_2d.m
+++ /dev/null
@@ -1,184 +0,0 @@
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%Authors: Bo Bernhardsson and Marcus Thelander Andrén, 2017.
-%
-%Script for numerically finding the value function V,
-%which determines the optimal trigger bound. The idea is to use the
-%simularity to the diffusion-convection equation, and simulate it to
-%stationarity while at each time-step enforcing an inequality on V.
-
-%The PDE we simulate is:
-%
-%   (x'Qx - J) + x'A'grad(V) + 1/2laplace(V) = dV/dt
-%   "Production" "Convection"   "Diffusion"
-%
-%while at each time step we enforce the inequality:
-%
-% 0 >= V(x,t)   (assumes V(0,t) = rho)
-
-%Here we assume that the state cost matrix Q=diag(...) and the noise 
-%intensity matrix  R=I.
-%Through linear transformations, we can always transform the system to this
-%setting, i.e., there is no loss of generality.
-
-%The integration is performed numerically using the Backward Time Central 
-%Space (BTCS) Finite Difference method.
-%
-%
-%The grid in space is defined as:
-%
-%
-%
-%     x2|^            *(i,j+1)
-%       |       
-%       |  *(i-1,j)   *(i,j) *(i+1,j)
-%       |_______________________________> x1
-%
-%
-%
-%For more information, please see the related submission:
-%
-% M.T Andén, B. Bernhardsson, A. Cervin and K. Soltesz, "On Event-Based 
-%Sampling for H2-Optimal Control", Submitted to 56th Conf. on Decision and
-%Control, 2017.
-%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-clear all
-close all
-%% Define Parameters
-
-%A-matrix
-a11 = 1;
-a12 = -1;
-a21 = 0;
-a22 = -70;
-Amat = [a11 a12;a21 a22];
-
-%Q-matrix and J
-q11 = 1;
-q22 = 1;
-J = 1;
-
-%State-space grid parameters
-x1min = -2;
-x1max = 2;
-x2min = -2;
-x2max = 2;
-
-nx1 = 200;
-nx2 = 200;
-dx1 = (x1max-x1min)/(nx1-1);
-dx2 = (x2max-x2min)/(nx2-1);
-
-x1 = x1min:dx1:x1max;
-x2 = x2min:dx2:x2max;
-
-%Time discretization and stopping criteria
-dt = 2.5e-4;
-maxiter = 5e4;           %Stop when # of time-steps = maxiter
-rel_stop_crit = 1e-1*dt; %Stop when abs(max(V(t+1) - V(t)) <= rel_stop_crit  
-
-%% Gridding
-
-%Grid up solution V
-V = zeros(nx2,nx1); %Initial condition V(x,0) = 0
-Vn = zeros(nx2,nx1);
-
-%Make grid of state space
-[X1,X2] = meshgrid(x1,x2);
-X2=flipud(X2);               
-X1cut = X1(2:end-1,2:end-1);
-X2cut = X2(2:end-1,2:end-1);
-
-%"Production" term at each (i,j)-location for duration dt
-prod = dt*(q11*X1.^2 + q22*X2.^2 - J);
-prod(1,:) = [];
-prod(end,:) = [];
-prod(:,1) = [];
-prod(:,end) = [];
-prodvec = reshape(prod,[],1);
-
-%Dirichlet Boundary condition vector (V(x) = 0 at boundary)
-bc = zeros(nx2-2,nx1-2);
-
-%% Coefficient matrices for BTCS scheme
-Ex1 = sparse(2:nx1-2,1:nx1-3,1,nx1-2,nx1-2);
-Ax1 = Ex1+Ex1'-2*speye(nx1-2);
-Ex2 = sparse(2:nx2-2,1:nx2-3,1,nx2-2,nx2-2);
-Ax2 = Ex2+Ex2' - 2*speye(nx2-2);
-A = kron(Ax2/dx2^2,speye(nx1-2)) + kron(speye(nx2-2),Ax1/dx1^2);
-D = speye((nx1-2)*(nx2-2))-0.5*dt*A;
-
-diffx1 = Ex1'-Ex1;
-diffx2 = Ex2'-Ex2;
-Dx1 = kron(speye(nx2-2),diffx1/(2*dx1));
-Dx2 = kron(diffx2/(2*dx2),speye(nx1-2));
-
-fx1 = reshape(a11*X1cut + a12*X2cut,[],1);
-fx2 = reshape(a21*X1cut + a22*X2cut,[],1);
-
-%% Simulating the distribution to steady-state
-maxVdiff = inf;
-Vplot = V;
-
-%Plotting
-h1=figure(1);
-
-subplot(1,2,1)
-h2=surf(x1,x2,Vplot,'EdgeColor','none','ZDataSource','Vplot');
-shading interp
-title('Value function V(x)')
-axis([x1min x1max x2min x2max -0.15 0.05])
-axis square
-xlabel('x1')
-ylabel('x2')
-zlabel('V(x)')
-
-subplot(1,2,2)
-[~,h3]=contour(x1,x2,Vplot,[0 0],'b','ZDataSource','Vplot');
-axis([x1min x1max x2min x2max])
-axis square
-title({['Optimal Trigger Bound with A = ', mat2str(Amat), ', Q = diag(',num2str(q11),',',num2str(q22),')']})
-xlabel('x1')
-ylabel('x2')
-grid on
-      
-for it=0:maxiter
-    Vn = V;
-    if mod(it,10) == 0
-        disp(['Iterations : ' num2str(it),' out of ',num2str(maxiter), ' | Maximum Vdiff : ', num2str(maxVdiff)])
-        
-        %Update plot
-        refreshdata(h2)
-        refreshdata(h3)
-        drawnow;
-    end
-    
-    %Time-step
-    v = Vn;
-    v(1,:) = [];
-    v(end,:) = [];
-    v(:,1) = [];
-    v(:,end) = [];
-    v = reshape(v+bc,[],1);
-    v = D\(v + prodvec + dt*fx1.*(Dx1*v) + dt*fx2.*(Dx2*v));
-    v = reshape(v,nx2 - 2, nx1 - 2);
-    V(2:nx2-1,2:nx1-1) = v;  
-    
-    %Enforce boundary conditons
-    V(1,:) = 0;
-    V(nx2,:) = 0;
-    V(:,1) = 0;
-    V(:,nx1) = 0;
-    V = min(V,0);
-    Vplot=V;
-    
-    %Check for steady-state
-    Vdiff = abs(V - Vn);
-    maxVdiff = max(max(Vdiff));
-    if maxVdiff <= rel_stop_crit
-        disp('Stopping Criteria Met')
-        break;
-    end
-end
diff --git a/script_optimal_bound_2d.m b/script_optimal_bound_2d.m
new file mode 100644
index 0000000000000000000000000000000000000000..1a75203f1a6fcb3e681f23d218f2f578b18f027f
--- /dev/null
+++ b/script_optimal_bound_2d.m
@@ -0,0 +1,78 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Written by: Marcus Thelander Andrén, 2017
+%
+% This script demonstrates for the 2D case how to numerically compute the 
+% value function V that determines the optimal reset bound. 
+%
+% Without loss of generality, the system is assumed to be of the form
+% Q = diag(q11, q22) and R = I (the system state can always be transformed
+% to achieve this).
+% 
+% For more information, see the related article: 
+%
+%   M. Thelander Andrén, B. Bernhardsson, A. Cervin and K. Soltesz,
+%   "On Event-Based Sampling for LQG-Optimal Control" in Proc. 56th IEEE
+%   Conf. on Decision and Control, Melbourne, Australia, 2017
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Preamble
+clear all
+close all
+addpath('./functions')
+
+%% Setup parameters
+
+%Define system
+A = [-1 20; 20  -1];
+q11 = 1;
+q22 = 1;
+J=1;
+
+%Gridding
+dx = 3e-2;
+x1min=-2;
+x1max = 2;
+x2min= -2;
+x2max = 2;
+x1vec = x1min:dx:x1max;
+x2vec = x2min:dx:x2max;
+h = 5e-4;
+
+%Enable/disable plotting and printing
+plot_progress = 0;
+print_progress = 1;
+plot_result = 1;
+
+%% Compute V
+disp('Computing V...')
+V = optimal_bound_2d(A, q11, q22, J, x1vec, x2vec, h,...
+    plot_progress, print_progress);
+disp('Done computing V')
+
+%% Plot the end result
+if  plot_result && ~plot_progress
+    figure()
+    colormap('jet')
+    
+    %Surface plot of V
+    subplot(1,2,1)
+    surf(x1vec,x2vec,V,'EdgeColor','none');
+    shading interp
+    xlabel('x_1')
+    ylabel('x_2')
+    zlabel('V')
+    title('Value Function')
+    axis tight
+    
+    %Optimal bound
+    subplot(1,2,2)
+    contour(x1vec,x2vec,V,[0 0],'b')
+    axis([x1vec(1) x1vec(end) x2vec(1) x2vec(end)])
+    xlabel('x_1')
+    ylabel('x_2')
+    title('Optimal Reset Bound')
+    grid on
+    axis equal    
+end