function [fv, normals] = MimCreateSurfaceFromSegmentation(segmentation, smoothing_size, small_structures, label, coordinate_system, template_image, limit_to_one_component_per_index, minimum_component_volume_mm3, reporting)
    % MimCreateSurfaceFromSegmentation. Creates a surface mesh from a segmentation volume.
    %
    %     Inputs
    %     ------
    %
    %     filepath, filename - path and filename for STL file
    %
    %     segmentation - a binary 3D PTKImage containing 1s for the voxels to
    %         visualise
    %
    %     smoothing_size - the amount of smoothing to perform. Higher
    %         smoothing makes the image look better but removes small
    %         structures such as airways and vessels
    %
    %     small_structures - set to true for improved visualisation of
    %         narrow structures such as airways and vessels
    %
    %     label - Only voxels of this colour are considered
    %
    %     coordinate_system  a MimCoordinateSystem enumeration
    %         specifying the coordinate system to use
    %
    %     template_image  A PTKImage providing voxel size and image size
    %         parameters, which may be required depending on the coordinate
    %         system
    %
    %     limit_to_one_component_per_index - set this flag to true if each colour
    %         index in the image represents only a single, large object.
    %         This will prevent the appearance of orphaned 'island' components caused by the
    %         image smoothing and surface rendering approximations.
    %
    %     minimum_component_volume_mm3 - if two or more separate components in the
    %         image could share the same colour index, but you want to prevent the
    %         appearance of orphaned 'island' components caused by the
    %         image smoothing and surface rendering approximations, then set this to
    %         an appropriate minimum volume value. Any components smaller than this
    %         value will not be rendered.
    %
    %     reporting - an object implementing CoreReportingInterface
    %                             for reporting progress and warnings
    %
    %
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    if ~isa(segmentation, 'PTKImage')
        error('Requires a PTKImage as input');
    end

    % Isolate this colour component
    sub_seg = segmentation.BlankCopy;
    raw_image = segmentation.GetMappedRawImage;
    sub_seg.ChangeRawImage(raw_image == label);

    sub_seg.CropToFit;

    % Perform a closing operation and filter the image to create a smoother appearance
    if small_structures
        morph_size = 1;
        threshold = 0.2;
    else
        morph_size = 3;
        threshold = 0.5;
    end

    required_padding_mm = 2*threshold + morph_size;
    required_padding = ceil(required_padding_mm/min(segmentation.VoxelSize)) + 1;
    sub_seg.AddBorder(required_padding);

    if morph_size > 0
        sub_seg.BinaryMorph(@imclose, morph_size);
    end

    if smoothing_size > 0
        sub_seg = MimGaussianFilter(sub_seg, smoothing_size);
    end

    if limit_to_one_component_per_index

        % The smoothing may split the segmentation, so we consider only the main component,
        % and remove any other components by setting their value to zero
        surviving_components = sub_seg.RawImage >= threshold;
        surviving_components = MimImageUtilities.GetLargestConnectedComponent(surviving_components);
    else

        cc = bwconncomp(sub_seg.RawImage >= threshold);
        num_pixels = cellfun(@numel, cc.PixelIdxList);
        voxel_threshold = minimum_component_volume_mm3/prod(segmentation.VoxelSize);
        larger_elements = num_pixels > voxel_threshold;
        surviving_components = false(sub_seg.ImageSize);
        for index = 1 : numel(larger_elements)
            if larger_elements(index)
                surviving_components(cc.PixelIdxList{index}) = true;
            end
        end
    end

    sub_seg_raw = sub_seg.RawImage >= threshold;
    sub_seg_raw(~surviving_components) = 0;
    sub_seg.ChangeRawImage(sub_seg_raw);

    [xc, yc, zc] = sub_seg.GetPTKCoordinates;
    [xc, yc, zc] = MimImageCoordinateUtilities.ConvertFromPTKCoordinatesCoordwise(xc, yc, zc, coordinate_system, template_image);

    fv = isosurface(xc, yc, zc, sub_seg.RawImage, threshold);

    if nargout > 1
        normals = isonormals(xc, yc, zc, sub_seg.RawImage, fv.vertices);
    end
end


function S=PartLabelCompSize(S,varargin)

%% PartLabel Assigns particle labels to particles
%% based on features in the carbon edge spectrum. Note that DiffMaps.m must
%% be run on S prior to execution of this function.
%% OUTPUT NOTES:
%% S.CompSize is a matrix of areas for each component with particles in the
%% rows and components in the columns. the components are ordered as
%% follows: ['OCArea','InArea','Sp2Area','KArea','CO3Area','Total Area']

%% 081201 Ryan C. Moffet

if isempty(S.DiffMap)
    return
end
figsav=0;
particle=S.particle;
if ~isempty(varargin)
    rootdir=varargin{1};
    sample=varargin{2};
    figsav=1;
end
ColorVec=[0,170,0;0,255,255;255,0,0;255,170,0;255,255,255]; %% rgb colors of the different components

MatSiz=size(S.DiffMap(:,:,1));
RgbMat=zeros([MatSiz,3]);
RedMat=zeros(MatSiz);
GreMat=zeros(MatSiz);
BluMat=zeros(MatSiz);

cnt=1;

for i=[5,1,3,2,4]; %% cooh, In, sp2, K, co3
        GrayImage=mat2gray(S.DiffMap(:,:,i)); %% Turn into a greyscale with vals [0 1]
        GrayImage=imadjust(GrayImage,[0 1],[0 1],0.1); %% increase contrast
%         GrayImage=medfilt2(GrayImage);  %% something weird was happening
                                          % %to the pixels on the corner
        Thresh=graythresh(GrayImage); %% Otsu thresholding
        Mask=im2bw(GrayImage,Thresh); %% Give binary image
        BinCompMap{cnt}=bwlabel(Mask,8);
        cnt=cnt+1;
end

% [j,k]=find(BinCompMap{3}>0); %% find index of >0 components
% [l,m]=find(S.LabelMat>0);
% S.BinCompMap=BinCompMap;
% if ~isempty(j) || ~isempty(k)
%     [orgspec]=ComponentSpec(S,'COOH');
%     [inorgspec]=ComponentSpec(S,'Inorg');
%     [ecspec]=ComponentSpec(S,'sp2');
%     ecspec(:,2)=ecspec(:,2)-mean(ecspec(1:8,2));
%     svdM = stackSVD(S,ecspec,orgspec,inorgspec);
%     svdM(svdM<0)=0;
%     figure,imagesc(svdM(:,:,1)),colormap gray, colorbar
%     BinCompMap{3}=threshimage(svdM(:,:,1));
% end
%
%% This first loop creates masks for the individual components over the
%% entire field of view. Each component is then defined as a colored
%% component for visualization.
cnt=1;
for i=[5,1,3,2,4]%i=[5,1,2,3] %% loop over chemical components
%         GrayImage=mat2gray(S.DiffMap(:,:,i)); %% Turn into a greyscale with vals [0 1]
%         GrayImage=imadjust(GrayImage,[0 1],[0 1],0.1); %% increase contrast
% %         GrayImage=medfilt2(GrayImage);  %% something weird was happening
%                                           % %to the pixels on the corner
%         Thresh=graythresh(GrayImage); %% Otsu thresholding
%         Mask=im2bw(GrayImage,Thresh); %% Give binary image
%         BinCompMap{cnt}=bwlabel(Mask,8);
%     end
    [j,k]=find(BinCompMap{cnt}>0); %% find index of >0 components
    [l,m]=find(S.LabelMat>0);
    if ~isempty(j) || ~isempty(k)  %% this conditional defines a color for the component areas (if it exists)
        linidx=sub2ind(size(BinCompMap{cnt}),j,k); %% change to linear in dex
        labidx=sub2ind(size(BinCompMap{cnt}),l,m);
        rejidx=setdiff(linidx,labidx);
        BinCompMap{cnt}(rejidx)=0;
        linidx=sub2ind(size(BinCompMap{cnt}),find(BinCompMap{cnt}>0));
        RedMat(linidx)=ColorVec(cnt,1); %% define the color for the ith component
        GreMat(linidx)=ColorVec(cnt,2);
        BluMat(linidx)=ColorVec(cnt,3);
        trmat=zeros(size(RedMat));
        tgmat=zeros(size(RedMat));
        tbmat=zeros(size(RedMat));
        trmat(linidx)=ColorVec(cnt,1);
        tgmat(linidx)=ColorVec(cnt,2);
        tbmat(linidx)=ColorVec(cnt,3);
        ccmap{cnt}(:,:,1)=trmat;
        ccmap{cnt}(:,:,2)=tgmat;
        ccmap{cnt}(:,:,3)=tbmat;
        clear trmat tgmat tbmat
    else
        ccmap{cnt}=zeros(MatSiz(1),MatSiz(2),3);
    end
    cnt=cnt+1;  %% this counter keeps track of the indivdual components.
    clear j k l m linidx GrayImage Thresh Mask rejidx;
end

%% This second loop assigns labels over individual particles defined
%% previously in Diffmaps.m
NumPart=max(max(S.LabelMat));
% LabelStr={'OC','In','K','EC'};
LabelStr={'OC','In','EC','K','CO3'};

CompSize=zeros(NumPart,length(LabelStr)+1);
PartLabel={};
for i=1:NumPart  %% Loop over particles defined in Diffmaps.m
    PartLabel{i}='';
    for j=1:5  %% Loop over chemical components
        [a1,b1]=find(S.LabelMat==i);  %% get particle i
        [a2,b2]=find(BinCompMap{j}>0); %% get component j
        if ~isempty([a1,b1]) && ~isempty([a2,b2])
            linidx1=sub2ind(size(S.LabelMat),a1,b1); %% Linear index for particle
            linidx2=sub2ind(size(BinCompMap{j}),a2,b2); %% Linear index for component
            IdxCom=intersect(linidx1,linidx2); %% find common indices
            if length(IdxCom)>3%0.05*length(linidx1) %% if component makes up greater than 2% of the pixels of the particle...
                    PartLabel{i}=strcat(PartLabel{i},LabelStr{j}); %% give label of component.
                    CompSize(i,j)=length(IdxCom); %% number of pixels in the component.
            end
        end
    end
    if isempty(PartLabel{i})
        PartLabel{i}='NoID'; %% Particles identified by Otsu's mehod here but not in Particle map script
    end
    CompSize(i,j+1)=length(linidx1);  %% number of pixels in the particle
    clear linidx1 linidx2 IdxCom a1 b1 a2 b2;
end
if isempty(PartLabel)
    S.PartLabel='NoID';
else
    S.PartLabel=PartLabel;
end
S=ParticleSize(S);

MatSiz=size(S.LabelMat);
XSiz=S.Xvalue/MatSiz(1);
YSiz=S.Yvalue/MatSiz(2);
CompSize=CompSize.*(XSiz*YSiz); %% Area of components in um^2
S.CompSize=CompSize;

RgbMat(:,:,1)=RedMat;
RgbMat(:,:,2)=GreMat;
RgbMat(:,:,3)=BluMat;
S.RGBCompMap=RgbMat;
for i=1:length(BinCompMap)
    temp{i}=BinCompMap{i};
    temp{i}(temp{i}>1)=1;
end
xdat=[0:XSiz:S.Xvalue];
ydat=[0:YSiz:S.Yvalue];

% Show DiffMaps
figure('Name',particle,'NumberTitle','off','Position',[1,1,634,872])
subplot(3,2,1)
% imagesc(temp{1})
image(xdat,ydat,uint8(ccmap{1}))
title('Carbox')
axis image
xlabel('X (\mum)');
ylabel('Y (\mum)');

subplot(3,2,2)
% imagesc(temp{2})
image(xdat,ydat,uint8(ccmap{2}))
title('Inorg.')
axis image
xlabel('X (\mum)');
ylabel('Y (\mum)');

subplot(3,2,3)
% imagesc(temp{3})
image(xdat,ydat,uint8(ccmap{3}))
title('%sp2>35')
axis image
xlabel('X (\mum)');
ylabel('Y (\mum)');

subplot(3,2,4)
% imagesc(temp{4})
image(xdat,ydat,uint8(ccmap{4}))
title('Potassium')
axis image
xlabel('X (\mum)');
ylabel('Y (\mum)');

% LabelMat=raw2mask(S);
subplot(3,2,5)
image(xdat,ydat,uint8(ccmap{5}))
title('CO_{3}')
axis image
xlabel('X (\mum)');
ylabel('Y (\mum)');

% title('Particle Map')
% axis image

subplot(3,2,6)
image(xdat,ydat,uint8(RgbMat))
title('Mixed')
axis image
xlabel('X (\mum)');
ylabel('Y (\mum)');

if figsav==1
filename=sprintf('%s%s%s%s',rootdir,sample,particle,'_f2_thresh');
saveas(gcf,filename,'tiff');
end
S.BinCompMap=temp;
S=MultPartAvSpec(S);
if figsav==1
filename=sprintf('%s%s%s%s',rootdir,sample,particle,'_f3_spec');
saveas(gcf,filename,'tiff');
close all
end


