Exemple #1
0
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))

# -- Project information -----------------------------------------------------

from eemeter import get_version

project = "eemeter"
copyright = "2016-2018, Open Energy Efficiency, Inc."
author = "Phil Ngo"

# The short X.Y version
version = get_version()
# The full version, including alpha/beta/rc tags
release = version

# -- General configuration ---------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.intersphinx",
Exemple #2
0
    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
Exemple #3
0
def test_get_version():
    assert get_version().startswith("2")
    def run_meter(self, meter_class='EnergyEfficiencyMeter',
                  meter_settings=None, project_run=None,
                  weather_source=None, weather_normal_source=None):
        """
        If possible, run the meter specified by meter_class.

        Parameters
        ----------
        meter_class : str
            One of the keys in METER_CLASS_CHOICES
        meter_settings : dict
            Dictionary of extra settings to send to the meter.

        Returns
        -------
        project_result : datastore.models.ProjectResult
            Object containing results of meter run.
        """
        try:
            project = self.eemeter_project()
        except ValueError:
            message = (
                "Cannot create eemeter project; skipping project id={}."
                .format(self.project_id)
            )
            warn(message)
            return None

        meter = self._get_meter(meter_class, settings=meter_settings)
        results = meter.evaluate(project, weather_source=weather_source,
                                 weather_normal_source=weather_normal_source)

        weather_source = results['weather_source']
        if weather_source is not None:
            weather_source_station = weather_source.station
        else:
            weather_source_station = None

        weather_normal_source = \
            results['weather_normal_source']
        if weather_normal_source is not None:
            weather_normal_source_station = weather_normal_source.station
        else:
            weather_normal_source_station = None

        project_result = ProjectResult.objects.create(
            project=self,
            project_run=project_run,
            eemeter_version=get_version(),
            meter_class=meter_class,
            meter_settings=meter_settings,
            weather_source_station=weather_source_station,
            weather_normal_source_station=weather_normal_source_station,
        )

        modeling_period_mapping = {}

        for modeling_period_label, modeling_period in \
                results['modeling_period_set'].iter_modeling_periods():
            mp = ModelingPeriod.objects.create(
                project_result=project_result,
                interpretation=modeling_period.interpretation,
                start_date=modeling_period.start_date,
                end_date=modeling_period.end_date,
            )
            modeling_period_mapping[modeling_period_label] = mp

        modeling_period_group_mapping = {}
        for (baseline_label, reporting_label), _ in \
                results['modeling_period_set'].iter_modeling_period_groups():
            mp_baseline = modeling_period_mapping[baseline_label]
            mp_reporting = modeling_period_mapping[reporting_label]
            mpg = ModelingPeriodGroup.objects.create(
                project_result=project_result,
                baseline_period=mp_baseline,
                reporting_period=mp_reporting,
            )
            modeling_period_group_mapping[
                (baseline_label, reporting_label)] = mpg

        # one result per trace per model
        energy_trace_model_result_mapping = {}
        for trace_label, modeled_energy_trace in \
                results['modeled_energy_traces'].items():
            for model_label, outputs in \
                    modeled_energy_trace.fit_outputs.items():

                etm = EnergyTraceModelResult.objects.create(
                    project_result=project_result,
                    energy_trace_id=trace_label,
                    modeling_period=modeling_period_mapping[model_label],
                    model_serializiation=None,
                    status=outputs['status'],
                    r2=outputs.get('r2'),
                    rmse=outputs.get('rmse'),
                    cvrmse=outputs.get('cvrmse'),
                    upper=outputs.get('upper'),
                    lower=outputs.get('lower'),
                    n=outputs.get('n'),
                )
                energy_trace_model_result_mapping[
                    (trace_label, model_label)] = etm

        aggregation_interpretations = [
            'annualized_weather_normal',
            'gross_predicted',
        ]

        for trace_label, modeling_period_group_derivatives in \
                results['modeled_energy_trace_derivatives'].items():

            # get all modeling period derivatives
            modeling_period_derivatives = {}
            for (baseline_label, reporting_label), derivatives in \
                    modeling_period_group_derivatives.items():
                modeling_period_derivatives[baseline_label] = \
                    derivatives['BASELINE']
                modeling_period_derivatives[reporting_label] = \
                    derivatives['REPORTING']

            for modeling_period_label, derivatives in \
                    modeling_period_derivatives.items():

                for interpretation in aggregation_interpretations:

                    derivative = derivatives.get(interpretation, None)

                    energy_trace_model_res = \
                        energy_trace_model_result_mapping[
                            (trace_label, modeling_period_label)]

                    if derivative is not None:
                        Derivative.objects.create(
                            energy_trace_model_result=energy_trace_model_res,
                            interpretation=interpretation,
                            value=derivative[0],
                            upper=derivative[1],
                            lower=derivative[2],
                            n=derivative[3],
                        )

        # one result per aggregation - baseline + reporting?
        for group_label, group_derivatives in \
                results['project_derivatives'].items():
            for name, named_results in group_derivatives.items():
                if named_results is None:
                    continue

                for interpretation in aggregation_interpretations:

                    baseline_output = \
                        named_results['BASELINE'][interpretation]
                    reporting_output = \
                        named_results['REPORTING'][interpretation]

                    modeling_period_group = \
                        modeling_period_group_mapping[group_label]

                    DerivativeAggregation.objects.create(
                        project_result=project_result,
                        modeling_period_group=modeling_period_group,
                        trace_interpretation=name,
                        interpretation=interpretation,
                        baseline_value=baseline_output[0],
                        baseline_upper=baseline_output[1],
                        baseline_lower=baseline_output[2],
                        baseline_n=baseline_output[3],
                        reporting_value=reporting_output[0],
                        reporting_upper=reporting_output[1],
                        reporting_lower=reporting_output[2],
                        reporting_n=reporting_output[3],
                    )

        return project_result
