# Nash-Ramsey Toolbox — Dynare Migration Notes

Covers changes required to move from Dynare 4 through Dynare 6 to Dynare 7, plus
toolbox infrastructure updates made alongside the Dynare 7 port.

---

## Dynare 4 → 6

### `*_steadystate.m` — function signature and parameter handling

**Old (Dynare 4):**
```matlab
function [ys,check] = MODELNAME_steadystate(junk,ys)
global M_
% ...
nparams = size(M_.param_names,1);
for icount = 1:nparams
    eval(['M_.params(icount) = ',M_.param_names(icount,:),';'])
end
for i_indx = 1:nvars
    eval(['ys(i_indx)=',M_.endo_names(i_indx,:),';'])
end
```

**New (Dynare 6):**
```matlab
function [ys,params,check] = MODELNAME_steadystate(ys,exo,M_,options_)
% no global declarations
% ...
nparams = size(M_.param_names,1);
params = nan(nparams,1);
for icount = 1:nparams
    eval(['params(icount) = ',M_.param_names{icount},';'])
end
for i_indx = 1:nvars
    eval(['ys(i_indx)=',M_.endo_names{i_indx},';'])
end
```

Key changes:
1. Add `params` output; replace `junk` input with `exo`; add `M_` and `options_` inputs.
2. Remove all `global` declarations (`global M_`, `global M_ params paramlist ...`).
3. Write to the local `params` vector, not to `M_.params`.
4. Use `M_.param_names{icount}` and `M_.endo_names{i_indx}` (cell indexing).

For Nash/Ramsey steadystate files that call `fsolve`, the static call also changes:
```matlab
% Dynare 4
optys = fsolve(@(ys) MODELNAME_static(ys,exog,M_.params), ys, options);

% Dynare 6
ys = fsolve(@(ys) MODELNAME.static(ys,exog,params), ys, options);
```
Pass the local `params` variable, not `M_.params` (which is no longer populated at
steadystate-function call time). See the Dynare 6→7 section for the further change
to `static_resid` required in Dynare 7.

**Files affected in this toolbox:**
- All `*_steadystate.m` files across `BBCDL_model_excl2ndorder/`, `BBCDL_model_2ndorder/`,
  `GK_model/`, `GK_model_with_altruism/`.
- Templates: `toolbox_programs/template_for_ramsey_steadystate.m`,
  `toolbox_programs/template_for_nash_steadystate.m`.
- Inline code in `toolbox_programs/add_shadow_economy.m`.

---

### `*_ss_defs.m` — `M_.endo_names` indexing

`M_.endo_names` changed from a 2-D char array to a cell array in Dynare 6. Loops
building `_ss` variables must use curly-brace indexing:

```matlab
% Dynare 4
varstring = deblank(M_.endo_names(i_indx,:));
if isempty(strfind(varstring,'mult')) & ...

% Dynare 6
varstring = M_.endo_names{i_indx};
if isempty(strfind(varstring,'mult')) && ...
```

Using `(i_indx,:)` in Dynare 6 returns a 1×1 cell; `isempty` on a non-empty cell is
always `false`, so **no `_ss` variables are created**, causing downstream errors.
Same change applies to `M_.param_names` wherever it is indexed by row.

---

### `.mod` files — parameter seeding for preprocessor normalization

Dynare 6's "Normalizing the static model" step evaluates the Jacobian numerically
before the steadystate function runs. Parameters not assigned in the `.mod` file
default to **1**, which makes expressions like `log(exp(c1c) - 1*exp(c1clag))`
evaluate to `log(0)`.

**Fix:** add explicit parameter assignments in the `.mod` file immediately before
`model;`, after all declaration blocks (`var`, `varexo`, `parameters`):

```dynare
// Seed values for Dynare 6 preprocessor normalization.
// Overwritten at runtime by the steadystate function.
c1kappa = 0.5;
c1beta  = 0.995024875621891;
// ... all parameters ...

model;
```

Generate values from the existing paramfile:
```matlab
MODELNAME_paramfile;
fprintf('%s = %.15g;\n', 'c1kappa', c1kappa);
% etc.
```