function adc=makeAdc(num,varargin)
%makeAdc Create a ADC readout event.
%   adc=makeAdc(n, 'Dwell', dwell) Create ADC with n samples
%   with given dwell time.
%
%   adc=makeAdc(n, 'Duration', dur) Create ADC with n
%   samples and specified total duration.
%
%   adc=makeAdc(..., 'Delay', d) Create ADC with initial delay.
%
%   adc=makeAdc(n, sys, ...) Create ADC considering system properties
%   given in sys. For example, adcDeadTime can be taken into account by
%   making sure that both the before the ADC is as least as long as
%   adcDeadTime and another period of adcDeadTime is added after sampling
%   is finished.
%
%   See also  Sequence.addBlock

persistent parser
if isempty(parser)
    parser = inputParser;
    parser.FunctionName = 'makeAdc';

    addRequired(parser,'numSamples',@(x)(isnumeric(x) && (fix(x)-x)==0));
    addOptional(parser,'system',mr.opts(),@isstruct);
    addParamValue(parser,'dwell',0,@isnumeric);
    addParamValue(parser,'duration',0,@isnumeric);
    addParamValue(parser,'delay',0,@isnumeric);
    addParamValue(parser,'freqOffset',0,@isnumeric);
    addParamValue(parser,'phaseOffset',0,@isnumeric);
end

parse(parser,num,varargin{:});

opt = parser.Results;
adc.type = 'adc';
adc.numSamples = num;
adc.dwell = opt.dwell;
adc.delay = opt.delay;
adc.freqOffset = opt.freqOffset;
adc.phaseOffset = opt.phaseOffset;
adc.deadTime = opt.system.adcDeadTime;

if (opt.dwell==0 && opt.duration==0) || (opt.dwell>0 && opt.duration>0)
    error('Either dwell or duration must be defined');
end

if opt.duration > 0
    adc.dwell = opt.duration/opt.numSamples;
end
if opt.dwell > 0
    adc.duration = opt.dwell*opt.numSamples;
end
if adc.deadTime > adc.delay
    adc.delay = adc.deadTime; % adcDeadTime is added before the actual sampling (and also second time after the sampling period)
end

end


function z = uminus( x )

%   Disciplined convex programming information for UMINUS (-):
%      Unary minus may be used in DCPs without restrictions---with the
%      understanding, of course, that it produces a result with the
%      opposite cuvature to its argument; i.e., the negative of a convex
%      expression is concave, and vice versa.
%
%   Disciplined geometric programming information for UMINUS(-):
%      Negation of non-constant values may not be used in disciplined
%      geometric programs.

persistent remap
if isempty( remap ),
    remap = cvx_remap( 'invalid', 'log-concave' ) & ~cvx_remap( 'log-affine' );
end
tt = remap( cvx_classify( x ) );
if nnz( tt ),
    xt = cvx_subsref( x, tt );
    error( 'Disciplined convex programming error:\n    Illegal operation: - {%s}', cvx_class( xt ) );
end

z = cvx( x.size_, -x.basis_ );

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


classdef SetKinematicStateAction < AbstractEventAction
    %SetTimePositionVelocityMassStateAction Summary of this class goes here
    %   Detailed explanation goes here

    properties
        stateLog LaunchVehicleStateLog
        orbitModel(1,1) AbstractElementSet = KeplerianElementSet.getDefaultElements();

        %Inherit Time
        inheritTime(1,1) logical = false;
        inheritTimeFrom(1,1) InheritStateEnum = InheritStateEnum.InheritFromLastState;
        inheritTimeFromEvent LaunchVehicleEvent = LaunchVehicleEvent.empty(1,0);

        %Inherit State
        inheritPosVel(1,1) logical = false;
        inheritPosVelFrom(1,1) InheritStateEnum = InheritStateEnum.InheritFromLastState;
        inheritPosVelFromEvent LaunchVehicleEvent = LaunchVehicleEvent.empty(1,0);

        %Inherit Stage States
        stageStates(1,:) SetKinematicStateStageState
        engineStates(1,:) SetKinematicStateEngineState
        tankStates(1,:) SetKinematicStateTankState
        epsSinkStates(1,:) SetKinematicStateEPSinkState
        epsSrcStates(1,:) SetKinematicStateEPSrcState
        epsStorageStates(1,:) SetKinematicStateEPStorageState

        %Inherit all other state elements
        inheritStateElems(1,1) logical = true;
        inheritStateElemsFrom(1,1) InheritStateEnum = InheritStateEnum.InheritFromLastState;
        inheritStateElemsFromEvent LaunchVehicleEvent = LaunchVehicleEvent.empty(1,0);

        optVar SetKinematicStateActionVariable
    end

    %deprecated
    properties
        inheritStageStates(1,1) logical = true;
        inheritStageStatesFrom(1,1) InheritStateEnum = InheritStateEnum.InheritFromLastState;
        inheritStageStatesFromEvent LaunchVehicleEvent = LaunchVehicleEvent.empty(1,0);
    end

    properties(Dependent)
        time(1,1) double
        centralBody(1,1) KSPTOT_BodyInfo
    end

    methods
        function obj = SetKinematicStateAction(stateLog, orbitModel)
            if(nargin > 0)
                obj.stateLog = stateLog;
                obj.orbitModel = orbitModel;
            end

            obj.id = rand();
        end

        function time = get.time(obj)
            time = obj.orbitModel.time;
        end

        function set.time(obj, newTime)
            obj.orbitModel.time = newTime;
        end

        function time = get.centralBody(obj)
            time = obj.orbitModel.frame.getOriginBody();
        end

        function set.centralBody(obj, newCentralBody)
            obj.orbitModel.frame.setOriginBody(newCentralBody);
        end

        function newStateLogEntry = executeAction(obj, stateLogEntry)
            newStateLogEntry = stateLogEntry.deepCopy();

            % Time
            if(obj.inheritTime)
                if(obj.inheritTimeFrom == InheritStateEnum.InheritFromSpecifiedEvent && ...
                   not(isempty(obj.inheritTimeFromEvent)) && ...
                   not(isempty(obj.inheritTimeFromEvent.getEventNum())) && ...
                   obj.inheritTimeFromEvent.getEventNum() > 0)

                    timeEvtStateLog = obj.stateLog.getLastStateLogForEvent(obj.inheritTimeFromEvent);
                    if(not(isempty(timeEvtStateLog)))
                        newStateLogEntry.time = timeEvtStateLog(end).time;
                        obj.orbitModel.time = newStateLogEntry.time;
                    end
                end
            else
                newStateLogEntry.time = obj.orbitModel.time;
            end

            % Orbit
            if(obj.inheritPosVel)
                if(obj.inheritPosVelFrom == InheritStateEnum.InheritFromSpecifiedEvent && ...
                   not(isempty(obj.inheritPosVelFromEvent)) && ...
                   not(isempty(obj.inheritPosVelFromEvent.getEventNum())) && ...
                   obj.inheritPosVelFromEvent.getEventNum() > 0)

                    posVelEvtStateLog = obj.stateLog.getLastStateLogForEvent(obj.inheritPosVelFromEvent);
                    if(not(isempty(posVelEvtStateLog)))
                        newStateLogEntry.position = posVelEvtStateLog(end).position;
                        newStateLogEntry.velocity = posVelEvtStateLog(end).velocity;
                        newStateLogEntry.centralBody = posVelEvtStateLog(end).centralBody;
                    end
                end
            else
                cartElemSet = obj.orbitModel.convertToCartesianElementSet();

                newStateLogEntry.setCartesianElementSet(cartElemSet);
            end

            for(i=1:length(obj.stageStates)) %#ok<*UNRCH>
                obj.stageStates(i).updateStateLogEntry(newStateLogEntry, obj.stateLog)
            end

            for(i=1:length(obj.engineStates))
                obj.engineStates(i).updateStateLogEntry(newStateLogEntry, obj.stateLog)
            end

            for(i=1:length(obj.tankStates))
                obj.tankStates(i).updateStateLogEntry(newStateLogEntry, obj.stateLog)
            end

            for(i=1:length(obj.epsSinkStates))
                obj.epsSinkStates(i).updateStateLogEntry(newStateLogEntry, obj.stateLog)
            end

            for(i=1:length(obj.epsSrcStates))
                obj.epsSrcStates(i).updateStateLogEntry(newStateLogEntry, obj.stateLog)
            end

            for(i=1:length(obj.epsStorageStates))
                obj.epsStorageStates(i).updateStateLogEntry(newStateLogEntry, obj.stateLog)
            end

            %Comment out this block once done
