00001
00005 #include <stdlib.h>
00006 #include <stdio.h>
00007 #include <errno.h>
00008 #include <math.h>
00009 #include <iostream>
00010 using namespace std;
00011
00012 #include "frequency_gen.h"
00013 #include "node.h"
00014
00016
00018 CLASS* frequency_gen::oclass = NULL;
00019 CLASS* frequency_gen::pclass = NULL;
00020
00021 frequency_gen::frequency_gen(MODULE *mod) : powerflow_object(mod)
00022 {
00023 if(oclass == NULL)
00024 {
00025 pclass = powerflow_object::oclass;
00026 oclass = gl_register_class(mod,"frequency_gen",sizeof(frequency_gen),PC_PRETOPDOWN|PC_POSTTOPDOWN);
00027 if(oclass == NULL)
00028 GL_THROW("unable to register object class implemented by %s",__FILE__);
00029
00030 if(gl_publish_variable(oclass,
00031 PT_enumeration,"Frequency_Mode",PADDR(FreqObjectMode),PT_DESCRIPTION,"Frequency object operations mode",
00032 PT_KEYWORD,"OFF", OFF,
00033 PT_KEYWORD,"AUTO", AUTO,
00034 PT_double,"Frequency[Hz]",PADDR(FrequencyValue),PT_DESCRIPTION,"Instantaneous frequency value",
00035 PT_double,"FreqChange[Hz/s]",PADDR(FrequencyChange),PT_DESCRIPTION,"Frequency change from last timestep",
00036 PT_double,"Deadband[Hz]",PADDR(Freq_Histr),PT_DESCRIPTION,"Frequency deadband of the governor",
00037 PT_double,"Tolerance[%]",PADDR(calc_tol_perc),PT_DESCRIPTION,"% threshold a power difference must be before it is cared about",
00038 PT_double,"M[pu.s]",PADDR(Mac_Inert),PT_DESCRIPTION,"Inertial constant of the system",
00039 PT_double,"D[%]",PADDR(D_Load),PT_DESCRIPTION,"Load-damping constant",
00040 PT_double,"Rated_power[W]",PADDR(system_base),PT_DESCRIPTION,"Rated power of system (base power)",
00041 PT_double,"Gen_power[W]",PADDR(PMech),PT_DESCRIPTION,"Mechanical power equivalent",
00042 PT_double,"Load_power[W]",PADDR(LoadPower),PT_DESCRIPTION,"Last sensed load value",
00043 PT_double,"Gov_delay[s]",PADDR(tgov_delay),PT_DESCRIPTION,"Governor delay time",
00044 PT_double,"Ramp_rate[W/s]",PADDR(ramp_rate),PT_DESCRIPTION,"Ramp ideal ramp rate",
00045 PT_double,"Low_Freq_OI[Hz]",PADDR(Freq_Low),PT_DESCRIPTION,"Low frequency setpoint for GFA devices",
00046 PT_double,"High_Freq_OI[Hz]",PADDR(Freq_High),PT_DESCRIPTION,"High frequency setpoint for GFA devices",
00047 PT_double,"avg24[Hz]",PADDR(day_average),PT_DESCRIPTION,"Average of last 24 hourly instantaneous measurements",
00048 PT_double,"std24[Hz]",PADDR(day_std),PT_DESCRIPTION,"Standard deviation of last 24 hourly instantaneous measurements",
00049 PT_double,"avg168[Hz]",PADDR(week_average),PT_DESCRIPTION,"Average of last 168 hourly instantaneous measurements",
00050 PT_double,"std168[Hz]",PADDR(week_std),PT_DESCRIPTION,"Standard deviation of last 168 hourly instantaneous measurements",
00051 PT_int32,"Num_Resp_Eqs",PADDR(Num_Resp_Eqs),PT_DESCRIPTION,"Total number of equations the response can contain",
00052 NULL) < 1) GL_THROW("unable to publish properties in %s",__FILE__);
00053 }
00054 }
00055
00056 int frequency_gen::isa(char *classname)
00057 {
00058 return strcmp(classname,"frequency_gen")==0;
00059 }
00060
00061 int frequency_gen::create(void)
00062 {
00063 int result = powerflow_object::create();
00064
00065 FreqObjectMode=AUTO;
00066
00067 FrequencyValue = 60.0;
00068 FrequencyChange = 0.0;
00069 Freq_Histr = 0.01;
00070
00071 Freq_Low = 59.95;
00072 Freq_High = 60.02;
00073
00074 ramp_rate = 10000000.0;
00075
00076 Mac_Inert = 10.0;
00077 D_Load = 0.75;
00078 system_base = 100000000.0;
00079 tgov_delay = 5.0;
00080
00081 Num_Resp_Eqs = 20;
00082
00083 calc_tol_perc = 0.01;
00084
00085 LoadPower = 0.0;
00086 PrevLoadPower = 0.0;
00087 PrevGenPower = 0.0;
00088
00089 CurrGenCondition = MATCHED;
00090 NextGenCondition = MATCHED;
00091 Gen_Ramp_Direction = false;
00092 Change_Lockout = false;
00093
00094 prev_time = 0;
00095 next_time = 0;
00096 curr_dt = 0;
00097 iter_passes=0;
00098
00099
00100 for (int index=0; index<168; index++)
00101 stored_freq[index] = 0.0;
00102
00103 curr_store_loc = 0;
00104 day_average = day_std = week_average = week_std = 0.0;
00105
00106 return result;
00107 }
00108
00109 int frequency_gen::init(OBJECT *parent)
00110 {
00111 OBJECT *obj = OBJECTHDR(this);
00112 char index;
00113
00114
00115 NominalFreq = FrequencyValue;
00116
00117 if (!(gl_object_isa(obj->parent,"load","powerflow") | gl_object_isa(obj->parent,"node","powerflow") | gl_object_isa(obj->parent,"meter","powerflow")))
00118 {
00119 GL_THROW("Parented object needs to be a node of some sort.");
00120
00121
00122
00123
00124
00125 }
00126
00127
00128 if (Num_Resp_Eqs==0)
00129 Num_Resp_Eqs=20;
00130
00131 CurrEquations = (ALGCOMPONENTS*)gl_malloc(Num_Resp_Eqs*sizeof(ALGCOMPONENTS));
00132
00133
00134 if (CurrEquations==NULL)
00135 {
00136 GL_THROW("Failed to allocate memory for equation structure in frequency_gen object.");
00137
00138
00139
00140
00141 }
00142
00143
00144 CurrEquations[0].coeffa = 0.0;
00145 CurrEquations[0].coeffa = 0.0;
00146 CurrEquations[0].coeffb = 0.0;
00147 CurrEquations[0].delay_time = 0.0;
00148 CurrEquations[0].contype = PERSCONST;
00149 CurrEquations[0].starttime = TS_NEVER;
00150 CurrEquations[0].endtime = TS_NEVER;
00151 CurrEquations[0].enteredtime = TS_NEVER;
00152
00153 for (index=1; index<Num_Resp_Eqs; index++)
00154 {
00155 CurrEquations[index].coeffa = 0.0;
00156 CurrEquations[index].coeffb = 0.0;
00157 CurrEquations[index].delay_time = 0.0;
00158 CurrEquations[index].contype = EMPTY;
00159 CurrEquations[index].starttime = TS_NEVER;
00160 CurrEquations[index].endtime = TS_NEVER;
00161 CurrEquations[index].enteredtime = TS_NEVER;
00162 }
00163
00164
00165 DB_Low = NominalFreq - Freq_Histr;
00166 DB_High = NominalFreq + Freq_Histr;
00167
00168
00169 F_Low_PU = Freq_Low/NominalFreq;
00170 F_High_PU = Freq_High/NominalFreq;
00171
00172
00173 P_delta = Freq_Histr/NominalFreq*D_Load*system_base;
00174
00175
00176 if (D_Load==0.0)
00177 {
00178 GL_THROW("The load-damping constant for the frequency_gen object must be non-zero!");
00179
00180
00181
00182
00183 }
00184
00185
00186 K_val = 1.0/D_Load;
00187 T_val = Mac_Inert/D_Load;
00188 invT_val = -1.0/T_val;
00189 AK_val = ramp_rate*K_val;
00190
00191
00192 eight_tau_value = (int)(Mac_Inert/D_Load*8.0*TS_SECOND);
00193
00194
00195 three_tau_value = (int)(Mac_Inert/D_Load*3.0*TS_SECOND);
00196
00197
00198 calc_tol = calc_tol_perc / 100.0;
00199
00200 return powerflow_object::init(parent);
00201 }
00202
00203 TIMESTAMP frequency_gen::presync(TIMESTAMP t0)
00204 {
00205 OBJECT *obj = OBJECTHDR(this);
00206 TIMESTAMP t1 = powerflow_object::presync(t0);
00207 int indexval, indexer;
00208 double tempval;
00209 bool PerformUpdate;
00210
00211 if (((solver_method==SM_NR) && (NR_cycle == true)) || (solver_method!=SM_NR))
00212 PerformUpdate = true;
00213 else
00214 PerformUpdate = false;
00215
00216 if (prev_time == 0)
00217 {
00218 prev_time = t0;
00219 next_time = TS_NEVER;
00220 track_time = t0;
00221 }
00222
00223 if (FreqObjectMode==AUTO)
00224 {
00225 curr_dt = (t0-prev_time);
00226
00227 if ((curr_dt>0) && PerformUpdate)
00228 {
00229 PrevLoadPower = LoadPower;
00230 PrevGenPower = PMech;
00231 prev_time = t0;
00232
00233 Change_Lockout = false;
00234
00235 next_time = pres_updatetime(t0, curr_dt);
00236
00237 CurrGenCondition = NextGenCondition;
00238
00239
00240 if ((t0 - track_time) > 3600)
00241 {
00242 stored_freq[curr_store_loc] = FrequencyValue;
00243
00244
00245 day_average = day_std = 0.0;
00246
00247
00248 for (indexer=0; indexer<24; indexer++)
00249 {
00250 indexval = curr_store_loc-indexer;
00251
00252 if (indexval<0)
00253 indexval+=168;
00254
00255 day_average += stored_freq[indexval];
00256 }
00257
00258
00259 week_average = day_average;
00260
00261
00262 for (indexer=24; indexer<168; indexer++)
00263 {
00264 indexval = curr_store_loc - indexer;
00265
00266 if (indexval<0)
00267 indexval+=168;
00268
00269 week_average += stored_freq[indexval];
00270 }
00271
00272
00273 day_average /=24.0;
00274 week_average /=168.0;
00275
00276
00277 for (indexer=0; indexer<24; indexer++)
00278 {
00279 indexval = curr_store_loc - indexer;
00280
00281 if (indexval<0)
00282 indexval+=168;
00283
00284 tempval = (stored_freq[indexval] - day_average);
00285 day_std += tempval*tempval;
00286 }
00287
00288
00289 for (indexer=0; indexer<24; indexer++)
00290 {
00291 indexval = curr_store_loc - indexer;
00292
00293 if (indexval<0)
00294 indexval+=168;
00295
00296 tempval = (stored_freq[indexval] - week_average);
00297 week_std += tempval*tempval;
00298 }
00299
00300
00301 day_std /= 23.0;
00302 week_std /= 167.0;
00303
00304
00305 day_std = sqrt(day_std);
00306 week_std = sqrt(week_std);
00307
00308
00309 curr_store_loc++;
00310
00311
00312 if (curr_store_loc >= 168)
00313 curr_store_loc=0;
00314
00315 track_time += 3600;
00316 }
00317 }
00318
00319
00320 if (Gov_Delay_Active == false)
00321 {
00322 Gov_Delay = t0 + (int64)(tgov_delay);
00323 }
00324 else
00325 {
00326 if ((Gov_Delay < next_time) && (t0 != Gov_Delay) && (NextGenCondition==GOV_DELAY))
00327 next_time = Gov_Delay;
00328
00329 }
00330
00331 if (next_time > t1)
00332 return t1;
00333 else
00334 {
00335 if (next_time == TS_NEVER)
00336 return TS_NEVER;
00337 else
00338 return -next_time;
00339 }
00340 }
00341 else
00342 return TS_NEVER;
00343 }
00344
00345 TIMESTAMP frequency_gen::postsync(TIMESTAMP t0)
00346 {
00347 OBJECT *obj = OBJECTHDR(this);
00348 node *ParNode = OBJECTDATA(obj->parent,node);
00349 TIMESTAMP tret = powerflow_object::postsync(t0);
00350 TIMESTAMP tupdate, tupdate_b;
00351 complex temp_power;
00352 double newFVal, power_diff, tempFreq, tempDFreq, work_val, LoadLow, LoadHigh, time_work;
00353 int index;
00354 bool PerformUpdate;
00355 bool CollapsedDone;
00356
00357 if (FreqObjectMode==AUTO)
00358 {
00359 if (((solver_method==SM_NR) && (NR_cycle == true)) || (solver_method!=SM_NR))
00360 PerformUpdate = true;
00361 else
00362 PerformUpdate = false;
00363
00364
00365 if (PerformUpdate)
00366 {
00367 temp_power = ParNode->voltage[0]*~ParNode->current_inj[0];
00368 temp_power += ParNode->voltage[1]*~ParNode->current_inj[1];
00369 temp_power += ParNode->voltage[2]*~ParNode->current_inj[2];
00370
00371
00372 LoadPower = temp_power.Re();
00373
00374 if (PrevLoadPower==0.0)
00375 {
00376 PrevLoadPower=LoadPower;
00377 PMech=LoadPower;
00378 return tret;
00379 }
00380
00381 iter_passes++;
00382
00383
00384 power_diff = (PrevLoadPower-LoadPower)/system_base;
00385
00386
00387 newFVal = FrequencyValue+power_diff/D_Load*NominalFreq;
00388
00389
00390 switch (CurrGenCondition)
00391 {
00392 case MATCHED:
00393 {
00394 if (iter_passes!=1)
00395 {
00396 RemoveScheduled(t0);
00397 Gov_Delay_Active = false;
00398 }
00399
00400
00401 LoadLow = PMech - P_delta;
00402 LoadHigh = PMech + P_delta;
00403 if ((LoadPower < LoadLow) || (LoadPower > LoadHigh))
00404 {
00405 NextGenCondition=GOV_DELAY;
00406 Gov_Delay_Active = true;
00407
00408
00409 for (index=0; index<Num_Resp_Eqs; index++)
00410 {
00411 if (CurrEquations[index].contype==EMPTY)
00412 {
00413 CurrEquations[index].coeffa = power_diff*K_val;
00414 CurrEquations[index].coeffb = invT_val;
00415 CurrEquations[index].contype = STEPEXP;
00416 CurrEquations[index].starttime = t0;
00417 CurrEquations[index].enteredtime = t0;
00418 CurrEquations[index].endtime = t0 + eight_tau_value;
00419
00420 break;
00421 }
00422
00423 if (index==(Num_Resp_Eqs-1))
00424 {
00425 gl_warning("Failed to process frequency deviation at time %lld",t0);
00426
00427
00428
00429
00430
00431 }
00432 }
00433
00434
00435 if ((newFVal <= Freq_Low) || (newFVal > Freq_High))
00436 {
00437 if (power_diff<0)
00438 {
00439 tupdate = (int64)(-1.0*T_val*log(1.0 - ((F_Low_PU-1.0)/power_diff/K_val)));
00440 }
00441 else
00442 {
00443 tupdate = (int64)(-1.0*T_val*log(1.0 - ((F_High_PU-1)/power_diff/K_val)));
00444 }
00445
00446
00447 tupdate += t0 + 1;
00448 }
00449 else
00450 tupdate = TS_NEVER;
00451
00452
00453 if (Gov_Delay < tupdate)
00454 next_time = Gov_Delay;
00455 else
00456 next_time = tupdate;
00457 }
00458 else
00459 {
00460
00461 if (power_diff != 0.0)
00462 {
00463 work_val = power_diff/D_Load;
00464
00465
00466 for (index=0; index<Num_Resp_Eqs; index++)
00467 {
00468 if (CurrEquations[index].contype == PERSCONST)
00469 {
00470 CurrEquations[index].coeffa += work_val;
00471
00472 break;
00473 }
00474 }
00475 }
00476
00477 NextGenCondition=MATCHED;
00478 tupdate = TS_NEVER;
00479 next_time = TS_NEVER;
00480 }
00481 break;
00482 }
00483 case GOV_DELAY:
00484 {
00485
00486 if (iter_passes!=1)
00487 {
00488 RemoveScheduled(t0);
00489 }
00490
00491
00492 if ((power_diff>calc_tol) || (power_diff<-calc_tol))
00493 {
00494
00495
00496 for (index=0; index<Num_Resp_Eqs; index++)
00497 {
00498 if (CurrEquations[index].contype==EMPTY)
00499 {
00500 CurrEquations[index].coeffa = power_diff*K_val;
00501 CurrEquations[index].coeffb = invT_val;
00502 CurrEquations[index].contype = STEPEXP;
00503 CurrEquations[index].starttime = t0;
00504 CurrEquations[index].enteredtime = t0;
00505 CurrEquations[index].endtime = t0 + eight_tau_value;
00506
00507 break;
00508 }
00509
00510 if (index==(Num_Resp_Eqs-1))
00511 {
00512 gl_warning("Failed to process frequency deviation at time %lld",t0);
00513
00514 }
00515 }
00516 }
00517
00518
00519 if (t0 >= Gov_Delay)
00520 {
00521 NextGenCondition = GEN_RAMP;
00522
00523
00524 for (index=0; index<Num_Resp_Eqs; index++)
00525 {
00526 if (CurrEquations[index].contype==EMPTY)
00527 {
00528
00529 power_diff = PMech - LoadPower;
00530
00531 if (power_diff>0)
00532 {
00533 work_val = -ramp_rate;
00534 Gen_Ramp_Direction = false;
00535 time_work = power_diff/ramp_rate;
00536 }
00537 else
00538 {
00539 work_val = ramp_rate;
00540 Gen_Ramp_Direction = true;
00541 time_work = -power_diff/ramp_rate;
00542 }
00543
00544
00545 tupdate_b = (int64)(time_work)+1;
00546
00547
00548 work_val /= system_base;
00549
00550
00551 CurrEquations[index].coeffa = work_val*K_val;
00552 CurrEquations[index].coeffb = invT_val;
00553 CurrEquations[index].delay_time = time_work;
00554 CurrEquations[index].contype = RAMPEXP;
00555 CurrEquations[index].starttime = t0;
00556 CurrEquations[index].enteredtime = t0;
00557 CurrEquations[index].endtime = t0 + eight_tau_value + tupdate_b;
00558
00559
00560 tupdate_b += t0;
00561
00562
00563 tempFreq = FrequencyValue;
00564 tupdate = updatetime((t0+1),1,tempFreq,tempDFreq);
00565
00566
00567 if (tupdate > tupdate_b)
00568 next_time = tupdate_b;
00569 else
00570 next_time = tupdate;
00571
00572 break;
00573 }
00574
00575 if (index==(Num_Resp_Eqs-1))
00576 {
00577 gl_warning("Failed to process frequency deviation at time %lld",t0);
00578
00579 }
00580 }
00581 }
00582 else
00583 {
00584
00585 tempFreq = FrequencyValue;
00586 tupdate = updatetime((t0+1),1,tempFreq,tempDFreq);
00587
00588
00589 if (tupdate > Gov_Delay)
00590 next_time = Gov_Delay;
00591 else
00592 next_time = tupdate;
00593
00594 NextGenCondition = GOV_DELAY;
00595 Gov_Delay_Active = true;
00596 }
00597 break;
00598 }
00599 case GEN_RAMP:
00600 {
00601
00602 if (Gen_Ramp_Direction)
00603 PMech += ramp_rate*(double)(curr_dt);
00604 else
00605 PMech -= ramp_rate*(double)(curr_dt);
00606
00607
00608 Gov_Delay_Active = false;
00609
00610
00611 if (((power_diff>calc_tol) || (power_diff<-calc_tol)) && (Change_Lockout==false))
00612 {
00613
00614 if (iter_passes!=1)
00615 {
00616 RemoveScheduled(t0);
00617 }
00618
00619
00620 for (index=0; index<Num_Resp_Eqs; index++)
00621 {
00622 if (CurrEquations[index].contype==EMPTY)
00623 {
00624 CurrEquations[index].coeffa = power_diff*K_val;
00625 CurrEquations[index].coeffb = invT_val;
00626 CurrEquations[index].contype = STEPEXP;
00627 CurrEquations[index].starttime = t0;
00628 CurrEquations[index].enteredtime = t0;
00629 CurrEquations[index].endtime = t0 + eight_tau_value;
00630
00631 break;
00632 }
00633
00634 if (index==(Num_Resp_Eqs-1))
00635 {
00636 gl_warning("Failed to process frequency deviation at time %lld",t0);
00637
00638 }
00639 }
00640
00641
00642 work_val = (PMech - LoadPower)/system_base;
00643
00644
00645 if (Gen_Ramp_Direction)
00646 {
00647 if (work_val<0)
00648 {
00649
00650 time_work = (LoadPower - PrevLoadPower)/ramp_rate;
00651 tupdate_b = (int64)(time_work)+1;
00652
00653
00654 for (index=0; index<Num_Resp_Eqs; index++)
00655 {
00656 if (CurrEquations[index].contype == RAMPEXP)
00657 {
00658
00659 tupdate = (int64)(CurrEquations[index].delay_time) + CurrEquations[index].starttime;
00660
00661
00662 if (tupdate > t0)
00663 {
00664
00665 CurrEquations[index].delay_time += time_work;
00666
00667
00668 CurrEquations[index].endtime += tupdate_b;
00669
00670
00671 Change_Lockout=true;
00672
00673
00674 break;
00675 }
00676 }
00677 }
00678
00679 if ((index==Num_Resp_Eqs) && (time_work > 0))
00680 {
00681
00682 for (index=0; index<Num_Resp_Eqs; index++)
00683 {
00684 if (CurrEquations[index].contype == EMPTY)
00685 {
00686
00687
00688 work_val = ramp_rate/system_base;
00689
00690
00691 CurrEquations[index].coeffa = work_val*K_val;
00692 CurrEquations[index].coeffb = invT_val;
00693 CurrEquations[index].delay_time = time_work;
00694 CurrEquations[index].contype = RAMPEXP;
00695 CurrEquations[index].starttime = t0;
00696 CurrEquations[index].enteredtime = t0;
00697 CurrEquations[index].endtime = t0 + eight_tau_value + tupdate_b;
00698
00699
00700 break;
00701 }
00702
00703 if (index==(Num_Resp_Eqs-1))
00704 {
00705 gl_warning("Failed to process frequency deviation at time %lld",t0);
00706
00707 }
00708 }
00709 }
00710 }
00711 else
00712 {
00713
00714 for (index=0; index<Num_Resp_Eqs; index++)
00715 {
00716 if (CurrEquations[index].contype == RAMPEXP)
00717 {
00718
00719 tupdate = (int64)(CurrEquations[index].delay_time) + CurrEquations[index].starttime;
00720
00721
00722 if (tupdate > t0)
00723 {
00724
00725 CurrEquations[index].delay_time = (double)(t0 - CurrEquations[index].starttime);
00726
00727
00728 Change_Lockout=true;
00729
00730
00731 break;
00732 }
00733 }
00734 }
00735
00736
00737 for (index=0; index<Num_Resp_Eqs; index++)
00738 {
00739 if (CurrEquations[index].contype == EMPTY)
00740 {
00741
00742 Gen_Ramp_Direction=false;
00743
00744
00745 time_work = (PMech - LoadPower)/ramp_rate;
00746 tupdate_b = (int64)(time_work)+1;
00747
00748
00749
00750 work_val = -ramp_rate/system_base;
00751
00752
00753 CurrEquations[index].coeffa = work_val*K_val;
00754 CurrEquations[index].coeffb = invT_val;
00755 CurrEquations[index].delay_time = time_work;
00756 CurrEquations[index].contype = RAMPEXP;
00757 CurrEquations[index].starttime = t0;
00758 CurrEquations[index].enteredtime = t0;
00759 CurrEquations[index].endtime = t0 + eight_tau_value + tupdate_b;
00760
00761
00762 break;
00763 }
00764
00765 if (index==(Num_Resp_Eqs-1))
00766 {
00767 gl_warning("Failed to process frequency deviation at time %lld",t0);
00768
00769 }
00770 }
00771 }
00772 }
00773 else
00774 {
00775 if (work_val>0)
00776 {
00777
00778 time_work = (PrevLoadPower - LoadPower)/ramp_rate;
00779 tupdate_b = (int64)(time_work)+1;
00780
00781
00782 for (index=0; index<Num_Resp_Eqs; index++)
00783 {
00784 if (CurrEquations[index].contype == RAMPEXP)
00785 {
00786
00787 tupdate = (int64)(CurrEquations[index].delay_time) + CurrEquations[index].starttime;
00788
00789
00790 if (tupdate > t0)
00791 {
00792
00793 CurrEquations[index].delay_time += time_work;
00794
00795
00796 CurrEquations[index].endtime += tupdate_b;
00797
00798
00799 Change_Lockout=true;
00800
00801
00802 break;
00803 }
00804 }
00805 }
00806
00807 if ((index==Num_Resp_Eqs) && (time_work > 0))
00808 {
00809
00810 for (index=0; index<Num_Resp_Eqs; index++)
00811 {
00812 if (CurrEquations[index].contype == EMPTY)
00813 {
00814
00815
00816 work_val = -ramp_rate/system_base;
00817
00818
00819 CurrEquations[index].coeffa = work_val*K_val;
00820 CurrEquations[index].coeffb = invT_val;
00821 CurrEquations[index].delay_time = time_work;
00822 CurrEquations[index].contype = RAMPEXP;
00823 CurrEquations[index].starttime = t0;
00824 CurrEquations[index].enteredtime = t0;
00825 CurrEquations[index].endtime = t0 + eight_tau_value + tupdate_b;
00826
00827
00828 break;
00829 }
00830
00831 if (index==(Num_Resp_Eqs-1))
00832 {
00833 gl_warning("Failed to process frequency deviation at time %lld",t0);
00834
00835 }
00836 }
00837 }
00838 }
00839 else
00840 {
00841
00842
00843 for (index=0; index<Num_Resp_Eqs; index++)
00844 {
00845 if (CurrEquations[index].contype == RAMPEXP)
00846 {
00847
00848 tupdate = (int64)(CurrEquations[index].delay_time) + CurrEquations[index].starttime;
00849
00850
00851 if (tupdate > t0)
00852 {
00853
00854 CurrEquations[index].delay_time = (double)(t0 - CurrEquations[index].starttime);
00855
00856
00857 Change_Lockout=true;
00858
00859
00860 break;
00861 }
00862 }
00863 }
00864
00865
00866 for (index=0; index<Num_Resp_Eqs; index++)
00867 {
00868 if (CurrEquations[index].contype == EMPTY)
00869 {
00870
00871 Gen_Ramp_Direction=true;
00872
00873
00874 time_work = (LoadPower - PMech)/ramp_rate;
00875 tupdate_b = (int64)(time_work)+1;
00876
00877
00878
00879 work_val = ramp_rate/system_base;
00880
00881
00882 CurrEquations[index].coeffa = work_val*K_val;
00883 CurrEquations[index].coeffb = invT_val;
00884 CurrEquations[index].delay_time = time_work;
00885 CurrEquations[index].contype = RAMPEXP;
00886 CurrEquations[index].starttime = t0;
00887 CurrEquations[index].enteredtime = t0;
00888 CurrEquations[index].endtime = t0 + eight_tau_value + tupdate_b;
00889
00890
00891 break;
00892 }
00893
00894 if (index==(Num_Resp_Eqs-1))
00895 {
00896 gl_warning("Failed to process frequency deviation at time %lld",t0);
00897
00898 }
00899 }
00900 }
00901 }
00902
00903
00904 NextGenCondition = GEN_RAMP;
00905 Gov_Delay_Active = false;
00906
00907
00908 power_diff = PMech - LoadPower;
00909
00910
00911 if (power_diff>0)
00912 tupdate_b = (int64)(power_diff/ramp_rate);
00913 else
00914 tupdate_b = (int64)(-power_diff/ramp_rate);
00915
00916 tupdate_b += t0;
00917
00918 if (tupdate_b <= t0)
00919 tupdate_b = t0+1;
00920
00921
00922 tempFreq = FrequencyValue;
00923 tupdate = updatetime((t0+1),1,tempFreq,tempDFreq);
00924
00925
00926 if (tupdate_b > tupdate)
00927 next_time = tupdate;
00928 else
00929 next_time = tupdate_b;
00930 }
00931 else
00932 {
00933
00934 LoadLow = PrevLoadPower - P_delta;
00935 LoadHigh = PrevLoadPower + P_delta;
00936
00937
00938 if (Gen_Ramp_Direction)
00939 {
00940
00941 if ((PrevGenPower < LoadLow) && (PMech > LoadHigh) && ((LoadHigh-LoadLow) < ramp_rate))
00942 PMech = PrevLoadPower;
00943 }
00944 else
00945 {
00946
00947 if ((PrevGenPower > LoadHigh) && (PMech < LoadLow) && ((LoadHigh-LoadLow) < ramp_rate))
00948 PMech = PrevLoadPower;
00949 }
00950
00951 if ((PMech > LoadLow) && (PMech < LoadHigh) && (Change_Lockout==false))
00952 {
00953 NextGenCondition = MATCHED;
00954 Gov_Delay_Active = false;
00955 Change_Lockout = true;
00956
00957
00958
00959 tupdate = t0 + eight_tau_value;
00960
00961
00962 tempFreq = FrequencyValue;
00963 tupdate_b = updatetime(tupdate,1,tempFreq,tempDFreq);
00964
00965
00966
00967 work_val = (NominalFreq - FrequencyValue) / NominalFreq;
00968
00969
00970 CollapsedDone = false;
00971
00972 for (index=0; index<Num_Resp_Eqs; index++)
00973 {
00974 if ((CurrEquations[index].contype == STEPEXP) || (CurrEquations[index].contype == RAMPEXP))
00975 {
00976
00977 if (CollapsedDone == false)
00978 {
00979 CurrEquations[index].contype = CSTEPEXP;
00980 CurrEquations[index].coeffa = work_val;
00981 CurrEquations[index].coeffb = invT_val;
00982 CurrEquations[index].delay_time = -work_val;
00983 CurrEquations[index].starttime = t0;
00984 CurrEquations[index].enteredtime = t0;
00985 CurrEquations[index].endtime = tupdate;
00986
00987 CollapsedDone = true;
00988 }
00989 else
00990 {
00991
00992 CurrEquations[index].coeffa = 0.0;
00993 CurrEquations[index].coeffb = 0.0;
00994 CurrEquations[index].delay_time = 0.0;
00995 CurrEquations[index].contype = EMPTY;
00996 CurrEquations[index].starttime = TS_NEVER;
00997 CurrEquations[index].enteredtime = TS_NEVER;
00998 CurrEquations[index].endtime = TS_NEVER;
00999 }
01000 }
01001 }
01002
01003
01004 next_time = tupdate;
01005
01006 }
01007 else
01008 {
01009
01010 power_diff = PMech - LoadPower;
01011
01012
01013 if (power_diff>0)
01014 tupdate_b = (int64)(power_diff/ramp_rate);
01015 else
01016 tupdate_b = (int64)(-power_diff/ramp_rate);
01017
01018 if (tupdate_b == 0)
01019 tupdate_b = 1;
01020
01021 tupdate_b += t0;
01022
01023
01024 tempFreq = FrequencyValue;
01025 tupdate = updatetime((t0+1),1,tempFreq,tempDFreq);
01026
01027
01028 if (tupdate_b > tupdate)
01029 next_time = tupdate;
01030 else
01031 next_time = tupdate_b;
01032
01033
01034 NextGenCondition = GEN_RAMP;
01035 Gov_Delay_Active = false;
01036 }
01037 }
01038 break;
01039 }
01040 default:
01041 {
01042 GL_THROW("The frequency object has entered an unknown state.");
01043
01044
01045
01046
01047 }
01048 }
01049
01050
01051
01052 if (tret > next_time)
01053 tret = next_time;
01054
01055
01056 if (tret==TS_NEVER)
01057 return TS_NEVER;
01058 else
01059 return -tret;
01060 }
01061 else
01062 {
01063 if (next_time == TS_NEVER)
01064 return TS_NEVER;
01065 else
01066 return -next_time;
01067 }
01068 }
01069 else
01070 return TS_NEVER;
01071 }
01072
01073
01074 TIMESTAMP frequency_gen::pres_updatetime(TIMESTAMP t0, TIMESTAMP dt_val)
01075 {
01076 TIMESTAMP tnext;
01077 int index, indexy;
01078 double WorkVal, OWorkVal, final_value;
01079 double dt;
01080
01081
01082 for (index=0; index<Num_Resp_Eqs; index++)
01083 {
01084 if (CurrEquations[index].endtime < t0)
01085 {
01086
01087 if (CurrEquations[index].contype == STEPEXP)
01088 {
01089 dt = (double)(t0 - CurrEquations[index].starttime);
01090
01091 WorkVal = 1.0 - exp(CurrEquations[index].coeffb*dt);
01092
01093 final_value = CurrEquations[index].coeffa*WorkVal;
01094 }
01095 else if (CurrEquations[index].contype == RAMPEXP)
01096 {
01097 dt = (double)(t0 - CurrEquations[index].starttime);
01098
01099 WorkVal = exp(CurrEquations[index].coeffb*dt) - 1.0;
01100 WorkVal *= -1.0/CurrEquations[index].coeffb;
01101 WorkVal += dt;
01102
01103
01104 OWorkVal = exp(CurrEquations[index].coeffb*(dt - CurrEquations[index].delay_time));
01105 OWorkVal /= CurrEquations[index].coeffb;
01106 OWorkVal -= 1.0/CurrEquations[index].coeffb;
01107 OWorkVal += CurrEquations[index].delay_time-dt;
01108
01109 WorkVal += OWorkVal;
01110
01111 final_value = CurrEquations[index].coeffa*WorkVal;
01112 }
01113 else
01114 final_value = 0.0;
01115
01116
01117 for (indexy=0; indexy<Num_Resp_Eqs; indexy++)
01118 {
01119 if (CurrEquations[indexy].contype==PERSCONST)
01120 {
01121 CurrEquations[indexy].coeffa += final_value;
01122
01123 break;
01124 }
01125
01126 if (index==(Num_Resp_Eqs-1))
01127 {
01128 GL_THROW("Failed to find persistent constant!");
01129
01130
01131
01132
01133 }
01134 }
01135
01136 CurrEquations[index].coeffa = 0.0;
01137 CurrEquations[index].coeffb = 0.0;
01138 CurrEquations[index].delay_time = 0.0;
01139 CurrEquations[index].contype = EMPTY;
01140 CurrEquations[index].starttime = TS_NEVER;
01141 CurrEquations[index].endtime = TS_NEVER;
01142 CurrEquations[index].enteredtime = TS_NEVER;
01143 }
01144 }
01145
01146
01147 tnext = updatetime(t0, dt_val, FrequencyValue, FrequencyChange);
01148
01149 return tnext;
01150 }
01151
01152
01153
01154 TIMESTAMP frequency_gen::updatetime(TIMESTAMP t0, TIMESTAMP dt_val, double &FreqValue, double &DeltaFreq)
01155 {
01156 TIMESTAMP tnext;
01157 double dt;
01158 int index;
01159 double CalcedFreq;
01160 double WorkVal, OWorkVal;
01161
01162
01163 CalcedFreq = 1.0;
01164
01165
01166 for (index=0; index<Num_Resp_Eqs; index++)
01167 {
01168
01169 switch (CurrEquations[index].contype)
01170 {
01171 case EMPTY:
01172 break;
01173 case PERSCONST:
01174 {
01175 CalcedFreq += CurrEquations[index].coeffa;
01176 break;
01177 }
01178 case CONSTANT:
01179 {
01180 if ((t0 >= CurrEquations[index].starttime) && (t0 < CurrEquations[index].endtime))
01181 {
01182 CalcedFreq += CurrEquations[index].coeffa;
01183 }
01184 break;
01185 }
01186 case SCALAR:
01187 {
01188 if ((t0 >= CurrEquations[index].starttime) && (t0 < CurrEquations[index].endtime))
01189 {
01190 dt = (double)(t0 - CurrEquations[index].starttime);
01191
01192 CalcedFreq += CurrEquations[index].coeffa*dt;
01193 }
01194 break;
01195 }
01196 case EXPONENT:
01197 {
01198 if ((t0 >= CurrEquations[index].starttime) && (t0 < CurrEquations[index].endtime))
01199 {
01200 dt = (double)(t0 - CurrEquations[index].starttime);
01201
01202 CalcedFreq += CurrEquations[index].coeffa*exp(CurrEquations[index].coeffb*dt);
01203 }
01204 break;
01205 }
01206 case STEPEXP:
01207 {
01208 if ((t0 >= CurrEquations[index].starttime) && (t0 < CurrEquations[index].endtime))
01209 {
01210 dt = (double)(t0 - CurrEquations[index].starttime);
01211
01212 WorkVal = 1.0 - exp(CurrEquations[index].coeffb*dt);
01213
01214 CalcedFreq += CurrEquations[index].coeffa*WorkVal;
01215 }
01216 break;
01217 }
01218 case RAMPEXP:
01219 {
01220 if ((t0 >= CurrEquations[index].starttime) && (t0 < CurrEquations[index].endtime))
01221 {
01222 dt = (double)(t0 - CurrEquations[index].starttime);
01223
01224 WorkVal = exp(CurrEquations[index].coeffb*dt) - 1.0;
01225 WorkVal *= -1.0/CurrEquations[index].coeffb;
01226 WorkVal += dt;
01227
01228 if (dt >= (CurrEquations[index].delay_time+1))
01229 {
01230 OWorkVal = exp(CurrEquations[index].coeffb*(dt - CurrEquations[index].delay_time));
01231 OWorkVal /= CurrEquations[index].coeffb;
01232 OWorkVal -= 1.0/CurrEquations[index].coeffb;
01233 OWorkVal += CurrEquations[index].delay_time-dt;
01234 }
01235 else
01236 OWorkVal = 0.0;
01237
01238 WorkVal += OWorkVal;
01239
01240 CalcedFreq += CurrEquations[index].coeffa*WorkVal;
01241 }
01242 break;
01243 }
01244 case CSTEPEXP:
01245 {
01246 if ((t0 >= CurrEquations[index].starttime) && (t0 < CurrEquations[index].endtime))
01247 {
01248 dt = (double)(t0 - CurrEquations[index].starttime);
01249
01250 WorkVal = 1.0 - exp(CurrEquations[index].coeffb*dt);
01251
01252 WorkVal *= CurrEquations[index].coeffa;
01253
01254 CalcedFreq += WorkVal + CurrEquations[index].delay_time;
01255 }
01256 break;
01257 }
01258 default:
01259 {
01260 GL_THROW("Unknown equation component encountered by frequency generation object.");
01261
01262
01263
01264
01265 }
01266 }
01267 }
01268
01269
01270 CalcedFreq *= NominalFreq;
01271
01272
01273 DeltaFreq=(CalcedFreq-FreqValue)/dt_val;
01274
01275
01276 FreqValue = CalcedFreq;
01277
01278
01279 if (FreqValue < Freq_Low)
01280 {
01281 if (DeltaFreq > 0)
01282 {
01283 tnext = t0 + (int64)((Freq_Low - FreqValue)/DeltaFreq);
01284 }
01285 else
01286 {
01287 tnext = TS_NEVER;
01288 }
01289 }
01290 else if ((FreqValue > Freq_Low) && (FreqValue < Freq_High))
01291 {
01292 if (DeltaFreq > 0)
01293 {
01294 tnext = t0 + (int64)((Freq_High - FreqValue)/DeltaFreq);
01295 }
01296 else if (DeltaFreq < 0)
01297 {
01298 tnext = t0 + (int64)((Freq_Low - FreqValue)/DeltaFreq);
01299 }
01300 else
01301 {
01302 tnext = TS_NEVER;
01303 }
01304 }
01305 else if (FreqValue > Freq_High)
01306 {
01307 if (DeltaFreq < 0)
01308 {
01309 tnext = t0 + (int64)((Freq_High - FreqValue)/DeltaFreq);
01310 }
01311 else
01312 {
01313 tnext = TS_NEVER;
01314 }
01315 }
01316 else
01317 tnext = TS_NEVER;
01318
01319 if (tnext==t0)
01320 tnext=t0+1;
01321
01322 return tnext;
01323 }
01324
01325
01326
01327
01328
01329 void frequency_gen::DumpScheduler(void)
01330 {
01331 FILE *FP = fopen("dumpout.txt","w");
01332
01333 for (int index=0; index<Num_Resp_Eqs; index++)
01334 {
01335 if (CurrEquations[index].contype == EMPTY)
01336 fprintf(FP,"%d - Empty - %lld - %lld - %lld - %f - %f ++ %f\n",index,CurrEquations[index].enteredtime,CurrEquations[index].starttime,CurrEquations[index].endtime,CurrEquations[index].coeffa,CurrEquations[index].coeffb,CurrEquations[index].delay_time);
01337 else if (CurrEquations[index].contype == CONSTANT)
01338 fprintf(FP,"%d - Constant - %lld - %lld - %lld - %f - %f ++ %f\n",index,CurrEquations[index].enteredtime,CurrEquations[index].starttime,CurrEquations[index].endtime,CurrEquations[index].coeffa,CurrEquations[index].coeffb,CurrEquations[index].delay_time);
01339 else if (CurrEquations[index].contype == SCALAR)
01340 fprintf(FP,"%d - Scalar - %lld - %lld - %lld - %f - %f ++ %f\n",index,CurrEquations[index].enteredtime,CurrEquations[index].starttime,CurrEquations[index].endtime,CurrEquations[index].coeffa,CurrEquations[index].coeffb,CurrEquations[index].delay_time);
01341 else if (CurrEquations[index].contype == EXPONENT)
01342 fprintf(FP,"%d - Exponent - %lld - %lld - %lld - %f - %f ++ %f\n",index,CurrEquations[index].enteredtime,CurrEquations[index].starttime,CurrEquations[index].endtime,CurrEquations[index].coeffa,CurrEquations[index].coeffb,CurrEquations[index].delay_time);
01343 else if (CurrEquations[index].contype == STEPEXP)
01344 fprintf(FP,"%d - Step Exp - %lld - %lld - %lld - %f - %f ++ %f\n",index,CurrEquations[index].enteredtime,CurrEquations[index].starttime,CurrEquations[index].endtime,CurrEquations[index].coeffa,CurrEquations[index].coeffb,CurrEquations[index].delay_time);
01345 else if (CurrEquations[index].contype == RAMPEXP)
01346 fprintf(FP,"%d - Ramp Exp - %lld - %lld - %lld - %f - %f ++ %f\n",index,CurrEquations[index].enteredtime,CurrEquations[index].starttime,CurrEquations[index].endtime,CurrEquations[index].coeffa,CurrEquations[index].coeffb,CurrEquations[index].delay_time);
01347 else if (CurrEquations[index].contype == PERSCONST)
01348 fprintf(FP,"%d - Persistent Constant - %lld - %lld - %lld - %f - %f ++ %f\n",index,CurrEquations[index].enteredtime,CurrEquations[index].starttime,CurrEquations[index].endtime,CurrEquations[index].coeffa,CurrEquations[index].coeffb,CurrEquations[index].delay_time);
01349 else if (CurrEquations[index].contype == CSTEPEXP)
01350 fprintf(FP,"%d - Const Step Exp - %lld - %lld - %lld - %f - %f ++ %f\n",index,CurrEquations[index].enteredtime,CurrEquations[index].starttime,CurrEquations[index].endtime,CurrEquations[index].coeffa,CurrEquations[index].coeffb,CurrEquations[index].delay_time);
01351 else
01352 fprintf(FP,"%d - ???\n",index);
01353 }
01354 fclose(FP);
01355
01356 }
01357
01358
01359 void frequency_gen::RemoveScheduled(TIMESTAMP t0)
01360 {
01361 int index;
01362
01363
01364 for (index=0; index<Num_Resp_Eqs; index++)
01365 {
01366 if (CurrEquations[index].enteredtime==t0)
01367 {
01368 CurrEquations[index].coeffa = 0.0;
01369 CurrEquations[index].coeffb = 0.0;
01370 CurrEquations[index].contype = EMPTY;
01371 CurrEquations[index].starttime = TS_NEVER;
01372 CurrEquations[index].endtime = TS_NEVER;
01373 CurrEquations[index].enteredtime = TS_NEVER;
01374 }
01375 }
01376 }
01377
01378
01379 EXPORT int commit_frequency_gen(OBJECT *obj)
01380 {
01381 frequency_gen *fgen = OBJECTDATA(obj,frequency_gen);
01382 try {
01383 fgen->iter_passes=0;
01384 return 1;
01385 }
01386 catch (const char *msg)
01387 {
01388 GL_THROW("%s (frequency_gen:%d): %s", fgen->get_name(), fgen->get_id(), msg);
01389 return 0;
01390 }
01391
01392 }
01393
01395
01397
01405 EXPORT int create_frequency_gen(OBJECT **obj, OBJECT *parent)
01406 {
01407 try
01408 {
01409 *obj = gl_create_object(frequency_gen::oclass);
01410 if (*obj!=NULL)
01411 {
01412 frequency_gen *my = OBJECTDATA(*obj,frequency_gen);
01413 gl_set_parent(*obj,parent);
01414 return my->create();
01415 }
01416 }
01417 catch (const char *msg)
01418 {
01419 gl_error("%s %s (id=%d): %s", (*obj)->name?(*obj)->name:"unnamed", (*obj)->oclass->name, (*obj)->id, msg);
01420 return 0;
01421 }
01422 return 1;
01423 }
01424
01425 EXPORT int init_frequency_gen(OBJECT *obj, OBJECT *parent)
01426 {
01427 try {
01428 return OBJECTDATA(obj,frequency_gen)->init(parent);
01429 }
01430 catch (const char *msg)
01431 {
01432 gl_error("%s %s (id=%d): %s", obj->name?obj->name:"unnamed", obj->oclass->name, obj->id, msg);
01433 return 0;
01434 }
01435 return 1;
01436 }
01437
01446 EXPORT TIMESTAMP sync_frequency_gen(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
01447 {
01448 frequency_gen *pObj = OBJECTDATA(obj,frequency_gen);
01449 try {
01450 TIMESTAMP t1 = TS_NEVER;
01451 switch (pass) {
01452 case PC_PRETOPDOWN:
01453 return pObj->presync(t0);
01454 case PC_BOTTOMUP:
01455 return pObj->sync(t0);
01456 case PC_POSTTOPDOWN:
01457 t1 = pObj->postsync(t0);
01458 obj->clock = t0;
01459 return t1;
01460 default:
01461 throw "invalid pass request";
01462 }
01463 }
01464 catch (const char *msg)
01465 {
01466 gl_error("frequency_gen %s (%s:%d): %s", obj->name, obj->oclass->name, obj->id, msg);
01467 }
01468 catch (...)
01469 {
01470 gl_error("frequency_gen %s (%s:%d): unknown exception", obj->name, obj->oclass->name, obj->id);
01471 }
01472 return TS_INVALID;
01473 }
01474
01475 EXPORT int isa_frequency_gen(OBJECT *obj, char *classname)
01476 {
01477 return OBJECTDATA(obj,frequency_gen)->isa(classname);
01478 }
01479