BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
1 ACCEPTED SOLUTION

Accepted Solutions
RobPratt
SAS Super FREQ

Here's a way to solve the problem with the NLP solver in PROC OPTMODEL by using PROC FCMP to define the rampLimitFunction:

data loadData;
   input t load;
   datalines;
1	100
2	100
3	100
4	110
5	120
6	130
7	170
8	210
9	220
10	230
11	240
12	235
13	245
14	250
15	260
16	270
17	300
18	300
19	290
20	280
21	270
22	260
23	200
24	150
;

proc fcmp outlib=work.funcs.test;
   function rampLimitFunction(g,x);
      if g = 1 then do;
         if x <= 200 then limit = 25 + ((25-5)/(200-50))*(x-200);
         else limit = 25 + ((10-25)/(300-200))*(x-200);
      end;
      else if g = 2 then do;
         if x <= 100 then limit = 50 + ((50-30)/(100-10))*(x-100);
         else limit = 50 + ((30-50)/(200-100))*(x-100);
      end;
      return (limit);
   endsub;
run;

option cmplib=work.funcs;

data fcmpTest1;
   do x = 50 to 300;
      y = rampLimitFunction(1,x);
      output; 
   end;
run;
proc sgplot data=fcmpTest1;
   series x=x y=y;
run;

data fcmpTest2;
   do x = 10 to 200;
      y = rampLimitFunction(2,x);
      output; 
   end;
run;
proc sgplot data=fcmpTest2;
   series x=x y=y;
run;

proc optmodel;
   set GENERATORS = 1..2;
   set PERIODS;
   num tmax = max {t in PERIODS} t;
   num q {GENERATORS} = [0.1 0.2];
   num c {GENERATORS} = [20 30];
   num load {PERIODS};
   read data loadData into PERIODS=[t] load;

   var Output {GENERATORS, PERIODS};
   min Objective = sum {g in GENERATORS, t in PERIODS} 
      (q[g]*Output[g,t]^2 + c[g]*Output[g,t]);
   con MeetLoad {t in PERIODS}:
      sum {g in GENERATORS} Output[g,t] = load[t];
   for {t in PERIODS} do;
      Output[1,t].lb = 50;
      Output[1,t].ub = 300;
      Output[2,t].lb = 10;
      Output[2,t].ub = 200;
   end;
   con RampLower {g in GENERATORS, t in PERIODS diff {tmax}}:
      -rampLimitFunction(g,Output[g,t]) <= Output[g,t+1] - Output[g,t];
   con RampUpper {g in GENERATORS, t in PERIODS diff {tmax}}:
      rampLimitFunction(g,Output[g,t]) >= Output[g,t+1] - Output[g,t];

   solve;
   print Output;
quit;

View solution in original post

9 REPLIES 9
RobPratt
SAS Super FREQ
Not directly, but there are some indirect approaches. What does your problem formulation look like?
jjjch
Obsidian | Level 7

Hi Rob, thank you very much for the reply. I created a small case to show the kind of problem I am solving.

RobPratt
SAS Super FREQ

Are G1[t] and G2[t] required to take integer values?

jjjch
Obsidian | Level 7
No, they are continuous variables.
RobPratt
SAS Super FREQ

Here's a way to solve the problem with the NLP solver in PROC OPTMODEL by using PROC FCMP to define the rampLimitFunction:

data loadData;
   input t load;
   datalines;
1	100
2	100
3	100
4	110
5	120
6	130
7	170
8	210
9	220
10	230
11	240
12	235
13	245
14	250
15	260
16	270
17	300
18	300
19	290
20	280
21	270
22	260
23	200
24	150
;

proc fcmp outlib=work.funcs.test;
   function rampLimitFunction(g,x);
      if g = 1 then do;
         if x <= 200 then limit = 25 + ((25-5)/(200-50))*(x-200);
         else limit = 25 + ((10-25)/(300-200))*(x-200);
      end;
      else if g = 2 then do;
         if x <= 100 then limit = 50 + ((50-30)/(100-10))*(x-100);
         else limit = 50 + ((30-50)/(200-100))*(x-100);
      end;
      return (limit);
   endsub;
run;

option cmplib=work.funcs;

data fcmpTest1;
   do x = 50 to 300;
      y = rampLimitFunction(1,x);
      output; 
   end;
run;
proc sgplot data=fcmpTest1;
   series x=x y=y;
run;

