def test_basic_usage(site): ws = get_weather_source(site) assert ws.usaf_id == '722880'
def test_bad_zip(site_bad_zip): ws = get_weather_source(site_bad_zip) assert ws is None
def evaluate(self, meter_input, formatter=None, model=None, weather_source=None, weather_normal_source=None): ''' Main entry point to the meter, which models traces and calculates derivatives. Parameters ---------- meter_input : dict Serialized input containing trace and project data. formatter : tuple of (class, dict), default None Formatter for trace and weather data. Used to create input for model. If None is provided, will be auto-matched to appropriate default formatter. Class name can be provided as a string (class.__name__) or object. model : tuple of (class, dict), default None Model to use in modeling. If None is provided, will be auto-matched to appropriate default model. Class can be provided as a string (class.__name__) or class object. weather_source : eemeter.weather.WeatherSource Weather source to be used for this meter. Overrides weather source found using :code:`project.site`. Useful for test mocking. weather_normal_source : eemeter.weather.WeatherSource Weather normal source to be used for this meter. Overrides weather source found using :code:`project.site`. Useful for test mocking. Returns ------- results : dict Dictionary of results with the following keys: - :code:`"status"`: SUCCESS/FAILURE - :code:`"failure_message"`: if FAILURE, message indicates reason for failure, may include traceback - :code:`"logs"`: list of collected log messages - :code:`"model_class"`: Name of model class - :code:`"model_kwargs"`: dict of model keyword arguments (settings) - :code:`"formatter_class"`: Name of formatter class - :code:`"formatter_kwargs"`: dict of formatter keyword arguments (settings) - :code:`"eemeter_version"`: version of the eemeter package - :code:`"modeled_energy_trace"`: modeled energy trace - :code:`"derivatives"`: derivatives for each interpretation - :code:`"weather_source_station"`: Matched weather source station. - :code:`"weather_normal_source_station"`: Matched weather normal source station. ''' SUCCESS = "SUCCESS" FAILURE = "FAILURE" output = OrderedDict([ ("status", None), ("failure_message", None), ("logs", []), ("eemeter_version", get_version()), ("trace_id", None), ("project_id", None), ("interval", None), ("meter_kwargs", self.kwargs), ("model_class", None), ("model_kwargs", None), ("formatter_class", None), ("formatter_kwargs", None), ("weather_source_station", None), ("weather_normal_source_station", None), ("derivatives", None), ("modeled_energy_trace", None), ]) # Step 1: Deserialize input and validate deserialized_input = deserialize_meter_input(meter_input) if "error" in deserialized_input: message = ("Meter input could not be deserialized:\n{}".format( deserialized_input)) output['status'] = FAILURE output['failure_message'] = message return output # Assume that deserialized input fails without these keys, so don't # bother error checking trace = deserialized_input["trace"] project = deserialized_input["project"] zipcode = project["zipcode"] site = ZIPCodeSite(zipcode) # Can be blank for models capable of structural change analysis, so # provide default modeling_period_set = project.get("modeling_period_set", None) project_id = project["project_id"] trace_id = trace.trace_id interval = trace.interval output['project_id'] = project_id output['trace_id'] = trace_id output['interval'] = interval logger.debug('Running meter for for trace {} and project {}'.format( project_id, trace_id)) # Step 2: Match weather use_cz2010 = (self.weather_station_mapping == 'CZ2010') if weather_source is None: weather_source = get_weather_source(site, use_cz2010=use_cz2010) if weather_source is None: message = ( "Could not find weather normal source matching site {}". format(site)) weather_source_usaf_id = None else: message = "Using weather_source {}".format(weather_source) weather_source_usaf_id = weather_source.usaf_id else: message = "Using supplied weather_source" weather_source_usaf_id = weather_source.usaf_id output['weather_source_station'] = weather_source_usaf_id output['logs'].append(message) logger.debug(message) if weather_normal_source is None: use_cz2010 = (self.weather_normal_station_mapping == 'CZ2010') weather_normal_source = get_weather_normal_source( site, use_cz2010=use_cz2010) if weather_normal_source is None: message = ( "Could not find weather normal source matching site {}". format(site)) weather_normal_source_usaf_id = None else: message = ("Using weather_normal_source {}".format( weather_normal_source)) weather_normal_source_usaf_id = weather_normal_source.usaf_id else: message = "Using supplied weather_normal_source" weather_normal_source_usaf_id = weather_normal_source.usaf_id output['weather_normal_source_station'] = weather_normal_source_usaf_id output['logs'].append(message) logger.debug(message) # Step 3: Check to see if trace is placeholder. If so, # return with SUCCESS, empty derivatives. if trace.placeholder: message = ( 'Skipping modeling for placeholder trace {}'.format(trace)) logger.info(message) output['logs'].append(message) output['status'] = SUCCESS output['derivatives'] = [] return output # Step 4: Determine trace interpretation and frequency # TODO use trace interval here. And enforce upstream that interval use # pandas interval strings? trace_frequency = get_approximate_frequency(trace) if trace_frequency not in ['H', 'D', '15T', '30T']: trace_frequency = None selector = (trace.interpretation, trace_frequency) # Step 5: create formatter instance FormatterClass, formatter_kwargs = self._get_formatter( formatter, selector) if FormatterClass is None: message = ("Default formatter mapping did not find a match for the" " selector {}".format(selector)) output['status'] = FAILURE output['failure_message'] = message return output output["formatter_class"] = FormatterClass.__name__ output["formatter_kwargs"] = formatter_kwargs formatter_instance = FormatterClass(**formatter_kwargs) # Step 6: create model instance ModelClass, model_kwargs = self._get_model(model, selector) if ModelClass is None: message = ("Default model mapping did not find a match for the" " selector {}".format(selector)) output['status'] = FAILURE output['failure_message'] = message return output output["model_class"] = ModelClass.__name__ output["model_kwargs"] = model_kwargs # Step 7: validate modeling period set. Always fails for now, since # no models are yet fully structural change analysis aware if modeling_period_set is None: message = ( "Model is not structural-change capable, so `modeling_period`" " argument must be supplied.") output['status'] == FAILURE output['failure_message'] = message return output # Step 8: create split modeled energy trace model_mapping = { modeling_period_label: ModelClass(modeling_period_interpretation=modeling_period_label, **model_kwargs) for modeling_period_label, _ in modeling_period_set.iter_modeling_periods() } modeled_trace = SplitModeledEnergyTrace(trace, formatter_instance, model_mapping, modeling_period_set) modeled_trace.fit(weather_source) output["modeled_energy_trace"] = \ serialize_split_modeled_energy_trace(modeled_trace) # Step 9: for each modeling period group, create derivatives derivative_freq = 'D' if 'freq_str' in formatter_kwargs.keys() and \ formatter_kwargs['freq_str'] == 'H': derivative_freq = 'H' derivatives = [] for ((baseline_label, reporting_label), (baseline_period, reporting_period)) in \ modeling_period_set.iter_modeling_period_groups(): raw_derivatives = [] deriv_input = unpack(modeled_trace, baseline_label, reporting_label, baseline_period, reporting_period, weather_source, weather_normal_source, site, derivative_freq=derivative_freq) if deriv_input is None: continue raw_derivatives.extend([ hdd_balance_point_baseline(deriv_input), hdd_coefficient_baseline(deriv_input), cdd_balance_point_baseline(deriv_input), cdd_coefficient_baseline(deriv_input), intercept_baseline(deriv_input), hdd_balance_point_reporting(deriv_input), hdd_coefficient_reporting(deriv_input), cdd_balance_point_reporting(deriv_input), cdd_coefficient_reporting(deriv_input), intercept_reporting(deriv_input), cumulative_baseline_model_minus_reporting_model_normal_year( deriv_input), baseline_model_minus_reporting_model_normal_year(deriv_input), cumulative_baseline_model_normal_year(deriv_input), baseline_model_normal_year(deriv_input), cumulative_baseline_model_reporting_period(deriv_input), baseline_model_reporting_period(deriv_input), masked_baseline_model_reporting_period(deriv_input), cumulative_baseline_model_minus_observed_reporting_period( deriv_input), baseline_model_minus_observed_reporting_period(deriv_input), masked_baseline_model_minus_observed_reporting_period( deriv_input), baseline_model_baseline_period(deriv_input), cumulative_reporting_model_normal_year(deriv_input), reporting_model_normal_year(deriv_input), reporting_model_reporting_period(deriv_input), cumulative_observed_reporting_period(deriv_input), observed_reporting_period(deriv_input), masked_observed_reporting_period(deriv_input), cumulative_observed_baseline_period(deriv_input), observed_baseline_period(deriv_input), observed_project_period(deriv_input), temperature_baseline_period(deriv_input), temperature_reporting_period(deriv_input), masked_temperature_reporting_period(deriv_input), temperature_normal_year(deriv_input), baseline_mask(deriv_input), reporting_mask(deriv_input), reporting_period_resource_curve(deriv_input) ]) resource_curve_normal_year = normal_year_resource_curve( deriv_input) raw_derivatives.extend([resource_curve_normal_year]) if resource_curve_normal_year is not None: resource_curve_normal_year = pd.Series( resource_curve_normal_year['value'], index=pd.to_datetime( resource_curve_normal_year['orderable'])) raw_derivatives.extend([ normal_year_co2_avoided(deriv_input, resource_curve_normal_year) ]) derivatives += [ Derivative( (baseline_label, reporting_label), d['series'], reduce(lambda a, b: a + ' ' + b, d['description'].split()), d['orderable'], d['value'], d['variance'], ) for d in raw_derivatives if d is not None ] output["derivatives"] = serialize_derivatives(derivatives) output["status"] = SUCCESS return output
def test_basic_usage(site): ws = get_weather_source(site) assert ws.station == '722880'
def test_bad_zip(project_bad_zip): ws = get_weather_source(project_bad_zip) assert ws is None
def test_basic_usage(project): ws = get_weather_source(project) assert ws.station == '722880'
def evaluate(self, project, weather_source=None, weather_normal_source=None): ''' Main entry point to the meter, taking in project data and returning results indicating energy efficiency performance. Parameters ---------- project : eemeter.structures.Project Project for which energy effienciency performance is to be evaluated. weather_source : eemeter.weather.WeatherSource Weather source to be used for this meter. Overrides weather source found using :code:`project.site`. Useful for test mocking. weather_normal_source : eemeter.weather.WeatherSource Weather normal source to be used for this meter. Overrides weather source found using :code:`project.site`. Useful for test mocking. Returns ------- out : dict Results of energy efficiency evaluation, organized into the following items. - :code:`"modeling_period_set"`: :code:`eemeter.structures.ModelingPeriodSet` determined from this project. - :code:`"modeled_energy_traces"`: dict of dispatched modeled energy traces. - :code:`"modeled_energy_trace_derivatives"`: derivatives for each modeled energy trace. - :code:`"project_derivatives"`: Project summaries for derivatives. ''' modeling_period_set = get_modeling_period_set(project.interventions) if weather_source is None: weather_source = get_weather_source(project) else: logger.info("Using supplied weather_source") if weather_normal_source is None: weather_normal_source = get_weather_normal_source(project) else: logger.info("Using supplied weather_normal_source") dispatches = get_energy_modeling_dispatches(modeling_period_set, project.energy_trace_set) derivatives = {} for trace_label, modeled_energy_trace in dispatches.items(): trace_derivatives = {} derivatives[trace_label] = trace_derivatives if modeled_energy_trace is None: continue modeled_energy_trace.fit(weather_source) for group_label, (_, reporting_period) in \ modeling_period_set.iter_modeling_period_groups(): period_derivatives = { "BASELINE": {}, "REPORTING": {}, } trace_derivatives[group_label] = \ period_derivatives baseline_label, reporting_label = group_label baseline_output = modeled_energy_trace.fit_outputs[ baseline_label] reporting_output = modeled_energy_trace.fit_outputs[ reporting_label] if baseline_output["status"] == "SUCCESS": awn = modeled_energy_trace.compute_derivative( baseline_label, annualized_weather_normal, weather_normal_source=weather_normal_source) if awn is not None: period_derivatives["BASELINE"].update(awn) gp = modeled_energy_trace.compute_derivative( baseline_label, gross_predicted, weather_source=weather_source, reporting_period=reporting_period) if gp is not None: period_derivatives["BASELINE"].update(gp) if reporting_output["status"] == "SUCCESS": awn = modeled_energy_trace.compute_derivative( reporting_label, annualized_weather_normal, weather_normal_source=weather_normal_source) if awn is not None: period_derivatives["REPORTING"].update(awn) gp = modeled_energy_trace.compute_derivative( reporting_label, gross_predicted, weather_source=weather_source, reporting_period=reporting_period) if gp is not None: period_derivatives["REPORTING"].update(gp) project_derivatives = self._get_project_derivatives( modeling_period_set, project.energy_trace_set, derivatives) return { "modeling_period_set": modeling_period_set, "modeled_energy_traces": dispatches, "modeled_energy_trace_derivatives": derivatives, "project_derivatives": project_derivatives, "weather_source": weather_source, "weather_normal_source": weather_normal_source, }