%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% To run:  Edit user options in npscalc.m and execute the code.
%
% This code is intended to accompany the manuscript entitled:
%
% A simple approach to measure computed tomography (CT) modulation transfer
% function (MTF) and noise-power spectrum (NPS) using the American College
% of Radiology (ACR) accreditation phantom
%
% Please do not distribute.
%
% This program requires the following 4 files:
%   npscalc.m
%   SliceBrowser.m
%   SliceBrowser.fig
%   license.txt
%
% Purpose:  To calculate the 3D NPS using CT data of the American College
%           of Radiology (ACR) accreditation phantom.
%
% Input:    Two consecutive scans of the phantom are needed.  The program
%           requires a data directory to be selected in which there are
%           only two subdirectories containing only CT slices corresponding
%           to the third module of the phantom.  Be careful of partial
%           volume effects with surrounding modules.
%
%           i.e.,  datadir
%                      |-> scan 1 dir
%                      |           | -> only module 3 slices
%                      |
%                      |-> scan 2 dir
%                                  |-> only module 3 slices
%
% Copyright 2012 Saul N. Friedman
%   Distributed under the terms of the "New BSD License."  Please see
%   license.txt.
%
% N.B.:  SliceBrowser.m is an altered version of code by Marian Uhercik.
%        Please see license.txt.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% ======================================================================
%> SLICEBROWSER M-file for SliceBrowser.fig
%>       SliceBrowser is an interactive viewer of 3D volumes,
%>       it shows 3 perpendicular slices (XY, YZ, ZX) with 3D pointer.
%>   Input:  a) VOLUME - a 3D matrix with volume data
%>           b) VOLUME - a 4D matrix with volume data over time
%>   Control:
%>       - Clicking into the window changes the location of 3D pointer.
%>       - 3D pointer can be moved also by keyboard arrows.
%>       - Pressing +/- will switch to next/previous volume.
%>       - Pressing 1,2,3 will change the focus of current axis.
%>       - Pressing 'e' will print the location of 3D pointer.
%>       - Pressing 'c' switches between color-mode and grayscale.
%>       - Pressing 'q' switches scaling of axis (equal/normal).
%>   Example of usage:
%>       load mri.dat
%>       volume = squeeze(D);
%>       SliceBrowser(volume);
%>
%> Last modified by Saul N. Friedman
% ======================================================================
function varargout = SliceBrowser(varargin)

% Documentation generated GUIDE:
%
%SLICEBROWSER M-file for SliceBrowser.fig
%      SLICEBROWSER, by itself, creates a new SLICEBROWSER or raises the existing
%      singleton*.
%
%      H = SLICEBROWSER returns the handle to a new SLICEBROWSER or the handle to
%      the existing singleton*.
%
%      SLICEBROWSER('Property','Value',...) creates a new SLICEBROWSER using the
%      given property value pairs. Unrecognized properties are passed via
%      varargin to SliceBrowser_OpeningFcn.  This calling syntax produces a
%      warning when there is an existing singleton*.
%
%      SLICEBROWSER('CALLBACK') and SLICEBROWSER('CALLBACK',hObject,...) call the
%      local function named CALLBACK in SLICEBROWSER.M with the given input
%      arguments.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help SliceBrowser

% Last Modified by GUIDE v2.5 01-Sep-2008 13:14:20

% Begin initialization code - DO NOT EDIT
gui_Singleton = 0;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @SliceBrowser_OpeningFcn, ...
                   'gui_OutputFcn',  @SliceBrowser_OutputFcn, ...
                   'gui_LayoutFcn',  [], ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
   gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before SliceBrowser is made visible.
function SliceBrowser_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   unrecognized PropertyName/PropertyValue pairs from the
%            command line (see VARARGIN)

% Choose default command line output for SliceBrowser
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes SliceBrowser wait for user response (see UIRESUME)
% uiwait(handles.figure1);

if (length(varargin) <=0)
    error('Input volume has not been specified.');
end;
volume = varargin{1};
if (ndims(volume) ~= 3 && ndims(volume) ~= 4)
    error('Input volume must have 3 or 4 dimensions.');
end;
handles.volume = volume;

handles.axis_equal = 0;
handles.color_mode = 1;
if (size(volume,4) ~= 3)
    handles.color_mode = 0;
end;

% set main wnd title
set(gcf, 'Name', 'Slice Viewer')

% init 3D pointer
vol_sz = size(volume);
if (ndims(volume) == 3)
    vol_sz(4) = 1;
end;
pointer3dt = floor(vol_sz/2)+1;
handles.pointer3dt = pointer3dt;
handles.vol_sz = vol_sz;

plot3slices(hObject, handles);

% stores ID of last axis window
% (0 means that no axis was clicked yet)
handles.last_axis_id = 0;

% Update handles structure
guidata(hObject, handles);

% --- Outputs from this function are returned to the command line.
function varargout = SliceBrowser_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

% --- Executes on mouse press over axes background.
function Subplot1_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to Subplot1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% This object contains the XY slice

xaxis = getappdata(0,'uaxis');
yaxis = getappdata(0,'vaxis');
zaxis = getappdata(0,'waxis');
axlabel = getappdata(0,'axlabel');
deltayaxis = yaxis(2) - yaxis(1);
deltaxaxis = xaxis(2) - xaxis(1);
deltazaxis = zaxis(2) - zaxis(1);

%disp('Subplot1:BtnDown');
pt=get(gca,'currentpoint');
if length(xaxis) > 0 && length(yaxis)
    xpos=round((pt(1,2)+xaxis(end))/deltaxaxis); ypos=round((pt(1,1)+yaxis(end))/deltayaxis);
else
xpos=round(pt(1,2)); ypos=round(pt(1,1));
end
zpos = handles.pointer3dt(3);
tpos = handles.pointer3dt(4);
handles.pointer3dt = [xpos ypos zpos tpos];
handles.pointer3dt = clipointer3d(handles.pointer3dt,handles.vol_sz);
plot3slices(hObject, handles);
% store this axis as last clicked region
handles.last_axis_id = 1;
% Update handles structure
guidata(hObject, handles);

% --- Executes on mouse press over axes background.
function Subplot2_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to Subplot2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% This object contains the YZ slice

xaxis = getappdata(0,'uaxis');
yaxis = getappdata(0,'vaxis');
zaxis = getappdata(0,'waxis');
axlabel = getappdata(0,'axlabel');
deltayaxis = yaxis(2) - yaxis(1);
deltaxaxis = xaxis(2) - xaxis(1);
deltazaxis = zaxis(2) - zaxis(1);

%disp('Subplot2:BtnDown');
pt=get(gca,'currentpoint');
if length(xaxis) > 0 && length(zaxis)
    xpos=round((pt(1,2)+xaxis(end))/deltaxaxis); zpos=round((pt(1,1)+zaxis(end))/deltazaxis);
else
xpos=round(pt(1,2)); zpos=round(pt(1,1));
end
ypos = handles.pointer3dt(2);
tpos = handles.pointer3dt(4);
handles.pointer3dt = [xpos ypos zpos tpos];
handles.pointer3dt = clipointer3d(handles.pointer3dt,handles.vol_sz);
plot3slices(hObject, handles);
% store this axis as last clicked region
handles.last_axis_id = 2;
% Update handles structure
guidata(hObject, handles);

% --- Executes on mouse press over axes background.
function Subplot3_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to Subplot3 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% This object contains the XZ slice

xaxis = getappdata(0,'uaxis');
yaxis = getappdata(0,'vaxis');
zaxis = getappdata(0,'waxis');
axlabel = getappdata(0,'axlabel');
deltayaxis = yaxis(2) - yaxis(1);
deltaxaxis = xaxis(2) - xaxis(1);
deltazaxis = zaxis(2) - zaxis(1);

%disp('Subplot3:BtnDown');
pt=get(gca,'currentpoint');
if length(xaxis) > 0 && length(zaxis)
    ypos=round((pt(1,1)+yaxis(end))/deltayaxis); zpos=round((pt(1,2)+zaxis(end))/deltazaxis);
else
zpos=round(pt(1,2)); ypos=round(pt(1,1));
end
xpos = handles.pointer3dt(1);
tpos = handles.pointer3dt(4);
handles.pointer3dt = [xpos ypos zpos tpos];
handles.pointer3dt = clipointer3d(handles.pointer3dt,handles.vol_sz);
plot3slices(hObject, handles);
% store this axis as last clicked region
handles.last_axis_id = 3;
% Update handles structure
guidata(hObject, handles);

