def update_information(ism, mkt): # Gather active time intervals ti ti = mkt.timeIntervals # index through active time intervals ti for i in range(len(ti)): # for i = 1:length(ti) # Get the start time for the indexed time interval st = ti(i).startTime # Extract the starting time hour hr = st.hour # Look up the value in a table. NOTE: tables may be used during # development until active information services are developed. # Is the purpose of this one to read MODERATE weather temperature? YES T = readtable(ism.file) value = T(hr + 1, 1) # Check whether the information exists in the indexed time interval # Question: what is ism? InformationServiceModel doesn't have 'values' as well as 'iv' properties. # Suggestion: use long name as much as possible # Need an example on what this one does. Really need a unit test here? #iv = findobj(ism.values, 'timeInterval', ti(i)) iv = [x for x in ism.values if x.timeInterval.startTime == ti[i].startTime] # iv = iv[0] if len(iv)>0 else None if iv is None: # isempty(iv): # The value does not exist in the indexed time interval. Create it and store it. #iv = IntervalValue(ism, ti(i), mkt, 'Temperature', value) iv = IntervalValue(ism, ti[i], mkt, MeasurementType.Temperature, value) ism.values = [ism.values, iv] else: # The value exists in the indexed time interval. Simply reassign it iv.value = value
def update_dual_costs(self, mkt): # Update the dual cost for all active time intervals # (NOTE: Choosing not to separate this def from the base class because # cost might need to be handled differently and redefined in subclasses.) # Gather the active time intervals ti time_intervals = mkt.timeIntervals time_interval_values = [t.startTime for t in time_intervals] self.dualCosts = [ x for x in self.dualCosts if x.timeInterval.startTime in time_interval_values ] # Index through the time intervals ti for i in range(1, len(time_intervals)): # Find the marginal price mp for the indexed time interval ti(i) in # the given market mkt mp = find_obj_by_ti(mkt.marginalPrices, time_intervals[i]) mp = mp.value # a marginal price [$/kWh] # Find the scheduled power sp for the asset in the indexed time interval ti(i) sp = find_obj_by_ti(self.scheduledPowers, time_intervals[i]) sp = sp.value # schedule power [avg.kW] # Find the production cost in the indexed time interval pc = find_obj_by_ti(self.productionCosts, time_intervals[i]) pc = pc.value # production cost [$] # Dual cost in the time interval is calculated as production cost, # minus the product of marginal price, scheduled power, and the # duration of the time interval. dur = time_intervals[i].duration.seconds // 3600 dc = pc - (mp * sp * dur) # a dual cost [$] # Check whether a dual cost exists in the indexed time interval iv = find_obj_by_ti(self.dualCosts, time_intervals[i]) if iv is None: # No dual cost was found in the indexed time interval. Create an # interval value and assign it the calculated value. iv = IntervalValue(self, time_intervals[i], mkt, MeasurementType.DualCost, dc) # Append the new interval value to the list of active interval values self.dualCosts.append(iv) else: # The dual cost value was found to already exist in the indexed # time interval. Simply reassign it the new calculated value. iv.value = dc # a dual cost [$] # Ensure that only active time intervals are in the list of dual costs adc #self.dualCosts = [x for x in self.dualCosts if x.timeInterval in ti] # Sum the total dual cost and save the value self.totalDualCost = sum([x.value for x in self.dualCosts]) dc = [(x.timeInterval.name, x.value) for x in self.dualCosts] _log.debug("{} asset model dual costs are: {}".format(self.name, dc))
def update_vertices(self, mkt): # Create vertices to represent the asset's flexibility # # For the base local asset model, a single, inelastic power is needed. # There is no flexibility. The constant power may be represented by a # single (price, power) point (See struct Vertex). # Gather active time intervals ti = mkt.timeIntervals # active TimeIntervals time_interval_values = [t.startTime for t in ti] self.activeVertices = [ x for x in self.activeVertices if x.timeInterval.startTime in time_interval_values ] # Index through active time intervals ti for i in range(len(ti)): # Find the scheduled power for the indexed time interval # Extract the scheduled power value sp = find_obj_by_ti(self.scheduledPowers, ti[i]) sp = sp.value # avg. kW] # Create the vertex that can represent this (lack of) flexibility value = Vertex(float("inf"), 0.0, sp, True) # Check to see if the active vertex already exists for this indexed time interval. iv = find_obj_by_ti(self.activeVertices, ti[i]) # If the active vertex does not exist, a new interval value must be # created and stored. if iv is None: # Create the interval value and place the active vertex in it iv = IntervalValue(self, ti[i], mkt, MeasurementType.ActiveVertex, value) # Append the interval value to the list of active vertices self.activeVertices.append(iv) else: # Otherwise, simply reassign the active vertex value to the # existing listed interval value. (NOTE that this base local # asset model unnecessarily reassigns constant values, but the # reassignment is allowed because it teaches how a more dynamic # assignment may be maintained. iv.value = value av = [(x.timeInterval.name, x.value.marginalPrice, x.value.power) for x in self.activeVertices] _log.debug("{} asset model active vertices are: {}".format( self.name, av))
def schedule_engagement(self, mkt): # To assign engagement, or commitment, which # is relevant to some local assets (supports future capabilities). # NOTE: The assignment of engagement schedule, if used, may be assigned # during the scheduling of power, not separately as demonstrated here. # Commitment and engagement are closely aligned with the optimal # production costs of schedulable generators and utility def of # engagements (e.g., demand responses). # NOTE: Because this is a future capability, Implementers might choose to # simply return from the call until LocalAsset behaviors are found to need # commitment or engagement. # Gather the active time intervals ti time_intervals = mkt.timeIntervals # active TimeIntervals time_interval_values = [t.startTime for t in time_intervals] self.engagementSchedule = [ x for x in self.engagementSchedule if x.timeInterval.startTime in time_interval_values ] # Index through the active time intervals ti for i in range(len(time_intervals)): # Check whether an engagement schedule exists in the indexed time interval iv = find_obj_by_ti(self.engagementSchedule, time_intervals[i]) # NOTE: this template currently assigns engagement value as true (i.e., engaged). val = True # Asset is committed or engaged if iv is None: # No engagement schedule was found in the indexed time interval. # Create an interval value and assign its value. iv = IntervalValue(self, time_intervals[i], mkt, MeasurementType.EngagementValue, val) # an IntervalValue # Append the interval value to the list of active interval values self.engagementSchedule.append(iv) else: # An engagement schedule was found in the indexed time interval. # Simpy reassign its value. iv.value = val # [$]
def update_production_costs(self, mkt): # Calculate the costs of generated energies. # (NOTE: Choosing not to separate this def from the base class because # cost might need to be handled differently and redefined in subclasses.) # Gather active time intervals ti time_intervals = mkt.timeIntervals time_interval_values = [t.startTime for t in time_intervals] self.productionCosts = [ x for x in self.productionCosts if x.timeInterval.startTime in time_interval_values ] # Index through the active time interval ti for i in range(1, len(time_intervals)): # Get the scheduled power sp in the indexed time interval sp = find_obj_by_ti(self.scheduledPowers, time_intervals[i]) sp = sp.value # schedule power [avg.kW] # Call on def that calculates production cost pc based on the # vertices of the supply or demand curve # NOTE that this def is now stand-alone because it might be # generally useful for a number of models. pc = prod_cost_from_vertices(self, time_intervals[i], sp) # interval production cost [$] # Check for a transition cost in the indexed time interval. # (NOTE: this differs from neighbor models, which do not posses the # concept of commitment and engagement. This is a good reason to keep # this method within its base class to allow for subtle differences.) tc = find_obj_by_ti(self.transitionCosts, time_intervals[i]) if tc is None: tc = 0.0 # [$] else: tc = tc.value # [$] # Add the transition cost to the production cost pc = pc + tc # Check to see if the production cost value has been defined for the # indexed time interval iv = find_obj_by_ti(self.productionCosts, time_intervals[i]) if iv is None: # The production cost value has not been defined in the indexed # time interval. Create it and assign its value pc. iv = IntervalValue(self, time_intervals[i], mkt, MeasurementType.ProductionCost, pc) # Append the production cost to the list of active production # cost values self.productionCosts.append(iv) # IntervalValues else: # The production cost value already exists in the indexed time # interval. Simply reassign its value. iv.value = pc # interval production cost [$] # Ensure that only active time intervals are in the list of active # production costs apc #self.productionCosts = [x for x in self.productionCosts if x.timeInterval in time_intervals] # Sum the total production cost self.totalProductionCost = sum([x.value for x in self.productionCosts ]) # total production cost [$] pc = [(x.timeInterval.name, x.value) for x in self.productionCosts] _log.debug("{} asset model production costs are: {}".format( self.name, pc))
def assign_transition_costs(self, mkt): # Assign the cost of changeing # engagement state from the prior to the current time interval # # PRESUMPTIONS: # - Time intervals exist and have been updated # - The engagement schedule exists and has been updated. Contents are # logical [true/false]. # - Engagement costs have been accurately assigned for [disengagement, # unchanged, engagement] # # INPUTS: # mkt - Market object # # USES: # - self.engagementCost - three costs that correspond to # [disengagement, unchanged, engagement) transitions # - self.engagement_cost() - assigns appropriate cost from # self.engagementCost property # - self.engagementSchedule - engagement states (true/false) for the asset # in active time intervals # # OUTPUTS: # Assigns values to self.transition_costs # Gather active time intervals time_intervals = mkt.timeIntervals time_interval_values = [t.startTime for t in time_intervals] self.transitionCosts = [ x for x in self.transitionCosts if x.timeInterval.startTime in time_interval_values ] # Ensure that ti is ordered by time interval start times time_intervals.sort(key=lambda x: x.startTime) # Index through all but the first time interval ti for i in range(len(time_intervals)): # Find the current engagement schedule ces in the current indexed # time interval ti(i) ces = [ x for x in self.engagementSchedule if x.timeInterval.startTime == time_intervals[i].startTime ] # Extract its engagement state ces = ces[0].value # logical (true/false) # Find the engagement schedule pes in the prior indexed time interval ti(i-1) pes = [ x for x in self.engagementSchedule if x.timeInterval.startTime == time_intervals[i - 1].startTime ] # And extract its value pes = pes[0].value # logical (true/false) # Calculate the state transition # - -1:Disengaging # - 0:Unchaged # - 1:Engaging dif = ces - pes # Assign the corresponding transition cost val = self.engagement_cost(dif) # Check whether a transition cost exists in the indexed time interval iv = find_obj_by_ti(self.transitionCosts, time_intervals[i]) if iv is None: # No transition cost was found in the indexed time interval. # Create an interval value and assign its value. iv = IntervalValue(self, time_intervals[i], mkt, MeasurementType.TransitionCost, val) # Append the interval value to the list of active interval values self.transitionCosts.append(iv) else: # A transition cost was found in the indexed time interval. # Simpy reassign its value. iv.value = val # [$]
def calculate_reserve_margin(self, mkt): # Estimate available (spinning) reserve margin for this asset. # # NOTES: # This method works with the simplest base classes that have constant # power and therefore provide no spinning reserve. This method may be # redefined by subclasses of the local asset model to add new features # or capabilities. # This calculation will be more meaningful and useful after resource # commitments and uncertainty estimates become implemented. Until then, # reserve margins may be tracked, even if they are not used. # # PRESUMPTIONS: # - Active time intervals exist and have been updated # - The asset's maximum power is a meaningful and accurate estimate of # the maximum power level that can be achieved on short notice, i.e., # spinning reserve. # # INPUTS: # mkt - market object # # OUTPUTS: # Modifies self.reserveMargins - an array of estimated (spinning) reserve # margins in active time intervals # Gather the active time intervals ti time_intervals = mkt.timeIntervals # active TimeIntervals time_interval_values = [t.startTime for t in time_intervals] self.reserveMargins = [ x for x in self.reserveMargins if x.timeInterval.startTime in time_interval_values ] # Index through active time intervals ti for i in range(len(time_intervals)): # Calculate the reserve margin for the indexed interval. This is the # non-negative difference between the maximum asset power and the # scheduled power. In principle, generation may be increased or # demand decreased by this quantity to act as spinning reserve. # Find the scheduled power in the indexed time interval iv = find_obj_by_ti(self.scheduledPowers, time_intervals[i]) # Calculate the reserve margin rm in the indexed time interval. The # reserve margin is the differnce between the maximum operational # power value in the interval and the scheduled power. The # operational maximum should be less than the object's hard physical # power constraint, so a check is in order. # start with the hard physical constraint. hard_const = self.object.maximumPower # [avg.kW] # Calculate the operational maximum constraint, which is the highest # point on the supply/demand curve (i.e., the vertex) that represents # the residual flexibility of the asset in the time interval. op_const = find_objs_by_ti(self.activeVertices, time_intervals[i]) if len(op_const) == 0: op_const = hard_const else: op_const = [x.value for x in op_const] # active vertices op_const = max([x.power for x in op_const ]) # operational max. power[avg.kW] # Check that the upper operational power constraint is less than or # equal to the object's hard physical constraint. soft_maximum = min(hard_const, op_const) # [avg.kW] # And finally calculate the reserve margin. rm = max(0, soft_maximum - iv.value) # reserve margin [avg. kW] # Check whether a reserve margin already exists for the indexed time interval iv = find_obj_by_ti(self.reserveMargins, time_intervals[i]) # an IntervalValue if iv is None: # A reserve margin does not exist for the indexed time interval. # create it. (See IntervalValue class.) iv = IntervalValue(self, time_intervals[i], mkt, MeasurementType.ReserveMargin, rm) # an IntervalValue # Append the new reserve margin interval value to the list of # reserve margins for the active time intervals self.reserveMargins.append(iv) else: # The reserve margin already exists for the indexed time # interval. Simply reassign its value. iv.value = rm # reserve margin [avg.kW]
def schedule_power(self, mkt): # Determine powers of an asset in active time # intervals. NOTE that this method may be redefined by subclasses if more # features are needed. NOTE that this method name is common for all asset # and neighbor models to facilitate its redefinition. # # PRESUMPTIONS: # - Active time intervals exist and have been updated # - Marginal prices exist and have been updated. NOTE: Marginal prices # are not used for inelastic assets. # - Transition costs, if relevant, are applied during the scheduling # of assets. # - An engagement schedule, if used, is applied during an asset's power # scheduling. # - Scheduled power and engagement schedule must be self consistent at # the end of this method. That is, power should not be scheduled while # the asset is disengaged (uncommitted). # # INPUTS: # mkt - market object # # OUTPUTS: # - Updates self.scheduledPowers - the schedule of power consumed # - Updates self.engagementSchedule - an array that states whether the # asset is engaged (committed) (true) or not (false) in the time interval # Gather the active time intervals ti time_intervals = mkt.timeIntervals time_interval_values = [t.startTime for t in time_intervals] self.scheduledPowers = [ x for x in self.scheduledPowers if x.timeInterval.startTime in time_interval_values ] time_intervals.sort(key=lambda x: x.startTime) len_powers = len(self.default_powers) default_value = self.defaultPower # Index through the active time intervals ti for i in range(len(time_intervals)): # Check whether a scheduled power already exists for the indexed time interval iv = find_obj_by_ti(self.scheduledPowers, time_intervals[i]) # Reassign default value if there is power value for this time interval if len_powers > i: default_value = self.default_powers[i] if self.default_powers[ i] is not None else self.defaultPower if iv is None: # A scheduled power does not exist for the indexed time interval # Create an interval value and assign the default value iv = IntervalValue(self, time_intervals[i], mkt, MeasurementType.ScheduledPower, default_value) # Append the new scheduled power to the list of scheduled # powers for the active time intervals self.scheduledPowers.append(iv) else: # The scheduled power already exists for the indexed time # interval. Simply reassign its value iv.value = default_value # [avg.kW] sp = [(x.timeInterval.name, x.value) for x in self.scheduledPowers] _log.debug("{} asset model scheduledPowers are: {}".format( self.name, sp))
def schedule_power(self, mkt): # Estimate stochastic generation from a solar # PV array as a function of time-of-day and a cloud-cover factor. # INPUTS: # obj - SolarPvResourceModel class object # tod - time of day # OUTPUTS: # p - calcalated maximum power production at this time of day # LOCAL: # h - hour (presumes 24-hour clock, local time) # ************************************************************************* # Gather active time intervals tis = mkt.timeIntervals # Index through the active time intervals ti for ti in tis: # Production will be estimated from the time-of-day at the center of # the time interval. tod = ti.startTime + ti.duration / 2 # a datetime # extract a fractional representation of the hour-of-day h = tod.hour m = tod.minute h = h + m / 60 # TOD stated as fractional hours # Estimate solar generation as a sinusoidal function of daylight hours. if h < 5.5 or h > 17.5: # The time is outside the time of solar production. Set power to zero. p = 0.0 # [avg.kW] else: # A sinusoidal function is used to forecast solar generation # during the normally sunny part of a day. p = 0.5 * (1 + math.cos((h - 12) * 2.0 * math.pi / 12)) p = self.object.maximumPower * p p = self.cloudFactor * p # [avg.kW] # Check whether a scheduled power exists in the indexed time interval. iv = find_obj_by_ti(self.scheduledPowers, ti) if iv is None: # No scheduled power value is found in the indexed time interval. # Create and store one. iv = IntervalValue(self, ti, mkt, MeasurementType.ScheduledPower, p) # Append the scheduled power to the list of scheduled powers. self.scheduledPowers.append(iv) else: # A scheduled power already exists in the indexed time interval. # Simply reassign its value. iv.value = p # [avg.kW] # Assign engagement schedule in the indexed time interval # NOTE: The assignment of engagement schedule, if used, will often be # assigned during the scheduling of power, not separately as # demonstrated here. # Check whether an engagement schedule exists in the indexed time interval iv = find_obj_by_ti(self.engagementSchedule, ti) # NOTE: this template assigns engagement value as true (i.e., engaged). val = True # Asset is committed or engaged if iv is None: # No engagement schedule was found in the indexed time interval. # Create an interval value and assign its value. iv = IntervalValue(self, ti, mkt, MeasurementType.EngagementSchedule, val) # an IntervalValue # Append the interval value to the list of active interval values self.engagementSchedule.append(iv) else: # An engagement schedule was found in the indexed time interval. # Simpy reassign its value. iv.value = val # [$] # Remove any extra scheduled powers self.scheduledPowers = [ x for x in self.scheduledPowers if x.timeInterval in tis ] # Remove any extra engagement schedule values self.engagementSchedule = [ x for x in self.engagementSchedule if x.timeInterval in tis ]
def update_supply_demand(self, mtn): # For each time interval, sum the power that is generated, imported, # consumed, or exported for all modeled local resources, neighbors, and # local load. # Extract active time intervals time_intervals = self.timeIntervals # active TimeIntervals time_interval_values = [t.startTime for t in time_intervals] # Delete netPowers not in active time intervals self.netPowers = [x for x in self.netPowers if x.timeInterval.startTime in time_interval_values] # Index through the active time intervals ti for i in range(1, len(time_intervals)): # Initialize total generation tg tg = 0.0 # [avg.kW] # Initialize total demand td td = 0.0 # [avg.kW] # Index through local asset models m. m = mtn.localAssets for k in range(len(m)): mo = find_obj_by_ti(m[k].model.scheduledPowers, time_intervals[i]) # Extract and include the resource's scheduled power p = mo.value # [avg.kW] if p > 0: # Generation # Add positive powers to total generation tg tg = tg + p # [avg.kW] else: # Demand # Add negative powers to total demand td td = td + p # [avg.kW] # Index through neighbors m m = mtn.neighbors for k in range(len(m)): # Find scheduled power for this neighbor in the indexed time interval mo = find_obj_by_ti(m[k].model.scheduledPowers, time_intervals[i]) # Extract and include the neighbor's scheduled power p = mo.value # [avg.kW] if p > 0: # Generation # Add positive power to total generation tg tg = tg + p # [avg.kW] else: # Demand # Add negative power to total demand td td = td + p # [avg.kW] # At this point, total generation and importation tg, and total # demand and exportation td have been calculated for the indexed # time interval ti[i] # Save the total generation in the indexed time interval # Check whether total generation exists for the indexed time interval iv = find_obj_by_ti(self.totalGeneration, time_intervals[i]) if iv is None: # No total generation was found in the indexed time interval. # Create an interval value. iv = IntervalValue(self, time_intervals[i], self, MeasurementType.TotalGeneration, tg) # an IntervalValue # Append the total generation to the list of total generations self.totalGeneration.append(iv) else: # Total generation exists in the indexed time interval. Simply # reassign its value. iv.value = tg # Calculate and save total demand for this time interval. # NOTE that this formulation includes both consumption and # exportation among total load. # Check whether total demand exists for the indexed time interval iv = find_obj_by_ti(self.totalDemand, time_intervals[i]) if iv is None: # No total demand was found in the indexed time interval. Create # an interval value. iv = IntervalValue(self, time_intervals[i], self, MeasurementType.TotalDemand, td) # an IntervalValue # Append the total demand to the list of total demands self.totalDemand.append(iv) else: # Total demand was found in the indexed time interval. Simply reassign it. iv.value = td # Update net power for the interval # Net power is the sum of total generation and total load. # By convention generation power is positive and consumption # is negative. # Check whether net power exists for the indexed time interval iv = find_obj_by_ti(self.netPowers, time_intervals[i]) if iv is None: # Net power is not found in the indexed time interval. Create an interval value. iv = IntervalValue(self, time_intervals[i], self, MeasurementType.NetPower, tg + td) # Append the net power to the list of net powers self.netPowers.append(iv) else: # A net power was found in the indexed time interval. Simply reassign its value. iv.value = tg + td np = [(x.timeInterval.name, x.value) for x in self.netPowers] _log.debug("{} market netPowers are: {}".format(self.name, np))
def update_costs(self, mtn): # Sum the production and dual costs from all modeled local resources, local # loads, and neighbors, and then sum them for the entire duration of the # time horizon being calculated. # # PRESUMPTIONS: # - Dual costs have been created and updated for all active time # intervals for all neighbor objects # - Production costs have been created and updated for all active time # intervals for all asset objects # # INTPUTS: # mtn - my Transactive Node object # # OUTPUTS: # - Updates Market.productionCosts - an array of total production cost in # each active time interval # - Updates Market.totalProductionCost - the sum of production costs for # the entire future time horizon of active time intervals # - Updates Market.dualCosts - an array of dual cost for each active time # interval # - Updates Market.totalDualCost - the sum of all the dual costs for the # entire future time horizon of active time intervals # Call each LocalAssetModel to update its costs for la in mtn.localAssets: la.model.update_costs(self) # Call each NeighborModel to update its costs for n in mtn.neighbors: n.model.update_costs(self) for i in range (1, len(self.timeIntervals)): ti = self.timeIntervals[i] # Initialize the sum dual cost sdc in this time interval sdc = 0.0 # [$] # Initialize the sum production cost spc in this time interval spc = 0.0 # [$] for la in mtn.localAssets: iv = find_obj_by_ti(la.model.dualCosts, ti) sdc = sdc + iv.value # sum dual cost [$] iv = find_obj_by_ti(la.model.productionCosts, ti) spc = spc + iv.value # sum production cost [$] for n in mtn.neighbors: iv = find_obj_by_ti(n.model.dualCosts, ti) sdc = sdc + iv.value # sum dual cost [$] iv = find_obj_by_ti(n.model.productionCosts, ti) spc = spc + iv.value # sum production cost [$] # Check to see if a sum dual cost exists in the indexed time interval iv = find_obj_by_ti(self.dualCosts, ti) if iv is None: # No dual cost was found for the indexed time interval. Create # an IntervalValue and assign it the sum dual cost for the # indexed time interval iv = IntervalValue(self, ti, self, MeasurementType.DualCost, sdc) # an IntervalValue # Append the dual cost to the list of interval dual costs self.dualCosts.append(iv) # = [mkt.dualCosts, iv] # IntervalValues else: # A sum dual cost value exists in the indexed time interval. # Simply reassign its value iv.value = sdc # sum dual cost [$] # Check to see if a sum production cost exists in the indexed time interval iv = find_obj_by_ti(self.productionCosts, ti) if iv is None: # No sum production cost was found for the indexed time # interval. Create an IntervalValue and assign it the sum # prodution cost for the indexed time interval iv = IntervalValue(self, ti, self, MeasurementType.ProductionCost, spc) # Append the production cost to the list of interval production costs self.productionCosts.append(iv) else: # A sum production cost value exists in the indexed time # interval. Simply reassign its value iv.value = spc # sum production cost [$] # Sum total dual cost for the entire time horizon self.totalDualCost = sum([x.value for x in self.dualCosts]) # [$] # Sum total primal cost for the entire time horizon self.totalProductionCost = sum([x.value for x in self.productionCosts]) # [$]
def schedule_power(self, mkt): """ Predict municipal load. This is a model of non-price-responsive load using an open-loop regression model. :param mkt: :return: """ # Get the active time intervals. time_intervals = mkt.timeIntervals # TimeInterval objects TEMP = None # Index through the active time intervals. for time_interval in time_intervals: # Extract the start time from the indexed time interval. interval_start_time = time_interval.startTime if self.temperature_forecaster is None: #if isempty(temperature_forecaster) # No appropriate information service was found, must use a # default temperature value. TEMP = 56.6 # [deg.F] else: # An appropriate information service was found. Get the # temperature that corresponds to the indexed time interval. interval_value = find_obj_by_ti( self.temperature_forecaster.predictedValues, time_interval) if interval_value is None: #if isempty(interval_value) # No stored temperature was found. Assign a default value. TEMP = 56.6 # [def.F] else: # A stored temperature value was found. Use it. TEMP = interval_value.value # [def.F] if TEMP is None: # The temperature value is not a number. Use a default value. TEMP = 56.6 # [def.F] # Determine the DOW_Intercept. # The DOW_Intercept is a function of categorical day-of-week number # DOWN. Calculate the weekday number DOWN. DOWN = interval_start_time.weekday() #weekday(interval_start_time) # Look up the DOW_intercept from the short table that is among the # class's constant properties. DOW_Intercept = self.dowIntercept[DOWN] # Determine categorical HOUR of the indexed time interval. This will # be needed to mine the HOUR_SEASON_REGIME_Intercept lookup table. # The hour is incremented by 1 because the lookup table uses hours # [1,24], not [0,23]. HOUR = interval_start_time.hour # + 1 # Determine the categorical SEASON of the indexed time interval. # SEASON is a function of MONTH, so start by determining the MONTH of # the indexed time interval. MONTH = interval_start_time.month #MONTH = month(interval_start_time) # Property season provides an index for use with the # HOUR_SEASON_REGIME_Intercept lookup table. SEASON = self.season[MONTH - 1] #obj.season(MONTH); # Determine categorical REGIME, which is also an index for use with # the HOUR_SEASON_REGIME_Intercept lookup table. REGIME = 0 # The default assignment if ( SEASON == 1 or SEASON == 4 ) and TEMP <= 56.6: # (Spring season index OR Fall season index) # AND Heating regime REGIME = 1 # Calcualte the table row. Add final 1 because of header row. row = 6 * HOUR + SEASON + REGIME #6 * (HOUR - 1) + SEASON + REGIME # Matlab is 1-based vs. python 0-based. row = row - 1 # Assign the Intercept and Factor values that were found. HOUR_SEASON_REGIME_Intercept = self.values[row][0] HOUR_SEASON_REGIME_Factor = self.values[row][1] # Finally, predict the city load. LOAD = DOW_Intercept + HOUR_SEASON_REGIME_Intercept + HOUR_SEASON_REGIME_Factor * TEMP # [avg.kW] # The table defined electric load as a positive value. The network # model defines load as a negative value. LOAD = -LOAD # [avg.kW] # Look for the scheduled power in the indexed time interval. interval_value = find_obj_by_ti(self.scheduledPowers, time_interval) if interval_value is None: # No scheduled power was found in the indexed time interval. # Create one and store it. interval_value = IntervalValue(self, time_interval, mkt, MeasurementType.ScheduledPower, LOAD) self.scheduledPowers.append(interval_value) else: # The interval value already exist. Simply reassign its value. interval_value.value = LOAD