function success = ChangeXPPsetFile(filename,parset)
%ChangeXPPsetFile Change parameters / initial conditions in XPP SET file
% (c) Robert Clewley, August 2004
%
% Usage: success = ChangeXPPsetFile(filename,parset) changes the values of the
% parameters and initial conditions named in the parset structure to by directly
% modifying the named SET file. The return value success is a boolean.
%
% Input arguments:
% filename should end with the extension '.set'
% parset is a structure array with fields 'type', 'name', and 'val'. type must be one
% of the strings 'PAR' or 'IC'.
success = false;
if isempty(filename) | exist(filename,'file') ~= 2
disp(' Problem with filename passed: file does not exist in specified path')
return
else
if ~strcmp('.set',filename(length(filename)-3:length(filename)))
disp(' Problem with filename passed: must end in `.set`')
return
end
end
fid = fopen(filename,'r');
lenpars = length(parset);
file_done = false;
ics_found = false;
pars_found = false;
lineCount = 0;
setfile = {};
numParsTot = 0; % initial value for total params
numEqsAuxsTot = 0; % initial value for total ics
ics_found = ~ismember('IC',{parset(:).type});
pars_found = ~ismember('PAR',{parset(:).type});
tots_found = false;
allnames = {parset.name};
% get par and ic totals
while ~file_done & ~tots_found
fline = fgetl(fid);
setfile = {setfile{:}, fline};
lineCount = lineCount + 1;
if ~ischar(fline) % then EOF
file_done = true;
break
end
foundNumEqs = strfind(fline,'Number of equations and auxiliaries');
if ~isempty(foundNumEqs)
parsed = ParseParLine(fline);
numEqsAuxsTot = parsed.num;
end
foundNumPars = strfind(fline,'Number of parameters');
if ~isempty(foundNumPars)
parsed = ParseParLine(fline);
numParsTot = parsed.num;
end
tots_found = (numParsTot > 0 ) & (numEqsAuxsTot > 0);
end
if file_done % check this before totals
disp(' Premature end of file reached. Cannot continue.')
fclose(fid);
return
end
if numEqsAuxsTot == 0 | numParsTot == 0 % then file is messed up
disp(' The number of equations or parameters was not found in the SET file')
fclose(fid);
return
end
% ics come first in the SET file
while ~file_done & ~ics_found
fline = fgetl(fid);
setfile = {setfile{:}, fline};
lineCount = lineCount + 1;
if ~ischar(fline) % then EOF
file_done = true;
break
end
if strcmp(fline,'# Old ICs')
ics_found = true;
end
end
if file_done
disp(' Premature end of file reached. ICs not found.')
fclose(fid);
return
end
% now change any specified ics, when encountered
icsDone = ~ics_found; % don't do the next code if no ics_found
icLineCount = 0;
if ~icsDone
icnames = {};
for parIx=1:lenpars % make a simple cell array list of parnames to be changed
if strcmp(parset(parIx).type,'IC')
icnames = {icnames{:},upper(parset(parIx).name)};
end
end
numics = length(icnames);
foundIcOccurrence = zeros(1,numics);
end
while ~icsDone & icLineCount <= numEqsAuxsTot
fline = fgetl(fid);
if ~ischar(fline) % then premature EOF
disp(' Premature end of file reached while searching for initial conditions.')
fclose(fid);
return
end
icLineCount = icLineCount + 1;
parsed = ParseParLine(fline);
if parsed.num == NaN
disp(' Problem parsing numerical value of parameter in SET file.')
fclose(fid);
return
end
[ispres ix] = ismember(parsed.name, upper(allnames));
if ispres % then ic to be changed has been found
if strcmp(parset(ix).type,'PAR')
fprintf(' Found name `%s` that is in SET file as an initial condition\n',parset(ix).name)
fclose(fid);
return
end
foundIcOccurrence(ix) = 1;
flineNew = [ num2str(parset(ix).val) ' ' parsed.name ];
else
flineNew = fline;
end
setfile = {setfile{:}, flineNew};
lineCount = lineCount + 1;
if sum(foundIcOccurrence) == numics % then made all changes necessary
icsDone = true;
end
end
if sum(foundIcOccurrence) ~= numics
disp(' Error: Some i.c.`s specified for change were not found in SET file.')
fclose(fid);
return
end
%%%%%%
% now jump to parameter changes
while ~file_done & ~pars_found
fline = fgetl(fid);
if ~ischar(fline) % then EOF
file_done = true;
break
end
setfile = {setfile{:}, fline};
lineCount = lineCount + 1;
if strcmp(fline,'# Parameters')
pars_found = true;
end
end
if file_done
disp(' Premature end of file reached. Parameters not found.')
fclose(fid);
return
end
% now change specified pars when encountered
parsDone = ~pars_found;
parLineCount = 0;
if ~parsDone
parnames = {};
for parIx=1:lenpars % make a simple cell array list of parnames to be changed
if strcmp(parset(parIx).type,'PAR')
parnames = {parnames{:},parset(parIx).name};
end
end
numparams=length(parnames);
foundParOccurrence = zeros(1,numparams);
end
while ~parsDone & parLineCount <= numParsTot
fline = fgetl(fid);
if ~ischar(fline) % then premature EOF
disp(' Premature end of file reached while searching parameters.')
fclose(fid);
return
end
parLineCount = parLineCount + 1;
parsed = ParseParLine(fline);
if parsed.num == NaN
disp(' Problem parsing numerical value of parameter in SET file.')
fclose(fid);
return
end
[ispres ix] = ismember(parsed.name, allnames);
if ispres % then par to be changed has been found
if strcmp(parset(ix).type,'IC')
fprintf(' Found name `%s` that is in SET file as a parameter\n',parset(ix).name)
fclose(fid);
return
end
foundParOccurrence(ix) = 1;
flineNew = [ num2str(parset(ix).val) ' ' parsed.name ];
else
flineNew = fline;
end
setfile = {setfile{:}, flineNew};
lineCount = lineCount + 1;
if sum(foundParOccurrence) == numparams % then made all changes necessary
parsDone = true;
end
end
if sum(foundParOccurrence) ~= numparams
disp(' Error: Some parameters specified for change were not found in SET file.')
fclose(fid);
return
end
% file_done is still not true if we get to this point
while ~file_done
fline = fgetl(fid);
if ~ischar(fline) % then EOF
file_done = true;
else
setfile = {setfile{:}, fline}; % finish reading in file
lineCount = lineCount + 1;
end
end
fclose(fid);
% write out changed file
fidw = fopen( filename ,'w');
if fidw ~= -1
for line=1:lineCount
fprintf(fidw, '%s\r\n', setfile{line});
end
fclose(fidw);
else
fprintf(' Problem opening file %s for writing. Cannot continue\n',filename);
return
end
success = true;
return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function parsed = ParseParLine(fline)
parsed.num = NaN;
parsed.name = '';
[numStr nameStr] = strtok(fline);
if isempty(numStr) | isempty(nameStr)
return
end
if ~isNum(numStr)
return
end
nameStr = nameStr(isspace(nameStr)==0); % get rid of all whitespace
parsed.num = str2num(numStr);
parsed.name = nameStr;
return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function result = isNum(data)
% parameter `data` is a string
result = false;
pointCount = 0;
num = [char(48:57),'.','-','e'];
for i=1:length(data)
if data(i) == '.'
pointCount = pointCount + 1;
if pointCount > 1
return % false
end
end
if ~ismember(data(i),num)
return % false
end
end
result = true; % can only get here if all chars were numeric
return