Example #1
0
    def make_supplier(self):
        # Add supplier
        supplier = Neighbor()
        supplier.name = 'BPA'
        supplier.description = 'The Bonneville Power Administration as electricity supplier to the City of Richland, WA'
        supplier.lossFactor = self.supplier_loss_factor
        supplier.maximumPower = 200800  # [avg.kW, twice the average COR load]
        supplier.minimumPower = 0.0  # [avg.kW, will not export]

        # Add supplier model
        supplierModel = BulkSupplier_dc()
        supplierModel.name = 'BPAModel'
        #supplierModel.demandThreshold = 0.75 * supplier.maximumPower
        supplierModel.converged = False  # Dynamically assigned
        supplierModel.convergenceThreshold = 0  # Not yet implemented
        supplierModel.effectiveImpedance = 0.0  # Not yet implemented
        supplierModel.friend = False  # Separate business entity from COR
        supplierModel.transactive = False  # Not a transactive neighbor

        # Add vertices
        # The first default vertex is, for now, based on the flat COR rate to
        # PNNL. The second vertex includes 2# losses at a maximum power that
        # is twice the average electric load for COR. This is helpful to
        # ensure that a unique price, power point will be found. In this
        # model the recipient pays the cost of energy losses.
        # The first vertex is based on BPA Jan HLH rate at zero power
        # importation.
        d1 = Vertex(0, 0, 0)  # create first default vertex
        d1.marginalPrice = 0.04196  # HLH BPA rate Jan 2018 [$/kWh]
        d1.cost = 2000.0  # Const. price shift to COR customer rate [$/h]
        d1.power = 0.0  # [avg.kW]
        # The second default vertex represents imported and lost power at a power
        # value presumed to be the maximum deliverable power from BPA to COR.
        d2 = Vertex(0, 0, 0)  # create second default vertex
        # COR pays for all sent power but receives an amount reduced by
        # losses. This creates a quadratic term in the production cost and
        # a slope to the marginal price curve.
        d2.marginalPrice = d1.marginalPrice / (1 - supplier.lossFactor
                                               )  # [$/kWh]
        # From the perspective of COR, it receives the power sent by BPA,
        # less losses.
        d2.power = (1 -
                    supplier.lossFactor) * supplier.maximumPower  # [avg.kW]
        # The production costs can be estimated by integrating the
        # marginal-price curve.
        d2.cost = d1.cost + d2.power * (d1.marginalPrice + 0.5 *
                                        (d2.marginalPrice - d1.marginalPrice)
                                        )  # [$/h]
        supplierModel.defaultVertices = [d1, d2]

        #   COST PARAMTERS
        #     A constant cost parameter is being used here to account for the
        #     difference between wholesale BPA rates to COR and COR distribution
        #     rates to customers like PNNL. A constant of $2,000/h steps the rates
        #     from about 0.04 $/kWh to about 0.06 $/kWh. This may be refined later.
        #     IMPORTANT: This shift has no affect on marginal pricing.
        supplierModel.costParameters[0] = 2000.0  # [$/h]

        # Cross-reference object & model
        supplierModel.object = supplier
        supplier.model = supplierModel

        # Meter
        bpaElectricityMeter = MeterPoint()  # Instantiate an electricity meter
        bpaElectricityMeter.name = 'BpaElectricityMeter'
        bpaElectricityMeter.description = 'BPA electricity to COR'
        bpaElectricityMeter.measurementType = MeasurementType.PowerReal
        bpaElectricityMeter.measurementUnit = MeasurementUnit.kWh
        supplierModel.meterPoints = [bpaElectricityMeter]

        return supplier