%             if(obj.inheritStageStates)
%                 if(obj.inheritStageStatesFrom == InheritStateEnum.InheritFromSpecifiedEvent && ...
%                    not(isempty(obj.inheritStageStatesFromEvent)) && ...
%                    not(isempty(obj.inheritStageStatesFromEvent.getEventNum())) && ...
%                    obj.inheritStageStatesFromEvent.getEventNum() > 0)
%
%
%                     stageStateEvtState = obj.stateLog.getLastStateLogForEvent(obj.inheritStageStatesFromEvent);
%                     stageStateEvtState = stageStateEvtState(end);
%                     if(not(isempty(stageStateEvtState)))
%                         lvState = stageStateEvtState.lvState.deepCopy();
%                         newStateLogEntry.lvState = lvState;
%
%                         for(i=1:length(stageStateEvtState.stageStates)) %#ok<NO4LP>
%                             newStateLogEntry.stageStates(i) = stageStateEvtState.stageStates(i).deepCopy(true, lvState);
%                         end
%                     end
%                 end
%
%             else
%                 %do nothing, there is no behavior for this yet
%                 warning('SetKinematicStateAction.inheritStageStates must be set to true, no behavior yet defined when this is set to false.');
%             end

            %Misc states
            if(obj.inheritStateElems)
              if(obj.inheritStateElemsFrom == InheritStateEnum.InheritFromSpecifiedEvent && ...
                   not(isempty(obj.inheritStateElemsFromEvent)) && ...
                   not(isempty(obj.inheritStateElemsFromEvent.getEventNum())) && ...
                   obj.inheritStateElemsFromEvent.getEventNum() > 0)

                    stateElemsEvtState = obj.stateLog.getLastStateLogForEvent(obj.inheritStateElemsFromEvent);
                    stateElemsEvtState = stateElemsEvtState(end);

                    if(not(isempty(stateElemsEvtState)))
                        newStateLogEntry.aero = stateElemsEvtState.aero.deepCopy();
                        newStateLogEntry.thirdBodyGravity = stateElemsEvtState.thirdBodyGravity.copy();
                        newStateLogEntry.stopwatchStates = stateElemsEvtState.stopwatchStates.copy();
                        newStateLogEntry.extremaStates = stateElemsEvtState.extremaStates.copy();
                        newStateLogEntry.steeringModel = stateElemsEvtState.steeringModel; %NEED TO WARN USE TO REINITIALIZE THROTTLE AND STEERING MODELS!
                        newStateLogEntry.throttleModel = stateElemsEvtState.throttleModel;
                    end
              end
            else
                %do nothing, there is no behavior for this yet
                warning('SetKinematicStateAction.inheritStateElems must be set to true, no behavior yet defined when this is set to false.');
            end
        end

        function initAction(obj, initialStateLogEntry)
            %nothing
        end

        function name = getName(obj)
            name = 'Set Kinematic State';
        end

        function tf = usesStage(obj, stage)
            tf = false;
        end

        function tf = usesEngine(obj, engine)
            tf = false;
        end

        function tf = usesTank(obj, tank)
            tf = false;
        end

        function tf = usesEngineToTankConn(obj, engineToTank)
            tf = false;
        end

        function tf = usesStopwatch(obj, stopwatch)
            tf = false;
        end

        function tf = usesExtremum(obj, extremum)
            tf = false;
        end

        function tf = usesTankToTankConn(obj, tankToTank)
            tf = false;
        end

        function tf = usesEvent(obj, event)
            tf = false;

            tankEvtsTf = logical([]);
            for(i=1:length(obj.tankStates))
                tankState = obj.tankStates(i);
                if(tankState.inheritTankStateFrom == InheritStateEnum.InheritFromSpecifiedEvent && ...
                   not(isempty(tankState.inheritTankStateFromEvent)) && tankState.inheritTankStateFromEvent == event)
                    tankEvtsTf(end+1) = tankState.inheritTankStateFromEvent == event; %#ok<AGROW>
                end
            end

            epsSrcEvtsTf = logical([]);
            for(i=1:length(obj.epsStorageStates))
                storageState = obj.epsStorageStates(i);

                if(storageState.inheritStorageStateFrom == InheritStateEnum.InheritFromSpecifiedEvent && ...
                   not(isempty(storageState.inheritStorageStateFromEvent)) && storageState.inheritStorageStateFromEvent == event)
                    epsSrcEvtsTf(end+1) = storageState.inheritStorageStateFromEvent == event; %#ok<AGROW>
                end
            end

            if((obj.inheritTime && obj.inheritTimeFrom == InheritStateEnum.InheritFromSpecifiedEvent && not(isempty(obj.inheritTimeFromEvent)) && obj.inheritTimeFromEvent == event) || ...
               (obj.inheritPosVel && obj.inheritPosVelFrom == InheritStateEnum.InheritFromSpecifiedEvent && not(isempty(obj.inheritPosVelFromEvent)) && obj.inheritPosVelFromEvent == event) || ...
               (obj.inheritStateElems && obj.inheritStateElemsFrom == InheritStateEnum.InheritFromSpecifiedEvent && not(isempty(obj.inheritStateElemsFromEvent)) && obj.inheritStateElemsFromEvent == event) || ...
               any(tankEvtsTf) || any(epsSrcEvtsTf))
                tf = true;
            end
        end

        function [tf, vars] = hasActiveOptimVar(obj)
            tf = false;
            vars = AbstractOptimizationVariable.empty(0,1);

            if(not(isempty(obj.optVar)))
                tf = any(obj.optVar.getUseTfForVariable());
                vars(end+1) = obj.optVar;
            end
        end

        function generateLvComponentElements(obj)
            lvdData = obj.event.lvdData;

            if(isempty(obj.stageStates))
                stages = lvdData.launchVehicle.stages;
                for(j=1:length(stages))
                    stageState = SetKinematicStateStageState(stages(j));
                    stageState.inheritStageState = obj.inheritStageStates;
                    stageState.inheritStageStateFrom = obj.inheritStageStatesFrom;
                    stageState.inheritStageStateFromEvent = obj.inheritStageStatesFromEvent;

                    obj.stageStates(j) = stageState;
                end
            end
            addlistener(lvdData.launchVehicle, 'StageAdded',   @(src, evt) stageAddedClbk(obj, src, evt));
            addlistener(lvdData.launchVehicle, 'StageDeleted', @(src, evt) stageDeletedClbk(obj, src, evt));

            if(isempty(obj.engineStates))
                [~, engines] = lvdData.launchVehicle.getEnginesListBoxStr();
                for(j=1:length(engines))
                    engineState = SetKinematicStateEngineState(engines(j));
                    engineState.inheritEngineState = obj.inheritStageStates;
                    engineState.inheritEngineStateFrom = obj.inheritStageStatesFrom;
                    engineState.inheritEngineStateFromEvent = obj.inheritStageStatesFromEvent;

                    obj.engineStates(j) = engineState;
                end
            end
            addlistener(lvdData.launchVehicle, 'EngineAdded',   @(src, evt) engineAddedClbk(obj, src, evt));
            addlistener(lvdData.launchVehicle, 'EngineDeleted', @(src, evt) engineDeletedClbk(obj, src, evt));

            if(isempty(obj.tankStates))
                [~, storages] = lvdData.launchVehicle.getTanksListBoxStr();
                for(j=1:length(storages))
                    tankState = SetKinematicStateTankState(storages(j));
                    tankState.inheritTankState = obj.inheritStageStates;
                    tankState.inheritTankStateFrom = obj.inheritStageStatesFrom;
                    tankState.inheritTankStateFromEvent = obj.inheritStageStatesFromEvent;

                    obj.tankStates(j) = tankState;
                end
            end
            addlistener(lvdData.launchVehicle, 'TankAdded',   @(src, evt) tankAddedClbk(obj, src, evt));
            addlistener(lvdData.launchVehicle, 'TankDeleted', @(src, evt) tankDeletedClbk(obj, src, evt));

            if(isempty(obj.epsSinkStates))
                [~, sinks] = lvdData.launchVehicle.getPowerSinksListBoxStr();
                for(j=1:length(sinks))
                    sinkState = SetKinematicStateEPSinkState(sinks(j));
                    sinkState.inheritSinkState = obj.inheritStageStates;
                    sinkState.inheritSinkStateFrom = obj.inheritStageStatesFrom;
                    sinkState.inheritSinkStateFromEvent = obj.inheritStageStatesFromEvent;

                    obj.epsSinkStates(j) = sinkState;
                end
            end
            addlistener(lvdData.launchVehicle, 'EpsSinkAdded',   @(src, evt) epsSinkAddedClbk(obj, src, evt));
            addlistener(lvdData.launchVehicle, 'EpsSinkDeleted', @(src, evt) epsSinkDeletedClbk(obj, src, evt));

            if(isempty(obj.epsSrcStates))
                [~, srcs] = lvdData.launchVehicle.getPowerSrcsListBoxStr();
                for(j=1:length(srcs))
                    srcState = SetKinematicStateEPSrcState(srcs(j));
                    srcState.inheritSrcState = obj.inheritStageStates;
                    srcState.inheritSrcStateFrom = obj.inheritStageStatesFrom;
                    srcState.inheritSrcStateFromEvent = obj.inheritStageStatesFromEvent;

                    obj.epsSrcStates(j) = srcState;
                end
            end
            addlistener(lvdData.launchVehicle, 'EpsSrcAdded',   @(src, evt) epsSrcAddedClbk(obj, src, evt));
            addlistener(lvdData.launchVehicle, 'EpsSrcDeleted', @(src, evt) epsSrcDeletedClbk(obj, src, evt));

            if(isempty(obj.epsStorageStates))
                [~, storages] = lvdData.launchVehicle.getPowerStoragesListBoxStr();
                for(j=1:length(storages))
                    storageState = SetKinematicStateEPStorageState(storages(j));
                    storageState.inheritStorageState = obj.inheritStageStates;
                    storageState.inheritStorageStateFrom = obj.inheritStageStatesFrom;
                    storageState.inheritStorageStateFromEvent = obj.inheritStageStatesFromEvent;

                    obj.epsStorageStates(j) = storageState;
                end
            end
            addlistener(lvdData.launchVehicle, 'EpsStorageAdded',   @(src, evt) epsStorageAddedClbk(obj, src, evt));
            addlistener(lvdData.launchVehicle, 'EpsStorageDeleted', @(src, evt) epsStorageDeletedClbk(obj, src, evt));

        end
    end

    methods(Access=private)
        function stageAddedClbk(obj, src, evt)
            stage = evt.stage;

            stageState = SetKinematicStateStageState(stage);
            obj.stageStates(end+1) = stageState;
        end

        function stageDeletedClbk(obj, src, evt)
            stage = evt.stage;

            obj.stageStates([obj.stageStates.stage] == stage) = [];
        end

        function engineAddedClbk(obj, src, evt)
            engine = evt.engine;

            engineState = SetKinematicStateEngineState(engine);
            obj.engineStates(end+1) = engineState;
        end

        function engineDeletedClbk(obj, src, evt)
            engine = evt.engine;

            obj.engineStates([obj.engineStates.engine] == engine) = [];
        end

        function tankAddedClbk(obj, src, evt)
            tank = evt.tank;

            tankState = SetKinematicStateTankState(tank);
            obj.tankStates(end+1) = tankState;

            SetKinematicStateActionVariable(tankState); %doesn't need to get stored somewhere, it'll get stored on the tankState
        end

        function tankDeletedClbk(obj, src, evt)
            tank = evt.tank;

            obj.tankStates([obj.tankStates.tank] == tank) = [];
        end

        function epsSinkAddedClbk(obj, src, evt)
            sink = evt.sink;

            sinkState = SetKinematicStateEPSinkState(sink);
            obj.epsSinkStates(end+1) = sinkState;
        end

        function epsSinkDeletedClbk(obj, src, evt)
            sink = evt.sink;

            obj.epsSinkStates([obj.epsSinkStates.sink] == sink) = [];
        end

        function epsSrcAddedClbk(obj, src, evt)
            src = evt.src;

            srcState = SetKinematicStateEPSrcState(src);
            obj.epsSrcStates(end+1) = srcState;
        end

        function epsSrcDeletedClbk(obj, ~, evt)
            src = evt.src;

            obj.epsSrcStates([obj.epsSrcStates.src] == src) = [];
        end

        function epsStorageAddedClbk(obj, src, evt)
            storage = evt.storage;

            storageState = SetKinematicStateEPStorageState(storage);
            obj.epsStorageStates(end+1) = storageState;
        end

        function epsStorageDeletedClbk(obj, src, evt)
            storage = evt.storage;

            obj.epsStorageStates([obj.epsStorageStates.storage] == storage) = [];
        end
    end

    methods(Static)
        function addActionTf = openEditActionUI(action, lv)
            action.generateLvComponentElements();

            output = AppDesignerGUIOutput({false});
            lvd_EditActionSetKinematicStateGUI_App(action, lv.lvdData, output);

            addActionTf = output.output{1};
        end
    end
end


function onl_write_background(varargin)
% Periodically process data using a predictive model, and write results to some external device.
% onl_write_background(ResultWriter,StreamNames,Model,OutputFormat,UpdateFrequency,StartDelay,PredictorName,PredictAt,Verbose,EmptyResultValue)
%
% This is a convenience function which simplifies the definition of components which load and then
% periodically query a predictive model, in real time, and forward the results to some external
% device. The function is internally implemented using a timer that periodically triggers the
% computation of updated estimates, and their transfer to the data sink.
%
% In:
%   ResultWriter : Callback function that receives one or more BCI estimates and writes them to some
%                  external device. The format passed to the ResultWriter is according to OutputFormat.
%
%   StreamNames : optional names of stream data structures in the MATLAB base workspace to consider as
%                 possible data sources (previously created with onl_newstream); any stream that
%                 contains channels that are needed by the predictor will be linked to it (assuming
%                 that the choice of stream to use is not ambiguous).
%
%                 The identification of needed channels is primarily done on the basis of the channel
%                 labels -- if a stream has channels with labels that are required by a filter pipeline,
%                 it will be used as a source for this pipeline. The framework attempts to gracefully
%                 handle cases where a stream only provides a subset of the channels that were in the
%                 training set and the model only effectively operates on this subset via flt_selchans.
%
%   Model : A model data structure (as obtained from bci_train) according to which predictions shall be
%           made; typically this is a model struct, but for convenience it can be a file name,
%           variable name in the base workspace, or a cell array of {file name, variable name} to
%           refer to a variable inside a .mat file. The model is not modified by this function.
%           (default: 'lastmodel')
%
%   OutputFormat : Output data format, see onl_predict (default: 'distribution')
%
%   UpdateFrequency : Frequency at which the device should be queried, in Hz (default: 10)
%
%   StartDelay : Delay before real-time processing begins; grace period until user resources are
%                created (default: 1)
%
%   PredictorName : Name of a predictor data structure that shall be created in the MATLAB base
%                   workspace to hold the predictor's state. If a variable of this name already exists
%                   it will be overridden. (default: 'lastpredictor')
%
%   PredictAt : Predict at markers. If nonempty, this is a cell array of online target markers
%               relative to which predictions shall be made. If empty, predictions are always made
%               on the most recently added sample. (default: {})
%
%   Verbose : Verbose output. If false, the console output of the online pipeline will be suppressed.
%             (default: false)
%
%   EmptyResultValue : Empty-result value. This value is returned for predictions that yielded no
%                      result (e.g., due to an error or because not enough data was available).
%                      (default: NaN)
%
% Examples:
%   % after a predictive model has been learned using bci_train, and a data stream supplying raw
%   % data has been established, load the model into the online system and periodically send its
%   % outputs to a target destination
%   onl_write_background(@send_outputs_to_destination,'mystream')
%
%   % as before, but also specify a custom output format and a higher update frequency
%   onl_write_background(@send_outputs_to_destination,'mystream','lastmodel','expectation',25)
%
%   % as before, but pass all arguments by their short names
%   onl_write_background('ResultWriter',@send_outputs_to_destination,'MatlabStream','mystream','Model','lastmodel','OutputFormat','expectation','UpdateFrequency',25)
%
% See also:
%   onl_predict
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2011-01-18

% read options
arg_define(varargin, ...
    arg_norep({'result_writer','ResultWriter'},[],[],'Result writing callback. Callback function that receives one or more BCI estimates and writes them to some external device. The format passed to the ResultWriter is according to OutputFormat.'), ...
    arg({'in_stream','StreamName','MatlabStream'}, 'laststream',[],'Input stream name. This is the name of the stream data structure in the MATLAB base workspace that shall be read from. Can also be a cell array of stream names, if multiple, or empty if non-ambiguous.','typecheck',false,'shapecheck',false), ...
    arg({'pred_model','Model'}, 'lastmodel', [], 'Predictive model. A model data structure (as obtained from bci_train) according to which predictions shall be made; typically this is a model struct, but for convenience it can be a file name, variable name in the base workspace, or a cell array of {file name, variable name} to refer to a variable inside a .mat file. The model is not modified by this function.','type','expression'), ...
    arg({'out_form','OutputFormat'},'distribution',{'expectation','distribution','mode','raw'},'Format of the produced output values. Can be the expected value (posterior mean) of the target variable, or the distribution over possible target values (probabilities for each outcome, or parametric distribution), or the mode (most likely value) of the target variable.'), ...
    arg({'update_freq','UpdateFrequency'},10,[0 Inf],'Update frequency. This is the rate at which the outputs should be calculated.'), ...
    arg({'start_delay','StartDelay'}, 1, [0 Inf],'Start-up delay. Delay before real-time processing begins; grace period to initialize everything.'), ...
    arg({'pred_name','PredictorName'}, 'lastpredictor',[],'Name of new predictor. Name of a predictor data structure that shall be created in the MATLAB base workspace to hold the predictor''s state. If a variable of this name already exists it will be overridden.'), ...
    arg({'predict_at','PredictAt'}, {},[],'Predict at markers. If nonempty, this is a cell array of online target markers relative to which predictions shall be made. If empty, predictions are always made on the most recently added sample.','type','expression'), ...
    arg({'verbose','Verbose'}, false,[],'Verbose output. If false, the console output of the online pipeline will be suppressed.'), ...
    arg({'empty_result_value','EmptyResultValue'},NaN,[],'Empty-result value. This value is returned for predictions that yielded no result (e.g., due to an error or because not enough data was available).','type','expression'));

