function aggregate(ofile, dynopt, rootfolder, varargin) % Agregates cherry-picked models. % Copyright © 2019-2023 Dynare Team % % This file is part of Dynare. % % Dynare 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. % % Dynare 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 Dynare. If not, see . MAX_NUMBER_OF_ELEMENTS = 10000; if ~isoctave && matlab_ver_less_than('9.14') % Warning removed in R2023a warning off MATLAB:subscripting:noSubscriptsSpecified end if ~isempty(dynopt) % Should be a list of options for the preprocessor in a cell % array. firstline = '// --+ options:'; if iscell(dynopt) for i = 1:length(dynopt) firstline = sprintf('%s %s', firstline, dynopt{i}); end firstline = sprintf('%s %s', firstline, '+--'); else error('Second argument has to be a cell array (list of options for dynare preprocessor).') end else firstline = ''; end % Get parameters. for i=1:length(varargin) fid = fopen(sprintf('%s/parameters.inc', varargin{i})); if fid<0 % No parameters in the cherrypicked (sub)model, go to the % next cherrypicked model. continue end statement = fgetl(fid); if exist('plist', 'var') plist = union(plist, strsplit(statement, {'parameters', ' ', ';'})); else plist = strsplit(statement, {'parameters', ' ', ';'}); end plist(cellfun(@(x) all(isempty(x)), plist)) = []; fclose(fid); end % Get equations eqlist = cell(MAX_NUMBER_OF_ELEMENTS, 4); tagnum = 1; eqnum = 0; for i=1:length(varargin) % Store all non-empty lines of model.inc in the “model” cell-array fid = fopen(sprintf('%s/model.inc', varargin{i})); model = {}; while ~feof(fid) line = fgetl(fid); if ~isempty(line) model{end+1} = line; end end fclose(fid); eqtag = false; for j=1:length(model) if isequationtag(model{j}) if eqtag error('An equation tag must be followed by an equation.') end % Ensure that the equation tag name matches the LHS variable. eqtagname = regexp(model{j}, 'name=''(\w*)''', 'match'); lhs = getequation(model{j+1}); endovar = getendovar(lhs); eqtagname_ = strcat('name=''', endovar{1}, ''''); if ~isempty(eqtagname) if ~isequal(eqtagname{1}, eqtagname_) model{j} = strrep(model{j}, eqtagname{1}, eqtagname_); end else model{j} = strcat('[', eqtagname_, ']'); end % Add equation tag with block name. if ~isempty(rootfolder) model{j} = strcat('[blockname=''', getblockname(varargin{i}, rootfolder), ''',', model{j}(2:end)); end eqlist{tagnum,4} = model{j}; eqtag = true; else eqnum = eqnum+1; [lhs, rhs] = getequation(model{j}); endovar = getendovar(lhs); eqlist{eqnum,1} = endovar{1}; eqlist{eqnum,2} = lhs; eqlist{eqnum,3} = rhs; eqtag = false; tagnum = tagnum+1; end end end eqlist = eqlist(1:eqnum,:); [~, idx] = unique(eqlist(:,1), 'stable'); eqlist = eqlist(idx, :); % Get endogenous variables. elist = cell(MAX_NUMBER_OF_ELEMENTS, 2); enum = 0; for i=1:length(varargin) fid = fopen(sprintf('%s/endogenous.inc', varargin{i})); cline = fgetl(fid); while ischar(cline) if ~isequal(cline, 'var') enum = enum+1; cline = regexprep(cline, '\t', ''); cline = regexprep(cline, ';', ''); [v, t] = getvarandtag(cline); elist(enum,1) = {v}; elist(enum,2) = {t}; end cline = fgetl(fid); end fclose(fid); end elist = elist(1:enum,:); [~, idx] = unique(elist(:,1), 'stable'); elist = elist(idx,:); % Get exogenous variables. xlist = cell(MAX_NUMBER_OF_ELEMENTS, 2); xnum = 0; for i=1:length(varargin) fid = fopen(sprintf('%s/exogenous.inc', varargin{i})); if fid<0 % No exogenous variables in the cherrypicked (sub)model, go to the % next cherrypicked model. continue end cline = fgetl(fid); while ischar(cline) if ~isequal(cline, 'varexo') xnum = xnum+1; cline = regexprep(cline, '\t', ''); cline = regexprep(cline, ';', ''); [v, t] = getvarandtag(cline); xlist(xnum,1) = {v}; xlist(xnum,2) = {t}; end cline = fgetl(fid); end fclose(fid); end xlist = xlist(1:xnum,:); [~, idx] = unique(xlist(:,1), 'stable'); xlist = xlist(idx,:); % Get parameter values. pArray = cell(0, 3); for i=1:length(varargin) fid = fopen(sprintf('%s/parameter-values.inc', varargin{i})); if fid<0 % No calibrations in the cherrypicked (sub)model, go to the % next cherrypicked model. continue end cline = fgetl(fid); while ischar(cline) tmp = textscan(cline, '%s = %f', 'Delimiter', ';= '); pArray(end+1,1) = tmp{1}; pArray{end,2} = tmp{2}; pArray{end,3} = varargin{i}; cline = fgetl(fid); end fclose(fid); end if rows(pArray)>1 irow = 2; while irow<=rows(pArray) ispreviouslydefined = strcmpi(pArray{irow,1}, pArray(1:irow-1,1)); if any(ispreviouslydefined) if isnan(pArray{ispreviouslydefined,2}) if ~isnan(pArray{irow,2}) % Remove first assignment (with NaN) pArray(ispreviouslydefined,:) = []; else % Remove second assignment (both assigments are NaNs) pArray(irow,:) = []; end elseif isnan(pArray{irow,2}) % New assigment is NaN but not the previous one. pArray(irow,:) = []; else % Check that the values are identical in both assignments. if abs(pArray{ispreviouslydefined,2}-pArray{irow,2})>1e-10 error('More than one assigment for parameter %s with different values (see cherrypicked files in %s and %s).', pArray{irow,1}, pArray{irow,3}, pArray{ispreviouslydefined,3}); else % Remove last assignement (duplicate). pArray(irow,:) = []; end end else irow = irow+1; end end end if any(isnan([pArray{:,2}])) msg =''; for i=1:rows(pArray) if isnan(pArray{i,2}) msg = sprintf('%sParameter %s has no value.\n', msg, pArray{i,1}); end end error(msg) end calibration = ''; for i=1:rows(pArray) calibration = sprintf('%s%s = %s;\n', calibration, pArray{i,1}, num2str(pArray{i,2}, 16)); end % Move the endogenous variables which are not LHS of an equation % into the set of exogenous variables. [~, i1] = intersect(elist(:,1), eqlist(:,1)); if ~isequal(length(i1),rows(eqlist)) error('Something is wrong with the endogenous variables.') end i2 = setdiff(1:rows(elist), i1); xlist = [xlist; elist(i2,:)]; [~,idx] = unique(xlist(:,1)); % Ensure that the exogenous variable names are unique. xlist = [xlist(idx,1) xlist(idx,2)]; % We do not test that the tags are the same. elist = elist(i1,:); % Remove endogenous variables from list of exogenous variables (if any). xlist1 = xlist(:,1); xlist2 = xlist(:,2); [xlist1, id] = setdiff(xlist1, elist(:,1)); xlist2 = xlist2(id); xlist = [xlist1, xlist2]; % Print all cherry-picked models in one mod-file. [filepath, filename, fileext] = fileparts(ofile); if ~isempty(filepath) && ~exist(filepath, 'dir') mkdir(filepath); end if isempty(filepath) fid = fopen(sprintf('%s%s', filename, fileext), 'w'); else fid = fopen(sprintf('%s%s%s%s', filepath, filesep(), filename, fileext), 'w'); end if ~isempty(firstline) fprintf(fid, '%s\n\n', firstline); end % Print list of endogenous variables. fprintf(fid, 'var\n'); for i=1:rows(elist) if size(elist,2)==1 || isempty(elist{i,2}) fprintf(fid, '\t%s\n', elist{i,1}); else fprintf(fid, '\t%s %s\n', elist{i,1}, elist{i,2}); end end fprintf(fid, ';\n\n'); if exist('plist', 'var') && ~isempty(plist) % Print list of parameters. fprintf(fid, 'parameters\n'); for i=1:length(plist) fprintf(fid, '\t%s\n', plist{i}); end fprintf(fid, ';\n\n'); % Print calibration. fprintf(fid, calibration); end if exist('xlist', 'var') && ~isempty(xlist) % Print list of exogenous variables. fprintf(fid, '\n\n'); fprintf(fid, 'varexo\n'); for i=1:rows(xlist) if size(xlist,2)==1 || isempty(xlist{i,2}) fprintf(fid, '\t%s\n', xlist{i,1}); else fprintf(fid, '\t%s %s\n', xlist{i,1}, xlist{i,2}); end end fprintf(fid, ';\n'); end skipline(1, fid) % Provide an interface to flip endogenous and exogenous variables. Active if only macrovariable % InvertModel is set to True. The calls to the change_type command must be provided in the file % model-inversion-setup.inc (in the current folder). fprintf(fid, '@#ifdef InvertModel\n'); fprintf(fid, ' @#if InvertModel\n'); fprintf(fid, ' @#include "model-inversion-setup.inc"\n'); fprintf(fid, ' @#endif\n'); fprintf(fid, '@#endif\n'); skipline(1, fid) fprintf(fid, 'model;\n\n'); for i=1:rows(eqlist) if isempty(eqlist{i,4}) fprintf(fid, '\t%s = %s;\n\n', eqlist{i,2}, eqlist{i,3}); else fprintf(fid, '\t%s\n', eqlist{i,4}); fprintf(fid, '\t%s = %s;\n\n', eqlist{i,2}, eqlist{i,3}); end end fprintf(fid, 'end;'); fclose(fid); if ~isoctave && matlab_ver_less_than('9.14') warning on MATLAB:subscripting:noSubscriptsSpecified end function b = isequationtag(str) b = true; if isempty(regexp(str, '\[.*\]','once')) b = false; end function [lhs, rhs] = getequation(str) terms = strsplit(str, {'=',';'}); terms(cellfun(@(x) all(isempty(x)), terms)) = []; terms(1) = {strrep(terms{1}, ' ', '')}; lhs = regexp(terms{1}, '^(diff\([\-]*(log|diff)\([\-\+\*\/\w]*\)\)|(log|diff)\([\(\-\+\*\/\)\w]*\)|\w*)', 'match'); if ~isempty(lhs) lhs = lhs{1}; if isequal(lhs, 'log') error('Malformed equation: log of log or diff are not allowed.') end rhs = terms{2}; else error('Malformed equation.') end function v = getendovar(lhs) v = strsplit(lhs, {'diff','log','(',')', '+', '-', '*', '/'}); v(cellfun(@(x) all(isempty(x)), v)) = []; if length(v)>1 error('Malformed equation: no more than one endogenous variable can be used on the LHS.') end function [v, t] = getvarandtag(str) tmp = regexp(str, '(?\w+)\s*(?\(.*\))', 'names'); if isempty(tmp) tmp = regexp(str, '(?\w+)\s*', 'names'); v = tmp.name; t = ''; else v = tmp.name; t = tmp.tag; end function blkname = getblockname(str, ROOT_FOLDER) str = strrep(str, '/', filesep()); str = strrep(str, [ROOT_FOLDER filesep() 'blocks' filesep()], ''); idx = strfind(str, filesep()); blkname = str(1:idx(1)-1);