Example #2
0
    def init_objects(self):
        # Add meter
        meter = MeterPoint()
        meter.measurementType = MeasurementType.PowerReal
        meter.name = 'CoRElectricMeter'
        meter.measurementUnit = MeasurementUnit.kWh
        self.meterPoints.append(meter)

        # Add weather forecast service
        weather_service = TemperatureForecastModel(self.config_path, self)
        self.informationServiceModels.append(weather_service)

        # Add inelastive asset
        # Source: https://www.ci.richland.wa.us/home/showdocument?id=1890
        #   Residential customers: 23,768
        #   Electricity sales in 2015:
        # Total:     100.0#   879,700,000 kWh     100,400 avg. kW)
        # Resident:   46.7    327,200,000          37,360
        # Gen. Serv.: 38.1    392,300,000          44,790
        # Industrial: 15.2    133,700,000          15,260
        # Irrigation:  2.4     21,110,000           2,410
        # Other:       0.6      5,278,000             603
        #   2015 Res. rate: $0.0616/kWh
        #   Avg. annual residential cust. use: 14,054 kWh
        #   Winter peak, 160,100 kW (1.6 x average)
        #   Summer peak, 180,400 kW (1.8 x average)
        #   Annual power supply expenses: $35.5M
        # *************************************************************************
        inelastive_load = LocalAsset()
        inelastive_load.name = 'InelasticLoad'
        inelastive_load.maximumPower = -50000  # Remember that a load is a negative power [kW]
        inelastive_load.minimumPower = -200000  # Assume twice the averag PNNL load [kW]

        # Add inelastive asset model
        inelastive_load_model = OpenLoopRichlandLoadPredictor(weather_service)
        inelastive_load_model.name = 'InelasticLoadModel'
        inelastive_load_model.defaultPower = -100420  # [kW]
        inelastive_load_model.defaultVertices = [
            Vertex(float("inf"), 0.0, -100420.0)
        ]

        # Cross-reference asset & asset model
        inelastive_load_model.object = inelastive_load
        inelastive_load.model = inelastive_load_model

        # Add inelastic as city's asset
        self.localAssets.extend([inelastive_load])

        # Add Market
        market = Market()
        market.name = 'dayAhead'
        market.commitment = False
        market.converged = False
        market.defaultPrice = 0.0428  # [$/kWh]
        market.dualityGapThreshold = self.duality_gap_threshold  # [0.02 = 2#]
        market.initialMarketState = MarketState.Inactive
        market.marketOrder = 1  # This is first and only market
        market.intervalsToClear = 1  # Only one interval at a time
        market.futureHorizon = timedelta(
            hours=24)  # Projects 24 hourly future intervals
        market.intervalDuration = timedelta(
            hours=1)  # [h] Intervals are 1 h long
        market.marketClearingInterval = timedelta(hours=1)  # [h]
        market.marketClearingTime = Timer.get_cur_time().replace(
            hour=0, minute=0, second=0,
            microsecond=0)  # Aligns with top of hour
        market.nextMarketClearingTime = market.marketClearingTime + timedelta(
            hours=1)
        self.markets.append(market)

        self.campus = self.make_campus()
        supplier = self.make_supplier()

        # Add campus as city's neighbor
        self.neighbors.extend([self.campus, supplier])
