CHECKMODEL - Standardize model structure and auto-populate missing fields Usage: model=dsCheckModel(model) Input: DynaSim model structure or equations Output: - DynaSim model structure (standardized) model.parameters : substructure with model parameters model.fixed_variables : substructure with fixed variable definitions model.functions : substructure with function definitions model.monitors : substructure with monitor definitions model.state_variables : cell array listing state variables model.ODEs : substructure with one ordinary differential equation (ODE) per state variable model.ICs : substructure with initial conditions (ICs) for each state variable model.conditionals(i) : structure array with each element indicating conditional actions specified in subfields "condition","action","else" (see NOTE 1) model.linkers(i) : structure array with each element indicating an "expression" that should be inserted (according to "operation") into any equations where the "target" appears. (see NOTE 2) .target : string giving the target where expression should be inserted .expression: string giving the expression to insert .operation : string giving the operation to use to insert expression model.comments{i} : cell array of comments found in model files model.specification : specification used to generate the model (see dsCheckSpecification) model.namespaces : (see NOTE 3) - NOTE 1: "action" may include multiple statements separated by semicolons. "condition" must be an expression that evaluates to true or false. - NOTE 2: "linkers" are used only when a model contains external model files. Equations and state variables defined in external files can be combined with equations in other model files (associated with the same population) or population equations in the specification. Recommended practice is to begin targets with the '@' character. - Example: linking mechanism to equations in specification: TODO - Example: linking mechanism to equations in a different mechanism: TODO - NOTE 3: all variables and functions have prefixes added to them that indicate their namespace; a mapping from original names found in equations to the names appearing in the model structure is available in model.namespaces. - Namespaces in the model structure: model.parameters .([namespace param_name])=expression model.fixed_variables .([namespace var_name])=expression model.functions .([namespace func_name])=@(variables)expression model.monitors .([namespace monitor_name])=expression model.state_variables = {namespace_var1,namespace_var2,...} model.ODEs .([namespace state_variable])=expression model.ICs .([namespace state_variable])=expression model.conditionals(i) .namespace,condition,action,else model.linkers(i) .namespace,target,expression,operation model.comments{i} string .specification,.namespaces Examples: - Example 1: obtain empty model structure with all fields model=dsCheckModel([]) - Example 2: standardize existing model model=dsCheckModel(model) see also: dsGenerateModel, dsCheckSpecification, dsCheckData Author: Jason Sherfey, PhD <jssherfey@gmail.com> Copyright (C) 2016 Jason Sherfey, Boston University, USA
0001 function model = dsCheckModel(model, varargin) 0002 %CHECKMODEL - Standardize model structure and auto-populate missing fields 0003 % 0004 % Usage: 0005 % model=dsCheckModel(model) 0006 % 0007 % Input: DynaSim model structure or equations 0008 % 0009 % Output: 0010 % - DynaSim model structure (standardized) 0011 % model.parameters : substructure with model parameters 0012 % model.fixed_variables : substructure with fixed variable definitions 0013 % model.functions : substructure with function definitions 0014 % model.monitors : substructure with monitor definitions 0015 % model.state_variables : cell array listing state variables 0016 % model.ODEs : substructure with one ordinary differential 0017 % equation (ODE) per state variable 0018 % model.ICs : substructure with initial conditions (ICs) for 0019 % each state variable 0020 % model.conditionals(i) : structure array with each element indicating 0021 % conditional actions specified in subfields 0022 % "condition","action","else" (see NOTE 1) 0023 % model.linkers(i) : structure array with each element indicating 0024 % an "expression" that should be inserted 0025 % (according to "operation") into any equations 0026 % where the "target" appears. (see NOTE 2) 0027 % .target : string giving the target where expression should be inserted 0028 % .expression: string giving the expression to insert 0029 % .operation : string giving the operation to use to insert expression 0030 % model.comments{i} : cell array of comments found in model files 0031 % model.specification : specification used to generate the model (see dsCheckSpecification) 0032 % model.namespaces : (see NOTE 3) 0033 % 0034 % - NOTE 1: "action" may include multiple statements separated by semicolons. 0035 % "condition" must be an expression that evaluates to true or false. 0036 % 0037 % - NOTE 2: "linkers" are used only when a model contains external model files. 0038 % Equations and state variables defined in external files can be combined with 0039 % equations in other model files (associated with the same population) or 0040 % population equations in the specification. Recommended practice is to begin 0041 % targets with the '@' character. 0042 % - Example: linking mechanism to equations in specification: TODO 0043 % - Example: linking mechanism to equations in a different mechanism: TODO 0044 % 0045 % - NOTE 3: all variables and functions have prefixes added to them that 0046 % indicate their namespace; a mapping from original names found in equations to 0047 % the names appearing in the model structure is available in model.namespaces. 0048 % - Namespaces in the model structure: 0049 % model.parameters .([namespace param_name])=expression 0050 % model.fixed_variables .([namespace var_name])=expression 0051 % model.functions .([namespace func_name])=@(variables)expression 0052 % model.monitors .([namespace monitor_name])=expression 0053 % model.state_variables = {namespace_var1,namespace_var2,...} 0054 % model.ODEs .([namespace state_variable])=expression 0055 % model.ICs .([namespace state_variable])=expression 0056 % model.conditionals(i) .namespace,condition,action,else 0057 % model.linkers(i) .namespace,target,expression,operation 0058 % model.comments{i} string 0059 % .specification,.namespaces 0060 % 0061 % Examples: 0062 % - Example 1: obtain empty model structure with all fields 0063 % model=dsCheckModel([]) 0064 % 0065 % - Example 2: standardize existing model 0066 % model=dsCheckModel(model) 0067 % 0068 % see also: dsGenerateModel, dsCheckSpecification, dsCheckData 0069 % 0070 % Author: Jason Sherfey, PhD <jssherfey@gmail.com> 0071 % Copyright (C) 2016 Jason Sherfey, Boston University, USA 0072 0073 %% auto_gen_test_data_flag argin 0074 options = dsCheckOptions(varargin,{'auto_gen_test_data_flag',0,{0,1}},false); 0075 if options.auto_gen_test_data_flag 0076 varargs = varargin; 0077 varargs{find(strcmp(varargs, 'auto_gen_test_data_flag'))+1} = 0; 0078 varargs(end+1:end+2) = {'unit_test_flag',1}; 0079 argin = [{model}, varargs]; % specific to this function 0080 end 0081 0082 field_order={'parameters','fixed_variables','functions','monitors',... 0083 'state_variables','ODEs','ICs','conditionals','linkers','comments',... 0084 'specification','namespaces'}; 0085 field_defaults={struct(''),struct(''),struct(''),struct(''),{},struct(''),... 0086 struct(''),struct(''),struct(''),{},struct(''),{}}; 0087 0088 if isempty(model) 0089 % prepare empty model structure 0090 for i=1:length(field_order) 0091 model.(field_order{i})=field_defaults{i}; 0092 end 0093 end 0094 0095 % % check if input is string with name of file containing model 0096 % if ischar(model) && exist(model,'file') 0097 % model=dsImportModel(model); 0098 % end 0099 0100 % check if input is string or cell with equations or spec struct and convert to model structure 0101 if ischar(model) || iscell(model) || ~isfield(model,'state_variables') 0102 model=dsGenerateModel(model); 0103 end 0104 0105 % check back compatibility 0106 model=backward_compatibility(model); 0107 0108 %% auto_gen_test_data_flag argout 0109 if options.auto_gen_test_data_flag 0110 argout = {model}; % specific to this function 0111 0112 dsUnitSaveAutoGenTestData(argin, argout); 0113 end 0114 0115 % % auto-populate missing data 0116 % for i=1:length(field_order) 0117 % if ~isfield(model,field_order{i}) 0118 % model.(field_order{i})=field_defaults{i}; 0119 % end 0120 % end 0121 % 0122 % % standardize field order 0123 % model=orderfields(model,field_order); 0124 0125 % note: auto-populating and standardization of field order may not be 0126 % necessary or beneficial for DynaSim model structures. It only adds extra 0127 % time... if the above is uncommented-out, then dsCombineModels() should also 0128 % be edited by uncommenting-out the call to dsCheckModel() and commenting-out 0129 % the call to orderfields according to first input (at the end of the 0130 % function). 0131 0132 0133 function model=backward_compatibility(model) 0134 % account for change in state variable dimensions: 0135 % cells used to be along rows in a column; now columns across a row. 0136 % replace cols (Npop,1) by rows (1,Npop). similar for Npre,Npost 0137 % do string substitution in ODEs and ICs 0138 target_types={'ODEs','ICs'}; 0139 % loop over types of model data 0140 for type_index=1:length(target_types) 0141 type=target_types{type_index}; 0142 % info for this type 0143 s=model.(type); 0144 if isstruct(s) 0145 update_these=fieldnames(s); 0146 expressions=struct2cell(s); 0147 % loop over target expressions from which to eliminate internal function calls 0148 updated=0; 0149 for i=1:length(expressions) 0150 if isempty(expressions{i}) 0151 continue; 0152 end 0153 % update expressions of this type 0154 % note: do single check first b/c will not normally be needed --> 0155 % reduces 3 conditional checks to 1 in most cases. 0156 if ~isempty(regexp(expressions{i},'\((\w+_)?(Npop|Npre|Npost),1\)','once')) 0157 updated=1; 0158 if ~isempty(regexp(expressions{i},'\((\w+_)?Npop,1\)','once')) 0159 expressions{i}=regexprep(expressions{i},'\((\w+_)Npop,1\)','\(1,$1Npop\)'); 0160 end 0161 if ~isempty(regexp(expressions{i},'\((\w+_)?Npre,1\)','once')) 0162 expressions{i}=regexprep(expressions{i},'\((\w+_)Npre,1\)','\(1,$Npre\)'); 0163 end 0164 if ~isempty(regexp(expressions{i},'\((\w+_)?Npost,1\)','once')) 0165 expressions{i}=regexprep(expressions{i},'\((\w+_)Npost,1\)','\(1,$Npost\)'); 0166 end 0167 end 0168 end 0169 if updated 0170 % update model with expressions that have parameter values in them 0171 model.(type)=cell2struct(expressions,update_these,1); 0172 end 0173 end 0174 end