Exemple #5
0
    def run_meter(self,
                  meter_class='EnergyEfficiencyMeter',
                  meter_settings=None,
                  project_run=None,
                  weather_source=None,
                  weather_normal_source=None):
        """
        If possible, run the meter specified by meter_class.

        Parameters
        ----------
        meter_class : str
            One of the keys in METER_CLASS_CHOICES
        meter_settings : dict
            Dictionary of extra settings to send to the meter.

        Returns
        -------
        project_result : datastore.models.ProjectResult
            Object containing results of meter run.
        """
        try:
            project = self.eemeter_project()
        except ValueError:
            message = ("Cannot create eemeter project; skipping project id={}."
                       .format(self.project_id))
            warn(message)
            return None

        meter = self._get_meter(meter_class, settings=meter_settings)
        results = meter.evaluate(project,
                                 weather_source=weather_source,
                                 weather_normal_source=weather_normal_source)

        weather_source = results['weather_source']
        if weather_source is not None:
            weather_source_station = weather_source.station
        else:
            weather_source_station = None

        weather_normal_source = \
            results['weather_normal_source']
        if weather_normal_source is not None:
            weather_normal_source_station = weather_normal_source.station
        else:
            weather_normal_source_station = None

        project_result = ProjectResult.objects.create(
            project=self,
            project_run=project_run,
            eemeter_version=get_version(),
            meter_class=meter_class,
            meter_settings=meter_settings,
            weather_source_station=weather_source_station,
            weather_normal_source_station=weather_normal_source_station,
        )

        modeling_period_mapping = {}

        for modeling_period_label, modeling_period in \
                results['modeling_period_set'].iter_modeling_periods():
            mp = ModelingPeriod.objects.create(
                project_result=project_result,
                interpretation=modeling_period.interpretation,
                start_date=modeling_period.start_date,
                end_date=modeling_period.end_date,
            )
            modeling_period_mapping[modeling_period_label] = mp

        modeling_period_group_mapping = {}
        for (baseline_label, reporting_label), _ in \
                results['modeling_period_set'].iter_modeling_period_groups():
            mp_baseline = modeling_period_mapping[baseline_label]
            mp_reporting = modeling_period_mapping[reporting_label]
            mpg = ModelingPeriodGroup.objects.create(
                project_result=project_result,
                baseline_period=mp_baseline,
                reporting_period=mp_reporting,
            )
            modeling_period_group_mapping[(baseline_label,
                                           reporting_label)] = mpg

        # one result per trace per model
        energy_trace_model_result_mapping = {}
        for trace_label, modeled_energy_trace in \
                results['modeled_energy_traces'].items():
            for model_label, outputs in \
                    modeled_energy_trace.fit_outputs.items():

                etm = EnergyTraceModelResult.objects.create(
                    project_result=project_result,
                    energy_trace_id=trace_label,
                    modeling_period=modeling_period_mapping[model_label],
                    model_serializiation=None,
                    status=outputs['status'],
                    r2=outputs.get('r2'),
                    rmse=outputs.get('rmse'),
                    cvrmse=outputs.get('cvrmse'),
                    upper=outputs.get('upper'),
                    lower=outputs.get('lower'),
                    n=outputs.get('n'),
                )
                energy_trace_model_result_mapping[(trace_label,
                                                   model_label)] = etm

        aggregation_interpretations = [
            'annualized_weather_normal',
            'gross_predicted',
        ]

        for trace_label, modeling_period_group_derivatives in \
                results['modeled_energy_trace_derivatives'].items():

            # get all modeling period derivatives
            modeling_period_derivatives = {}
            for (baseline_label, reporting_label), derivatives in \
                    modeling_period_group_derivatives.items():
                modeling_period_derivatives[baseline_label] = \
                    derivatives['BASELINE']
                modeling_period_derivatives[reporting_label] = \
                    derivatives['REPORTING']

            for modeling_period_label, derivatives in \
                    modeling_period_derivatives.items():

                for interpretation in aggregation_interpretations:

                    derivative = derivatives.get(interpretation, None)

                    energy_trace_model_res = \
                        energy_trace_model_result_mapping[
                            (trace_label, modeling_period_label)]

                    if derivative is not None:
                        Derivative.objects.create(
                            energy_trace_model_result=energy_trace_model_res,
                            interpretation=interpretation,
                            value=derivative[0],
                            upper=derivative[1],
                            lower=derivative[2],
                            n=derivative[3],
                        )

        # one result per aggregation - baseline + reporting?
        for group_label, group_derivatives in \
                results['project_derivatives'].items():
            for name, named_results in group_derivatives.items():
                if named_results is None:
                    continue

                for interpretation in aggregation_interpretations:

                    baseline_output = \
                        named_results['BASELINE'][interpretation]
                    reporting_output = \
                        named_results['REPORTING'][interpretation]

                    modeling_period_group = \
                        modeling_period_group_mapping[group_label]

                    DerivativeAggregation.objects.create(
                        project_result=project_result,
                        modeling_period_group=modeling_period_group,
                        trace_interpretation=name,
                        interpretation=interpretation,
                        baseline_value=baseline_output[0],
                        baseline_upper=baseline_output[1],
                        baseline_lower=baseline_output[2],
                        baseline_n=baseline_output[3],
                        reporting_value=reporting_output[0],
                        reporting_upper=reporting_output[1],
                        reporting_lower=reporting_output[2],
                        reporting_n=reporting_output[3],
                    )

        return project_result
Exemple #6
0
def test_get_version():
    assert get_version() is not None
Exemple #7
0
# you can simply install eemeter by pip install eemeter
import eemeter
eemeter.get_version()