Commit 72409180 authored by Sumudu Kankanamge's avatar Sumudu Kankanamge 🤔
Browse files

-Added almost all options available in the Dynare documentation for the Estimation command.

-Added interfaces for most outputs corresponding to these options, for instance: smoother, forecast, bayesian_irf, etc.
-Various bug fixes.
parent 3a857395
......@@ -2,3 +2,7 @@ node_modules
dynare_m
dynare_m_old
dynare.db
webApp_Node.code-workspace
optionsList.xlsx
WebApp_v2.docx
~$optionsList.xlsx
......@@ -7,7 +7,7 @@
# Dynare webApp
webApp is a Graphical User Interface (GUI) for Dynare that embeds a Dynare code editor, an output console and graphical reporting of results. It offers an unified Dynare user experience with graphically enhanced functionalities. webApp jointly uses several technologies. The interface is rendered using web technologies whereas the computations are performed using [Dynare](http://www.dynare.org/) and a numerical computing environment such as [MatLab](https://www.mathworks.com/products/matlab.html?s_tid=hp_products_matlab). In more details, the server side elements of Dynare-interface are written in NodeJS and the front side are written in Javascript.
webApp is a Graphical User Interface (GUI) for Dynare that embeds a Dynare code editor, an output console and graphical reporting of results. It offers an unified Dynare user experience with graphically enhanced functionalities. webApp jointly uses several technologies. The interface is rendered using web technologies whereas the computations are performed using [Dynare](http://www.dynare.org/) and a numerical computing environment such as [MatLab](https://www.mathworks.com/products/matlab.html?s_tid=hp_products_matlab). In more details, the server side elements of the webApp are written in NodeJS and the front side are written in Javascript.
# License
......@@ -16,7 +16,7 @@ Most of the source files are covered by the GNU General Public Licence version
# Installing the webApp
[Node.js](https://nodejs.org/en/) is required to run the webApp and [npm](https://www.npmjs.com/) is the default package manager for the project. A fresh install of [Dynare](https://www.dynare.org/) and a Dynare compatible version of [MATLAB](https://www.mathworks.com/products/matlab.html) is also required.
[Node.js](https://nodejs.org/en/) is required to run the webApp and [npm](https://www.npmjs.com/) is the default package manager for the project. A fresh install of [Dynare](https://www.dynare.org/) and a Dynare compatible version of [MATLAB](https://www.mathworks.com/products/matlab.html) are also required.
To run the development version of the webApp, follow these steps:
- Download and install NodeJS and npm,
......@@ -24,7 +24,7 @@ To run the development version of the webApp, follow these steps:
- cd to the webApp directory and run `npm install` to install required dependencies,
- From the Dynare directory, copy the preprocessor binary from `/matlab/preprocessor64` to the webApp `/assets/preprocessor` directory
- Edit the `/assets/dynareapp.JSON` file and enter the path to your Matlab binary path and the Dynare install directory path
- In webApp directory run `npm start` to run the app
- In the webApp directory run `npm start` to run the app
- The default port is 3000, the webApp should be available at `http://localhost:3000/`
# Documentation
......
......@@ -6,7 +6,7 @@ const loadjsonfile = require('./appModules').loadjsonfile;
var logger = require('morgan');
var exphbs = require('express-handlebars');
var exphbs = require('express-handlebars');
//routes
var indexRouter = require('./routes/index');
......@@ -26,19 +26,21 @@ app.set('view engine', 'handlebars');;
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.urlencoded({
extended: false
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
......@@ -57,7 +59,7 @@ const fork = require('child_process').fork;
const program = path.resolve('background.js');
const parameters = [];
const options = {
stdio: [ 'pipe', 'pipe', 'pipe', 'ipc' ]
stdio: ['pipe', 'pipe', 'pipe', 'ipc']
};
const child = fork(program, parameters, options);
......@@ -79,8 +81,13 @@ const child = fork(program, parameters, options);
// );
// ipc.server.start();
var initprogressbar=0;
var progressbarcount=0;
var initprogressbar = 0;
var initprogressbarBAYES = 0;
var initprogressbarSMOOTH = 0;
var progressbarcount = 0;
var progressbarcountBAYES = 0;
var progressbarcountSMOOTH = 0;
global.mysocket;
......@@ -88,19 +95,27 @@ ipc.config.id = 'appIPC';
ipc.config.retry = 1500;
ipc.config.silent = true;
ipc.serve(() => ipc.server.on('rundynarematlab', (data, socket) => {
global.mysocket=socket;
console.log('received ipc message from router');
console.log(data)
if (data=='estimation')
{
initprogressbar=1;
progressbarcount=0;
}
global.mysocket = socket;
console.log('received ipc message from router');
console.log(data)
if (data == 'estimation') {
initprogressbar = 1;
progressbarcount = 0;
mymessage={'messageid': 'rundynarematlab','messagetype': data};
child.send(mymessage);
})
initprogressbarBAYES = 1;
progressbarcountBAYES = 0;
initprogressbarSMOOTH = 1;
progressbarcountSMOOTH = 0;
}
mymessage = {
'messageid': 'rundynarematlab',
'messagetype': data
};
child.send(mymessage);
})
);
ipc.server.start();
......@@ -108,19 +123,19 @@ ipc.server.start();
child.on('message', message => {
var messageprocess=0;
var messageprocess = 0;
if (message.messageid=='matlabready'){
if (message.messageid == 'matlabready') {
console.log("matlabready");
console.log(message.message);
messageprocess=1;
messageprocess = 1;
if (message.message==1){
if (message.message == 1) {
console.log('-=stochastic simulation completed=-');
finishstochastic();
// ipc.server.emit(
// global.mysocket,
// 'app.message',
......@@ -129,56 +144,72 @@ child.on('message', message => {
// message: 'Server emitted message'
// }
// );
}
if (message.message==2){
if (message.message == 2) {
console.log('-=perfect foresight computation completed=-');
finishperfect();
}
if (message.message==3){
if (message.message == 3) {
console.log('-=estimation computation completed=-');
finishestimation();
}
}
if (message.messageid=='stdoutmessage'){
if (message.messageid == 'stdoutmessage') {
// console.log("stdoutmessage");
// console.log(`${message.message}`);
// console.log(message.message);
// console.log(message.messagetype);
if (message.messagetype=='progressbar'){
if (initprogressbar==1)
{
initprogressbar=0;
if (message.messagetype == 'progressbar') {
if (initprogressbar == 1) {
initprogressbar = 0;
sockIO.emit('matlabmessagetoconsole_progressbar_init', message.message);
console.log('matlabmessagetoconsole_progressbar_init sent');
}
else {
} else {
progressbarcount=progressbarcount+1;
progressbarcount = progressbarcount + 1;
if (progressbarcount%50==0){
if (progressbarcount % 50 == 0) {
sockIO.emit('matlabmessagetoconsole_progressbar', message.message);
// console.log('matlabmessagetoconsole_progressbar');
}
}
}
else{
}
} else if (message.messagetype == 'progressbarBAYES') {
if (initprogressbarBAYES == 1) {
initprogressbarBAYES = 0;
sockIO.emit('matlabmessagetoconsole_progressbarbayes_init', message.message);
} else {
progressbarcountBAYES = progressbarcountBAYES + 1;
if (progressbarcountBAYES % 5 == 0) {
sockIO.emit('matlabmessagetoconsole_progressbarbayes', message.message);
}
}
} else if (message.messagetype == 'progressbarSMOOTH') {
if (initprogressbarSMOOTH == 1) {
initprogressbarSMOOTH = 0;
sockIO.emit('matlabmessagetoconsole_progressbarsmooth_init', message.message);
} else {
progressbarcountSMOOTH = progressbarcountSMOOTH + 1;
if (progressbarcountSMOOTH % 5 == 0) {
sockIO.emit('matlabmessagetoconsole_progressbarsmooth', message.message);
}
}
} else {
console.log(message.message);
sockIO.emit('matlabmessagetoconsole', message.message);
}
messageprocess=1;
messageprocess = 1;
}
if (messageprocess==0){
console.log('Message from child:', message.message, 'with id:',message.messageid);
if (messageprocess == 0) {
console.log('Message from child:', message.message, 'with id:', message.messageid);
}
});
//---------------
......@@ -192,7 +223,7 @@ initdb();
// sockIO.on('connection', function(socket){
// console.log('A client connection occurred!');
// });
async function finishestimation() {
......
......@@ -478,7 +478,7 @@ module.exports = {
movenewdatafile: function (myfilename, myfiletype, myfilepath) {
//moves an uploaded data file from its temporary directory to the work folder
return new Promise(function (resolve, reject) {
var filePath = path.resolve(__dirname, './assets/modfiles/' + myfilename + '_datafile.' + myfiletype);
var filePath = path.resolve(__dirname, './assets/modfiles/' + 'datafile_' + myfilename + '.' + myfiletype);
fs.rename(myfilepath, filePath, function (err) {
if (err) return reject(new Error('Unable to move datafile to work folder: ' + err));
return resolve();
......@@ -667,6 +667,42 @@ module.exports = {
});
}
if (runtype == 1) {
try {
await module.exports.removeoldfile('perforout.JSON');
} catch (error) {
return reject({
'status': 100,
'message': error.toString()
});
}
}
if (runtype == 2) {
try {
await module.exports.removeoldfile('stochsimout.JSON');
} catch (error) {
return reject({
'status': 100,
'message': error.toString()
});
}
}
if (runtype == 3) {
try {
await module.exports.removeoldfile('estimout.JSON');
} catch (error) {
return reject({
'status': 100,
'message': error.toString()
});
}
}
// preparing temp.mod file content
......@@ -804,6 +840,15 @@ module.exports = {
if (runtype == 3) {
//for estimation we don't write a json file but write everything on the mod file
try {
await module.exports.removeoldfile('estimout.JSON');
} catch (error) {
return reject({
'status': 100,
'message': error.toString()
});
}
//writing observables
if ('obsdescription' in jsonindata) {
modfilecontent += 'varobs ';
......@@ -896,33 +941,60 @@ module.exports = {
//writing estimation command
console.log(jsonindata['nobs'])
console.log(jsonindata['mhreplic'])
console.log(jsonindata['mhnblocks'])
console.log(jsonindata['mhjscale'])
// console.log(jsonindata['nobs'])
// console.log(jsonindata['mhreplic'])
// console.log(jsonindata['mhnblocks'])
// console.log(jsonindata['mhjscale'])
modfilecontent +="estimation(datafile='"+modelhash + "_datafile."+jsonindata['datafiletype']+"'";
//put all the estimation options here
if (!(jsonindata['nobs'] === null)) {
modfilecontent +=',nobs='+parseInt(jsonindata['nobs'], 10);
}
modfilecontent +="estimation(datafile='datafile_"+modelhash+"."+jsonindata['datafiletype']+"'";
if (!(jsonindata['mhreplic'] === null)) {
modfilecontent +=',mh_replic='+parseInt(jsonindata['mhreplic'], 10);
}
// if (!(jsonindata['nobs'] === null)) {
// modfilecontent +=',nobs='+parseInt(jsonindata['nobs'], 10);
// }
if (!(jsonindata['mhnblocks'] === null)) {
modfilecontent +=',mh_nblocks='+parseInt(jsonindata['mhnblocks'], 10);
}
// if (!(jsonindata['mhreplic'] === null)) {
// modfilecontent +=',mh_replic='+parseInt(jsonindata['mhreplic'], 10);
// }
if (!(jsonindata['mhjscale'] === null)) {
modfilecontent +=',mh_jscale='+parseInt(jsonindata['mhjscale'], 10);
}
// if (!(jsonindata['mhnblocks'] === null)) {
// modfilecontent +=',mh_nblocks='+parseInt(jsonindata['mhnblocks'], 10);
// }
// if (!(jsonindata['mhjscale'] === null)) {
// modfilecontent +=',mh_jscale='+parseInt(jsonindata['mhjscale'], 10);
// }
// //put all the estimation options here
console.log('---===== ESTIM OPTIONS ====---')
var option0flag=null;
Object.keys(jsonindata['optionsdescription']).forEach((key, index) => {
if (jsonindata['optionsdescription'][key]['estoptinputtype'] == 0){//no input options
modfilecontent +=","+jsonindata['optionsdescription'][key]['estoptdynarename'];
}
else {//options with input
if (jsonindata['optionsdescription'][key]['estoptindex']==0){
option0flag=jsonindata['optionsdescription'][key]['estoptvalue'];
}
else {
modfilecontent +=","+jsonindata['optionsdescription'][key]['estoptdynarename']+"="+jsonindata['optionsdescription'][key]['estoptvalue'];
}
}
});
//end of options close estimation command
modfilecontent +=');'
if (option0flag !=null){//there is a specific list of endogenous to be added behind the estimation command
modfilecontent +=') '+option0flag+';'
}
else{//we add no endogenous behind the estimation command
modfilecontent +=');'
}
......
function di_stochastic_simulations()
global M_ options_ oo_ dataset_ estim_params_
disp('di_estimation performing Matlab tasks');
global M_ options_ oo_ dataset_ estim_params_
data2json=struct();
disp('di_estimation performing Matlab tasks');
data2json.exo_names=char(M_.exo_names);
data2json.param_names=char(M_.param_names);
data2json.exo_nbr=M_.exo_nbr;
data2json.param_nbr=M_.param_nbr;
data2json = struct();
%Smoothed shocks
if isfield(oo_,'SmoothedShocks')==1
data2json.SmoothedShocks=oo_.SmoothedShocks;
end
data2json.exo_names = char(M_.exo_names);
data2json.param_names = char(M_.param_names);
data2json.exo_nbr = M_.exo_nbr;
data2json.param_nbr = M_.param_nbr;
%Data
data2json.obs_nbr=length(dataset_.name);
data2json.obs_names=char(dataset_.name);
data2json.obs_data=dataset_.data;
%Data
data2json.obs_nbr = length(dataset_.name);
data2json.obs_names = char(dataset_.name);
data2json.obs_data = dataset_.data;
data2json.obs_data={};
for nobs = 1:length(dataset_.name)
data2json.obs_data.(strtrim(char(dataset_.name(nobs,:))))=dataset_.data(:,nobs);
end
data2json.obs_data = {};
%Smoothed observables
if isfield(oo_,'SmoothedVariables')==1
data2json.SmoothedObs={};
for nobs = 1:length(dataset_.name)
data2json.SmoothedObs.(strtrim(char(dataset_.name(nobs,:))))=oo_.SmoothedVariables.(strtrim(char(dataset_.name(nobs,:))));
end
end
for nobs = 1:length(dataset_.name)
data2json.obs_data.(strtrim(char(dataset_.name(nobs, :)))) = dataset_.data(:, nobs);
end
if isfield(oo_,'mle_mode')==1
%this is produced only with ML estim
data2json.estimtypeflag=0; %this will indicate ML estimation
%Smoothed observables
% if isfield(oo_.mle_mode,'parameters')==1
% data2json.mle_mode_params=oo_.mle_mode.parameters;
% data2json.mle_std_params=oo_.mle_std_at_mode.parameters;
% end
% if isfield(oo_.mle_mode,'shocks_std')==1
% data2json.mle_mode_shocks_std=oo_.mle_mode.shocks_std;
% data2json.mle_std_shocks_std=oo_.mle_std_at_mode.shocks_std;
% end
if isfield(oo_, 'mle_mode') == 1
%this is produced only with ML estim
data2json.estimtypeflag = 0; %this will indicate ML estimation
if isfield(oo_,'mle_mode')==1
data2json.mle_mode=oo_.mle_mode;
end
% if isfield(oo_.mle_mode,'parameters')==1
% data2json.mle_mode_params=oo_.mle_mode.parameters;
% data2json.mle_std_params=oo_.mle_std_at_mode.parameters;
% end
if isfield(oo_,'mle_std_at_mode')==1
data2json.mle_std_at_mode=oo_.mle_std_at_mode;
end
% if isfield(oo_.mle_mode,'shocks_std')==1
% data2json.mle_mode_shocks_std=oo_.mle_mode.shocks_std;
% data2json.mle_std_shocks_std=oo_.mle_std_at_mode.shocks_std;
% end
else
data2json.estimtypeflag=1; %this will indicate Bayesian estimation
if isfield(oo_, 'mle_mode') == 1
data2json.mle_mode = oo_.mle_mode;
end
%prior info
if isfield(oo_, 'mle_std_at_mode') == 1
data2json.mle_std_at_mode = oo_.mle_std_at_mode;
end
else
data2json.estimtypeflag = 1; %this will indicate Bayesian estimation
if isfield(estim_params_,'param_vals')==1
if isempty(estim_params_.param_vals)==0
data2json.param_vals=estim_params_.param_vals;
end
end
%prior info
if isfield(estim_params_,'var_exo')==1
if isempty(estim_params_.var_exo)==0
data2json.var_exo=estim_params_.var_exo;
end
end
if isfield(estim_params_, 'param_vals') == 1
if isfield(estim_params_,'corrx')==1
if isempty(estim_params_.corrx)==0
data2json.corrx=estim_params_.corrx;
end
end
if isempty(estim_params_.param_vals) == 0
data2json.param_vals = estim_params_.param_vals;
end
%prior density
if isfield(oo_,'prior_density')==1
data2json.prior_density=oo_.prior_density;
end
end
%posterior
if isfield(oo_,'posterior_density')==1
data2json.posterior_density=oo_.posterior_density;
end
if isfield(estim_params_, 'var_exo') == 1
if isfield(oo_,'posterior_mode')==1
data2json.posterior_mode=oo_.posterior_mode;
data2json.posterior_std=oo_.posterior_std_at_mode;
data2json.posterior_mean=oo_.posterior_mean;
data2json.posterior_hpdinf=oo_.posterior_hpdinf;
data2json.posterior_hpdsup=oo_.posterior_hpdsup;
end
if isempty(estim_params_.var_exo) == 0
data2json.var_exo = estim_params_.var_exo;
end
end
savejson('',data2json,'estimout.JSON');
return;
end
if isfield(estim_params_, 'corrx') == 1
if isempty(estim_params_.corrx) == 0
data2json.corrx = estim_params_.corrx;
end
end
%prior density
if isfield(oo_, 'prior_density') == 1
data2json.prior_density = oo_.prior_density;
end
%posterior
if isfield(oo_, 'posterior_density') == 1
data2json.posterior_density = oo_.posterior_density;
end
if isfield(oo_, 'posterior_mode') == 1
data2json.posterior_mode = oo_.posterior_mode;
data2json.posterior_std = oo_.posterior_std_at_mode;
data2json.posterior_mean = oo_.posterior_mean;
data2json.posterior_hpdinf = oo_.posterior_hpdinf;
data2json.posterior_hpdsup = oo_.posterior_hpdsup;
end
%bayesian_irfs
if isfield(oo_, 'PosteriorIRF') == 1
irfnames = fieldnames(oo_.PosteriorIRF.dsge.Mean);
for jj = 1:numel(fieldnames(oo_.PosteriorIRF.dsge.Mean))
data2json.bayesianirfmean.(strtrim(char(irfnames(jj)))) = oo_.PosteriorIRF.dsge.Mean.(irfnames{jj});
data2json.bayesianirfhpdinf.(strtrim(char(irfnames(jj)))) = oo_.PosteriorIRF.dsge.HPDinf.(irfnames{jj});
data2json.bayesianirfhpdsup.(strtrim(char(irfnames(jj)))) = oo_.PosteriorIRF.dsge.HPDsup.(irfnames{jj});
end
end
end
%smoother: some identical oo fields are used in a different way whether the option "smoother" is active or not. We need to take this into account
if isfield(oo_.SmoothedVariables, 'deciles') == 1
%when the "smoother" option is active
smoothedvarnames = fieldnames(oo_.SmoothedVariables.Mean);
for jj = 1:numel(fieldnames(oo_.SmoothedVariables.Mean))
data2json.smoothedvarmean.(strtrim(char(smoothedvarnames(jj)))) = oo_.SmoothedVariables.Mean.(smoothedvarnames{jj});
data2json.smoothedvardeciles.(strtrim(char(smoothedvarnames(jj)))) = oo_.SmoothedVariables.deciles.(smoothedvarnames{jj});
data2json.constantmean.(strtrim(char(smoothedvarnames(jj)))) = oo_.Constant.Mean.(smoothedvarnames{jj});
data2json.constantdeciles.(strtrim(char(smoothedvarnames(jj)))) = oo_.Constant.deciles.(smoothedvarnames{jj});