function om = opf_setup(mpc, mpopt)
%OPF  Constructs an OPF model object from a MATPOWER case struct.
%   OM = OPF_SETUP(MPC, MPOPT)
%
%   Assumes that MPC is a MATPOWER case struct with internal indexing,
%   all equipment in-service, etc.
%
%   See also OPF, EXT2INT, OPF_EXECUTE.

%   MATPOWER
%   Copyright (c) 1996-2016, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%   and Carlos E. Murillo-Sanchez, PSERC Cornell & Universidad Nacional de Colombia
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%% options
dc  = strcmp(upper(mpopt.model), 'DC');
alg = upper(mpopt.opf.ac.solver);
use_vg = mpopt.opf.use_vg;
vcart = ~dc && mpopt.opf.v_cartesian;

%% define named indices into data matrices
[PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
    VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
[GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
    MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
    QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
[F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
    TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
    ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
[PW_LINEAR, POLYNOMIAL, MODEL, STARTUP, SHUTDOWN, NCOST, COST] = idx_cost;

%% define flag to indicate whether we are tied to legacy formulation
%% implemented by legacy MINOS and PDIPM solvers (e.g. with hard-coded
%% costs and constrain
if strcmp(alg, 'MINOPF') || strcmp(alg, 'PDIPM') || ...
        strcmp(alg, 'TRALM') || strcmp(alg, 'SDPOPF')
    legacy_formulation = 1;
    if vcart
        error('Option ''opf.v_cartesian'' = 1 is not compatible with ''opf.solver.ac''=''%s''.', alg);
    end
    if mpopt.opf.current_balance
        error('Option ''opf.current_balance'' = 1 is not compatible with ''opf.solver.ac''=''%s''.', alg);
    end
else
    legacy_formulation = 0;
end
if ~dc && ( ~isempty(mpopt.exp.sys_wide_zip_loads.pw) && ...
                    ~isequal(mpopt.exp.sys_wide_zip_loads.pw, [1 0 0]) || ...
            ~isempty(mpopt.exp.sys_wide_zip_loads.qw) && ...
                    ~isequal(mpopt.exp.sys_wide_zip_loads.qw, [1 0 0]) )
    if vcart
        warning('Voltage dependent loads are not supported with option ''opf.v_cartesian'' = 1. Reverting to constant power load model.');
    end
    if mpopt.opf.current_balance
        warning('Voltage dependent loads are not supported with option ''opf.current_balance'' = 1. Reverting to constant power load model.');
    end
end

%% data dimensions
nb   = size(mpc.bus, 1);    %% number of buses
nl   = size(mpc.branch, 1); %% number of branches
ng   = size(mpc.gen, 1);    %% number of dispatchable injections
nnle = 0;                   %% number of nonlinear user-defined equality cons
nnli = 0;                   %% number of nonlinear user-defined inequality cons
if isfield(mpc, 'A')
  nlin = size(mpc.A, 1);    %% number of linear user constraints
else
  nlin = 0;
end
if isfield(mpc, 'N')
  nw = size(mpc.N, 1);      %% number of general cost vars, w
else
  nw = 0;
end

if dc
  %% ignore reactive costs for DC
  mpc.gencost = pqcost(mpc.gencost, ng);

  %% reduce A and/or N from AC dimensions to DC dimensions, if needed
  if nlin || nw
    acc = [nb+(1:nb) 2*nb+ng+(1:ng)];   %% Vm and Qg columns
    if nlin && size(mpc.A, 2) >= 2*nb + 2*ng
      %% make sure there aren't any constraints on Vm or Qg
      if any(any(mpc.A(:, acc)))
        error('opf_setup: attempting to solve DC OPF with user constraints on Vm or Qg');
      end
      mpc.A(:, acc) = [];               %% delete Vm and Qg columns
    end
    if nw && size(mpc.N, 2) >= 2*nb + 2*ng
      %% make sure there aren't any costs on Vm or Qg
      if any(any(mpc.N(:, acc)))
        [ii, jj] = find(mpc.N(:, acc));
        ii = unique(ii);    %% indices of w with potential non-zero cost terms from Vm or Qg
        if any(mpc.Cw(ii)) || (isfield(mpc, 'H') && ~isempty(mpc.H) && ...
                any(any(mpc.H(:, ii))))
          error('opf_setup: attempting to solve DC OPF with user costs on Vm or Qg');
        end
      end
      mpc.N(:, acc) = [];               %% delete Vm and Qg columns
    end
  end
else    %% AC
  if use_vg     %% adjust bus voltage limits based on generator Vg setpoint
    %% gen connection matrix, element i, j is 1 if, generator j at bus i is ON
    Cg = sparse(mpc.gen(:, GEN_BUS), (1:ng)', mpc.gen(:, GEN_STATUS) > 0, nb, ng);
    Vbg = Cg * sparse(1:ng, 1:ng, mpc.gen(:, VG), ng, ng);
    Vmax = max(Vbg, [], 2); %% zero for non-gen buses, else max Vg of gens @ bus
    ib = find(Vmax);                %% buses with online gens
    Vmin = max(2*Cg - Vbg, [], 2);  %% same as Vmax, except min Vg of gens @ bus
    Vmin(ib) = 2 - Vmin(ib);

    if use_vg == 1      %% use Vg setpoint directly
        mpc.bus(ib, VMAX) = Vmax(ib);   %% max set by max Vg @ bus
        mpc.bus(ib, VMIN) = Vmin(ib);   %% min set by min Vg @ bus
        mpc.bus(ib, VM) = mpc.bus(ib, VMAX);
    elseif use_vg > 0 && use_vg < 1     %% fractional value
        %% use weighted avg between original Vmin/Vmax limits and Vg
        mpc.bus(ib, VMAX) = (1-use_vg) * mpc.bus(ib, VMAX) + use_vg * Vmax(ib);
        mpc.bus(ib, VMIN) = (1-use_vg) * mpc.bus(ib, VMIN) + use_vg * Vmin(ib);
    else
        error('opf_setup: option ''opf.use_vg'' (= %g) cannot be negative or greater than 1', use_vg);
    end
  end
  if isfield(mpc, 'user_constraints')
    if isfield(mpc.user_constraints, 'nle')
      for k = 1:length(mpc.user_constraints.nle)
        nnle = nnle + mpc.user_constraints.nle{k}{2};
      end
    end
    if isfield(mpc.user_constraints, 'nli')
      for k = 1:length(mpc.user_constraints.nli)
        nnli = nnli + mpc.user_constraints.nli{k}{2};
      end
    end
  end
end

%% convert single-block piecewise-linear costs into linear polynomial cost
pwl1 = find(mpc.gencost(:, MODEL) == PW_LINEAR & mpc.gencost(:, NCOST) == 2);
% p1 = [];
if ~isempty(pwl1)
  x0 = mpc.gencost(pwl1, COST);
  y0 = mpc.gencost(pwl1, COST+1);
  x1 = mpc.gencost(pwl1, COST+2);
  y1 = mpc.gencost(pwl1, COST+3);
  m = (y1 - y0) ./ (x1 - x0);
  b = y0 - m .* x0;
  mpc.gencost(pwl1, MODEL) = POLYNOMIAL;
  mpc.gencost(pwl1, NCOST) = 2;
  mpc.gencost(pwl1, COST:COST+1) = [m b];
end

%% create (read-only) copies of individual fields for convenience
[baseMVA, bus, gen, branch, gencost, Au, lbu, ubu, mpopt, ...
    N, fparm, H, Cw, z0, zl, zu, userfcn] = opf_args(mpc, mpopt);

%% warn if there is more than one reference bus
refs = find(bus(:, BUS_TYPE) == REF);
if length(refs) > 1 && mpopt.verbose > 0
  errstr = ['\nopf_setup: Warning: Multiple reference buses.\n', ...
              '           For a system with islands, a reference bus in each island\n', ...
              '           may help convergence, but in a fully connected system such\n', ...
              '           a situation is probably not reasonable.\n\n' ];
  fprintf(errstr);
end

%% set up initial variables and bounds
Va   = bus(:, VA) * (pi/180);
Vau = Inf(nb, 1);       %% voltage angle limits
Val = -Vau;
Vau(refs) = Va(refs);   %% voltage angle reference constraints
Val(refs) = Va(refs);
Pg   = gen(:, PG) / baseMVA;
Pmin = gen(:, PMIN) / baseMVA;
Pmax = gen(:, PMAX) / baseMVA;
if ~dc
  Vm   = bus(:, VM);
  Qg   = gen(:, QG) / baseMVA;
  Qmin = gen(:, QMIN) / baseMVA;
  Qmax = gen(:, QMAX) / baseMVA;
  if vcart
    V = Vm .* exp(1j*Va);
    Vr = real(V);
    Vi = imag(V);
  end
end

%% find/prepare polynomial generator costs
cpg = [];
cqg = [];
%% reduce order where highest order has zero coefficient
gc = mpc.gencost;
while 1
    ii = find(  gc(:, MODEL) == POLYNOMIAL & ...
                gc(:, NCOST) > 2 & ...
                gc(:, COST) == 0    );
    if isempty(ii), break; end  %% exit loop if there are none
    gc(ii, NCOST) = gc(ii, NCOST) - 1;          %% reduce order
    gc(ii, COST:end-1) = gc(ii, COST+1:end);    %% shift coefficients
    gc(ii, end) = 0;
end
[pcost qcost] = pqcost(gc, ng);
ip0 = find(pcost(:, MODEL) == POLYNOMIAL & pcost(:, NCOST) == 1);   %% constant
ip1 = find(pcost(:, MODEL) == POLYNOMIAL & pcost(:, NCOST) == 2);   %% linear
ip2 = find(pcost(:, MODEL) == POLYNOMIAL & pcost(:, NCOST) == 3);   %% quadratic
ip3 = find(pcost(:, MODEL) == POLYNOMIAL & pcost(:, NCOST) > 3);    %% cubic or greater
if ~isempty(ip2) || ~isempty(ip1) || ~isempty(ip0)
    kpg = zeros(ng, 1);
    cpg = zeros(ng, 1);
    if ~isempty(ip2)
        Qpg = zeros(ng, 1);
        Qpg(ip2) = 2 * pcost(ip2, COST) * baseMVA^2;
        cpg(ip2) = cpg(ip2) + pcost(ip2, COST+1) * baseMVA;
        kpg(ip2) = kpg(ip2) + pcost(ip2, COST+2);
    else
        Qpg = [];   %% no quadratic terms
    end
    if ~isempty(ip1)
        cpg(ip1) = cpg(ip1) + pcost(ip1, COST) * baseMVA;
        kpg(ip1) = kpg(ip1) + pcost(ip1, COST+1);
    end
    if ~isempty(ip0)
        kpg(ip0) = kpg(ip0) + pcost(ip0, COST);
    end
end
if ~isempty(qcost)
    iq0 = find(qcost(:, MODEL) == POLYNOMIAL & qcost(:, NCOST) == 1);   %% constant
    iq1 = find(qcost(:, MODEL) == POLYNOMIAL & qcost(:, NCOST) == 2);   %% linear
    iq2 = find(qcost(:, MODEL) == POLYNOMIAL & qcost(:, NCOST) == 3);   %% quadratic
    iq3 = find(qcost(:, MODEL) == POLYNOMIAL & qcost(:, NCOST) > 3);    %% cubic or greater
    if ~isempty(iq2) || ~isempty(iq1) || ~isempty(iq0)
        kqg = zeros(ng, 1);
        cqg = zeros(ng, 1);
        if ~isempty(iq2)
            Qqg = zeros(ng, 1);
            Qqg(iq2) = 2 * qcost(iq2, COST) * baseMVA^2;
            cqg(iq2) = cqg(iq2) + qcost(iq2, COST+1) * baseMVA;
            kqg(iq2) = kqg(iq2) + qcost(iq2, COST+2);
        else
            Qqg = [];   %% no quadratic terms
        end
        if ~isempty(iq1)
            cqg(iq1) = cqg(iq1) + qcost(iq1, COST) * baseMVA;
            kqg(iq1) = kqg(iq1) + qcost(iq1, COST+1);
        end
        if ~isempty(iq0)
            kqg(iq0) = kqg(iq0) + qcost(iq0, COST);
        end
    end
end

%% branch voltage angle difference limits
[Aang, lang, uang, iang]  = makeAang(baseMVA, branch, nb, mpopt);
nang = length(iang);

if dc               %% DC model
  %% check generator costs
  if ~isempty(ip3)
    error('opf_setup: DC OPF cannot handle polynomial costs with higher than quadratic order.');
  end

  %% more problem dimensions
  nv    = 0;            %% number of voltage magnitude vars
  nq    = 0;            %% number of Qg vars
  q1    = [];           %% index of 1st Qg column in Ay

  %% power mismatch constraints
  [B, Bf, Pbusinj, Pfinj] = makeBdc(baseMVA, bus, branch);
  neg_Cg = sparse(gen(:, GEN_BUS), 1:ng, -1, nb, ng);   %% Pbus w.r.t. Pg
  Amis = [B neg_Cg];
  bmis = -(bus(:, PD) + bus(:, GS)) / baseMVA - Pbusinj;

  %% branch flow constraints
  il = find(branch(:, RATE_A) ~= 0 & branch(:, RATE_A) < 1e10);
  nl2 = length(il);         %% number of constrained lines
  if nl2
    upf = branch(il, RATE_A) / baseMVA - Pfinj(il);
    upt = branch(il, RATE_A) / baseMVA + Pfinj(il);
  else
    upf = [];
    upt = [];
  end

  user_vars = {'Va', 'Pg'};
  ycon_vars = {'Pg', 'y'};
else                %% AC model
  %% more problem dimensions
  nv    = nb;           %% number of voltage magnitude vars
  nq    = ng;           %% number of Qg vars
  q1    = 1+ng;         %% index of 1st Qg column in Ay

  %% find branches with flow limits
  il = find(branch(:, RATE_A) ~= 0 & branch(:, RATE_A) < 1e10);
  nl2 = length(il);         %% number of constrained lines

  %% build admittance matrices
  [Ybus, Yf, Yt] = makeYbus(baseMVA, bus, branch);

  %% dispatchable load, constant power factor constraints
  [Avl, lvl, uvl]  = makeAvl(mpc);

  %% generator PQ capability curve constraints
  [Apqh, ubpqh, Apql, ubpql, Apqdata] = makeApq(baseMVA, gen);

  if vcart
      user_vars = {'Vr', 'Vi', 'Pg', 'Qg'};
      nodal_balance_vars = {'Vr', 'Vi', 'Pg', 'Qg'};
      flow_lim_vars = {'Vr', 'Vi'};
  else
      user_vars = {'Va', 'Vm', 'Pg', 'Qg'};
      nodal_balance_vars = {'Va', 'Vm', 'Pg', 'Qg'};
      flow_lim_vars = {'Va', 'Vm'};
  end
  ycon_vars = {'Pg', 'Qg', 'y'};

  %% nonlinear constraint functions
  if mpopt.opf.current_balance
    mis_cons = {'rImis', 'iImis'};
    fcn_mis = @(x)opf_current_balance_fcn(x, mpc, Ybus, mpopt);
    hess_mis = @(x, lam)opf_current_balance_hess(x, lam, mpc, Ybus, mpopt);
  else
    mis_cons = {'Pmis', 'Qmis'};
    fcn_mis = @(x)opf_power_balance_fcn(x, mpc, Ybus, mpopt);
    hess_mis = @(x, lam)opf_power_balance_hess(x, lam, mpc, Ybus, mpopt);
  end
  fcn_flow = @(x)opf_branch_flow_fcn(x, mpc, Yf(il, :), Yt(il, :), il, mpopt);
  hess_flow = @(x, lam)opf_branch_flow_hess(x, lam, mpc, Yf(il, :), Yt(il, :), il, mpopt);
  if vcart
    fcn_vref = @(x)opf_vref_fcn(x, mpc, refs, mpopt);
    hess_vref = @(x, lam)opf_vref_hess(x, lam, mpc, refs, mpopt);
    veq = find(mpc.bus(:, VMIN) == mpc.bus(:, VMAX));
    viq = find(mpc.bus(:, VMIN) ~= mpc.bus(:, VMAX));
    nveq = length(veq);
    nvlims = length(viq);
    if nveq
      fcn_veq = @(x)opf_veq_fcn(x, mpc, veq, mpopt);
      hess_veq = @(x, lam)opf_veq_hess(x, lam, mpc, veq, mpopt);
    end
    fcn_vlim = @(x)opf_vlim_fcn(x, mpc, viq, mpopt);
    hess_vlim = @(x, lam)opf_vlim_hess(x, lam, mpc, viq, mpopt);
    fcn_ang = @(x)opf_branch_ang_fcn(x, Aang, lang, uang);
    hess_ang = @(x, lam)opf_branch_ang_hess(x, lam, Aang, lang, uang);
  end

  %% nonlinear cost functions
  if ~isempty(ip3)
    cost_Pg = @(x)opf_gen_cost_fcn(x, baseMVA, pcost, ip3);
  end
  if ~isempty(qcost) && ~isempty(iq3)
    cost_Qg = @(x)opf_gen_cost_fcn(x, baseMVA, qcost, iq3);
  end
end

%% basin constraints for piece-wise linear gen cost variables
if (strcmp(alg, 'PDIPM') && mpopt.pdipm.step_control) || strcmp(alg, 'TRALM')
  %% SC-PDIPM or TRALM, no CCV cost vars
  ny = 0;
  Ay = sparse(0, ng+nq);
  by =[];
else
  ipwl = find(gencost(:, MODEL) == PW_LINEAR);  %% piece-wise linear costs
  ny = size(ipwl, 1);   %% number of piece-wise linear cost vars
  [Ay, by] = makeAy(baseMVA, ng, gencost, 1, q1, 1+ng+nq);
end
if any(gencost(:, MODEL) ~= POLYNOMIAL & gencost(:, MODEL) ~= PW_LINEAR)
    error('opf_setup: some generator cost rows have invalid MODEL value');
end

%% more problem dimensions
nx    = nb+nv + ng+nq;  %% number of standard OPF control variables
if nlin
  nz = size(mpc.A, 2) - nx; %% number of user z variables
  if nz < 0
    error('opf_setup: user supplied A matrix must have at least %d columns.', nx);
  end
else
  nz = 0;               %% number of user z variables
  if nw                 %% still need to check number of columns of N
    if size(mpc.N, 2) ~= nx;
      error('opf_setup: user supplied N matrix must have %d columns.', nx);
    end
  end
end

%% construct OPF model object
om = opf_model(mpc);
if ~isempty(pwl1)
  om.userdata.pwl1 = pwl1;
end
om.userdata.iang = iang;
if dc
  %% user data
  om.userdata.Bf = Bf;
  om.userdata.Pfinj = Pfinj;

  %% optimization variables
  om.add_var('Va', nb, Va, Val, Vau);
  om.add_var('Pg', ng, Pg, Pmin, Pmax);

  %% linear constraints
  om.add_lin_constraint('Pmis', Amis, bmis, bmis, {'Va', 'Pg'});    %% nb
  om.add_lin_constraint('Pf',  Bf(il,:), -upt, upf, {'Va'});        %% nl2
  om.add_lin_constraint('ang', Aang, lang, uang, {'Va'});           %% nang

  %% quadratic generator costs
  if ~isempty(cpg)
    om.add_quad_cost('polPg', Qpg, cpg, kpg, {'Pg'});
  end
else
  %% user data
  om.userdata.Apqdata = Apqdata;

  %% optimization variables
  if vcart
      Vclim = 1.1 * bus(:, VMAX);
      om.add_var('Vr', nb, Vr, -Vclim, Vclim);
      om.add_var('Vi', nb, Vi, -Vclim, Vclim);
  else
      om.add_var('Va', nb, Va, Val, Vau);
      om.add_var('Vm', nb, Vm, bus(:, VMIN), bus(:, VMAX));
  end
  om.add_var('Pg', ng, Pg, Pmin, Pmax);
  om.add_var('Qg', ng, Qg, Qmin, Qmax);

  %% nonlinear constraints
  om.add_nln_constraint(mis_cons, [nb;nb], 1, fcn_mis, hess_mis, nodal_balance_vars);
  if legacy_formulation
    om.add_nln_constraint({'Sf', 'St'}, [nl;nl], 0, fcn_flow, hess_flow, flow_lim_vars);
  else
    om.add_nln_constraint({'Sf', 'St'}, [nl2;nl2], 0, fcn_flow, hess_flow, flow_lim_vars);
  end
  if vcart
    om.userdata.veq = veq;  %% buses with voltage magnitude equality constraints
    om.userdata.viq = viq;  %% buses with voltage magnitude limits
    om.add_nln_constraint('Vref', length(refs), 1, fcn_vref, hess_vref, {'Vr', 'Vi'});
    if nveq
      om.add_nln_constraint('Veq', nveq, 1, fcn_veq, hess_veq, {'Vr', 'Vi'});
    end
    om.add_nln_constraint({'Vmin', 'Vmax'}, [nvlims;nvlims], 0, fcn_vlim, hess_vlim, {'Vr', 'Vi'});
    om.add_nln_constraint({'angL', 'angU'}, [nang;nang], 0, fcn_ang, hess_ang, {'Vr', 'Vi'});
  end

  %% linear constraints
  om.add_lin_constraint('PQh', Apqh, [], ubpqh, {'Pg', 'Qg'});      %% npqh
  om.add_lin_constraint('PQl', Apql, [], ubpql, {'Pg', 'Qg'});      %% npql
  om.add_lin_constraint('vl',  Avl, lvl, uvl,   {'Pg', 'Qg'});      %% nvl
  if ~vcart
    om.add_lin_constraint('ang', Aang, lang, uang, {'Va'});         %% nang
  end

  %% polynomial generator costs
  if ~legacy_formulation
    %% quadratic/linear generator costs
    if ~isempty(cpg)
      om.add_quad_cost('polPg', Qpg, cpg, kpg, {'Pg'});
    end
    if ~isempty(cqg)
      om.add_quad_cost('polQg', Qqg, cqg, kqg, {'Qg'});
    end

    %% higher order polynomial generator costs
    if ~isempty(ip3)
      om.add_nln_cost('polPg', 1, cost_Pg, {'Pg'});
    end
    if ~isempty(qcost) && ~isempty(iq3)
      om.add_nln_cost('polQg', 1, cost_Qg, {'Qg'});
    end
  end
end

%% y vars, constraints for piece-wise linear gen costs
if ny > 0
  om.add_var('y', ny);
  om.add_lin_constraint('ycon', Ay, [], by, ycon_vars);             %% ncony
  if dc || ~legacy_formulation
    om.add_quad_cost('pwl', [], ones(ny, 1), 0, {'y'});
  end
end

%% add user vars, constraints and costs (as specified via A, ..., N, ...)
if nz > 0
  om.add_var('z', nz, z0, zl, zu);
  user_vars{end+1} = 'z';
end
if nlin
  om.add_lin_constraint('usr', mpc.A, lbu, ubu, user_vars);         %% nlin
end
if nnle
  for k = 1:length(mpc.user_constraints.nle)
    nlc = mpc.user_constraints.nle{k};
    fcn  = eval(['@(x)' nlc{3} '(x, nlc{6}{:})']);
    hess = eval(['@(x, lam)' nlc{4} '(x, lam, nlc{6}{:})']);
    om.add_nln_constraint(nlc{1:2}, 1, fcn, hess, nlc{5});
  end
end
if nnli
  for k = 1:length(mpc.user_constraints.nli)
    nlc = mpc.user_constraints.nli{k};
    fcn  = eval(['@(x)' nlc{3} '(x, nlc{6}{:})']);
    hess = eval(['@(x, lam)' nlc{4} '(x, lam, nlc{6}{:})']);
    om.add_nln_constraint(nlc{1:2}, 0, fcn, hess, nlc{5});
  end
end
if nw
  user_cost.N = mpc.N;
  user_cost.Cw = Cw;
  if ~isempty(fparm)
    user_cost.dd = fparm(:, 1);
    user_cost.rh = fparm(:, 2);
    user_cost.kk = fparm(:, 3);
    user_cost.mm = fparm(:, 4);
  end
  if ~isempty(H)
    user_cost.H = H;
  end
  om.add_legacy_cost('usr', user_cost, user_vars);
end

%% execute userfcn callbacks for 'formulation' stage
om = run_userfcn(userfcn, 'formulation', om, mpopt);

%% implement legacy user costs using quadratic or general non-linear costs
cp = om.params_legacy_cost();   %% construct/fetch the parameters
[N, H, Cw, rh, mm] = deal(cp.N, cp.H, cp.Cw, cp.rh, cp.mm);
[nw, nx] = size(N);
if nw
    if any(cp.dd ~= 1) || any(cp.kk)    %% not simple quadratic form
        if dc                           %% (includes "dead zone" or
            if any(cp.dd ~= 1)          %%  quadratic "penalty")
                error('opf_setup: DC OPF can only handle legacy user-defined costs with d = 1');
            end
            if any(cp.kk)
                error('opf_setup: DC OPF can only handle legacy user-defined costs with no "dead zone", i.e. k = 0');
            end
        elseif ~legacy_formulation
            %% use general nonlinear cost to implement legacy user cost
            legacy_cost_fcn = @(x)opf_legacy_user_cost_fcn(x, cp);
            om.add_nln_cost('usr', 1, legacy_cost_fcn);
        end
    else                                %% simple quadratic form
        %% use a quadratic cost to implement legacy user cost
        if dc || ~legacy_formulation
            %% f = 1/2 * w'*H*w + Cw'*w, where w = diag(mm)*(N*x - rh)
            %% Let: MN = diag(mm)*N
            %%      MR = M * rh
            %%      HMR  = H  * MR;
            %%      HtMR = H' * MR;
            %%  =>   w = MN*x - MR
            %% f = 1/2 * (MN*x - MR)'*H*(MN*x - MR) + Cw'*(MN*x - MR)
            %%   = 1/2 * x'*MN'*H*MN*x +
            %%          (Cw'*MN - 1/2 * MR'*(H+H')*MN)*x +
            %%          1/2 * MR'*H*MR - Cw'*MR
            %%   = 1/2 * x'*Q*w + c'*x + k

            M    = sparse(1:nw, 1:nw, mm, nw, nw);
            MN   = M * N;
            MR   = M * rh;
            HMR  = H  * MR;
            HtMR = H' * MR;
            Q = MN' * H * MN;
            c = full(MN' * (Cw - 1/2*(HMR+HtMR)));
            k = (1/2 * HtMR - Cw)' * MR;
            om.add_quad_cost('usr', Q, c, k);
        end
    end
end


function sensor_data = kspaceFirstOrder_extractSensorData(dim, sensor_data, file_index, sensor_mask_index, record, p, ux_sgx, uy_sgy, uz_sgz)
% DESCRIPTION:
%       Function to extract the required sensor data from the acoustic
%       variables at each time step. This is defined as a function rather
%       than a script to avoid the computational overhead of using scripts
%       to access variables local to another function. For k-Wave < V1.1,
%       this code was included directly in the simulation functions.
%
% ABOUT:
%       author      - Bradley Treeby
%       date        - 9th July 2013
%       last update - 6th September 2013
%
% This function is part of the k-Wave Toolbox (http://www.k-wave.org)
% Copyright (C) 2009-2014 Bradley Treeby and Ben Cox

% This file is part of k-Wave. k-Wave is free software: you can
% redistribute it and/or modify it under the terms of the GNU Lesser
% General Public License as published by the Free Software Foundation,
% either version 3 of the License, or (at your option) any later version.
%
% k-Wave 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 Lesser General Public License for
% more details.
%
% You should have received a copy of the GNU Lesser General Public License
% along with k-Wave. If not, see <http://www.gnu.org/licenses/>.

% =========================================================================
% GRID STAGGERING
% =========================================================================

% shift the components of the velocity field onto the non-staggered
% grid if required for output
if (...
        record.u_non_staggered || ...
        record.I || ...
        record.I_avg)

    switch dim
        case 1
            ux_shifted = real(ifft(bsxfun(@times, record.x_shift_neg, fft(ux_sgx, [], 1)), [], 1));
        case 2
            ux_shifted = real(ifft(bsxfun(@times, record.x_shift_neg, fft(ux_sgx, [], 1)), [], 1));
            uy_shifted = real(ifft(bsxfun(@times, record.y_shift_neg, fft(uy_sgy, [], 2)), [], 2));
        case 3
            ux_shifted = real(ifft(bsxfun(@times, record.x_shift_neg, fft(ux_sgx, [], 1)), [], 1));
            uy_shifted = real(ifft(bsxfun(@times, record.y_shift_neg, fft(uy_sgy, [], 2)), [], 2));
            uz_shifted = real(ifft(bsxfun(@times, record.z_shift_neg, fft(uz_sgz, [], 3)), [], 3));
    end
end

% =========================================================================
% BINARY SENSOR MASK
% =========================================================================

if record.binary_sensor_mask

    % store the time history of the acoustic pressure
    if record.p || record.I || record.I_avg
        if ~record.compute_directivity
            sensor_data.p(:, file_index) = p(sensor_mask_index);
        end
    end

    % store the maximum acoustic pressure
    if record.p_max
        if file_index == 1
            sensor_data.p_max = p(sensor_mask_index);
        else
            sensor_data.p_max = max(sensor_data.p_max, p(sensor_mask_index));
        end
    end

    % store the minimum acoustic pressure
    if record.p_min
        if file_index == 1
            sensor_data.p_min = p(sensor_mask_index);
        else
            sensor_data.p_min = min(sensor_data.p_min, p(sensor_mask_index));
        end
    end

    % store the rms acoustic pressure
    if record.p_rms
        sensor_data.p_rms = sqrt((sensor_data.p_rms.^2*(file_index - 1) + p(sensor_mask_index).^2)./file_index);
    end

    % store the time history of the particle velocity on the staggered grid
    if record.u
        switch dim
            case 1
                sensor_data.ux(:, file_index) = ux_sgx(sensor_mask_index);
            case 2
                sensor_data.ux(:, file_index) = ux_sgx(sensor_mask_index);
                sensor_data.uy(:, file_index) = uy_sgy(sensor_mask_index);
            case 3
                sensor_data.ux(:, file_index) = ux_sgx(sensor_mask_index);
                sensor_data.uy(:, file_index) = uy_sgy(sensor_mask_index);
                sensor_data.uz(:, file_index) = uz_sgz(sensor_mask_index);
        end
    end

    % store the time history of the particle velocity
    if record.u_non_staggered || record.I || record.I_avg
        switch dim
            case 1
                sensor_data.ux_non_staggered(:, file_index) = ux_shifted(sensor_mask_index);
            case 2
                sensor_data.ux_non_staggered(:, file_index) = ux_shifted(sensor_mask_index);
                sensor_data.uy_non_staggered(:, file_index) = uy_shifted(sensor_mask_index);
            case 3
                sensor_data.ux_non_staggered(:, file_index) = ux_shifted(sensor_mask_index);
                sensor_data.uy_non_staggered(:, file_index) = uy_shifted(sensor_mask_index);
                sensor_data.uz_non_staggered(:, file_index) = uz_shifted(sensor_mask_index);
        end
    end

    % store the maximum particle velocity
    if record.u_max
        if file_index == 1
            switch dim
                case 1
                    sensor_data.ux_max = ux_sgx(sensor_mask_index);
                case 2
                    sensor_data.ux_max = ux_sgx(sensor_mask_index);
                    sensor_data.uy_max = uy_sgy(sensor_mask_index);
                case 3
                    sensor_data.ux_max = ux_sgx(sensor_mask_index);
                    sensor_data.uy_max = uy_sgy(sensor_mask_index);
                    sensor_data.uz_max = uz_sgz(sensor_mask_index);
            end
        else
            switch dim
                case 1
                    sensor_data.ux_max = max(sensor_data.ux_max, ux_sgx(sensor_mask_index));
                case 2
                    sensor_data.ux_max = max(sensor_data.ux_max, ux_sgx(sensor_mask_index));
                    sensor_data.uy_max = max(sensor_data.uy_max, uy_sgy(sensor_mask_index));
                case 3
                    sensor_data.ux_max = max(sensor_data.ux_max, ux_sgx(sensor_mask_index));
                    sensor_data.uy_max = max(sensor_data.uy_max, uy_sgy(sensor_mask_index));
                    sensor_data.uz_max = max(sensor_data.uz_max, uz_sgz(sensor_mask_index));
            end
        end
    end

    % store the minimum particle velocity
    if record.u_min
        if file_index == 1
            switch dim
                case 1
                    sensor_data.ux_min = ux_sgx(sensor_mask_index);
                case 2
                    sensor_data.ux_min = ux_sgx(sensor_mask_index);
                    sensor_data.uy_min = uy_sgy(sensor_mask_index);
                case 3
                    sensor_data.ux_min = ux_sgx(sensor_mask_index);
                    sensor_data.uy_min = uy_sgy(sensor_mask_index);
                    sensor_data.uz_min = uz_sgz(sensor_mask_index);
            end
        else
            switch dim
                case 1
                    sensor_data.ux_min = min(sensor_data.ux_min, ux_sgx(sensor_mask_index));
                case 2
                    sensor_data.ux_min = min(sensor_data.ux_min, ux_sgx(sensor_mask_index));
                    sensor_data.uy_min = min(sensor_data.uy_min, uy_sgy(sensor_mask_index));
                case 3
                    sensor_data.ux_min = min(sensor_data.ux_min, ux_sgx(sensor_mask_index));
                    sensor_data.uy_min = min(sensor_data.uy_min, uy_sgy(sensor_mask_index));
                    sensor_data.uz_min = min(sensor_data.uz_min, uz_sgz(sensor_mask_index));
            end
        end
    end

    % store the rms particle velocity
    if record.u_rms
        switch dim
            case 1
                sensor_data.ux_rms(:) = sqrt((sensor_data.ux_rms(:).^2*(file_index - 1) + ux_sgx(sensor_mask_index).^2)./file_index);
            case 2
                sensor_data.ux_rms(:) = sqrt((sensor_data.ux_rms(:).^2*(file_index - 1) + ux_sgx(sensor_mask_index).^2)./file_index);
                sensor_data.uy_rms(:) = sqrt((sensor_data.uy_rms(:).^2*(file_index - 1) + uy_sgy(sensor_mask_index).^2)./file_index);
            case 3
                sensor_data.ux_rms(:) = sqrt((sensor_data.ux_rms(:).^2*(file_index - 1) + ux_sgx(sensor_mask_index).^2)./file_index);
                sensor_data.uy_rms(:) = sqrt((sensor_data.uy_rms(:).^2*(file_index - 1) + uy_sgy(sensor_mask_index).^2)./file_index);
                sensor_data.uz_rms(:) = sqrt((sensor_data.uz_rms(:).^2*(file_index - 1) + uz_sgz(sensor_mask_index).^2)./file_index);
        end
    end

% =========================================================================
% CARTESIAN SENSOR MASK
% =========================================================================

% extract data from specified Cartesian coordinates using interpolation
% (record.record.tri and record.record.bc are the Delaunay triangulation
% and Barycentric coordinates returned by gridDataFast3D)
else

    % store the time history of the acoustic pressure
    if record.p || record.I || record.I_avg
        if dim == 1
            sensor_data.p(:, file_index) = interp1(record.grid_x, p, record.sensor_x);
        else
            sensor_data.p(:, file_index) = sum(p(record.tri) .* record.bc, 2);
        end
    end

    % store the maximum acoustic pressure
    if record.p_max
        if dim == 1
            if file_index == 1
                sensor_data.p_max = interp1(record.grid_x, p, record.sensor_x);
            else
                sensor_data.p_max = max(sensor_data.p_max, interp1(record.grid_x, p, record.sensor_x));
            end
        else
            if file_index == 1
                sensor_data.p_max = sum(p(record.tri) .* record.bc, 2);
            else
                sensor_data.p_max = max(sensor_data.p_max, sum(p(record.tri) .* record.bc, 2));
            end
        end
    end

    % store the minimum acoustic pressure
    if record.p_min
        if dim == 1
            if file_index == 1
                sensor_data.p_min = interp1(record.grid_x, p, record.sensor_x);
            else
                sensor_data.p_min = min(sensor_data.p_min, interp1(record.grid_x, p, record.sensor_x));
            end
        else
            if file_index == 1
                sensor_data.p_min = sum(p(record.tri) .* record.bc, 2);
            else
                sensor_data.p_min = min(sensor_data.p_min, sum(p(record.tri) .* record.bc, 2));
            end
        end
    end

    % store the rms acoustic pressure
    if record.p_rms
        if dim == 1
            sensor_data.p_rms = sqrt((sensor_data.p_rms.^2*(file_index - 1) + (interp1(record.grid_x, p, record.sensor_x)).^2)./file_index);
        else
            sensor_data.p_rms(:) = sqrt((sensor_data.p_rms(:).^2*(file_index - 1) + (sum(p(record.tri) .* record.bc, 2)).^2)./file_index);
        end
    end

    % store the time history of the particle velocity on the staggered grid
    if record.u
        switch dim
            case 1
                sensor_data.ux(:, file_index) = interp1(record.grid_x, ux_sgx, record.sensor_x);
            case 2
                sensor_data.ux(:, file_index) = sum(ux_sgx(record.tri) .* record.bc, 2);
                sensor_data.uy(:, file_index) = sum(uy_sgy(record.tri) .* record.bc, 2);
            case 3
                sensor_data.ux(:, file_index) = sum(ux_sgx(record.tri) .* record.bc, 2);
                sensor_data.uy(:, file_index) = sum(uy_sgy(record.tri) .* record.bc, 2);
                sensor_data.uz(:, file_index) = sum(uz_sgz(record.tri) .* record.bc, 2);
        end
    end

    % store the time history of the particle velocity
    if record.u_non_staggered || record.I || record.I_avg
        switch dim
            case 1
                sensor_data.ux_non_staggered(:, file_index) = interp1(record.grid_x, ux_shifted, record.sensor_x);
            case 2
                sensor_data.ux_non_staggered(:, file_index) = sum(ux_shifted(record.tri) .* record.bc, 2);
                sensor_data.uy_non_staggered(:, file_index) = sum(uy_shifted(record.tri) .* record.bc, 2);
            case 3
                sensor_data.ux_non_staggered(:, file_index) = sum(ux_shifted(record.tri) .* record.bc, 2);
                sensor_data.uy_non_staggered(:, file_index) = sum(uy_shifted(record.tri) .* record.bc, 2);
                sensor_data.uz_non_staggered(:, file_index) = sum(uz_shifted(record.tri) .* record.bc, 2);
        end
    end

    % store the maximum particle velocity
    if record.u_max
        if file_index == 1
            switch dim
                case 1
                    sensor_data.ux_max = interp1(record.grid_x, ux_sgx, record.sensor_x);
                case 2
                    sensor_data.ux_max = sum(ux_sgx(record.tri) .* record.bc, 2);
                    sensor_data.uy_max = sum(uy_sgy(record.tri) .* record.bc, 2);
                case 3
                    sensor_data.ux_max = sum(ux_sgx(record.tri) .* record.bc, 2);
                    sensor_data.uy_max = sum(uy_sgy(record.tri) .* record.bc, 2);
                    sensor_data.uz_max = sum(uz_sgz(record.tri) .* record.bc, 2);
            end
        else
            switch dim
                case 1
                    sensor_data.ux_max = max(sensor_data.ux_max, interp1(record.grid_x, ux_sgx, record.sensor_x));
                case 2
                    sensor_data.ux_max = max(sensor_data.ux_max, sum(ux_sgx(record.tri) .* record.bc, 2));
                    sensor_data.uy_max = max(sensor_data.uy_max, sum(uy_sgy(record.tri) .* record.bc, 2));
                case 3
                    sensor_data.ux_max = max(sensor_data.ux_max, sum(ux_sgx(record.tri) .* record.bc, 2));
                    sensor_data.uy_max = max(sensor_data.uy_max, sum(uy_sgy(record.tri) .* record.bc, 2));
                    sensor_data.uz_max = max(sensor_data.uz_max, sum(uz_sgz(record.tri) .* record.bc, 2));
            end
        end
    end

    % store the minimum particle velocity
    if record.u_min
        if file_index == 1
            switch dim
                case 1
                    sensor_data.ux_min = interp1(record.grid_x, ux_sgx, record.sensor_x);
                case 2
                    sensor_data.ux_min = sum(ux_sgx(record.tri) .* record.bc, 2);
                    sensor_data.uy_min = sum(uy_sgy(record.tri) .* record.bc, 2);
                case 3
                    sensor_data.ux_min = sum(ux_sgx(record.tri) .* record.bc, 2);
                    sensor_data.uy_min = sum(uy_sgy(record.tri) .* record.bc, 2);
                    sensor_data.uz_min = sum(uz_sgz(record.tri) .* record.bc, 2);
            end
        else
            switch dim
                case 1
                    sensor_data.ux_min = min(sensor_data.ux_min, interp1(record.grid_x, ux_sgx, record.sensor_x));
                case 2
                    sensor_data.ux_min = min(sensor_data.ux_min, sum(ux_sgx(record.tri) .* record.bc, 2));
                    sensor_data.uy_min = min(sensor_data.uy_min, sum(uy_sgy(record.tri) .* record.bc, 2));
                case 3
                    sensor_data.ux_min = min(sensor_data.ux_min, sum(ux_sgx(record.tri) .* record.bc, 2));
                    sensor_data.uy_min = min(sensor_data.uy_min, sum(uy_sgy(record.tri) .* record.bc, 2));
                    sensor_data.uz_min = min(sensor_data.uz_min, sum(uz_sgz(record.tri) .* record.bc, 2));
            end
        end
    end

    % store the rms particle velocity
    if record.u_rms
        switch dim
            case 1
                sensor_data.ux_rms = sqrt((sensor_data.ux_rms.^2*(file_index - 1) + (interp1(record.grid_x, ux_sgx, record.sensor_x)).^2)./file_index);
            case 2
                sensor_data.ux_rms(:) = sqrt((sensor_data.ux_rms(:).^2*(file_index - 1) + (sum(ux_sgx(record.tri) .* record.bc, 2)).^2)./file_index);
                sensor_data.uy_rms(:) = sqrt((sensor_data.uy_rms(:).^2*(file_index - 1) + (sum(uy_sgy(record.tri) .* record.bc, 2)).^2)./file_index);
            case 3
                sensor_data.ux_rms(:) = sqrt((sensor_data.ux_rms(:).^2*(file_index - 1) + (sum(ux_sgx(record.tri) .* record.bc, 2)).^2)./file_index);
                sensor_data.uy_rms(:) = sqrt((sensor_data.uy_rms(:).^2*(file_index - 1) + (sum(uy_sgy(record.tri) .* record.bc, 2)).^2)./file_index);
                sensor_data.uz_rms(:) = sqrt((sensor_data.uz_rms(:).^2*(file_index - 1) + (sum(uz_sgz(record.tri) .* record.bc, 2)).^2)./file_index);
        end
    end
end

% =========================================================================
% RECORDED VARIABLES OVER ENTIRE GRID
% =========================================================================

% store the maximum acoustic pressure over all the grid elements
if record.p_max_all
    switch dim
        case 1
            if file_index == 1
                sensor_data.p_max_all = p(record.x1_inside:record.x2_inside);
            else
                sensor_data.p_max_all = max(sensor_data.p_max_all, p(record.x1_inside:record.x2_inside));
            end
        case 2
            if file_index == 1
                sensor_data.p_max_all = p(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside);
            else
                sensor_data.p_max_all = max(sensor_data.p_max_all, p(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside));
            end
        case 3
            if file_index == 1
                sensor_data.p_max_all = p(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside);
            else
                sensor_data.p_max_all = max(sensor_data.p_max_all, p(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside));
            end
    end
end

% store the minimum acoustic pressure over all the grid elements
if record.p_min_all
    switch dim
        case 1
            if file_index == 1
                sensor_data.p_min_all = p(record.x1_inside:record.x2_inside);
            else
                sensor_data.p_min_all = min(sensor_data.p_min_all, p(record.x1_inside:record.x2_inside));
            end
        case 2
            if file_index == 1
                sensor_data.p_min_all = p(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside);
            else
                sensor_data.p_min_all = min(sensor_data.p_min_all, p(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside));
            end
        case 3
            if file_index == 1
                sensor_data.p_min_all = p(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside);
            else
                sensor_data.p_min_all = min(sensor_data.p_min_all, p(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside));
            end
    end
end

% store the maximum particle velocity over all the grid elements
if record.u_max_all
    switch dim
        case 1
            if file_index == 1
                sensor_data.ux_max_all = ux_sgx(record.x1_inside:record.x2_inside);
            else
                sensor_data.ux_max_all = max(sensor_data.ux_max_all, ux_sgx(record.x1_inside:record.x2_inside));
            end
        case 2
            if file_index == 1
                sensor_data.ux_max_all = ux_sgx(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside);
                sensor_data.uy_max_all = uy_sgy(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside);
            else
                sensor_data.ux_max_all = max(sensor_data.ux_max_all, ux_sgx(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside));
                sensor_data.uy_max_all = max(sensor_data.uy_max_all, uy_sgy(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside));
            end
        case 3
            if file_index == 1
                sensor_data.ux_max_all = ux_sgx(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside);
                sensor_data.uy_max_all = uy_sgy(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside);
                sensor_data.uz_max_all = uz_sgz(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside);
            else
                sensor_data.ux_max_all = max(sensor_data.ux_max_all, ux_sgx(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside));
                sensor_data.uy_max_all = max(sensor_data.uy_max_all, uy_sgy(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside));
                sensor_data.uz_max_all = max(sensor_data.uz_max_all, uz_sgz(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside));
            end
    end
end

% store the minimum particle velocity over all the grid elements
if record.u_min_all
    switch dim
        case 1
            if file_index == 1
                sensor_data.ux_min_all = ux_sgx(record.x1_inside:record.x2_inside);
            else
                sensor_data.ux_min_all = min(sensor_data.ux_min_all, ux_sgx(record.x1_inside:record.x2_inside));
            end
        case 2
            if file_index == 1
                sensor_data.ux_min_all = ux_sgx(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside);
                sensor_data.uy_min_all = uy_sgy(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside);
            else
                sensor_data.ux_min_all = min(sensor_data.ux_min_all, ux_sgx(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside));
                sensor_data.uy_min_all = min(sensor_data.uy_min_all, uy_sgy(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside));
            end
        case 3
            if file_index == 1
                sensor_data.ux_min_all = ux_sgx(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside);
                sensor_data.uy_min_all = uy_sgy(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside);
                sensor_data.uz_min_all = uz_sgz(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside);
            else
                sensor_data.ux_min_all = min(sensor_data.ux_min_all, ux_sgx(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside));
                sensor_data.uy_min_all = min(sensor_data.uy_min_all, uy_sgy(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside));
                sensor_data.uz_min_all = min(sensor_data.uz_min_all, uz_sgz(record.x1_inside:record.x2_inside, record.y1_inside:record.y2_inside, record.z1_inside:record.z2_inside));
            end
    end
end


function r = parse_formula(varargin)

% PARSE_FORMULA Parses a chemical formula to form an atomic representation.
%
% SYNTAX
%
% r = parse_formula(str)
% r = parse_formula({str1,str2,str3, ...})
%
%   Parses chemical formulas and returns a structure array holding the an
%   atomic representation of the chemical forulas. The input is a string or
%   a cell array of strings.
%
%
% EXAMPLES
%
%   1. Chemical formulas of varying complexity
%
%       parse_formula('H2O');            % Water
%       parse_formula('NaHCO3');         % Sodium Bicarbonate
%       parse_formula('(CH4)8(H2O)46');  % Methane Clathrate
%       parse_formula('CH3COOCH2CH3');   % Ethyl Acetate
%       parse_formula('MnO4-');          % Negative Charge Ion
%
%       parse_formula('dCH4');           % Returns error message
%
%   2. Create an structure array of atomic representations for a set of
%      compounds
%
%       r = parse_formula({'CH4','O2','CO2','H2O'});
%
%
% USAGE NOTES
%
%   1. Formulas are made of up of sequences of elements followed by
%      integers  indicating the number of included atoms. Omitted integers
%      are assumed to be one.
%
%   2. Elements are the conventional one or two character abbreviations.
%      The character is captialized. If present, the second character is
%      lower case. In addition to the standard elements, the parser allows
%      for
%
%       Symbol  Entity                 Interpretation
%          e    electron               like an element with MW = 0
%          D    deuterium              an element
%          T    tritium                an element
%          M    any metal              like an element, mw = NaN
%          X    any halogen            like an element, mw = NaN
%          Me   methyl group (CH3)     CH3 substituted for Me
%          Et   ethyl group (C2H5)     C2H5 substituted for Et
%          Bu   butyl group (C4H9)     C4H9 substituted for Bu
%          Ph   phenol group (C6H5)    C6H5 substituted for Ph
%
%   3. Subgroups may be included between parenthesis or brackets followed
%      by an integer indicating number of repetitions. Two levels of
%      subgrouping are allowed.
%
%   4. A terminal lower case suffix denoting phases will be correctly
%      parsed. The phase must be one of (aq), (l), (g), or (s).
%
%   5. The charge on an ionic species is appended as a + or - followed by
%      an optional integer.  Examples are H+, OH-, or Ca+2.
%
%   6. The bare electron e- is used in balancing chemical half reactions.
%
%   7. Error messages are generated for invalid fomulas
%      Change for use with the cobra toolbox
%      A is accepted as the symbol for an element that could
%      represent an R group in an atom mapping scenario for an incompletely
%      defined metabolite.
%
%   8. str can be a cell array of chemical formula. The results is a
%      structure array. The elements of the output structure array are in
%      one-to-one correspondence with elements of the cell array. For
%      example
%
%          r = parse_formula({'CH4','CH3OH','CHOOH'})
%
%      r(1) holds the atomic formula for CH4, r(2) for CH3OH, and r(3) for
%      CHOOH.

% AUTHOR
%
%   Jeff Kantor
%   December 18, 2010

    assert(nargin > 0, 'parse_formula:input', ['No input. Expects a  ', ...
                        'string or cell array of chemical formulas.']);
    assert(nargin < 2, 'stoich:input', 'Unexpected extra inputs.');

    switch class(varargin{1})
        case 'char'                      % Single formula
            str = varargin;

        case 'cell'                      % Cell array of formulas
            str = varargin{1};

        otherwise
            error('parse_formula:input',['requires cell array of  ',...
              'chemical formulas.']);
    end

    assert(iscellstr(str), 'parse_formula:input', ...
        'Formulas must be strings.');

    % Trim any whitespace at front or back

    str = strtrim(str);

    % Remove phase information. This information is currently neglected. In
    % a later version we may wish to incorporate phase into a more complete
    % data structure for representing chemical formula.

    prex = '|\((aq|g|l|s)\)$';
    str = regexprep(str,prex,'');

    % Substitute for some common chemical abbreviations

    str = regexprep(str,'Bu','C4H9');    % Butyl
    str = regexprep(str,'Et','C2H5');    % Ethyl
    str = regexprep(str,'Me','CH3');     % Methyl
    str = regexprep(str,'Ph','C6H5');    % Phenol

    % Apply the main parser to every element of str

    q = cellfun(@(s)parse_formula_(s,3),str,'Uniform',false);

    % Union of all atomic species

    atoms = {};
    for i = 1:length(q(:))
        atoms = union(atoms, fields(q{i}));
    end

    % Add all atomic species to all structures.

    for i = 1:length(q(:))
        for j = 1:length(atoms)
            if ~ismember(atoms{j},fields(q{i}))
                q{i}.(atoms{j}) = 0;
            end
        end
    end

    % Form the structure array to have the same shape as str

    r = reshape([q{:}],size(str));

end % parse_formula

function r = parse_formula_(str,kdepth)

    assert(kdepth > 0, 'parse_formula_:Recursion', ...
        'Reached maximum recursion depth');

    r = struct([]);

    % Regular expression returning tokens for element and number
    % sexpr matches single elements followed by a digit, or a +/-
    % followed by a digit to denote charge

    persistent srex;  % Regexp pattern to match elements and charges
    persistent grex;  % Regexp pattern to match groups

    if isempty(srex) || isempty(grex)
        %this was the original line
        %srex = ['(A[lrsgutcm]|B[eraik]?|C[laroudsemf]?|D[y]?|E[urs]|',...
        srex = ['(A[lrsgutcm]?|B[eraik]?|C[laroudsemf]?|D[y]?|E[urs]|', ...
                'F[erm]?|G[aed]|H[eofgas]?|I[nr]?|Kr?|L[iaur]|', ...
                'M[gnodt]?|N[eaibdpos]?|Os?|P[drmtboau]?|R[buhenaf]|', ...
                'S[icernbmg]?|T[icebmalh]?|U|V|W|X[e]?|Yb?|Z[nr])', ...
                '(\d*\.\d+|\d*)', ...
                '|(e|+|-)(\d*)'];
        grex = '|\(([^\)]*)\)(\d*\.\d+|\d*)|\[([^\]]*)\](\d*\.\d+|\d*)';
    end

    % Parse formula for chemical groups. This picks out anything that looks
    % an element followed by a number, or a subgroup within parentheses.
    % The tokens are returned in the cell array u. Each u{k} has two
    % elements, the first is a string denoting the group, and the second is
    % number string of repetitions.

    [u,s,e] = regexp(str,[srex,grex],'tokens','start','end');

    % Report any parsing errors. A parse error occurs if there are any
    % characters not matched as tokens. We scan the start and end positions
    % of the tokens to determine if there are any gaps.

    g(1:length(str)) = '^';
    for i = 1:length(s);
        g(s(i):e(i)) = ' ';
    end

    assert(all(g ~= '^'), 'parse_formula:ParseError', ...
        'Could not parse formula:\n    %s\n    %s\n', str, char(g));

    % Extract atom tokens from the first part of each token

    tok = cellfun(@(v)v{1},u,'Uni',false);

    % Extract counts from the second part of each token, convert to
    % doubles, empty counts set to 1

    cnt = cellfun(@(v)v{2},u,'Uni',false);
    cnt = str2double(cnt);
    cnt(isnan(cnt)) = 1;

    % Loop over tokens

    for j = 1:length(u)

        % See if token matches an element

        if strcmp(tok{j},regexp(tok{j},srex,'match'))

            % The token exactly matches an element.
            % Change + or - tokens to 'Q'.

            tok{j} = regexprep(tok{j},'+','Q');

            if strcmp(tok{j}, '-')
                tok{j} = 'Q';
                cnt(j) = -cnt(j);
            end

            % Update atomic representation, adding a field if needed.

            if isfield(r,tok{j})
                r.(tok{j}) = r.(tok{j}) + cnt(j);
            else
                r(1).(tok{j}) = cnt(j);
            end

        else

            % The token must be a group, so do a recursion to find
            % an atomic represenation of the group.

            q = parse_formula_(tok{j},kdepth-1);

            % Updatethe  atomic representation to include the group.
            % Add fields if needed. Multiply by number of groups in the
            % formula we're parsing.

            f = fields(q);

            for k = 1:length(f)

                if isfield(r,f{k})
                    r.(f{k}) = r.(f{k}) + cnt(j)*q.(f{k});
                else
                    r(1).(f{k}) = cnt(j)*q.(f{k});
                end

            end
        end
    end

end % parse_formula_


function model = setFeedingFastingConstraints(model, feedingStatus,fastingValue,storageValue)
% This function sets constraints corresponding either to feeding (i.e.,
% storage reactions are set to have a lower bound of 0 while
% the upper bounds are set to >0 (storage is enabled) or to fasting (i.e.,
% storage reactions are set to have a lower bound of <0 (use of stored metabolites is enabled) while
% the upper bounds are set to 0 (storage is disabled).
%
% function model = setFeedingFastingConstraints(model, feedingStatus,fastingValue,storageValue)
%
% INPUT
% model             model structure
% feedingStatus     'feeding' = default bounds for diet uptake fastingValue for all diet exchanges, use diet script to refine, organ storage is turned on;
%                   'fasting' = all diet uptakes are closed, organ storage release is turned on
% fastingValue      default: fastingValue = -10;
% storageValue      default: storageValue = 10;
%
% OUTPUT
% model             with updated constraints
%
% Ines Thiele, 2015/2016
if ~exist('fastingValue','var')
    fastingValue = -10;
end
if ~exist('storageValue','var')
    storageValue = 10;
end

% load amino acids that can be stored
AAStorage;
storedInLiver={'nad(c)';'nadp(c)';'fad(c)';...
    'thmtp(c)';'pydam(c)';'pydx(c)';'pydxn(c)';...
    'coa(c)';'thf(c)';'btn(c)';'retinol(c)';'retfa(c)';...
    '11_cis_retfa(c)';'9_cis_retfa(c)';'25hvitd2(c)';'vitd3(c)';...
    'avite1(c)';'avite2(c)';'chol(c)';'fe3(c)';'phyQ(c)';...
    'glygn2(c)'};
StoreInKidney = {'ascb_L(c)';'chol(c)';};
StoreInMuscle ={'thmpp(c)';'25hvitd2(c)';'vitd3(c)';'chol(c)';'glygn2(c)'};

StoreInAdi={'25hvitd2(c)';'vitd3(c)';'avite1(c)';'avite2(c)';'phyQ(c)';...
    'hdca(c)';'tmndnc(c)';'lnlc(c)';'tag_hs(c)';...
    'c226coa(c)';'doco13ecoa(c)';'lnlccoa(c)';'lnlncacoa(c)';'lnlncgcoa(c)';...
    'odecoa(c)';'pmtcoa(c)';'stcoa(c)';'tmndnccoa(c)'
    };
if isfield(model,'rxnGeneMat')
    model = rmfield(model,'rxnGeneMat');
end
% set all sinks to 0
for i = 1 : length(model.rxns)
    if length(strfind(model.rxns{i},'sink_'))==1 ...
            && length(strfind(model.rxns{i},'sink_pre_prot(r)'))==0  ...
            && length(strfind(model.rxns{i},'sink_Ser_Gly_Ala_X_Gly(r)'))==0 ...
            && length(strfind(model.rxns{i},'sink_5hpet(c)'))==0
        %&& length(strfind(model.rxns{i},'sink_Tyr_ggn(c)'))==0 % ...
        %% && length(strfind(model.rxns{i},'sink_citr(c)'))==0
        % && length(strfind(model.rxns{i},'sink_Ser_Gly_Ala_X_Gly(r)'))==0 ...
        if isfield(model,'Microbiota') && model.Microbiota(i) ==0 %no microbe sink
            model.lb(i)=0;
        elseif ~isfield(model,'Microbiota')
            model.lb(i)=0;
        end
    end
end

DMs= (find(~cellfun(@isempty,strfind(model.rxns,'DM_'))));
model.lb(DMs) = 0;

% but not at the beginning of the abbr --> leaves in the
% microbe sinks
if 1
    tmp = strmatch('sink_',model.rxns);
    model.lb(tmp)=-10;
end

for i = 1 : length(model.rxns)
    if strfind(model.rxns{i},'sink_')
        model.rxns{i} = regexprep(model.rxns{i},'\[c\]','(c)');
        model.rxns{i} = regexprep(model.rxns{i},'\[r\]','(r)');
        %    model.rxns{i}
    end
end
modelexchanges1 = strmatch('Diet_EX_',model.rxns);
modelexchanges2 = strmatch('Diet_Ex_',model.rxns);
modelexchanges = [modelexchanges1;modelexchanges2];

if strcmp(feedingStatus,'feeding') % storage no sinks
    model.lb(modelexchanges)=fastingValue;
    model.ub(modelexchanges)=0;
elseif strcmp(feedingStatus,'fasting') % storage no sinks
    model.lb(modelexchanges)=0;
    model.ub(modelexchanges)=0;
    % exception for water

end

if strcmp(feedingStatus,'feeding') % storage no sinks
    for i = 1 : length(storedInLiver)
        storedInLiver{i};
        rxnName = strcat('Liver_sink_',storedInLiver{i});
        %   rxnName
        model = changeRxnBounds(model,rxnName,0,'l');
        model = changeRxnBounds(model,rxnName, storageValue,'u');
    end
    for i = 1 : length(StoreInKidney)
        StoreInKidney{i};
        rxnName = strcat('Kidney_sink_',StoreInKidney{i});
        %  rxnName
        model = changeRxnBounds(model,rxnName,0,'l');
        model = changeRxnBounds(model,rxnName, storageValue,'u');
    end
    for i = 1 : length(StoreInMuscle)
        rxnName = strcat('Muscle_sink_',StoreInMuscle{i});
        %  rxnName
        model = changeRxnBounds(model,rxnName,0,'l');
        model = changeRxnBounds(model,rxnName, storageValue,'u');
    end
    for i = 1 : length(storageAA)
        rxnName = strcat('Muscle_',storageAA{i});
        %    rxnName
        model = changeRxnBounds(model,rxnName,0,'l');
        model = changeRxnBounds(model,rxnName, storageValue,'u');
    end
    for i = 1 : length(StoreInAdi)
        rxnName = strcat('Adipocytes_sink_',StoreInAdi{i});
        %   rxnName
        StoreInAdi{i};
        model = changeRxnBounds(model,rxnName,0,'l');
        model = changeRxnBounds(model,rxnName, storageValue,'u');
    end
    rxnName = 'Retina_sink_crvnc(c)';
    model = changeRxnBounds(model,rxnName,0,'l');
    model = changeRxnBounds(model,rxnName, storageValue,'u');
    rxnName = 'Heart_sink_chol(c)';
    model = changeRxnBounds(model,rxnName,0,'l');
    model = changeRxnBounds(model,rxnName, storageValue,'u');
    rxnName = 'Brain_sink_crvnc(c)';
    model = changeRxnBounds(model,rxnName,0,'l');
    model = changeRxnBounds(model,rxnName, storageValue,'u');
    rxnName = 'Brain_sink_chol(c)';
    model = changeRxnBounds(model,rxnName,0,'l');
    model = changeRxnBounds(model,rxnName, storageValue,'u');
    rxnName = 'RBC_sink_glygn2(c)';
    model = changeRxnBounds(model,rxnName,0,'l');
    model = changeRxnBounds(model,rxnName, storageValue,'u');
    rxnName = 'Skin_sink_vitd3(c)';
    model = changeRxnBounds(model,rxnName,0,'l');
    model = changeRxnBounds(model,rxnName, storageValue,'u');
elseif strcmp(feedingStatus,'fasting')
    for i = 1 : length(storedInLiver)
        rxnName = strcat('Liver_sink_',storedInLiver{i});
        model = changeRxnBounds(model,rxnName,fastingValue,'l');
        model = changeRxnBounds(model,rxnName,0,'u');
    end
    for i = 1 : length(StoreInKidney)
        rxnName = strcat('Kidney_sink_',StoreInKidney{i});
        model = changeRxnBounds(model,rxnName,fastingValue,'l');
        model = changeRxnBounds(model,rxnName,0,'u');
    end
    for i = 1 : length(StoreInMuscle)
        rxnName = strcat('Muscle_sink_',StoreInMuscle{i});
        model = changeRxnBounds(model,rxnName,fastingValue,'l');
        model = changeRxnBounds(model,rxnName,0,'u');
    end
    for i = 1 : length(storageAA)
        rxnName = strcat('Muscle_',storageAA{i});
        model = changeRxnBounds(model,rxnName,fastingValue,'l');
        model = changeRxnBounds(model,rxnName,0,'u');
    end
    for i = 1 : length(StoreInAdi)
        rxnName = strcat('Adipocytes_sink_',StoreInAdi{i});
        model = changeRxnBounds(model,rxnName,fastingValue,'l');
        model = changeRxnBounds(model,rxnName,0,'u');
    end
    rxnName = 'Retina_sink_crvnc(c)';
    model = changeRxnBounds(model,rxnName,fastingValue,'l');
    model = changeRxnBounds(model,rxnName,0,'u');
    rxnName = 'Heart_sink_chol(c)';
    model = changeRxnBounds(model,rxnName,fastingValue,'l');
    model = changeRxnBounds(model,rxnName,0,'u');
    rxnName = 'Brain_sink_crvnc(c)';
    model = changeRxnBounds(model,rxnName,fastingValue,'l');
    model = changeRxnBounds(model,rxnName,0,'u');
    rxnName = 'Brain_sink_chol(c)';
    model = changeRxnBounds(model,rxnName,fastingValue,'l');
    model = changeRxnBounds(model,rxnName,0,'u');
    rxnName = 'RBC_sink_glygn2(c)';
    model = changeRxnBounds(model,rxnName,fastingValue,'l');
    model = changeRxnBounds(model,rxnName,0,'u');
    rxnName = 'Skin_sink_vitd3(c)';
    model = changeRxnBounds(model,rxnName,fastingValue,'l');
    model = changeRxnBounds(model,rxnName,0,'u');
end

model.SetupInfo.FeedingStatus = feedingStatus;


function C = mtimes(A, B)
%*   CHEBOP composition, multiplication, or application.
%   C = A*B, where either A or B is a scalar, returns a CHEBOP C representing
%   scalar multiplication of the original operator.  Boundary conditions
%   are copied from A or B to C.
%
%   If N is a CHEBOP and U a CHEBFUN or CHEBMATRIX of dimension compatible
%   with N.op, then N*U is equivalent to FEVAL(N, U).
%
%   C = A*B, where A and B are CHEBOP objects, should return a CHEBOP C
%   representing the composition of the operators of A and B. Boundary
%   conditions on A or B are destroyed by this process. Note this is not yet
%   supported.
%
% See also CHEBOP/PLUS, CHEBOP/MINUS, CHEBOP/MLDIVIDE, CHEBOP/FEVAL

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

if ( isa(A, 'chebfun') )
    error('CHEBFUN:CHEBOP:mtimes:invalid', 'Operation is undefined.');

elseif ( isa(B, 'chebfun') || isa(B, 'chebmatrix') )
    % Let A operate on B:
    C = feval(A, B);

elseif ( isnumeric(B) )
    % Switch argument to make sure A is numeric:
    C = mtimes(B, A);

elseif ( isnumeric(A) )

    if ( ~isscalar(A) )
        error('CHEBFUN:CHEBOP:mtimes:nonScalar', ...
            'CHEBOP * DOUBLE multiplication is defined only for scalars.');
    end

    % Initialize C as a CHEBOP
    C = B;

    % Make the pretty string:
    funArgs = getFunArgs(C);
    argStr = ['@(',funArgs,')'];
    if ( ~isempty(C.opShow) )
        op = C.opShow;
    else
        op = func2str(C.op);
    end
    C.opShow = strrep(op, argStr, [argStr num2str(A) '*']);

    % Multiplication of anonymous functions is not supported in Matlab. Need to
    % work around that.
    C.op = eval(['@(', funArgs, ') A*C.op(', funArgs, ')']);

elseif ( isa(A,'chebop') && isa(B,'chebop') )
    % CHEBOP composition is not yet supported. It will probably be a mess once
    % we start getting systems involved, as the inputs need to be shuffled
    % correctly. A remedy might be through a nested function in this file.
    error('CHEBFUN:CHEBOP:mtimes:notSupported1',...
        'CHEBOP composition is not supported.');

else
    error('CHEBFUN:CHEBOP:mtimes:notSupported2', ...
        '%s * %s multiplication is not supported.', class(A), class(B));

end

end


% Source Code Listings for the Calspan Version
% of the FlOl Engine Response Model
% Based on Report DNA-TR-93-124 by Adam J. Baran
% and Dr. Michael G. Dunn of Calspan Corporation
%
% AFRL/RQTI Sept 2012
%
% engine parameters:
% xn1 - fan speed (rpm)
% xn2 - core speed (rpm)
%    (fan speed correlation)
% xigv - fan inlet variable guide vane angle (deg.)
% psl4 - fan discharge static pressure (psig)
% pt25 - fan discharge total pressure (psig)
% xoil1 - oil pressure [xn1 correlation) (psig)
%    (core speed correlation)
% thrust - thrust (lbf)
% air - airflow (lbm/sec)
% vgv -compressor inlet variable guide vane angle (deg.)
% cdp - compressor discharge pressure Cpsig)
% pyro - pyrometer temperature (F)
% egt - exhaust gas temperature (F)
% fuel - fuel flow (Pph)
% xoil2c - oil pressure [xn2 correlation] (psig)
%
%       (subscripts)
% ()nom - nominal value
% ()del - Cin/de)crement over current interval
% ()sumc - sum of (in/de)crements over previous intervals
%        (prescripts)
% f() - function which generates the value of the variable
%
% Normal syntax to run is "Dunnv1('infile.dat','outfile.dat')" from matlab
% prompt. To build datafile on first run just run "Dunnv1"...
%% Main Routine
function Dunnv1(Infile,Outfile)
dbstop if error
% Check for whether a runtime datafile exists
if nargin == 0 ;            % no formatted input file exists
    defaultdata ;
    Infile = 'infile.dat';
    Outfile = 'outfile.dat';
elseif nargin == 1 ;        % formatted input file exists
    Outfile = 'outfile.dat';
end ;
STOR = [];
%% -------------------------------
% ROUTINE - READIN DATA FILE
%  -------------------------------
% readindata(Infile) ; Read in data from inputfile ; IN = fopen(Infile,'r') ;
%if (Infile == '');
%%%    Infile = 'infile.dat' ; %%% Remove this when converting to input
%end;
IN = fopen(Infile); %IN = fopen('infile.dat','w')
[pla] = textread(Infile, '%f',1,'headerlines',4) ;
[pamb] = textread(Infile, '%f',1,'headerlines',6) ;
[tamb] = textread(Infile, '%f',1,'headerlines',8) ;
[debug] = textread(Infile, '%u',1,'headerlines',10) ;
[nenc] = textread(Infile, '%u',1,'headerlines',12) ;
% Preallocate the arrays
% ib = zeros(nenc,1); blend = zeros(nenc,1); c = zeros(nenc,1); delt = zeros(nenc,1);
[ib blend c delt] =  textread(Infile,'%u %u %f %f','headerlines',14) ;
fclose('all') ;
%% Start writting out Header Block of Output File
%% -------------------------------
% ROUTINE - WRITEOUT RESULTS
%  -------------------------------
%if (Infile == '');
%%%    Outfile = 'outfile.dat' ; %%% Remove this when converting to input
%end;
OUT = fopen(Outfile,'w') ;
fprintf(OUT,'PEARL - F101 reponse model \n') ;
fprintf(OUT,'%s\n',datestr(now)) ;
fprintf(OUT,' \n') ; fprintf(OUT,' \n') ; fprintf(OUT,' \n') ;
fprintf(OUT,'inputs :  \n'); fprintf(OUT,' \n') ;
fprintf(OUT,'PLA (deg) from (16.5-80.0) =>  \n') ;
fprintf(OUT,'%-4.2e  \n',pla) ;
fprintf(OUT,'ambient pressure (psia) => ') ; fprintf(OUT,'%-4.2e  \n',pamb) ;
fprintf(OUT,'ambient temperature (F) => ') ; fprintf(OUT,'%-4.2e  \n',tamb) ;
fprintf(OUT,' \n') ;
fprintf(OUT,'#_of_encounters(-)(1-10)=> ') ; fprintf(OUT,'%-2i  \n',nenc) ;
fprintf(OUT,'enc#, blend (1=mpb,2=blend2), dust conc. (mg/m^3), duration (min) \n') ;
fprintf(OUT,' \n') ;
for i = 1:1:max(ib) ; % 'max(ib) is 'nenc'
    fprintf(OUT,'%-2i \t %-1i \t %-4.2e \t  %-4.2e \n', ib(i), blend(i),c(i),delt(i));
end ;
fprintf(OUT,' \n') ;
fprintf(OUT,'debug mode (0=off,1=on)  => ') ; fprintf(OUT,'%-2i  \n',debug) ;
fprintf(OUT,' \n') ; fprintf(OUT,' \n') ;
fprintf(OUT,'calculation begun   \n') ; fprintf(OUT,' \n'); fprintf(OUT,' \n');
fprintf(OUT,' i blend dust cnc. time int. AIR FLOW   ACC DUST    N2     CDP    FUEL FLOW   THRUST  \n');
fprintf(OUT,'         (mg/m^3)  (min)     lbm/sec)  (kg)        (rpm)  (psig) (lbm/hr)    (lbf)   \n');
fprintf(OUT,'+--+---+--------+--------+-------------+----------+-------+------+----------+-------+ \n');
%% START MAIN LOGIC HERE
% Initialize all Runtime Engine Parameters to zero
ienc = 0; none = 0; dust = 0.; icdpflag = 0; cdplim = 54.; accdust = 0. ;
ibug = 0; time = 0. ;
xn2nom = 0.; xn2del = 0.; xn2sum = 0.;            % Spool speeds
cdpnom = 0.; cdpdel = 0.; cdpsum = 0.;            % Compressor pressure
fuelnom = 0.; fueldel = 0.; fuelsum = 0.;         % Fuel mass flow
thrustnom = 0.; thrustdel = 0.; thrustsum = 0.;   % Engine Thrust
%%% F101 Specific DATA Here
const = 1.699e-06;                                % F101 engine correl. parameters for dust accum in F101
cdprate1 = 9.36e-03; cdprate2 = 1.59e-02;         % F101 engine correl. parameters for cdp w/ dust1 and 2
fuelrate1 = 1.22e-01; fuelrate2 = 3.32e-01;       % F101 engine correl. parameters for fuel mass flow w/ dust1 and 2
thrustrate1 = 1.41e-01; thrustrate2 = 3.33e-01;   % F101 engine correl. parameters for thrust w/ dust1 and 2
%%% F101 Specific DATA Here
% Calculate Ambient Density of Air (lbm/sec)
rhoamb = 144.*pamb/(53.3*(tamb+460.));            % Air equation of state
% Nominal clear air engine parameter values
xn2nom = FXN2NOM(pla) ;     % call function "figure nominal core speed from PLA"
xn2  = xn2nom ;             % set core speed from nominal core speed
air = FAIR(xn2) ;           % call function for "obtain air mass flow from core speed"
cdpnom = FCDPNOM(xn2) ;     % call function "get nominal compress discharge pressure from core speed"
cdp = cdpnom ;              % set comp. discharge pressure from nominal comp. discharge pressure
fuelnom = FFUELNOM(xn2) ;   % call function "get nominal fuel flow from core speed"
fuel = fuelnom ;            % set fuel flow from the nominal fuel flow
thrustnom = FTHRUSTNOM(xn2) ;
thrust = thrustnom ;
fprintf(OUT,'%2i\t%2i\t%4.1f\t%5.1f\t\t%4.1f\t\t%3.2e\t%6.1f\t%4.1f\t%6.1f\t%6.1f\n',0,0,0,0,air,accdust,xn2,cdp,fuel,thrust) ;
STORBASE = [0 0 0 0 0 air accdust xn2 cdp fuel thrust];
% LOOP over all dust clound encounters
for ienc = 1:1:nenc;
    %% Calculate Airflow, ingested dust over this interval and total accumulated dust
    % based on conditions at start of encounter
    air = FAIR(xn2);
    q = air/rhoamb ;
    dust = const*q*c(ienc)*delt(ienc) ; % Incremental Dust accumulation
    accdust = dust + accdust;           % Cumulative Dust accumulation
    %% Calculate Core Speed          %ibug %ib(ienc)%accdust %c(ienc)
    xn2rate = FXN2RATE(ibug,blend(ienc),accdust,c(ienc));
    xn2del = xn2rate*delt(ienc);
    xn2 = xn2nom - xn2del - xn2sum ;
    %% Calculate cdp
    cdpnom = FCDPNOM(xn2);
    if(blend(ienc) == 1);    % If dust blend #1 (mpb)
       cdprate = cdprate1 ;
    elseif(blend(ienc) == 2); % If dust blend #2 (blend-2)
       cdprate = cdprate2 ;
    end;
    cdpdel = cdprate*c(ienc)*delt(ienc);
    cdp = cdpnom + cdpdel + cdpsum ;
    %% Check for surge probability
    if((cdpdel+cdpsum >= cdplim)&&(icdpflag == 0));
       icdpflag = 1;
       cdpdelc = cdplim - cdpsum ;
       tau = cdpdelc/(cdprate*c(ienc));
       fprintf(OUT,'%s %3.1f  %s  %2i %s','*** engine surge at ',tau,'minutes into the',ienc,' -th encounter');
       fprintf(OUT,' \n') ;
    end
    %% Calculate Fuel Burn Rate
    fuelnom = FFUELNOM(xn2) ;
    if(blend(ienc) == 1);    % If dust blend #1 (mpb)
       fuelrate = fuelrate1 ;
    elseif(blend(ienc) == 2); % If dust blend #2 (blend-2)
       fuelrate = fuelrate2 ;
    end;
    fueldel = fuelrate*c(ienc)*delt(ienc) ;
    fuel = fuelnom + fueldel + fuelsum ;
    %% Calculate Thrust
    thrustnom = FTHRUSTNOM(xn2) ;
    if(blend(ienc) == 1);    % If dust blend #1 (mpb)
       thrustrate = thrustrate1 ;
    elseif(blend(ienc) == 2); % If dust blend #2 (blend-2)
       thrustrate = thrustrate2 ;
    end;
    thrustdel = thrustrate*c(ienc)*delt(ienc);
    thrust = thrustnom + thrustdel + thrustsum ;
    %% Updat the sum terms of each engine parameter
    xn2sum = xn2sum + xn2del ;
    cdpsum = cdpsum + cdpdel ;
    fuelsum = fuelsum + fueldel ;
    thrustsum = thrustsum + thrustdel ;
    %% Write and store out runtime results
    fprintf(OUT,'%2i\t%2i\t%4.1f\t%5.1f\t\t%4.1f\t\t%3.2e\t%6.1f\t%4.1f\t%6.1f\t%6.1f\n',ienc,blend(ienc),c(ienc),delt(ienc),air,accdust,xn2,cdp,fuel,thrust) ;
    time = time + delt(ienc);
    STOR(ienc,:) = [ienc time blend(ienc) c(ienc) delt(ienc) air accdust xn2 cdp fuel thrust];
end
STOR = [STORBASE ; STOR] ;
% Plot Out Trends
figure(1); scale = 0;
subplot(2,3,1); plot(STOR(:,2),STOR(:,4)); grid on ; axis([ 0 max(STOR(:,2)) 0 1.1.*max(STOR(:,4))]) ;
title('dust concentration at enc') ; xlabel('time (min)'); ylabel('concentration (mg/m^3)') ;
subplot(2,3,2); plot(STOR(:,2),STOR(:,6)); grid on ;
if (scale == 1); axis([ 0 max(STOR(:,2)) 0 1.1.*max(STOR(:,6))]) ; end;
title('air flow') ; xlabel('time (min)'); ylabel('air mass flow (lbm/sec)') ;
subplot(2,3,3); plot(STOR(:,2),STOR(:,8)); grid on ;
if (scale == 1); axis([ 0 max(STOR(:,2)) 0 1.1.*max(STOR(:,8))]) ; end;
title('fan speed') ; xlabel('time (min)'); ylabel('fan speed (rpm)') ;
subplot(2,3,4); plot(STOR(:,2),STOR(:,9)); grid on ;
if (scale == 1); axis([ 0 max(STOR(:,2)) 0 1.1.*max(STOR(:,9))]) ; end ;
title('compressor discharge pressure') ; xlabel('time (min)'); ylabel('pressure (psig)') ;
subplot(2,3,5); plot(STOR(:,2),STOR(:,10)); grid on ;
if (scale == 1); axis([ 0 max(STOR(:,2)) 0 1.1.*max(STOR(:,10))]) ; end;
title('fuel burn') ; xlabel('time (min)'); ylabel('fuel burn rate (pph)') ;
subplot(2,3,6); plot(STOR(:,2),STOR(:,11)); grid on ;
if (scale == 1); axis([ 0 max(STOR(:,2)) 0 1.1.*max(STOR(:,11))]) ; end;
title('thrust') ; xlabel('time (min)'); ylabel('thrust (lbf)') ;
%
fclose(OUT) ;
% END of MAIN Routine
end
%% Calculate Airflow as a function a fan of fan speed
% "x" is passed from "xn2" and "y" is passed from "air"
function [y] = FAIR(x);  %%% F101 Specific DATA Here
%%% F101 Specific DATA Here
a0= 440922.79744; a1= -174.0731208; a2= 0.027346652504;
a3= -2.1376216416e-06; a4= 8.3178976743e-11; a5= -1.2891058570e-15;
p = [440922.79744 -174.0731208 0.027346652504 -2.1376216416e-06 8.3178976743e-11];
%%% F101 Specific DATA Here
if(x<10348.);
    y=0.;
elseif(x>15000.);
    y=0.;
elseif((x>=10348.)&&(x<=15000.));
    y = a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4 + a5*x^5;
%    y = polyval(p,x)
end
end
%% Calculate the nominal fuel a fan of fan speed, x is passed from xn2
function [y] = FFUELNOM(x) ;%%% F101 Specific DATA Here
%%% F101 Specific DATA Here
a0 = -49816.415015; a1 = 15.16955213; a2 = -0.00090483247417;
a3 = -1.0788972042E-07; a4 = 1.3439057313e-11; a5 = -3.6898802700E-16;
%%% F101 Specific DATA Here
if(x<10348.);
    y = 0. ;
elseif(x>15000.);
    y = 0. ;
elseif((x>=10348.)&&(x<=15000.));
    y = a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4 + a5*x^5;
end
end
%% Function calculates the nominal thrust a fan of fan speed
function [y] = FTHRUSTNOM(x);%%% F101 Specific DATA Here
% 'x' is 'xn2' while 'y' is 'fthrustnom'
%%% F101 Specific DATA Here
a10=-90190833.084; a11=38206.13731; a12=-6.4537917919; a13=0.00054340227729;
a14=-2.2807645464e-08; a15=3.8182035691e-13;
a20=-26525834.184; a21=5596.7412494; a22=-0.24258263565; a23=-2.2544154617e-05;
a24=2.2276804666e-09; a25=-5.2033103514e-14;
%%% F101 Specific DATA Here
if(x<10543.) ;
    y = 0. ;
elseif(x>15000.) ;
    y = 0. ;
elseif((x>=10543.)&&(x<=13246.));
    y = a10 + a11*x + a12*x^2 + a13*x^3 + a14*x^4 + a15*x^5;
elseif((x>13426.)&&(x<=15000.));     % else
    y = a20 + a21*x + a22*x^2 + a23*x^3 + a24*x^4 + a25*x^5;
end
end
%%  Calculate Nominal value of Core Speed as function of power setting
function [y] = FXN2NOM(x);%%% F101 Specific DATA Here
% "x" is passed from the global variable "pla", and "y" is "xn2nom"
%%% F101 Specific DATA Here
a10=10353.360465; a11=0.18067978533; a20=10306.641571;
a21=220.99533596; a22=-21.326092808; a23=0.69563112177;
a24=-0.0087987179757; a25=3.8820512919e-05;
%%% F101 Specific DATA Here
if(x<16.5) ;
    y = 0. ;
elseif(x>80.);
    y = 0. ;
elseif((x>=16.5)&&(x<30.));
    y = a10 + a11*x ;
elseif((x>=30.)&&(x<=80.));
    y = a20 + a21*x + a22*x^2 + a23*x^3 + a24*x^4 + a25*x^5 ;
end
end
%% Calculate Compressor Discharge Pressure from core speed
function [y]= FCDPNOM(x);%%% F101 Specific DATA Here
% "x" is passed from "xn2" and "y" is passed at "cdpnom"
%%% F101 Specific DATA Here
a0 = 52767.656799 ; a1 = -20.435530217 ; a2 = 0.0031594211659 ;
a3 = -2.441489353e-07 ; a4 = 9.4426267256e-12 ; a5 = -1.4592076510e-16 ;
%%% F101 Specific DATA Here
if(x<10348.);
    y = 0. ;
elseif(x>15000.);
    y = 0. ;
elseif((x>=10348.)&&(x<=15000.));
    y = a0 + a1*x + a2*x^2 + a3*x^3 + a4*x^4 + a5*x^5 ;
end
end
%% Calculates core speed drop rate due to dust
function [y] = FXN2RATE(libug,lblend,lacc,lc) ;%%% F101 Specific DATA Here
%% where 'libug' is passed from 'ibug', is debug mode
% where 'li' is passed from 'i', is cloud encounter index counter
% where 'lacc' is passed from 'acc', is sum accumulation of dust
% where 'lc' is passed from 'c', concentration of the dust cloud
% where 'y' is passed back as 'fxn2rate', the core speed drop rate
%%% F101 Specific DATA Here
a110 = 0. ; a111 = 0.031091666884 ; a120 = 2.2386 ; a130 = 0.41616348802 ;
a131= 0.062687011283 ; a210= -0.023120229598 ; a211= 0.19569725331 ;
a212= -0.001954960762 ; a213= 6.9569031307e-06;
%%% F101 Specific DATA Here
if(lacc<=0.); y = 0.0; end;
if(lblend==1);                  % Model for the mpb blend
    if(lacc<72.);
        if((lc>=0.)&&(lc<72.));
            y = a110 + a111*lc ;
        elseif((lc>=72.)&&(lc<=293.));
            y = a120 ;
        elseif(lc>293);
            y = 1.0e10 ;
        end;
    elseif(lacc>=72.);
        if((lc>=0.)&&(lc<=282.));
            y = a130 + a131*lc ;
        elseif(lc>282.);
            y = 1.0e10;
        end;
    end;
elseif(lblend==2);              % Model for Blend #2
    if((lc>=0.)&&(lc<=295.));
        y = a210 + a211*lc + a212*lc^2 + a213*lc^3 ;
    elseif(lc>295.);
        y = 1.0e10;
    end;
end
end
%% DEFAULT DATA INPUT FILE
function defaultdata() ;
%% --------------------------------
%       DEFAULT DATA INPUT FILE
%  --------------------------------
% Write out a formatted 'infile.dat' for user editing if none exists in the
% directory where 'Dunnv1.m' is run.
% Defualt INPUT VALUES BLOCK
pla = 80.0 ;    % 65 percent throttle (from 16.5 to 80)
tamb = 59.0 ;   % Ambient temperature (in degF)
pamb =  14.7 ;  % Ambient pressure (in psia)
nenc = 10 ;     % No. of dust cloud encounters (No. of enc)
% Dust cloud encounter index numbers
ib = [0 1 2 3 4 5 6 7 8 9 10];
% CMAS blend (1 = mpb, 2 = blend2), as defined in DNA TR-92-121
blend = [0 2 2 2 2 2 2 2 2 2 2];
% dust conc. (mg/m^3)
c = [0. 100. 100. 100. 100. 100. 100. 100. 100. 100. 100.];
% duration (min)
delt = [0.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0];
% debug mode {0=off, 1=on}
debug = 0;
% Write out these properties into default file in carefully formatted
% output
IN = fopen('infile.dat','w') ;
fprintf(IN,'PEARL - F101 reponse model \n') ;
fprintf(IN,' \n') ;
fprintf(IN,' \n') ;
fprintf(IN,'PLA (deg) from (16.5-80.0) =>  \n') ;
fprintf(IN,'%-4.2e  \n',pla) ;
fprintf(IN,'ambient pressure (psia) =>  \n') ;
fprintf(IN,'%-4.2e  \n',pamb) ;
fprintf(IN,'ambient temperature (F) =>  \n') ;
fprintf(IN,'%-4.2e  \n',tamb) ;
fprintf(IN,'debug mode (0=off,1=on)  =>  \n') ;
fprintf(IN,'%-2i  \n',debug) ;
fprintf(IN,'#_of_encounters(-)(1-10)=>  \n') ;
fprintf(IN,'%-2i  \n',nenc) ;
fprintf(IN,'enc#, blend (1=mpb,2=blend2), dust conc. (mg/m^3), duration (min) \n') ;
fprintf(IN,' \n') ;
for i = 1:1:nenc; fprintf(IN,'%-2i \t %-1i \t %-4.2e \t  %-4.2e \n', ib(i+1),blend(i+1),c(i+1),delt(i+1));end;
fclose(IN); % Close file
end


function DPMpcOneBeam9(leak, spectrum_File, planC_File, nhist, OutputError, whichBeam, PBMaxWidth, gradsense, MCsolver, saveIM, sourceModel, doseToWater, fillWater, useWedge, inputPB, Softening, batch)
% written by KZ, JC
% Calculate doseV dose3D directly from DPM, without compressing it.
% Use generateDPMdose.m instead of DPMInfluenceJing.m
% Calculate IM struct, call DPM, get
%
% Usage: IIMCalc = calculateBeamIM(0.018, 'DPM_10beams_planC', 1, 1, 5)
%   leak = 0.018
%   spectrum_File = file name of photon spectrum. eg. photon6MV.spectrum
%   planC_File = file name contained planC, eg. 'DPM_10beams_planC'
%   indexBeam = Beam Index: 1, 2, ...
%   imin = Beamlet index
%   imax = Beamlet index
%
%   eval(['load Beam',num2str(indexBeam),'_w']);
%   where, Beam1_w.mat (Beam2_w.mat) need to be in the current directory. It's the weight of
%   the beamlets for each beam.
%
% JC Feb 27 06
% Call in DPMInfluence.m; use sum(10*clock) as the random seeds input to DPM.
%
% JC. Aug 10, 2005, Need to make planC read from the disk, since in the
% stand-alone mode, no matlab run is expected.
%
% JC Dec 18 06
% Add 'saveIM' input,
%   If it's larger than 0, say 1, then save IM
%   If it's 0, don't save IM
%
% LM: JC Jan 26 2007
% Include non-zero couchAngle
% The desination coordinates should be the patient support
% system.
% The previous assumption is that the "fixed system" is the same as
% the "Patient support system".
% test
%
% LM: JC Mar 03, 2007
% Score dose to water flag. Only valid for VMC++ for now. "doseToWater"
% Add "fillWater" flag. It 1, fill the skin strucutere with all water
% density, i.e. CT value = 1024.
%
% LM: JC Mar 29, 2007
% get rid of 'stateS' arguments to 'generateVMCdose.m', 'generateDPMdose.m'
% get rid of the call for 'IMRTP_plancheck'
% add 'useWedge' flag. default = 0; when useWedge == 1, use Elekta
% universal wedge.
% only implemented for DPM, openField == 0, sourceModel == 0;
%
% open planC_File
% In matlab release 14, "eval" can be compiled.
Error = []; %Initialize Error to NULL.
load(planC_File);
%eval(['load ',planC_File]);
% This planC_File should contains planC and stateS
%leak = 0.018; %leakage in per *100
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

if ischar(leak)
    leak = str2num(leak)
end

if ischar(nhist)
    nhist = str2num(nhist)
end

if ischar(OutputError)
    OutputError = str2num(OutputError)
end

if ischar(whichBeam)
    whichBeam = str2num(whichBeam)
end

if ischar(PBMaxWidth)
    PBMaxWidth = str2num(PBMaxWidth)
end

if ischar(gradsense)
    gradsense = str2num(gradsense)
end

% If MCsolver == 1, use DPM, default
% If MCsolver == 2, use VMC++
% other, throw out error
if ischar(MCsolver)
    MCsolver = str2num(MCsolver)
end

% If saveIM == 0, calculate doseV, do not calc/save IM
% If saveIM ~= 0, calc/save IM, only valid for DPM for now, ie. MCsolver == 1.
if ischar(saveIM)
    saveIM = str2num(saveIM)
end

% If sourceModel == 0, no source model, only point source
% If sourceModel == 1, source model version 1: point source (Primary) + FF
% + electron contamination.
% Expandable to other source models for the future development.

if ischar(sourceModel)
    sourceModel = str2num(sourceModel)
end

if ischar(doseToWater)
    doseToWater = str2num(doseToWater)
end

if ischar(fillWater)
    fillWater = str2num(fillWater)
end

if ischar(useWedge)
    useWedge = str2num(useWedge)
end

if ischar(inputPB)
    inputPB = str2num(inputPB)           %Flag, inputPB == 1, ask user to input .mat file storing PB info.
                                 % That file can be generated using
                                 % function generateOpenfiledPB.m
end
% Now, only work for DPM, planC{7} not empty.

if ischar(Softening)
    Softening = str2num(Softening)           %Flag,  == 1, use off-axis-softening in dpm calc.

end
% Now, only work for DPM, planC{7} not empty.

if ischar(batch)
    batch = str2num(batch)
end

currentDir = cd;
if (~isempty(planC{7}))
    % When there are multiple beam definition in planC, use the first BEAM
    % data as the default.

    %for indexBeam = 1 : planC{7}(1).FractionGroupSequence.Item_1.NumberOfBeams
    for indexBeam = whichBeam

            bs = planC{7}(1).BeamSequence.(['Item_' num2str(indexBeam)]);

            %Parse BlockData into PB information.
            input = bs.BlockSequence.Item_1.BlockData;
            input = reshape(input, 2, [])';

            [xPosV, yPosV, beamlet_delta_x, beamlet_delta_y]=...
                BlockToPB(input, 10, 10);   %PB size here is in mm.
            w_field = ones(size(xPosV));

            gA = bs.ControlPointSequence.Item_1.GantryAngle;
            iC = bs.ControlPointSequence.Item_1.IsocenterPosition;
            IMBase.beams.gantryAngle = gA;
            % couchAngle is PatientSupportAngle, in DICOM, i.e. IEC format
            % (in deg)
            IMBase.beams.couchAngle = bs.ControlPointSequence.Item_1.PatientSupportAngle;
            % JC Apr. 10 2007,
            % Add Beam Limiting Deviceing Angle, i.e. collimator angle from
            % DICOM, in IED format. (in deg)
            % Not test for "Non-zero angle" yet.
            IMBase.beams.collimatorAngle = bs.ControlPointSequence.Item_1.BeamLimitingDeviceAngle

            try
            if ~isfield(planC{7}(1).PatientSetupSequence,(['Item_' num2str(indexBeam)]))
                position = {planC{7}(1).PatientSetupSequence.(['Item_' num2str(1)]).PatientPosition};
            else
                position = {planC{7}(1).PatientSetupSequence.(['Item_' num2str(indexBeam)]).PatientPosition};
            end
            catch
                position = 'HFS';
                disp('Use default patient posiont HFS')
            end

            if strcmpi(position, 'HFP')
                IMBase.beams(1).isocenter.x = iC(1)/10;
                IMBase.beams(1).isocenter.y = iC(2)/10;
                IMBase.beams(1).isocenter.z = -iC(3)/10;
            else
                IMBase.beams(1).isocenter.x = iC(1)/10;
                IMBase.beams(1).isocenter.y = -iC(2)/10;
                IMBase.beams(1).isocenter.z = -iC(3)/10;
            end

            IMBase.beams(1).isodistance = bs.SourceAxisDistance/10;

            IMBase.beams(1).beamEnergy = bs.ControlPointSequence.Item_1.NominalBeamEnergy;

            xPosV = xPosV/10;
            yPosV = yPosV/10;
            beamlet_delta_x = beamlet_delta_x/10;
            beamlet_delta_y = beamlet_delta_y/10;

            IMBase.goals.PBMargin = 0.5;
            IMBase.goals.structNum = 2;
            IMBase.params.xyDownsampleIndex = 1;
            IMBase.goals.isTarget(1) = 'y';
            IMBase.goals.xySampleRate = 2;
            IMBase.params.numCTSamplePts = 300;

            IMBase.beams(1).zRel = 0;
            IMBase.beams(1).xRel =  IMBase.beams(1).isodistance * sindeg(IMBase.beams(1).gantryAngle);
            IMBase.beams(1).yRel =  IMBase.beams(1).isodistance * cosdeg(IMBase.beams(1).gantryAngle);

            % JC In the above calc. for xRel and yRel, the assumption is the couchAngle
            % == 0. i.e. the "fixed system" is the same as the "patient support
            % system".
            % LM: JC Jan 26 2007
            % Include non-zero couchAngle
            % The desination coordinates should be the patient support
            % system.
            % The previous assumption is that the "fixed system" is the same as
            % the "Patient support system".
            % test

            %         patientVectorsM = zeros(size(RTOGVectorsM));
            patientxRel = cosdeg(IMBase.beams.couchAngle) * IMBase.beams(1).xRel- sindeg(IMBase.beams.couchAngle) * IMBase.beams(1).zRel;
            patientyRel = IMBase.beams(1).yRel;
            patientzRel = sindeg(IMBase.beams.couchAngle) * IMBase.beams(1).xRel + cosdeg(IMBase.beams.couchAngle) * IMBase.beams(1).zRel;
            IMBase.beams(1).xRel = patientxRel;
            IMBase.beams(1).yRel = patientyRel;
            IMBase.beams(1).zRel = patientzRel;
            clear patientxRel patientyRel patientzRel

            %IMBase.params.algorithm = 'VMC++';
            %IMCalc = IMRTP_MapCheck(IMBase);

            IMBase.params.algorithm = 'DPM';
            %      load PB_40x40.mat beamlet_delta_x beamlet_delta_y xPosV yPosV w_field
            %      load PB_15x15.mat beamlet_delta_x beamlet_delta_y xPosV yPosV w_field

            if inputPB         % get PB info from a specified .mat file.
                setappdata(0, 'usenativesystemdialogs', false)
                currentDir = cd;

                [FileName,path] = uigetfile('*.mat','Select MAT file containing beamlet_delta_x beamlet_delta_y xPosV yPosV w_field');

                if path == 0
                    errordlg('File Should exist');
                    error('File Should exist');
                end

                cd(path);
                load (FileName, 'beamlet_delta_x', 'beamlet_delta_y', 'xPosV', 'yPosV', 'w_field')
                cd(currentDir);
            end

            disp('Number of pencile beams are:')
            length(xPosV)

            sourceS = IMBase.beams(1);
            [RTOGPBVectorsM, RTOGPBVectorsM_MC, PBMaskM, rowPBV, colPBV, xPBPosV, yPBPosV, beamlet_delta_x, beamlet_delta_y] = ...
                    getPBRays(xPosV, yPosV, beamlet_delta_x, beamlet_delta_y, sourceS);
            IMCalc = IMBase;
            IMCalc.beams(1).RTOGPBVectorsM_MC = RTOGPBVectorsM_MC;
            IMCalc.beams(1).RTOGPBVectorsM    = RTOGPBVectorsM;
            IMCalc.beams(1).xPBPosV           = xPBPosV;
            IMCalc.beams(1).yPBPosV           = yPBPosV;
            IMCalc.beams(1).rowPBV            = rowPBV;
            IMCalc.beams(1).colPBV            = colPBV;
            % No need of this field
            IMCalc.beams(1).CTTraceS          = struct;
            IMCalc.beams(1).beamletDelta_x    = beamlet_delta_x;
            IMCalc.beams(1).beamletDelta_y    = beamlet_delta_y;

            %RTOG positions of sources
            IMCalc.beams(1).x = IMCalc.beams(1).xRel + IMCalc.beams(1).isocenter.x;
            IMCalc.beams(1).y = IMCalc.beams(1).yRel + IMCalc.beams(1).isocenter.y;
            IMCalc.beams(1).z = IMCalc.beams(1).zRel + IMCalc.beams(1).isocenter.z;
            %IMCalc = IMRTP_MapCheck(IMBase, planC, stateS, xPosV, yPosV, beamlet_delta_x, beamlet_delta_y, gA);
            % JC. 11 Aug, 05 Added more arguments to IMRTP_MapCheck,

            % JC. Aug. 3, 2005
            % Add fields to IMCalc, necessary for generateDPMInfluence.m
            IMCalc.beams.collimatorAngle = 0;
            IMCalc.params.ScatterMethod = 'exponential';
            % Use 0.002 instead the original default 0.01 as threshold value.
            % Change to 0.004, since the stored IM struct is very large.
            IMCalc.params.Scatter.Threshold = 0.004;
            IMCalc.params.Scatter.RandomStep = 30;
            IMCalc.beamlets = struct;

            %JC Add/porpulate VMC field in IM struct.
            IMCalc.params.VMC.NumParticles = nhist;
            IMCalc.params.VMC.NumBatches = 10;
            IMCalc.params.VMC.scoreDoseToWater = 'No';
            IMCalc.params.VMC.monoEnergy = 0;   %?
            IMCalc.params.VMC.repeatHistory = 0.2510;
            IMCalc.params.VMC.splitPhotons = 'Yes';
            IMCalc.params.VMC.photonSplitFactor = -40;
            IMCalc.params.VMC.base = 2;;
            IMCalc.params.VMC.dimension = 60;    %?
            IMCalc.params.VMC.skip = 0;  %? 1?
            IMCalc.params.VMC.includeError = 'No';
            IMCalc.params.VMC.spectrum = spectrum_File;
            IMCalc.beams.beamModality = 'photons';

            if doseToWater
            IMCalc.params.VMC.scoreDoseToWater = 'Yes';
            disp('VMC++ score dose to Water.');
            end

            % JC Jun 06 2006
            % Still need to fill in the structure of IMCalc, but don't need put
            % it back(?), since it's not to be used to calculate dose.
            % Will also need to use 'DoseScale' inside generateDPMdose.m,
            % since DPM output dose on a per partical base.
            % Need to use w_field as a way of telling DPM how to add beamlets
            % up.

            % JC Aug 30. 2006
            if (MCsolver == 1)

                [IMCalc doseV indV numberParticles] = generateDPMdose7(IMCalc,spectrum_File,nhist, OutputError,planC, indexBeam, w_field, saveIM, sourceModel, fillWater, useWedge, Softening);

            elseif (MCsolver == 2)
                % use VMC++
                [doseV indV] = generateVMCdose(IMCalc, planC, w_field, sourceModel, doseToWater, fillWater,saveIM);
            else
                error('INVALID input: MCsolver == 1, DPM; MCsolver == 2, VMC++')
            end

    end

else %If it's a conventional plan, planC{7} is empty

    indexBeam = whichBeam;
    if (length(planC{6}(indexBeam).file) > 5)
        [xPosV, yPosV, beamlet_delta_x, beamlet_delta_y, beamGeometry]=...
            planCToPB(planC, indexBeam, 1, 1);
    else
        % The field is shaped by Jaws, not MLC.
        [xPosV, yPosV, beamlet_delta_x, beamlet_delta_y, beamGeometry]=...
            planCToPBJaws(planC, indexBeam, 1, 1);
    end

    IMBase.beams(1).isocenter.x = beamGeometry.isocenter(1);
    IMBase.beams(1).isocenter.y = beamGeometry.isocenter(2);
    IMBase.beams(1).isocenter.z = beamGeometry.isocenter(3);
    % RTOG file has the same couchAngle definition as in IEC1217.
    IMBase.beams(1).couchAngle = beamGeometry.couchAngle;
    % RTOG file has different gantryAngle definition from IEC1217.
    % Use IEC 1217 coordinates.
    IMBase.beams(1).gantryAngle = - beamGeometry.gantryAngle;
    IMBase.beams(1).isodistance = beamGeometry.isoDistance;

    IMBase.goals.PBMargin = 0.5;
    IMBase.goals.structNum = 2;
    IMBase.params.xyDownsampleIndex = 1;
    IMBase.goals.isTarget(1) = 'y';
    IMBase.goals.xySampleRate = 1;
    IMBase.params.numCTSamplePts = 300;
    IMBase.beams(1).beamEnergy = planC{6}(1).beamEnergyMeV;

    IMBase.beams(1).zRel = 0;
    IMBase.beams(1).xRel =  IMBase.beams(1).isodistance * sindeg(IMBase.beams(1).gantryAngle);
    IMBase.beams(1).yRel =  IMBase.beams(1).isodistance * cosdeg(IMBase.beams(1).gantryAngle);

    % JC In the above calc. for xRel and yRel, the assumption is the couchAngle
    % == 0. i.e. the "fixed system" is the same as the "patient support
    % system".
    % LM: JC Jan 26 2007
    % Include non-zero couchAngle
    % The desination coordinates should be the patient support
    % system.
    % The previous assumption is that the "fixed system" is the same as
    % the "Patient support system".

    patientxRel = cosdeg(IMBase.beams.couchAngle) * IMBase.beams(1).xRel- sindeg(IMBase.beams.couchAngle) * IMBase.beams(1).zRel;
    patientyRel = IMBase.beams(1).yRel;
    patientzRel = sindeg(IMBase.beams.couchAngle) * IMBase.beams(1).xRel + cosdeg(IMBase.beams.couchAngle) * IMBase.beams(1).zRel;
    IMBase.beams(1).xRel = patientxRel;
    IMBase.beams(1).yRel = patientyRel;
    IMBase.beams(1).zRel = patientzRel;
    clear patientxRel patientyRel patientzRel

    IMBase.beams(1).collimatorAngle = beamGeometry.collimatorAngle;

    disp('Number of pencile beams are:')
            length(xPosV)

    % JC Mar 29, 2007
    % Repace 'IMRTP_plancheck' by the following command
            sourceS = IMBase.beams(1);
            [RTOGPBVectorsM, RTOGPBVectorsM_MC, PBMaskM, rowPBV, colPBV, xPBPosV, yPBPosV, beamlet_delta_x, beamlet_delta_y] = ...
                    getPBRays(xPosV, - yPosV, beamlet_delta_x, beamlet_delta_y, sourceS);
    % Note: In the above line, use '- yPosV' instead of 'yPosV'. Since IEC and RTOG have different definition about yPosV.
            IMCalc = IMBase;
            IMCalc.beams(1).RTOGPBVectorsM_MC = RTOGPBVectorsM_MC;
            IMCalc.beams(1).RTOGPBVectorsM    = RTOGPBVectorsM;
            IMCalc.beams(1).xPBPosV           = xPBPosV;
            IMCalc.beams(1).yPBPosV           = yPBPosV;
            IMCalc.beams(1).rowPBV            = rowPBV;
            IMCalc.beams(1).colPBV            = colPBV;
            % No need of this field
            IMCalc.beams(1).CTTraceS          = struct;
            IMCalc.beams(1).beamletDelta_x    = beamlet_delta_x;
            IMCalc.beams(1).beamletDelta_y    = beamlet_delta_y;

            %RTOG positions of sources
            IMCalc.beams(1).x = IMCalc.beams(1).xRel + IMCalc.beams(1).isocenter.x;
            IMCalc.beams(1).y = IMCalc.beams(1).yRel + IMCalc.beams(1).isocenter.y;
            IMCalc.beams(1).z = IMCalc.beams(1).zRel + IMCalc.beams(1).isocenter.z;

    % JC Mar 29, 2007
    % Replace 'IMRTP_MapCheck' by above commands.
    % IMCalc = IMRTP_MapCheck(IMBase, planC, stateS, xPosV, - yPosV, beamlet_delta_x, beamlet_delta_y, gA);
    clear IMBase beamlet_delta_x beamlet_delta_y PlanC_File xPosV yPosV

    % Add fields to IMCalc, necessary for generateDPMInfluence.m
    IMBase.params.algorithm = 'DPM';
    IMCalc.params.ScatterMethod = 'exponential';
    IMCalc.params.Scatter.Threshold = 0.004;
    IMCalc.params.Scatter.RandomStep = 30;
    IMCalc.beamlets = struct;

    % For the conventional RT. No change in w_field
    w_field = ones(length(IMCalc.beams.beamletDelta_x),1);
    %[doseV indV] = generateDPMdose(IMCalc,spectrum_File,nhist, OutputError,planC,stateS, indexBeam, w_field);
    % Dec 18, 2006
    % Use the new modified DPM, with Source Model.

    %JC Add/porpulate VMC field in IM struct.
    IMCalc.params.VMC.NumParticles = nhist;
    IMCalc.params.VMC.NumBatches = 10;
    IMCalc.params.VMC.scoreDoseToWater = 'No';
    IMCalc.params.VMC.monoEnergy = 0;   %?
    IMCalc.params.VMC.repeatHistory = 0.2510;
    IMCalc.params.VMC.splitPhotons = 'Yes';
    IMCalc.params.VMC.photonSplitFactor = -40;
    IMCalc.params.VMC.base = 2;;
    IMCalc.params.VMC.dimension = 60;    %?
    IMCalc.params.VMC.skip = 0;  %? 1?
    IMCalc.params.VMC.includeError = 'No';
    IMCalc.params.VMC.spectrum = spectrum_File;
    IMCalc.beams.beamModality = 'photons';

    if doseToWater
            IMCalc.params.VMC.scoreDoseToWater = 'Yes';
            disp('VMC++ score dose to Water.');
     end

    if (MCsolver == 1)

        [IMCalc doseV indV numberParticles] = generateDPMdose7(IMCalc,spectrum_File,nhist, OutputError,planC, indexBeam, w_field, saveIM, sourceModel, fillWater, useWedge, Softening);

    elseif (MCsolver == 2)
        % use VMC++
        [doseV indV] = generateVMCdose(IMCalc, planC, w_field, sourceModel, doseToWater, fillWater,saveIM);
    else
        error('INVALID input: MCsolver == 1, DPM; MCsolver == 2, VMC++')
    end

    % ?? Right place to put end???
end

%%%%%%%%%%%%%%%
    siz = getUniformizedSize(planC);
    dose3D = zeros(siz);

    if (OutputError == 1)            % OutputError only valid for DPM,ie., MCsolver == 1
        doseV = doseV(:,1);
        Error_3D = zeros(siz);
        Error_3D(indV') = doseV(:,2);
    else
        disp('Do not output error')
        dose3D(indV') = doseV;
    end

    %        filename = ['doseV_',num2str(indexBeam),'_', num2str(nhist), '_', num2str(batch)];
    %        save(filename, 'doseV', 'indV');

    filename = ['dose3D_',num2str(indexBeam),'_', num2str(nhist), '_', num2str(batch)];
    save(filename, 'dose3D');

    % Only Output w_field for the first batch
    if(batch == 1)
        filename = ['w_field',num2str(indexBeam)];
        save(filename, 'w_field');%dose3D = (dose3D/max(dose3D(:)));
    end

if (saveIM == 1)
%saveIM  %Only valid for DPM, i.e. IM
    filename = ['IMCalc_Beam',num2str(indexBeam),'_', num2str(nhist), '_', num2str(batch)];
    save(filename, 'IMCalc');
end
clear IMCalc dose3D w_field

end