**Placement rule:** the seed block must come after ALL `var`/`varexo`/`parameters`
blocks. Placing it between two `var` blocks causes the preprocessor to ignore it.

---

### Ramsey `.mod` files — `initval` block for Lagrange multipliers

Parameter seeding fixes log(0) caused by bad parameter defaults, but Ramsey models
have a second source: Lagrange multipliers default to zero, making the static
Jacobian structurally zero. Nash models do not have this problem because their FOCs
involve the instrument directly (non-zero Jacobian regardless of multiplier values).

**Fix:** add an `initval` block after `shocks;` and before `steady;`:

```dynare
initval;
lmult1 = 1e-4;
lmult2 = 1e-4;
// ... all lmult variables ...
end;

steady;
```

The `_steadystate.m` function overwrites these with the correct values at runtime.

Note: `model(block);` (block decomposition) did **not** resolve this for
`BCGmodelcomp_ramsey_c1dpd_c2dpd`; both the seed block and the `initval` block are
required.

---

### Run scripts (`runsim_*.m`) — `endo_names` and `param_names`

```matlab
% Dynare 4
deblank(Mbase_.endo_names(i,:))
Mbase_.param_names(i,:)

% Dynare 6
Mbase_.endo_names{i}
Mbase_.param_names{i}
```

Also: scripts that unpacked results from the global `M_` (instead of the `Mbase_`
output of `solve_one_constraint`) were corrected to use `Mbase_`.

---

### ZLB / alternative-regime `.mod` files — constant assignment in model block

**Dynare 6 change:** assigning an endogenous variable to a literal constant in the
model block (e.g. `r = 0`) causes the preprocessor to substitute that value through
all time periods including lags. If `r(-1)` appears elsewhere, those references are
silently replaced with `0`, removing `r` from `M_.lead_lag_incidence` and breaking
OccBin's lag-structure consistency check.

**Fix:** replace `r = 0` with `r = ZEROP` where `ZEROP` is a parameter declared in
the `parameters` block and set to `0` in the param file.

Files affected: `model_dnk/dnk_zlb.mod`, `model_newkeynesian/fv_zlb.mod`,
`model_cgg/cgg_zlb.mod`, `model_smetswouters/usmodelzlb.mod`.

---

## Dynare 6 → 7

### Version detection

Do **not** use `isfield(M_, 'dynamic_g1_sparse_rowval')` — this field exists in both
Dynare 6.5 and 7, so it gives false positives. Use:

```matlab
ver_parts = strsplit(dynare_version(), '.');
dynare_major = str2double(ver_parts{1});
if dynare_major >= 7
    % Dynare 7+ path
else
    % Dynare 6 and earlier path
end
```

---

### `static()` replaced by `static_resid()` in steadystate files

| | Dynare 6 | Dynare 7 |
|---|---|---|
| Residual + Jacobian | `[resid, g1] = fname.static(y, x, params)` | not available |
| Residual only | — | `[resid, T_order, T] = fname.static_resid(y, x, params)` |
| Jacobian only | — | `[g1, T_order, T] = fname.static_g1(y, x, params, rowval, colval, colptr)` (sparse) |

`fname.static` no longer exists in Dynare 7. All `*_steadystate.m` files that use
`fsolve` with `'Jacobian','on'` must switch to `static_resid` (numerical Jacobian):

```matlab
ver_parts = strsplit(dynare_version(), '.');
dynare_major = str2double(ver_parts{1});

algo = char('active-set','trust-region-reflective','interior-point', ...
            'interior-point-convex','levenberg-marquardt', ...
            'trust-region-dogleg','lm-line-search','sqp');

if dynare_major >= 7
    options = optimset('Display','Iter','MaxFunEvals',1e4,'MaxIter',1e4, ...
                       'TolFun',1e-9,'TolX',1e-9,'Algorithm',algo(5,:));
    ys = fsolve(@(ys) MODELNAME.static_resid(ys,exog,params), ys, options);
else
    options = optimset('Display','Iter','Jacobian','on','MaxFunEvals',1e4, ...
                       'MaxIter',1e4,'TolFun',1e-9,'TolX',1e-9,'Algorithm',algo(5,:));
    ys = fsolve(@(ys) MODELNAME.static(ys,exog,params), ys, options);
end
```

