Home > functions > dsSimulate.m

dsSimulate

PURPOSE ^

% data=dsSimulate(model,'option',value,...)

SYNOPSIS ^

function [data,studyinfo,result] = dsSimulate(model,varargin)

DESCRIPTION ^

% data=dsSimulate(model,'option',value,...)
 Purpose: manage simulation of a DynaSim model. This high-level function
 offers many options to control the number of simulations, how the model is
 optionally varied across simulations, and how the numerical integration
 is performed. It can optionally save the simulated data and create/submit
 simulation jobs to a compute cluster.
 Inputs:
   model: DynaSim model structure or equations (see dsGenerateModel and
          dsCheckModel for more details)

   solver options (provided as key/value pairs: 'option1',value1,'option2',value2,...):
     'solver'      : solver for numerical integration (see dsGetSolveFile)
                     {'euler','rk2','rk4', or any built-in matlab solver} (default: 'rk4')
     'tspan'       : time limits of simulation [begin,end] (default: [0 100]) [ms]
                     note: units must be consistent with dt and model equations
     'dt'          : time step used for DynaSim solvers (default: .01) [ms]
     'downsample_factor': downsampling applied during simulation (default: 1, no downsampling)
                     (only every downsample_factor-time point is stored in memory and/or written to disk)
     'ic'          : numeric array of initial conditions, one value per state
                     variable (default: all zeros). overrides definition in model structure
     'random_seed' : seed for random number generator (default: 'shuffle', set randomly) (usage: rng(options.random_seed))
     'compile_flag': whether to compile simulation using coder instead of
                     interpreting Matlab {0 or 1} (default: 0)
     'sparse_flag' : whether to convert numeric fixed variables to sparse matrices {0 or 1} (default: 0)

   options for running sets of simulations:
     'vary'        : (default: [], vary nothing): cell matrix specifying model
                     components to vary across simulations (see NOTE 1 and dsVary2Modifications)

   options to control saved data:
     'matCompatibility_flag': whether to save mat files in compatible mode, vs to prioritize > 2GB VARs {0 or 1} (default: 1)
     'save_results_flag': whether to save results of analysis and plotting
     'save_data_flag': whether to save simulated data to disk after completion {0 or 1} (default: 0)
     'overwrite_flag': whether to overwrite existing data files {0 or 1} (default: 0)
     'study_dir'     : relative or absolute path to output directory (default: current directory)
     'prefix'        : string to prepend to all output file names (default: 'study')
     'disk_flag'     : whether to write to disk during simulation instead of storing in memory {0 or 1} (default: 0)
     'precision'     : {'single','double'} precision of simulated data saved to disk (default: 'single')

   options for cluster computing:
     'cluster_flag'  : whether to run simulations on a cluster submitted
                     using qsub (see dsCreateBatch) {0 or 1} (default: 0)
     'sims_per_job'  : number of simulations to run per batch job (default: 1)
     'memory_limit'  : memory to allocate per batch job (default: '8G')
     'qsub_mode'     : whether to use SGE -t array for 1 qsub, mode: 'array'; or
                         qsub in csh for loop, mode: 'loop'. (default: 'loop').
     'one_solve_file_flag': only use 1 file of each time when solving (default: 0)
     'optimize_big_vary': Select best options for doing many sims {0 or 1} (default: 0)

   options for parallel computing: (requires Parallel Computing Toolbox)
     'parallel_flag' : whether to use parfor to run simulations {0 or 1} (default: 0)
     'num_cores'     : number of cores to specify in the parallel pool
     *note: parallel computing has been disabled for debugging...

   options for post-processing:
     'analysis_functions': cell array of analysis function handles
     'analysis_options'  : cell array of option cell arrays {'option1',value1,...}
     'plot_functions'    : cell array of plot function handles
     'plot_options'      : cell array of option cell arrays {'option1',value1,...}

   other options:
     'verbose_flag'  : whether to display informative messages/logs (default: 0)
     'modifications' : how to modify DynaSim specification structure component before simulation (see dsApplyModifications)
     'experiment'    : function handle of experiment function (see NOTE 2)
     'experiment_options' : single cell array of key/value options for experiment function
     'optimization'  : function handle of optimization function (see NOTE 2)
     'debug_flag'    : set to debug mode
     'benchmark_flag': set to benchmark mode. will add tic/toc to sims.

 Outputs:
   DynaSim data structure:
     data.labels           : list of state variables and monitors recorded
     data.(state_variables): state variable data matrix [time x cells]
     data.(monitors)       : monitor data matrix [time x cells]
     data.time             : time vector [time x 1]
     data.simulator_options: simulator options used to generate simulated data
     data.model            : model used to generate simulated data
     [data.varied]         : list of varied model components (present only if anything was varied)

   DynaSim studyinfo structure (only showing select fields, see dsCheckStudyinfo for more details)
     studyinfo.study_dir
     studyinfo.base_model (=[]): original model from which a set of simulations was derived
     studyinfo.base_simulator_options (=[])
     studyinfo.base_solve_file (='')
     studyinfo.simulations(k): metadata for each simulation in a set of simulations
                           .sim_id         : unique identifier in study
                           .modifications  : modifications made to the base model during this simulation
                           .data_file      : full filename of eventual output file
                           .batch_dir (=[]): directory where batch jobs were saved (if cluster_flag=1)
                           .job_file (=[]) : m-file batch job that runs this simulation (if cluster_flag=1)
                           .simulator_options: simulator options for this simulation
                           .solve_file     : full filename of m- or mex-file that numerically integrated the model

 NOTE 1: 'vary' indicates the variable to vary, the values
 it should take, and the object whose variable should be varied.
 Syntax: vary={object, variable, values; ...}. For instance, to vary
 parameter 'gNa', taking on values 100 and 120, in population 'E', set
 vary={'E','gNa',[100 120]}. To additionally vary 'gSYN' in the connection
 mechanism from 'E' to 'I', set vary={'E','gNa',[100 120];'E->I','gSYN',[0 1]}.
 Mechanism lists and equations can also be varied. (see dsVary2Modifications
 for more details and examples).

 EXAMPLES:
 Example 1: Lorenz equations with phase plot
   eqns={
     's=10; r=27; b=2.666';
     'dx/dt=s*(y-x)';
     'dy/dt=r*x-y-x*z';
     'dz/dt=-b*z+x*y';
   };
   data=dsSimulate(eqns,'tspan',[0 100],'ic',[1 2 .5]);
   plot(data.pop1_x,data.pop1_z); title('Lorenz equations'); xlabel('x'); ylabel('z')

 Example 2: Leaky integrate-and-fire with spike monitor
   eqns={
     'tau=10; R=10; E=-70; I=1.55; thresh=-55; reset=-75';
     'dV/dt=(E-V+R*I)/tau; if(V>thresh)(V=reset)';
     'monitor V.spikes(thresh)';
   };
   data=dsSimulate(eqns,'tspan',[0 200],'ic',-75);
   data.pop1_V(data.pop1_V_spikes==1)=20; % insert spike
   plot(data.time,data.pop1_V); xlabel('time (ms)'); ylabel('V'); title('LIF with spikes')

 Example 3: Hodgkin-Huxley-type Intrinsically Bursting neuron
   eqns='dv/dt=5+@current; {iNaF,iKDR,iM}; gNaF=100; gKDR=5; gM=1.5; v(0)=-70';
   data=dsSimulate(eqns,'tspan',[0 200]);
   figure; plot(data.time,data.(data.labels{1}))
   xlabel('time (ms)'); ylabel('membrane potential (mV)'); title('Intrinsically Bursting neuron')

 Example 4: varying max Na+ conductance in Hodgkin-Huxley neuron
   eqns='dv/dt=@current+10; {iNa,iK}; v(0)=-60';
   data=dsSimulate(eqns,'vary',{'','gNa',[50 100 200]});
   % plot how mean firing rate varies with parameter
   dsPlotFR(data,'bin_size',30,'bin_shift',10); % bin_size and bin_shift in [ms]

 Example 5: simulate predefined Hodgkin-Huxley neuron with Iapp=10
   data = dsSimulate('HH','vary',{'HH','Iapp',10});
   figure; plot(data.time,data.HH_V);

 Example 6: Sparse Pyramidal-Interneuron-Network-Gamma rhythm with rastergram
   % define equations of cell model (same for E and I populations)
   eqns={
     'dv/dt=Iapp+@current/Cm+noise*randn(1,N_pop)*sqrt(dt)/dt';
     'monitor v.spikes, iGABAa.functions, iAMPA.functions'
   };
   % define specification for two-population network model
   s=[];
   s.populations(1).name='E';
   s.populations(1).size=80;
   s.populations(1).equations=eqns;
   s.populations(1).mechanism_list={'iNa','iK'};
   s.populations(1).parameters={'Iapp',5,'gNa',120,'gK',36,'Cm',1,'noise',4};
   s.populations(2).name='I';
   s.populations(2).size=20;
   s.populations(2).equations=eqns;
   s.populations(2).mechanism_list={'iNa','iK'};
   s.populations(2).parameters={'Iapp',0,'gNa',120,'gK',36,'Cm',1,'noise',4};
   s.connections(1).source='I';
   s.connections(1).target='E';
   s.connections(1).mechanism_list={'iGABAa'};
   s.connections(1).parameters={'tauD',10,'gSYN',.1,'netcon','ones(N_pre,N_post)'};
   s.connections(2).source='E';
   s.connections(2).target='I';
   s.connections(2).mechanism_list={'iAMPA'};
   s.connections(2).parameters={'tauD',2,'gSYN',.1,'netcon',ones(80,20)};
   % simulate model
   data=dsSimulate(s);
   % plot voltages and rastergram
   figure;
   subplot(2,1,1); % voltage traces
   plot(data.time,data.E_v,'b-',data.time,data.I_v,'r-')
   title('Sparse Pyramidal-Interneuron-Network-Gamma (sPING)'); ylabel('membrane potential (mV)');
   subplot(2,1,2); % rastergram
   E_spikes=nan(size(data.E_v_spikes)); E_spikes(data.E_v_spikes==1)=1;
   I_spikes=nan(size(data.I_v_spikes)); I_spikes(data.I_v_spikes==1)=1;
   plot(data.time,E_spikes+repmat(1:80,[length(data.time) 1]),'bo'); hold on
   plot(data.time,I_spikes+repmat(80+(1:20),[length(data.time) 1]),'ro'); axis([0 100 0 100]);
   title('rastergram'); xlabel('time (ms)'); ylabel('cell index');
   % simulate model varying two parameters (Iapp and tauD in sPING)
   % warning: this may take up to a minute to complete:
   vary={
     'E'   ,'Iapp',[0 10 20];     % amplitude of tonic input to E-cells
     'I->E','tauD',[5 10 15]      % inhibition decay time constant from I to E
     };
   data=dsSimulate(s,'vary',vary);
   % plot firing rates calculated from spike monitor in both populations
   dsPlotFR(data,'variable','*_spikes','bin_size',30,'bin_shift',10);


 See also: dsGenerateModel, dsCheckModel, dsGetSolveFile, dsCheckData,
           dsVary2Modifications, dsCheckStudyinfo, dsCreateBatch

 Author: Jason Sherfey, PhD <jssherfey@gmail.com>
 Copyright (C) 2016 Jason Sherfey, Boston University, USA

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function [data,studyinfo,result] = dsSimulate(model,varargin)
0002 %% data=dsSimulate(model,'option',value,...)
0003 % Purpose: manage simulation of a DynaSim model. This high-level function
0004 % offers many options to control the number of simulations, how the model is
0005 % optionally varied across simulations, and how the numerical integration
0006 % is performed. It can optionally save the simulated data and create/submit
0007 % simulation jobs to a compute cluster.
0008 % Inputs:
0009 %   model: DynaSim model structure or equations (see dsGenerateModel and
0010 %          dsCheckModel for more details)
0011 %
0012 %   solver options (provided as key/value pairs: 'option1',value1,'option2',value2,...):
0013 %     'solver'      : solver for numerical integration (see dsGetSolveFile)
0014 %                     {'euler','rk2','rk4', or any built-in matlab solver} (default: 'rk4')
0015 %     'tspan'       : time limits of simulation [begin,end] (default: [0 100]) [ms]
0016 %                     note: units must be consistent with dt and model equations
0017 %     'dt'          : time step used for DynaSim solvers (default: .01) [ms]
0018 %     'downsample_factor': downsampling applied during simulation (default: 1, no downsampling)
0019 %                     (only every downsample_factor-time point is stored in memory and/or written to disk)
0020 %     'ic'          : numeric array of initial conditions, one value per state
0021 %                     variable (default: all zeros). overrides definition in model structure
0022 %     'random_seed' : seed for random number generator (default: 'shuffle', set randomly) (usage: rng(options.random_seed))
0023 %     'compile_flag': whether to compile simulation using coder instead of
0024 %                     interpreting Matlab {0 or 1} (default: 0)
0025 %     'sparse_flag' : whether to convert numeric fixed variables to sparse matrices {0 or 1} (default: 0)
0026 %
0027 %   options for running sets of simulations:
0028 %     'vary'        : (default: [], vary nothing): cell matrix specifying model
0029 %                     components to vary across simulations (see NOTE 1 and dsVary2Modifications)
0030 %
0031 %   options to control saved data:
0032 %     'matCompatibility_flag': whether to save mat files in compatible mode, vs to prioritize > 2GB VARs {0 or 1} (default: 1)
0033 %     'save_results_flag': whether to save results of analysis and plotting
0034 %     'save_data_flag': whether to save simulated data to disk after completion {0 or 1} (default: 0)
0035 %     'overwrite_flag': whether to overwrite existing data files {0 or 1} (default: 0)
0036 %     'study_dir'     : relative or absolute path to output directory (default: current directory)
0037 %     'prefix'        : string to prepend to all output file names (default: 'study')
0038 %     'disk_flag'     : whether to write to disk during simulation instead of storing in memory {0 or 1} (default: 0)
0039 %     'precision'     : {'single','double'} precision of simulated data saved to disk (default: 'single')
0040 %
0041 %   options for cluster computing:
0042 %     'cluster_flag'  : whether to run simulations on a cluster submitted
0043 %                     using qsub (see dsCreateBatch) {0 or 1} (default: 0)
0044 %     'sims_per_job'  : number of simulations to run per batch job (default: 1)
0045 %     'memory_limit'  : memory to allocate per batch job (default: '8G')
0046 %     'qsub_mode'     : whether to use SGE -t array for 1 qsub, mode: 'array'; or
0047 %                         qsub in csh for loop, mode: 'loop'. (default: 'loop').
0048 %     'one_solve_file_flag': only use 1 file of each time when solving (default: 0)
0049 %     'optimize_big_vary': Select best options for doing many sims {0 or 1} (default: 0)
0050 %
0051 %   options for parallel computing: (requires Parallel Computing Toolbox)
0052 %     'parallel_flag' : whether to use parfor to run simulations {0 or 1} (default: 0)
0053 %     'num_cores'     : number of cores to specify in the parallel pool
0054 %     *note: parallel computing has been disabled for debugging...
0055 %
0056 %   options for post-processing:
0057 %     'analysis_functions': cell array of analysis function handles
0058 %     'analysis_options'  : cell array of option cell arrays {'option1',value1,...}
0059 %     'plot_functions'    : cell array of plot function handles
0060 %     'plot_options'      : cell array of option cell arrays {'option1',value1,...}
0061 %
0062 %   other options:
0063 %     'verbose_flag'  : whether to display informative messages/logs (default: 0)
0064 %     'modifications' : how to modify DynaSim specification structure component before simulation (see dsApplyModifications)
0065 %     'experiment'    : function handle of experiment function (see NOTE 2)
0066 %     'experiment_options' : single cell array of key/value options for experiment function
0067 %     'optimization'  : function handle of optimization function (see NOTE 2)
0068 %     'debug_flag'    : set to debug mode
0069 %     'benchmark_flag': set to benchmark mode. will add tic/toc to sims.
0070 %
0071 % Outputs:
0072 %   DynaSim data structure:
0073 %     data.labels           : list of state variables and monitors recorded
0074 %     data.(state_variables): state variable data matrix [time x cells]
0075 %     data.(monitors)       : monitor data matrix [time x cells]
0076 %     data.time             : time vector [time x 1]
0077 %     data.simulator_options: simulator options used to generate simulated data
0078 %     data.model            : model used to generate simulated data
0079 %     [data.varied]         : list of varied model components (present only if anything was varied)
0080 %
0081 %   DynaSim studyinfo structure (only showing select fields, see dsCheckStudyinfo for more details)
0082 %     studyinfo.study_dir
0083 %     studyinfo.base_model (=[]): original model from which a set of simulations was derived
0084 %     studyinfo.base_simulator_options (=[])
0085 %     studyinfo.base_solve_file (='')
0086 %     studyinfo.simulations(k): metadata for each simulation in a set of simulations
0087 %                           .sim_id         : unique identifier in study
0088 %                           .modifications  : modifications made to the base model during this simulation
0089 %                           .data_file      : full filename of eventual output file
0090 %                           .batch_dir (=[]): directory where batch jobs were saved (if cluster_flag=1)
0091 %                           .job_file (=[]) : m-file batch job that runs this simulation (if cluster_flag=1)
0092 %                           .simulator_options: simulator options for this simulation
0093 %                           .solve_file     : full filename of m- or mex-file that numerically integrated the model
0094 %
0095 % NOTE 1: 'vary' indicates the variable to vary, the values
0096 % it should take, and the object whose variable should be varied.
0097 % Syntax: vary={object, variable, values; ...}. For instance, to vary
0098 % parameter 'gNa', taking on values 100 and 120, in population 'E', set
0099 % vary={'E','gNa',[100 120]}. To additionally vary 'gSYN' in the connection
0100 % mechanism from 'E' to 'I', set vary={'E','gNa',[100 120];'E->I','gSYN',[0 1]}.
0101 % Mechanism lists and equations can also be varied. (see dsVary2Modifications
0102 % for more details and examples).
0103 %
0104 % EXAMPLES:
0105 % Example 1: Lorenz equations with phase plot
0106 %   eqns={
0107 %     's=10; r=27; b=2.666';
0108 %     'dx/dt=s*(y-x)';
0109 %     'dy/dt=r*x-y-x*z';
0110 %     'dz/dt=-b*z+x*y';
0111 %   };
0112 %   data=dsSimulate(eqns,'tspan',[0 100],'ic',[1 2 .5]);
0113 %   plot(data.pop1_x,data.pop1_z); title('Lorenz equations'); xlabel('x'); ylabel('z')
0114 %
0115 % Example 2: Leaky integrate-and-fire with spike monitor
0116 %   eqns={
0117 %     'tau=10; R=10; E=-70; I=1.55; thresh=-55; reset=-75';
0118 %     'dV/dt=(E-V+R*I)/tau; if(V>thresh)(V=reset)';
0119 %     'monitor V.spikes(thresh)';
0120 %   };
0121 %   data=dsSimulate(eqns,'tspan',[0 200],'ic',-75);
0122 %   data.pop1_V(data.pop1_V_spikes==1)=20; % insert spike
0123 %   plot(data.time,data.pop1_V); xlabel('time (ms)'); ylabel('V'); title('LIF with spikes')
0124 %
0125 % Example 3: Hodgkin-Huxley-type Intrinsically Bursting neuron
0126 %   eqns='dv/dt=5+@current; {iNaF,iKDR,iM}; gNaF=100; gKDR=5; gM=1.5; v(0)=-70';
0127 %   data=dsSimulate(eqns,'tspan',[0 200]);
0128 %   figure; plot(data.time,data.(data.labels{1}))
0129 %   xlabel('time (ms)'); ylabel('membrane potential (mV)'); title('Intrinsically Bursting neuron')
0130 %
0131 % Example 4: varying max Na+ conductance in Hodgkin-Huxley neuron
0132 %   eqns='dv/dt=@current+10; {iNa,iK}; v(0)=-60';
0133 %   data=dsSimulate(eqns,'vary',{'','gNa',[50 100 200]});
0134 %   % plot how mean firing rate varies with parameter
0135 %   dsPlotFR(data,'bin_size',30,'bin_shift',10); % bin_size and bin_shift in [ms]
0136 %
0137 % Example 5: simulate predefined Hodgkin-Huxley neuron with Iapp=10
0138 %   data = dsSimulate('HH','vary',{'HH','Iapp',10});
0139 %   figure; plot(data.time,data.HH_V);
0140 %
0141 % Example 6: Sparse Pyramidal-Interneuron-Network-Gamma rhythm with rastergram
0142 %   % define equations of cell model (same for E and I populations)
0143 %   eqns={
0144 %     'dv/dt=Iapp+@current/Cm+noise*randn(1,N_pop)*sqrt(dt)/dt';
0145 %     'monitor v.spikes, iGABAa.functions, iAMPA.functions'
0146 %   };
0147 %   % define specification for two-population network model
0148 %   s=[];
0149 %   s.populations(1).name='E';
0150 %   s.populations(1).size=80;
0151 %   s.populations(1).equations=eqns;
0152 %   s.populations(1).mechanism_list={'iNa','iK'};
0153 %   s.populations(1).parameters={'Iapp',5,'gNa',120,'gK',36,'Cm',1,'noise',4};
0154 %   s.populations(2).name='I';
0155 %   s.populations(2).size=20;
0156 %   s.populations(2).equations=eqns;
0157 %   s.populations(2).mechanism_list={'iNa','iK'};
0158 %   s.populations(2).parameters={'Iapp',0,'gNa',120,'gK',36,'Cm',1,'noise',4};
0159 %   s.connections(1).source='I';
0160 %   s.connections(1).target='E';
0161 %   s.connections(1).mechanism_list={'iGABAa'};
0162 %   s.connections(1).parameters={'tauD',10,'gSYN',.1,'netcon','ones(N_pre,N_post)'};
0163 %   s.connections(2).source='E';
0164 %   s.connections(2).target='I';
0165 %   s.connections(2).mechanism_list={'iAMPA'};
0166 %   s.connections(2).parameters={'tauD',2,'gSYN',.1,'netcon',ones(80,20)};
0167 %   % simulate model
0168 %   data=dsSimulate(s);
0169 %   % plot voltages and rastergram
0170 %   figure;
0171 %   subplot(2,1,1); % voltage traces
0172 %   plot(data.time,data.E_v,'b-',data.time,data.I_v,'r-')
0173 %   title('Sparse Pyramidal-Interneuron-Network-Gamma (sPING)'); ylabel('membrane potential (mV)');
0174 %   subplot(2,1,2); % rastergram
0175 %   E_spikes=nan(size(data.E_v_spikes)); E_spikes(data.E_v_spikes==1)=1;
0176 %   I_spikes=nan(size(data.I_v_spikes)); I_spikes(data.I_v_spikes==1)=1;
0177 %   plot(data.time,E_spikes+repmat(1:80,[length(data.time) 1]),'bo'); hold on
0178 %   plot(data.time,I_spikes+repmat(80+(1:20),[length(data.time) 1]),'ro'); axis([0 100 0 100]);
0179 %   title('rastergram'); xlabel('time (ms)'); ylabel('cell index');
0180 %   % simulate model varying two parameters (Iapp and tauD in sPING)
0181 %   % warning: this may take up to a minute to complete:
0182 %   vary={
0183 %     'E'   ,'Iapp',[0 10 20];     % amplitude of tonic input to E-cells
0184 %     'I->E','tauD',[5 10 15]      % inhibition decay time constant from I to E
0185 %     };
0186 %   data=dsSimulate(s,'vary',vary);
0187 %   % plot firing rates calculated from spike monitor in both populations
0188 %   dsPlotFR(data,'variable','*_spikes','bin_size',30,'bin_shift',10);
0189 %
0190 %
0191 % See also: dsGenerateModel, dsCheckModel, dsGetSolveFile, dsCheckData,
0192 %           dsVary2Modifications, dsCheckStudyinfo, dsCreateBatch
0193 %
0194 % Author: Jason Sherfey, PhD <jssherfey@gmail.com>
0195 % Copyright (C) 2016 Jason Sherfey, Boston University, USA
0196 
0197 % TODO: rename 'disk_flag' to something more descriptive
0198 
0199 % dependencies: dsWriteDynaSimSolver, dsWriteMatlabSolver, dsPropagateFunctions, dsCheckModel,
0200 % dsCheckOptions, dsOptions2Keyval, displayError, DynaSim2Odefun
0201 
0202 % <-- temporarily removed from help section -->
0203 % NOTE 2: special functions that recursively call dsSimulate:
0204 % - "Experiments" are ways of hacking the ODE system to incorporate additional
0205 % models (e.g., controlled inputs) and use them to simulate experimental
0206 % protocols by systematically varying Model Components across simulations in
0207 % prescribed ways. Technically, an Experiment could be any function that takes
0208 % a DynaSim model structure as its first input followed by key/value options.
0209 % Ideally, Experiments represent standardized procedural methods (experimental
0210 % protocols) for studying the modeled system. Experiment functions typically
0211 % involve applying a set of Modifications to a Base Model and varying the
0212 % modified model in prescribed ways.
0213 % - during "Optimization", each iteration involves a single Study producing
0214 % modified models, their simulated data sets and analysis results (e.g.,
0215 % cost functions) that shape the Base Model for a subsequent iteration and
0216 % its Study. Hence, a closed-loop optimization protocol produces a set of
0217 % evolving Studies. Technically, an Optimization could be any function
0218 % that takes a DynaSim model structure as its first input followed by
0219 % key/value options. Optimization functions will typically involve
0220 % while-looping through multiple Studies and analyzing data sets to update
0221 % Model Components on each iteration until some stop condition is reached.
0222 
0223 %% 0.0 Outputs & inputs.
0224 % Initialize outputs
0225 data=[];
0226 studyinfo=[];
0227 
0228 % check path
0229 dynasim_path=fileparts(which(mfilename));
0230 onPath=~isempty(strfind(path,[dynasim_path, pathsep]));
0231 if ~onPath
0232   if 1
0233     fprintf('adding dynasim and sub-directory to Matlab path: %s\n',dynasim_path);
0234   end
0235   addpath(genpath(dynasim_path)); % necessary b/c of changing directory for simulation
0236 end
0237 
0238 % Check inputs
0239 varargin = backward_compatibility(varargin);
0240 options=dsCheckOptions(varargin,{...
0241   'tspan',[0 100],[],...          % [beg,end] (units must be consistent with dt and equations)
0242   'ic',[],[],...                  % initial conditions (overrides definition in model structure; can input as IC structure or numeric array)
0243   'solver','rk4',{'euler','rk1','rk2','rk4','modified_euler','rungekutta','rk',...
0244   'ode23','ode45','ode113','ode15s','ode23s','ode23t','ode23tb'},... % DynaSim and built-in Matlab solvers
0245   'matlab_solver_options',[],[],... % options from odeset for use with built-in Matlab solvers
0246   'dt',.01,[],...                 % time step used for fixed step DynaSim solvers
0247   'downsample_factor',1,[],...    % downsampling applied during simulation (only every downsample_factor-time point is stored in memory or written to disk)
0248   'reduce_function_calls_flag',1,{0,1},...   % whether to eliminate internal (anonymous) function calls
0249   'save_parameters_flag',1,{0,1},...
0250   'random_seed','shuffle',[],...        % seed for random number generator (usage: rng(random_seed))
0251   'data_file','data.csv',[],... % name of data file if disk_flag=1
0252   'precision','single',{'single','double'},...
0253   'logfid',1,[],...
0254   'store_model_flag',1,{0,1},...  % whether to store model structure with data
0255   'verbose_flag',0,{0,1},...
0256   'modifications',[],[],...       % *DynaSim modifications structure
0257   'vary',[],[],...                % specification of things to vary or custom modifications_set
0258   'experiment',[],[],...          % experiment function. func(model,args)
0259   'experiment_options',[],[],...
0260   'optimization',[],[],...
0261   'cluster_flag',0,{0,1},...      % whether to run simulations on a cluster
0262   'sims_per_job',1,[],... % how many sims to run per batch job
0263   'memory_limit','8G',[],... % how much memory to allocate per batch job
0264   'qsub_mode','loop',{'loop','array'},... % whether to submit jobs as an array using qsub -t or in a for loop
0265   'one_solve_file_flag',0,{0,1},... % use only 1 solve file of each type, but can't vary mechs yet
0266   'parallel_flag',0,{0,1},...     % whether to run simulations in parallel (using parfor)
0267   'num_cores',4,[],... % # cores for parallel processing (SCC supports 1-12)
0268   'compile_flag',0,{0,1},... % exist('codegen')==6, whether to compile using coder instead of interpreting Matlab
0269   'sparse_flag',0,{0,1},... % whether to sparsify fixed variables before simulation
0270   'disk_flag',0,{0,1},...            % whether to write to disk during simulation instead of storing in memory
0271   'matCompatibility_flag',1,{0,1},...  % whether to save mat files in compatible mode, vs to prioritize > 2GB VARs
0272   'save_data_flag',0,{0,1},...  % whether to save simulated data
0273   'save_results_flag',0,{0,1},...  % whether to save results from simulated data
0274   'project_dir',pwd,[],...
0275   'study_dir',[],[],... % study directory
0276   'prefix','study',[],... % prefix prepended to all output files
0277   'overwrite_flag',0,{0,1},... % whether to overwrite existing data
0278   'solve_file',[],[],... % m- or mex-file solving the system
0279   'sim_id',[],[],... % sim id in an existing study
0280   'studyinfo',[],[],...
0281   'email',[],[],... % email to send notification upon study completion
0282   'analysis_functions',[],[],...
0283   'analysis_options',[],[],...
0284   'plot_functions',[],[],...
0285   'plot_options',[],[],...
0286   'optimize_big_vary',0,{0,1},...
0287   'mex_dir_flag',1,{0,1},... % Flag to tell whether or not to search in mex_dir for pre-compiled solve files (solve*_mex*).
0288   'mex_dir',[],[],... % Directory to search for pre-compiled mex files. Can be relative to 'study_dir' or absolute path.
0289   'in_parfor_loop_flag',0,{0,1},... % if inside parfor loop
0290   'debug_flag',0,{0,1},...
0291   'auto_gen_test_data_flag',0,{0,1},...
0292   'unit_test_flag',0,{0,1},...
0293   'benchmark_flag',0,{0,1},...
0294   },false);
0295 % more options: remove_solve_dir, remove_batch_dir, post_downsample_factor
0296 
0297 %% 0.1 Auto_gen_test_data_flag save argin (arguments passed to dsSimulate).
0298 if options.auto_gen_test_data_flag
0299   varargs = varargin;
0300   varargs{find(strcmp(varargs, 'auto_gen_test_data_flag'))+1} = 0;
0301   varargs(end+1:end+2) = {'unit_test_flag',1};
0302   argin = [{model}, varargs]; % specific to this function
0303 end
0304 
0305 %% 0.2 Unit testing.
0306 if options.unit_test_flag
0307   options.study_dir = getAbsolutePath(options.study_dir);
0308 end
0309 
0310 %% 0.3 Prepare solve options.
0311 
0312 if options.compile_flag && ~strcmp(reportUI,'matlab')
0313   fprintf('Setting ''compile_flag'' to 0 in Octave.\n')
0314   options.compile_flag = 0;
0315 end
0316 
0317 if isempty(options.mex_dir)
0318     options.mex_dir = dsGetConfig('mex_path');
0319 end
0320 
0321 if options.compile_flag && options.sparse_flag
0322   error('The Matlab Coder toolbox does not support sparse matrices. Choose either ''compile_flag'' or ''sparse_flag''.');
0323 end
0324 
0325 if options.parallel_flag && (strcmp(reportUI,'matlab') && feature('numCores') == 1 || ~strcmp(reportUI,'matlab') && nproc() == 1)
0326   fprintf('Setting ''parallel_flag'' to 0 since only 1 core detected on this machine.\n')
0327   options.parallel_flag = 0;
0328 end
0329 
0330 if options.compile_flag && ~options.reduce_function_calls_flag
0331   fprintf('Setting ''reduce_function_calls_flag'' to 1 for compatibility with ''compile_flag=1'' (coder does not support anonymous functions).\n');
0332   options.reduce_function_calls_flag=1;
0333 end
0334 
0335 % Make sure that data is either saved to disk, saved to variable, or plotted
0336 if (nargout==0 || options.cluster_flag) && ~options.save_data_flag && ~options.save_results_flag && isempty(options.plot_functions)
0337   if ~isempty(options.analysis_functions)
0338     fprintf('Setting ''save_results_flag'' to 1 since output from dsSimulate is not stored in a variable and analysis functions specified.\n')
0339     options.save_results_flag = 1;
0340   else
0341     fprintf('Setting ''save_data_flag'' to 1 since output from dsSimulate is not stored in a variable and no plot or analysis functions specified.\n')
0342     options.save_data_flag = 1;
0343   end
0344 end
0345 
0346 if options.cluster_flag && ~options.save_data_flag
0347   options.save_results_flag=1;
0348   if options.verbose_flag
0349     fprintf('Setting ''save_results_flag'' to 1 for storing results of batch jobs for later access.\n');
0350   end
0351 end
0352 
0353 if any(strcmp(options.solver, {'ode23','ode45','ode113','ode15s','ode23s','ode23t','ode23tb'}))
0354   matlabSolverBool = 1;
0355 else
0356   matlabSolverBool = 0;
0357 end
0358 
0359 if options.disk_flag && matlabSolverBool
0360   fprintf('Since using built-in solver, setting options.disk_flag=1.\n');
0361   options.disk_flag = 1;
0362 end
0363 
0364 % if ischar(options.study_dir) && options.save_data_flag==0
0365 %   options.save_data_flag=1;
0366 %   if options.verbose_flag
0367 %     fprintf('Setting ''save_data_flag'' to 1 for storing results in study_dir: %s.\n',options.study_dir);
0368 %   end
0369 % end
0370 
0371 % Convert matlab solver options from key/value to struct using odeset if
0372 % necessary.
0373 if iscell(options.matlab_solver_options)
0374   options.matlab_solver_options = odeset(options.matlab_solver_options{:});
0375 end
0376 
0377 %% 0.4 Non-Batch checks.
0378 if isempty(options.sim_id) % not in part of a batch sim
0379   if options.optimize_big_vary
0380     options.cluster_flag = 1;
0381     options.qsub_mode = 'array';
0382     options.compile_flag = 1;
0383     options.downsample_factor = max(1/options.dt, options.downsample_factor); % at most 1000Hz sampling
0384     options.one_solve_file_flag = 1;
0385     options.sims_per_job = 2;
0386   end
0387 
0388   % check for one_solve_file_flag
0389   if options.one_solve_file_flag && ~options.cluster_flag
0390     % One file flag only for cluster
0391     fprintf('Since cluster_flag==0, setting options.one_solve_file_flag=0\n')
0392     options.one_solve_file_flag = 0;
0393     % TODO: this is a temp setting until iss_90 is fully implemented
0394   end
0395 
0396   if options.one_solve_file_flag && ~options.overwrite_flag
0397     % One file flag will overwrite
0398     fprintf('Since one_solve_file_flag==1, setting options.overwrite_flag=1\n')
0399     options.overwrite_flag = 1;
0400     % TODO: this is a temp setting until iss_90 is fully implemented
0401   end
0402 
0403   if options.one_solve_file_flag && ~strcmp(options.qsub_mode, 'array')
0404     % One file flag needs array mode
0405     fprintf('Since one_solve_file_flag==1, setting options.qsub_mode=''array''\n')
0406     options.qsub_mode = 'array';
0407     % TODO: this is a temp setting until iss_90 is fully implemented
0408   end
0409 
0410   if options.one_solve_file_flag && options.parallel_flag
0411     % One file flag can't do parallel_flag
0412     fprintf('Since one_solve_file_flag==1, setting options.parallel_flag=0\n')
0413     options.parallel_flag = 0;
0414     % TODO: this is a temp setting until iss_90 is fully implemented
0415   end
0416 
0417   if options.one_solve_file_flag && isa(options.experiment,'function_handle')
0418     error('one_solve_file_flag doesn''t work with experiments.')
0419   end
0420 
0421   if options.one_solve_file_flag && ~options.save_parameters_flag
0422     fprintf('Since one_solve_file_flag==1, setting options.save_parameters_flag=1\n')
0423     options.save_parameters_flag = 1;
0424     % TODO: this is a temp setting until iss_90 is fully implemented
0425   end
0426 end % isempty(options.sim_id)
0427 
0428 %% 0.5 Prepare analysis functions and options.
0429 if ~isempty(options.analysis_functions)
0430   if ~iscell(options.analysis_functions)
0431     % convert function handle into cell array of function handles
0432     options.analysis_functions={options.analysis_functions};
0433   end
0434 
0435   if any(~cellfun(@(x)isa(x,'function_handle'),options.analysis_functions))
0436     error('at least one analysis function was not provided as a function handle.');
0437   end
0438 
0439   if isempty(options.analysis_options)
0440     % convert to empty option cell array
0441     options.analysis_options={};
0442   end
0443 
0444   if ~iscell(options.analysis_options)
0445     error('''analysis_options'' must be a cell array of options or option cell arrays');
0446   end
0447 
0448   % force to be a cell array of option cell arrays
0449   if isempty(options.analysis_options) || ischar(options.analysis_options{1}) % first element is an option
0450     options.analysis_options={options.analysis_options};
0451   end
0452 
0453   % make sure there is one option cell array per analysis function
0454   if length(options.analysis_options)==1 && length(options.analysis_functions)>1
0455     % copy options for each analysis function
0456     options.analysis_options=repmat(options.analysis_options,[1 length(options.analysis_functions)]);
0457   elseif length(options.analysis_options) ~= length(options.analysis_functions)
0458     error('there must be one option cell array per analysis function.');
0459   end
0460 
0461 end
0462 
0463 %% 0.6 Prepare plot functions and options.
0464 if ~isempty(options.plot_functions)
0465   if ~iscell(options.plot_functions)
0466     % convert function handle into cell array of function handles
0467     options.plot_functions={options.plot_functions};
0468   end
0469 
0470   if any(~cellfun(@(x)isa(x,'function_handle'),options.plot_functions))
0471     error('at least one plot function was not provided as a function handle.');
0472   end
0473 
0474   if isempty(options.plot_options)
0475     % convert to empty option cell array
0476     options.plot_options={};
0477   end
0478 
0479   if ~iscell(options.plot_options)
0480     error('''plot_options'' must be a cell array of options or option cell arrays');
0481   end
0482 
0483   % force to be a cell array of option cell arrays
0484   if isempty(options.plot_options) || ischar(options.plot_options{1}) % first element is an option
0485     options.plot_options={options.plot_options};
0486   end
0487 
0488   % make sure there is one option cell array per plot function
0489   if length(options.plot_options)==1 && length(options.plot_functions)>1
0490     % copy options for each plot function
0491     options.plot_options=repmat(options.plot_options,[1 length(options.plot_functions)]);
0492   elseif length(options.plot_options) ~= length(options.plot_functions)
0493     error('there must be one option cell array per plot function.');
0494   end
0495 end
0496 
0497 %% 1.0 Prepare model and study structures for simulation.
0498 
0499 % Handle special case of input equations with vary() statement.
0500 [model,options] = extract_vary_statement(model,options);
0501 
0502 % Check/standardize model.
0503 model = dsCheckModel(model, varargin{:}); % handles conversion when input is a string w/ equations or a DynaSim specification structure
0504 
0505 %% 1.1 Apply modifications before simulation and optional further variation across simulations.
0506 if ~isempty(options.modifications)
0507   [model,options.modifications] = dsApplyModifications(model,options.modifications, varargin{:});
0508 end
0509 
0510 %% 1.2 Incorporate user-supplied initial conditions.
0511 if ~isempty(options.ic)
0512   if isstruct(options.ic)
0513     % TODO: create subfunc that converts numeric ICs to strings for
0514     % consistency (call here and in ProcessNumericICs <-- use code already there)
0515     % user provided structure with ICs
0516     warning('off','catstruct:DuplicatesFound');
0517     model.ICs=catstruct(model.ICs,options.ic);
0518   elseif isnumeric(options.ic)
0519     % user provided numeric array with one value per state variable per
0520     % cell with state variables ordered according to model.state_variables.
0521     model.ICs=ProcessNumericICs;
0522   end
0523 end
0524 
0525 % expand set of things to vary across simulations
0526 if ~isempty(options.vary)
0527   modifications_set=dsVary2Modifications(options.vary,model);       % Note: options.vary can also be itself a modifications set.
0528 else
0529   modifications_set={[]};
0530 end
0531 
0532 % check for one_solve_file_flag
0533 if options.one_solve_file_flag && is_varied_mech_list()
0534   % Can't vary mechs if using 1 file mode
0535   error('Can''t vary mechanism_list if using one_solve_file_flag')
0536 
0537   % TODO: this is a temp setting until iss_90 is fully implemented
0538 end
0539 
0540 %% 1.3 Handle parallel simulations.
0541 %% 1.3.1 Manage cluster computing.
0542 % whether to write jobs for distributed processing on cluster
0543 if options.cluster_flag
0544   % add to model any parameters in 'vary' not explicit in current model
0545   %   approach: use dsApplyModifications(), it does that automatically
0546   for i = 1:length(modifications_set)
0547     if ~isempty(modifications_set{i}) && ~strcmp(modifications_set{i}{2},'mechanism_list') && ~strcmp(modifications_set{i}{2},'equations')
0548       model = dsApplyModifications(model,modifications_set{i}, varargin{:});
0549       break
0550     end
0551   end
0552   keyvals = dsOptions2Keyval(options);
0553   studyinfo = dsCreateBatch(model,modifications_set,'simulator_options',options,'process_id',options.sim_id,keyvals{:});
0554 
0555   if options.one_solve_file_flag
0556     % copy params.mat from project_dir to batchdirs
0557     param_file_path = fullfile(options.project_dir, options.study_dir, 'solve','params.mat');
0558     [~,home]=system('echo $HOME');
0559     batch_dir = fullfile(strtrim(home),'batchdirs',options.study_dir);
0560     batch_param_file_path = fullfile(batch_dir,'params.mat');
0561     [success,msg]=copyfile(param_file_path, batch_param_file_path);
0562   end
0563 
0564   %if options.overwrite_flag==0
0565   % check status of study
0566   %     [~,s]=dsMonitorStudy(studyinfo.study_dir,'verbose_flag',0,'process_id',options.sim_id);
0567   %     if s==1 % study finished
0568   %       if options.verbose_flag
0569   %         fprintf('Study already finished. Importing data...\n');
0570   %       end
0571   %       studyinfo=dsCheckStudyinfo(studyinfo.study_dir,'process_id',options.sim_id);
0572   %       data=dsImport(studyinfo,'process_id',options.sim_id);
0573   %     end
0574   %end
0575 
0576   %% auto_gen_test_data_flag argout
0577   if options.auto_gen_test_data_flag
0578     if isfield(data, 'simulator_options')
0579       data= rmfield(data, 'simulator_options'); % specific to this function
0580     end
0581     if ~isempty(studyinfo)
0582       studyinfo = []; % specific to this function
0583     end
0584 
0585     argout = {data, studyinfo}; % specific to this function
0586 
0587     % file output dir
0588     %   if ~isempty(studyinfo) && ~isempty(studyinfo.study_dir)
0589     %     dirOut = studyinfo.study_dir;
0590     %   else
0591     dirOut = options.study_dir;
0592     %   end
0593 
0594     removeStudyinfo(); % remove studyinfo file
0595     renameMexFilesForUnitTesting(); % rename mex files for unit testing
0596 
0597     dsUnitSaveAutoGenTestDir(argin, argout, [], dirOut);
0598   end
0599 
0600   %% unit test
0601   if options.unit_test_flag
0602     % remove fields that cause issues in unit testing
0603     if isfield(data, 'simulator_options')
0604       data= rmfield(data, 'simulator_options'); % specific to this function
0605     end
0606     if ~isempty(studyinfo)
0607       studyinfo = [];
0608     end
0609 
0610     removeStudyinfo(); % remove studyinfo file
0611     renameMexFilesForUnitTesting(); % rename mex files for unit testing
0612   end
0613 
0614   return;
0615 end
0616 
0617 %% 1.3.2 Manage parallel computing on local machine.
0618 % TODO: debug local parallel sims, doesn't seem to be working right...
0619 % (however SCC cluster+parallel works)
0620 if options.parallel_flag
0621   % prepare studyinfo
0622   [studyinfo,options]=dsSetupStudy(model,'simulator_options',options,'modifications_set',modifications_set);
0623 
0624   if options.compile_flag
0625     % Ensure mex_dir is absolute
0626     if (ispc && options.mex_dir(2) == ':') || (~ispc && options.mex_dir(1) == filesep)
0627       relMexPath = false;
0628     else
0629       relMexPath = true;
0630     end
0631 
0632     if relMexPath % then make absolute by prepending study_dir
0633       options.mex_dir = fullfile(getAbsolutePath(studyinfo.study_dir), options.mex_dir);
0634     end
0635 
0636     % Create mex_dir if it does not yet exist
0637     if ~exist(options.mex_dir,'dir') && ~options.cluster_flag && options.mex_dir_flag
0638       mkdir(options.mex_dir);
0639     end
0640   end
0641 
0642   % prepare options
0643   options_temp = rmfield(options,{'vary','modifications','solve_file','parallel_flag','studyinfo','in_parfor_loop_flag'});
0644   keyvals=dsOptions2Keyval(options_temp);
0645 
0646   keyvals{find(strcmp(keyvals, 'auto_gen_test_data_flag'))+1} = 0;
0647   if options.auto_gen_test_data_flag || options.unit_test_flag
0648     keyvals{find(strcmp(keyvals, 'unit_test_flag'))+1} = 1; % prevents time-stamped outputs
0649   end
0650 
0651   % run embarrassingly-parallel simulations
0652 
0653   % Previous parallel code would overwrite the same params.mat file on each
0654   % parallel iteration, resulting in the same parameters being used for all
0655   % simulations.
0656 
0657   %   % List any core files - these should be deleted, as they are huge (debug)
0658   %   system (['ls ' fullfile(options.study_dir,'output*')],'-echo');
0659   %   system('find * -name "core*"','-echo');
0660 
0661   clear data
0662 
0663   % note that parfor currently acts just as a regular for in Octave
0664   parfor sim=1:length(modifications_set)
0665     data(sim)=dsSimulate(model, 'modifications', modifications_set{sim}, keyvals{:},...
0666         'studyinfo', studyinfo, 'sim_id',sim, 'in_parfor_loop_flag', 1);  % My modification; now specifies a separate study directory for each sim.
0667     %disp(sim);
0668   end
0669 
0670   % Clean up files leftover from sim
0671   % Unfortunately we can't remove the folders due to locked .nfs files.
0672   % Need to do this manually later...
0673 
0674   if strcmp(reportUI,'matlab') % parfor currently acts just as a regular for in Octave
0675     % Delete any core files in parent directory
0676     delete(fullfile(options.study_dir,'core*'));
0677 
0678     % Verify all core files are deleted
0679     [~,result] = system('find * -name "core*"','-echo');
0680     if ~isempty(result); fprintf(strcat(result,'\n')); warning('Core files found. Consider deleting to free up space'); end
0681   end
0682 
0683   % TODO: sort data sets by things varied in modifications_set
0684   % TODO: Figure out how to delete locked .nfs files
0685 
0686   if options.verbose_flag
0687     fprintf('\nParallel simulations complete.\n\n')
0688   end
0689 
0690   %% auto_gen_test_data_flag argout
0691   if options.auto_gen_test_data_flag
0692     if isfield(data, 'simulator_options')
0693       data= rmfield(data, 'simulator_options'); % specific to this function
0694     end
0695     if ~isempty(studyinfo)
0696       studyinfo = []; % specific to this function
0697     end
0698 
0699     argout = {data, studyinfo}; % specific to this function
0700 
0701     % file output dir
0702     %   if ~isempty(studyinfo) && ~isempty(studyinfo.study_dir)
0703     %     dirOut = studyinfo.study_dir;
0704     %   else
0705     dirOut = options.study_dir;
0706     %   end
0707 
0708     removeStudyinfo(); % remove studyinfo file
0709     renameMexFilesForUnitTesting(); % rename mex files for unit testing
0710 
0711     dsUnitSaveAutoGenTestDir(argin, argout, [], dirOut);
0712   end
0713 
0714   %% unit test
0715   if options.unit_test_flag
0716     % remove fields that cause issues in unit testing
0717     if isfield(data, 'simulator_options')
0718       data= rmfield(data, 'simulator_options'); % specific to this function
0719     end
0720     if ~isempty(studyinfo)
0721       studyinfo = [];
0722     end
0723 
0724     removeStudyinfo(); % remove studyinfo file
0725     renameMexFilesForUnitTesting(); % rename mex files for unit testing
0726   end
0727 
0728   return
0729 end %parallel_flag
0730 
0731 %% 1.4 prepare study_dir and studyinfo if saving data
0732 if isempty(options.studyinfo)
0733   [studyinfo,options] = dsSetupStudy(model,'modifications_set',modifications_set,'simulator_options',options,'process_id',options.sim_id);
0734 else % in parfor loop and/or cluster job
0735   studyinfo = options.studyinfo;
0736 end
0737 
0738 if options.compile_flag
0739   % Ensure mex_dir is absolute
0740   if (ispc && options.mex_dir(2) == ':') || (~ispc && options.mex_dir(1) == filesep)
0741     relMexPath = false;
0742   else
0743     relMexPath = true;
0744   end
0745 
0746   if relMexPath % then make absolute by prepending study_dir
0747     if ~isempty(studyinfo) && ~isempty(studyinfo.study_dir)
0748       options.mex_dir = fullfile(getAbsolutePath(studyinfo.study_dir), options.mex_dir);
0749     else
0750 
0751       options.mex_dir = fullfile(getAbsolutePath(options.study_dir), options.mex_dir);
0752     end
0753   end
0754 
0755   % Create mex_dir if it does not yet exist
0756   if ~exist(options.mex_dir,'dir') && ~options.cluster_flag && options.mex_dir_flag
0757     mkdir(options.mex_dir);
0758   end
0759 end
0760 
0761 % put solver blocks in try statement to catch and handle errors/cleanup
0762 cwd=pwd; % record current directory
0763 
0764 data_index=0;
0765 tmpdata = [];
0766 
0767 if ~options.debug_flag
0768   try
0769     tryFn(nargout)
0770   catch err % error handling
0771     if options.compile_flag && ~isempty(options.solve_file) && ~options.one_solve_file_flag
0772 
0773       if options.verbose_flag
0774         fprintf('Removing failed compiled solve file: %s\n',options.solve_file);
0775       end
0776 
0777       delete([options.solve_file '*']);
0778     end
0779 
0780     displayError(err);
0781 
0782     % update studyinfo
0783     if options.save_data_flag && ~options.one_solve_file_flag
0784       studyinfo=dsUpdateStudy(studyinfo.study_dir,'process_id',sim_id,'status','failed','verbose_flag',options.verbose_flag);
0785       data=studyinfo;
0786     end
0787     cleanup('error');
0788 
0789     rethrow(err)
0790   end
0791 else
0792   tryFn(nargout) % outside of try block if options.debug_flag
0793 end
0794 
0795 if options.verbose_flag
0796   fprintf('\nSimulations complete.\n\n')
0797 end
0798 
0799 if ~options.in_parfor_loop_flag % if not inside of parfor loop
0800   %% auto_gen_test_data_flag argout
0801   if options.auto_gen_test_data_flag
0802     if isfield(data, 'simulator_options')
0803       data = rmfield(data, 'simulator_options'); % specific to this function
0804     end
0805     if ~isempty(studyinfo)
0806       studyinfo = []; % specific to this function
0807     end
0808 
0809     argout = {data, studyinfo}; % specific to this function
0810 
0811     % file output dir
0812     %   if ~isempty(studyinfo) && ~isempty(studyinfo.study_dir)
0813     %     dirOut = studyinfo.study_dir;
0814     %   else
0815     dirOut = options.study_dir;
0816     %   end
0817 
0818     removeStudyinfo(); % remove studyinfo file
0819     renameMexFilesForUnitTesting(); % rename mex files for unit testing
0820 
0821     dsUnitSaveAutoGenTestDir(argin, argout, [], dirOut);
0822   end
0823 
0824   %% unit test
0825   if options.unit_test_flag
0826     % remove fields that cause issues in unit testing
0827     if isfield(data, 'simulator_options')
0828       data= rmfield(data, 'simulator_options'); % specific to this function
0829     end
0830     if ~isempty(studyinfo)
0831       studyinfo = [];
0832     end
0833 
0834     removeStudyinfo(); % remove studyinfo file
0835     renameMexFilesForUnitTesting(); % rename mex files for unit testing
0836   end
0837 end % in_parfor_loop_flag
0838 
0839 % ---------------------------------------------
0840 % TODO:
0841 % - create helper function that handles log files (creation, standardized format,...)
0842 % ---------------------------------------------
0843 
0844 
0845 
0846 %% -------------------------
0847 % NESTED FUNCTIONS
0848 % -------------------------
0849   function tryFn(nargoutmain)
0850     % functions:             outputs:
0851     % dsWriteDynaSimSolver  m-file for DynaSim solver
0852     % dsWriteMatlabSolver   m-file for Matlab solver (including @odefun)
0853     % dsPrepareMEX          mex-file for m-file
0854 
0855     %% 1.5 loop over simulations, possibly varying things
0856     base_model=model;
0857 
0858     for sim=1:length(modifications_set)
0859       if ~strcmp(pwd,cwd) % move back to original directory before potentially regenerating to make sure the model files used are the same
0860         cd(cwd);
0861       end
0862 
0863       % get index for this simulation
0864       if ~isempty(options.sim_id)
0865         sim_ind=find([studyinfo.simulations.sim_id]==options.sim_id);
0866         sim_id=options.sim_id;
0867       else
0868         sim_ind=sim;
0869         sim_id=sim;
0870       end
0871 
0872       if options.save_data_flag
0873         % check if output data already exists. load if so and skip simulation
0874         data_file=studyinfo.simulations(sim_ind).data_file;
0875         if exist(data_file,'file') && ~options.overwrite_flag
0876           if 1%options.verbose_flag
0877             % note: this is important, should always display
0878             fprintf('Loading data from %s\n',data_file);
0879           end
0880           tmpdata=dsImport(data_file,'process_id',sim_id);
0881           update_data; % concatenate data structures across simulations
0882           continue; % skip to next simulation
0883         end
0884       end
0885 
0886       % apply modifications for this point in search space
0887       if ~isempty(modifications_set{sim})
0888         model=dsApplyModifications(base_model,modifications_set{sim}, varargin{:});
0889       end
0890 
0891       % update studyinfo
0892       if options.save_data_flag
0893         %studyinfo=dsUpdateStudy(studyinfo.study_dir,'process_id',sim_id,'status','started','model',model,'simulator_options',options,'verbose_flag',options.verbose_flag);
0894       end
0895 
0896       %% Experiment
0897       if isa(options.experiment,'function_handle')
0898         % EXPERIMENT (wrapping around a set of simulations)
0899         if options.cluster_flag && options.compile_flag
0900           warning('compiled solver is not available for experiments on the cluster. Simulation will be run in Matlab.');
0901         end
0902 
0903         % from varargin...
0904         % remove 'experiment', 'modifications', 'vary', 'cluster_flag' to avoid undesired recursive action in experiment function
0905         % remove 'save_data_flag' to prevent individual simulations from being saved during experiment
0906         keyvals=dsRemoveKeyval(varargin,{'experiment','cluster_flag','vary','modifications','save_data_flag'});
0907 
0908         if ~isempty(options.experiment_options)
0909           % user-supplied experiment options override any found in dsSimulate options
0910           keyvals=dsRemoveKeyval(keyvals,options.experiment_options(1:2:end));
0911           keyvals=cat(2,keyvals,options.experiment_options);
0912         end
0913 
0914         tmpdata=feval(options.experiment,model,keyvals{:});
0915       else
0916         %% NOT AN EXPERIMENT (single simulation)
0917         %% 2.0 prepare solver function (solve_ode.m/mex)
0918         % - Matlab solver: create @odefun with vectorized state variables
0919         % - DynaSim solver: write solve_ode.m and params.mat  (based on dnsimulator())
0920         % check if model solver needs to be created
0921         % (i.e., if is first simulation or a search space varying mechanism list)
0922 
0923         if sim==1 || ( ~isempty(modifications_set{1}) && is_varied_mech_list() )
0924           % prepare file that solves the model system
0925           if isempty(options.solve_file) || (~exist(options.solve_file,'file') &&...
0926               ~exist([options.solve_file '.mexa64'],'file') &&...
0927               ~exist([options.solve_file '.mexa32'],'file') &&...
0928               ~exist([options.solve_file '.mexmaci64'],'file'))
0929             options.solve_file = dsGetSolveFile(model,studyinfo,options); % store name of solver file in options struct
0930           end
0931 
0932           % TODO: consider providing better support for studies that produce different m-files per sim (e.g., varying mechanism_list)
0933 
0934           if options.verbose_flag
0935             fprintf('\nSIMULATING MODEL:\n');
0936             fprintf('Solving system using %s\n',options.solve_file);
0937           end
0938         else
0939           % use previous solve_file
0940         end
0941         [fpath,fname,fext]=fileparts(options.solve_file);
0942 
0943         %% 3.0 integrate model with solver of choice and prepare output data
0944         % - matlab solver: solve @odefun with feval and solver_options
0945         % - DynaSim solver: run solve_ode.m or create/run MEX
0946         % move to directory with solver file
0947 
0948         if options.verbose_flag
0949           fprintf('Changing directory to %s\n',fpath);
0950         end
0951 
0952         cd(fpath);
0953 
0954         % save parameters there
0955         warning('off','catstruct:DuplicatesFound');
0956         p = catstruct(dsCheckSolverOptions(options),model.parameters);
0957 
0958         if matlabSolverBool
0959           % add IC to p for use in matlab solver
0960           if isempty(options.ic)
0961             [~, p.ic]=dsDynasim2odefun(  dsPropagateParameters( dsPropagateFunctions(model, varargin{:}), varargin{:} ), 'odefun_output','func_body', varargin{:}  );
0962           else
0963             p.ic = options.ic;
0964           end
0965 
0966           % add matlab_solver_options to p
0967           if ~isempty(options.matlab_solver_options)
0968             p.matlab_solver_options = options.matlab_solver_options;
0969           end
0970         end
0971 
0972         param_file = fullfile(fpath,'params.mat');
0973         if options.verbose_flag
0974           fprintf('Saving model parameters: %s\n',param_file);
0975         end
0976         %pause(.01);
0977 
0978         %% Solve System
0979         if options.disk_flag  % ### data stored on disk during simulation ###
0980           sim_start_time=tic;
0981           if ~options.one_solve_file_flag
0982             save(param_file,'p','-v7'); % save params immediately before solving
0983           end
0984           csv_data_file=feval(fname);  % returns name of file storing the simulated data
0985           duration=toc(sim_start_time);
0986 
0987           if nargout>0 || options.save_data_flag
0988             tmpdata=dsImport(csv_data_file,'process_id',sim_id); % eg, data.csv
0989           end
0990         else                  % ### data stored in memory during simulation ###
0991           % create list of output variables to capture
0992           output_variables=cat(2,'time',model.state_variables);
0993 
0994           if ~isempty(model.monitors)
0995             output_variables=cat(2,output_variables,fieldnames(model.monitors)');
0996           end
0997 
0998           if ~isempty(model.fixed_variables)
0999             fields=fieldnames(model.fixed_variables)';
1000             output_variables=cat(2,output_variables,fields);
1001             num_fixed_variables=length(fields);
1002           else
1003             num_fixed_variables=0;
1004           end
1005 
1006           % run simulation
1007           if options.verbose_flag
1008             fprintf('\nRunning simulation %g/%g (solver=''%s'', dt=%g, tspan=[%g %g]) ...\n',sim,length(modifications_set),options.solver,options.dt,options.tspan);
1009           end
1010           sim_start_time=tic;
1011 
1012           outputs=cell(1,length(output_variables)); % preallocate for PCT compatibility
1013 
1014           if ~options.one_solve_file_flag
1015             save(param_file,'p','-v7'); % save params immediately before solving
1016           end
1017 
1018           % feval solve file
1019           if ~options.one_solve_file_flag
1020             [outputs{1:length(output_variables)}]=feval(fname);
1021           else
1022             % pass sim_id for slicing params
1023             [outputs{1:length(output_variables)}]=feval(fname, sim_id);
1024           end
1025 
1026           duration=toc(sim_start_time);
1027 
1028 
1029           % prepare DynaSim data structure
1030           % organize simulated data in data structure (move time to last)
1031           tmpdata.labels=output_variables([2:length(output_variables)-num_fixed_variables 1]);
1032           for i=1:length(output_variables)
1033             if ~isempty(model.fixed_variables) && isfield(model.fixed_variables,output_variables{i})
1034               % store fixed variables in model substructure
1035               model.fixed_variables.(output_variables{i})=outputs{i};
1036             else
1037               % store state variables and monitors as data fields
1038               tmpdata.(output_variables{i})=outputs{i};
1039             end
1040 
1041             outputs{i}=[]; % clear assigned outputs from memory
1042           end
1043         end
1044 
1045         if options.verbose_flag
1046           fprintf('Elapsed time: %g seconds.\n',duration);
1047         end
1048 
1049         % add metadata to tmpdata
1050         tmpdata.simulator_options=options; % store simulator controls
1051 
1052         if options.store_model_flag==1  % optionally store the simulated model
1053           tmpdata.model=model;
1054         end
1055       end
1056 
1057       tmpdata = dsModifications2Vary(tmpdata,options.modifications,options,modifications_set,sim);
1058 
1059       if (options.auto_gen_test_data_flag || options.unit_test_flag) && isfield(tmpdata, 'simulator_options')
1060         tmpdata= rmfield(tmpdata, 'simulator_options');
1061       end
1062 
1063       % save single data set and update studyinfo
1064       if options.save_data_flag
1065         dsExportData(tmpdata,'filename',data_file,'format','mat','matCompatibility_flag',options.matCompatibility_flag,'verbose_flag',options.verbose_flag);
1066         %studyinfo=dsUpdateStudy(studyinfo.study_dir,'process_id',sim_id,'status','finished','duration',duration,'solve_file',options.solve_file,'email',options.email,'verbose_flag',options.verbose_flag,'model',model,'simulator_options',options);
1067       end
1068 
1069       % do post-simulation analysis and plotting
1070       if ~isempty(options.analysis_functions) || ~isempty(options.plot_functions)
1071         if options.save_data_flag || options.save_results_flag
1072           % do analysis and plotting while saving results
1073           siminfo=studyinfo.simulations(sim_ind);
1074           for f=1:length(siminfo.result_functions)
1075             tmpresult=dsAnalyze(tmpdata,siminfo.result_functions{f},'result_file',siminfo.result_files{f},'save_data_flag',1,'save_results_flag',1,siminfo.result_options{f}{:},'parallel_flag',options.parallel_flag);
1076 
1077             % since the plots are saved, close all generated figures
1078             if all(ishandle(tmpresult))
1079               close(tmpresult);
1080             end
1081           end
1082         else
1083           % do analysis and plotting without saving results
1084           if ~isempty(options.analysis_functions) && nargoutmain > 2
1085             for f=1:length(options.analysis_functions)
1086               tmpresult=dsAnalyze(tmpdata,options.analysis_functions{f},'result_file',[],'save_data_flag',0,'save_results_flag',options.save_results_flag,options.analysis_options{f}{:},'parallel_flag',options.parallel_flag);
1087             end
1088           end
1089 
1090           if ~isempty(options.plot_functions)
1091             for f=1:length(options.plot_functions)
1092               dsAnalyze(tmpdata,options.plot_functions{f},'result_file',[],'save_data_flag',0,'save_results_flag',options.save_results_flag,options.plot_options{f}{:},'parallel_flag',options.parallel_flag);
1093             end
1094           end
1095         end
1096       end
1097 
1098       if nargoutmain>0
1099         update_data; % concatenate data structures across simulations
1100       end
1101 
1102       if nargoutmain>2
1103         update_result;
1104       end
1105     end % end loop over sims
1106 
1107     cleanup('success');
1108   end %tryfn
1109 
1110   function update_data
1111     % store tmpdata
1112     if sim==1
1113       % replicate first data set as preallocation for all
1114       data=repmat(tmpdata,[1 length(modifications_set)]);
1115       data_index=length(tmpdata);
1116     else
1117       inds=data_index+(1:length(tmpdata)); % support multiple data sets returned by experiments
1118       data(inds)=tmpdata;
1119       data_index=inds(end);
1120     end
1121   end
1122 
1123   function update_result
1124     % store tmpdata
1125     if sim==1
1126       % replicate first data set as preallocation for all
1127       result=repmat(tmpresult,[1 length(modifications_set)]);
1128       result_index=length(tmpresult);
1129     else
1130       inds=data_index+(1:length(tmpresult)); % support multiple data sets returned by experiments
1131       result(inds)=tmpresult;
1132       result_index=inds(end);
1133     end
1134   end
1135 
1136   function cleanup(status)
1137     % remove temporary files and optionally store info for debugging
1138     % ...
1139     % return to original directory
1140     if options.verbose_flag
1141       fprintf('Changing directory to %s\n',cwd);
1142     end
1143     cd(cwd);
1144     switch status
1145       case 'success'
1146         % ...
1147         % TODO: consider removing solve folder if nothing being saved
1148       case 'error'
1149         % ... error logs
1150     end
1151   end
1152 
1153   function all_ICs=ProcessNumericICs
1154     % first, figure out how many IC values we need (i.e., how many state
1155     % variables we need across all cells).
1156     var_names=model.state_variables;
1157     [nvals_per_var,monitor_counts]=dsGetOutputCounts(model);
1158     num_state_variables=sum(nvals_per_var);
1159     % check that the correct number of IC values was provided
1160     if length(options.ic)~=num_state_variables
1161       error('incorrect number of initial conditions. %g values are needed for %g state variables across %g cells',num_state_variables,length(model.state_variables),sum(pop_sizes));
1162     end
1163     % organize user-supplied ICs into array for each state variable (assume
1164     cnt=0; all_ICs=[];
1165     for i=1:length(var_names)
1166       ICs=options.ic(cnt+(1:nvals_per_var(i)));
1167       % store ICs as string for writing solve_ode and consistent evaluation
1168       all_ICs.(var_names{i})=sprintf('[%s]',num2str(ICs));
1169       cnt=cnt+nvals_per_var(i);
1170     end
1171   end
1172 
1173   function logicalOut = is_varied_mech_list()
1174     if ~isempty(modifications_set{1})
1175       logicalOut = any(cellfun(@(x) strcmp(x{2},'mechanism_list'),modifications_set));
1176     else
1177       logicalOut = false;
1178     end
1179   end
1180 
1181   function removeStudyinfo()
1182     % Problem: studyinfo file has many timestamps and absolute paths
1183     % Solution: remove studyinfo file
1184     studyinfoFile = fullfile(options.study_dir, 'studyinfo.mat');
1185     if exist(studyinfoFile, 'file')
1186       delete(studyinfoFile)
1187     end
1188   end
1189 
1190   function renameMexFilesForUnitTesting()
1191     % Problem: different systems make different mex files, and mex files are
1192     %          different from simulation to simulation on same computer
1193     % Solution: rename extension to a general one, mex4unittest, and skip these
1194     %           files when testing
1195 
1196     studyDirFiles = rls(options.study_dir);
1197     studyDirFiles = studyDirFiles(~cellfun(@isempty, strfind(studyDirFiles, '.mex')));
1198     for k = 1:length(studyDirFiles)
1199       thisFile = studyDirFiles{k};
1200       renamedFile = regexprep(thisFile, '\.mex.+', '.mex4unittest');
1201       movefile(thisFile, renamedFile);
1202     end
1203   end
1204 
1205 end %main fn
1206 
1207 
1208 %% Subfunctions
1209 function [model,options]=extract_vary_statement(model,options)
1210 % Purpose: extract vary statement, remove from model, and set options.vary
1211 if ischar(model) && any(regexp(model,';\s*vary\(.*\)','once'))
1212   % extract vary statement
1213   str=regexp(model,';\s*(vary\(.*\);?)','tokens','once');
1214 
1215   % remove from model
1216   model=strrep(model,str{1},'');
1217 
1218   % set options
1219   var=regexp(str{1},'\((.*)=','tokens','once'); % variable
1220   val=regexp(str{1},'=(.*)\)','tokens','once'); % values
1221   options.vary={'pop1',var{1},eval(val{1})};
1222 end
1223 end
1224 
1225 function options = backward_compatibility(options)
1226 % option_names: (old_name, new_name; ...}
1227 option_names = {...
1228   'override','modifications';
1229   'timelimits','tspan';
1230   'time_limits','tspan';
1231   'IC','ic';
1232   'verbose','verbose_flag';
1233   'SOLVER','solver';
1234   'nofunctions','reduce_function_calls_flag';
1235   'dsfact','downsample_factor';
1236   'memlimit','memory_limit';
1237   };
1238 
1239 if any(ismember(option_names(:,1),options(1:2:end)))
1240   for i=1:size(option_names,1)
1241     % check if any options have this old name
1242     if ismember(option_names{i,1},options(1:2:end))
1243       ind=find(ismember(options(1:2:end),option_names{i,1}));
1244 
1245       % replace old option name by new option name
1246       options{2*ind-1}=option_names{i,2};
1247     end
1248   end
1249 end
1250 
1251 end

Generated on Tue 12-Dec-2017 11:32:10 by m2html © 2005