Example #3
0
    def init_objects(self):
        # Add meter
        # meter = MeterPoint()
        # meter.name = 'BuildingElectricMeter'
        # meter.measurementType = MeasurementType.PowerReal
        # meter.measurementUnit = MeasurementUnit.kWh
        # self.meterPoints.append(meter)

        # Add weather forecast service
        weather_service = TemperatureForecastModel(self.config_path, self)
        self.informationServiceModels.append(weather_service)

        # # Add inelastive asset
        # inelastive_load = LocalAsset()
        # inelastive_load.name = 'InelasticBldgLoad'
        # inelastive_load.maximumPower = 0  # Remember that a load is a negative power [kW]
        # inelastive_load.minimumPower = -200
        #
        # # Add inelastive asset model
        # inelastive_load_model = LocalAssetModel()
        # inelastive_load_model.name = 'InelasticBuildingModel'
        # inelastive_load_model.defaultPower = -100  # [kW]
        # inelastive_load_model.defaultVertices = [Vertex(float("inf"), 0, -100, True)]
        #
        # # Cross-reference asset & asset model
        # inelastive_load_model.object = inelastive_load
        # inelastive_load.model = inelastive_load_model

        # Add elastive asset
        elastive_load = LocalAsset()
        elastive_load.name = 'TccLoad'
        elastive_load.maximumPower = 0  # Remember that a load is a negative power [kW]
        elastive_load.minimumPower = -self.max_deliver_capacity

        # Add inelastive asset model
        # self.elastive_load_model = LocalAssetModel()
        self.elastive_load_model = TccModel()
        self.elastive_load_model.name = 'TccModel'
        self.elastive_load_model.defaultPower = -0.5*self.max_deliver_capacity  # [kW]
        self.elastive_load_model.defaultVertices = [Vertex(0.055, 0, -self.elastive_load_model.defaultPower, True),
                                                    Vertex(0.06, 0, -self.elastive_load_model.defaultPower/2, True)]

        # Cross-reference asset & asset model
        self.elastive_load_model.object = elastive_load
        elastive_load.model = self.elastive_load_model

        # Add inelastive and elastive loads as building' assets
        self.localAssets.extend([elastive_load])

        # Add Market
        market = Market()
        market.name = 'dayAhead'
        market.commitment = False
        market.converged = False
        market.defaultPrice = 0.0428  # [$/kWh]
        market.dualityGapThreshold = self.duality_gap_threshold  # [0.02 = 2#]
        market.initialMarketState = MarketState.Inactive
        market.marketOrder = 1  # This is first and only market
        market.intervalsToClear = 1  # Only one interval at a time
        market.futureHorizon = timedelta(hours=24)  # Projects 24 hourly future intervals
        market.intervalDuration = timedelta(hours=1)  # [h] Intervals are 1 h long
        market.marketClearingInterval = timedelta(hours=1)  # [h]
        market.marketClearingTime = Timer.get_cur_time().replace(hour=0,
                                                                 minute=0,
                                                                 second=0,
                                                                 microsecond=0)  # Aligns with top of hour
        market.nextMarketClearingTime = market.marketClearingTime + timedelta(hours=1)
        self.markets.append(market)

        # Campus object
        campus = Neighbor()
        campus.name = 'PNNL_Campus'
        campus.description = 'PNNL_Campus'
        campus.maximumPower = self.max_deliver_capacity
        campus.minimumPower = 0.  # [avg.kW]
        campus.lossFactor = self.campus_loss_factor

        # Campus model
        campus_model = NeighborModel()
        campus_model.name = 'PNNL_Campus_Model'
        campus_model.location = self.name
        campus_model.defaultVertices = [Vertex(0.045, 25, 0, True), Vertex(0.048, 0, self.max_deliver_capacity, True)]
        campus_model.demand_threshold_coef = self.demand_threshold_coef
        # campus_model.demandThreshold = self.demand_threshold_coef * self.monthly_peak_power
        campus_model.demandThreshold = self.monthly_peak_power
        campus_model.transactive = True
        campus_model.inject(self, system_loss_topic=self.system_loss_topic)

        # Avg building meter
        building_meter = MeterPoint()
        building_meter.name = self.name + ' ElectricMeter'
        building_meter.measurementType = MeasurementType.AverageDemandkW
        building_meter.measurementUnit = MeasurementUnit.kWh
        campus_model.meterPoints.append(building_meter)

        # Cross-reference object & model
        campus_model.object = campus
        campus.model = campus_model
        self.campus = campus

        # Add campus as building's neighbor
        self.neighbors.append(campus)
