diff --git a/CHANGELOG.md b/CHANGELOG.md index cbae39093..623ef2cec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.15.1] - 2026-01-19 12:00:00 + +### Added + +- A new parameter `c_min` to the `Parameters` class that allows the user to specify minium consumption amounts by consumption good. See PR [#1085](https://github.com/PSLmodels/OG-Core/pull/1085) + ## [0.15.0] - 2025-12-03 12:00:00 ### Added diff --git a/docs/book/content/intro/parameters.md b/docs/book/content/intro/parameters.md index ca94628d8..442b9f8f3 100644 --- a/docs/book/content/intro/parameters.md +++ b/docs/book/content/intro/parameters.md @@ -43,6 +43,14 @@ _Valid Range:_ min = 0.0 and max = 1.0 _Out-of-Range Action:_ error +#### `c_min` +_Description:_ Minimum consumption levels for each good in the composite consumption good. +_Notes:_ Enter this value in model units. +_Value Type:_ float +_Valid Range:_ min = 0.0 and max = 100.0 +_Out-of-Range Action:_ error + + #### `chi_b` _Description:_ Household utility weight on bequests. _Value Type:_ float @@ -114,6 +122,7 @@ _Out-of-Range Action:_ error #### `use_zeta` _Description:_ Indicator variable for whether or not to use the zeta matrix to distribute bequests. _Value Type:_ bool +_Valid Choices:_[True, False] ### Model Dimensions @@ -395,6 +404,12 @@ _Valid Range:_ min = 0.0 and max = 10.0 _Out-of-Range Action:_ error +#### `baseline_theta` +_Description:_ Flag for use in reform simulations to keep Social Security system replacement rate constant between baseline and reform runs. +_Value Type:_ bool +_Valid Choices:_[True, False] + + ### Spending #### `alpha_T` @@ -586,21 +601,25 @@ _Out-of-Range Action:_ error #### `constant_rates` _Description:_ Flag to use linear tax functions. _Value Type:_ bool +_Valid Choices:_[True, False] #### `zero_taxes` _Description:_ Flag to run model without any individual income taxes. _Value Type:_ bool +_Valid Choices:_[True, False] #### `analytical_mtrs` _Description:_ Flag to use analytically derived marginal tax rates in tax functions. _Value Type:_ bool +_Valid Choices:_[True, False] #### `age_specific` _Description:_ Flag to use analytically derived marginal tax rates in tax functions. _Value Type:_ bool +_Valid Choices:_[True, False] #### `tax_func_type` @@ -730,6 +749,7 @@ _Out-of-Range Action:_ error _Description:_ Use constant demographics. _Notes:_ This boolean allows one to use empirical mortality rates, but keep the population distribution constant across periods. Note that immigration is shut off when this is true. _Value Type:_ bool +_Valid Choices:_[True, False] #### `omega` @@ -842,6 +862,7 @@ _Out-of-Range Action:_ error #### `reform_use_baseline_solution` _Description:_ Whether or not the baseline SS solution is used for starting values when solving the reform. _Value Type:_ bool +_Valid Choices:_[True, False] #### `initial_guess_r_SS` diff --git a/docs/make_params.py b/docs/make_params.py index f0d9c5c9e..05761a204 100644 --- a/docs/make_params.py +++ b/docs/make_params.py @@ -5,7 +5,6 @@ import os import sys - CURDIR_PATH = os.path.abspath(os.path.dirname(__file__)) OGCORE_PATH = os.path.join(CURDIR_PATH, "..", "ogcore") TEMPLATE_PATH = os.path.join(CURDIR_PATH, "templates") diff --git a/docs/make_vars.py b/docs/make_vars.py index a73402699..c6c7e79d0 100644 --- a/docs/make_vars.py +++ b/docs/make_vars.py @@ -5,7 +5,6 @@ import os import sys - CURDIR_PATH = os.path.abspath(os.path.dirname(__file__)) OGCORE_PATH = os.path.join(CURDIR_PATH, "..", "ogcore") TEMPLATE_PATH = os.path.join(CURDIR_PATH, "templates") diff --git a/examples/multi_industry_example.py b/examples/multi_industry_example.py index 9c58d1e1c..1c4975515 100644 --- a/examples/multi_industry_example.py +++ b/examples/multi_industry_example.py @@ -19,7 +19,6 @@ import matplotlib.pyplot as plt import ogcore - ogcore.TPI.ENFORCE_SOLUTION_CHECKS = False # Use a custom matplotlib style file for plots diff --git a/ogcore/SS.py b/ogcore/SS.py index c29be159e..9287d822c 100644 --- a/ogcore/SS.py +++ b/ogcore/SS.py @@ -39,7 +39,7 @@ def euler_equation_solver(guesses, *args): Args: guesses (Numpy array): initial guesses for b and n, length 2S - args (tuple): tuple of arguments (r, w, p_tilde, bq, TR, factor, j, p) + args (tuple): tuple of arguments (r, w, p_tilde, p_i, bq, TR, factor, j, p) r (scalar): real interest rate w (scalar): real wage rate p_tilde (scalar): composite good price @@ -54,7 +54,7 @@ def euler_equation_solver(guesses, *args): errros (Numpy array): errors from FOCs, length 2S """ - (r, w, p_tilde, bq, rm, tr, ubi, factor, j, p) = args + r, w, p_tilde, p_i, bq, rm, tr, ubi, factor, j, p = args b_guess = np.array(guesses[: p.S]) n_guess = np.array(guesses[p.S :]) @@ -67,6 +67,7 @@ def euler_equation_solver(guesses, *args): r, w, p_tilde, + p_i, b_s, b_splus1, n_guess, @@ -88,6 +89,7 @@ def euler_equation_solver(guesses, *args): r, w, p_tilde, + p_i, b_s, b_splus1, n_guess, @@ -143,6 +145,7 @@ def euler_equation_solver(guesses, *args): r, w, p_tilde, + p_i, b_s, b_splus1, n_guess, @@ -150,6 +153,7 @@ def euler_equation_solver(guesses, *args): rm, taxes, p.e[-1, :, j], + p.tau_c[-1, :], p, ) mask6 = cons < 0 @@ -164,6 +168,7 @@ def solve_for_j( r_p, w, p_tilde, + p_i, bq_j, rm_j, tr_j, @@ -180,6 +185,7 @@ def solve_for_j( r_p (scalar): return on household investment portfolio w (scalar): real wage rate p_tilde (scalar): composite good price + p_i (Numpy array): prices for consumption good i bq_j (Numpy array): bequest amounts by age, length S rm_j (Numpy array): remittance amounts by age, length S tr_j (Numpy array): government transfer amount by age, length S @@ -200,6 +206,7 @@ def solve_for_j( r_p, w, p_tilde, + p_i, bq_j, rm_j, tr_j, @@ -314,6 +321,7 @@ def inner_loop(outer_loop_vars, p, client): r_p, w, p_tilde, + p_i, bq[:, j], rm[:, j], tr[:, j], @@ -346,6 +354,7 @@ def inner_loop(outer_loop_vars, p, client): r_p, w, p_tilde, + p_i, bq[:, j], rm[:, j], tr[:, j], @@ -365,6 +374,7 @@ def inner_loop(outer_loop_vars, p, client): r_p, w, p_tilde, + p_i, bq[:, j], rm[:, j], tr[:, j], @@ -416,6 +426,7 @@ def inner_loop(outer_loop_vars, p, client): r_p, w, p_tilde, + p_i, b_s, b_splus1, nssmat, @@ -423,9 +434,12 @@ def inner_loop(outer_loop_vars, p, client): rm, net_tax, np.squeeze(p.e[-1, :, :]), + p.tau_c[-1, :], p, ) - c_i = household.get_ci(c_s, p_i, p_tilde, p.tau_c[-1, :], p.alpha_c) + c_i = household.get_ci( + c_s, p_i, p_tilde, p.tau_c[-1, :], p.alpha_c, p.c_min + ) L = aggr.get_L(nssmat, p, "SS") B = aggr.get_B(bssmat, p, "SS", False) @@ -551,6 +565,7 @@ def inner_loop(outer_loop_vars, p, client): new_r_p, new_w, new_p_tilde, + new_p_i, b_s, bssmat, nssmat, @@ -558,6 +573,7 @@ def inner_loop(outer_loop_vars, p, client): new_rm, taxss, np.squeeze(p.e[-1, :, :]), + p.tau_c[-1, :], p, ) ( @@ -990,6 +1006,7 @@ def SS_solver( r_p_ss, wss, p_tilde_ss, + p_i_ss, bssmat_s, bssmat_splus1, nssmat, @@ -997,6 +1014,7 @@ def SS_solver( rmssmat, taxss, np.squeeze(p.e[-1, :, :]), + p.tau_c[-1, :], p, ) c_i = household.get_ci( @@ -1005,6 +1023,7 @@ def SS_solver( p_tilde_ss, p.tau_c[-1, :], p.alpha_c, + p.c_min, "SS", ) sales_tax_ss = tax.cons_tax_liab(c_i, p_i_ss, p, "SS") @@ -1013,7 +1032,7 @@ def SS_solver( ) Css = aggr.get_C(cssmat, p, "SS") c_i_ss_mat = household.get_ci( - cssmat, p_i_ss, p_tilde_ss, p.tau_c[-1, :], p.alpha_c + cssmat, p_i_ss, p_tilde_ss, p.tau_c[-1, :], p.alpha_c, p.c_min ) C_vec_ss = np.zeros(p.I) for i_ind in range( @@ -1235,7 +1254,7 @@ def SS_fsolve(guesses, *args): implied outer loop variables """ - (bssmat, nssmat, TR_ss, Ig_baseline, factor_ss, p, client) = args + bssmat, nssmat, TR_ss, Ig_baseline, factor_ss, p, client = args # Rename the inputs r_p = guesses[0] diff --git a/ogcore/TPI.py b/ogcore/TPI.py index 42a34582d..e6e448a56 100644 --- a/ogcore/TPI.py +++ b/ogcore/TPI.py @@ -23,7 +23,6 @@ import warnings import logging - if not SHOW_RUNTIME: warnings.simplefilter("ignore", RuntimeWarning) @@ -129,7 +128,20 @@ def get_initial_SS_values(p): def firstdoughnutring( - guesses, r, w, p_tilde, bq, rm, tr, theta, factor, ubi, j, initial_b, p + guesses, + r, + w, + p_tilde, + p_i, + bq, + rm, + tr, + theta, + factor, + ubi, + j, + initial_b, + p, ): """ Solves the first entries of the upper triangle of the twist doughnut. This @@ -141,6 +153,7 @@ def firstdoughnutring( r (scalar): real interest rate w (scalar): real wage rate p_tilde (scalar): composite good price + p_i (Numpy array): output goods prices bq (scalar): bequest amounts by age rm (scalar): remittance amounts by age tr (scalar): government transfer amount @@ -166,6 +179,7 @@ def firstdoughnutring( np.array([r]), np.array([w]), np.array([p_tilde]), + np.array([p_i]), b_s, np.array([b_splus1]), np.array([n]), @@ -188,6 +202,7 @@ def firstdoughnutring( np.array([r]), np.array([w]), np.array([p_tilde]), + np.array([p_i]), b_s, b_splus1, np.array([n]), @@ -219,6 +234,7 @@ def twist_doughnut( r, w, p_tilde, + p_i, bq, rm, tr, @@ -244,6 +260,7 @@ def twist_doughnut( r (Numpy array): real interest rate w (Numpy array): real wage rate p_tilde (Numpy array): composite good price + p_i (Numpy array): output goods prices bq (Numpy array): bequest amounts by age, length s rm (Numpy array): remittance amounts by age, length s tr (Numpy array): government transfer amount @@ -281,6 +298,7 @@ def twist_doughnut( w_s = w[t : t + length] r_s = r[t : t + length] p_tilde_s = p_tilde[t : t + length] + p_i_s = p_i[t : t + length, :] n_s = n_guess chi_n_s = np.diag(p.chi_n[t : t + p.S, :], max(p.S - length, 0)) rho_s = np.diag(p.rho[t : t + p.S, :], max(p.S - length, 0)) @@ -289,6 +307,7 @@ def twist_doughnut( r_s, w_s, p_tilde_s, + p_i_s, b_s, b_splus1, n_s, @@ -311,6 +330,7 @@ def twist_doughnut( r_s, w_s, p_tilde_s, + p_i_s, b_s, b_splus1, n_s, @@ -379,7 +399,7 @@ def inner_loop(guesses, outer_loop_vars, initial_values, ubi, j, ind, p): * n_mat (Numpy array): labor supply amounts, size = TxS """ - (K0, b_sinit, b_splus1init, factor, initial_b, initial_n) = initial_values + K0, b_sinit, b_splus1init, factor, initial_b, initial_n = initial_values guesses_b, guesses_n = guesses r_p, r, w, p_m, BQ, RM, TR, theta = outer_loop_vars @@ -408,6 +428,7 @@ def inner_loop(guesses, outer_loop_vars, initial_values, ubi, j, ind, p): r_p[0], w[0], p_tilde[0], + p_i[0, :], bq[0, -1, j], rm[0, -1, j], tr[0, -1, j], @@ -463,6 +484,7 @@ def inner_loop(guesses, outer_loop_vars, initial_values, ubi, j, ind, p): r_p, w, p_tilde, + p_i, bq_to_use, rm_to_use, tr_to_use, @@ -518,6 +540,7 @@ def inner_loop(guesses, outer_loop_vars, initial_values, ubi, j, ind, p): r_p, w, p_tilde, + p_i, bq_to_use, rm_to_use, tr_to_use, @@ -561,7 +584,7 @@ def run_TPI(p, client=None): """ # unpack tuples of parameters initial_values, ss_vars, theta, baseline_values = get_initial_SS_values(p) - (B0, b_sinit, b_splus1init, factor, initial_b, initial_n) = initial_values + B0, b_sinit, b_splus1init, factor, initial_b, initial_n = initial_values ( Ybaseline, TRbaseline, @@ -916,11 +939,13 @@ def run_TPI(p, client=None): ) r_p_path = utils.to_timepath_shape(r_p) p_tilde_path = utils.to_timepath_shape(p_tilde) + wpath = utils.to_timepath_shape(w) c_mat = household.get_cons( r_p_path[: p.T, :, :], wpath[: p.T, :, :], p_tilde_path[: p.T, :, :], + p_i[: p.T, :], bmat_s, bmat_splus1, n_mat[: p.T, :, :], @@ -928,7 +953,9 @@ def run_TPI(p, client=None): rmmat[: p.T, :, :], tax_mat, p.e, + p.tau_c[: p.T, :], p, + method="TPI", ) c_i = household.get_ci( c_mat[: p.T, :, :], @@ -936,6 +963,7 @@ def run_TPI(p, client=None): p_tilde[: p.T], p.tau_c[: p.T, :], p.alpha_c, + p.c_min, "TPI", ) sales_tax_mat = tax.cons_tax_liab(c_i, p_i, p, "TPI") @@ -947,6 +975,7 @@ def run_TPI(p, client=None): p_tilde[: p.T], p.tau_c[: p.T, :], p.alpha_c, + p.c_min, "TPI", ) y_before_tax_mat = household.get_y( diff --git a/ogcore/__init__.py b/ogcore/__init__.py index c76863edf..c5a59ca58 100644 --- a/ogcore/__init__.py +++ b/ogcore/__init__.py @@ -20,4 +20,4 @@ from ogcore.txfunc import * from ogcore.utils import * -__version__ = "0.15.0" +__version__ = "0.15.1" diff --git a/ogcore/default_parameters.json b/ogcore/default_parameters.json index 2922d15f3..9d8f1ee69 100644 --- a/ogcore/default_parameters.json +++ b/ogcore/default_parameters.json @@ -404,6 +404,26 @@ } } }, + "c_min": { + "title": "Minimum consumption levels for each good in the composite consumption good", + "description": "Minimum consumption levels for each good in the composite consumption good.", + "section_1": "Household Parameters", + "section_2": "Behavioral Assumptions", + "notes": "Enter this value in model units.", + "type": "float", + "number_dims": 1, + "value": [ + { + "value": [0.0] + } + ], + "validators": { + "range": { + "min": 0.0, + "max": 100.0 + } + } + }, "gamma": { "title": "Capital's share of output in firm production function", "description": "Capital's share of output in firm production function.", diff --git a/ogcore/elliptical_u_est.py b/ogcore/elliptical_u_est.py index ce8b4c019..ac1178eaf 100644 --- a/ogcore/elliptical_u_est.py +++ b/ogcore/elliptical_u_est.py @@ -188,6 +188,6 @@ def estimation(frisch, l_tilde): bounds=bnds_MU, tol=1e-15, ) - (b_MU_til, upsilon_MU_til) = ellipse_MU_params_til.x + b_MU_til, upsilon_MU_til = ellipse_MU_params_til.x return b_MU_til, upsilon_MU_til diff --git a/ogcore/household.py b/ogcore/household.py index bc21eb140..b68a61270 100644 --- a/ogcore/household.py +++ b/ogcore/household.py @@ -284,7 +284,22 @@ def get_rm(RM, j, p, method): return rm -def get_cons(r_p, w, p_tilde, b, b_splus1, n, bq, rm, net_tax, e, p): +def get_cons( + r_p, + w, + p_tilde, + p_i, + b, + b_splus1, + n, + bq, + rm, + net_tax, + e, + tau_c, + p, + method="SS", +): r""" Calculate household composite consumption. @@ -302,6 +317,7 @@ def get_cons(r_p, w, p_tilde, b, b_splus1, n, bq, rm, net_tax, e, p): r_p (array_like): the real interest rate w (array_like): the real wage rate p_tilde (array_like): the ratio of real GDP to nominal GDP + p_i (Numpy array): prices for consumption good i b (Numpy array): household savings b_splus1 (Numpy array): household savings one period ahead n (Numpy array): household labor supply @@ -309,12 +325,32 @@ def get_cons(r_p, w, p_tilde, b, b_splus1, n, bq, rm, net_tax, e, p): rm (Numpy array): household remittances received net_tax (Numpy array): household net taxes paid e (Numpy array): effective labor units + tau_c (Numpy array): consumption tax rates p (OG-Core Specifications object): model parameters + method (str): adjusts calculation dimensions based on 'SS' or 'TPI' Returns: cons (Numpy array): household consumption """ + if method == "SS": + min_cons_expenditure = np.sum((1 + tau_c) * p_i * p.c_min) + elif method == "TPI_scalar": + min_cons_expenditure = np.sum((1 + tau_c) * p_i * p.c_min) + else: # TPI case + min_cons_expenditure = np.sum( + (1 + tau_c) * p_i * p.c_min.reshape((1, p.I)), axis=1 + ) + length = r_p.shape[0] + # reshape if needed for broadcasting + n_dims = np.ndim(bq) + if n_dims == 1: + min_cons_expenditure = min_cons_expenditure.reshape((length,)) + elif n_dims == 2: + min_cons_expenditure = min_cons_expenditure.reshape((length, 1)) + else: + min_cons_expenditure = min_cons_expenditure.reshape((length, 1, 1)) + cons = ( (1 + r_p) * b + w * e * n @@ -322,12 +358,13 @@ def get_cons(r_p, w, p_tilde, b, b_splus1, n, bq, rm, net_tax, e, p): + rm - net_tax - b_splus1 * np.exp(p.g_y) + - min_cons_expenditure ) / p_tilde return cons -def get_ci(c_s, p_i, p_tilde, tau_c, alpha_c, method="SS"): +def get_ci(c_s, p_i, p_tilde, tau_c, alpha_c, c_min, method="SS"): r""" Compute consumption of good i given amount of composite consumption and prices. @@ -341,6 +378,7 @@ def get_ci(c_s, p_i, p_tilde, tau_c, alpha_c, method="SS"): p_tilde (array_like): composite good price tau_c (array_like): consumption tax rate alpha_c (array_like): consumption share parameters + c_min (array_like): minimum consumption levels for each good i method (str): adjusts calculation dimensions based on 'SS' or 'TPI' Returns: @@ -352,10 +390,11 @@ def get_ci(c_s, p_i, p_tilde, tau_c, alpha_c, method="SS"): J = c_s.shape[1] tau_c = tau_c.reshape(I, 1, 1) alpha_c = alpha_c.reshape(I, 1, 1) + c_min = c_min.reshape(I, 1, 1) p_tilde.reshape(1, 1, 1) p_i = p_i.reshape(I, 1, 1) c_s = c_s.reshape(1, S, J) - c_si = alpha_c * (((1 + tau_c) * p_i) / p_tilde) ** (-1) * c_s + c_si = alpha_c * (((1 + tau_c) * p_i) / p_tilde) ** (-1) * c_s + c_min else: # Time path case I = alpha_c.shape[0] T = p_i.shape[0] @@ -363,10 +402,11 @@ def get_ci(c_s, p_i, p_tilde, tau_c, alpha_c, method="SS"): J = c_s.shape[2] tau_c = tau_c.reshape(T, I, 1, 1) alpha_c = alpha_c.reshape(1, I, 1, 1) + c_min = c_min.reshape(1, I, 1, 1) p_tilde = p_tilde.reshape(T, 1, 1, 1) p_i = p_i.reshape(T, I, 1, 1) c_s = c_s.reshape(T, 1, S, J) - c_si = alpha_c * (((1 + tau_c) * p_i) / p_tilde) ** (-1) * c_s + c_si = alpha_c * (((1 + tau_c) * p_i) / p_tilde) ** (-1) * c_s + c_min return c_si @@ -374,6 +414,7 @@ def FOC_savings( r, w, p_tilde, + p_i, b, b_splus1, n, @@ -407,6 +448,7 @@ def FOC_savings( r (array_like): the real interest rate w (array_like): the real wage rate p_tilde (array_like): composite good price + p_i (Numpy array): prices for consumption good i b (Numpy array): household savings b_splus1 (Numpy array): household savings one period ahead b_splus2 (Numpy array): household savings two periods ahead @@ -483,14 +525,17 @@ def FOC_savings( m_wealth = p.m_wealth[-1] p_wealth = p.p_wealth[-1] p_tilde = np.ones_like(p.rho[-1, :]) * p_tilde + tau_c = p.tau_c[-1, :] elif method == "TPI_scalar": h_wealth = p.h_wealth[0] m_wealth = p.m_wealth[0] p_wealth = p.p_wealth[0] + tau_c = p.tau_c[0, :] else: - h_wealth = p.h_wealth[t] - m_wealth = p.m_wealth[t] - p_wealth = p.p_wealth[t] + h_wealth = p.h_wealth[t : t + length] + m_wealth = p.m_wealth[t : t + length] + p_wealth = p.p_wealth[t : t + length] + tau_c = p.tau_c[t : t + length, :] taxes = tax.net_taxes( r, w, @@ -509,7 +554,9 @@ def FOC_savings( etr_params, p, ) - cons = get_cons(r, w, p_tilde, b, b_splus1, n, bq, rm, taxes, e, p) + cons = get_cons( + r, w, p_tilde, p_i, b, b_splus1, n, bq, rm, taxes, e, tau_c, p, method + ) deriv = ( (1 + r) - ( @@ -562,6 +609,7 @@ def FOC_labor( r, w, p_tilde, + p_i, b, b_splus1, n, @@ -596,6 +644,7 @@ def FOC_labor( r (array_like): the real interest rate w (array_like): the real wage rate p_tilde (array_like): composite good price + p_i (Numpy array): prices for consumption good i b (Numpy array): household savings b_splus1 (Numpy array): household savings one period ahead n (Numpy array): household labor supply @@ -625,11 +674,14 @@ def FOC_labor( """ if method == "SS": tau_payroll = p.tau_payroll[-1] + tau_c = p.tau_c[-1, :] elif method == "TPI_scalar": # for 1st donut ring only tau_payroll = p.tau_payroll[0] + tau_c = p.tau_c[0, :] else: length = r.shape[0] tau_payroll = p.tau_payroll[t : t + length] + tau_c = p.tau_c[t : t + length, :] if j is not None: if method == "SS": tax_noncompliance = p.labor_income_tax_noncompliance_rate[-1, j] @@ -668,13 +720,6 @@ def FOC_labor( axis=0, ) e = np.diag(e_long[t : t + p.S, :, j], max(p.S - length, 0)) - if method == "SS": - tau_payroll = p.tau_payroll[-1] - elif method == "TPI_scalar": # for 1st donut ring only - tau_payroll = p.tau_payroll[0] - else: - length = r.shape[0] - tau_payroll = p.tau_payroll[t : t + length] if method == "TPI": if b.ndim == 2: r = r.reshape(r.shape[0], 1) @@ -699,7 +744,9 @@ def FOC_labor( etr_params, p, ) - cons = get_cons(r, w, p_tilde, b, b_splus1, n, bq, rm, taxes, e, p) + cons = get_cons( + r, w, p_tilde, p_i, b, b_splus1, n, bq, rm, taxes, e, tau_c, p, method + ) deriv = ( 1 - tau_payroll diff --git a/ogcore/txfunc.py b/ogcore/txfunc.py index a6df207fb..54d67fbbe 100644 --- a/ogcore/txfunc.py +++ b/ogcore/txfunc.py @@ -28,7 +28,6 @@ from matplotlib import cm import random - if not SHOW_RUNTIME: warnings.simplefilter("ignore", RuntimeWarning) @@ -1690,7 +1689,7 @@ def tax_func_estimate( futures = client.compute(lazy_values) results = client.gather(futures) else: - results = results = compute( + results = compute( *lazy_values, scheduler=dask.multiprocessing.get, num_workers=num_workers, diff --git a/setup.py b/setup.py index ea3f5aff2..67b4c4b10 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="ogcore", - version="0.15.0", + version="0.15.1", author="Jason DeBacker and Richard W. Evans", license="CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", description="A general equilibrium overlapping generations model for fiscal policy analysis", diff --git a/tests/test_SS.py b/tests/test_SS.py index a871b3f3f..d47e1708e 100644 --- a/tests/test_SS.py +++ b/tests/test_SS.py @@ -95,7 +95,7 @@ def dask_client(): input_tuple = utils.safe_read_pickle( os.path.join(CUR_PATH, "test_io_data", "SS_fsolve_inputs.pkl") ) -(bssmat, nssmat, TR_ss, factor_ss) = input_tuple +bssmat, nssmat, TR_ss, factor_ss = input_tuple # Parameterize the baseline, closed econ case p1 = Specifications(baseline=True) p1.update_specifications({"zeta_D": [0.0], "zeta_K": [0.0]}) @@ -254,6 +254,7 @@ def dask_client(): "I": 4, "io_matrix": np.eye(4), "alpha_c": [0.1, 0.5, 0.3, 0.1], + "c_min": [0.0, 0.0, 0.0, 0.0], "epsilon": [1.0, 1.0, 1.0, 1.0], "gamma": [0.3, 0.4, 0.35, 0.45], "gamma_g": [0.0, 0.0, 0.0, 0.0], @@ -313,7 +314,7 @@ def test_SS_fsolve(tmpdir, guesses, args, expected): ensure that output returned matches what it has been before. """ # args = - (bssmat, nssmat, TR_ss, Ig_baseline, factor_ss, p, client) = args + bssmat, nssmat, TR_ss, Ig_baseline, factor_ss, p, client = args p.baseline_dir = tmpdir p.output_base = tmpdir @@ -532,6 +533,7 @@ def test_solve_for_j(): r_p = 0.04 w = 1.2 p_tilde = 1.0 + p_i = 1.0 bq_j = 0.0002 rm_j = 0.005 tr_j = 0.1 @@ -539,7 +541,7 @@ def test_solve_for_j(): factor = 100000 j = 1 test_result = SS.solve_for_j( - guesses, r_p, w, p_tilde, bq_j, rm_j, tr_j, ubi_j, factor, j, p + guesses, r_p, w, p_tilde, p_i, bq_j, rm_j, tr_j, ubi_j, factor, j, p ) expected_result = 0.15574086659957984 assert np.allclose(test_result.x[4], expected_result) @@ -560,6 +562,7 @@ def test_solve_for_j(): "I": 4, "io_matrix": np.eye(4), "alpha_c": [0.1, 0.5, 0.3, 0.1], + "c_min": [0.0, 0.0, 0.0, 0.0], "epsilon": [1.0, 1.0, 1.0, 1.0], "gamma": [0.3, 0.4, 0.35, 0.45], "gamma_g": [0.0, 0.0, 0.0, 0.0], @@ -578,6 +581,7 @@ def test_solve_for_j(): ] ), "alpha_c": [0.1, 0.4, 0.3, 0.1, 0.1], + "c_min": [0.0, 0.0, 0.0, 0.0, 0.0], "epsilon": [1.0, 1.0, 1.0, 1.0], "gamma": [0.3, 0.4, 0.35, 0.45], "gamma_g": [0.0, 0.0, 0.0, 0.0], @@ -1136,7 +1140,7 @@ def test_euler_equation_solver(input_tuple, ubi_j, p, expected): # Test SS.inner_loop function. Provide inputs to function and # ensure that output returned matches what it has been before. guesses, r, w, bq, rm, tr, _, factor, j = input_tuple - args = (r, w, 1.0, bq, rm, tr, ubi_j, factor, j, p) + args = (r, w, 1.0, 1.0, bq, rm, tr, ubi_j, factor, j, p) test_list = SS.euler_equation_solver(guesses, *args) print(repr(test_list)) @@ -1218,6 +1222,7 @@ def test_euler_equation_solver(input_tuple, ubi_j, p, expected): "gamma": [0.3, 0.35, 0.4], "gamma_g": [0.1, 0.05, 0.15], "alpha_c": [0.2, 0.4, 0.4], + "c_min": [0.0, 0.0, 0.0], "initial_guess_r_SS": 0.11, "initial_guess_TR_SS": 0.07, "alpha_I": [0.01], @@ -1237,6 +1242,7 @@ def test_euler_equation_solver(input_tuple, ubi_j, p, expected): "gamma": [0.3, 0.35, 0.4], "gamma_g": [0.0, 0.0, 0.0], "alpha_c": [0.2, 0.4, 0.4], + "c_min": [0.0, 0.0, 0.0], "initial_guess_r_SS": 0.11, "initial_guess_TR_SS": 0.07, "debt_ratio_ss": 1.5, @@ -1247,6 +1253,24 @@ def test_euler_equation_solver(input_tuple, ubi_j, p, expected): "initial_guess_r_SS": 0.04, "reform_use_baseline_solution": False, } +param_updates16 = { + "start_year": 2023, + "budget_balance": True, + "frisch": 0.41, + "cit_rate": [[0.21, 0.25, 0.35]], + "M": 3, + "I": 3, + "io_matrix": np.eye(3), + "epsilon": [1.0, 1.0, 1.0], + "gamma": [0.3, 0.35, 0.4], + "gamma_g": [0.0, 0.0, 0.0], + "alpha_c": [0.2, 0.4, 0.4], + "c_min": [0.002, 0.004, 0.0004], + "initial_guess_r_SS": 0.11, + "initial_guess_TR_SS": 0.07, + "debt_ratio_ss": 1.5, +} +filename16 = "run_SS_baseline_M3_Kg_zero_cmin.pkl" # Note that changing the order in which these tests are run will cause @@ -1255,38 +1279,40 @@ def test_euler_equation_solver(input_tuple, ubi_j, p, expected): @pytest.mark.parametrize( "baseline,param_updates,filename", [ - # (True, param_updates1, filename1), - # (False, param_updates9, filename9), - # (True, param_updates2, filename2), - # (False, param_updates10, filename10), - # (True, param_updates3, filename3), - # # True, param_updates4, filename4), - # (False, param_updates5, filename5), - # (False, param_updates6, filename6), - # (False, param_updates7, filename7), - # # (False, param_updates8, filename8), - # (False, param_updates11, filename11), - # (True, param_updates12, filename12), - # (True, param_updates13, filename13), - # (True, param_updates14, filename14), + (True, param_updates1, filename1), + (False, param_updates9, filename9), + (True, param_updates2, filename2), + (False, param_updates10, filename10), + (True, param_updates3, filename3), + # (True, param_updates4, filename4), + (False, param_updates5, filename5), + (False, param_updates6, filename6), + (False, param_updates7, filename7), + (False, param_updates8, filename8), + (False, param_updates11, filename11), + (True, param_updates12, filename12), + (True, param_updates13, filename13), + (True, param_updates14, filename14), (False, param_updates15, filename3), + (True, param_updates16, filename16), ], ids=[ - # "Baseline", - # "Reform, baseline spending", - # "Baseline, use zeta", - # "Reform, baseline spending, use zeta", - # "Baseline, small open", - # # "Baseline, small open use zeta", - # "Reform", - # "Reform, use zeta", - # "Reform, small open", - # # "Reform, small open use zeta", - # "Reform, delta_tau=0", - # "Baseline, non-zero Kg", - # "Baseline, M=3, non-zero Kg", - # "Baseline, M=3, zero Kg", + "Baseline", + "Reform, baseline spending", + "Baseline, use zeta", + "Reform, baseline spending, use zeta", + "Baseline, small open", + # "Baseline, small open use zeta", + "Reform", + "Reform, use zeta", + "Reform, small open", + "Reform, small open use zeta", + "Reform, delta_tau=0", + "Baseline, non-zero Kg", + "Baseline, M=3, non-zero Kg", + "Baseline, M=3, zero Kg", "Reform, not use baseline solution", + "Baseline, M=3, zero Kg, cmin > 0", ], ) @pytest.mark.local @@ -1327,7 +1353,11 @@ def test_run_SS(tmpdir, baseline, param_updates, filename, dask_client): pass for k, v in expected_dict.items(): print("Checking item = ", k) - assert np.allclose(test_dict[VAR_NAME_MAPPING[k]], v, atol=5e-04) + try: + assert np.allclose(test_dict[VAR_NAME_MAPPING[k]], v, atol=5e-04) + except KeyError: + print(f"Assertion failed for variable {k}") + assert np.allclose(test_dict[k], v, atol=5e-04) @pytest.mark.parametrize( diff --git a/tests/test_TPI.py b/tests/test_TPI.py index e04863f2c..f9a3e7482 100644 --- a/tests/test_TPI.py +++ b/tests/test_TPI.py @@ -266,9 +266,23 @@ def test_firstdoughnutring(): ) guesses, r, w, bq, rm, tr, theta, factor, ubi, j, initial_b = input_tuple p_tilde = 1.0 # needed for multi-industry version + p_i = 1.0 # needed for multi-industry version p = Specifications() test_list = TPI.firstdoughnutring( - guesses, r, w, p_tilde, bq, rm, tr, theta, factor, ubi, j, initial_b, p + guesses, + r, + w, + p_tilde, + p_i, + bq, + rm, + tr, + theta, + factor, + ubi, + j, + initial_b, + p, ) expected_list = utils.safe_read_pickle( @@ -323,12 +337,14 @@ def test_twist_doughnut(file_inputs, file_outputs): initial_b, ) = input_tuple p_tilde = np.ones_like(r) # needed for multi-industry version + p_i = np.ones((r.shape[0], 1)) # needed for multi-industry version p = Specifications() input_tuple2 = ( guesses, r, w, p_tilde, + p_i, bq, rm, tr, @@ -434,6 +450,7 @@ def test_inner_loop(): "gamma": [0.3, 0.35, 0.4], "gamma_g": [0.1, 0.05, 0.15], "alpha_c": [0.2, 0.4, 0.4], + "c_min": [0.0, 0.0, 0.0], "initial_guess_r_SS": 0.11, "initial_guess_TR_SS": 0.07, "alpha_I": [0.01], @@ -464,6 +481,7 @@ def test_inner_loop(): "gamma": [0.3, 0.35, 0.4], "gamma_g": [0.0, 0.0, 0.0], "alpha_c": [0.2, 0.4, 0.4], + "c_min": [0.0, 0.0, 0.0], "initial_guess_r_SS": 0.11, "initial_guess_TR_SS": 0.07, "debt_ratio_ss": 1.5, @@ -492,6 +510,7 @@ def test_inner_loop(): "gamma": [0.3, 0.35, 0.4], "gamma_g": [0.0, 0.0, 0.0], "alpha_c": [0.2, 0.4, 0.3, 0.1], + "c_min": [0.0, 0.0, 0.0, 0.0], "initial_guess_r_SS": 0.11, "initial_guess_TR_SS": 0.07, "debt_ratio_ss": 1.5, @@ -501,6 +520,35 @@ def test_inner_loop(): filename11 = os.path.join( CUR_PATH, "test_io_data", "run_TPI_baseline_MneI.pkl" ) +param_updates12 = { + "start_year": 2023, + "budget_balance": True, + "frisch": 0.41, + "cit_rate": [[0.21, 0.25, 0.35]], + "M": 3, + "I": 4, + "io_matrix": np.array( + [ + [0.3, 0.3, 0.4], + [0.6, 0.1, 0.3], + [0.25, 0.5, 0.25], + [0.0, 1.0, 0.0], + ] + ), + "epsilon": [1.0, 1.0, 1.0], + "gamma": [0.3, 0.35, 0.4], + "gamma_g": [0.0, 0.0, 0.0], + "alpha_c": [0.2, 0.4, 0.3, 0.1], + "c_min": [0.001, 0.0002, 0.0, 0.0003], + "initial_guess_r_SS": 0.11, + "initial_guess_TR_SS": 0.07, + "debt_ratio_ss": 1.5, + "alpha_T": alpha_T.tolist(), + "alpha_G": alpha_G.tolist(), +} +filename12 = os.path.join( + CUR_PATH, "test_io_data", "run_TPI_baseline_MneI_cmin.pkl" +) @pytest.mark.local @@ -518,6 +566,7 @@ def test_inner_loop(): (True, param_updates9, filename9), (True, param_updates10, filename10), (True, param_updates11, filename11), + (True, param_updates12, filename12), ], ids=[ "Baseline, balanced budget", @@ -531,6 +580,7 @@ def test_inner_loop(): "Baseline, M=3 non-zero Kg", "Baseline, M=3 zero Kg", "Baseline, M!=I", + "Baseline, M!=I, cmin>0", ], ) def test_run_TPI_full_run( @@ -606,20 +656,23 @@ def test_run_TPI_full_run( except KeyError: pass + # if old variable names, update keys with VAR_NAME_MAPPING + if "Y_vec" in expected_dict.keys(): + expected_dict_updated = {} + for k, v in expected_dict.items(): + expected_dict_updated[VAR_NAME_MAPPING[k]] = v + expected_dict = expected_dict_updated for k, v in expected_dict.items(): print("Testing, ", k) try: print( "Diff = ", - np.abs(test_dict[VAR_NAME_MAPPING[k]][: p.T] - v[: p.T]).max(), + np.abs(test_dict[k][: p.T] - v[: p.T]).max(), ) except ValueError: print( "Diff = ", - np.abs( - test_dict[VAR_NAME_MAPPING[k]][: p.T, :, :] - - v[: p.T, :, :] - ).max(), + np.abs(test_dict[k][: p.T, :, :] - v[: p.T, :, :]).max(), ) for k, v in expected_dict.items(): @@ -627,10 +680,10 @@ def test_run_TPI_full_run( try: print( "Diff = ", - np.abs(test_dict[VAR_NAME_MAPPING[k]][: p.T] - v[: p.T]).max(), + np.abs(test_dict[k][: p.T] - v[: p.T]).max(), ) assert np.allclose( - test_dict[VAR_NAME_MAPPING[k]][: p.T], + test_dict[k][: p.T], v[: p.T], rtol=1e-04, atol=1e-04, @@ -638,13 +691,10 @@ def test_run_TPI_full_run( except ValueError: print( "Diff = ", - np.abs( - test_dict[VAR_NAME_MAPPING[k]][: p.T, :, :] - - v[: p.T, :, :] - ).max(), + np.abs(test_dict[k][: p.T, :, :] - v[: p.T, :, :]).max(), ) assert np.allclose( - test_dict[VAR_NAME_MAPPING[k]][: p.T, :, :], + test_dict[k][: p.T, :, :], v[: p.T, :, :], rtol=1e-04, atol=1e-04, diff --git a/tests/test_aggregates.py b/tests/test_aggregates.py index 9cfdf8cb8..16cfe1ffe 100644 --- a/tests/test_aggregates.py +++ b/tests/test_aggregates.py @@ -4,7 +4,6 @@ from ogcore import aggregates as aggr from ogcore.parameters import Specifications - p = Specifications() rho_vec = np.zeros((1, 40)) rho_vec[0, -1] = 1.0 diff --git a/tests/test_demographics.py b/tests/test_demographics.py index 7ea0e2167..1a62dcbfb 100644 --- a/tests/test_demographics.py +++ b/tests/test_demographics.py @@ -3,7 +3,6 @@ import os from ogcore import demographics - # Read in some test population data to use in select tests below # Tests that ping the UN data portal are marked with the "local" mark data_dir = os.path.join( diff --git a/tests/test_firm.py b/tests/test_firm.py index a3693e2ad..9b42618ef 100644 --- a/tests/test_firm.py +++ b/tests/test_firm.py @@ -4,7 +4,6 @@ import numpy as np from ogcore.parameters import Specifications - p1 = Specifications() new_param_values = {"Z": [[2.0]], "gamma": [0.5], "epsilon": [1.0]} # update parameters instance with new values for test diff --git a/tests/test_household.py b/tests/test_household.py index 3857c01d9..8adc2ef9b 100644 --- a/tests/test_household.py +++ b/tests/test_household.py @@ -330,6 +330,10 @@ def test_get_rm(RM, j, p, method, expected): j3 = None p4 = Specifications() +p4.T = 4 +p4.S = 3 +p4.J = 2 +p4.I = 5 p4.e = np.array([[1.0, 2.1], [0.4, 0.5], [1.6, 0.9]]) p4.lambdas = np.array([0.7, 0.3]) p4.g_y = 0.05 @@ -369,14 +373,18 @@ def test_get_rm(RM, j, p, method, expected): ) bq4 = BQ4 / p4.lambdas.reshape(1, 1, 2) rm4 = np.zeros_like(bq4) -tau_c4 = np.array( +tau_c2D = np.array( [ - np.array([[0.02, 0.03], [0.04, 0.05], [0.055, 0.066]]), - np.array([[0.07, 0.08], [0.02, 0.01], [0.0, 0.04]]), - np.array([[0.01, 0.02], [0.14, 0.15], [0.05, 0.06]]), - np.array([[0.04, 0.06], [0.099, 0.044], [0.035, 0.065]]), + [0.04, 0.06, 0.055, 0.066, 0.10], + [0.04, 0.06, 0.055, 0.066, 0.10], + [0.04, 0.06, 0.055, 0.066, 0.10], + [0.04, 0.06, 0.055, 0.066, 0.10], ] ) +c_min4 = np.array([0.001, 0.002, 0.003, 0.0, 0.004]) +p4.c_min = c_min4 +min_c_expend = ((1 + tau_c2D) * c_min4.reshape(1, p4.I)).sum(axis=1) +p4.tau_c = tau_c2D net_tax4 = np.array( [ np.array([[0.01, 0.02], [0.4, 0.5], [0.05, 0.06]]), @@ -389,15 +397,15 @@ def test_get_rm(RM, j, p, method, expected): test_data = [ ( - (r1, w1, b1, b_splus1_1, n1, bq1, rm1, net_tax1, p1), + (r1, w1, b1, b_splus1_1, n1, bq1, rm1, net_tax1, p1, "SS"), 1.288650006, ), ( - (r2, w2, b2, b_splus1_2, n2, bq2, rm2, net_tax2, p2), + (r2, w2, b2, b_splus1_2, n2, bq2, rm2, net_tax2, p2, "SS"), np.array([1.288650006, 13.76350909, 5.188181864]), ), ( - (r3, w3, b3, b_splus1_3, n3, bq3, rm3, net_tax3, p3), + (r3, w3, b3, b_splus1_3, n3, bq3, rm3, net_tax3, p3, "SS"), np.array( [ [4.042579933, 0.3584699], @@ -407,7 +415,7 @@ def test_get_rm(RM, j, p, method, expected): ), ), ( - (r4, w4, b4, b_splus1_4, n4, bq4, rm4, net_tax4, p4), + (r4, w4, b4, b_splus1_4, n4, bq4, rm4, net_tax4, p4, "TPI"), np.array( [ np.array( @@ -439,7 +447,8 @@ def test_get_rm(RM, j, p, method, expected): ] ), ] - ), + ) + - np.reshape(min_c_expend, (4, 1, 1)), ), ] @@ -451,10 +460,28 @@ def test_get_rm(RM, j, p, method, expected): ) def test_get_cons(model_args, expected): # Test consumption calculation - r, w, b, b_splus1, n, bq, rm, net_tax, p = model_args + r, w, b, b_splus1, n, bq, rm, net_tax, p, method = model_args p_tilde = np.ones_like(w) + p_i = np.ones((p.T, p.I)) + if method == "SS": + tau_c = p.tau_c[-1, :] + else: + tau_c = p.tau_c test_value = household.get_cons( - r, w, p_tilde, b, b_splus1, n, bq, rm, net_tax, p.e, p + r, + w, + p_tilde, + p_i, + b, + b_splus1, + n, + bq, + rm, + net_tax, + p.e, + tau_c, + p, + method, ) assert np.allclose(test_value, expected) @@ -723,13 +750,16 @@ def test_FOC_savings(model_vars, in_params, expected): ) if method == "TPI": p_tilde = np.ones_like(w) + p_i = np.ones((params.T, params.I)) else: p_tilde = np.array([1.0]) + p_i = np.array([1.0]) if j is not None: test_value = household.FOC_savings( r, w, p_tilde, + p_i, b, b_splus1, n, @@ -752,6 +782,7 @@ def test_FOC_savings(model_vars, in_params, expected): r, w, p_tilde, + p_i, b, b_splus1, n, @@ -1015,12 +1046,15 @@ def test_FOC_labor(model_vars, params, expected): ) if method == "TPI": p_tilde = np.ones_like(w) + p_i = np.ones((params.T, params.I)) else: p_tilde = np.array([1.0]) + p_i = np.array([1.0]) test_value = household.FOC_labor( r, w, p_tilde, + p_i, b, b_splus1, n, @@ -1093,23 +1127,65 @@ def test_constraint_checker_TPI(bssmat, nssmat, cssmat, ltilde): assert True -def test_ci(): +c_s = np.array([2.0, 3.0, 5.0, 7.0]).reshape(4, 1) +p_i = np.array([1.1, 0.8, 1.0]) +p_tilde = np.array([2.3]) +tau_c = np.array([0.2, 0.3, 0.5]) +alpha_c = np.array([0.5, 0.3, 0.2]) +c_min = np.array([0.01, 0.02, 0.0]) +expected_ci = np.array( + [ + [ + 1.742424242 + 0.01, + 2.613636364 + 0.01, + 4.356060606 + 0.01, + 6.098484848 + 0.01, + ], + [ + 1.326923077 + 0.02, + 1.990384615 + 0.02, + 3.317307692 + 0.02, + 4.644230769 + 0.02, + ], + [ + 0.613333333 + 0.0, + 0.92 + 0.0, + 1.533333333 + 0.0, + 2.146666667 + 0.0, + ], + ] +).reshape(3, 4, 1) + +c_s_tpi = np.tile(c_s.reshape(1, 4, 1), (3, 1, 1)) +p_i_tpi = np.tile(p_i.reshape(1, 3), (3, 1)) +p_tilde_tpi = np.array([2.3, 2.3, 2.3]) +tau_c_tpi = np.tile(tau_c.reshape(1, 3), (3, 1)) +expected_ci_tpi = np.tile(expected_ci.reshape(1, 4, 3), (3, 1, 1)) + + +@pytest.mark.parametrize( + "c_s,p_i,p_tilde,tau_c,alpha_c,c_min,method,expected", + [ + (c_s, p_i, p_tilde, tau_c, alpha_c, c_min, "SS", expected_ci), + ( + c_s_tpi, + p_i_tpi, + p_tilde_tpi, + tau_c_tpi, + alpha_c, + c_min, + "TPI", + expected_ci_tpi, + ), + ], + ids=["SS", "TPI"], +) +def test_get_ci(c_s, p_i, p_tilde, tau_c, alpha_c, c_min, method, expected): """ Test of the get_ci function """ - c_s = np.array([2.0, 3.0, 5.0, 7.0]).reshape(4, 1) - p_i = np.array([1.1, 0.8, 1.0]) - p_tilde = np.array([2.3]) - tau_c = np.array([0.2, 0.3, 0.5]) - alpha_c = np.array([0.5, 0.3, 0.2]) - expected_ci = np.array( - [ - [1.742424242, 2.613636364, 4.356060606, 6.098484848], - [1.326923077, 1.990384615, 3.317307692, 4.644230769], - [0.613333333, 0.92, 1.533333333, 2.146666667], - ] - ).reshape(3, 4, 1) - - test_ci = household.get_ci(c_s, p_i, p_tilde, tau_c, alpha_c) + test_ci = household.get_ci( + c_s, p_i, p_tilde, tau_c, alpha_c, c_min, method=method + ) assert np.allclose(test_ci, expected_ci) diff --git a/tests/test_io_data/run_SS_baseline_M3_Kg_zero_cmin.pkl b/tests/test_io_data/run_SS_baseline_M3_Kg_zero_cmin.pkl new file mode 100644 index 000000000..9599b5463 Binary files /dev/null and b/tests/test_io_data/run_SS_baseline_M3_Kg_zero_cmin.pkl differ diff --git a/tests/test_io_data/run_TPI_baseline_MneI_cmin.pkl b/tests/test_io_data/run_TPI_baseline_MneI_cmin.pkl new file mode 100644 index 000000000..c1ddfb596 Binary files /dev/null and b/tests/test_io_data/run_TPI_baseline_MneI_cmin.pkl differ diff --git a/tests/test_output_plots.py b/tests/test_output_plots.py index fcbe19ab8..daa6bd14c 100644 --- a/tests/test_output_plots.py +++ b/tests/test_output_plots.py @@ -10,7 +10,6 @@ import matplotlib.pyplot as plt from ogcore import utils, output_plots, constants - # Load in test results and parameters CUR_PATH = os.path.abspath(os.path.dirname(__file__)) base_ss = utils.safe_read_pickle( diff --git a/tests/test_output_tables.py b/tests/test_output_tables.py index 5db16d99d..c8f6db97f 100644 --- a/tests/test_output_tables.py +++ b/tests/test_output_tables.py @@ -9,7 +9,6 @@ import numpy as np from ogcore import utils, output_tables - # Load in test results and parameters CUR_PATH = os.path.abspath(os.path.dirname(__file__)) base_ss = utils.safe_read_pickle( diff --git a/tests/test_parameter_plots.py b/tests/test_parameter_plots.py index ef7741c4a..e29be6d27 100644 --- a/tests/test_parameter_plots.py +++ b/tests/test_parameter_plots.py @@ -11,7 +11,6 @@ import matplotlib.image as mpimg from ogcore import utils, parameter_plots, Specifications - # Load in test results and parameters CUR_PATH = os.path.abspath(os.path.dirname(__file__)) if sys.version_info[1] == 11: diff --git a/tests/test_parameter_tables.py b/tests/test_parameter_tables.py index 082e02d30..0e0f0e8dc 100644 --- a/tests/test_parameter_tables.py +++ b/tests/test_parameter_tables.py @@ -8,7 +8,6 @@ from ogcore import utils, parameter_tables from ogcore.parameters import Specifications - # Load in test results and parameters CUR_PATH = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/test_pensions.py b/tests/test_pensions.py index 88b36fed6..6fc472dcd 100644 --- a/tests/test_pensions.py +++ b/tests/test_pensions.py @@ -4,7 +4,6 @@ from ogcore import pensions from ogcore.parameters import Specifications - p = Specifications() rho_vec = np.zeros((1, 4)) rho_vec[0, -1] = 1.0 @@ -203,7 +202,7 @@ def test_deriv_DB_loop(args, deriv_DB_loop_expected): """ Test of the pensions.deriv_DB_loop() function. """ - (w, e, S, retire, per_rmn, last_career_yrs, rep_rate_py, yr_contrib) = args + w, e, S, retire, per_rmn, last_career_yrs, rep_rate_py, yr_contrib = args deriv_DB_loop = pensions.deriv_DB_loop( w, e, S, retire, per_rmn, last_career_yrs, rep_rate_py, yr_contrib ) @@ -235,7 +234,7 @@ def test_deriv_PS_loop(args, deriv_PS_loop_expected): """ Test of the pensions.deriv_PS_loop() function. """ - (w, e, S, retire, per_rmn, d_theta_empty, vpoint, factor) = args + w, e, S, retire, per_rmn, d_theta_empty, vpoint, factor = args deriv_PS_loop = pensions.deriv_PS_loop( w, e, S, retire, per_rmn, d_theta_empty, vpoint, factor @@ -288,7 +287,7 @@ def test_deriv_DB(args, deriv_DB_expected): """ Test of the pensions.deriv_DB() function. """ - (w, e, per_rmn, p) = args + w, e, per_rmn, p = args deriv_DB = pensions.deriv_DB(w, e, per_rmn, p) assert np.allclose(deriv_DB, deriv_DB_expected) @@ -335,7 +334,7 @@ def test_deriv_S(args, deriv_PS_expected): """ Test of the pensions.deriv_PS() function. """ - (w, e, per_rmn, factor, p) = args + w, e, per_rmn, factor, p = args deriv_PS = pensions.deriv_PS(w, e, per_rmn, factor, p) @@ -560,7 +559,7 @@ def test_deriv_NDC_loop(args, deriv_NDC_loop_expected): """ Test of the pensions.deriv_NDC_loop() function. """ - (w, e, per_rmn, g_ndc_value, delta_ret_value, d_theta, p) = args + w, e, per_rmn, g_ndc_value, delta_ret_value, d_theta, p = args print("TESTING", p.tau_p, delta_ret_value, g_ndc_value) deriv_NDC_loop = pensions.deriv_NDC_loop( @@ -602,7 +601,7 @@ def test_delta_ret_loop(args, dir_delta_ret_expected): """ Test of the pensions.delta_ret_loop() function. """ - (surv_rates, g_ndc_value, dir_delta_s_empty, p) = args + surv_rates, g_ndc_value, dir_delta_s_empty, p = args dir_delta = pensions.delta_ret_loop( p.S, p.retire, surv_rates, g_ndc_value, dir_delta_s_empty ) @@ -848,7 +847,7 @@ def test_PS_1dim_loop(args, PS_loop_expected): """ Test of the pensions.PS_1dim_loop() function. """ - (w, e, n, S_ret, S, g_y, vpoint, factor, L_inc_avg_s, PS) = args + w, e, n, S_ret, S, g_y, vpoint, factor, L_inc_avg_s, PS = args PS_loop = pensions.PS_1dim_loop( w, e, n, S_ret, S, g_y, vpoint, factor, L_inc_avg_s, PS ) diff --git a/tests/test_tax.py b/tests/test_tax.py index 454dc01e4..93aef196a 100644 --- a/tests/test_tax.py +++ b/tests/test_tax.py @@ -4,7 +4,6 @@ from ogcore import tax from ogcore.parameters import Specifications - b1 = np.array([0.1, 0.5, 0.9]) p1 = Specifications() rho_vec = np.zeros((1, 3))