% --- Executes on key press with focus on SliceBrowserFigure and no controls selected.
function SliceBrowserFigure_KeyPressFcn(hObject, eventdata, handles)
% hObject    handle to SliceBrowserFigure (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

%disp('SliceBrowserFigure_KeyPressFcn');
curr_char = int8(get(gcf,'CurrentCharacter'));
if isempty(curr_char)
    return;
end;

xpos = handles.pointer3dt(1);
ypos = handles.pointer3dt(2);
zpos = handles.pointer3dt(3);
tpos = handles.pointer3dt(4);
% Keys:
% - up:   30
% - down:   31
% - left:   28
% - right:   29
% - '1': 49
% - '2': 50
% - '3': 51
% - 'e': 101
% - plus:  43
% - minus:  45
switch curr_char
    case 99 % 'c'
        handles.color_mode = 1 - handles.color_mode;
        if (handles.color_mode ==1 && size(handles.volume,4) ~= 3)
            handles.color_mode = 0;
        end;

    case 113 % 'q'
        handles.axis_equal = 1 - handles.axis_equal;

    case 30
        switch handles.last_axis_id
            case 1
                xpos = xpos -1;
            case 2
                xpos = xpos -1;
            case 3
                zpos = zpos -1;
            case 0
        end;
    case 31
        switch handles.last_axis_id
            case 1
                xpos = xpos +1;
            case 2
                xpos = xpos +1;
            case 3
                zpos = zpos +1;
            case 0
        end;
    case 28
        switch handles.last_axis_id
            case 1
                ypos = ypos -1;
            case 2
                zpos = zpos -1;
            case 3
                ypos = ypos -1;
            case 0
        end;
    case 29
        switch handles.last_axis_id
            case 1
                ypos = ypos +1;
            case 2
                zpos = zpos +1;
            case 3
                ypos = ypos +1;
            case 0
        end;
    case 43
        % plus key
        tpos = tpos+1;
    case 45
        % minus key
        tpos = tpos-1;
    case 49
        % key 1
        handles.last_axis_id = 1;
    case 50
        % key 2
        handles.last_axis_id = 2;
    case 51
        % key 3
        handles.last_axis_id = 3;
    case 101
        disp(['[' num2str(xpos) ' ' num2str(ypos) ' ' num2str(zpos) ' ' num2str(tpos) ']']);
    otherwise
        return
end;
handles.pointer3dt = [xpos ypos zpos tpos];
handles.pointer3dt = clipointer3d(handles.pointer3dt,handles.vol_sz);
plot3slices(hObject, handles);
% Update handles structure
guidata(hObject, handles);

% --- Plots all 3 slices XY, YZ, XZ into 3 subplots
function [sp1,sp2,sp3] = plot3slices(hObject, handles)
% pointer3d     3D coordinates in volume matrix (integers)

handles.pointer3dt;
size(handles.volume);
value3dt = handles.volume(handles.pointer3dt(1), handles.pointer3dt(2), handles.pointer3dt(3), handles.pointer3dt(4));

text_str = ['[X:' int2str(handles.pointer3dt(1)) ...
           ', Y:' int2str(handles.pointer3dt(2)) ...
           ', Z:' int2str(handles.pointer3dt(3)) ...
           ', Time:' int2str(handles.pointer3dt(4)) '/' int2str(handles.vol_sz(4)) ...
           '], value:' num2str(value3dt)];
set(handles.pointer3d_info, 'String', text_str);
guidata(hObject, handles);

if (handles.color_mode ==1)
    sliceXY = squeeze(handles.volume(:,:,handles.pointer3dt(3),:));
    sliceYZ = squeeze(handles.volume(handles.pointer3dt(1),:,:,:));
    sliceXZ = squeeze(handles.volume(:,handles.pointer3dt(2),:,:));

    max_xyz = max([ max(sliceXY(:)) max(sliceYZ(:)) max(sliceXZ(:)) ]);
    min_xyz = min([ min(sliceXY(:)) min(sliceYZ(:)) min(sliceXZ(:)) ]);
    clims = [ min_xyz max_xyz ];
else
    sliceXY = squeeze(handles.volume(:,:,handles.pointer3dt(3),handles.pointer3dt(4)));
    sliceYZ = squeeze(handles.volume(handles.pointer3dt(1),:,:,handles.pointer3dt(4)));
    sliceXZ = squeeze(handles.volume(:,handles.pointer3dt(2),:,handles.pointer3dt(4)));

    max_xyz = max([ max(sliceXY(:)) max(sliceYZ(:)) max(sliceXZ(:)) ]);
    min_xyz = min([ min(sliceXY(:)) min(sliceYZ(:)) min(sliceXZ(:)) ]);
    clims = [ min_xyz max_xyz ];
end;
sliceZY = squeeze(permute(sliceYZ, [2 1 3]));

xaxis = getappdata(0,'uaxis');
yaxis = getappdata(0,'vaxis');
zaxis = getappdata(0,'waxis');
axlabel = getappdata(0,'axlabel');
if length(xaxis) > 0 && length(yaxis) && length(zaxis)
deltayaxis = yaxis(2) - yaxis(1);
deltaxaxis = xaxis(2) - xaxis(1);
deltazaxis = zaxis(2) - zaxis(1);
end

sp1 = subplot(2,2,1);
%colorbar;
if length(xaxis) > 0 && length(yaxis)
    imagesc([yaxis(1) yaxis(end)],[xaxis(1) xaxis(end)],sliceXY,clims)
else
imagesc(sliceXY, clims);
end
if (handles.axis_equal == 1)
    axis image;
else
    axis normal;
end;
title('Slice XY');
if exist('axlabel')
    xlabel(['Y',axlabel]);ylabel(['X',axlabel])
else
ylabel('X');xlabel('Y');
end
if length(xaxis) > 0 && length(yaxis)
    line([handles.pointer3dt(2).*deltayaxis - yaxis(end) handles.pointer3dt(2).*deltayaxis - yaxis(end)], [-size(handles.volume,1) size(handles.volume,1)]);
    line([-size(handles.volume,2) size(handles.volume,2)], [handles.pointer3dt(1).*deltaxaxis - xaxis(end) handles.pointer3dt(1).*deltaxaxis - xaxis(end)]);
else
line([handles.pointer3dt(2) handles.pointer3dt(2)], [0 size(handles.volume,1)]);
line([0 size(handles.volume,2)], [handles.pointer3dt(1) handles.pointer3dt(1)]);
end
%set(allchild(gca),'ButtonDownFcn',@Subplot1_ButtonDownFcn);
set(allchild(gca),'ButtonDownFcn','SliceBrowser(''Subplot1_ButtonDownFcn'',gca,[],guidata(gcbo))');

sp2 = subplot(2,2,2);
if length(xaxis) > 0 &&length(zaxis) > 0
   imagesc([zaxis(1) zaxis(end)],[xaxis(1) xaxis(end)],sliceXZ,clims)
else
imagesc(sliceXZ, clims);
end
if (handles.axis_equal == 1)
    axis image;
else
    axis normal;
end;
title('Slice XZ');
if exist('axlabel')
    ylabel(['X',axlabel]);xlabel(['Z',axlabel])
else
ylabel('X');xlabel('Z');
end
if length(xaxis) > 0 && length(zaxis)
    line([handles.pointer3dt(3).*deltazaxis - zaxis(end) handles.pointer3dt(3).*deltazaxis - zaxis(end)], [-size(handles.volume,1).*deltaxaxis size(handles.volume,1).*deltaxaxis]);
line([-size(handles.volume,3).*deltazaxis size(handles.volume,3).*deltazaxis], [handles.pointer3dt(1).*deltaxaxis - xaxis(end) handles.pointer3dt(1).*deltaxaxis - xaxis(end)]);
else
line([handles.pointer3dt(3) handles.pointer3dt(3)], [0 size(handles.volume,1)]);
line([0 size(handles.volume,3)], [handles.pointer3dt(1) handles.pointer3dt(1)]);
end
%set(allchild(gca),'ButtonDownFcn',@Subplot2_ButtonDownFcn);
set(allchild(gca),'ButtonDownFcn','SliceBrowser(''Subplot2_ButtonDownFcn'',gca,[],guidata(gcbo))');

sp3 = subplot(2,2,3);
if length(yaxis) > 0 && length(zaxis) > 0
   imagesc([yaxis(1) yaxis(end)],[zaxis(1) zaxis(end)],sliceZY,clims)
else
imagesc(sliceZY, clims);
end
if (handles.axis_equal == 1)
    axis image;
else
    axis normal;
end;
title('Slice ZY');
if exist('axlabel')
    ylabel(['Z',axlabel]);xlabel(['Y',axlabel])
else
ylabel('Z');xlabel('Y');
end

if length(yaxis) > 0 && length(zaxis)
    line([-size(handles.volume,2) size(handles.volume,2)], [handles.pointer3dt(3).*deltazaxis - zaxis(end) handles.pointer3dt(3).*deltazaxis - zaxis(end)]);
    line([handles.pointer3dt(2).*deltayaxis - yaxis(end) handles.pointer3dt(2).*deltayaxis - yaxis(end)], [-size(handles.volume,3).*deltazaxis size(handles.volume,3).*deltazaxis]);
else
line([0 size(handles.volume,2)], [handles.pointer3dt(3) handles.pointer3dt(3)]);
line([handles.pointer3dt(2) handles.pointer3dt(2)], [0 size(handles.volume,3)]);
end
%set(allchild(gca),'ButtonDownFcn',@Subplot3_ButtonDownFcn);
set(allchild(gca),'ButtonDownFcn','SliceBrowser(''Subplot3_ButtonDownFcn'',gca,[],guidata(gcbo))');

function pointer3d_out = clipointer3d(pointer3d_in,vol_size)
pointer3d_out = pointer3d_in;
for p_id=1:4
    if (pointer3d_in(p_id) > vol_size(p_id))
        pointer3d_out(p_id) = vol_size(p_id);
    end;
    if (pointer3d_in(p_id) < 1)
        pointer3d_out(p_id) = 1;
    end;
end;


classdef unbndfun < classicfun
%UNBNDFUN    Represent global functions on an unbounded interval [-inf inf] or
% a semi-infinite interval [-inf b] or [a inf].
%
% Constructor inputs:
%   UNBNDFUN(OP) constructs a UNBNDFUN object from the function handle OP on the
%   domain determined by the default in CHEBFUNPREF by mapping the domain to
%   [-1, 1] and constructing a ONEFUN object to represent the function
%   prescribed by OP.  OP should be vectorised (i.e., accept a vector input)
%   and output a vector of the same length as its input.
%
%   UNBNDFUN(OP, DATA) constructs an UNBNDFUN object using the data supplied in
%   the DATA structure.  DATA fields used by UNBNDFUN are:
%     DATA.DOMAIN       (Default:  Determined by CHEBFUNPREF)
%         A row vector with two elements in increasing order defining the
%         construction domain.  At least one element must be infinite.
%   In addition, UNBNDFUN may modify the following DATA fields before passing
%   them on to the ONEFUN constructor:
%     DATA.HSCALE       (Default:  Determined by DATA.DOMAIN)
%         Horizontal construction scale.
%     DATA.EXPONENTS    (Default:  Empty)
%         Exponents describing the behavior of the function at the endpoints of
%         the construction domain.  (See SINGFUN.)
%     DATA.SINGTYPE     (Default:  Empty)
%         Types of endpoint singularities to search for.  (See SINGFUN.)
%   If any fields in DATA are empty or not supplied, or if DATA itself is empty
%   or not supplied, appropriate default values are set.  Any fields in DATA
%   which are not recognized will be passed as-is to the ONEFUN constructor.
%
%   UNBNDFUN(OP, DATA, PREF) overrides the default behavior with that given by
%   the preferences in the structure or CHEBFUNPREF object PREF. See
%   CHEBFUNPREF for details.
%
% See also CLASSICFUN, CHEBFUNPREF, ONEFUN.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% UNBNDFUN Class Description:
%
% The UNBNDFUN class represents global functions on the infinite or
% semi-infinite interval [-inf, b], [a, inf], or [-inf, inf]. It achieves this
% by taking a onefun on [-1, 1] and applying a nonlinear mapping.
%
% Note that all binary UNBNDFUN operators (methods which can take two UNBNDFUN
% arguments) assume that the domains of the UNBNDFUN objects agree. The methods
% will not throw warnings if assumption is violated, but the results will not be
% meaningful under that circumstance.
%
% Class diagram: [<<CLASSICFUN>>] <>-- [<<onefun>>]
%                       ^
%                       |
%                   [unbndfun]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% CLASS CONSTRUCTOR:
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods ( Access = public, Static = false )

        function obj = unbndfun(op, data, pref)

            % Parse inputs.
            if ( (nargin < 1) || isempty(op) )
                obj.domain = [];
                obj.mapping = [];
                obj.onefun = [];
                return
            end

            if ( (nargin < 2) || isempty(data) )
                data = struct();
            end

            if ( (nargin < 3) || isempty(pref) )
                pref = chebfunpref();
            else
                pref = chebfunpref(pref);
            end

            data = parseDataInputs(data, pref);

            % Check domain
            if ( ~all(size(data.domain) == [1, 2]) || (diff(data.domain) <= 0) )
                error('CHEBFUN:UNBNDFUN:unbndfun:domain',...
                    ['Domain argument should be a row vector with two ', ...
                    'entries in increasing order.']);
            elseif ( ~any(isinf(data.domain)) )
                error('CHEBFUN:UNBNDFUN:unbndfun:boundedDomain',...
                    'Domain argument should be unbounded.');
            end

            % Remap the OP to be a function on [-1, 1].
            unbndmap = unbndfun.createMap(data.domain);
            if ( isa(op, 'function_handle') )
                op = @(x) op(unbndmap.For(x));
            elseif ( isnumeric(op) )
                if ( ~any(op(:)) )
                    op = @(x) zeros(length(x), size(op, 2));
                else
                    %[TODO]: Implement this.
                    error('CHEBFUN:UNBNDFUN:unbndfun:inputValues',...
                        ['UNBNDFUN does not support non-zero construction ' ...
                         'from values.']);
                end
            end

            % Deal with exponents for singular functions.
            if ( isempty(data.exponents) )
                % The user hasn't supplied exponents, but functions on
                % unbounded domains are often singular once remapped to [-1,
                % 1].  Try to detect this by seeing if the function is infinite
                % at either endpoint and, if so, enable singularity detection.
                lVal = feval(op, -1);
                rVal = feval(op, 1);
                if ( any(isinf([lVal rVal])) )
                    pref.blowup = true;
                    singType = pref.blowupPrefs.defaultSingType;
                    data.singType = {singType, singType};
                end
            else
                % Remapping to [-1, 1] negates exponents, which are given with
                % respect to the function on the infinite domain.
                ind = isinf(data.domain);
                pref.blowup = true;
                data.exponents(ind) = -data.exponents(ind);
            end

            % Call the ONEFUN constructor:
            obj.onefun = onefun.constructor(op, data, pref);

            % Add the domain and mapping:
            obj.domain = data.domain;
            obj.mapping = unbndmap;
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% STATIC METHODS:
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods ( Access = public, Static = true )

        % Retrieve and modify preferences for this class.
        prefs = pref(varargin);

        % Noninear map from [-1, 1] to the domain of the UNBNDFUN.
        m = createMap(domain);

        % Make a UNBNDFUN (constructor shortcut):
        f = make(varargin);

    end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% PRIVATE METHODS IMPLEMENTED IN THIE FILE:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function data = parseDataInputs(data, pref)
%PARSEDATAINPUTS   Parse inputs from the DATA structure and assign defaults.

if ( ~isfield(data, 'domain') || isempty(data.domain) )
    data.domain = pref.domain;
end

if ( ~isfield(data, 'hscale') || isempty(data.hscale) )
    % TODO:  Why is the hscale of an unbounded domain always 1?
    data.hscale = 1;
end

if ( ~isfield(data, 'exponents') || isempty(data.exponents) )
    data.exponents = [];
end

if ( ~isfield(data, 'singType') || isempty(data.singType) )
    data.singType = [];
end

end


function cvt_write ( ndim, n, batch, seed_init, seed, init_string, it_max, ...
  it_fixed, it_num, it_diff, energy, sample_string, sample_num, r, ...
  file_out_name )

%*****************************************************************************80
%
%% CVT_WRITE writes a CVT dataset to a file.
%
%  Discussion:
%
%    The initial lines of the file are comments, which begin with a
%    '#' character.
%
%    Thereafter, each line of the file contains the NDIM-dimensional
%    components of the next entry in the set.
%
%  Modified:
%
%    17 November 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer NDIM, the spatial dimension.
%
%    Input, integer N, the number of points.
%
%    Input, integer BATCH, sets the maximum number of sample points
%    generated at one time.  It is inefficient to generate the sample
%    points 1 at a time, but memory intensive to generate them all
%    at once.  You might set BATCH to min ( SAMPLE_NUM, 10000 ), for instance.
%
%    Input, integer SEED_INIT, the initial random number seed.
%
%    Input, integer SEED, the current random number seed.
%
%    Input, character INIT_STRING(*), specifies how the initial
%    generators are chosen:
%    filename, by reading data from a file;
%    'GRID', picking points from a grid;
%    'HALTON', from a Halton sequence;
%    'RAND', using MATLAB RAND function;
%    'UNIFORM', using a simple uniform RNG;
%
%    Input, integer IT_MAX, the maximum number of iterations allowed.
%
%    Input, integer IT_FIXED, the maximum number of iterations to take
%    with a fixed set of sample points.
%
%    Input, integer IT_NUM, the actual number of iterations taken.
%
%    Input, real IT_DIFF, the L2 norm of the change
%    in the CVT coordinates on the last iteration.
%
%    Input, real ENERGY, the discrete "energy", divided
%    by the number of sample points.
%
%    Input, string SAMPLE_STRING, specifies how the region is sampled:
%    'GRID', picking points from a grid;
%    'HALTON', from a Halton sequence;
%    'RAND', using MATLAB RAND function;
%    'UNIFORM', using a simple uniform RNG;
%
%    Input, integer SAMPLE_NUM, the number of sampling points used on
%    each iteration.
%
%    Input, real R(NDIM,N), the points.
%
%    Input, string FILE_OUT_NAME, the name of
%    the output file.
%
  file_out_unit = fopen ( file_out_name, 'w' );

  if ( file_out_unit < 0 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'CVT_WRITE - Fatal error!\n' );
    fprintf ( 1, '  Could not open the output file:\n' );
    fprintf ( 1, '  "%s".\n', file_out_name );
    error ( 'CVT_WRITE - Fatal error!' );
  end

  fprintf ( file_out_unit, '#  %s\n', file_out_name );
  fprintf ( file_out_unit, '#  created by CVT_WRITE.M\n' );
  fprintf ( file_out_unit, '#\n' );
  fprintf ( file_out_unit, '#  Spatial dimension NDIM =   %12d\n', ndim );
  fprintf ( file_out_unit, '#  Number of points N =       %12d\n', n );
  fprintf ( file_out_unit, '#  Initial SEED_INIT =        %12d\n', seed_init );
  fprintf ( file_out_unit, '#  Current SEED =             %12d\n', seed );
  fprintf ( file_out_unit, '#  INIT =                    "%s"\n', init_string );
  fprintf ( file_out_unit, '#  Max iterations IT_MAX =    %12d\n', it_max );
  fprintf ( file_out_unit, '#  IT_FIXED (fixed samples) = %12d\n', it_fixed );
  fprintf ( file_out_unit, '#  Iterations IT_NUM =        %12d\n', it_num );
  fprintf ( file_out_unit, '#  Difference IT_DIFF =       %12f\n', it_diff );
  fprintf ( file_out_unit, '#  CVT ENERGY =               %12f\n', energy );
  fprintf ( file_out_unit, '#  SAMPLE =                  "%s"\n', sample_string );
  fprintf ( file_out_unit, '#  SAMPLE_NUM =               %12d\n', sample_num );
  fprintf ( file_out_unit, '#  Sample BATCH size =        %12d\n', batch );
  fprintf ( file_out_unit, '#  EPSILON (unit roundoff ) = %e\n', eps );
  fprintf ( file_out_unit, '#\n' );

  for j = 1 : n
    for i = 1 : ndim
      fprintf ( file_out_unit, '  %10f', r(i,j) );
    end
    fprintf ( file_out_unit, '\n' );
  end

  fclose ( file_out_unit );

  return
end


function [concentrationMatrix, excRxnNames, timeVec, biomassVec] = dynamicFBA(model, substrateRxns, initConcentrations, initBiomass, timeStep, nSteps, plotRxns, exclUptakeRxns)
% Performs dynamic FBA simulation using the static optimization approach
%
% USAGE:
%
%    [concentrationMatrix, excRxnNames, timeVec, biomassVec] = dynamicFBA(model, substrateRxns, initConcentrations, initBiomass, timeStep, nSteps, plotRxns, exclUptakeRxns)
%
% INPUTS:
%    model:                  COBRA model structure
%    substrateRxns:          List of exchange reaction names for substrates
%                            initially in the media that may change (e.g. not
%                            h2o or co2)
%    initConcentrations:     Initial concentrations of substrates (in the same
%                            structure as `substrateRxns`)
%    initBiomass:            Initial biomass (must be non zero)
%    timeStep:               Time step size
%    nSteps:                 Maximum number of time steps
%
% OPTIONAL INPUTS:
%    plotRxns:               Reactions to be plotted (Default = {'EX_glc(e)', 'EX_ac(e)', 'EX_for(e)'})
%    exclUptakeRxns:         List of uptake reactions whose substrate concentrations do not change
%                            (Default = {'EX_co2(e)', 'EX_o2(e)', 'EX_h2o(e)', 'EX_h(e)'})
%
% OUTPUTS:
%    concentrationMatrix:    Matrix of extracellular metabolite concentrations
%    excRxnNames:            Names of exchange reactions for the EC metabolites
%    timeVec:                Vector of time points
%    biomassVec:             Vector of biomass values
%
% If no initial concentration is given for a substrate that has an open
% uptake in the model (i.e. `model.lb < 0`) the concentration is assumed to
% be high enough to not be limiting. If the uptake rate for a nutrient is
% calculated to exceed the maximum uptake rate for that nutrient specified
% in the model and the max uptake rate specified is > 0, the maximum uptake
% rate specified in the model is used instead of the calculated uptake
% rate.
%
% NOTE:
%
%    The dynamic FBA method implemented in this function is essentially
%    the same as the method described in
%    [`Varma, A., and B. O. Palsson. Appl. Environ. Microbiol. 60:3724 (1994)`].
%    This function does not implement the dynamic FBA using dynamic optimization approach
%    described in [`Mahadevan, R. et al. Biophys J, 83:1331-1340 (2003)`].
%
% .. Author: - Markus Herrgard 8/22/06

global WAITBAR_TYPE

if (nargin < 7)
    plotRxns = {'EX_glc(e)','EX_ac(e)','EX_for(e)'};
end

% Uptake reactions whose substrate concentrations do not change
if (nargin < 8)
    exclUptakeRxns = {'EX_co2(e)','EX_o2(e)','EX_h2o(e)','EX_h(e)'};
end

% Find exchange rxns
excInd = findExcRxns(model,false);
excInd = excInd & ~ismember(model.rxns,exclUptakeRxns);
excRxnNames = model.rxns(excInd);
length(excRxnNames)
% Figure out if substrate reactions are correct
missingInd = find(~ismember(substrateRxns,excRxnNames));
if (~isempty(missingInd))
    for i = 1:length(missingInd)
        fprintf('%s\n',substrateRxns{missingInd(i)});
    end
    error('Invalid substrate uptake reaction!');
end

% Initialize concentrations
[~, substrateMatchInd] = ismember(substrateRxns,excRxnNames);
concentrations = zeros(length(excRxnNames),1);
concentrations(substrateMatchInd) = initConcentrations;

% Deal with reactions for which there are no initial concentrations
originalBound = -model.lb(excInd);
noInitConcentration = (concentrations == 0 & originalBound > 0);
concentrations(noInitConcentration) = 1000;

biomass = initBiomass;

% Initialize bounds
uptakeBound =  concentrations/(biomass*timeStep);

% Make sure bounds are not higher than what are specified in the model
aboveOriginal = (uptakeBound > originalBound) & (originalBound > 0);
uptakeBound(aboveOriginal) = originalBound(aboveOriginal);
model.lb(excInd) = -uptakeBound;

concentrationMatrix = sparse(concentrations);
biomassVec = biomass;
timeVec(1) = 0;

fprintf('Step number\tBiomass\n');
showprogress(0,'Dynamic FBA analysis in progress ...');
for stepNo = 1:nSteps
    % Run FBA
    sol = optimizeCbModel(model,'max','one');
    mu = sol.f;
    if (sol.stat ~= 1 || mu == 0)
        fprintf('\nNo feasible solution - nutrients exhausted. Biomass:\t %f\n', biomass);
        break;
    end
    uptakeFlux = sol.x(excInd);
    biomass = biomass*exp(mu*timeStep);
    %biomass = biomass*(1+mu*timeStep);
    biomassVec(end+1) = biomass;

    % Update concentrations
    concentrations = concentrations - uptakeFlux/mu*biomass*(1-exp(mu*timeStep));
    %concentrations = concentrations + uptakeFlux*biomass*timeStep;
    concentrations(concentrations <= 0) = 0;
    concentrationMatrix(:,end+1) = sparse(concentrations);

    % Update bounds for uptake reactions
    uptakeBound =  concentrations/(biomass*timeStep);
    % This is to avoid any numerical issues
    uptakeBound(uptakeBound > 1000) = 1000;
    % Figure out if the computed bounds were above the original bounds
    aboveOriginal = (uptakeBound > originalBound) & (originalBound > 0);
    % Revert to original bounds if the rate was too high
    uptakeBound(aboveOriginal) = originalBound(aboveOriginal);
    uptakeBound(abs(uptakeBound) < 1e-9) = 0;

    model.lb(excInd) = -uptakeBound;

    if WAITBAR_TYPE ~= 1
        fprintf('%d\t%f\n',stepNo,biomass);
    end
    showprogress(stepNo/nSteps);
    timeVec(stepNo+1) = stepNo*timeStep;
end

selNonZero = any(concentrationMatrix>0,2);
concentrationMatrix = concentrationMatrix(selNonZero,:);
excRxnNames = excRxnNames(selNonZero);
selPlot = ismember(excRxnNames,plotRxns);

% Plot concentrations as a function of time
clf
subplot(1,2,1);
plot(timeVec,biomassVec);
axis tight
title('Biomass');
subplot(1,2,2);
plot(timeVec,concentrationMatrix(selPlot,:));
axis tight
legend(strrep(excRxnNames(selPlot),'EX_',''));


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright 2014 National Renewable Energy Laboratory and National
% Technology & Engineering Solutions of Sandia, LLC (NTESS).
% Under the terms of Contract DE-NA0003525 with NTESS,
% the U.S. Government retains certain rights in this software.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

classdef responseClass<handle
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % The  ``responseClass`` creates an ``output`` object saved to the MATLAB workspace
    % that contains structures for each instance of a WEC-Sim class (e.g.
    % ``waveClass``, ``bodyClass``, ``constraintClass``, ``ptoClass``,
    % ``cableClass``, ``mooringClass``, etc).
    %
    %.. autoattribute:: objects.responseClass.responseClass
    %
    %.. autoattribute:: objects.responseClass.wave
    %
    %   * ``type`` (`string`) = 'waveType'
    %   * ``time`` (`array`) = [# of time-steps x 1]
    %   * ``elevation`` (`array`) = [# of time-steps x 1]
    %   * ``waveGauge1Elevation`` (`array`) = [# of time-steps x 1] Wave elevation at the location of wave gauge 1
    %   * ``waveGauge2Elevation`` (`array`) = [# of time-steps x 1] Wave elevation at the location of wave gauge 2
    %   * ``waveGauge3Elevation`` (`array`) = [# of time-steps x 1] Wave elevation at the location of wave gauge 3
    %
    %.. autoattribute:: objects.responseClass.bodies
    %
    %   * ``name`` (`string`) = 'bodyName'
    %   * ``time`` (`array`) = [# of time-steps x 1]
    %   * ``position`` (`array`) = [# of time-steps x 6]
    %   * ``velocity`` (`array`) = [# of time-steps x 6]
    %   * ``acceleration`` (`array`) = [# of time-steps x 6]
    %   * ``forceTotal`` (`array`) = [# of time-steps x 6] The sum of all hydrodynamic forces applied to the body
    %   * ``forceExcitation`` (`array`) = [# of time-steps x 6] The sum of the Froude-Krylov excitation force and the mean drift force exerted by the incoming wave on the body
    %   * ``forceRadiationDamping`` (`array`) = [# of time-steps x 6] The negative radiation damping force due to body velocity
    %   * ``forceAddedMass`` (`array`) = [# of time-steps x 6] The negative added mass force due to body acceleration
    %   * ``forceRestoring`` (`array`) = [# of time-steps x 6] The negative sum of the gravity force, buoyant force, the hydrostatic stiffness force, and any moment due to separation between the center of gravity and the center of buoyancy
    %   * ``forceMorisonAndViscous`` (`array`) = [# of time-steps x 6] The negative sum of the Morison element force and the viscous drag force
    %   * ``forceLinearDamping`` (`array`) = [# of time-steps x 6] The negative force due to linear damping and the body velocity
    %
    %   There are 4 additional ``output.bodies`` arrays when using nonlinear hydro and Paraview output:
    %
    %   * ``cellPressures_time`` (`array`) = [# of Paraview time-steps x 1] Nonlinear calculation timeseries
    %   * ``cellPressures_hydrostatic`` (`array`) = [# of Paraview time-steps x # of mesh faces] Hydrostatic pressure on each stl facet
    %   * ``cellPressures_waveLinear`` (`array`) = [# of Paraview time-steps x # of mesh faces] Excitation pressure on each stl facet given zero displacement and the mean free surface
    %   * ``cellPressures_waveNonLinear`` (`array`) = [# of Paraview time-steps x # of mesh faces] Excitation pressure on each stl facet given the instantaneous displacement and instantaneous free surface
    %
    %.. autoattribute:: objects.responseClass.constraints
    %
    %   * ``name`` (`string`) = 'constraintName'
    %   * ``time`` (`array`) = [# of time-steps x 1]
    %   * ``position`` (`array`) = [# of time-steps x 6] The constraint position relative to the initial condition
    %   * ``velocity`` (`array`) = [# of time-steps x 6] The constraint velocity relative to the initial condition
    %   * ``acceleration`` (`array`) = [# of time-steps x 6] The constraint acceleration relative to the initial condition
    %   * ``forceConstraint`` (`array`) = [# of time-steps x 6] The force required to resist motion in the restricted DOFs
    %
    %.. autoattribute:: objects.responseClass.ptos
    %
    %   * ``name`` (`string`) = 'ptoName'
    %   * ``time`` (`array`) = [# of time-steps x 1]
    %   * ``position`` (`array`) = [# of time-steps x 6] The constraint position relative to the initial condition
    %   * ``velocity`` (`array`) = [# of time-steps x 6] The constraint velocity relative to the initial condition
    %   * ``acceleration`` (`array`) = [# of time-steps x 6] The constraint acceleration relative to the initial condition
    %   * ``forceTotal`` (`array`) = [# of time-steps x 6] The sum of the actuation, constraint and internal mechanics forces
    %   * ``forceActuation`` (`array`) = [# of time-steps x 6] The prescribed force input to the PTO to control its motion
    %   * ``forceConstraint`` (`array`) = [# of time-steps x 6] The force required to resist motion in the restricted DOFs
    %   * ``forceInternalMechanics`` (`array`) = [# of time-steps x 6] The net force in the joint DOF due to stiffness and damping
    %   * ``powerInternalMechanics`` (`array`) = [# of time-steps x 6] The net power lost in the joint DOF due to stiffness and damping
    %
    %.. autoattribute:: objects.responseClass.cables
    %
    %   * ``name`` (`string`) = 'cableName'
    %   * ``time`` (`array`) = [# of time-steps x 1]
    %   * ``position`` (`array`) = [# of time-steps x 6]
    %   * ``velocity`` (`array`) = [# of time-steps x 6]
    %   * ``acceleration`` (`array`) = [# of time-steps x 6]
    %   * ``forceTotal`` (`array`) = [# of time-steps x 6] The sum of the actuation and constraint forces
    %   * ``forceActuation`` (`array`) = [# of time-steps x 6] The cable tension
    %   * ``forceConstraint`` (`array`) = [# of time-steps x 6] The force required to resist motion in the restricted DOFs
    %
    %.. autoattribute:: objects.responseClass.mooring
    %
    %   * ``position`` (`array`) = [# of time-steps x 6]
    %   * ``velocity`` (`array`) = [# of time-steps x 6]
    %   * ``forceMooring`` (`array`) = [# of time-steps x 6] The sum of the stiffness, damping and pretension forces applied on the body by the mooring
    %
    %.. autoattribute:: objects.responseClass.moorDyn
    %
    %   * ``Lines`` (`struct`) = [1 x 1] Contains the time and fairlead tensions
    %   * ``Line#`` (`struct`) = [1 x 1] One structure for each mooring line: Line1, Line2, etc. Each line structure contains node positions in x, y, z and segment tensions
    %
    %.. autoattribute:: objects.responseClass.ptosim
    %
    %   * ``time`` (`struct`) = [# of time-steps x 1] Simulation timeseries
    %
    %   There are additional ``output.ptosim`` structs corresponding to the Simulink blocks used:
    %
    %   * ``pistonCF`` (`struct`) = [1 x # of pistons] Structure containing timeseries of compressible fluid piston properties including absolute power, force, position, velocity
    %   * ``pistonNCF`` (`array`) = [1 x # of pistons] Structure containing timeseries of non-compressible fluid piston properties including absolute power, force, top pressure and bottom pressure
    %   * ``checkValve`` (`struct`) = [1 x # of valves] Structure containing timeseries of check valve properies including volume flow rate
    %   * ``valve`` (`struct`) = [1 x # of valves] Structure containing timeseries of valve properties including volume flow rate
    %   * ``accumulator`` (`struct`) = [1 x # of accumulators] Structure containing timeseries of accumulator properties including pressure and volume
    %   * ``hydraulicMotor`` (`struct`) = [1 x # of motors] Structure containing timeseries of hydraulic motor properties including angular velocity and volume flow rate
    %   * ``rotaryGenerator`` (`struct`) = [1 x # of generators] Structure containing timeseries of rotary generator properties including electrical power and generated power
    %   * ``pmLinearGenerator`` (`struct`) = [1 x # of generators] Structure containing timeseries of permanent magnet linear generator properties including absolute power, force, friction force, current, voltage, velocity and electrical power
    %   * ``pmRotaryGenerator`` (`struct`) = [1 x # of generators] Structure containing timeseries of permanent magnet rotary generator properties including absolute power, force, friction force, current, voltage, velocity and electrical power
    %   * ``motionMechanism`` (`struct`) = [1 x # of mechanisms] Structure containing timeseries of motion mechanism properties including PTO torque, angular position and angular velocity
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    properties (SetAccess = 'public', GetAccess = 'public')
        bodies              = struct()     % This property contains a structure for each instance of the ``bodyClass`` (i.e. for each Body block)
        cables              = struct()     % This property contains a structure for each instance of the ``cableClass`` (i.e. for each Cable block)
        constraints         = struct()     % This property contains a structure for each instance of the ``constraintClass`` (i.e. for each Constraint block). Constraint motion is relative from frame F to frame B. Constraint forces act on frame F.
        moorDyn             = struct()     % This property contains a structure for each instance of the ``mooringClass`` using MoorDyn (i.e. for each MoorDyn block)
        mooring             = struct()     % This property contains a structure for each instance of the ``mooringClass`` using the mooring matrix (i.e. for each MooringMatrix block)
        ptos                = struct()     % This property contains a structure for each instance of the ``ptoClass`` (i.e. for each PTO block). PTO motion is relative from frame F to frame B. PTO forces act on frame F.
        ptosim              = struct()     % This property contains a structure for each instance of the ``ptoSimClass`` (i.e. for each PTO-Sim block).
        wave                = struct()     % This property contains a structure for each instance of the ``waveClass``
    end

    methods (Access = 'public')
        function obj = responseClass(bodiesOutput,ptosOutput,constraintsOutput,ptosimOutput,cablesOutput,mooringOutput,waveOutput)
            % This method initializes the ``responseClass``, reads
            % output from each instance of a WEC-Sim class (e.g.
            % ``waveClass``, ``bodyClass``, ``ptoClass``, ``mooringClass``, etc)
            % , and saves the response to an ``output`` object.
            %
            % Returns
            % ------------
            %     output : obj
            %         responseClass object
            %

            % Wave
            obj.wave.type = waveOutput.type;
            obj.wave.time = waveOutput.waveAmpTime(:,1);
            obj.wave.elevation = waveOutput.waveAmpTime(:,2);

            % Bodies
            signals = {'position','velocity','acceleration','forceTotal','forceExcitation','forceRadiationDamping','forceAddedMass','forceRestoring','forceMorisonAndViscous','forceLinearDamping'};
            for ii = 1:length(bodiesOutput)
                obj.bodies(ii).name = bodiesOutput(ii).name;
                obj.bodies(ii).time = bodiesOutput(ii).time;
                obj.bodies(ii).centerGravity   = bodiesOutput(ii).centerGravity;
                for jj = 1:length(signals)
                    obj.bodies(ii).(signals{jj}) = bodiesOutput(ii).signals.values(:, (jj-1)*6+1:(jj-1)*6+6);
                end
                if(bodiesOutput(ii).yaw==1)
                    for t = 1:length(obj.wave.time)
                        % convert kinematic data from global frame to local frame (for use with yaw when yaw displacements may be large).
                        rotMatYaw = eulXYZ2RotMat(0, 0, obj.bodies(ii).position(t,6));
                        rotMatGlobal = eulXYZ2RotMat(obj.bodies(ii).position(t,4), obj.bodies(ii).position(t,5), obj.bodies(ii).position(t,6));
                        rotMatLocal = rotMatYaw.' * rotMatGlobal;
                        [phiLoc, thetaLoc, psiLoc] = rotMatXYZ2Eul(rotMatLocal); % get orientation in local frame
                        % position in local frame
                        obj.bodies(ii).positionLocal(t,1:3) = rotMatYaw.'*obj.bodies(ii).position(t,1:3).'; % rotate linear position vector from global to local frame
                        obj.bodies(ii).positionLocal(t,4:6) = [phiLoc, thetaLoc, psiLoc];
                        % velocity in local frame
                        obj.bodies(ii).velocityLocal(t,1:3) = rotMatYaw.'*obj.bodies(ii).velocity(t,1:3).';
                        obj.bodies(ii).velocityLocal(t,4:6) = rotMatYaw.'*obj.bodies(ii).velocity(t,4:6).';
                        % acceleration in local frame
                        obj.bodies(ii).accelerationLocal(t,1:3) = rotMatYaw.'*obj.bodies(ii).acceleration(t,1:3).';
                        obj.bodies(ii).accelerationLocal(t,4:6) = rotMatYaw.'*obj.bodies(ii).acceleration(t,4:6).';
                    end
                end
                if ~isempty(bodiesOutput(ii).hspressure)
                    obj.bodies(ii).cellPressures_time = bodiesOutput(ii).hspressure.time;
                    obj.bodies(ii).cellPressures_hydrostatic   = bodiesOutput(ii).hspressure.signals.values;
                    obj.bodies(ii).cellPressures_waveLinear    = bodiesOutput(ii).wpressurel.signals.values;
                    obj.bodies(ii).cellPressures_waveNonLinear = bodiesOutput(ii).wpressurenl.signals.values;
                else
                    obj.bodies(ii).cellPressures_time = [];
                    obj.bodies(ii).cellPressures_hydrostatic   = [];
                    obj.bodies(ii).cellPressures_waveLinear    = [];
                    obj.bodies(ii).cellPressures_waveNonLinear = [];
                end
            end
            % PTOs
            if isstruct(ptosOutput)
                signals = {'position','velocity','acceleration','forceTotal','forceActuation','forceConstraint','forceInternalMechanics','powerInternalMechanics'};
                for ii = 1:length(ptosOutput)
                    obj.ptos(ii).name = ptosOutput(ii).name;
                    obj.ptos(ii).time = ptosOutput(ii).time;
                    for jj = 1:length(signals)
                        obj.ptos(ii).(signals{jj}) =  ptosOutput(ii).signals.values(:,(jj-1)*6+1:(jj-1)*6+6);
                    end
                end
            end
            % Constraints
            if isstruct(constraintsOutput)
                signals = {'position','velocity','acceleration','forceConstraint'};
                for ii = 1:length(constraintsOutput)
                    obj.constraints(ii).name = constraintsOutput(ii).name;
                    obj.constraints(ii).time = constraintsOutput(ii).time;
                    for jj = 1:length(signals)
                        obj.constraints(ii).(signals{jj}) = constraintsOutput(ii).signals.values(:, (jj-1)*6+1:(jj-1)*6+6);
                    end
                end
            end
            % Mooring
            if isstruct(mooringOutput)
                signals = {'position','velocity','forceMooring'};
                for ii = 1:length(mooringOutput)
                    obj.mooring(ii).name = mooringOutput(ii).name;
                    obj.mooring(ii).time = mooringOutput(ii).time;
                    for jj = 1:length(signals)
                        obj.mooring(ii).(signals{jj}) = mooringOutput(ii).signals.values(:, (jj-1)*6+1:(jj-1)*6+6);
                    end
                end
            end
            % Cables
            if isstruct(cablesOutput)
                signals = {'position','velocity','acceleration','forceTotal','forceActuation',...
                    'forceConstraint'};
                for ii = 1:length(cablesOutput)
                    obj.cables(ii).name = cablesOutput(ii).name;
                    obj.cables(ii).time = cablesOutput(ii).time;
                    for jj = 1:length(signals)
                        obj.cables(ii).(signals{jj}) = cablesOutput(ii).signals.values(:,(jj-1)*6+1:(jj-1)*6+6);
                    end
                end
            end
            % PTO-Sim
            if isstruct(ptosimOutput)
                %names = {'HydPistonCompressible','GasHydAccumulator','RectifyingCheckValve','HydraulicMotorV2','ElectricMachineEC'};
                hydPistonCompressibleSignals = {'pressureA','forcePTO','pressureB'};
                gasHydAccumulatorSignals = {'pressure','flowRate'};
                rectifyingCheckValveSignals = {'flowRateA','flowRateB','flowRateC','flowRateD'};
                hydraulicMotorSignals = {'shaftSpeed','torque','deltaP','flowRate'};
                electricMachineECSignals = {'torqueEM','shaftSpeed','current','voltage'};
                linearCrankSignals = {'ptoTorque','angPosition','angVelocity'};
                adjustableRodSignals = {'ptoTorque','angPosition','angVelocity'};
                checkValveSignals = {'flowCheckValve','deltaPCheckValve'};
                linearGeneratorSignals = {'absPower','force','fricForce','Ia','Ib','Ic','Va','Vb','Vc','elecPower','vel'};
                rotaryGeneratorSignals = {'absPower','Torque','fricTorque','Ia','Ib','Ic','Va','Vb','Vc','elecPower','vel'};

                for ii = 1:length(ptosimOutput)
                    obj.ptosim(ii).name = ptosimOutput(ii).name;
                    obj.ptosim(ii).time = ptosimOutput(ii).time;
                    obj.ptosim(ii).type = ptosimOutput(ii).type;
                    if ptosimOutput(ii).type == 1
                        signals = electricMachineECSignals;
                    elseif ptosimOutput(ii).type == 2
                        signals = hydPistonCompressibleSignals;
                    elseif ptosimOutput(ii).type == 3
                        signals = gasHydAccumulatorSignals;
                    elseif ptosimOutput(ii).type == 4
                        signals = rectifyingCheckValveSignals;
                    elseif ptosimOutput(ii).type == 5
                        signals = hydraulicMotorSignals;
                    elseif ptosimOutput(ii).type == 6
                        signals = linearCrankSignals;
                    elseif ptosimOutput(ii).type == 7
                        signals = adjustableRodSignals;
                    elseif ptosimOutput(ii).type == 8
                        signals = checkValveSignals;
                    elseif ptosimOutput(ii).type == 9
                        signals = linearGeneratorSignals;
                    elseif ptosimOutput(ii).type == 10
                        signals = rotaryGeneratorSignals;
                    end
                    for jj = 1:length(signals)
                        obj.ptosim(ii).(signals{jj}) = ptosimOutput(ii).signals.values(:,jj);
                    end
                end
            end
        end

        function obj = loadMoorDyn(obj,linesNum)
            % This method reads MoorDyn outputs for each instance of the
            % ``mooringClass``
            %
            % Parameters
            % ------------
            %     linesNum : integer
            %         the number of MoorDyn lines
            %

            arguments
                obj
                linesNum (1,1) double {mustBeInteger}
            end

            % load Lines.out
            filename = './Mooring/Lines.out';
            fid = fopen(filename, 'r');
            header = strsplit(fgetl(fid));
            data = dlmread(filename,'',1,0);
            tmp = size(data);
            ncol = tmp(2);clear tmp
            for icol=1:ncol
               eval(['obj.moorDyn.Lines.' header{icol} ' = data(:,' num2str(icol) ');']);
            end
            fclose(fid);
            % load Line#.out
            for iline=1:linesNum
                eval(['obj.moorDyn.Line' num2str(iline) '=struct();']);
                filename = ['./Mooring/Line' num2str(iline) '.out'];
                try
                    fid = fopen(filename);
                    header = strsplit(strtrim(fgetl(fid)));
                    data = dlmread(filename,'',1,0);
                    tmp = size(data);
                    ncol = tmp(2);clear tmp
                    for icol=1:ncol
                        eval(['obj.moorDyn.Line' num2str(iline) '.' header{icol} ' = data(:,' num2str(icol) ');']);
                    end
                    fclose(fid);
                catch
                    fprintf('\n No moorDyn *.out file saved for Line%u\n',iline);
                end
            end
        end

        function plotResponse(obj,bodyNum,comp)
            % This method plots the response of a body for a given DOF.
            %
            % Parameters
            % ------------
            %     bodyNum : integer
            %         the body number to plot
            %
            %     comp : integer
            %         the response component (i.e. dof) to be plotted (e.g. 1-6)
            %

            arguments
                obj
                bodyNum (1,1) double {mustBeInteger}
                comp (1,1) double {mustBeMember(comp,1:6)}
            end

            DOF = {'Surge','Sway','Heave','Roll','Pitch','Yaw'};
            t=obj.bodies(bodyNum).time;
            if comp < 4
                pos=obj.bodies(bodyNum).position(:,comp)-obj.bodies(bodyNum).centerGravity(comp);
            else
                pos=obj.bodies(bodyNum).position(:,comp);
            end
            vel=obj.bodies(bodyNum).velocity(:,comp);
            acc=obj.bodies(bodyNum).acceleration(:,comp);
            figure()
            plot(t,pos,'k-',...
                t,vel,'b-',...
                t,acc,'r-')
            legend({'position','velocity','acceleration'})
            xlabel('Time (s)')
            ylabel('Response in (m) or (radians)')
            title(['body' num2str(bodyNum) ' (' obj.bodies(bodyNum).name ') ' DOF{comp} ' Response'])
            clear t pos vel acc i
        end

        function plotForces(obj,bodyNum,comp)
            % This method plots the forces on a body for a given DOF.
            %
            % Parameters
            % ------------
            %     bodyNum : integer
            %         the body number to plot
            %
            %     comp : integer
            %         the force component (i.e. dof) to be plotted (e.g. 1-6)
            %

            arguments
                obj
                bodyNum (1,1) double {mustBeInteger}
                comp (1,1) double {mustBeMember(comp,1:6)}
            end

            DOF = {'Surge','Sway','Heave','Roll','Pitch','Yaw'};
            t=obj.bodies(bodyNum).time;
            FT=obj.bodies(bodyNum).forceTotal(:,comp);
            FE=obj.bodies(bodyNum).forceExcitation(:,comp);
            FRD=-1*obj.bodies(bodyNum).forceRadiationDamping(:,comp);
            FAM=-1*obj.bodies(bodyNum).forceAddedMass(:,comp);
            FR=-1*obj.bodies(bodyNum).forceRestoring(:,comp);
            FMV=-1*obj.bodies(bodyNum).forceMorisonAndViscous(:,comp);
            FLD=-1*obj.bodies(bodyNum).forceLinearDamping(:,comp);
            figure();
            plot(t,FT,...
                t,FE,...
                t,FRD,...
                t,FAM,...
                t,FR,...
                t,FMV,...
                t,FLD)
            legend('forceTotal','forceExcitation','forceRadiationDamping','forceAddedMass','forceRestoring','forceViscous','forceLinearDamping')
            xlabel('Time (s)')
            ylabel('Force (N) or Torque (N*m)')
            title(['body' num2str(bodyNum) ' (' obj.bodies(bodyNum).name ') ' DOF{comp} ' Forces'])
            clear t FT FE FRD FR FV FM i
        end

        function saveViz(obj,simu,body,waves,options)
            % This method plots and saves the wave elevation and body
            % geometry over time to visualize the waves and response
            %
            % Parameters
            % ------------
            %     simu :
            %         simulationClass object
            %
            %     body :
            %         bodyClass object
            %
            %     waves :
            %         waveClass object
            %
            %     options
            %         axisLimits : 1x6 float matrix
            %             x, y, and z-bounds of figure axes in the format:
            %             [min x, max x, min y, max y, min z, max z]
            %             Default = [-simu.domainSize/2 simu.domainSize/2
            %             -simu.domainSize/2 simu.domainSize/2 -waves.waterDepth maximumHeight]
            %         timesPerFrame : float
            %             number of simulation time steps per video frame
            %             (higher number decreases computation time)
            %             Default = 1
            %         startEndTime : 1x2 float matrix
            %             Array defining the start and end times of the
            %             visualization
            %             Default = [min(t) max(t)]
            %         saveSetting : integer
            %             0 = video, 1 = gif. Default = 0
            %

            arguments
                obj
                simu
                body
                waves
                options.axisLimits (1,6) double {mustBeReal, mustBeNonNan, mustBeFinite} = [-simu.domainSize/2 simu.domainSize/2 -simu.domainSize/2 simu.domainSize/2 -waves.waterDepth -999];
                options.timesPerFrame (1,1) double {mustBeInteger, mustBePositive} = 1;
                options.startEndTime (1,2) double {mustBeReal, mustBeNonnegative, mustBeNonNan} = [0 0];
                options.saveSetting (1,1) {mustBeMember(options.saveSetting,0:1)} = 0;
            end
            % Set time vector
            t = obj.wave.time(1:options.timesPerFrame*round(simu.dtOut/simu.dt,0):end,1);
            if isequal(options.startEndTime, [0 0])
                options.startEndTime = [min(t) max(t)];
            end
            % Create grid using provided x and y coordinates
            x = linspace(options.axisLimits(1),options.axisLimits(2),200);
            y = linspace(options.axisLimits(3),options.axisLimits(4),200);
            [X,Y] = meshgrid(x,y);
            % Initialize maximum body height
            maxHeight = 0;
            % Read in data for each body
            for ibod=1:length(obj.bodies)
                % Read and assign geometry data
                if isempty(body(ibod).geometry.vertex)
                    % Import body geometry for linear hydro cases
                    body(ibod).importBodyGeometry(simu.domainSize);
                end
                bodyMesh(ibod).Points = body(ibod).geometry.vertex;
                bodyMesh(ibod).Conns = body(ibod).geometry.face;
                % Read changes and assign angles and position changes over time
                bodyMesh(ibod).deltaPos = [obj.bodies(ibod).position(1:options.timesPerFrame:end,1)-obj.bodies(ibod).position(1,1),...
                obj.bodies(ibod).position(1:options.timesPerFrame:end,2)-obj.bodies(ibod).position(1,2),...
                obj.bodies(ibod).position(1:options.timesPerFrame:end,3)-obj.bodies(ibod).position(1,3)];
                % Save maximum body height
                if max(bodyMesh(ibod).Points(:,3))+max(bodyMesh(ibod).deltaPos(:,3)) > maxHeight
                    maxHeight = max(bodyMesh(ibod).Points(:,3))+max(bodyMesh(ibod).deltaPos(:,3));
                end
            end
            if options.axisLimits(6) == -999
                options.axisLimits(6) = maxHeight;
            end
            if options.saveSetting == 0
                % Create video file and open it for writing
                video = VideoWriter('waveViz.avi');
                video.FrameRate = 1/(simu.dtOut*options.timesPerFrame);
                open(video);
            elseif options.saveSetting == 1
                % Create the gif file
                gifFilename = 'waveViz.gif';
            end
            % Initialize figure and image counter
            figure();
            imageCount = 0;
            for i=1:length(t)
                if t(i) >= options.startEndTime(1) && t(i) <= options.startEndTime(2)
                    imageCount = imageCount + 1;
                    for ibod = 1:length(obj.bodies)
                        % Apply rotation to each point
                        rotMat = eulXYZ2RotMat(obj.bodies(ibod).position(1+options.timesPerFrame*(i-1),4), ...
                            obj.bodies(ibod).position(1+options.timesPerFrame*(i-1),5), ...
                            obj.bodies(ibod).position(1+options.timesPerFrame*(i-1),6));
                        for ipts=1:length(bodyMesh(ibod).Points(:,1))
                            bodyMesh(ibod).rotation(ipts,:) = (rotMat*bodyMesh(ibod).Points(ipts,:).').';
                        end

                        % Calculate full position changes due to rotation,
                        % translation, and center of gravity
                        bodyMesh(ibod).pointsNew = bodyMesh(ibod).rotation + bodyMesh(ibod).deltaPos(i,:) + body(ibod).centerGravity.';

                        % Create and plot final triangulation of geometry with applied changes
                        bodFinal = triangulation(bodyMesh(ibod).Conns,bodyMesh(ibod).pointsNew);
                        trisurf(bodFinal,'FaceColor',[1 1 0],'EdgeColor','k','EdgeAlpha',.2)
                        hold on
                    end
                    % Create and wave elevation grid
                    Z = waveElevationGrid(waves, t(i), X, Y, simu.dtOut, simu.gravity);
                    surf(X,Y,Z,'FaceAlpha',.85,'EdgeColor','none')
                    hold on
                    % Display seafloor
                    seaFloor = -waves.waterDepth*ones(size(X, 1));
                    surf(X,Y,seaFloor,'FaceColor',[.4 .4 0],'EdgeColor','none');
                    hold on
                    % Time visual
                    nDecimals = max(0,ceil(-log10(simu.dtOut*options.timesPerFrame)));
                    nLeading = ceil(log10(max(t)));
                    tAnnot = sprintf(['time = %' num2str(nDecimals+nLeading+1) '.' num2str(nDecimals) 'f s'],t(i));
                    % Settings and labels
                    caxis([min(waves.waveAmpTime(:,2)) max(waves.waveAmpTime(:,2))])
                    colormap winter
                    c = colorbar;
                    ylabel(c, 'Wave Elevation (m)')
                    title({'Wave Elevation and Geometry Visualization',tAnnot})
                    xlabel('x(m)')
                    ylabel('y(m)')
                    zlabel('z(m)')
                    daspect([1 1 1])
                    axis(options.axisLimits)
                    % Create figure while iterating through time loop
                    drawnow;
                    % Capture figure for saving
                    frame = getframe(gcf);
                    if options.saveSetting == 0
                        % Save to video
                        writeVideo(video,frame);
                    elseif options.saveSetting == 1
                        % Save to gif
                        im = frame2im(frame);
                        [imind,cm] = rgb2ind(im,256);
                        if imageCount == 1
                            imwrite(imind,cm,gifFilename,'gif', 'Loopcount',inf);
                        else
                            imwrite(imind,cm,gifFilename,'gif','WriteMode','append','DelayTime',simu.dtOut);
                        end
                    end
                    hold off
                end
            end
            % Close video file
            if options.saveSetting == 0
                close(video);
            end
        end

        function writeText(obj)
            % This method writes WEC-Sim outputs to a (ASCII) text file.
            % This method is executed by specifying ``simu.outputtxt=1``
            % in the ``wecSimInputFile.m``.
            filename = ['output/wave.txt'];
            fid = fopen(filename,'w+');
            header = {'time','elevation'};
            for ii=1:length(header)
                tmp(ii) = length(header{ii});
            end
            numChar = max(tmp)+2; clear tmp;
            header_fmt = ['%' num2str(numChar) 's '];
            ncols = length(header);
            tmp = size(obj.wave.time);
            nrows = tmp(1); clear tmp;
            data_fmt = [repmat('%10.5f ',1,ncols) '\n'];
            data = zeros(nrows,ncols);
            data(:,1) = obj.wave.time;
            data(:,2) = obj.wave.elevation;
            if ncols > 2
                for ii = 3:length(header)
                    eval(['data(:,' num2str(ii) ') = obj.wave.' header{ii} ';']);
                end
            end
            for ii = 1:length(header)
                fprintf(fid,header_fmt,header{ii});
            end
            fprintf(fid,'\n');
            fprintf(fid,data_fmt,data');
            fclose(fid);
            % bodies
            signals = {'position','velocity','acceleration','forceTotal','forceExcitation','forceRadiationDamping','forceAddedMass','forceRestoring','forceMorisonAndViscous','forceLinearDamping'};
            header = {'time', ...
                      'position_1'               ,'position_2'               ,'position_3'               ,'position_4'               ,'position_5'               ,'position_6'               , ...
                      'velocity_1'               ,'velocity_2'               ,'velocity_3'               ,'velocity_4'               ,'velocity_5'               ,'velocity_6'               , ...
                      'acceleration_1'           ,'acceleration_2'           ,'acceleration_3'           ,'acceleration_4'           ,'acceleration_5'           ,'acceleration_6'           , ...
                      'forceTotal_1'             ,'forceTotal_2'             ,'forceTotal_3'             ,'forceTotal_4'             ,'forceTotal_5'             ,'forceTotal_6'             , ...
                      'forceExcitation_1'        ,'forceExcitation_2'        ,'forceExcitation_3'        ,'forceExcitation_4'        ,'forceExcitation_5'        ,'forceExcitation_6'        , ...
                      'forceRadiationDamping_1'  ,'forceRadiationDamping_2'  ,'forceRadiationDamping_3'  ,'forceRadiationDamping_4'  ,'forceRadiationDamping_5'  ,'forceRadiationDamping_6'  , ...
                      'forceAddedMass_1'         ,'forceAddedMass_2'         ,'forceAddedMass_3'         ,'forceAddedMass_4'         ,'forceAddedMass_5'         ,'forceAddedMass_6'         , ...
                      'forceRestoring_1'         ,'forceRestoring_2'         ,'forceRestoring_3'         ,'forceRestoring_4'         ,'forceRestoring_5'         ,'forceRestoring_6'         , ...
                      'forceMorisonAndViscous_1' ,'forceMorisonAndViscous_2' ,'forceMorisonAndViscous_3' ,'forceMorisonAndViscous_4' ,'forceMorisonAndViscous_5' ,'forceMorisonAndViscous_6' , ...
                      'forceLinearDamping_1'     ,'forceLinearDamping_2'     ,'forceLinearDamping_3'     ,'forceLinearDamping_4'     ,'forceLinearDamping_5'     ,'forceLinearDamping_6'     };
            for ii=1:length(signals)
                tmp(ii) = length(signals{ii});
            end
            numChar = max(tmp)+2; clear tmp;
            ncols = 1 + length(signals)*6;
            tmp = size(obj.bodies(1).time);
            nrows = tmp(1); clear tmp;
            header_fmt = ['%' num2str(numChar) 's '];
            data_fmt = [repmat('%10.5f ',1,ncols) '\n'];
            for ibod = 1:length(obj.bodies)
                filename = ['output/body' num2str(ibod) '_' obj.bodies(ibod).name '.txt'];
                fid = fopen(filename,'w+');
                data = zeros(nrows,ncols);
                data(:,1) = obj.bodies(ibod).time;
                fprintf(fid,header_fmt,header{1});
                for isignal=1:length(signals)
                    for idof = 1:6
                        fprintf(fid,header_fmt,header{1 + (isignal-1)*6+idof});
                    end
                    data(:, 1+(isignal-1)*6+1:1+(isignal-1)*6+6) = obj.bodies(ibod).(signals{isignal});
                end
                fprintf(fid,'\n');
                fprintf(fid,data_fmt,data');
                fclose(fid);
                if isfield(obj.bodies(ibod),'cellPressures_hydrostatic')
                    filename = ['output/body' num2str(ibod) '_' obj.bodies(ibod).name '_cellPressure_hydrostatic.txt'];
                    fid = fopen(filename,'w+');
                    header_2 = {'time'};
                    tmp = size(obj.bodies(ibod).cellPressures_hydrostatic);
                    nrows2 = tmp(1);
                    ncols2 = tmp(2)+1;
                    for icell=1:ncols2-1
                        header_2{icell+1} = ['cell_' num2str(icell)];
                    end
                    tmp = length(header_2{end});
                    numChar = max(tmp)+2; clear tmp;
                    header_fmt_2 = ['%' num2str(numChar) 's '];
                    data_fmt_2 = [repmat('%10.5f ',1,ncols2) '\n'];
                    data = zeros(nrows2,ncols2);
                    data(:,1) = obj.bodies(ibod).cellPressures_time;
                    data(:,2:end) = obj.bodies(ibod).cellPressures_hydrostatic;
                    for jj = 1:length(header_2)
                        fprintf(fid,header_fmt_2,header_2{jj});
                    end
                    fprintf(fid,'\n');
                    fprintf(fid,data_fmt_2,data');
                    fclose(fid);
                    % wave linear
                    filename = ['output/body' num2str(ibod) '_' obj.bodies(ibod).name '_cellPressure_waveLinear.txt'];
                    fid = fopen(filename,'w+');
                    data = zeros(nrows2,ncols2);
                    data(:,1) = obj.bodies(ibod).cellPressures_time;
                    data(:,2:end) = obj.bodies(ibod).cellPressures_waveLinear;
                    for jj = 1:length(header_2)
                        fprintf(fid,header_fmt_2,header_2{jj});
                    end
                    fprintf(fid,'\n');
                    fprintf(fid,data_fmt_2,data');
                    fclose(fid);
                    % wave nonlinear
                    filename = ['output/body' num2str(ibod) '_' obj.bodies(ibod).name '_cellPressure_waveNonLinear.txt'];
                    fid = fopen(filename,'w+');
                    data = zeros(nrows2,ncols2);
                    data(:,1) = obj.bodies(ibod).cellPressures_time;
                    data(:,2:end) = obj.bodies(ibod).cellPressures_waveNonLinear;
                    for jj = 1:length(header_2)
                        fprintf(fid,header_fmt_2,header_2{jj});
                    end
                    fprintf(fid,'\n');
                    fprintf(fid,data_fmt_2,data');
                    fclose(fid);
                end
            end
            % ptos
            if isfield(obj.ptos,'name')
                signals = {'position','velocity','acceleration','forceTotal','forceActuation','forceConstraint','forceInternalMechanics','powerInternalMechanics'};
                header = {'time', ...
                          'position_1'              ,'position_2'              ,'position_3'              ,'position_4'              ,'position_5'              ,'position_6'              , ...
                          'velocity_1'              ,'velocity_2'              ,'velocity_3'              ,'velocity_4'              ,'velocity_5'              ,'velocity_6'              , ...
                          'acceleration_1'          ,'acceleration_2'          ,'acceleration_3'          ,'acceleration_4'          ,'acceleration_5'          ,'acceleration_6'          , ...
                          'forceTotal_1'            ,'forceTotal_2'            ,'forceTotal_3'            ,'forceTotal_4'            ,'forceTotal_5'            ,'forceTotal_6'            , ...
                          'forceActuation_1'        ,'forceActuation_2'        ,'forceActuation_3'        ,'forceActuation_4'        ,'forceActuation_5'        ,'forceActuation_6'        , ...
                          'forceConstraint_1'       ,'forceConstraint_2'       ,'forceConstraint_3'       ,'forceConstraint_4'       ,'forceConstraint_5'       ,'forceConstraint_6'       , ...
                          'forceInternalMechanics_1','forceInternalMechanics_2','forceInternalMechanics_3','forceInternalMechanics_4','forceInternalMechanics_5','forceInternalMechanics_6', ...
                          'powerInternalMechanics_1','powerInternalMechanics_2','powerInternalMechanics_3','powerInternalMechanics_4','powerInternalMechanics_5','powerInternalMechanics_6', };
                for ii=1:length(signals)
                    tmp(ii) = length(signals{ii});
                end
                numChar = max(tmp)+2; clear tmp;
                ncols = 1 + length(signals)*6;
                tmp = size(obj.ptos(1).time);
                nrows = tmp(1); clear tmp;
                header_fmt = ['%' num2str(numChar) 's '];
                data_fmt = [repmat('%10.5f ',1,ncols) '\n'];
                for ipto = 1:length(obj.ptos)
                    filename = ['output/pto' num2str(ipto) '_' obj.ptos(ipto).name '.txt'];
                    fid = fopen(filename,'w+');
                    data = zeros(nrows,ncols);
                    data(:,1) = obj.ptos(ipto).time;
                    fprintf(fid,header_fmt,header{1});
                    for isignal=1:length(signals)
                        for idof = 1:6
                            fprintf(fid,header_fmt,header{1 + (isignal-1)*6+idof});
                        end
                        data(:, 1+(isignal-1)*6+1:1+(isignal-1)*6+6) = obj.ptos(ipto).(signals{isignal});
                    end
                    fprintf(fid,'\n');
                    fprintf(fid,data_fmt,data');
                    fclose(fid);
                end
            end
            % constraints
            if isfield(obj.constraints,'name')
                signals = {'position','velocity','acceleration','forceConstraint'};
                header = {'time', ...
                          'position_1'       ,'position_2'       ,'position_3'       ,'position_4'       ,'position_5'       ,'position_6'       , ...
                          'velocity_1'       ,'velocity_2'       ,'velocity_3'       ,'velocity_4'       ,'velocity_5'       ,'velocity_6'       , ...
                          'acceleration_1'   ,'acceleration_2'   ,'acceleration_3'   ,'acceleration_4'   ,'acceleration_5'   ,'acceleration_6'   , ...
                          'forceConstraint_1','forceConstraint_2','forceConstraint_3','forceConstraint_4','forceConstraint_5','forceConstraint_6'     };
                for ii=1:length(signals)
                    tmp(ii) = length(signals{ii});
                end
                numChar = max(tmp)+2; clear tmp;
                ncols = 1 + length(signals)*6;
                tmp = size(obj.constraints(1).time);
                nrows = tmp(1); clear tmp;
                header_fmt = ['%' num2str(numChar) 's '];
                data_fmt = [repmat('%10.5f ',1,ncols) '\n'];
                for icon = 1:length(obj.constraints)
                    filename = ['output/constraint' num2str(icon) '_' obj.constraints(icon).name '.txt'];
                    fid = fopen(filename,'w+');
                    data = zeros(nrows,ncols);
                    data(:,1) = obj.constraints(icon).time;
                    fprintf(fid,header_fmt,header{1});
                    for isignal=1:length(signals)
                        for idof = 1:6
                            fprintf(fid,header_fmt,header{1 + (isignal-1)*6+idof});
                        end
                        data(:, 1+(isignal-1)*6+1:1+(isignal-1)*6+6) = obj.constraints(icon).(signals{isignal});
                    end
                    fprintf(fid,'\n');
                    fprintf(fid,data_fmt,data');
                    fclose(fid);
                end
            end
            %ptoSim
            if isfield(obj.ptosim,'time')
                f1 = fields(obj.ptosim);
                count = 1;
                header = {'time'};
                data = obj.ptosim.time;
                for ifld1=1:(length(f1)-1)
                    f2 = fields(obj.ptosim.(f1{ifld1}));
                    for iins = 1:length(obj.ptosim.(f1{ifld1}))
                        for ifld2 = 1:length(f2)
                            count = count+1;
                            header{count} = [f1{ifld1} num2str(iins) '_' f2{ifld2}];
                            data(:,count) = obj.ptosim.(f1{ifld1}).(f2{ifld2});
                        end
                    end
                end
                for ii=1:length(header)
                    tmp(ii) = length(header{ii});
                end
                numChar = max(tmp)+2; clear tmp;
                header_fmt = ['%' num2str(numChar) 's '];
                data_fmt = [repmat('%10.5f ',1,length(header)) '\n'];
                filename = ['output/ptosim.txt'];
                fid = fopen(filename,'w+');
                for ii=1:length(header)
                    fprintf(fid,header_fmt,header{ii});
                end
                fprintf(fid,'\n');
                fprintf(fid,data_fmt,data');
                fclose(fid);
            end
            % mooring
            if isfield(obj.mooring,'name')
                signals = {'position','velocity','forceMooring'};
                header = {'time', ...
                          'position_1'       ,'position_2'       ,'position_3'       ,'position_4'       ,'position_5'       ,'position_6'       , ...
                          'velocity_1'       ,'velocity_2'       ,'velocity_3'       ,'velocity_4'       ,'velocity_5'       ,'velocity_6'       , ...
                          'forceMooring_1','forceMooring_2','forceMooring_3','forceMooring_4','forceMooring_5','forceMooring_6'     };
                for ii=1:length(signals)
                    tmp(ii) = length(signals{ii});
                end
                numChar = max(tmp)+2; clear tmp;
                ncols = 1 + length(signals)*6;
                tmp = size(obj.mooring(1).time);
                nrows = tmp(1); clear tmp;
                header_fmt = ['%' num2str(numChar) 's '];
                data_fmt = [repmat('%10.5f ',1,ncols) '\n'];
                for imoor = 1:length(obj.mooring)
                    filename = ['output/mooring' num2str(imoor) '_' obj.mooring(imoor).name '.txt'];
                    fid = fopen(filename,'w+');
                    data = zeros(nrows,ncols);
                    data(:,1) = obj.mooring(imoor).time;
                    fprintf(fid,header_fmt,header{1});
                    for isignal=1:length(signals)
                        for idof = 1:6
                            fprintf(fid,header_fmt,header{1 + (isignal-1)*6+idof});
                        end
                        data(:, 1+(isignal-1)*6+1:1+(isignal-1)*6+6) = obj.mooring(imoor).(signals{isignal});
                    end
                    fprintf(fid,'\n');
                    fprintf(fid,data_fmt,data');
                    fclose(fid);
                end
            end
        end
    end
end


function [meanDoseV, stdDoseV, DVHm, volsV, shiftXv, shiftYv, shiftZv] = simulate_organ_motion(planC,structNumV,doseNum, igrtFlag)
%function [DVH_out, DVHm, volsV] = simulate_organ_motion(planC,structNumV,doseNum)
%
%APA, 09/21/2012
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

indexS = planC{end};

%Number of Fractions and Trials
numFractions = 1; % 48
numTrials    = 1; % 200
numStructs   = length(structNumV);

shiftXv = zeros(1,numTrials);
shiftYv = shiftXv;
shiftZv = shiftXv;

structurenamesC = {planC{indexS.structures}.structureName};
rectumIndex = getMatchingIndex('rectum_o',lower(structurenamesC),'exact');
rectalCS = calculate_structure_cross_sectional_area(rectumIndex,planC);
bladderVol = getMatchingIndex('bladder_o',lower(structurenamesC),'exact');

% % Systematic Shifts in cm (Inter-treatment)
% XdispSys_Std = 0; % L-R
% YdispSys_Std = 0; % A-P
% ZdispSys_Std = 0; % S-I

% % Random Shifts in cm (inter-fraction)
% XdispRnd_Std = 1; % L-P
% YdispRnd_Std = 1; % A-P
% ZdispRnd_Std = 1; % S-I

%Distances are in cm
XpdfMean = 0; % L-P
YpdfMean = 0; % A-P
ZpdfMean = 0; % S-I

%Get scan associated with doseNum
scanNum = getAssociatedScan(planC{indexS.dose}(doseNum).assocScanUID, planC);

if isempty(scanNum) %Assume dose is associated with this scan
    scanNum = getStructureAssociatedScan(structNumV(1),planC);
end

%Get reference transformation matrix for doseNum
if ~isempty(scanNum) && isempty(planC{indexS.dose}(doseNum).transM)
    referenceTransM = planC{indexS.scan}(scanNum).transM;
else
    referenceTransM = planC{indexS.dose}(doseNum).transM;
end

%Store the reference doseArray
referenceDoseArray = planC{indexS.dose}(doseNum).doseArray;

%Try and get a binWidth from stateS.  If it doesnt exist, get it from
%the CERROptions file (allows this function to be called outside CERR)
global stateS;
if ~isempty(stateS) && isfield(stateS, 'optS') && isfield(stateS.optS, 'DVHBinWidth') && ~isempty(stateS.optS.DVHBinWidth)
    binWidth = stateS.optS.DVHBinWidth;
else
    optS = CERROptions;
    binWidth = optS.DVHBinWidth;
end

%Compute DVH at given dose
structCount = 0;
for structNum = structNumV
    structCount = structCount + 1;
    [dosesCurrentV{structCount}, volsV{structCount}] = getDVH(structNum, doseNum, planC);
end

%Divide doseArray into fractions
planC{indexS.dose}(doseNum).doseArray = planC{indexS.dose}(doseNum).doseArray / numFractions;

%Get systematic errors
% deltaX_systematic = randn(1, numTrials) * XdispSys_Std + XpdfMean;
% deltaY_systematic = randn(1, numTrials) * YdispSys_Std + YpdfMean;
% deltaZ_systematic = randn(1, numTrials) * ZdispSys_Std + ZpdfMean;

hWait = waitbar(0,'Computing plan robustness...');

if isempty(referenceTransM)
    referenceTransM_matrix = eye(4);
else
    referenceTransM_matrix = referenceTransM;
end

%Obtain DVH calculation points in blocks
optS    = planC{indexS.CERROptions};

ROIImageSize = [planC{indexS.scan}(scanNum).scanInfo(1).sizeOfDimension1  planC{indexS.scan}(scanNum).scanInfo(1).sizeOfDimension2];

deltaY = planC{indexS.scan}(scanNum).scanInfo(1).grid1Units;

%Get raster segments for structure.
structCount = 0;
for structNum = structNumV

    structCount = structCount + 1;

    [segmentsM, planC, isError] = getRasterSegments(structNum, planC);

    if isempty(segmentsM)
        isError = 1;
    end
    numSegs = size(segmentsM,1);

    %Relative sampling of ROI voxels in this place, compared to CT spacing.
    %Set when rasterSegments are generated (usually on import).
    sampleRate = optS.ROISampleRate;

    %Sample the rows
    indFullV =  1 : numSegs;
    if sampleRate ~= 1
        rV = 1 : length(indFullV);
        rV([rem(rV+sampleRate-1,sampleRate)~=0]) = [];
        indFullV = rV;
    end

    %Block process to avoid swamping on large structures
    if isfield(optS, 'DVHBlockSize') & ~isempty(optS.DVHBlockSize)
        DVHBlockSize = optS.DVHBlockSize;
    else
        DVHBlockSize = 5000;
    end

    blocks{structCount} = ceil(length(indFullV)/DVHBlockSize);

    start = 1;

    for b = 1 : blocks{structCount}

        %Build the interpolation points matrix

        dummy = zeros(1,DVHBlockSize * ROIImageSize(1));
        x1V = dummy;
        y1V = dummy;
        z1V = dummy;
        volsSectionV{structCount} =  dummy;

        if start+DVHBlockSize > length(indFullV)
            stop = length(indFullV);
        else
            stop = start + DVHBlockSize - 1;
        end

        indV = indFullV(start:stop);

        mark = 1;
        for i = indV

            tmpV = segmentsM(i,1:10);
            delta = tmpV(5) * sampleRate;
            xV = tmpV(3): delta : tmpV(4);
            len = length(xV);
            rangeV = ones(1,len);
            yV = tmpV(2) * rangeV;
            zV = tmpV(1) * rangeV;
            sliceThickness = tmpV(10);
            %v = delta^2 * sliceThickness;
            v = delta * (deltaY*sampleRate) * sliceThickness;
            x1V(mark : mark + len - 1) = xV;
            y1V(mark : mark + len - 1) = yV;
            z1V(mark : mark + len - 1) = zV;
            volsSectionV{structCount}(mark : mark + len - 1) = v;
            mark = mark + len;

        end

        %cut unused matrix elements
        x1V = x1V(1:mark-1);
        y1V = y1V(1:mark-1);
        z1V = z1V(1:mark-1);
        volsSectionV{structCount} = volsSectionV{structCount}(1:mark-1);

        %Get transformation matrices for both dose and structure.
        transMDose    = getTransM('dose', doseNum, planC);
        transMStruct  = getTransM('struct', structNum, planC);

        %Forward transform the structure's coordinates.
        if ~isempty(transMStruct)
            [x1V, y1V, z1V] = applyTransM(transMStruct, x1V, y1V, z1V);
        end

        dvhCalcPtsC{structCount}{b} = [x1V', y1V', z1V'];

        %Interpolate.
        %   [dosesSectionV] = getDoseAt(doseNum, x1V, y1V, z1V, planC);

        %   dosesV = [dosesV, dosesSectionV];
        %   volsV  = [volsV, volsSectionV{structCount}];

        start = stop + 1;

    end

end

%Loop over to generate DVH
for iTrial = 1:numTrials

    [deltaX_systematic, deltaY_systematic, deltaZ_systematic, XdispRnd_Std, YdispRnd_Std, ZdispRnd_Std] = getShiftParameters(rectalCS,bladderVol,igrtFlag);
    deltaX_systematic = min(max(deltaX_systematic,-1.0),1.0);
    deltaY_systematic = min(max(deltaY_systematic,-1.5),1.5);
    deltaZ_systematic = min(max(deltaZ_systematic,-1.0),1.0);
    deltaX = deltaX_systematic + randn(1, numFractions) * XdispRnd_Std + XpdfMean;
    deltaY = deltaY_systematic + randn(1, numFractions) * YdispRnd_Std + YpdfMean;
    deltaZ = deltaZ_systematic + randn(1, numFractions) * ZdispRnd_Std + ZpdfMean;

    % To get planned DVH - comment it otherwise
    deltaX = 0;
    deltaY = 0;
    deltaZ = 0;

    shiftXv(1,iTrial) = mean(deltaX);
    shiftYv(1,iTrial) = mean(deltaY);
    shiftZv(1,iTrial) = mean(deltaZ);

    structCount = 0;
    for structNum = structNumV

        structCount = structCount + 1;

        tmpDoseV = zeros(1,length(dosesCurrentV{structCount}));

        for iFraction = 1:numFractions

            waitbar(((iTrial-1)*numStructs + structCount)/(numTrials*numStructs),hWait)

            transM = referenceTransM_matrix;
            transM(1:3,4) = transM(1:3,4) + [deltaX(iFraction); deltaY(iFraction); deltaZ(iFraction)];

            %Apply the new transM to dose
            planC{indexS.dose}(doseNum).transM = transM;

            % Convert Dose to Gy
            if max(planC{indexS.dose}(doseNum).doseArray(:)) > 150
                planC{indexS.dose}(doseNum).doseArray = planC{indexS.dose}(doseNum).doseArray/100;
            end

            %Get doses and volumes of points in structure.
            %[dosesV, volsV] = getDVH(structNum, doseNum, planC);

            volsV{structCount}  = [];
            dosesV{structCount} = [];

            for b = 1 : blocks{structCount}
                x1V = dvhCalcPtsC{structCount}{b}(:,1);
                y1V = dvhCalcPtsC{structCount}{b}(:,2);
                z1V = dvhCalcPtsC{structCount}{b}(:,3);

                %Back transform the coordinates into the doses' coordinate system.
                [x1V, y1V, z1V] = applyTransM(inv(transM), x1V, y1V, z1V);

                %Interpolate.
                [dosesSectionV] = getDoseAt(doseNum, x1V, y1V, z1V, planC);

                dosesV{structCount} = [dosesV{structCount}, dosesSectionV];
                volsV{structCount}  = [volsV{structCount}, volsSectionV{structCount}];
            end

            tmpDoseV = tmpDoseV + dosesV{structCount};

        end

        DVHm{structCount}(:,iTrial) = single(tmpDoseV(:));

    end

end

close(hWait)

%Reassign reference transM and doseArray to doseNum
planC{indexS.dose}(doseNum).transM = referenceTransM;
planC{indexS.dose}(doseNum).doseArray = referenceDoseArray;

%Compute Mean and Std Dev in blocks
structCount = 0;
for structNum = structNumV

    structCount = structCount + 1;

    numVoxels = length(volsV{structCount});
    DVHBlockSize = 5000;
    blocks = ceil(numVoxels/DVHBlockSize);
    start = 1;

    for b = 1 : blocks

        if start+DVHBlockSize > numVoxels
            stop = numVoxels;
        else
            stop = start + DVHBlockSize - 1;
        end

        DVH_block = DVHm{structCount}(start:stop,:);

        meanDoseV{structCount}(start:stop) = mean(DVH_block,2);
        stdDoseV{structCount}(start:stop)  = std(DVH_block,0,2);

        start = stop + 1;

    end
end

% %Histogram for the expectation of dose over all trials
% [doseBinsV, volsHistV] = doseHist(meanDoseV, volsV, binWidth);
% DVH_out = [doseBinsV(:)'; volsHistV(:)'];


function [x,y,K]=postprocessSDP(newx,newy,prepinfo,newK)
% [x,y,K]=postprocessSDP(newx,newy,prepinfo,newK)
% postprocessSDP: Postprocesses an SDP solution using the info from
% preprocessing (preprocessSDP)
%
% **********  INTERNAL FUNCTION OF SEDUMI **********
%
% See also sedumi, preprocessSDP

% This file is part of SeDuMi 1.1 by Imre Polik and Oleksandr Romanko
% Copyright (C) 2005 McMaster University, Hamilton, CANADA  (since 1.1)
%
% Copyright (C) 2001 Jos F. Sturm (up to 1.05R5)
%   Dept. Econometrics & O.R., Tilburg University, the Netherlands.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% Affiliation SeDuMi 1.03 and 1.04Beta (2000):
%   Dept. Quantitative Economics, Maastricht University, the Netherlands.
%
% Affiliations up to SeDuMi 1.02 (AUG1998):
%   CRL, McMaster University, Canada.
%   Supported by the Netherlands Organization for Scientific Research (NWO).
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
% 02110-1301, USA

if issparse(newx)
    x=sparse([]);
else
    x=[];
end
y=newy;
K=newK;

for j=length(prepinfo):-1:1
    op=prepinfo{j};
    switch op(1)
        case 0
            %Do nothing
            x=[newx(end-op(2)+1:end);x];
            newx=newx(1:end-op(2));
        case 1
            %Diagonal matrix
            x=[reshape(diag(newx(1:op(2))),op(2)^2,1);x];
            newx=newx(op(2)+1:end);
            K.l=K.l-op(2);
            K.s(end+1)=op(2);
            K.rsdpN=K.rsdpN+1;
    end
end