Example #4
0
    def init_objects(self):
        # Add meter
        meter = MeterPoint()
        meter.measurementType = MeasurementType.PowerReal
        meter.name = 'CampusElectricityMeter'
        meter.measurementUnit = MeasurementUnit.kWh
        self.meterPoints.append(meter)

        # Add weather forecast service
        weather_service = TemperatureForecastModel(self.config_path, self)
        self.informationServiceModels.append(weather_service)

        # Add inelastive asset
        inelastive_load = LocalAsset()
        inelastive_load.name = 'InelasticBuildings'  # Campus buildings that are not responsive
        inelastive_load.maximumPower = 0  # Remember that a load is a negative power [kW]
        inelastive_load.minimumPower = -2 * 8200  # Assume twice the average PNNL load [kW]

        # Add inelastive asset model
        inelastive_load_model = OpenLoopPnnlLoadPredictor(weather_service)
        inelastive_load_model.name = 'InelasticBuildingsModel'
        inelastive_load_model.engagementCost = [
            0, 0, 0
        ]  # Transition costs irrelevant
        inelastive_load_model.defaultPower = -6000  # [kW]
        inelastive_load_model.defaultVertices = [Vertex(0, 0, -6000.0, 1)]

        # Cross-reference asset & asset model
        inelastive_load_model.object = inelastive_load
        inelastive_load.model = inelastive_load_model

        # Add solar PV asset
        solar_pv = SolarPvResource()
        solar_pv.maximumPower = self.PV_max_kW  # [avg.kW]
        solar_pv.minimumPower = 0.0  # [avg.kW]
        solar_pv.name = 'SolarPv'
        solar_pv.description = '120 kW solar PV site on the campus'

        # Add solar PV asset model
        solar_pv_model = SolarPvResourceModel()
        solar_pv_model.cloudFactor = 1.0  # dimensionless
        solar_pv_model.engagementCost = [0, 0, 0]
        solar_pv_model.name = 'SolarPvModel'
        solar_pv_model.defaultPower = 0.0  # [avg.kW]
        solar_pv_model.defaultVertices = [Vertex(0, 0, 30.0, True)]
        solar_pv_model.costParameters = [0, 0, 0]
        solar_pv_model.inject(self, power_topic=self.solar_topic)

        # Cross-reference asset & asset model
        solar_pv.model = solar_pv_model
        solar_pv_model.object = solar_pv

        # Add inelastive and solar_pv as campus' assets
        self.localAssets.extend([inelastive_load, solar_pv])

        # Add Market
        market = Market()
        market.name = 'dayAhead'
        market.commitment = False
        market.converged = False
        market.defaultPrice = 0.04  # [$/kWh]
        market.dualityGapThreshold = self.duality_gap_threshold  # [0.02 = 2#]
        market.initialMarketState = MarketState.Inactive
        market.marketOrder = 1  # This is first and only market
        market.intervalsToClear = 1  # Only one interval at a time
        market.futureHorizon = timedelta(
            hours=24)  # Projects 24 hourly future intervals
        market.intervalDuration = timedelta(
            hours=1)  # [h] Intervals are 1 h long
        market.marketClearingInterval = timedelta(hours=1)  # [h]
        market.marketClearingTime = Timer.get_cur_time().replace(
            hour=0, minute=0, second=0,
            microsecond=0)  # Aligns with top of hour
        market.nextMarketClearingTime = market.marketClearingTime + timedelta(
            hours=1)
        self.markets.append(market)

        # City object
        city = Neighbor()
        city.name = 'CoR'
        city.description = 'City of Richland (COR) electricity supplier node'
        city.maximumPower = 20000  # Remember loads have negative power [avg.kW]
        city.minimumPower = 0  # [avg.kW]
        city.lossFactor = self.city_loss_factor

        # City model
        city_model = NeighborModel()
        city_model.name = 'CoR_Model'
        city_model.location = self.name
        city_model.transactive = True
        city_model.defaultPower = 10000  # [avg.kW]
        city_model.defaultVertices = [
            Vertex(0.046, 160, 0, True),
            Vertex(0.048,
                   160 + city.maximumPower * (0.046 + 0.5 * (0.048 - 0.046)),
                   city.maximumPower, True)
        ]
        city_model.costParameters = [0, 0, 0]
        city_model.demand_threshold_coef = self.demand_threshold_coef
        city_model.demandThreshold = self.monthly_peak_power
        city_model.inject(self,
                          system_loss_topic=self.system_loss_topic,
                          dc_threshold_topic=self.dc_threshold_topic)

        # Cross-reference object & model
        city_model.object = city
        city.model = city_model
        self.city = city

        # Add city as campus' neighbor
        self.neighbors.append(city)

        # Add buildings
        for bldg_name in self.building_names:
            bldg_neighbor = self.make_bldg_neighbor(bldg_name)
            self.neighbors.append(bldg_neighbor)