data fcmpTest2;
   do x = 10 to 200;
      y = rampLimitFunction(2,x);
      output; 
   end;
run;
proc sgplot data=fcmpTest2;
   series x=x y=y;
run;

proc optmodel;
   set GENERATORS = 1..2;
   set PERIODS;
   num tmax = max {t in PERIODS} t;
   num q {GENERATORS} = [0.1 0.2];
   num c {GENERATORS} = [20 30];
   num load {PERIODS};
   read data loadData into PERIODS=[t] load;

   var Output {GENERATORS, PERIODS};
   min Objective = sum {g in GENERATORS, t in PERIODS} 
      (q[g]*Output[g,t]^2 + c[g]*Output[g,t]);
   con MeetLoad {t in PERIODS}:
      sum {g in GENERATORS} Output[g,t] = load[t];
   for {t in PERIODS} do;
      Output[1,t].lb = 50;
      Output[1,t].ub = 300;
      Output[2,t].lb = 10;
      Output[2,t].ub = 200;
   end;
   con RampLower {g in GENERATORS, t in PERIODS diff {tmax}}:
      -rampLimitFunction(g,Output[g,t]) <= Output[g,t+1] - Output[g,t];
   con RampUpper {g in GENERATORS, t in PERIODS diff {tmax}}:
      rampLimitFunction(g,Output[g,t]) >= Output[g,t+1] - Output[g,t];

   solve;
   print Output;
quit;
jjjch
Obsidian | Level 7

Thank you very much! Your model is clearer and easier than I thought because your rampLimitFunction has if statements. I originally thought I will need to introduce a integer variable for each segment of the rampLimit function for each hour each generator. But is it easy to extend the model to include 1000 generators and each rampLimitFunction can have up to 10 segments? Looks like the rampLimitFunction will be very large and hard to write manually.

RobPratt
SAS Super FREQ

Here's an alternative approach that uses implicit variables instead of PROC FCMP.  First populate a data set:

data rampData;
   input generator breakpoint output rate;
   datalines;
1 1  50  5
1 2 200 25
1 3 300 10
2 1  10 30
2 2 100 50
2 3 200 30
;

 

And then use the IMPVAR statement in PROC OPTMODEL:

 

   set BREAKPOINTS = 1..3;
   num output_gb {GENERATORS, BREAKPOINTS};
   num rate_gb {GENERATORS, BREAKPOINTS};
   read data rampData into [generator breakpoint] output_gb=output rate_gb=rate;
   impvar RampLimitFunction {g in GENERATORS, t in PERIODS} = 
      if Output[g,t] <= output_gb[g,2]
      then rate_gb[g,1] + ((rate_gb[g,2]-rate_gb[g,1])/(output_gb[g,2]-output_gb[g,1]))*(Output[g,t]-output_gb[g,1])
      else rate_gb[g,2] + ((rate_gb[g,3]-rate_gb[g,2])/(output_gb[g,3]-output_gb[g,2]))*(Output[g,t]-output_gb[g,2]);
   con RampLower {g in GENERATORS, t in PERIODS diff {tmax}}:
      -RampLimitFunction[g,t] <= Output[g,t+1] - Output[g,t];
   con RampUpper {g in GENERATORS, t in PERIODS diff {tmax}}:
      RampLimitFunction[g,t] >= Output[g,t+1] - Output[g,t];

For either approach, if you have more generators and segments, you can use the SAS macro language to generate the RampLimitFunction code.

 

jjjch
Obsidian | Level 7

RobPratt, thank you! It is amazing that impvar can have if statement too, which simplify the model a lot.

RobPratt
SAS Super FREQ

Glad to help.  I should mention that you can make the code more data driven by introducing a data set:

data generatorData;
   input generator q c lb ub;
   datalines;
1 0.1 20 50 300
2 0.2 30 10 200
;

And then reading the data in PROC OPTMODEL:

   set GENERATORS;
   num q {GENERATORS};
   num c {GENERATORS};
   num lb {GENERATORS};
   num ub {GENERATORS};
   read data generatorData into GENERATORS=[generator] q c lb ub;

Then you can include the bounds in the VAR statement:

   var Output {g in GENERATORS, PERIODS} >= lb[g] <= ub[g];

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!

Multiple Linear Regression in SAS

Learn how to run multiple linear regression models with and without interactions, presented by SAS user Alex Chaplin.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 9 replies
  • 2304 views
  • 3 likes
  • 2 in conversation