% input validation
if ischar(result_writer) && ~isempty(result_writer) %#ok<NODEF>
    if result_writer(1) ~= '@' && ~exist(result_writer,'file')
        error('The given ResultWriter argument (%s) is not a valid function name',result_writer); end
    result_writer = str2func(result_writer);
end
if ~isa(result_writer,'function_handle')
    error('The given ResultWriter argument must be a function handle (or function name), but was: %s',hlp_tostring(result_writer,10000)); end
if ~iscell(in_stream) %#ok<NODEF>
    in_stream = {in_stream}; end
for c=1:length(in_stream)
    stream_name = in_stream{c};
    if ~isvarname(stream_name)
        error('The given StreamName argument must be a valid variable name, but was: %s',hlp_tostring(stream_name,10000)); end
    try
        stream = evalin('base',stream_name);
    catch e
        error('Failed to look up stream named %s in MATLAB base workspace with error: %s',stream_name,e.message);
    end
    if ~isstruct(stream)
        error('The given data structure named %s in the MATLAB base workspace was expected to be a stream data structure, but was not a struct (wrong name?): %s',stream_name,hlp_tostring(stream,10000)); end
    if ~isfield(stream,'streamid')
        if isfield(stream,{'data','srate'})
            error('The given stream data structure named %s appears to be an EEGLAB data set struct but is not a stream (use onl_newstream to create a valid stream)',stream_name);
        else
            error('The given data structure named %s is not a valid stream (use onl_newstream to create a valid stream)',stream_name);
        end
    end
    streamids{c} = stream.streamid; %#ok<AGROW>
end

% create new predictor
predid = onl_newpredictor(pred_name,pred_model,in_stream,predict_at);

% create & start timer (which periodically writes to the stream)
start(timer('ExecutionMode','fixedRate', 'Name',[pred_name '_timer'], 'Period',1/update_freq, ...
    'StartDelay',start_delay, 'TimerFcn',@(timer_handle,varargin) write_data(pred_name,in_stream,out_form,result_writer,predid,streamids,timer_handle,verbose,empty_result_value)));

% background data writer
function write_data(predictor,stream_names,fmt,result_writer,pred_id,stream_ids,timer_handle,verbose,empty_result_value)
try
    % check if the stream and the predictor are still there
    for c=1:length(stream_names)
        s = evalin('base',stream_names{c});
        if s.streamid ~= stream_ids{c}
            error('Note: the stream named %s was recreated.',stream_names{c}); end
    end
    p = evalin('base',predictor);
    if p.predictorid ~= pred_id
        error('Note: the predictor named %s was recreated.',predictor); end
    % make a prediction
    y = onl_predict(predictor,fmt,~verbose,empty_result_value);
    % and write it out
    try
        result_writer(y);
    catch e
        disp('Error in result-writing function:');
        hlp_handleerror(e);
    end
catch e
    if ~strcmp(e.identifier,'MATLAB:UndefinedFunction')
        hlp_handleerror(e); end
    % stream or predictor have changed (e.g., replaced/deleted) --> stop timer
    stop(timer_handle);
    delete(timer_handle);
end


function f = pskernelObjective(lntheta, models, prior)

% PSKERNELOBJECTIVE Likelihood approximation for point set IVM.

% KERN

% KERN

% PSIVM

if nargin < 3
  prior = 1;
end
f = 0;
numTasks = length(models.task);
for taskNo = 1:numTasks
  models.task(taskNo).lntheta = models.lntheta;
  f = f + kernelObjective(lntheta, models.task(taskNo), prior);
end