def test_update_dc_threshold():
    print('Running BulkSupplier_dc.test_update_dc_threshold()')
    pf = 'pass'

    ## Basic configuration for tests:
    # Create a test object and initialize demand-realted properties
    test_obj = BulkSupplier_dc()
    test_obj.demandMonth = datetime.now().month  # month(datetime)
    test_obj.demandThreshold = 1000

    # Create a test market   
    test_mkt = Market()

    # Create and store two time intervals
    dt = datetime.now()
    at = dt
    dur = timedelta(hours=1)  # Hours(1)
    mkt = test_mkt
    mct = dt
    st = dt
    ti = [TimeInterval(at, dur, mkt, mct, st)]
    st = st + dur
    ti.append(TimeInterval(at, dur, mkt, mct, st))
    test_mkt.timeIntervals = ti

    ##  Test case when there is no MeterPoint object  
    test_obj.demandThreshold = 1000
    test_obj.demandMonth = datetime.now().month  # month(datetime)
    test_obj.meterPoints = []  # MeterPoint.empty

    # Create and store a couple scheduled powers
    # iv(1) = IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 900)
    # iv(2) = IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    iv = [
        IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 900),
        IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    ]
    test_obj.scheduledPowers = iv

    try:
        test_obj.update_dc_threshold(test_mkt)
        print('- the method ran without errors')
    except:
        pf = 'fail'
        print('- the method encountered errors when called')

    if test_obj.demandThreshold != 1000:
        pf = 'fail'
        print('- the method inferred the wrong demand threshold value')
    else:
        print('- the method properly kept the old demand threshold value with no meter')

    iv = [
        IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 1100),
        IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    ]
    test_obj.scheduledPowers = iv

    try:
        test_obj.update_dc_threshold(test_mkt)
        print('- the method ran without errors when there is no meter')
    except:
        pf = 'fail'
        print('- the method encountered errors when there is no meter')

    if test_obj.demandThreshold != 1100:
        pf = 'fail'
        print('- the method did not update the inferred demand threshold value')
    else:
        print('- the method properly updated the demand threshold value with no meter')

    ## Test with an appropriate MeterPoint meter
    # Create and store a MeterPoint test object
    test_mtr = MeterPoint()
    test_mtr.measurementType = MeasurementType.AverageDemandkW
    test_mtr.currentMeasurement = 900
    test_obj.meterPoints = [test_mtr]

    # Reconfigure the test object for this test:
    iv = [
        IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 900),
        IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    ]
    test_obj.scheduledPowers = iv

    test_obj.demandThreshold = 1000
    test_obj.demandMonth = datetime.now().month

    # Run the test. Confirm it runs.
    try:
        test_obj.update_dc_threshold(test_mkt)
        print('- the method ran without errors when there is a meter')
    except:
        pf = 'fail'
        print('- the method encountered errors when there is a meter')

    # Check that the old threshold is correctly retained.
    if test_obj.demandThreshold != 1000:
        pf = 'fail'
        print('- the method failed to keep the correct demand threshold value when there is a meter')
    else:
        print('- the method properly kept the old demand threshold value when there is a meter')

    # Reconfigure the test object with a lower current threshold
    iv = [
        IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 900),
        IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    ]
    test_obj.scheduledPowers = iv
    test_obj.demandThreshold = 800

    # Run the test.
    test_obj.update_dc_threshold(test_mkt)

    # Check that a new, higher demand threshold was set.
    if test_obj.demandThreshold != 900:
        pf = 'fail'
        print(['- the method failed to update the demand threshold value when there is a meter'])
    else:
        print('- the method properly updated the demand threshold value when there is a meter')

    ## Test rollover to new month
    # Configure the test object
    test_obj.demandMonth = dt + relativedelta.relativedelta(months=-1)  # month(datetime - days(31))  # prior month
    test_obj.demandThreshold = 1000
    test_obj.scheduledPowers[0].value = 900
    test_obj.scheduledPowers[1].value = 900
    test_obj.meterPoints = []  # MeterPoint.empty

    # Run the test
    test_obj.update_dc_threshold(test_mkt)

    # See if the demand threshold was reset at the new month.
    if test_obj.demandThreshold != 0.8 * 1000:
        pf = 'fail'
        print('- the method did not reduce the threshold properly in a new month')
    else:
        print('- the method reduced the threshold properly in a new month')

    # Success
    print('- the test ran to completion')
    print('Result: #s\n\n', pf)
