def test_update_vertices(): # TEST_UPDATE_VERTICES() - test method update_vertices(), which for this # base class of LocalAssetModel does practically nothing and must be # redefined by child classes that represent flesible assets. print('Running LocalAssetModel.test_update_vertices()') pf = 'pass' # Create a test Market object. test_market = Market # Create and store a TimeInterval object. dt = datetime.now() # datetime that may be used for most datetime arguments time_interval = TimeInterval(dt, timedelta(hours=1), test_market, dt, dt) test_market.timeIntervals = [time_interval] # Create a test LocalAssetModel object. test_model = LocalAssetModel() # Create and store a scheduled power IntervalValue in the active time interval. test_model.scheduledPowers = [ IntervalValue(test_model, time_interval, test_market, MeasurementType.ScheduledPower, 50)] # Create a LocalAsset object and its maximum and minimum powers. test_object = LocalAsset() test_object.maximumPower = 200 test_object.minimumPower = 0 # Have the LocalAsset model and object cross reference one another. test_object.model = test_model test_model.object = test_object ## TEST 1 print('- Test 1: Basic operation') test_model.update_vertices(test_market) print(' - the method ran without errors') if len(test_model.activeVertices) != 1: pf = 'fail' print(' - there is an unexpected number of active vertices') else: print(' - the expected number of active vertices was found') # Success. print('- the test ran to completion') print('\nResult: #s\n\n', pf)
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])
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)
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_calculate_reserve_margin(): # TEST_LAM_CALCULATE_RESERVE_MARGIN() - a LocalAssetModel ("LAM") class # method NOTE: Reserve margins are introduced but not fully integrated into # code in early template versions. # CASES: # 1. uses hard maximum if no active vertices exist # 2. vertices exist # 2.1 uses maximum vertex power if it is less than hard power constraint # 2.2 uses hard constraint if it is less than maximum vertex power # 2.3 upper flex power is greater than scheduled power assigns correct # positive reserve margin # 2.4 upperflex power less than scheduled power assigns zero value to # reserve margin. print('Running LocalAssetModel.test_calculate_reserve_margin()') pf = 'pass' # Establish test market test_mkt = Market() # Establish test market with an active time interval # Note: modified 1/29/18 due to new TimeInterval constructor dt = datetime.now() at = dt # NOTE: def Hours() corrects behavior of Matlab hours(). dur = timedelta(hours=1) mkt = test_mkt mct = dt # st = datetime(date) st = datetime.combine(date.today(), time()) ti = TimeInterval(at, dur, mkt, mct, st) # Store time interval test_mkt.timeIntervals = [ti] # Establish a test object that is a LocalAsset with assigned maximum power test_object = LocalAsset() test_object.maximumPower = 100 # Establish test object that is a LocalAssetModel test_model = LocalAssetModel() test_model.scheduledPowers = [ IntervalValue(test_model, ti, test_mkt, MeasurementType.ScheduledPower, 0.0)] # Allow object and model to cross-reference one another. test_object.model = test_model test_model.object = test_object # Run the first test case. test_model.calculate_reserve_margin(test_mkt) print('- method ran without errors') if len(test_model.reserveMargins) != 1: raise Exception('- an unexpected number of results were stored') else: print('- one reserve margin was stored, as expected') if test_model.reserveMargins[0].value != test_object.maximumPower: pf = 'fail' raise Exception('- the method did not use the available maximum power') else: print('- the method used maximum power value, as expected') # create some vertices and store them iv = [ IntervalValue(test_model, ti, test_mkt, MeasurementType.Vertex, Vertex(0, 0, -10)), IntervalValue(test_model, ti, test_mkt, MeasurementType.Vertex, Vertex(0, 0, 10)) ] test_model.activeVertices = iv # run test with maximum power greater than maximum vertex test_object.maximumPower = 100 test_model.calculate_reserve_margin(test_mkt) if test_model.reserveMargins[0].value != 10: pf = 'fail' raise Exception('- the method should have used vertex for comparison') else: print('- the method correctly chose to use the vertex power') # run test with maximum power less than maximum vertex test_object.maximumPower = 5 test_model.calculate_reserve_margin(test_mkt) if test_model.reserveMargins[0].value != 5: pf = 'fail' raise Exception('- method should have used maximum power for comparison') else: print('- the method properly chose to use the maximum power') # run test with scheduled power greater than maximum vertex test_model.scheduledPowers[0].value = 20 test_object.maximumPower = 500 test_model.calculate_reserve_margin(test_mkt) if test_model.reserveMargins[0].value != 0: pf = 'fail' raise Exception('- method should have assigned zero for a neg. result') else: print('- the method properly assigned 0 for a negative result') # Success. print('- the test ran to completion') print('\nResult: #s\n\n', pf)
def test_schedule(): print('Running AbstractModel.test_schedule()') pf = 'pass' # Create a test market test_mkt test_mkt = Market() # Create a sample time interval ti dt = datetime.now() at = dt # NOTE: Function Hours() corrects behavior of Matlab hours(). dur = timedelta(hours=1) mkt = test_mkt mct = dt # NOTE: Function Hours() corrects behavior of Matlab hours(). st = datetime.combine(date.today(), time()) + timedelta(hours=20) ti = TimeInterval(at, dur, mkt, mct, st) # Save the time interval test_mkt.timeIntervals = [ti] # Assign a marginal price in the time interval test_mkt.check_marginal_prices() # Create a Neighbor test object and give it a default maximum power value test_obj = Neighbor() test_obj.maximumPower = 100 # Create a corresponding NeighborModel test_mdl = NeighborModel() # Make sure that the model and object cross-reference one another test_obj.model = test_mdl test_mdl.object = test_obj # Run a test with a NeighborModel object print('- running test with a NeighborModel:') test_mdl.schedule(test_mkt) print(' - the method encountered no errors') if len(test_mdl.scheduledPowers) != 1: pf = 'fail' raise ' - the method did not store a scheduled power' else: print(' - the method calculated and stored a scheduled power') if len(test_mdl.reserveMargins) != 1: pf = 'fail' raise ' - the method did not store a reserve margin' else: print(' - the method stored a reserve margin') if len(test_mdl.activeVertices) != 1: pf = 'fail' raise ' - the method did not store an active vertex' else: print(' - the method stored an active vertex') # Run a test again with a LocalAssetModel object test_obj = LocalAsset() test_obj.maximumPower = 100 test_mdl = LocalAssetModel() test_obj.model = test_mdl test_mdl.object = test_obj print('- running test with a LocalAssetModel:') test_mdl.schedule(test_mkt) print(' - the method encountered no errors') if len(test_mdl.scheduledPowers) != 1: pf = 'fail' raise ' - the method did not store a scheduled power' else: print(' - the method calculated and stored a scheduled power') if len(test_mdl.reserveMargins) != 1: pf = 'fail' raise ' - the method did not store a reserve margin' else: print(' - the method stored a reserve margin') if len(test_mdl.activeVertices) != 1: pf = 'fail' raise ' - the method did not store an active vertex' else: print(' - the method stored an active vertex') # Success print('- the test ran to completion') print('Result: %s', pf)
def test_update_costs(): print('Running AbstractModel.test_update_costs()') pf = 'pass' # Create a test market test_mkt test_mkt = Market() # Create a sample time interval ti dt = datetime.now() at = dt # NOTE: Function Hours() corrects behavior of Matlab hours(). dur = timedelta(hours=1) mkt = test_mkt mct = dt st = datetime.combine(date.today(), time()) + timedelta(hours=20) ti = TimeInterval(at, dur, mkt, mct, st) # Save the time interval test_mkt.timeIntervals = [ti] # Assign a marginal price in the time interval test_mkt.check_marginal_prices() # Create a Neighbor test object and give it a default maximum power value test_obj = Neighbor() # test_obj.maximumPower = 100 # Create a corresponding NeighborModel test_mdl = NeighborModel() # Make sure that the model and object cross-reference one another test_obj.model = test_mdl test_mdl.object = test_obj test_mdl.scheduledPowers = [ IntervalValue(test_mdl, ti, test_mkt, MeasurementType.ScheduledPower, 100) ] test_mdl.activeVertices = [ IntervalValue(test_mdl, ti, test_mkt, MeasurementType.ActiveVertex, Vertex(0.05, 0, 100)) ] # Run a test with a NeighborModel object print('- running test with a NeighborModel:') try: test_mdl.update_costs(test_mkt) print(' - the method encountered no errors') except: pf = 'fail' raise ' - the method did not run without errors' if len(test_mdl.productionCosts) != 1: pf = 'fail' raise ' - the method did not store a production cost' else: print(' - the method calculated and stored a production cost') if len(test_mdl.dualCosts) != 1: pf = 'fail' raise ' - the method did not store a dual cost' else: print(' - the method stored a dual cost') if test_mdl.totalProductionCost != sum( [x.value for x in test_mdl.productionCosts]): pf = 'fail' raise ' - the method did not store a total production cost' else: print(' - the method stored an total production cost') if test_mdl.totalDualCost != sum([x.value for x in test_mdl.dualCosts]): pf = 'fail' raise ' - the method did not store a total dual cost' else: print(' - the method stored an total dual cost') # Run a test again with a LocalAssetModel object test_obj = LocalAsset() # test_obj.maximumPower = 100 test_mdl = LocalAssetModel() test_obj.model = test_mdl test_mdl.object = test_obj test_mdl.scheduledPowers = [ IntervalValue(test_mdl, ti, test_mkt, MeasurementType.ScheduledPower, 100) ] test_mdl.activeVertices = [ IntervalValue(test_mdl, ti, test_mkt, MeasurementType.ActiveVertex, Vertex(0.05, 0, 100)) ] print('- running test with a LocalAssetModel:') try: test_mdl.update_costs(test_mkt) print(' - the method encountered no errors') except: pf = 'fail' raise ' - the method did not run without errors' if len(test_mdl.productionCosts) != 1: pf = 'fail' raise ' - the method did not store a production cost' else: print(' - the method calculated and stored a production cost') if len(test_mdl.dualCosts) != 1: pf = 'fail' raise ' - the method did not store a dual cost' else: print(' - the method stored a dual cost') if test_mdl.totalProductionCost != sum( [x.value for x in test_mdl.productionCosts]): pf = 'fail' raise ' - the method did not store a total production cost' else: print(' - the method stored an total production cost') if test_mdl.totalDualCost != sum([x.value for x in test_mdl.dualCosts]): pf = 'fail' raise ' - the method did not store a total dual cost' else: print(' - the method stored an total dual cost') # Success print('- the test ran to completion') print('Result: %s', pf)
def test_sum_vertices(): print('Running Market.test_sum_vertices()') pf = 'pass' # Create a test myTransactiveNode object. test_node = myTransactiveNode() # Create a test Market object. test_market = Market() # List the test market with the test_node. test_node.markets = test_market # Create and store a time interval to work with. dt = datetime.now() at = dt dur = timedelta(hours=1) mkt = test_market mct = dt st = dt time_interval = TimeInterval(at, dur, mkt, mct, st) test_market.timeIntervals = [time_interval] # Create test LocalAsset and LocalAssetModel objects test_asset = LocalAsset() test_asset_model = LocalAssetModel() # Add the test_asset to the test node list. test_node.localAssets = [test_asset] # Have the test asset and its model cross reference one another. test_asset.model = test_asset_model test_asset_model.object = test_asset # Create and store an active Vertex or two for the test asset test_vertex = [Vertex(0.2, 0, -110), Vertex(0.2, 0, -90)] interval_values = [ IntervalValue(test_node, time_interval, test_market, MeasurementType.ActiveVertex, test_vertex[0]), IntervalValue(test_node, time_interval, test_market, MeasurementType.ActiveVertex, test_vertex[1]) ] test_asset_model.activeVertices = [interval_values[0], interval_values[1] ] # interval_value(1:2) # Create test Neighbor and NeighborModel objects. test_neighbor = Neighbor() test_neighbor_model = NeighborModel() # Add the test neighbor to the test node list. test_node.neighbors = [test_neighbor] # Have the test neighbor and its model cross reference one another. test_neighbor.model = test_neighbor_model test_neighbor.model.object = test_neighbor # Create and store an active Vertex or two for the test neighbor test_vertex.append(Vertex(0.1, 0, 0)) test_vertex.append(Vertex(0.3, 0, 200)) interval_values.append( IntervalValue(test_node, time_interval, test_market, MeasurementType.ActiveVertex, test_vertex[2])) interval_values.append( IntervalValue(test_node, time_interval, test_market, MeasurementType.ActiveVertex, test_vertex[3])) test_neighbor_model.activeVertices = [ interval_values[2], interval_values[3] ] ## Case 1 print('- Case 1: Basic case with interleaved vertices') # Run the test. try: vertices = test_market.sum_vertices(test_node, time_interval) print(' - the method ran without errors') except: pf = 'fail' print(' - the method had errors when called and stopped') if len(vertices) != 4: pf = 'fail' print(' - an unexpected number of vertices was returned') else: print(' - the expected number of vertices was returned') powers = [x.power for x in vertices] # if any(~ismember(single(powers), single([-110.0000, -10.0000, 10.0000, 110.0000]))) if len([ x for x in powers if round(x, 4) not in [-110.0000, -10.0000, 10.0000, 110.0000] ]) > 0: pf = 'fail' print(' - the vertex powers were not as expected') else: print(' - the vertex powers were as expected') marginal_prices = [round(x.marginalPrice, 4) for x in vertices] # if any(~ismember(single(marginal_prices), single([0.1000, 0.2000, 0.3000]))) if len([ x for x in marginal_prices if round(x, 4) not in [0.1000, 0.2000, 0.3000] ]) > 0: pf = 'fail' print(' - the vertex powers were not as expected') else: print(' - the vertex marginal prices were as expected') ## CASE 2: NEIGHBOR MODEL TO BE EXCLUDED # This case is needed when a demand or supply curve must be created for a # transactive Neighbor object. The active vertices of the target Neighbor # must be excluded, leaving a residual supply or demand curve against which # the Neighbor may plan. print('- Case 2: Exclude test Neighbor model') # Run the test. try: # [vertices] = test_market.sum_vertices(test_node, time_interval, test_neighbor_model) vertices = test_market.sum_vertices(test_node, time_interval, test_neighbor_model) print(' - the method ran without errors') except: pf = 'fail' print(' - the method encountered errors and stopped') if len(vertices) != 2: pf = 'fail' print(' - an unexpected number of vertices was returned') else: print(' - the expected number of vertices was returned') powers = [round(x.power, 4) for x in vertices] # if any(~ismember(single(powers), single([-110.0000, -90.0000]))) if len([x for x in powers if x not in [-110.0000, -90.0000]]) > 0: pf = 'fail' print(' - the vertex powers were not as expected') else: print(' - the vertex powers were as expected') marginal_prices = [x.marginalPrice for x in vertices] # if any(~ismember(single(marginal_prices), single([0.2000]))) if len([x for x in marginal_prices if round(x, 4) not in [0.2000]]) > 0: pf = 'fail' print(' - the vertex powers were not as expected') else: print(' - the vertex marginal prices were as expected') ## CASE 3: CONSTANT SHOULD NOT CREATE NEW NET VERTEX print('- Case 3: Include a constant vertex. No net vertex should be added') # Change the test asset to NOT have any flexibility. A constant should # not introduce a net vertex at a constant's marginal price. Marginal # price is NOT meaningful for an inelastic device. test_asset_model.activeVertices = [interval_values[0]] # Run the test. try: # [vertices] = test_market.sum_vertices(test_node, time_interval) vertices = test_market.sum_vertices(test_node, time_interval) print(' - the method ran without errors') except: pf = 'fail' print(' - the method encountered errors and stopped') #%[180907DJH: THIS TEST IS CORRECTED. THE NEIGHBOR HAS TWO VERTICES. ADDING #AN ASSET WITH ONE VERTEX (NO FLEXIBILITY) SHOULD NOT CHANGE THE NUMBER OF #ACTIVE VERTICES, SO THE CORRECTED TEST CONFIRMS TWO VERTICES. THE CODE HAS #BEEN CORRECTED ACCORDINGLY.] if len(vertices) != 2: pf = 'fail' print(' - an unexpected number of vertices was returned') else: print(' - the expected number of vertices was returned') powers = [x.power for x in vertices] # if any(~ismember(single(powers), single([-110.0000, 90]))) if len([x for x in powers if round(x, 4) not in [-110.0000, 90]]) > 0: pf = 'fail' print(' - the vertex powers were not as expected') else: print(' - the vertex powers were as expected') marginal_prices = [x.marginalPrice for x in vertices] # if any(~ismember(single(marginal_prices), single([0.1000, 0.3000, Inf]))) if len([ x for x in marginal_prices if round(x, 4) not in [0.1000, 0.3000, float("inf")] ]) > 0: pf = 'fail' print(' - the vertex powers were not as expected') else: print(' - the vertex marginal prices were as expected') # CASE 4: More than two vertices at any marginal price print('- Case 4: More than two vertices at same marginal price') # Move the two active vertices of the test asset to be at the same # marginal price as one of the neighbor active vertices. test_vertex = [Vertex(0.1, 0, -110), Vertex(0.1, 0, -90)] interval_values = [ IntervalValue(test_node, time_interval, test_market, MeasurementType.ActiveVertex, test_vertex[0]), IntervalValue(test_node, time_interval, test_market, MeasurementType.ActiveVertex, test_vertex[1]) ] test_asset_model.activeVertices = [interval_values[0], interval_values[1] ] # interval_value(1:2) # Run the test. try: vertices = test_market.sum_vertices(test_node, time_interval) print(' - the method ran without errors') except: pf = 'fail' print(' - the method encountered errors and stopped') if len(vertices) != 3: pf = 'fail' print(' - an unexpected number of vertices was returned') else: print(' - the expected number of vertices was returned') powers = [x.power for x in vertices] # if any(~ismember(single(powers), single([-110.0000, -90.0000, 110.0000]))) if len([ x for x in powers if round(x, 4) not in [-110.0000, -90.0000, 110.0000] ]) > 0: pf = 'fail' print(' - the vertex powers were not as expected') else: print(' - the vertex powers were as expected') marginal_prices = [x.marginalPrice for x in vertices] # if any(~ismember(single(marginal_prices), single([0.1000, 0.3000]))) if len([x for x in marginal_prices if round(x, 4) not in [0.1000, 0.3000] ]) > 0: pf = 'fail' print(' - the vertex powers were not as expected') else: print(' - the vertex marginal prices were as expected') # Success print('- the test ran to completion') print('Result: #s\n\n', pf)