**Files updated:**
- `toolbox_programs/template_for_ramsey_steadystate.m` (fix is in the template;
  all future `convertmodfiles` output is automatically correct)
- `toolbox_programs/template_for_nash_steadystate.m` (same)
- `toolbox_programs/add_shadow_economy.m` (shadow steadystate written inline;
  also fixed a pre-existing bug where `M_.params` was passed instead of local `params`)
- All 40 pre-existing `*_steadystate.m` files in `BBCDL_model_excl2ndorder/`,
  `BBCDL_model_2ndorder/`, `GK_model/`, `GK_model_with_altruism/` — batch-patched
- `shadow_BBCDLmodelcomp_ramsey_c1pid_c2pid_steadystate.m` and
  `shadow_BBCDLmodelcomp_nash_c1pid_c2pid_steadystate.m` — patched manually
  (these files had trailing spaces on every line, which caused the regex to miss them)

**Old Dynare 4-style shadow steadystate files** had two additional problems:
1. `global params ...` — remove entirely
2. Parameter loop wrote `M_.params(icount)` then did `params = M_.params` — replace
   with `params = nan(nparams,1)` and write directly to `params(icount)`

---

### `dynamic()` split into `dynamic_resid()` and `dynamic_g1()` in `get_deriv.m`

| | Dynare 6 | Dynare 7 |
|---|---|---|
| Residual | `[resid, g1] = fname.dynamic(y, x, params, ys_, it_)` | `[resid, T_order, T] = fname.dynamic_resid(y, x, params, ys_)` |
| Jacobian | returned by `dynamic` above (dense) | `[g1, T_order, T] = fname.dynamic_g1(y, x, params, ys_, rowval, colval, colptr)` (sparse) |

**y vector size:** Dynare 6 uses a compact y vector (length = non-zero entries of
`lead_lag_incidence`). Dynare 7 requires the full `3*nvars` vector
`[ys_; ys_; ys_]`.

**Jacobian column layout in Dynare 7:** columns `1..nvars` = lags,
`nvars+1..2*nvars` = current, `2*nvars+1..3*nvars` = leads,
`3*nvars+1..end` = exogenous shocks.

**Solution — dispatcher pattern:**
- `toolkit_files/get_deriv.m` — thin dispatcher, calls `get_deriv_d6` or `get_deriv_d7`
- `toolkit_files/get_deriv_d6.m` — original Dynare 6 code, unchanged
- `toolkit_files/get_deriv_d7.m` — Dynare 7 code using full y vector and sparse g1

---

### `setpathdynare` scripts

Each example directory needs `setpathdynare7_0.m` alongside `setpathdynare6_5.m`.
Root-level versions of both files were created; local copies were placed in example
directories that have their own `setpathdynare6_5.m`.

Path detection checks for `7.0-arm64` before `7.0` on macOS (Apple Silicon).

After running `setup_toolbox`, all `setpathdynare*.m` files at the root have
hardcoded absolute paths for Dynare, `utilities/`, and `toolbox_programs/`, so
they can be copied into any subdirectory and work without modification.

---

### MATLAB batch mode — known issue

MATLAB R2026a segfaults when running Dynare 7 in `-batch` mode (non-interactive).
The same code runs correctly in interactive mode. This is a MATLAB/Dynare interaction
bug, not a toolbox code issue. Run models and tests interactively.

---

### Verified compatibility

`BBCDL_figure3.m` runs successfully on both Dynare 6.5 and Dynare 7.0
(MATLAB R2026a, macOS ARM64).

`convertmodfiles('BBCDLmodelcomp','ramsey','c1pid','c2pid','c1beta',1)` and the
generated `BBCDLmodelcomp_ramsey_c1pid_c2pid` model run successfully under Dynare 7.0.

---

## Toolbox infrastructure changes (made alongside the Dynare 7 port)

### Cleanup system

**Before:** each example subdirectory had its own `call_cleanup.m` with hardcoded
model-name prefixes and Unix shell commands (`rm -rf`, `find`).