Example #6
0
def test_update_dc_threshold():
    print('Running BulkSupplier_dc.test_update_dc_threshold()')

    # Basic configuration for tests:
    # Create a test object and initialize demand-related properties
    test_obj = BulkSupplier_dc()
    test_obj.demandMonth = datetime.now().month  # month(datetime)
    test_obj.demandThreshold = 1000

    # Create a test market
    test_mkt = Market()

    # Create and store two time intervals
    dt = datetime.now()
    at = dt
    dur = timedelta(hours=1)  # Hours(1)
    mkt = test_mkt
    mct = dt
    st = dt
    ti = [TimeInterval(at, dur, mkt, mct, st)]
    st = st + dur
    ti.append(TimeInterval(at, dur, mkt, mct, st))
    test_mkt.timeIntervals = ti

    #  Test case when there is no MeterPoint object
    test_obj.demandThreshold = 1000
    test_obj.demandMonth = datetime.now().month  # month(datetime)
    test_obj.meterPoints = []  # MeterPoint.empty

    # Create and store a couple scheduled powers
    # iv(1) = IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 900)
    # iv(2) = IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    iv = [
        IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 900),
        IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    ]
    test_obj.scheduledPowers = iv

    try:
        test_obj.update_dc_threshold(test_mkt)
        print('- the method ran without errors')
    except RuntimeWarning:
        print('- the method encountered errors when called')

    assert test_obj.demandThreshold == 1000, '- the method inferred the wrong demand threshold value'

    iv = [
        IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 1100),
        IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    ]
    test_obj.scheduledPowers = iv

    try:
        test_obj.update_dc_threshold(test_mkt)
        print('- the method ran without errors when there is no meter')
    except RuntimeWarning:
        print('- the method encountered errors when there is no meter')

    assert test_obj.demandThreshold == 1100, '- the method did not update the inferred demand threshold value'

    # Test with an appropriate MeterPoint meter
    # Create and store a MeterPoint test object
    test_mtr = MeterPoint()
    test_mtr.measurementType = MeasurementType.AverageDemandkW
    test_mtr.currentMeasurement = 900
    test_obj.meterPoints = [test_mtr]

    # Reconfigure the test object for this test:
    iv = [
        IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 900),
        IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)
    ]
    test_obj.scheduledPowers = iv

    test_obj.demandThreshold = 1000
    test_obj.demandMonth = datetime.now().month

    # Run the test. Confirm it runs.
    try:
        test_obj.update_dc_threshold(test_mkt)
        print('- the method ran without errors when there is a meter')
    except RuntimeWarning:
        print('- the method encountered errors when there is a meter')

    # Check that the old threshold is correctly retained.
    assert test_obj.demandThreshold == 1000, \
                            '- the method failed to keep the correct demand threshold value when there is a meter'

    # Reconfigure the test object with a lower current threshold
    iv = [
        IntervalValue(test_obj, ti[0], test_mkt, MeasurementType.ScheduledPower, 900),
        IntervalValue(test_obj, ti[1], test_mkt, MeasurementType.ScheduledPower, 900)]
    test_obj.scheduledPowers = iv
    test_obj.demandThreshold = 800

    # Run the test.
    test_obj.update_dc_threshold(test_mkt)

    # Check that a new, higher demand threshold was set.
    assert test_obj.demandThreshold == 900, \
                                    '- the method failed to update the demand threshold value when there is a meter'

    # Test rollover to new month
    # Configure the test object
    last_month = dt.month - 1
    if last_month == 0:
        last_month = 12
    test_obj.demandMonth = last_month  # month(datetime - days(31))  # prior month
    test_obj.demandThreshold = 1000
    test_obj.scheduledPowers[0].value = 900
    test_obj.scheduledPowers[1].value = 900
    test_obj.meterPoints = []  # MeterPoint.empty
    test_obj.demandThresholdCoef = 0.8

    # Run the test
    try:
        test_obj.update_dc_threshold(test_mkt)
        print('  - The method ran without errors')
    except RuntimeWarning:
        print('  - ERRORS ENCOUNTERED')

    # See if the demand threshold was reset at the new month.
    assert test_obj.demandThreshold == test_obj.demandThresholdCoef * 1000, \
        '- the method did not reduce the threshold properly in a new month'

    # Success
    print('test_update_dc_threshold() ran to completion.\n')