diff --git a/deploy/taxbrain_server/celery_tasks.py b/deploy/taxbrain_server/celery_tasks.py index 5477ebb2..beb95073 100644 --- a/deploy/taxbrain_server/celery_tasks.py +++ b/deploy/taxbrain_server/celery_tasks.py @@ -147,14 +147,15 @@ def elasticity_gdp_task_async(year, user_mods, first_budget_year, elast_params): @celery_app.task -def ogusa_async(user_mods, ogusa_params, guid): +def ogusa_async(start_year, user_mods, ogusa_params, guid): print("user mods: ", user_mods) user_mods = convert_int_key(user_mods) user_reform = {'policy': user_mods} for key in EXPECTED_KEYS: if key not in user_reform: user_reform[key] = {} - diff_data = run_ogusa.run_micro_macro(reform=user_reform, + diff_data = run_ogusa.run_micro_macro(start_year=start_year, + reform=user_reform, user_params=ogusa_params, guid=guid) diff --git a/deploy/taxbrain_server/flask_server.py b/deploy/taxbrain_server/flask_server.py index ee9344b4..2b0a9cba 100644 --- a/deploy/taxbrain_server/flask_server.py +++ b/deploy/taxbrain_server/flask_server.py @@ -179,6 +179,7 @@ def example(): @app.route('/ogusa_start_job', methods=['POST']) def ogusa_start_job(): + start_year = int(request.form["start_year"]) user_mods = json.loads(request.form['user_mods']) ogusa_params = json.loads(request.form['ogusa_params']) @@ -186,7 +187,12 @@ def ogusa_start_job(): with ticket_lock_context(): guid = uuid.uuid1().hex - job = ogusa_async.delay(user_mods=user_mods, ogusa_params=ogusa_params, guid=guid) + job = ogusa_async.delay( + start_year=start_year, + user_mods=user_mods, + ogusa_params=ogusa_params, + guid=guid + ) RUNNING_JOBS[job.id] = job print('job_id', job) print('GUID IS ', guid) diff --git a/deploy/taxbrain_server/run_ogusa.py b/deploy/taxbrain_server/run_ogusa.py index e673dde3..db056fb7 100644 --- a/deploy/taxbrain_server/run_ogusa.py +++ b/deploy/taxbrain_server/run_ogusa.py @@ -13,20 +13,16 @@ from ogusa.scripts.execute import runner -def run_micro_macro(reform, user_params, guid): +def run_micro_macro(start_year, reform, user_params, guid): + + small_open = user_params if bool(user_params.get("small_open")) else False + budget_balance = bool(user_params.get("budget_balance", False)) start_time = time.time() REFORM_DIR = "./OUTPUT_REFORM_" + guid BASELINE_DIR = "./OUTPUT_BASELINE_" + guid - # Add start year from reform to user parameters - if isinstance(reform, tuple): - start_year = sorted(reform[0].keys())[0] - else: - start_year = sorted(reform.keys())[0] - user_params['start_year'] = start_year - with open("log_{}.log".format(guid), 'w') as f: f.write("guid: {}\n".format(guid)) f.write("reform: {}\n".format(reform)) @@ -37,12 +33,15 @@ def run_micro_macro(reform, user_params, guid): Run baseline ------------------------------------------------------------------------ ''' + + user_params["start_year"] = start_year output_base = BASELINE_DIR kwargs={'output_base':output_base, 'baseline_dir':BASELINE_DIR, 'test':False, 'time_path':True, 'baseline':True, 'analytical_mtrs':False, 'age_specific':False, 'user_params':user_params,'guid':guid, - 'run_micro':True, 'small_open': False, 'budget_balance':False, 'baseline_spending':False} + 'run_micro':True, 'small_open': small_open, 'budget_balance': budget_balance, + 'baseline_spending':False} #p2 = Process(target=runner, kwargs=kwargs) #p2.start() runner(**kwargs) @@ -58,7 +57,8 @@ def run_micro_macro(reform, user_params, guid): 'test':False, 'time_path':True, 'baseline':False, 'analytical_mtrs':False, 'age_specific':False, 'user_params':user_params,'guid':guid, - 'run_micro':True, 'small_open': False, 'budget_balance':False, 'baseline_spending':False} + 'run_micro':True, 'small_open': small_open, 'budget_balance': budget_balance, + 'baseline_spending':False} #p1 = Process(target=runner, kwargs=kwargs) #p1.start() runner(**kwargs) @@ -75,13 +75,25 @@ def run_micro_macro(reform, user_params, guid): if __name__ == "__main__": reform = { - 2017: { - '_II_rt1': [.09], - '_II_rt2': [.135], - '_II_rt3': [.225], - '_II_rt4': [.252], - '_II_rt5': [.297], - '_II_rt6': [.315], - '_II_rt7': [0.3564], - }, } - run_micro_macro(reform=reform, user_params={'frisch': 0.44}, guid='abc') + u'growdiff_response': {}, + u'consumption': {}, + u'growdiff_baseline': {}, + u'behavior': {}, + u'policy': { + 2017: { + u'_II_no_em_nu18': [False], + u'_NIIT_PT_taxed': [False], + u'_FICA_ss_trt': [0.1], + u'_ID_BenefitCap_Switch': [[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]], + u'_ALD_InvInc_ec_base_RyanBrady': [False], + u'_EITC_indiv': [False], + u'_ID_BenefitSurtax_Switch': [[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]], + u'_CTC_new_refund_limited': [False], + u'_CG_nodiff': [False] + } + }, + u'gdp_elasticity': {} + } + user_params = {u'g_y_annual': 0.04, u'frisch': 0.3} + + run_micro_macro(reform=reform, user_params=user_params, guid='abc') diff --git a/templates/dynamic/dynamic_input_form.html b/templates/dynamic/dynamic_input_form.html index 3ebc8905..b83688ad 100644 --- a/templates/dynamic/dynamic_input_form.html +++ b/templates/dynamic/dynamic_input_form.html @@ -61,11 +61,6 @@

Tax-Calculator Code Build

diff --git a/templates/dynamic/includes/params/inputs/dynamic.html b/templates/dynamic/includes/params/inputs/dynamic.html index 6b9f5f07..d903a403 100644 --- a/templates/dynamic/includes/params/inputs/dynamic.html +++ b/templates/dynamic/includes/params/inputs/dynamic.html @@ -13,6 +13,14 @@

Macroeconomic Parameters

{% endblock %} +

Open Economy Assumptions

+{% include 'taxbrain/includes/params/inputs/param.html' with param=params.small_open %} +{% include 'taxbrain/includes/params/inputs/param.html' with param=params.world_int_rate %} + +

Budget Closure Parameters

+{% include 'taxbrain/includes/params/inputs/param.html' with param=params.budget_balance %} +{% include 'taxbrain/includes/params/inputs/param.html' with param=params.debt_ratio_ss %} +

Macroeconomic Parameters

{% include 'taxbrain/includes/params/inputs/param.html' with param=params.g_y_annual %} {% include 'taxbrain/includes/params/inputs/param.html' with param=params.frisch %} diff --git a/templates/dynamic/landing.html b/templates/dynamic/landing.html index 168ce4de..12f4ca44 100644 --- a/templates/dynamic/landing.html +++ b/templates/dynamic/landing.html @@ -72,21 +72,14 @@

Choose a Dynamic Simulation Type

{% flatblock "dynamic_behavioral_blurb" %} - {% if include_ogusa %} - Overlapping Generations Simulation + Overlapping Generations Simulation -
-
-
-
-
{% flatblock "dynamic_olg_blurb" %} - {% endif %} Macro Elasticities Simulation diff --git a/webapp/apps/dynamic/compute.py b/webapp/apps/dynamic/compute.py index cb394685..1789b4db 100644 --- a/webapp/apps/dynamic/compute.py +++ b/webapp/apps/dynamic/compute.py @@ -7,7 +7,6 @@ import taxcalc from ..taxbrain.compute import DropqCompute, MockCompute from .models import OGUSAWorkerNodesCounter -from .helpers import filter_ogusa_only from ..constants import START_YEAR dqversion_info = taxcalc._version.get_versions() @@ -33,27 +32,8 @@ def remote_register_job(self, theurl, data, timeout=TIMEOUT_IN_SECONDS): response = requests.post(theurl, data=data, timeout=timeout) return response - def submit_json_ogusa_calculation(self, ogusa_mods, first_budget_year, - microsim_data, pack_up_user_mods): - return self.submit_ogusa_calculation(ogusa_mods, first_budget_year, - microsim_data, pack_up_user_mods=False) - - def submit_ogusa_calculation(self, ogusa_mods, first_budget_year, microsim_data, - pack_up_user_mods=True): - - print "mods is ", ogusa_mods - ogusa_params = filter_ogusa_only(ogusa_mods) - data = {} - if pack_up_user_mods: - microsim_params = package_up_vars(microsim_data, first_budget_year) - microsim_params = {first_budget_year:microsim_params} - print "microsim data is", microsim_params - else: - data['taxio_format'] = True - data['first_budget_year'] = first_budget_year - microsim_params = microsim_data - - + def submit_ogusa_calculation(self, data): + print "mods is ", data print "submit dynamic work" hostnames = OGUSA_WORKERS @@ -62,9 +42,6 @@ def submit_ogusa_calculation(self, ogusa_mods, first_budget_year, microsim_data, 'callback': "http://{}/dynamic/dynamic_finished".format(CALLBACK_HOSTNAME), } - data['ogusa_params'] = json.dumps(ogusa_params) - data['user_mods'] = json.dumps(microsim_params) - data['first_year'] = first_budget_year job_ids = [] guids = [] diff --git a/webapp/apps/dynamic/forms.py b/webapp/apps/dynamic/forms.py index 8f3fc37d..ffd5b056 100644 --- a/webapp/apps/dynamic/forms.py +++ b/webapp/apps/dynamic/forms.py @@ -575,6 +575,10 @@ class Meta: exclude = ['creation_date'] widgets = {} labels = {} + boolean_fields = [ + "small_open", + "budget_balance" + ] for param in OGUSA_DEFAULT_PARAMS.values(): for field in param.col_fields: attrs = { @@ -584,8 +588,10 @@ class Meta: if param.coming_soon: attrs['disabled'] = True - attrs['checked'] = False - widgets[field.id] = forms.CheckboxInput(attrs=attrs, check_test=bool_like) + + if param.tc_id in boolean_fields: + checkbox = forms.CheckboxInput(attrs=attrs, check_test=bool_like) + widgets[field.id] = checkbox else: widgets[field.id] = forms.TextInput(attrs=attrs) diff --git a/webapp/apps/dynamic/helpers.py b/webapp/apps/dynamic/helpers.py index 7a9f9d85..a9a2b15d 100644 --- a/webapp/apps/dynamic/helpers.py +++ b/webapp/apps/dynamic/helpers.py @@ -209,7 +209,7 @@ def filter_ogusa_only(user_values): print "Removing ", k, v del user_values[k] else: - user_values[k] = float(v) + user_values[k] = float(v[0]) if isinstance(v, list) else float(v) return user_values diff --git a/webapp/apps/dynamic/migrations/0016_auto_20171026_1617.py b/webapp/apps/dynamic/migrations/0016_auto_20171026_1617.py new file mode 100644 index 00000000..dda0eea4 --- /dev/null +++ b/webapp/apps/dynamic/migrations/0016_auto_20171026_1617.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import webapp.apps.taxbrain.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dynamic', '0015_auto_20170918_1255'), + ] + + operations = [ + migrations.AddField( + model_name='dynamicsaveinputs', + name='small_open', + field=models.NullBooleanField(default=None), + ), + migrations.AddField( + model_name='dynamicsaveinputs', + name='world_int_rate', + field=webapp.apps.taxbrain.models.CommaSeparatedField(default=None, max_length=200, null=True, blank=True), + ), + ] diff --git a/webapp/apps/dynamic/migrations/0017_auto_20171026_2319.py b/webapp/apps/dynamic/migrations/0017_auto_20171026_2319.py new file mode 100644 index 00000000..390c56c8 --- /dev/null +++ b/webapp/apps/dynamic/migrations/0017_auto_20171026_2319.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import webapp.apps.taxbrain.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dynamic', '0016_auto_20171026_1617'), + ] + + operations = [ + migrations.AddField( + model_name='dynamicsaveinputs', + name='budget_balance', + field=models.NullBooleanField(default=None), + ), + migrations.AddField( + model_name='dynamicsaveinputs', + name='debt_ratio_ss', + field=webapp.apps.taxbrain.models.CommaSeparatedField(default=None, max_length=200, null=True, blank=True), + ), + ] diff --git a/webapp/apps/dynamic/models.py b/webapp/apps/dynamic/models.py index b782c6e6..87568260 100644 --- a/webapp/apps/dynamic/models.py +++ b/webapp/apps/dynamic/models.py @@ -20,8 +20,12 @@ class DynamicSaveInputs(models.Model): """ This model contains all the parameters for the dynamic tax model and the tax result. + """ - """ + small_open = models.NullBooleanField(default=None, blank=True, null=True) + world_int_rate = CommaSeparatedField(default=None, null=True, blank=True) + budget_balance = models.NullBooleanField(default=None, blank=True, null=True) + debt_ratio_ss = CommaSeparatedField(default=None, null=True, blank=True) # Parameters used for the dynamic model g_y_annual = CommaSeparatedField(default=None, null=True, blank=True) diff --git a/webapp/apps/dynamic/ogusa_parameters.json b/webapp/apps/dynamic/ogusa_parameters.json index 2572d61a..7feea18a 100644 --- a/webapp/apps/dynamic/ogusa_parameters.json +++ b/webapp/apps/dynamic/ogusa_parameters.json @@ -1 +1,74 @@ -{"g_y_annual": {"col_label": ["Growth rate of technology"], "description": "This parameter gives the underlying growth rate for labor augmenting technological change. The growth rate determines long-run economic growth in OG-USA.", "irs_ref": "", "notes": "", "value": 0.03, "long_name": "Growth rate of technology"}, "frisch": {"col_label": ["Frisch elasticity"], "description": "The Frisch elasticity gives the elasticity of labor supply to changes in the net of tax wage rate, holding wealth constant. This means that the Frisch elasticity captures the substitution effects, but not the income effects, of a change in the net of tax wage rate. A Frisch elasticity of 0.4 would indicate that if the net of tax wage rate increases by 10% then hours worked would increase by 4%, if wealth were held constant. Note that OG-USA uses an elliptical utility function for labor supply. We parameterize this function to fit a constant Frisch elasticity function with a Frisch elasticity as input here.", "irs_ref": "", "notes": "", "validations": {"max": 0.8, "min": 0.1}, "value": 0.4, "long_name": "Frisch elastictiy of labor supply used to fit elliptical utility"}} \ No newline at end of file +{ + "g_y_annual": { + "col_label": [ + "Growth rate of technology" + ], + "description": "This parameter gives the underlying growth rate for labor augmenting technological change. The growth rate determines long-run economic growth in OG-USA.", + "long_name": "Growth rate of technology", + "notes": "", + "value": 0.03, + "irs_ref": "" + }, + "frisch": { + "col_label": [ + "Frisch elasticity" + ], + "description": "The Frisch elasticity gives the elasticity of labor supply to changes in the net of tax wage rate, holding wealth constant. This means that the Frisch elasticity captures the substitution effects, but not the income effects, of a change in the net of tax wage rate. A Frisch elasticity of 0.4 would indicate that if the net of tax wage rate increases by 10% then hours worked would increase by 4%, if wealth were held constant. Note that OG-USA uses an elliptical utility function for labor supply. We parameterize this function to fit a constant Frisch elasticity function with a Frisch elasticity as input here.", + "long_name": "Frisch elastictiy of labor supply used to fit elliptical utility", + "notes": "", + "validations": { + "max": 0.8, + "min": 0.1 + }, + "value": 0.4, + "irs_ref": "" + }, + "world_int_rate": { + "col_label": [ + "World interest rate" + ], + "description": "World interest rate", + "long_name": "World interest rate if using 'small open' economy", + "notes": "World interest rate if using 'small open' economy", + "validations": { + "max": 0.8, + "min": -0.8 + }, + "value": 0.04, + "irs_ref": "" + }, + "debt_ratio_ss": { + "col_label": [ + "Debt Ratio SS" + ], + "description": "Debt Ratio SS", + "long_name": "Debt Ratio SS", + "notes": "Debt Ratio SS", + "validations": { + "max": 0.8, + "min": -0.8 + }, + "value": 0.04, + "irs_ref": "" + }, + "small_open": { + "col_label": [ + "Use 'small open' economy" + ], + "description": "Small open economy assumptionx", + "long_name": "TODO long_name", + "notes": "TODO notes", + "value": false, + "irs_ref": "" + }, + "budget_balance": { + "col_label": [ + "Budget Balance" + ], + "description": "Budget Balance", + "long_name": "TODO long_name", + "notes": "TODO notes", + "value": false, + "irs_ref": "" + } +} diff --git a/webapp/apps/dynamic/ogusa_user_modifiable.json b/webapp/apps/dynamic/ogusa_user_modifiable.json index e4ecc1c5..b6af7949 100644 --- a/webapp/apps/dynamic/ogusa_user_modifiable.json +++ b/webapp/apps/dynamic/ogusa_user_modifiable.json @@ -1 +1 @@ -{"USER_MODIFIABLE_PARAMS": ["g_y_annual", "frisch"]} \ No newline at end of file +{"USER_MODIFIABLE_PARAMS": ["g_y_annual", "frisch", "world_int_rate", "debt_ratio_ss"]} \ No newline at end of file diff --git a/webapp/apps/dynamic/views.py b/webapp/apps/dynamic/views.py index 0c41f9c4..b1cff254 100644 --- a/webapp/apps/dynamic/views.py +++ b/webapp/apps/dynamic/views.py @@ -45,7 +45,8 @@ failure_text, normalize, denormalize, strip_empty_lists, cc_text_finished, cc_text_failure, dynamic_params_from_model, send_cc_email, default_behavior_parameters, - elast_results_to_tables, default_elasticity_parameters) + elast_results_to_tables, default_elasticity_parameters, + filter_ogusa_only) from .compute import DynamicCompute @@ -89,17 +90,15 @@ def dynamic_input(request, pk): if dyn_mod_form.is_valid(): model = dyn_mod_form.save() - #Can't proceed if there is no email address + # Can't proceed if there is no email address if not (request.user.is_authenticated() or model.user_email): - msg = 'Dynamic simulation must have an email address to send notification to!' - return HttpResponse(msg, status=403) - - curr_dict = dict(model.__dict__) - for key, value in curr_dict.items(): - print "got this ", key, value + msg = ("Dynamic simulation must have an email " + "address to send notification to!") + return HttpResponse(msg, status=403) # get macrosim data from form - worker_data = {k:v for k, v in curr_dict.items() if v not in (u'', None, [])} + curr_dict = dict(model.__dict__) + worker_data = parse_fields(curr_dict) #get microsim data outputsurl = OutputUrl.objects.get(pk=pk) @@ -107,36 +106,31 @@ def dynamic_input(request, pk): taxbrain_model = outputsurl.unique_inputs submitted_ids = None + # necessary for simulations before PR 641 if not taxbrain_model.json_text: - taxbrain_dict = dict(taxbrain_model.__dict__) - growth_fixup(taxbrain_dict) - for key, value in taxbrain_dict.items(): - if type(value) == type(unicode()): - try: - taxbrain_dict[key] = [float(x) for x in value.split(',') if x] - except ValueError: - taxbrain_dict[key] = [make_bool(x) for x in value.split(',') if x] - else: - print "missing this: ", key - - - microsim_data = {k:v for k, v in taxbrain_dict.items() if not (v == [] or v == None)} + (reform_dict, _, _, _, + errors_warnings) = get_reform_from_gui( + request, + taxbrain_model=taxbrain_model, + behavior_model=None + ) - #Don't need to pass around the microsim results - if 'tax_result' in microsim_data: - del microsim_data['tax_result'] + else: + reform_dict = json.loads(taxbrain_model.json_text.reform_text) - benefit_switch_fixup(request.REQUEST, microsim_data, taxbrain_model) + # package up variables + ogusa_params = filter_ogusa_only(worker_data) + data = { + 'taxio_format': True, + 'ogusa_params': json.dumps(ogusa_params), + 'user_mods': json.dumps(reform_dict), + 'start_year': int(start_year) + } + # start model run + submitted_ids, guids = dynamic_compute.submit_ogusa_calculation( + data + ) - # start calc job - submitted_ids, guids = dynamic_compute.submit_ogusa_calculation(worker_data, int(start_year), microsim_data) - else: - microsim_data = {"reform": taxbrain_model.json_text.reform_text, "assumptions": taxbrain_model.json_text.assumption_text} - # start calc job - submitted_ids, guids = dynamic_compute.submit_json_ogusa_calculation(worker_data, - int(start_year), - microsim_data, - pack_up_user_mods=False) # TODO: use OutputUrl class if submitted_ids: model.job_ids = denormalize(submitted_ids) diff --git a/webapp/apps/taxbrain/migrations/0055_remove_taxsaveinputs_reform_style.py b/webapp/apps/taxbrain/migrations/0055_remove_taxsaveinputs_reform_style.py new file mode 100644 index 00000000..afa875f0 --- /dev/null +++ b/webapp/apps/taxbrain/migrations/0055_remove_taxsaveinputs_reform_style.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('taxbrain', '0054_outputurl_webapp_vers'), + ] + + operations = [ + migrations.RemoveField( + model_name='taxsaveinputs', + name='reform_style', + ), + ]