**After:**
- `toolbox_programs/cleanup_dynare_dir(target_dir, ...)` — shared function; removes
  `*.log`, `*.eps`, `*.asv`, `*.dvi`, `*.aux`, `*.fls`, `*.gz`, `*.*latexmk`,
  `*_static.m`, `*_dynamic.m`, `*_set_auxiliary_variables.m`, all `*.mat`/`*.pdf`/`*.tex`
  not in a preserve list, all `+*` package directories, and plain output directories
  whose name matches a `*.mod` stem. Default preserved files:
  `extra_parameters.mat`, `readme.pdf`, `readme.tex`.
- `call_cleanup.m` (toolbox root) — loops over all five example subdirectories and
  calls `cleanup_dynare_dir` on each.
- Each subdirectory `call_cleanup.m` replaced with a three-line one-liner that
  adds `toolbox_programs` to the path and calls `cleanup_dynare_dir(this_dir)`.

`cleanup_dynare_dir` does **not** delete `.mod` files or toolbox source `.m` files
(only `*_static.m`, `*_dynamic.m`, `*_set_auxiliary_variables.m` are removed).
Files generated by `convertmodfiles` (`*_paramfile.m`, `*_steadystate.m`,
`*_ss_defs.m`, `*_lmss.m`, `*_endoss.m`) are therefore preserved by cleanup.

---

### Test suite (`run_tests.m`)

`run_tests.m` at the toolbox root verifies compatibility with the currently active
Dynare version. Call it after running `setpathdynare6_5` or `setpathdynare7_0`.

```matlab
run_tests          % auto-detects version via dynare_version()
run_tests('7.0')   % same, with explicit version label in results
```

**Tests run:**
1. **convertmodfiles round-trip** — runs `convertmodfiles('BBCDLmodelcomp','ramsey',
   'c1pid','c2pid','c1beta',1)` in `tests/test_convertmodfiles/`, then runs
   `dynare BBCDLmodelcomp_ramsey_c1pid_c2pid nostrict noclearall nograph`.
2. **Example models** — runs `dynare MODFILE nostrict noclearall nograph` for one
   Ramsey and one Nash model from each of the five example directories:
   - `BBCDL_model_excl2ndorder`: `BBCDLmodelcomp_ramsey_c1pid_c2pid`,
     `BBCDLmodelcomp_nash_c1pid_c2pid`
   - `BBCDL_model_2ndorder`: `shadow_BBCDLmodelcomp_ramsey_c1pid_c2pid`,
     `shadow_BBCDLmodelcomp_nash_c1pid_c2pid`
   - `GK_model`: `rbcb_monprud_ramsey_infl_bt`, `rbcb_monprud_nash_infl_bt`
   - `GK_model_with_altruism`: `rbcb_monprud_ramsey_infl_bt`,
     `rbcb_monprud_nash_infl_bt`
   - `LQ_BBCDL_model`: `LQBBCDL`

**Cleanup:** after each example model test, only the `+MODELNAME/` package directory
and `MODELNAME.log` are removed — pre-existing `.mat` result files in
`BBCDL_model_excl2ndorder/` are preserved. The `tests/test_convertmodfiles/`
directory is cleaned with `cleanup_dynare_dir` (which preserves `.mod` source files
and `convertmodfiles`-generated support files needed by the subsequent dynare run).

Results are saved as timestamped `.mat` files in `tests/test_results/`.

---

### `setup_toolbox.m`

Run once after installing or moving the toolbox:

```matlab
run setup_toolbox   % from the toolbox root, or any directory
```

What it does:
- Detects Dynare 6.5 and 7.0 in standard installation paths (Mac ARM64/Intel,
  Windows, Linux).
- Embeds the detected Dynare path into each `setpathdynare*.m` file at the root
  (replacing `dynare_path = ''` with the actual path).
- Embeds absolute paths for `utilities/` and `toolbox_programs/` in those same files
  (replacing `../../utilities` etc. with full paths).
- **Warns** (does not error) if one Dynare version is not installed.
- **Errors** only if neither Dynare 6.5 nor Dynare 7.0 is found.
- Never asks for user input.