function obj = Jacob_pTop_func_new(in1,in2,in3,in4,in5)
coef_f0_q_sym1 = in1(:,1);
coef_f0_q_sym2 = in1(:,2);
coef_f0_q_sym3 = in1(:,3);
coef_f0_q_sym4 = in1(:,4);
coef_f0_q_sym5 = in1(:,5);
coef_f0_q_sym6 = in1(:,6);
coef_f0_q_sym7 = in1(:,7);
coef_f0_q_sym8 = in1(:,8);
coef_f0_q_sym9 = in1(:,9);
coef_f0_q_sym10 = in1(:,10);
coef_f0_q_sym11 = in1(:,11);
coef_f1_q_sym1 = in2(:,1);
coef_f0_q_sym12 = in1(:,12);
coef_f1_q_sym2 = in2(:,2);
coef_f0_q_sym13 = in1(:,13);
coef_f1_q_sym3 = in2(:,3);
coef_f0_q_sym14 = in1(:,14);
coef_f1_q_sym4 = in2(:,4);
coef_f0_q_sym15 = in1(:,15);
coef_f1_q_sym5 = in2(:,5);
coef_f0_q_sym16 = in1(:,16);
coef_f1_q_sym6 = in2(:,6);
coef_f0_q_sym17 = in1(:,17);
coef_f1_q_sym7 = in2(:,7);
coef_f0_q_sym18 = in1(:,18);
coef_f1_q_sym8 = in2(:,8);
coef_f0_q_sym19 = in1(:,19);
coef_f1_q_sym9 = in2(:,9);
coef_f0_q_sym20 = in1(:,20);
coef_f0_q_sym21 = in1(:,21);
coef_f2_q_sym1 = in3(:,1);
coef_f0_q_sym22 = in1(:,22);
coef_f2_q_sym2 = in3(:,2);
coef_f0_q_sym23 = in1(:,23);
coef_f2_q_sym3 = in3(:,3);
coef_f0_q_sym24 = in1(:,24);
coef_f2_q_sym4 = in3(:,4);
coef_f2_q_sym5 = in3(:,5);
coef_f2_q_sym6 = in3(:,6);
coef_f2_q_sym7 = in3(:,7);
coef_f2_q_sym8 = in3(:,8);
coef_f2_q_sym9 = in3(:,9);
coef_f3_q_sym1 = in4(:,1);
coef_f3_q_sym2 = in4(:,2);
coef_f3_q_sym3 = in4(:,3);
coef_f3_q_sym4 = in4(:,4);
coef_f3_q_sym5 = in4(:,5);
coef_f3_q_sym6 = in4(:,6);
coef_f3_q_sym7 = in4(:,7);
coef_f3_q_sym8 = in4(:,8);
coef_f3_q_sym9 = in4(:,9);
coef_f1_q_sym10 = in2(:,10);
coef_f1_q_sym11 = in2(:,11);
coef_f1_q_sym12 = in2(:,12);
coef_f1_q_sym13 = in2(:,13);
coef_f1_q_sym14 = in2(:,14);
coef_f1_q_sym15 = in2(:,15);
coef_f1_q_sym16 = in2(:,16);
coef_f1_q_sym17 = in2(:,17);
coef_f1_q_sym18 = in2(:,18);
coef_f1_q_sym19 = in2(:,19);
coef_f1_q_sym20 = in2(:,20);
coef_f1_q_sym21 = in2(:,21);
coef_f1_q_sym22 = in2(:,22);
coef_f1_q_sym23 = in2(:,23);
coef_f1_q_sym24 = in2(:,24);
coef_f2_q_sym10 = in3(:,10);
coef_f2_q_sym11 = in3(:,11);
coef_f2_q_sym12 = in3(:,12);
coef_f2_q_sym13 = in3(:,13);
coef_f2_q_sym14 = in3(:,14);
coef_f2_q_sym15 = in3(:,15);
coef_f2_q_sym16 = in3(:,16);
coef_f2_q_sym17 = in3(:,17);
coef_f2_q_sym18 = in3(:,18);
coef_f2_q_sym19 = in3(:,19);
coef_f2_q_sym20 = in3(:,20);
coef_f2_q_sym21 = in3(:,21);
coef_f2_q_sym22 = in3(:,22);
coef_f2_q_sym23 = in3(:,23);
coef_f2_q_sym24 = in3(:,24);
coef_f3_q_sym10 = in4(:,10);
coef_f3_q_sym11 = in4(:,11);
coef_f3_q_sym12 = in4(:,12);
coef_f3_q_sym13 = in4(:,13);
coef_f3_q_sym14 = in4(:,14);
coef_f3_q_sym15 = in4(:,15);
coef_f3_q_sym16 = in4(:,16);
coef_f3_q_sym17 = in4(:,17);
coef_f3_q_sym18 = in4(:,18);
coef_f3_q_sym19 = in4(:,19);
coef_f3_q_sym20 = in4(:,20);
coef_f3_q_sym21 = in4(:,21);
coef_f3_q_sym22 = in4(:,22);
coef_f3_q_sym23 = in4(:,23);
coef_f3_q_sym24 = in4(:,24);
q0 = in5(1,:);
q1 = in5(2,:);
q2 = in5(3,:);
q3 = in5(4,:);
t2 = coef_f0_q_sym11.*q0;
t3 = coef_f0_q_sym18.*q1;
t4 = coef_f0_q_sym22.*q2;
t5 = coef_f0_q_sym24.*q3;
t6 = q0.^2;
t7 = q0.^3;
t8 = q1.^2;
t9 = q1.^3;
t10 = q2.^2;
t11 = q2.^3;
t12 = q3.^2;
t13 = q3.^3;
t24 = coef_f0_q_sym6.*q0.*q1.*q2.*2.0;
t25 = coef_f0_q_sym7.*q0.*q1.*q3.*2.0;
t26 = coef_f0_q_sym9.*q0.*q2.*q3.*2.0;
t27 = coef_f0_q_sym16.*q1.*q2.*q3.*2.0;
t14 = coef_f0_q_sym1.*t7;
t15 = coef_f0_q_sym12.*t9;
t16 = coef_f0_q_sym19.*t11;
t17 = coef_f0_q_sym23.*t13;
t18 = coef_f0_q_sym2.*q1.*t6;
t19 = coef_f0_q_sym3.*q2.*t6;
t20 = coef_f0_q_sym5.*q0.*t8;
t21 = coef_f0_q_sym4.*q3.*t6;
t22 = coef_f0_q_sym8.*q0.*t10;
t23 = coef_f0_q_sym10.*q0.*t12;
obj = reshape([coef_f0_q_sym11.*q1-coef_f1_q_sym11.*q0.*2.0-coef_f1_q_sym18.*q1-coef_f1_q_sym22.*q2-coef_f1_q_sym24.*q3+coef_f0_q_sym5.*t9-coef_f1_q_sym1.*t7.*4.0-coef_f1_q_sym12.*t9-coef_f1_q_sym19.*t11-coef_f1_q_sym23.*t13+coef_f0_q_sym1.*q1.*t6.*3.0+coef_f0_q_sym2.*q0.*t8.*2.0+coef_f0_q_sym6.*q2.*t8+coef_f0_q_sym7.*q3.*t8+coef_f0_q_sym8.*q1.*t10-coef_f1_q_sym2.*q1.*t6.*3.0-coef_f1_q_sym3.*q2.*t6.*3.0+coef_f0_q_sym10.*q1.*t12-coef_f1_q_sym4.*q3.*t6.*3.0-coef_f1_q_sym5.*q0.*t8.*2.0-coef_f1_q_sym8.*q0.*t10.*2.0-coef_f1_q_sym10.*q0.*t12.*2.0-coef_f1_q_sym13.*q2.*t8-coef_f1_q_sym14.*q3.*t8-coef_f1_q_sym15.*q1.*t10-coef_f1_q_sym17.*q1.*t12-coef_f1_q_sym20.*q3.*t10-coef_f1_q_sym21.*q2.*t12+coef_f0_q_sym3.*q0.*q1.*q2.*2.0+coef_f0_q_sym4.*q0.*q1.*q3.*2.0+coef_f0_q_sym9.*q1.*q2.*q3-coef_f1_q_sym6.*q0.*q1.*q2.*2.0-coef_f1_q_sym7.*q0.*q1.*q3.*2.0-coef_f1_q_sym9.*q0.*q2.*q3.*2.0-coef_f1_q_sym16.*q1.*q2.*q3,coef_f0_q_sym11.*q2-coef_f2_q_sym11.*q0.*2.0-coef_f2_q_sym18.*q1-coef_f2_q_sym22.*q2-coef_f2_q_sym24.*q3+coef_f0_q_sym8.*t11-coef_f2_q_sym1.*t7.*4.0-coef_f2_q_sym12.*t9-coef_f2_q_sym19.*t11-coef_f2_q_sym23.*t13+coef_f0_q_sym1.*q2.*t6.*3.0+coef_f0_q_sym3.*q0.*t10.*2.0+coef_f0_q_sym5.*q2.*t8+coef_f0_q_sym6.*q1.*t10+coef_f0_q_sym9.*q3.*t10+coef_f0_q_sym10.*q2.*t12-coef_f2_q_sym2.*q1.*t6.*3.0-coef_f2_q_sym3.*q2.*t6.*3.0-coef_f2_q_sym4.*q3.*t6.*3.0-coef_f2_q_sym5.*q0.*t8.*2.0-coef_f2_q_sym8.*q0.*t10.*2.0-coef_f2_q_sym10.*q0.*t12.*2.0-coef_f2_q_sym13.*q2.*t8-coef_f2_q_sym14.*q3.*t8-coef_f2_q_sym15.*q1.*t10-coef_f2_q_sym17.*q1.*t12-coef_f2_q_sym20.*q3.*t10-coef_f2_q_sym21.*q2.*t12+coef_f0_q_sym2.*q0.*q1.*q2.*2.0+coef_f0_q_sym4.*q0.*q2.*q3.*2.0+coef_f0_q_sym7.*q1.*q2.*q3-coef_f2_q_sym6.*q0.*q1.*q2.*2.0-coef_f2_q_sym7.*q0.*q1.*q3.*2.0-coef_f2_q_sym9.*q0.*q2.*q3.*2.0-coef_f2_q_sym16.*q1.*q2.*q3,coef_f0_q_sym11.*q3-coef_f3_q_sym11.*q0.*2.0-coef_f3_q_sym18.*q1-coef_f3_q_sym22.*q2-coef_f3_q_sym24.*q3+coef_f0_q_sym10.*t13-coef_f3_q_sym1.*t7.*4.0-coef_f3_q_sym12.*t9-coef_f3_q_sym19.*t11-coef_f3_q_sym23.*t13+coef_f0_q_sym1.*q3.*t6.*3.0+coef_f0_q_sym4.*q0.*t12.*2.0+coef_f0_q_sym5.*q3.*t8+coef_f0_q_sym7.*q1.*t12+coef_f0_q_sym8.*q3.*t10+coef_f0_q_sym9.*q2.*t12-coef_f3_q_sym2.*q1.*t6.*3.0-coef_f3_q_sym3.*q2.*t6.*3.0-coef_f3_q_sym4.*q3.*t6.*3.0-coef_f3_q_sym5.*q0.*t8.*2.0-coef_f3_q_sym8.*q0.*t10.*2.0-coef_f3_q_sym10.*q0.*t12.*2.0-coef_f3_q_sym13.*q2.*t8-coef_f3_q_sym14.*q3.*t8-coef_f3_q_sym15.*q1.*t10-coef_f3_q_sym17.*q1.*t12-coef_f3_q_sym20.*q3.*t10-coef_f3_q_sym21.*q2.*t12+coef_f0_q_sym2.*q0.*q1.*q3.*2.0+coef_f0_q_sym3.*q0.*q2.*q3.*2.0+coef_f0_q_sym6.*q1.*q2.*q3-coef_f3_q_sym6.*q0.*q1.*q2.*2.0-coef_f3_q_sym7.*q0.*q1.*q3.*2.0-coef_f3_q_sym9.*q0.*q2.*q3.*2.0-coef_f3_q_sym16.*q1.*q2.*q3,q0.*2.0,t2+t3.*2.0+t4+t5+t14+t15.*4.0+t16+t17+t18.*2.0+t19+t20.*3.0+t21+t22+t23+t24+t25+t27-coef_f1_q_sym18.*q0-coef_f1_q_sym2.*t7-coef_f1_q_sym5.*q1.*t6.*2.0+coef_f0_q_sym13.*q2.*t8.*3.0-coef_f1_q_sym6.*q2.*t6+coef_f0_q_sym14.*q3.*t8.*3.0+coef_f0_q_sym15.*q1.*t10.*2.0-coef_f1_q_sym7.*q3.*t6+coef_f0_q_sym17.*q1.*t12.*2.0+coef_f0_q_sym20.*q3.*t10+coef_f0_q_sym21.*q2.*t12-coef_f1_q_sym12.*q0.*t8.*3.0-coef_f1_q_sym15.*q0.*t10-coef_f1_q_sym17.*q0.*t12+coef_f0_q_sym9.*q0.*q2.*q3-coef_f1_q_sym13.*q0.*q1.*q2.*2.0-coef_f1_q_sym14.*q0.*q1.*q3.*2.0-coef_f1_q_sym16.*q0.*q2.*q3,coef_f0_q_sym18.*q2-coef_f2_q_sym18.*q0+coef_f0_q_sym15.*t11-coef_f2_q_sym2.*t7+coef_f0_q_sym2.*q2.*t6+coef_f0_q_sym6.*q0.*t10+coef_f0_q_sym12.*q2.*t8.*3.0+coef_f0_q_sym13.*q1.*t10.*2.0+coef_f0_q_sym16.*q3.*t10+coef_f0_q_sym17.*q2.*t12-coef_f2_q_sym5.*q1.*t6.*2.0-coef_f2_q_sym6.*q2.*t6-coef_f2_q_sym7.*q3.*t6-coef_f2_q_sym12.*q0.*t8.*3.0-coef_f2_q_sym15.*q0.*t10-coef_f2_q_sym17.*q0.*t12+coef_f0_q_sym5.*q0.*q1.*q2.*2.0+coef_f0_q_sym7.*q0.*q2.*q3+coef_f0_q_sym14.*q1.*q2.*q3.*2.0-coef_f2_q_sym13.*q0.*q1.*q2.*2.0-coef_f2_q_sym14.*q0.*q1.*q3.*2.0-coef_f2_q_sym16.*q0.*q2.*q3,coef_f0_q_sym18.*q3-coef_f3_q_sym18.*q0+coef_f0_q_sym17.*t13-coef_f3_q_sym2.*t7+coef_f0_q_sym2.*q3.*t6+coef_f0_q_sym7.*q0.*t12+coef_f0_q_sym12.*q3.*t8.*3.0+coef_f0_q_sym14.*q1.*t12.*2.0+coef_f0_q_sym15.*q3.*t10+coef_f0_q_sym16.*q2.*t12-coef_f3_q_sym5.*q1.*t6.*2.0-coef_f3_q_sym6.*q2.*t6-coef_f3_q_sym7.*q3.*t6-coef_f3_q_sym12.*q0.*t8.*3.0-coef_f3_q_sym15.*q0.*t10-coef_f3_q_sym17.*q0.*t12+coef_f0_q_sym5.*q0.*q1.*q3.*2.0+coef_f0_q_sym6.*q0.*q2.*q3+coef_f0_q_sym13.*q1.*q2.*q3.*2.0-coef_f3_q_sym13.*q0.*q1.*q2.*2.0-coef_f3_q_sym14.*q0.*q1.*q3.*2.0-coef_f3_q_sym16.*q0.*q2.*q3,q1.*2.0,coef_f0_q_sym22.*q1-coef_f1_q_sym22.*q0-coef_f1_q_sym3.*t7+coef_f0_q_sym13.*t9+coef_f0_q_sym3.*q1.*t6+coef_f0_q_sym6.*q0.*t8-coef_f1_q_sym6.*q1.*t6+coef_f0_q_sym15.*q2.*t8.*2.0-coef_f1_q_sym8.*q2.*t6.*2.0+coef_f0_q_sym16.*q3.*t8-coef_f1_q_sym9.*q3.*t6+coef_f0_q_sym19.*q1.*t10.*3.0+coef_f0_q_sym21.*q1.*t12-coef_f1_q_sym13.*q0.*t8-coef_f1_q_sym19.*q0.*t10.*3.0-coef_f1_q_sym21.*q0.*t12+coef_f0_q_sym8.*q0.*q1.*q2.*2.0+coef_f0_q_sym9.*q0.*q1.*q3+coef_f0_q_sym20.*q1.*q2.*q3.*2.0-coef_f1_q_sym15.*q0.*q1.*q2.*2.0-coef_f1_q_sym16.*q0.*q1.*q3-coef_f1_q_sym20.*q0.*q2.*q3.*2.0,t2+t3+t4.*2.0+t5+t14+t15+t16.*4.0+t17+t18+t19.*2.0+t20+t21+t22.*3.0+t23+t24+t26+t27-coef_f2_q_sym22.*q0-coef_f2_q_sym3.*t7+coef_f0_q_sym13.*q2.*t8.*2.0+coef_f0_q_sym14.*q3.*t8+coef_f0_q_sym15.*q1.*t10.*3.0+coef_f0_q_sym17.*q1.*t12+coef_f0_q_sym20.*q3.*t10.*3.0-coef_f2_q_sym6.*q1.*t6+coef_f0_q_sym21.*q2.*t12.*2.0-coef_f2_q_sym8.*q2.*t6.*2.0-coef_f2_q_sym9.*q3.*t6-coef_f2_q_sym13.*q0.*t8-coef_f2_q_sym19.*q0.*t10.*3.0-coef_f2_q_sym21.*q0.*t12+coef_f0_q_sym7.*q0.*q1.*q3-coef_f2_q_sym15.*q0.*q1.*q2.*2.0-coef_f2_q_sym16.*q0.*q1.*q3-coef_f2_q_sym20.*q0.*q2.*q3.*2.0,coef_f0_q_sym22.*q3-coef_f3_q_sym22.*q0+coef_f0_q_sym21.*t13-coef_f3_q_sym3.*t7+coef_f0_q_sym3.*q3.*t6+coef_f0_q_sym9.*q0.*t12+coef_f0_q_sym13.*q3.*t8+coef_f0_q_sym16.*q1.*t12+coef_f0_q_sym19.*q3.*t10.*3.0+coef_f0_q_sym20.*q2.*t12.*2.0-coef_f3_q_sym6.*q1.*t6-coef_f3_q_sym8.*q2.*t6.*2.0-coef_f3_q_sym9.*q3.*t6-coef_f3_q_sym13.*q0.*t8-coef_f3_q_sym19.*q0.*t10.*3.0-coef_f3_q_sym21.*q0.*t12+coef_f0_q_sym6.*q0.*q1.*q3+coef_f0_q_sym8.*q0.*q2.*q3.*2.0+coef_f0_q_sym15.*q1.*q2.*q3.*2.0-coef_f3_q_sym15.*q0.*q1.*q2.*2.0-coef_f3_q_sym16.*q0.*q1.*q3-coef_f3_q_sym20.*q0.*q2.*q3.*2.0,q2.*2.0,coef_f0_q_sym24.*q1-coef_f1_q_sym24.*q0-coef_f1_q_sym4.*t7+coef_f0_q_sym14.*t9+coef_f0_q_sym4.*q1.*t6+coef_f0_q_sym7.*q0.*t8-coef_f1_q_sym7.*q1.*t6+coef_f0_q_sym16.*q2.*t8-coef_f1_q_sym9.*q2.*t6+coef_f0_q_sym17.*q3.*t8.*2.0+coef_f0_q_sym20.*q1.*t10+coef_f0_q_sym23.*q1.*t12.*3.0-coef_f1_q_sym10.*q3.*t6.*2.0-coef_f1_q_sym14.*q0.*t8-coef_f1_q_sym20.*q0.*t10-coef_f1_q_sym23.*q0.*t12.*3.0+coef_f0_q_sym9.*q0.*q1.*q2+coef_f0_q_sym10.*q0.*q1.*q3.*2.0+coef_f0_q_sym21.*q1.*q2.*q3.*2.0-coef_f1_q_sym16.*q0.*q1.*q2-coef_f1_q_sym17.*q0.*q1.*q3.*2.0-coef_f1_q_sym21.*q0.*q2.*q3.*2.0,coef_f0_q_sym24.*q2-coef_f2_q_sym24.*q0+coef_f0_q_sym20.*t11-coef_f2_q_sym4.*t7+coef_f0_q_sym4.*q2.*t6+coef_f0_q_sym9.*q0.*t10+coef_f0_q_sym14.*q2.*t8+coef_f0_q_sym16.*q1.*t10+coef_f0_q_sym21.*q3.*t10.*2.0-coef_f2_q_sym7.*q1.*t6+coef_f0_q_sym23.*q2.*t12.*3.0-coef_f2_q_sym9.*q2.*t6-coef_f2_q_sym10.*q3.*t6.*2.0-coef_f2_q_sym14.*q0.*t8-coef_f2_q_sym20.*q0.*t10-coef_f2_q_sym23.*q0.*t12.*3.0+coef_f0_q_sym7.*q0.*q1.*q2+coef_f0_q_sym10.*q0.*q2.*q3.*2.0+coef_f0_q_sym17.*q1.*q2.*q3.*2.0-coef_f2_q_sym16.*q0.*q1.*q2-coef_f2_q_sym17.*q0.*q1.*q3.*2.0-coef_f2_q_sym21.*q0.*q2.*q3.*2.0,t2+t3+t4+t5.*2.0+t14+t15+t16+t17.*4.0+t18+t19+t20+t21.*2.0+t22+t23.*3.0+t25+t26+t27-coef_f3_q_sym24.*q0-coef_f3_q_sym4.*t7+coef_f0_q_sym13.*q2.*t8+coef_f0_q_sym14.*q3.*t8.*2.0+coef_f0_q_sym15.*q1.*t10+coef_f0_q_sym17.*q1.*t12.*3.0+coef_f0_q_sym20.*q3.*t10.*2.0+coef_f0_q_sym21.*q2.*t12.*3.0-coef_f3_q_sym7.*q1.*t6-coef_f3_q_sym9.*q2.*t6-coef_f3_q_sym10.*q3.*t6.*2.0-coef_f3_q_sym14.*q0.*t8-coef_f3_q_sym20.*q0.*t10-coef_f3_q_sym23.*q0.*t12.*3.0+coef_f0_q_sym6.*q0.*q1.*q2-coef_f3_q_sym16.*q0.*q1.*q2-coef_f3_q_sym17.*q0.*q1.*q3.*2.0-coef_f3_q_sym21.*q0.*q2.*q3.*2.0,q3.*2.0],[4,4]);


