def on_failure(self, exc, task_id, args, kwargs, einfo): """ log a bunch of stuff for debugging save message: error and outputs: Scenario: status need to stop rest of chain!? :param exc: The exception raised by the task. :param task_id: Unique id of the failed task. (not the run_uuid) :param args: Original arguments for the task that failed. :param kwargs: Original keyword arguments for the task that failed. :param einfo: ExceptionInfo instance, containing the traceback. :return: None, The return value of this handler is ignored. """ if not isinstance(exc, REoptError): exc_type, exc_value, exc_traceback = sys.exc_info() exc = UnexpectedError(exc_type, exc_value.args[0], exc_traceback, task=self.name, run_uuid=kwargs['run_uuid'], user_uuid=kwargs['data']['inputs'] ['Scenario'].get('user_uuid')) msg = exc.message exc.save_to_db() self.data["messages"]["error"] = msg self.data["outputs"]["Scenario"][ "status"] = "An error occurred. See messages for more." ModelManager.update_scenario_and_messages(self.data, run_uuid=self.run_uuid) self.request.chain = None # stop the chain? self.request.callback = None self.request.chord = None # this seems to stop the infinite chord_unlock call
def proforma(request, run_uuid): try: uuid.UUID(run_uuid) # raises ValueError if not valid uuid except ValueError as e: if e.args[0] == "badly formed hexadecimal UUID string": resp = {"Error": e.args[0]} return JsonResponse(resp, status=400) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value, exc_traceback, task='proforma', run_uuid=run_uuid) err.save_to_db() return JsonResponse({"Error": str(err.message)}, status=400) try: scenario = ScenarioModel.objects.get(run_uuid=run_uuid) if scenario.status.lower() == "optimizing...": return HttpResponse( "Problem is still solving. Please try again later.", status=425) # too early status try: # see if Proforma already created pf = ProForma.objects.get(scenariomodel=scenario) except: pf = ProForma.create(scenariomodel=scenario) pf.generate_spreadsheet() pf.save() wrapper = FileWrapper(open(pf.output_file, "rb")) response = HttpResponse( wrapper, content_type='application/vnd.ms-excel.sheet.macroEnabled.12') response['Content-Length'] = os.path.getsize(pf.output_file) response['Content-Disposition'] = 'attachment; filename=%s' % ( pf.output_file_name) return response except Exception as e: if type(e).__name__ == 'DoesNotExist': msg = "Scenario {} does not exist.".format(run_uuid) return HttpResponse(msg, status=404) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value, exc_traceback, task='proforma', run_uuid=run_uuid) err.save_to_db() return HttpResponse({"Error": str(err.message)}, status=400)
def validate_run_uuid(run_uuid): try: uuid.UUID(run_uuid) # raises ValueError if not valid uuid except ValueError as e: if e.args[0] == "badly formed hexadecimal UUID string": raise ValidationError("Error:" + str(e.args[0])) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], exc_traceback, task='resilience_stats', run_uuid=run_uuid) err.save_to_db() raise ValidationError("Error" + str(err.message))
def load_builder(request): """ Convert the SolarResilient Component Load Builder CSV into an 8760 Load :param request: :return: 8760 list for critical_load_kw input into REOpt """ try: if request.method == 'POST': post = request.body try: # Try to import JSON, then try to import CSV try: loads_table = json.loads(post) except: loads_table = unicode(post, "utf-8") finally: if not isinstance(loads_table, list): csv_reader = csv.DictReader(io.StringIO(loads_table)) loads_table = list(csv_reader) except: return JsonResponse({"Error": "Invalid JSON or CSV"}) # Validation if not check_load_builder_inputs(loads_table): return JsonResponse({ "Error": "There are missing required inputs. Must include the following: 'Power (W)', 'Quantity', '% Run Time', 'Start Mo.', 'Stop Mo.', 'Start Hr.', 'Stop Hr.'" }) if not validate_load_builder_inputs(loads_table): return JsonResponse({"Error": "Some input values are invalid"}) # Run conversion and respond loads_kw = convert_loads(loads_table) return JsonResponse({"critical_loads_kw": loads_kw}) else: return JsonResponse({ "Error": "Must POST a JSON based on the SolarResilient component based load builder downloadable CSV" }) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value, exc_traceback, task='load_builder') err.save_to_db() return JsonResponse({"Error": err.message}, status=500)
def remove(request, run_uuid): try: ModelManager.remove( run_uuid) # ModelManager has some internal exception handling return JsonResponse({"Success": True}, status=204) except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], tb.format_tb(exc_traceback), task='reo.views.results', run_uuid=run_uuid) err.save_to_db() resp = make_error_resp(err.message) return JsonResponse(resp)
def unlink(request, user_uuid, run_uuid): """ Retrieve a summary of scenarios for given user_uuid :param request: :param user_uuid: :return True, bool """ content = {'user_uuid':user_uuid, 'run_uuid':run_uuid} for name, check_id in content.items(): try: uuid.UUID(check_id) # raises ValueError if not valid uuid except ValueError as e: if e.args[0] == "badly formed hexadecimal UUID string": return JsonResponse({"Error": "{} {}".format(name, e.args[0]) }, status=400) else: exc_type, exc_value, exc_traceback = sys.exc_info() if name == 'user_uuid': err = UnexpectedError(exc_type, exc_value, exc_traceback, task='unlink', user_uuid=check_id) if name == 'run_uuid': err = UnexpectedError(exc_type, exc_value, exc_traceback, task='unlink', run_uuid=check_id) err.save_to_db() return JsonResponse({"Error": str(err.message)}, status=400) try: if not ScenarioModel.objects.filter(user_uuid=user_uuid).exists(): return JsonResponse({"Error":"User {} does not exist".format(user_uuid)}, status=400) if not ScenarioModel.objects.filter(run_uuid=run_uuid).exists(): return JsonResponse({"Error":"Run {} does not exist".format(run_uuid)}, status=400) runs = ScenarioModel.objects.filter(run_uuid=run_uuid) if runs.exists(): if runs[0].user_uuid != user_uuid: return JsonResponse({"Error":"Run {} is not associated with user {}".format(run_uuid, user_uuid)}, status=400) if not UserUnlinkedRuns.objects.filter(run_uuid=run_uuid).exists(): UserUnlinkedRuns.create(**content) return JsonResponse({"Success":True}, status=204) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value, exc_traceback, task='unlink', user_uuid=user_uuid) err.save_to_db() return JsonResponse({"Error": err.message}, status=404)
def add_user_uuid(request): """ update the user_uuid associated with a Scenario run_uuid :param request POST: { "user_uuid", "run_uuid" } :return: None """ try: if request.method == 'POST': post = request.body # Try to import JSON try: data = json.loads(post) try: user_uuid = str(data['user_uuid']) run_uuid = str(data['run_uuid']) uuid.UUID(user_uuid) # raises ValueError if not valid uuid uuid.UUID(run_uuid) # raises ValueError if not valid uuid try: scenario = ScenarioModel.objects.filter(run_uuid=run_uuid).first() print (scenario.user_uuid) if scenario.user_uuid is None: ModelManager.add_user_uuid(user_uuid, run_uuid) response = JsonResponse( {"Success": "user_uuid for run_uuid {} has been set to {}".format(run_uuid, user_uuid)}) return response else: return JsonResponse({"Error": "a user_uuid already exists for run_uuid {}".format(run_uuid)}) except: return JsonResponse({"Error": "run_uuid does not exist"}) except: return JsonResponse({"Error": "Invalid inputs: must provide user_uuid and run_uuid key value pairs as valid UUIDs"}) except: return JsonResponse({"Error": "Invalid JSON"}) else: return JsonResponse({"Error": "Must POST a JSON with user_uuid and run_uuid key value pairs"}) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value, exc_traceback, task='add_user_uuid') err.save_to_db() return JsonResponse({"Error": err.message}, status=500)
def results(request, run_uuid): try: uuid.UUID(run_uuid) # raises ValueError if not valid uuid except ValueError as e: if e.args[0] == "badly formed hexadecimal UUID string": resp = make_error_resp(e.args[0]) return JsonResponse(resp, status=400) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], tb.format_tb(exc_traceback), task='results', run_uuid=run_uuid) err.save_to_db() return JsonResponse({"Error": str(err.args[0])}, status=400) try: d = ModelManager.make_response( run_uuid) # ModelManager has some internal exception handling response = JsonResponse(d) return response except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], tb.format_tb(exc_traceback), task='reo.views.results', run_uuid=run_uuid) err.save_to_db() resp = make_error_resp(err.message) return JsonResponse(resp)
def resilience_stats(request, run_uuid=None, financial_check=None): """ Run outage simulator for given run_uuid :param request: :param run_uuid: :return: {"resilience_by_timestep", "resilience_hours_min", "resilience_hours_max", "resilience_hours_avg", "outage_durations", "probs_of_surviving", } """ try: uuid.UUID(run_uuid) # raises ValueError if not valid uuid except ValueError as e: if e.args[0] == "badly formed hexadecimal UUID string": return JsonResponse({"Error": str(e.args[0])}, status=400) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], exc_traceback, task='resilience_stats', run_uuid=run_uuid) err.save_to_db() return JsonResponse({"Error": str(err.message)}, status=400) try: # to run outage simulator scenario = ScenarioModel.objects.get(run_uuid=run_uuid) if scenario.status == "Optimizing...": raise ScenarioOptimizing elif "error" in scenario.status.lower(): raise ScenarioErrored if financial_check == "financial_check": query = request.GET financial_uuid = query['financial_uuid'] scenario = ScenarioModel.objects.get(run_uuid=financial_uuid) if scenario.status == "Optimizing...": raise ScenarioOptimizing elif "error" in scenario.status.lower(): raise ScenarioErrored ## retrieve sizes from db resilience_result = ModelManager.make_response(run_uuid) financial_result = ModelManager.make_response(financial_uuid) resilience_size = parse_system_sizes( resilience_result["outputs"]["Scenario"]["Site"]) financial_size = parse_system_sizes( financial_result["outputs"]["Scenario"]["Site"]) results = simulate_outage( resilience_run_site_result=resilience_size, financial_run_site_result=financial_size, financial_check=financial_check) results = {"survives_specified_outage": results} else: try: query = request.GET bau = query['bau'] in ["True", "true", "1"] except: bau = False wtch = True try: # see if ResilienceModel already created rm = ResilienceModel.objects.get(scenariomodel=scenario) results = model_to_dict(rm) if bau and "probs_of_surviving_bau" not in results: wtch = False raise Exception('no resilience_stat_bau in database') if not bau: for k, v in results.items(): if k[-4:] == "_bau": results.pop(k) # remove items that user does not need del results['scenariomodel'] del results['id'] except: load_profile = LoadProfileModel.objects.filter( run_uuid=scenario.run_uuid).first() gen = GeneratorModel.objects.filter( run_uuid=scenario.run_uuid).first() batt = StorageModel.objects.filter( run_uuid=scenario.run_uuid).first() pv = PVModel.objects.filter(run_uuid=scenario.run_uuid).first() financial = FinancialModel.objects.filter( run_uuid=scenario.run_uuid).first() wind = WindModel.objects.filter( run_uuid=scenario.run_uuid).first() batt_roundtrip_efficiency = batt.internal_efficiency_pct \ * batt.inverter_efficiency_pct \ * batt.rectifier_efficiency_pct results = dict() kwargs_dict = dict() # if wtch and bau: pool = Pool(processes=2 if wtch and bau else 1) # else: # pool = Pool(processes=1) if wtch: kwargs = { "batt_kwh": batt.size_kwh or 0, "batt_kw": batt.size_kw or 0, "pv_kw_ac_hourly": pv.year_one_power_production_series_kw, "wind_kw_ac_hourly": wind.year_one_power_production_series_kw, "init_soc": batt.year_one_soc_series_pct, "critical_loads_kw": load_profile.critical_load_series_kw, "batt_roundtrip_efficiency": batt_roundtrip_efficiency, "diesel_kw": gen.size_kw or 0, "fuel_available": gen.fuel_avail_gal, "b": gen.fuel_intercept_gal_per_hr, "m": gen.fuel_slope_gal_per_kwh, "diesel_min_turndown": gen.min_turn_down_pct } kwargs_dict["wtch"] = kwargs if bau: # only PV and diesel generator may have existing size kwargs = { "batt_kwh": 0, "batt_kw": 0, "pv_kw_ac_hourly": [ p * pv.size_kw * pv.existing_kw for p in pv.year_one_power_production_series_kw ], "critical_loads_kw": load_profile.critical_load_series_kw, "diesel_kw": gen.existing_kw or 0, "fuel_available": gen.fuel_avail_gal, "b": gen.fuel_intercept_gal_per_hr, "m": gen.fuel_slope_gal_per_kwh, "diesel_min_turndown": gen.min_turn_down_pct } kwargs_dict["bau"] = kwargs p = { k: pool.apply_async(simulate_outage, tuple(), v) for k, v in kwargs_dict.items() } pool.close() pool.join() for k, v in p.items(): if k == 'wtch': results.update(v.get()) if k == 'bau': results.update({ key + '_bau': val for key, val in v.get().items() }) """ add avg_crit_ld and pwf to results so that avoided outage cost can be determined as: avoided_outage_costs_us_dollars = resilience_hours_avg * value_of_lost_load_us_dollars_per_kwh * avg_crit_ld * present_worth_factor """ avg_critical_load = round( sum(load_profile.critical_load_series_kw) / len(load_profile.critical_load_series_kw), 5) if load_profile.outage_is_major_event: # assume that outage occurs only once in analysis period present_worth_factor = 1 else: present_worth_factor = annuity( financial.analysis_years, financial.escalation_pct, financial.offtaker_discount_pct) results.update({ "present_worth_factor": present_worth_factor, "avg_critical_load": avg_critical_load, }) try: # new model try: rm = ResilienceModel.create(scenariomodel=scenario) except Exception as e: if isinstance(e, REoptError): return JsonResponse({"Error": e.message}, status=500) raise e ResilienceModel.objects.filter(id=rm.id).update(**results) except IntegrityError: # have run resiliense_stat & bau=false # return both w/tech and bau ResilienceModel.objects.filter(id=rm.id).update(**results) rm = ResilienceModel.objects.get(scenariomodel=scenario) results = model_to_dict(rm) # remove items that user does not need del results['scenariomodel'] del results['id'] results.update({ "help_text": "The present_worth_factor and avg_critical_load are provided such that one can calculate an avoided outage cost in dollars by multiplying a value of load load ($/kWh) times the avg_critical_load, resilience_hours_avg, and present_worth_factor. Note that if the outage event is 'major', i.e. only occurs once, then the present_worth_factor is 1." }) response = JsonResponse(results) return response except ScenarioOptimizing: return JsonResponse( { "Error": "The scenario is still optimizing. Please try again later." }, content_type='application/json', status=500) except ScenarioErrored: return JsonResponse( { "Error": "An error occured in the scenario. Please check the messages from your results." }, content_type='application/json', status=500) except Exception as e: if type(e).__name__ == 'DoesNotExist': msg = "Scenario {} does not exist.".format(run_uuid) return JsonResponse({"Error": msg}, content_type='application/json', status=404) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], exc_traceback, task='resilience_stats', run_uuid=run_uuid) err.save_to_db() return JsonResponse({"Error": err.message}, status=500)
def summary(request, user_uuid): """ Retrieve a summary of scenarios for given user_uuid :param request: :param user_uuid: :return: { "user_uuid", "scenarios": [{ "run_uuid", # Run ID "status", # Status "created", # Date "description", # Description "focus", # Focus "address", # Address "urdb_rate_name", # Utility Tariff "doe_reference_name", # Load Profile "npv_us_dollars", # Net Present Value ($) "net_capital_costs", # DG System Cost ($) "year_one_savings_us_dollars",# Year 1 Savings ($) "pv_kw", # PV Size (kW) "wind_kw", # Wind Size (kW) "gen_kw", # Generator Size (kW) "batt_kw", # Battery Power (kW) "batt_kwh" # Battery Capacity (kWh) "" }] } """ try: uuid.UUID(user_uuid) # raises ValueError if not valid uuid except ValueError as e: if e.args[0] == "badly formed hexadecimal UUID string": return JsonResponse({"Error": str(e.message)}, status=404) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value, exc_traceback, task='summary', user_uuid=user_uuid) err.save_to_db() return JsonResponse({"Error": str(err.message)}, status=404) try: scenarios = ScenarioModel.objects.filter(user_uuid=user_uuid).order_by('-created') unlinked_run_uuids = [i.run_uuid for i in UserUnlinkedRuns.objects.filter(user_uuid=user_uuid)] scenarios = [s for s in scenarios if s.run_uuid not in unlinked_run_uuids] json_response = {"user_uuid": user_uuid, "scenarios": []} if len(scenarios) == 0: response = JsonResponse({"Error": "No scenarios found for user '{}'".format(user_uuid)}, content_type='application/json', status=404) return response scenario_run_uuids = [s.run_uuid for s in scenarios] scenario_run_ids = [s.id for s in scenarios] #saving time by only calling each table once messages = MessageModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid','message_type','message') sites = SiteModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid','address') loads = LoadProfileModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid','outage_start_hour','loads_kw','doe_reference_name') batts = StorageModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid','max_kw','size_kw','size_kwh') pvs = PVModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid','max_kw','size_kw') winds = WindModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid','max_kw','size_kw') gens = GeneratorModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid', 'max_kw', 'size_kw') financials = FinancialModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid','npv_us_dollars','net_capital_costs','lcc_us_dollars','lcc_bau_us_dollars','net_capital_costs_plus_om_us_dollars', 'net_capital_costs','net_om_us_dollars_bau') tariffs = ElectricTariffModel.objects.filter(run_uuid__in=scenario_run_uuids).values('run_uuid','urdb_rate_name','year_one_energy_cost_us_dollars','year_one_demand_cost_us_dollars','year_one_fixed_cost_us_dollars','year_one_min_charge_adder_us_dollars','year_one_bill_us_dollars','year_one_energy_cost_bau_us_dollars','year_one_demand_cost_bau_us_dollars','year_one_fixed_cost_bau_us_dollars','year_one_min_charge_adder_bau_us_dollars','year_one_bill_bau_us_dollars') resiliences = ResilienceModel.objects.filter(scenariomodel_id__in=scenario_run_ids).values('scenariomodel_id','resilience_hours_avg','resilience_hours_max','resilience_hours_min') def get_scenario_data(data, run_uuid): if type(data)==dict: if str(data.get('run_uuid')) == str(run_uuid): return data if str(data.get('scenariomodel_id')) == str(run_uuid): return data result = [s for s in data if str(s.get('run_uuid')) == str(run_uuid)] if len(result) > 0: return result result = [s for s in data if str(s.get('scenariomodel_id')) == str(run_uuid)] if len(result) > 0: return result return [{}] for scenario in scenarios: results = {} message_set = get_scenario_data(messages, scenario.run_uuid) if not type(message_set) == list: message_set = [message_set] site = get_scenario_data(sites, scenario.run_uuid)[0] load = get_scenario_data(loads, scenario.run_uuid)[0] batt = get_scenario_data(batts, scenario.run_uuid)[0] pv = get_scenario_data(pvs, scenario.run_uuid)[0] wind = get_scenario_data(winds, scenario.run_uuid)[0] gen = get_scenario_data(gens, scenario.run_uuid)[0] financial = get_scenario_data(financials, scenario.run_uuid)[0] tariff = get_scenario_data(tariffs, scenario.run_uuid)[0] resilience = get_scenario_data(resiliences, scenario.id)[0] # Messages results['messages'] = {} for message in message_set: if len(message.keys()) > 0: results['messages'][message.get('message_type') or "type"] = message.get('message') or "" # Run ID results['run_uuid'] = str(scenario.run_uuid) # Status results['status'] = scenario.status # Date results['created'] = scenario.created if site: # Description results['description'] = scenario.description # Focus if load['outage_start_hour']: results['focus'] = "Resilience" else: results['focus'] = "Financial" # Address results['address'] = site.get('address') # Utility Tariff if tariff['urdb_rate_name']: results['urdb_rate_name'] = tariff.get('urdb_rate_name') else: results['urdb_rate_name'] = "Custom" # Load Profile if load['loads_kw']: results['doe_reference_name'] = "Custom" else: results['doe_reference_name'] = load.get('doe_reference_name') # NPV results['npv_us_dollars'] = financial.get('npv_us_dollars') # DG System Cost results['net_capital_costs'] = financial.get('net_capital_costs') # Lifecycle Costs results['lcc_us_dollars'] = financial.get('lcc_us_dollars') # Lifecycle Costs BAU results['lcc_bau_us_dollars'] = financial.get('lcc_bau_us_dollars') #Other Financials results['net_capital_costs_plus_om_us_dollars'] = financial.get('net_capital_costs_plus_om_us_dollars') results['net_om_us_dollars_bau'] = financial.get('net_om_us_dollars_bau') results['net_capital_costs'] = financial.get('net_capital_costs') # Year 1 Savings year_one_costs = sum(filter(None, [ tariff.get('year_one_energy_cost_us_dollars') or 0, tariff.get('year_one_demand_cost_us_dollars') or 0, tariff.get('year_one_fixed_cost_us_dollars') or 0, tariff.get('year_one_min_charge_adder_us_dollars') or 0, tariff.get('year_one_bill_us_dollars') or 0 ])) year_one_costs_bau = sum(filter(None, [ tariff.get('year_one_energy_cost_bau_us_dollars') or 0, tariff.get('year_one_demand_cost_bau_us_dollars') or 0, tariff.get('year_one_fixed_cost_bau_us_dollars') or 0, tariff.get('year_one_min_charge_adder_bau_us_dollars') or 0, tariff.get('year_one_bill_bau_us_dollars') or 0 ])) #Resilience Stats results['resilience_hours_min'] = resilience.get('resilience_hours_min') results['resilience_hours_max'] = resilience.get('resilience_hours_max') results['resilience_hours_avg'] = resilience.get('resilience_hours_avg') if results['resilience_hours_max'] is None: results['resilience_hours_max'] = 'not evaluated' if results['resilience_hours_min'] is None: results['resilience_hours_min'] = 'not evaluated' if results['resilience_hours_avg'] is None: results['resilience_hours_avg'] = 'not evaluated' results['year_one_savings_us_dollars'] = year_one_costs_bau - year_one_costs # PV Size if pv is not None: if pv['max_kw'] > 0: results['pv_kw'] = pv.get('size_kw') else: results['pv_kw'] = 'not evaluated' else: results['pv_kw'] = 'not evaluated' # Wind Size if wind is not None: if wind.get('max_kw') or -1 > 0: results['wind_kw'] = wind.get('size_kw') else: results['wind_kw'] = 'not evaluated' else: results['wind_kw'] = 'not evaluated' # Generator Size if gen is not None: if gen.get('max_kw') or -1 > 0: results['gen_kw'] = gen.get('size_kw') else: results['gen_kw'] = 'not evaluated' else: results['gen_kw'] = 'not evaluated' # Battery Size if batt is not None: if batt.get('max_kw') or -1 > 0: results['batt_kw'] = batt.get('size_kw') results['batt_kwh'] = batt.get('size_kwh') else: results['batt_kw'] = 'not evaluated' results['batt_kwh'] = 'not evaluated' else: results['batt_kw'] = 'not evaluated' results['batt_kwh'] = 'not evaluated' json_response['scenarios'].append(results) response = JsonResponse(json_response, status=200) return response except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value, exc_traceback, task='summary', user_uuid=user_uuid) err.save_to_db() return JsonResponse({"Error": err.message}, status=404)
def obj_create(self, bundle, **kwargs): run_uuid = str(uuid.uuid4()) data = dict() data["outputs"] = {"Scenario": {'run_uuid': run_uuid, 'api_version': api_version, 'Profile': {'pre_setup_scenario_seconds': 0, 'setup_scenario_seconds': 0, 'reopt_seconds': 0, 'reopt_bau_seconds': 0, 'parse_run_outputs_seconds': 0}, } } # Setup and start profile profiler = Profiler() uuidFilter = UUIDFilter(run_uuid) log.addFilter(uuidFilter) log.info('Beginning run setup') try: input_validator = ValidateNestedInput(bundle.data) except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], traceback.format_tb(exc_traceback), task='ValidateNestedInput', run_uuid=run_uuid) err.save_to_db() set_status(data, 'Internal Server Error during input validation. No optimization task has been created. Please check your POST for bad values.') data['inputs'] = bundle.data data['messages'] = {} data['messages']['error'] = err.message # "Unexpected Error." log.error("Internal Server error: " + err.message) raise ImmediateHttpResponse(HttpResponse(json.dumps(data), content_type='application/json', status=500)) # internal server error data["inputs"] = input_validator.input_dict data["messages"] = input_validator.messages if not input_validator.isValid: # 400 Bad Request log.debug("input_validator not valid") log.debug(json.dumps(data)) set_status(data, 'Error. No optimization task has been created. See messages for more information. ' \ 'Note that inputs have default values filled in.') if saveToDb: badpost = BadPost(run_uuid=run_uuid, post=json.dumps(bundle.data), errors=str(data['messages'])) badpost.save() raise ImmediateHttpResponse(HttpResponse(json.dumps(data), content_type='application/json', status=400)) log.info('Entering ModelManager') model_manager = ModelManager() profiler.profileEnd() if saveToDb: set_status(data, 'Optimizing...') data['outputs']['Scenario']['Profile']['pre_setup_scenario_seconds'] = profiler.getDuration() if bundle.request.META.get('HTTP_X_API_USER_ID') or False: if bundle.request.META.get('HTTP_X_API_USER_ID') or '' == '6f09c972-8414-469b-b3e8-a78398874103': data['outputs']['Scenario']['job_type'] = 'REopt Lite Web Tool' else: data['outputs']['Scenario']['job_type'] = 'developer.nrel.gov' else: data['outputs']['Scenario']['job_type'] = 'Internal NREL' test_case = bundle.request.META.get('HTTP_USER_AGENT') or '' if test_case.startswith('check_http/'): data['outputs']['Scenario']['job_type'] = 'Monitoring' try: model_manager.create_and_save(data) except Exception as e: log.error("Could not create and save run_uuid: {}\n Data: {}".format(run_uuid,data)) exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], traceback.format_tb(exc_traceback), task='ModelManager.create_and_save', run_uuid=run_uuid) err.save_to_db() set_status(data, "Internal Server Error during saving of inputs. Please see messages.") data['messages']['error'] = err.message # "Unexpected Error." log.error("Internal Server error: " + err.message) raise ImmediateHttpResponse(HttpResponse(json.dumps(data), content_type='application/json', status=500)) # internal server error setup = setup_scenario.s(run_uuid=run_uuid, data=data, raw_post=bundle.data) call_back = process_results.s(data=data, meta={'run_uuid': run_uuid, 'api_version': api_version}) # (use .si for immutable signature, if no outputs were passed from reopt_jobs) rjm = run_jump_model.s(data=data, run_uuid=run_uuid) rjm_bau = run_jump_model.s(data=data, run_uuid=run_uuid, bau=True) log.info("Starting celery chain") try: chain(setup | group(rjm, rjm_bau) | call_back)() except Exception as e: if isinstance(e, REoptError): pass # handled in each task else: # for every other kind of exception exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], traceback.format_tb(exc_traceback), task='api.py', run_uuid=run_uuid) err.save_to_db() set_status(data, 'Internal Server Error. See messages for more.') if 'messages' not in data.keys(): data['messages'] = {} data['messages']['error'] = err.message log.error("Internal Server error: " + err.message) raise ImmediateHttpResponse(HttpResponse(json.dumps(data), content_type='application/json', status=500)) # internal server error log.info("Returning with HTTP 201") raise ImmediateHttpResponse(HttpResponse(json.dumps({'run_uuid': run_uuid}), content_type='application/json', status=201))
def resilience_stats(request: Union[Dict, HttpRequest], run_uuid=None): """ Run outage simulator for given run_uuid :param request: optional parameter for 'bau', boolean :param run_uuid: :return: {"resilience_by_timestep", "resilience_hours_min", "resilience_hours_max", "resilience_hours_avg", "outage_durations", "probs_of_surviving", } Also can GET the same values as above with '_bau' appended if 'bau=true' for the site's existing capacities. """ try: uuid.UUID(run_uuid) # raises ValueError if not valid uuid except ValueError as e: if e.args[0] == "badly formed hexadecimal UUID string": return JsonResponse({"Error": str(e.args[0])}, status=400) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], exc_traceback, task='resilience_stats', run_uuid=run_uuid) err.save_to_db() return JsonResponse({"Error": str(err.message)}, status=400) bau = False # whether or not user wants outage simulator run with existing sizes if isinstance(request, HttpRequest): if request.GET.get('bau') in ["True", "true", "1"]: bau = True elif isinstance(request, dict): bau = request.get("bau") # Safety check; No exception is expected if called after POST-ing to /outagesimjob end point try: scenario = ScenarioModel.objects.get(run_uuid=run_uuid) except ScenarioModel.DoesNotExist: msg = "Scenario {} does not exist.".format(run_uuid) return JsonResponse({"Error": msg}, content_type='application/json', status=404) if scenario.status == "Optimizing...": return JsonResponse( { "Error": "The scenario is still optimizing. Please try again later." }, content_type='application/json', status=404) elif "error" in scenario.status.lower(): return JsonResponse( { "Error": "An error occurred in the scenario. Please check the messages from your results." }, content_type='application/json', status=500) try: # catch all exceptions try: # catch specific exception not_ready_msg = ( 'Outage sim results are not ready. ' 'If you have already submitted an outagesimjob, please try again later. ' 'If not, please first submit an outagesimjob by sending a POST request to ' 'v1/outagesimjob/ with run_uuid and bau parameters. This will generate' ' outage simulation results that you can access from a GET request to the ' 'v1/job/<run uuid>/resilience_stats endpoint. ') not_ready_msg += 'Sample body data for POST-ing to /outagesimjob/: {"run_uuid\": \"6ea30f0f-3723-4fd1-8a3f-bebf8a3e4dbf\", \"bau\": false}' rm = ResilienceModel.objects.get(scenariomodel=scenario) if rm.resilience_by_timestep is None: return JsonResponse({"Error": not_ready_msg}, content_type='application/json', status=404) except ResilienceModel.DoesNotExist: # case for no resilience_stats generated yet return JsonResponse({"Error": not_ready_msg}, content_type='application/json', status=404) else: # ResilienceModel does exist results = model_to_dict(rm) # remove items that user does not need del results['scenariomodel'] del results['id'] if bau and results[ "probs_of_surviving_bau"] is None: # then need to run outage_sim with existing sizes (BAU) bau_results = run_outage_sim(run_uuid, with_tech=False, bau=bau) ResilienceModel.objects.filter(id=rm.id).update(**bau_results) results.update(bau_results) if not bau: # remove BAU results from results dict (if they're there) filtered_dict = { k: v for k, v in results.items() if "_bau" not in k } results = filtered_dict results.update({ "help_text": ("The present_worth_factor and avg_critical_load are provided such" " that one can calculate an avoided outage cost in dollars by multiplying a value " "of load load ($/kWh) by the avg_critical_load, resilience_hours_avg, and present_worth_factor." " Note that if the outage event is 'major' (i.e. only occurs once), then the present_worth_factor is 1." ) }) response = JsonResponse({"outage_sim_results": results}, content_type='application/json', status=200) return response except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], exc_traceback, task='resilience_stats', run_uuid=run_uuid) err.save_to_db() return JsonResponse({"Error": err.message}, status=500)
def financial_check(request, run_uuid=None): """ Check to see if resilience scenario system sizes are the same as financial scenario sizes """ resilience_uuid = request.GET.get('resilience_uuid') if resilience_uuid is None: # preserving old behavior resilience_uuid = run_uuid financial_uuid = request.GET.get('financial_uuid') def parse_system_sizes(site): size_dict = dict() if "Generator" in site: size_dict["Generator"] = site["Generator"]["size_kw"] if "Storage" in site: size_dict["Storage_kw"] = site["Storage"]["size_kw"] size_dict["Storage_kwh"] = site["Storage"]["size_kwh"] if "Wind" in site: size_dict["Wind"] = site["Wind"]["size_kw"] if "PV" in site: size_dict["PV"] = site["PV"]["size_kw"] return size_dict # validate uuid's try: uuid.UUID(str(resilience_uuid)) # raises ValueError if not valid uuid uuid.UUID(str(financial_uuid)) # raises ValueError if not valid uuid except ValueError as e: if e.args[0] == "badly formed hexadecimal UUID string": return JsonResponse({"Error": str(e.args[0])}, status=400) else: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], exc_traceback, task='resilience_stats', run_uuid=resilience_uuid) err.save_to_db() return JsonResponse({"Error": str(err.message)}, status=400) try: resil_scenario = ScenarioModel.objects.get(run_uuid=resilience_uuid) except ScenarioModel.DoesNotExist: msg = "Scenario {} does not exist.".format(resilience_uuid) return JsonResponse({"Error": msg}, content_type='application/json', status=404) if resil_scenario.status == "Optimizing...": return JsonResponse( { "Error": "The resilience scenario is still optimizing. Please try again later." }, content_type='application/json', status=500) elif "error" in resil_scenario.status.lower(): return JsonResponse( { "Error": "An error occurred in the resilience scenario. Please check the messages from your results." }, content_type='application/json', status=500) try: financial_scenario = ScenarioModel.objects.get(run_uuid=financial_uuid) except ScenarioModel.DoesNotExist: msg = "Scenario {} does not exist.".format(financial_uuid) return JsonResponse({"Error": msg}, content_type='application/json', status=404) if financial_scenario.status == "Optimizing...": return JsonResponse( { "Error": "The financial scenario is still optimizing. Please try again later." }, content_type='application/json', status=500) elif "error" in financial_scenario.status.lower(): return JsonResponse( { "Error": "An error occurred in the financial scenario. Please check the messages from your results." }, content_type='application/json', status=500) try: # retrieve sizes from db resilience_result = ModelManager.make_response(resilience_uuid) financial_result = ModelManager.make_response(financial_uuid) resilience_sizes = parse_system_sizes( resilience_result["outputs"]["Scenario"]["Site"]) financial_sizes = parse_system_sizes( financial_result["outputs"]["Scenario"]["Site"]) survives = True if resilience_sizes.keys() == financial_sizes.keys(): for tech, resil_size in resilience_sizes.items(): if float(resil_size - financial_sizes[tech]) / float( max(resil_size, 1)) > 1.0e-3: survives = False break else: survives = False response = JsonResponse({"survives_specified_outage": survives}) except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() err = UnexpectedError(exc_type, exc_value.args[0], exc_traceback, task='resilience_stats', run_uuid=resilience_uuid) err.save_to_db() return JsonResponse({"Error": err.message}, status=500) else: return response