This package contains components models for load prediction.
Data-driven model that predicts the electrical load.
This load prediction can for example be used in a demand response client.
The model computes either an average baseline or
a linear regression with respect to outside temperature.
For both, optionally a day-of adjustment can be made.
Separate loads are computed for any types of days.
The type of day is an input signal received from the connector
typeOfDay, and must be equal to any value defined
in
Buildings.Controls.Types.Day.
This input is a vector where the first element corresponds to the
current day, the next element to tomorrow, and so on.
The dimension of this input vector is typically 2 if
the demand is to be predicted for the next 24 hours.
If it is for the next 48 hours, then the dimension is 3.
Using a vector is required as the prediction could be
from noon of a workday to noon of a holiday or week-end day.
The linear regression model computes the predicted power as a linear function of the
current outside temperature. The two coefficients for the linear function are
obtained using a regression of the past nhis days.
If no history term is present for the current time interval and
the current type of day, then the predicted power consumption
PPre[:] will be zero.
block ElectricalLoad
"Block that predicts an electrical load"
extends Modelica.Blocks.Icons.DiscreteBlock;
parameter Integer nSam(min=1) = 24
"Number of intervals in a day for which baseline is computed";
parameter Integer nPre(min=1) = 1
"Number of intervals for which future load need to be predicted (set to one to only predict current time, or to nSam to predict one day)";
parameter Integer nHis(min=1) = 10
"Number of history terms to be stored";
parameter Buildings.Controls.Predictors.Types.PredictionModel
predictionModel = Types.PredictionModel.WeatherRegression
"Load prediction model";
parameter Boolean use_dayOfAdj=true
"if true, use the day of adjustment";
parameter Modelica.SIunits.Time dayOfAdj_start(
max=0,
displayUnit="h") = -14400
"Number of hours prior to current time when day of adjustment starts";
parameter Modelica.SIunits.Time dayOfAdj_end(
max=0,
displayUnit="h") = -3600
"Number of hours prior to current time when day of adjustment ends";
parameter Real minAdjFac(min=0) = 0.8
"Minimum adjustment factor";
parameter Real maxAdjFac(min=0) = 1.2
"Maximum adjustment factor";
Modelica.Blocks.Interfaces.RealInput TOut(unit="K")
if
(predictionModel == Buildings.Controls.Predictors.Types.PredictionModel.WeatherRegression)
"Outside air temperature";
Modelica.Blocks.Interfaces.RealInput TOutFut[nPre-1](
each unit="K")
if
(predictionModel == Buildings.Controls.Predictors.Types.PredictionModel.WeatherRegression)
"Future outside air temperatures";
Modelica.Blocks.Interfaces.RealInput ECon(unit="J", nominal=1E5)
"Consumed electrical energy";
discrete Modelica.Blocks.Interfaces.RealOutput PPre[nPre](
each unit="W")
"Predicted power consumptions (first element is for current time";
Buildings.Controls.Interfaces.DayTypeInput typeOfDay[
integer((nPre-1)/nSam)+2]
"Type of day for the current and the future days for which a prediction is to be made.
Typically, this has dimension 2 for predictions up to and including 24 hours, and 2+n for any additional day";
Modelica.Blocks.Interfaces.BooleanInput storeHistory
"If false, history terms are no longer stored for the remainder of the day";
discrete Real adj(unit="1")
"Load adjustment factor";
protected
parameter Modelica.SIunits.Time samplePeriod=86400/nSam
"Sample period of the component";
parameter Modelica.SIunits.Time samStart(fixed=false)
"Time when the first sampling starts";
parameter Integer iDayOf_start =
integer((nSam*dayOfAdj_start/86400+1E-8))
"Counter where day of look up begins";
parameter Integer iDayOf_end =
integer((nSam*dayOfAdj_end /86400+1E-8))
"Counter where day of look up ends";
parameter Integer nDayOf = iDayOf_end-iDayOf_start
"Number of samples used for the day of adjustment";
parameter Modelica.SIunits.Time dt = 86400/nSam
"Length of one sampling interval";
discrete Modelica.SIunits.Power PAve
"Average power over the past interval";
Boolean sampleTrigger
"True, if sample time instant";
discrete output Modelica.SIunits.Energy ELast
"Energy at the last sample";
discrete output Modelica.SIunits.Time tLast
"Time at which last sample occurred";
output Integer[nPre] iSam
"Index for power of the current sampling interval";
discrete output Modelica.SIunits.Power P[Buildings.Controls.Types.nDayTypes,nSam,nHis]
"Baseline power consumption";
// The temperature history is set to a zero array if it is not needed.
// This significantly reduces the size of the code that needs to be compiled.
discrete output Modelica.SIunits.Temperature T[
if predictionModel == Types.PredictionModel.WeatherRegression
then Buildings.Controls.Types.nDayTypes
else 0,
if predictionModel == Types.PredictionModel.WeatherRegression
then nSam
else 0,
if predictionModel == Types.PredictionModel.WeatherRegression
then nHis
else 0]
"Temperature history";
Integer _typeOfDay[nPre]
"Type of day for each time interval for which prediction is to be made";
Integer iHis[Buildings.Controls.Types.nDayTypes,nSam]
"Index for power of the current sampling history, for the currrent time interval";
Boolean historyComplete[Buildings.Controls.Types.nDayTypes,nSam](
each start=false,
each fixed=true)
"Flage, set to true when all history terms are built up for the given day type and given time interval";
Boolean _storeHistory
"Flag, switched to false when block gets an storeHistory=false signal, and remaining false until midnight";
discrete Modelica.SIunits.Energy EActAve
"Actual energy over the day off period";
discrete Modelica.SIunits.Energy EHisAve
"Actual load over the day off period, summed over all time intervals";
discrete Modelica.SIunits.Power PPreHis[Buildings.Controls.Types.nDayTypes, nSam]
"Predicted power consumptions for all day off time intervals";
Boolean PPreHisSet[Buildings.Controls.Types.nDayTypes, nSam](
each start=false,
each fixed=true)
"Flag, true if a value in PPreHis has been set for that element";
Real intTOut(unit="K.s", start=0, fixed=true)
"Time integral of outside temperature";
discrete Real intTOutLast(unit="K.s")
"Last sampled value of time integral of outside temperature";
Integer idxSam
"Index to access iSam[1]";
// Conditional connectors
Modelica.Blocks.Interfaces.RealInput TOut_in_internal(unit="K")
"Needed to connect to conditional connector";
Modelica.Blocks.Interfaces.RealInput TOutFut_in_internal[nPre-1](
each unit="K")
"Needed to connect to conditional connector";
// Functions
function isMidNight
input Modelica.SIunits.Time t
"Simulation time";
output Boolean r
"True if time is midnight, false otherwise";
algorithm
r :=
rem(t, 86400.0) < 1;
end isMidNight;
function getTypeOfDays
input Modelica.SIunits.Time t
"Simulation time";
input Buildings.Controls.Types.Day[:] typeOfDay
"Type of day as received from input connector";
input Modelica.SIunits.Time dt
"Length of one sampling interval";
input Integer nPre
"Number of predictions to be made";
output Integer[nPre] tod
"Type of day for each prediction interval";
protected
Integer itod
"Pointer to the type of day";
algorithm
tod[1] :=
Integer(typeOfDay[1]);
if nPre > 1
then
itod :=1;
for i
in 2:nPre
loop
// We reached mid-night. Hence, we need to take the next type of day.
if isMidNight(t + (i-1)*dt)
then
itod := itod + 1;
end if;
tod[i] :=
Integer(typeOfDay[itod]);
end for;
end if;
end getTypeOfDays;
function incrementIndex
input Integer i
"Counter";
input Integer n
"Maximum value of counter";
output Integer iNew
"New value of counter";
algorithm
iNew :=
if i == n
then 1
else i + 1;
end incrementIndex;
function getIndex
input Integer i
"Counter";
input Integer n
"Maximum value of counter";
output Integer iNew
"New value of counter";
algorithm
iNew :=
mod(i, n);
if iNew == 0
then
iNew := n;
end if;
end getIndex;
initial equation
for i
in 1:nPre
loop
iSam[i] = i;
end for;
P =
zeros(
Buildings.Controls.Types.nDayTypes,
nSam,
nHis);
PPre =
zeros(nPre);
T =
zeros(
size(T,1),
size(T,2),
size(T,3));
EActAve = 0;
EHisAve = 0;
PPreHis =
zeros(Buildings.Controls.Types.nDayTypes, nSam);
ELast = 0;
intTOutLast = 0;
tLast = time;
iHis =
zeros(Buildings.Controls.Types.nDayTypes, nSam);
_storeHistory = true;
samStart =
Buildings.Controls.Predictors.BaseClasses.sampleStart(
t=time,
samplePeriod=samplePeriod);
for i
in 1:nPre
loop
_typeOfDay[i] =
Integer(Buildings.Controls.Types.Day.WorkingDay);
end for;
// Compute the offset of the index that is used to look up the data for
// the dayOfAdj
if use_dayOfAdj
then
assert(iDayOf_start < iDayOf_end, "
Wrong values for parameters.
Require dayOfAdjustement_start < dayOfAdjustement_end + 86400/nSam.
Received dayOfAdj_start = " +
String(dayOfAdj_start) + "
dayOfAdj_end = " +
String(dayOfAdj_end));
end if;
equation
// Conditional connector
connect(TOut, TOut_in_internal);
connect(TOutFut, TOutFut_in_internal);
if predictionModel <> Types.PredictionModel.WeatherRegression
then
TOutFut_in_internal =
zeros(nPre-1);
TOut_in_internal = 0;
end if;
// Sample trigger
sampleTrigger =
sample(samStart, samplePeriod);
// Averaging of outside temperature
if predictionModel == Types.PredictionModel.WeatherRegression
then
der(intTOut) = TOut_in_internal;
else
intTOut = 0;
end if;
algorithm
when sampleTrigger
then
// Set flag for event day.
// isMidnight is true if time is within 1 second of midnight.
_storeHistory :=
if not pre(_storeHistory)
and (
not isMidNight(t=time))
then false
else storeHistory;
_typeOfDay :=
getTypeOfDays(t=time, typeOfDay=typeOfDay, dt=dt, nPre=nPre);
// Index to access iSam[1]
idxSam :=
getIndex(iSam[1] - 1, nSam);
// Update the history terms with the average power of the time interval,
// unless we have an event day.
if (_storeHistory)
or (
pre(_storeHistory))
then
if (time - tLast) > 1E-5
then
// Update iHis, which points to where the last interval's power
// consumption will be stored.
iHis[
pre(_typeOfDay[1]), idxSam] :=
incrementIndex(iHis[
pre(_typeOfDay[1]), idxSam], nHis);
if iHis[
pre(_typeOfDay[1]), idxSam] == nHis
then
historyComplete[
pre(_typeOfDay[1]), idxSam] :=true;
end if;
PAve :=(ECon - ELast)/(time - tLast);
P[
pre(_typeOfDay[1]), idxSam, iHis[
pre(_typeOfDay[1]), idxSam]] := PAve;
if predictionModel == Types.PredictionModel.WeatherRegression
then
T[
pre(_typeOfDay[1]), idxSam, iHis[
pre(_typeOfDay[1]), idxSam]] := (intTOut-intTOutLast)/(time - tLast);
end if;
end if;
end if;
// Initialize the energy consumed since the last sampling.
ELast := ECon;
intTOutLast :=intTOut;
tLast := time;
// Compute the baseline prediction for the current hour,
// with k being equal to the number of stored history terms.
// If in a later implementation, we want more terms into the future, then
// a loop should be added over for i = iSam[1]...upper_bound, whereas
// the loop needs to wrap around nSam.
if predictionModel == Types.PredictionModel.Average
then
for m
in 1:nPre
loop
// Note that as this branch does not use TOutFut, at every time step, computations
// are repeated because PPre[m] = pre(PPre[m+1]). This could be improved
// in future versions.
PPre[m] :=
Buildings.Controls.Predictors.BaseClasses.average(
P={P[_typeOfDay[m], iSam[m], i]
for i
in 1:nHis},
k=
if historyComplete[_typeOfDay[m], iSam[m]]
then nHis
else iHis[_typeOfDay[m], iSam[m]]);
end for;
elseif predictionModel == Types.PredictionModel.WeatherRegression
then
for m
in 1:nPre
loop
PPre[m] :=
Buildings.Controls.Predictors.BaseClasses.weatherRegression(
TCur=
if m == 1
then TOut_in_internal
else TOutFut_in_internal[m-1],
T={T[_typeOfDay[m], iSam[m], i]
for i
in 1:nHis},
P={P[_typeOfDay[m], iSam[m], i]
for i
in 1:nHis},
k=
if historyComplete[_typeOfDay[m], iSam[m]]
then nHis
else iHis[_typeOfDay[m], iSam[m]]);
end for;
else
PPre:=
zeros(nPre);
assert(false, "Wrong value for prediction model.");
end if;
if use_dayOfAdj
then
if _storeHistory
or pre(_storeHistory)
then
// Store the predicted power consumption. This variable is stored
// to avoid having to compute the average or weather regression multiple times.
PPreHis[_typeOfDay[1],
getIndex(idxSam+1, nSam)] := PPre[1];
// If iHis == 0, then there is no history yet and hence PPre[1] is 0.
PPreHisSet[_typeOfDay[1],
getIndex(idxSam+1, nSam)] := (iHis[_typeOfDay[1], iSam[1]] > 0);
end if;
// Compute average historical load.
// This is a running sum over the past nHis days for the time window from iDayOf_start to iDayOf_end.
EHisAve := 0;
EActAve := 0;
for i
in iDayOf_start:iDayOf_end-1
loop
if Modelica.Math.BooleanVectors.allTrue(
{PPreHisSet[_typeOfDay[1],
getIndex(iSam[1]+i, nSam)]
for i
in iDayOf_start:iDayOf_end-1})
then
EHisAve := EHisAve + dt*PPreHis[_typeOfDay[1],
getIndex(idxSam+i+1, nSam)];
EActAve := EActAve + dt*P[_typeOfDay[1],
getIndex(idxSam+i+1, nSam), iHis[_typeOfDay[1],
getIndex(idxSam+i+1, nSam)]];
else
EHisAve := 0;
EActAve := 0;
end if;
end for;
// Compute the load adjustment factor.
if (EHisAve > Modelica.Constants.eps
or EHisAve < -Modelica.Constants.eps)
then
adj :=
min(maxAdjFac,
max(minAdjFac, EActAve/EHisAve));
else
adj := 1;
end if;
// Apply the load adjustment factor to all predicted loads, not only to the
// predicted load of the current time step.
PPre[:] :=PPre[:]*adj;
else
EActAve := 0;
EHisAve := 0;
PPreHis[_typeOfDay[1],
getIndex(iSam[1], nSam)] := 0;
PPreHisSet[_typeOfDay[1],
getIndex(iSam[1], nSam)] := false;
adj := 1;
end if;
// Update iSam
for i
in 1:nPre
loop
iSam[i] :=
incrementIndex(iSam[i], nSam);
end for;
end when;
end ElectricalLoad;