function hash = wsddnVOChash_init(strs)
% From the PASCAL VOC 2011 devkit

hsize=4999;
hash.key=cell(hsize,1);
hash.val=cell(hsize,1);

for i=1:numel(strs)
    s=strs{i};
    h=mod(str2double(s([4 6:end])),hsize)+1;
    j=numel(hash.key{h})+1;
    hash.key{h}{j}=strs{i};
    hash.val{h}(j)=i;
end


function [net, info] = bcnn_train_simplenn(net, imdb, getBatch_train, getBatch_val, varargin)

% BNN_TRAIN_SW   training a symmetric BCNN
%    BCNN_TRAIN() is an example learner implementing stochastic gradient
%    descent with momentum to train a symmetric BCNN for image classification.
%    The net should built in simplenn structure

% INPUT
% net: a network with a bcnn layer
% imdb: imdb structure of a dataset
% getBatch_train: function to read a batch of training images
% getBatch_val: function to read a batch of validation images

% OUTPUT
% net: bcnn network after fine-tuning
% info: log of training and validation

% A symmetric BCNN consists of multiple layers of convolutions, pooling, and
% nonlinear activation. A bilinearpool layer is built on top followed by square-root
% and L2 normalization and a sofmaxloss layer.

% Copyright (C) 2015 Tsung-Yu Lin, Aruni RoyChowdhury, Subhransu Maji.
% All rights reserved.
%
% This file is part of the BCNN and is made available under
% the terms of the BSD license (see the COPYING file).

% This function is modified from CNN_TRAIN of MatConvNet

% basic setting
opts.train = [] ;
opts.val = [] ;
opts.numEpochs = 300 ;
opts.batchSize = 256 ;
opts.numSubBatches = 1 ;
opts.gpus = [] ; % which GPU devices to use (none, one, or more)
opts.learningRate = 0.001 ;
opts.continue = false ;
opts.expDir = fullfile('data','exp') ;
opts.conserveMemory = false ;
opts.backPropDepth = +inf ;
opts.sync = false ;
opts.prefetch = false ;
opts.cudnn = true ;
opts.weightDecay = 0.0005 ;
opts.momentum = 0.9 ;
opts.errorFunction = 'multiclass' ;
opts.errorLabels = {} ;
opts.plotDiagnostics = false ;
opts.memoryMapFile = fullfile(tempdir, 'matconvnet.bin') ;

opts = vl_argparse(opts, varargin) ;

if ~exist(opts.expDir, 'dir'), mkdir(opts.expDir) ; end
if isempty(opts.train), opts.train = find(imdb.images.set==1) ; end
if isempty(opts.val), opts.val = find(imdb.images.set==2) ; end
if isnan(opts.train), opts.train = [] ; end

% -------------------------------------------------------------------------
%                                                    Network initialization
% -------------------------------------------------------------------------
% set hyperparameters
evaluateMode = isempty(opts.train) ;

if ~evaluateMode
  for i=1:numel(net.layers)
    if isfield(net.layers{i}, 'weights')
      J = numel(net.layers{i}.weights) ;
      for j=1:J
        net.layers{i}.momentum{j} = zeros(size(net.layers{i}.weights{j}), 'single') ;
      end
      if ~isfield(net.layers{i}, 'learningRate')
        net.layers{i}.learningRate = ones(1, J, 'single') ;
      end
      if ~isfield(net.layers{i}, 'weightDecay')
        net.layers{i}.weightDecay = ones(1, J, 'single') ;
      end
    end
    % Legacy code: will be removed
    if isfield(net.layers{i}, 'filters')
      net.layers{i}.momentum{1} = zeros(size(net.layers{i}.filters), 'single') ;
      net.layers{i}.momentum{2} = zeros(size(net.layers{i}.biases), 'single') ;
      if ~isfield(net.layers{i}, 'learningRate')
        net.layers{i}.learningRate = ones(1, 2, 'single') ;
      end
      if ~isfield(net.layers{i}, 'weightDecay')
        net.layers{i}.weightDecay = single([1 0]) ;
      end
    end
  end
end

% -------------------------------------------------------------------------
%                                                Move network to GPU or CPU
% -------------------------------------------------------------------------

% setup GPUs
numGpus = numel(opts.gpus) ;
if numGpus > 1
  if isempty(gcp('nocreate')),
    parpool('local',numGpus) ;
    spmd, gpuDevice(opts.gpus(labindex)), end
  end
elseif numGpus == 1
  gpuDevice(opts.gpus)
end
if exist(opts.memoryMapFile), delete(opts.memoryMapFile) ; end

% setup error calculation function
if isstr(opts.errorFunction)
  switch opts.errorFunction
    case 'none'
      opts.errorFunction = @error_none ;
    case 'multiclass'
      opts.errorFunction = @error_multiclass ;
      if isempty(opts.errorLabels), opts.errorLabels = {'top1e', 'top5e'} ; end
    case 'binary'
      opts.errorFunction = @error_binary ;
      if isempty(opts.errorLabels), opts.errorLabels = {'bine'} ; end
    otherwise
      error('Uknown error function ''%s''', opts.errorFunction) ;
  end
end

modelPath = @(ep) fullfile(opts.expDir, sprintf('net-epoch-%d.mat', ep));
modelFigPath = fullfile(opts.expDir, 'net-train.pdf') ;

start = opts.continue * findLastCheckpoint(opts.expDir) ;
if start >= 1
  fprintf('resuming by loading epoch %d\n', start) ;
  load(modelPath(start), 'net', 'info') ;
end

for epoch=start+1:opts.numEpochs

  % train one epoch and validate
  learningRate = opts.learningRate(min(epoch, numel(opts.learningRate))) ;
  train = opts.train(randperm(numel(opts.train))) ; % shuffle
  val = opts.val ;
  if numGpus <= 1
    [net,stats.train] = process_epoch(opts, getBatch_train, epoch, train, learningRate, imdb, net) ;
    [~,stats.val] = process_epoch(opts, getBatch_val, epoch, val, 0, imdb, net) ;
  else
    spmd(numGpus)
      [net_, stats_train_] = process_epoch(opts, getBatch_train, epoch, train, learningRate, imdb, net) ;
      [~, stats_val_] = process_epoch(opts, getBatch_val, epoch, val, 0, imdb, net_) ;
    end
    net = net_{1} ;
    stats.train = sum([stats_train_{:}],2) ;
    stats.val = sum([stats_val_{:}],2) ;
  end

  % save
  if evaluateMode, sets = {'val'} ; else sets = {'train'} ; end
  if ~evaluateMode && ~isempty(opts.val)
      sets{2} = 'val';
  end
  for f = sets
    f = char(f) ;
    n = numel(eval(f)) ;
    info.(f).speed(epoch) = n / stats.(f)(1) * max(1, numGpus) ;
    info.(f).objective(epoch) = stats.(f)(2) / n ;
    info.(f).error(:,epoch) = stats.(f)(3:end) / n ;
  end
  if ~evaluateMode, save(modelPath(epoch), 'net', 'info') ; end

  figure(1) ; clf ;
  hasError = isa(opts.errorFunction, 'function_handle') ;
  subplot(1,1+hasError,1) ;
  if ~evaluateMode
    semilogy(1:epoch, info.train.objective, '.-', 'linewidth', 2) ;
    hold on ;
  end
  if ~isempty(opts.val)
      semilogy(1:epoch, info.val.objective, '.--') ;
      xlabel('training epoch') ; ylabel('energy') ;
  end
  grid on ;
  h=legend(sets) ;
  set(h,'color','none');
  title('objective') ;
  if hasError
    subplot(1,2,2) ; leg = {} ;
    if ~evaluateMode
      plot(1:epoch, info.train.error', '.-', 'linewidth', 2) ;
      hold on ;
      leg = horzcat(leg, strcat('train ', opts.errorLabels)) ;
    end

    if ~isempty(opts.val)
        plot(1:epoch, info.val.error', '.--') ;
        leg = horzcat(leg, strcat('val ', opts.errorLabels)) ;
    end
    set(legend(leg{:}),'color','none') ;
    grid on ;
    xlabel('training epoch') ; ylabel('error') ;
    title('error') ;
  end
  drawnow ;
  print(1, modelFigPath, '-dpdf') ;
end

% -------------------------------------------------------------------------
function err = error_multiclass(opts, labels, res)
% -------------------------------------------------------------------------
predictions = gather(res(end-1).x) ;
[~,predictions] = sort(predictions, 3, 'descend') ;

% be resilient to badly formatted labels
if numel(labels) == size(predictions, 4)
  labels = reshape(labels,1,1,1,[]) ;
end

% skip null labels
mass = single(labels(:,:,1,:) > 0) ;
if size(labels,3) == 2
  % if there is a second channel in labels, used it as weights
  mass = mass .* labels(:,:,2,:) ;
  labels(:,:,2,:) = [] ;
end

error = ~bsxfun(@eq, predictions, labels) ;
err(1,1) = sum(sum(sum(mass .* error(:,:,1,:)))) ;
err(2,1) = sum(sum(sum(mass .* min(error(:,:,1:5,:),[],3)))) ;

% -------------------------------------------------------------------------
function err = error_binaryclass(opts, labels, res)
% -------------------------------------------------------------------------
predictions = gather(res(end-1).x) ;
error = bsxfun(@times, predictions, labels) < 0 ;
err = sum(error(:)) ;

% -------------------------------------------------------------------------
function err = error_none(opts, labels, res)
% -------------------------------------------------------------------------
err = zeros(0,1) ;

% -------------------------------------------------------------------------
function  [net_cpu,stats,prof] = process_epoch(opts, getBatch, epoch, subset, learningRate, imdb, net_cpu)
% -------------------------------------------------------------------------

% move CNN to GPU as needed
numGpus = numel(opts.gpus) ;
if numGpus >= 1
  net = vl_simplenn_move(net_cpu, 'gpu') ;
else
  net = net_cpu ;
  net_cpu = [] ;
end

% validation mode if learning rate is zero
training = learningRate > 0 ;if training
  mode = 'train' ;
  evalMode = 'normal' ;
else
  mode = 'val' ;
  evalMode = 'test' ;
end
if nargout > 2, mpiprofile on ; end

numGpus = numel(opts.gpus) ;
if numGpus >= 1
  one = gpuArray(single(1)) ;
else
  one = single(1) ;
end
res = [] ;
mmap = [] ;
stats = [] ;
start = tic ;

for t=1:opts.batchSize:numel(subset)
  fprintf('%s: epoch %02d: batch %3d/%3d: ', mode, epoch, ...
          fix(t/opts.batchSize)+1, ceil(numel(subset)/opts.batchSize)) ;
  batchSize = min(opts.batchSize, numel(subset) - t + 1) ;
  numDone = 0 ;
  error = [] ;
  for s=1:opts.numSubBatches
    % get this image batch and prefetch the next
    batchStart = t + (labindex-1) + (s-1) * numlabs ;
    batchEnd = min(t+opts.batchSize-1, numel(subset)) ;
    batch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
    [im, labels] = getBatch(imdb, batch) ;

    if opts.prefetch
      if s==opts.numSubBatches
        batchStart = t + (labindex-1) + opts.batchSize ;
        batchEnd = min(t+2*opts.batchSize-1, numel(subset)) ;
      else
        batchStart = batchStart + numlabs ;
      end
      nextBatch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
      getBatch(imdb, nextBatch) ;
    end

    if numGpus >= 1
      im = gpuArray(im{1}) ;
    end

    % evaluate CNN
    net.layers{end}.class = labels ;
    if training, dzdy = one; else, dzdy = [] ; end
    if s==1, res = []; end
    res = vl_bilinearnn(net, im, dzdy, res, ...
                      'accumulate', s ~= 1, ...
                      'mode', evalMode, ...
                      'conserveMemory', opts.conserveMemory, ...
                      'backPropDepth', opts.backPropDepth, ...
                      'sync', opts.sync, ...
                      'cudnn', opts.cudnn) ;

    % accumulate training errors
    error = sum([error, [...
      sum(double(gather(res(end).x))) ;
      reshape(opts.errorFunction(opts, labels, res),[],1) ; ]],2) ;
    numDone = numDone + numel(batch) ;
  end

  % gather and accumulate gradients across labs
  if training
    if numGpus <= 1
      [net,res] = accumulate_gradients(opts, learningRate, batchSize, net, res) ;
    else
      if isempty(mmap)
        mmap = map_gradients(opts.memoryMapFile, net, res, numGpus) ;
      end
      write_gradients(mmap, net, res) ;
      labBarrier() ;
      [net,res] = accumulate_gradients(opts, learningRate, batchSize, net, res, mmap) ;
    end
  end

  % print learning statistics

  time = toc(start) ;
  stats = sum([stats,[0 ; error]],2); % works even when stats=[]
  stats(1) = time ;
  n = (t + batchSize - 1) / max(1,numlabs) ;
  speed = n/time ;
  fprintf('%.1f Hz%s\n', speed) ;

  fprintf(' obj:%.3g', stats(2)/n) ;
  for i=1:numel(opts.errorLabels)
    fprintf(' %s:%.3g', opts.errorLabels{i}, stats(i+2)/n) ;
  end
  fprintf(' [%d/%d]', numDone, batchSize);
  fprintf('\n') ;

  % debug info
  if opts.plotDiagnostics && numGpus <= 1
    figure(2) ; vl_simplenn_diagnose(net,res) ; drawnow ;
  end
end

if nargout > 2
  prof = mpiprofile('info');
  mpiprofile off ;
end

if numGpus >= 1
  net_cpu = vl_simplenn_move(net, 'cpu') ;
else
  net_cpu = net ;
end

% -------------------------------------------------------------------------
function [net,res] = accumulate_gradients(opts, lr, batchSize, net, res, mmap)
% -------------------------------------------------------------------------
if nargin >= 6
  numGpus = numel(mmap.Data) ;
else
  numGpus = 1 ;
end

for l=numel(net.layers):-1:1
  for j=1:numel(res(l).dzdw)

    % accumualte from multiple labs (GPUs) if needed
    if numGpus > 1
      tag = sprintf('l%d_%d',l,j) ;
      tmp = zeros(size(mmap.Data(labindex).(tag)), 'single') ;
      for g = setdiff(1:numGpus, labindex)
        tmp = tmp + mmap.Data(g).(tag) ;
      end
      res(l).dzdw{j} = res(l).dzdw{j} + tmp ;
    end

    if j == 3 && strcmp(net.layers{l}.type, 'bnorm')
      % special case for learning bnorm moments
      thisLR = net.layers{l}.learningRate(j) ;
      net.layers{l}.weights{j} = ...
        (1-thisLR) * net.layers{l}.weights{j} + ...
        (thisLR/batchSize) * res(l).dzdw{j} ;
    else
      % standard gradient training
      thisDecay = opts.weightDecay * net.layers{l}.weightDecay(j) ;
      thisLR = lr * net.layers{l}.learningRate(j) ;
      net.layers{l}.momentum{j} = ...
        opts.momentum * net.layers{l}.momentum{j} ...
        - thisDecay * net.layers{l}.weights{j} ...
        - (1 / batchSize) * res(l).dzdw{j} ;
      net.layers{l}.weights{j} = net.layers{l}.weights{j} + ...
        thisLR * net.layers{l}.momentum{j} ;
    end
  end
end

% -------------------------------------------------------------------------
function mmap = map_gradients(fname, net, res, numGpus)
% -------------------------------------------------------------------------
format = {} ;
for i=1:numel(net.layers)
  for j=1:numel(res(i).dzdw)
    format(end+1,1:3) = {'single', size(res(i).dzdw{j}), sprintf('l%d_%d',i,j)} ;
  end
end
format(end+1,1:3) = {'double', [3 1], 'errors'} ;
if ~exist(fname) && (labindex == 1)
  f = fopen(fname,'wb') ;
  for g=1:numGpus
    for i=1:size(format,1)
      fwrite(f,zeros(format{i,2},format{i,1}),format{i,1}) ;
    end
  end
  fclose(f) ;
end
labBarrier() ;
mmap = memmapfile(fname, 'Format', format, 'Repeat', numGpus, 'Writable', true) ;

% -------------------------------------------------------------------------
function write_gradients(mmap, net, res)
% -------------------------------------------------------------------------
for i=1:numel(net.layers)
  for j=1:numel(res(i).dzdw)
    mmap.Data(labindex).(sprintf('l%d_%d',i,j)) = gather(res(i).dzdw{j}) ;
  end
end

% -------------------------------------------------------------------------
function epoch = findLastCheckpoint(modelDir)
% -------------------------------------------------------------------------
list = dir(fullfile(modelDir, 'net-epoch-*.mat')) ;
tokens = regexp({list.name}, 'net-epoch-([\d]+).mat', 'tokens') ;
epoch = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
epoch = max([epoch 0]) ;


function [s, cfg] = ft_statfun_actvsblT(cfg, dat, design)

% FT_STATFUN_ACTVSBLT calculates the activation-versus-baseline T-statistic on the
% biological data (the dependent variable), using the information on the independent
% variable (ivar) in design.
%
% Note that it does not make sense to use this test statistic when baseline
% correction was performed by subtracting the mean of the baseline period from the
% whole data (for ERP data) or by dividing by the mean (for TFR data). If baseline
% correction is desired, you should subtract the full baseline and activation period.
%
% Use this function by calling one of the high-level statistics functions as
%   [stat] = ft_timelockstatistics(cfg, timelock1, timelock2, ...)
%   [stat] = ft_freqstatistics(cfg, freq1, freq2, ...)
%   [stat] = ft_sourcestatistics(cfg, source1, source2, ...)
% with the following configuration option
%   cfg.statistic = 'ft_statfun_actvsblT'
%
% Configuration options
%   cfg.computestat    = 'yes' or 'no', calculate the statistic (default='yes')
%   cfg.computecritval = 'yes' or 'no', calculate the critical values of the test statistics (default='no')
%   cfg.computeprob    = 'yes' or 'no', calculate the p-values (default='no')
%
% The following options are relevant if cfg.computecritval='yes' and/or
% cfg.computeprob='yes'.
%   cfg.alpha = critical alpha-level of the statistical test (default=0.05)
%   cfg.tail  = -1, 0, or 1, left, two-sided, or right (default=1)
%               cfg.tail in combination with cfg.computecritval='yes'
%               determines whether the critical value is computed at
%               quantile cfg.alpha (with cfg.tail=-1), at quantiles
%               cfg.alpha/2 and (1-cfg.alpha/2) (with cfg.tail=0), or at
%               quantile (1-cfg.alpha) (with cfg.tail=1).
%
% Design specification
%   cfg.ivar  = row number of the design that contains the labels of the conditions that must be
%               compared (default=1). The first condition, indicated by 1, corresponds to the
%               activation period and the second, indicated by 2, corresponds to the baseline period.
%   cfg.uvar  = row number of design that contains the labels of the units-of-observation (subjects or trials)
%               (default=2). The labels are assumed to be integers ranging from 1 to
%               the number of units-of-observation.
%
% See also FT_TIMELOCKSTATISTICS, FT_FREQSTATISTICS or FT_SOURCESTATISTICS

% Copyright (C) 2006, Eric Maris
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% set defaults
if ~isfield(cfg, 'computestat'),       cfg.computestat='yes';     end
if ~isfield(cfg, 'computecritval'),    cfg.computecritval='no';   end
if ~isfield(cfg, 'computeprob'),       cfg.computeprob='no';      end
if ~isfield(cfg, 'alpha'),             cfg.alpha=0.05;            end
if ~isfield(cfg, 'tail'),              cfg.tail=1;                end

% perform some checks on the configuration
if strcmp(cfg.computeprob,'yes') && strcmp(cfg.computestat,'no')
  ft_error('P-values can only be calculated if the test statistics are calculated.');
end

% calculate the number of time samples
switch cfg.dimord
  case 'chan_freq_time'
    nchan = cfg.dim(1);
    nfreq = cfg.dim(2);
    ntime = cfg.dim(3);
    [nsmpls,nrepl] = size(dat);
  otherwise
    ft_error('Inappropriate dimord for the statistics function FT_STATFUN_ACTVSBLT.');
end

sel1 = find(design(cfg.ivar,:)==1);
sel2 = find(design(cfg.ivar,:)==2);
n1  = length(sel1);
n2  = length(sel2);
if (n1+n2)<size(design,2) || (n1~=n2)
  ft_error('Invalid specification of the design array.');
end
nunits = max(design(cfg.uvar,:));
df = nunits - 1;
if nunits<2
  ft_error('The data must contain at least two units (trials or subjects).')
end
if (nunits*2)~=(n1+n2)
  ft_error('Invalid specification of the design array.');
end

if strcmp(cfg.computestat,'yes')
  % compute the statistic
  % calculate the time averages of the activation and the baseline period
  % for all units-of-observation.
  meanreshapeddat=nanmean(reshape(dat,nchan,nfreq,ntime,nrepl),3);
  timeavgdat=repmat(reshape(meanreshapeddat,(nchan*nfreq),nrepl),ntime,1);

  % store the positions of the 1-labels and the 2-labels in a nunits-by-2 array
  poslabelsperunit=zeros(nunits,2);
  poslabel1=find(design(cfg.ivar,:)==1);
  poslabel2=find(design(cfg.ivar,:)==2);
  [dum,i]=sort(design(cfg.uvar,poslabel1),'ascend');
  poslabelsperunit(:,1)=poslabel1(i);
  [dum,i]=sort(design(cfg.uvar,poslabel2),'ascend');
  poslabelsperunit(:,2)=poslabel2(i);

  % calculate the differences between the conditions
  diffmat=dat(:,poslabelsperunit(:,1))-timeavgdat(:,poslabelsperunit(:,2));

  % calculate the dependent samples t-statistics
  avgdiff=nanmean(diffmat,2);
  vardiff=nanvar(diffmat,0,2);
  s.stat=sqrt(nunits)*avgdiff./sqrt(vardiff);
end

if strcmp(cfg.computecritval,'yes')
  % also compute the critical values
  s.df      = df;
  if cfg.tail==-1
    s.critval = tinv(cfg.alpha,df);
  elseif  cfg.tail==0
    s.critval = [tinv(cfg.alpha/2,df),tinv(1-cfg.alpha/2,df)];
  elseif cfg.tail==1
    s.critval = tinv(1-cfg.alpha,df);
  end
end

if strcmp(cfg.computeprob,'yes')
  % also compute the p-values
  s.df      = df;
  if cfg.tail==-1
    s.prob = tcdf(s.stat,s.df);
  elseif  cfg.tail==0
    s.prob = 2*tcdf(-abs(s.stat),s.df);
  elseif cfg.tail==1
    s.prob = 1-tcdf(s.stat,s.df);
  end
end


function [out,cl] = imageCluster_block(varargin)
% :Usage:
% ::
%
%    [out,cl] = imageCluster_block(input arguments)
%
% Images a cluster isosurface on an existing 3D head or brain plot
%
% works with tsu (Talairach Space Utility) main window as current figure
% old: clusters = getappdata(gcf, 'clusters');
%
% Inputs (in any order): keyword followed by input argument
%
%   **'cluster':**
%        followed by cluster to image, from SPM or TSU.
%
%   **'getclusters':**
%        no other args necessary - starts gui for cluster selection
%        function returns all clusters.  select with clusters(i)
%
%   **'getfigclusters':**
%        get clusters from TSU main figure.  must be current figure.
%
%   **'figure':**
%        create a new figure to image color on
%
%   **'color':**
%        followed by color value - either text or vector
%
%   **'alpha':**
%        followed by transparency value for cluster surface, 0-1
%        1 is opaque, 0 is completely transparent
%
% :Outputs:
%
%   **out:**
%        Patch handle
%
%   **cl:**
%        cluster struct
%
% ..
%    Works in Matlab 5.3, but with no transparency.
%    By Tor Wager, 10/3/2001, last edit 10/18/01
% ..

% ..
%    Set up arguments and default values
% ..
clusters = [];
mycolor = 'y';
myalpha = 1;

for i = 1:nargin
    if isstr(varargin{i})
        switch varargin{i}
        case 'cluster', cl = varargin{i+1};
        case 'getclusters', clusters = tor_ihb_getClusters;
        case 'getfigclusters', clusters = getappdata(gcf, 'clusters');
        case 'figure', h3dfig = figure; set(h3dfig,'Tag','myFig');
        case 'color',mycolor = varargin{i+1};
        case 'alpha',myalpha = varargin{i+1};
        end
    end
end

% If you chose to get clusters, no imaging done - just returns all clusters.
if ~isempty(clusters), out = clusters;, return, end

% do not do this [X,Y,Z] = meshgrid(x,y,z);

%------------------------------------------------------------------------------
% Prepare volume data and create isosurface patch object
%------------------------------------------------------------------------------
    try
	    V = cl.vTal;
        %V = smooth3(cl.vTal,'gaussian',[5 5 5]);
        V = smooth3(cl.vTal);
    catch
	    % no vTal field, make it here.
	    disp(['No vTal field for cluster, attempting to create it.'])
	    cl = get_cluster_volume(cl);
	    V = cl.vTal;
        %V = smooth3(cl.vTal,'gaussian',[5 5 5]);
        V = smooth3(cl.vTal);
    end

	if isfield(cl,'hThreshold'), height_threshold = cl.hThreshold;
	elseif isfield(cl,'threshold'), height_threshold = cl.threshold;
	else height_threshold = 0;,warning('No height threshold found in cluster.')
	end

    %height_threshold = min(cl.Z);
    % to image the whole cluster.  Smoothing will increase the cluster size if you choose this!
    height_threshold = 0;   % set this to 0 because all above 0 should be imaged.

    % use with smoothing, to smooth out the clusters and keep them a reasonable size
    %height_threshold = .5 * mean(mean(mean(V)));

 	set(0,'CurrentFigure', gcf)
	%set(gcf, 'CurrentAxes', gca);

	FV = isosurface(cl.xTal, cl.yTal, cl.zTal, V, height_threshold);
	hPatch = patch(FV);
    IC = isocaps(cl.xTal, cl.yTal, cl.zTal, V, height_threshold);
    iPatch = patch(IC);
	isonormals(cl.xTal, cl.yTal, cl.zTal, cl.vTal, hPatch);
	try
		set(hPatch, 'Tag', 'ihb_surfaceCluster', 'FaceColor', mycolor, 'EdgeColor', 'none', 'FaceAlpha', myalpha);
        set(iPatch, 'Tag', 'ihb_surfaceCluster', 'FaceColor', mycolor, 'EdgeColor', 'none', 'FaceAlpha', myalpha);
	catch
		set(hPatch, 'Tag', 'ihb_surfaceCluster', 'FaceColor', mycolor, 'EdgeColor', 'none');
        set(iPatch, 'Tag', 'ihb_surfaceCluster', 'FaceColor', mycolor, 'EdgeColor', 'none');
	end

    %delete(hMsg);
    set(gcf, 'Visible', 'on');
    out = hPatch;

return


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 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 simulationClass<handle
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % The  ``simulationClass`` creates a ``simu`` object saved to the MATLAB
    % workspace. The ``simulationClass`` includes properties and methods used
    % to define WEC-Sim's simulation parameters and settings.
    %
    %.. autoattribute:: objects.simulationClass.simulationClass
    %
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    properties (SetAccess = 'public', GetAccess = 'public') % WEC-Sim input
        adjMassFactor       = 2                                            % (`integer`) Weighting function for adjusting added mass term in the translational direction. Default = ``2``
        b2b                 = 0                                            % (`integer`) Flag for body2body interactions, Options: 0 (off), 1 (on). Default = ``0``
        cicDt               = []                                           % (`float`) Time step to calculate Convolution Integral. Default = ``dt``
        cicEndTime          = 60                                           % (`float`) Convolution integral time. Default = ``60`` s
        domainSize          = 200                                          % (`float`) Size of free surface and seabed. This variable is only used for visualization. Default = ``200`` m
        explorer            = 'on'                                         % (`string`) SimMechanics Explorer 'on' or 'off'. Default = ``'on'``
        dt                  = 0.1                                          % (`float`) Simulation time step. Default = ``0.1`` s
        dtOut               = []                                           % (`float`) Output sampling time. Default = ``dt``
        endTime             = []                                           % (`float`) Simulation end time. Default = ``[]``
        gravity             = 9.81                                         % (`float`) Acceleration due to gravity. Default = ``9.81`` m/s
        mcrMatFile          = []                                           % (`string`) mat file that contain a list of the multiple conditions runs with given conditions. Default = ``[]``
        mcrExcelFile        = []                                           % (`string`) File name from which to load wave statistics data. Default = ``[]``
        mode                = 'normal'                                     % (`string`) Simulation execution mode, 'normal', 'accelerator', 'rapid-accelerator'. Default = ``'normal'``
        morisonDt           = []                                           % (`float`) Sample time to calculate Morison Element forces. Default = ``dt``
        nonlinearDt         = []                                           % (`float`) Sample time to calculate nonlinear forces. Default = ``dt``
        paraview            = struct(...                                   % (`structure`) Defines the Paraview visualization.
            'option',           0,...                                      %
            'startTime',        0, ...                                     %
            'endTime',          100, ...                                   %
            'dt',               0.1, ...                                   %
            'path',             'vtk')                                     % (`structure`) Defines the Paraview visualization. ``option`` (`integer`) Flag for paraview visualization, and writing vtp files, Options: 0 (off) , 1 (on). Default = ``0``. ``startTime`` (`float`) Start time for the vtk file of Paraview. Default = ``0``. ``endTime`` (`float`) End time for the vtk file of Paraview. Default = ``100``.  ``dt`` (`float`) Timestep for Paraview. Default = ``0.1``. ``path`` (`string`) Path of the folder for Paraview vtk files. Default = ``'vtk'``.
        pressure            = 0                                            % (`integer`) Flag to save pressure distribution, Options: 0 (off), 1 (on). Default = ``0``
        rampTime            = 100                                          % (`float`) Ramp time for wave forcing. Default = ``100`` s
        rateTransition      = 'on'                                         % (`string`) Flag for automatically handling rate transition for data transfer, Opyions: 'on', 'off'. Default = ``'on'``
        reloadH5Data        = 0                                            % (`integer`) Flag to re-load hydro data from h5 file between runs, Options: 0 (off), 1 (on). Default = ``0``
        rho                 = 1000                                         % (`float`) Density of water. Default = ``1000`` kg/m^3
        saveStructure       = 0                                            % (`integer`) Flag to save results as a MATLAB structure, Options: 0 (off), 1 (on). Default = ``0``
        saveText            = 0                                            % (`integer`) Flag to save results as ASCII files, Options: 0 (off), 1 (on). Default = ``0``
        saveWorkspace       = 1                                            % (`integer`) FLag to save .mat file for each run, Options: 0 (off), 1 (on). Default = ``1``
        simMechanicsFile    = 'NOT DEFINED'                                % (`string`) Simulink/SimMechanics model file. Default = ``'NOT DEFINED'``
        solver              = 'ode4'                                       % (`string`) PDE solver used by the Simulink/SimMechanics simulation. Any continuous solver in Simulink possible. Recommended to use 'ode4, 'ode45' for WEC-Sim. Default = ``'ode4'``
        stateSpace          = 0                                            % (`integer`) Flag for convolution integral or state-space calculation, Options: 0 (convolution integral), 1 (state-space). Default = ``0``
        startTime           = 0                                            % (`float`) Simulation start time. Default = ``0`` s
        zeroCross           = 'DisableAll'                                 % (`string`) Disable zero cross control. Default = ``'DisableAll'``
        outputDir           = 'output'                                     % (`string`) Data output directory name. Default = ``'output'``
    end

    properties (SetAccess = 'public', GetAccess = 'public') % internal WEC-Sim
        caseDir             = []                                           % (`string`) WEC-Sim case directory. Default = dependent
        numCables           = 0                                            % (`integer`) Number of cables in the wec model. Default = ``0``
        numConstraints      = 0                                            % (`integer`) Number of contraints in the wec model. Default = ``0``
        numDragBodies       = 0                                            % (`integer`) Number of drag bodies that comprise the WEC device (excluding hydrodynamic bodies). Default = ``0``
        numHydroBodies      = 0                                            % (`integer`) Number of hydrodynamic bodies that comprise the WEC device. Default = ``0``
        numMoorings         = 0                                            % (`integer`) Number of moorings in the wec model. Default = ``0``
        numPtos             = 0                                            % (`integer`) Number of power take-off elements in the model. Default = ``0``
        numPtoSim           = 0                                            % (`integer`) Number of PTO-Sim elements in the model. Default = ``0``
        time                = 0                                            % (`float`) Simulation time [s]. Default = ``0`` s
    end

    properties (SetAccess = 'private', GetAccess = 'public') % internal WEC-Sim
        caseFile            = []                                           % (`string`) .mat file with all simulation information. Default = dependent
        cicLength           = []                                           % (`integer`) Number of timesteps in the convolution integral length. Default = dependent
        cicTime             = []                                           % (`float vector`) Convolution integral time series. Default = dependent
        date                = datetime                                     % (`string`) Simulation date and time
        gitCommit           = []                                           % (`string`) GitHub commit
        maxIt               = []                                           % (`integer`) Total number of simulation time steps. Approximate for variable step solvers. Default = dependent
        wsVersion           = '5.0.1'                                        % (`string`) WEC-Sim version
    end

    methods
        function obj = simulationClass()
            % This method initializes the ``simulationClass``.
            fprintf('WEC-Sim: An open-source code for simulating wave energy converters\n')
            fprintf('Version: %s\n\n',obj.wsVersion)
            fprintf('Initializing the Simulation Class...\n')
            obj.caseDir = pwd;
            fprintf('\tCase Dir: %s \n',obj.caseDir)
            obj.outputDir = ['.' filesep obj.outputDir];
        end

        function checkInputs(obj)
            % This method checks WEC-Sim user inputs and generates error messages if parameters are not properly defined.

            % Check that simMechanics file exists
            obj.simMechanicsFile = [obj.caseDir filesep obj.simMechanicsFile];
            if exist(obj.simMechanicsFile,'file') ~= 4
                error('The simMechanics file, %s, does not exist in the case directory',obj.simMechanicsFile)
            end
            % Check 'simu.paraview' fields
            if length(fieldnames(obj.paraview)) ~=5
                error(['Unrecognized method, property, or field for class "simulationClass", ' ...
                    '"simulationClass.paraview" structure must only include fields: "option", "startTime", "endTime", "dt", "outputDir"']);
            end
            % Check that visualization is off when using accelerator modes
            if (strcmp(obj.mode,'accelerator') || strcmp(obj.mode,'rapid-accelerator')) ...
                    && strcmp(obj.explorer,'on')
                warning('Mechanics explorer not allowed in accelerator or rapid-accelerator modes. Turning mechanics explorer off.');
                obj.explorer = 'off';
            end

             % Checks user input to ensure that ``simu.endTime`` is specified
            if isempty(obj.endTime) || obj.endTime < obj.startTime
                error('simu.endTime, the simulation end time must be specified in the wecSimInputFile and must be greater than simu.startTime.')
            end
            % Checks user input to ensure that output timestep is larger
            % than or equal to simulation timestep
            if obj.dtOut < obj.dt
                warning('Simu.dtOut, the output timestep, should be greater than or equal to simu.dt, the simulation timestep. The smaller output timestep will be overwritten with the value of the simulation timestep to avoid errors.')
            end
        end

        function setup(obj)
            % Sets simulation properties based on values specified in input file
            obj.time = obj.startTime:obj.dt:obj.endTime;
            obj.maxIt = floor((obj.endTime - obj.startTime) / obj.dt);
            % Set dtOut if it was not specificed in input file
            if isempty(obj.dtOut) || obj.dtOut < obj.dt
                obj.dtOut = obj.dt;
            end
            % Set nonlinearDt if it was not specificed in input file
            if isempty(obj.nonlinearDt) || obj.nonlinearDt < obj.dt
                obj.nonlinearDt = obj.dt;
            end
            % Set cicDt if it was not specificed in input file
            if isempty(obj.cicDt) || obj.cicDt < obj.dt
                obj.cicDt = obj.dt;
            end
            % Set morisonDt if it was not specificed in input file
            if isempty(obj.morisonDt) || obj.morisonDt < obj.dt
                obj.morisonDt = obj.dt;
            end
            % Set paraview.dt if it was not specified in input file
            if isempty(obj.paraview.dt) || obj.paraview.dt < obj.dtOut
                obj.paraview.dt = obj.dtOut;
            end
            obj.cicTime = 0:obj.cicDt:obj.cicEndTime;
            obj.cicLength = length(obj.cicTime);
            obj.caseFile = [obj.simMechanicsFile(length(obj.caseDir)+1:end-4) '_matlabWorkspace.mat'];
            % Remove existing output folder
            if exist(obj.outputDir,'dir') ~= 0
                try
                    rmdir(obj.outputDir,'s')
                catch
                    warning('The output directory could not be removed. Please close any files in the output directory and try running WEC-Sim again')
                end
            end
            mkdir(obj.outputDir)
            obj.getGitCommit;
        end

        function listInfo(obj,waveTypeNum)
            % Lists simulation info
            fprintf('\nWEC-Sim Simulation Settings:\n');
            %fprintf('\tTime Marching Solver                 = Fourth-Order Runge-Kutta Formula \n')
            fprintf('\tStart Time                     (sec) = %G\n',obj.startTime)
            fprintf('\tEnd Time                       (sec) = %G\n',obj.endTime)
            fprintf('\tTime Step Size                 (sec) = %G\n',obj.dt)
            fprintf('\tRamp Function Time             (sec) = %G\n',obj.rampTime)
            if waveTypeNum > 10
                fprintf('\tConvolution Integral Interval  (sec) = %G\n',obj.cicEndTime)
            end
            fprintf('\t Number of Time Steps     = %u \n',obj.maxIt)
        end

        function obj = loadSimMechModel(obj,fName)
            % This method loads the simulink model and sets parameters
            %
            % Parameters
            % ------------
            %   fname : string
            %       the name of the SimMechanics ``.slx`` file
            %

            load_system(fName);
            [~,modelName,~] = fileparts(fName);
            set_param(modelName,'Solver',obj.solver,...
                'StopTime',num2str(obj.endTime),...
                'SimulationMode',obj.mode,...
                'StartTime',num2str(obj.startTime),...
                'FixedStep',num2str(obj.dt),...
                'MaxStep',num2str(obj.dt),...
                'AutoInsertRateTranBlk',obj.rateTransition,...
                'ZeroCrossControl',obj.zeroCross,...
                'SimCompilerOptimization','on',...
                'ReturnWorkspaceOutputs','off',...
                'SimMechanicsOpenEditorOnUpdate',obj.explorer);
        end

        function rhoDensitySetup(obj,rho,gravity)
            % Assigns density and gravity values
            %
            % Parameters
            % ------------
            %   rho : float
            %       density of the fluid medium (kg/m^3)
            %   gravity : float
            %       gravitational acceleration constant (m/s^2)
            %
            obj.rho = rho;
            obj.gravity   = gravity;
        end

        function getGitCommit(obj)
            % Determines GitHub commit tag
            try
                ws_exe = which('wecSim');
                ws_dir = fileparts(ws_exe);
                git_ver_file = [ws_dir '/../.git/refs/heads/master'];
                obj.gitCommit = textread(git_ver_file,'%s');
            catch
                obj.gitCommit = 'No git commit tag available';
            end
        end
    end
end