00001
00039 #include <stdlib.h>
00040 #include <stdio.h>
00041 #include <errno.h>
00042
00043 #include "metrics.h"
00044
00045 CLASS *metrics::oclass = NULL;
00046 metrics *metrics::defaults = NULL;
00047
00048 double metrics::major_event_threshold = 0.01;
00049 bool metrics::report_event_log = true;
00050
00051 static PASSCONFIG passconfig = PC_POSTTOPDOWN;
00052 static PASSCONFIG clockpass = PC_POSTTOPDOWN;
00053
00054
00055 metrics::metrics(MODULE *module)
00056 {
00057 if (oclass==NULL)
00058 {
00059 oclass = gl_register_class(module,"metrics",sizeof(metrics),passconfig);
00060 if (oclass==NULL)
00061 GL_THROW("unable to register object class implemented by %s", __FILE__);
00062
00063 if (gl_publish_variable(oclass,
00064 PT_char1024, "report_file", PADDR(report_file),
00065 PT_double, "SAIFI", PADDR(SAIFI),
00066 PT_double, "SAIDI", PADDR(SAIDI),
00067 PT_double, "CAIDI", PADDR(CAIDI),
00068 PT_double, "CAIFI", PADDR(CAIFI),
00069 PT_double, "CTAIDI", PADDR(CTAIDI),
00070 PT_double, "ASAI", PADDR(ASAI),
00071 PT_double, "ASIFI", PADDR(ASIFI),
00072 PT_double, "ASIDI", PADDR(ASIDI),
00073 PT_double, "CEMI", PADDR(CEMI),
00074 PT_double, "MAIFI", PADDR(MAIFI),
00075 PT_double, "MAIFIE", PADDR(MAIFIE),
00076 PT_double, "CEMSMI", PADDR(CEMSMI),
00077 PT_int16, "n", PADDR(n),
00078 NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
00079 defaults = this;
00080 memset(this,0,sizeof(metrics));
00081 customers = interrupted = NULL;
00082 SAIFI = SAIDI = CAIDI = CAIFI = CTAIDI = ASAI = ASIFI = ASIDI = CEMI = MAIFI = MAIFIE = CEMSMI = n =0.0;
00083 strcpy(report_file,"reliability.txt");
00084 }
00085 }
00086
00087
00088 int metrics::create(void)
00089 {
00090 memcpy(this,defaults,sizeof(metrics));
00091 return 1;
00092 }
00093
00094
00095 int metrics::init(OBJECT *parent)
00096 {
00097 fp = fopen(report_file,"w");
00098 if (fp==NULL)
00099 {
00100 gl_error("unable to create file '%s' for reliability metrics report",report_file);
00101 return 0;
00102 }
00103 char buffer[1024];
00104 fprintf(fp,"Reliability report for %s\n", gl_global_getvar("modelname",buffer,sizeof(buffer)));
00105
00106 customers=gl_find_objects(FL_GROUP,("class=triplex_meter"));
00107
00108 FINDLIST *normmeter=gl_find_objects(FL_GROUP,("class=meter"));
00109
00110 OBJECT *metercust=NULL;
00111 while ((metercust=gl_find_next(normmeter,metercust))!=NULL)
00112 gl_findlist_add(customers,metercust);
00113
00114
00115
00116 interrupted = gl_find_objects(FL_NEW,NULL);
00117 fprintf(fp,"Number of customers: %d\n", customers->hit_count);
00118 first_year = true;
00119 return 1;
00120 }
00121
00122
00123 TIMESTAMP metrics::postsync(TIMESTAMP t0, TIMESTAMP t1)
00124 {
00125 DATETIME dt;
00126 int valid = gl_localtime(t1, &dt);
00127
00128
00129 if (totals.period!=dt.year)
00130 {
00131 if (totals.Nevents>0)
00132 {
00133 if (report_event_log)
00134 {
00135 fprintf(fp, "\nAnnual reliability metrics report for %d", dt.year-1);
00136 fprintf(fp, "\n=================================================================================================================\n");
00137 fprintf(fp, "SAIDI\tSAIFI\tCAIDI\tCAIFI\tCTAIDI\tCEMI\tCEMSMI\tASIFI\tASIDI\tMAIFI\tMAIFIE\tASAI\n");
00138 }
00139 else if (first_year)
00140 {
00141 fprintf(fp, "\nYear\tSAIDI\tSAIFI\tCAIDI\tCAIFI\tCTAIDI\tCEMI\tCEMSMI\tASIFI\tASIDI\tMAIFI\tMAIFIE\tASAI\n");
00142 first_year=false;
00143 }
00144
00145
00146 if (!report_event_log)
00147 fprintf(fp,"%4d\t", dt.year-1);
00148 fprintf(fp, "%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.6f\n", saidi(), saifi(), caidi(), caifi(), ctaidi(), cemi(), cemsmi(),asifi(), asidi(), maifi(), maifie(),asai()*100);
00149 }
00150
00151
00152 memset(&totals,0,sizeof(totals));
00153 totals.period = dt.year;
00154 }
00155 dt.month=1; dt.day=1; dt.year++; dt.hour=0; dt.minute=0; dt.second=0; dt.microsecond=0;
00156 return valid?-gl_mktime(&dt):TS_NEVER;
00157 }
00158
00159 static void set_values(OBJECT *obj, const char *targets, char *new_values, char *old_values=NULL, int size=0)
00160 {
00161 const char *v=new_values, *p=targets;
00162 char propname[64];
00163 char value[64];
00164 while (p!=NULL && v!=NULL)
00165 {
00166 bool valid = sscanf(p,"%[A-Za-z._0-9]",propname)>0 && sscanf(v,"%[^;]",value)>0;
00167 if (valid) {
00168 gl_verbose("%s %s <- %s", obj->name?obj->name:"(null)",propname,value);
00169
00170
00171 if (old_values)
00172 {
00173 if (strcmp(old_values,"")!=0)
00174 strcat(old_values,";");
00175 if (!gl_get_value_by_name(obj,propname,old_values+strlen(old_values),(int)(size-strlen(old_values))))
00176 {
00177 gl_error("eventgen refers to non-existant property %s in object %s (%s:%d)", propname,obj->name?obj->name:"anonymous",obj->oclass->name,obj->id);
00178 goto GetNext;
00179 }
00180 }
00181
00182
00183 gl_set_value_by_name(obj,propname,value);
00184 GetNext:
00185
00186 p = strchr(p,';');
00187 v = strchr(v,';');
00188 while (p!=NULL && *p==';') p++;
00189 while (v!=NULL && *v==';') v++;
00190 }
00191 else
00192 p = v = NULL;
00193 }
00194 }
00195
00199 OBJECT *metrics::start_event(EVENT *pEvent,
00200 FINDLIST *objectlist,
00201 const char *targets,
00202 char *event_values,
00203 char *old_values,
00204 int size)
00205 {
00206
00207 if (strcmp(old_values,"")==0)
00208 {
00209 totals.Nevents++;
00210 gl_verbose("event %d starts", totals.Nevents);
00211 gl_verbose("%d objects eligible", objectlist->hit_count);
00212
00213
00214 unsigned int nth = (unsigned int)gl_random_uniform(0,objectlist->hit_count);
00215 OBJECT *obj=gl_find_next(objectlist,NULL);
00216 while (nth-->0)
00217 obj=gl_find_next(objectlist,obj);
00218 gl_verbose("object %s (%s:%d) chosen", obj->name?obj->name:"anonymous", obj->oclass->name,obj->id);
00219 set_values(obj,targets,event_values,old_values,size);
00220 gl_verbose("old values = '%s'", old_values);
00221
00222 if (report_event_log)
00223 {
00224
00225 DATETIME dt; char buffer[64]="(na)";
00226 gl_localtime(pEvent->event_time,&dt);
00227 if (totals.Nevents==1)
00228 {
00229 fprintf(fp, "\nEVENT LOG FOR YEAR %d\n", dt.year);
00230 fprintf(fp, "=======================\n");
00231 fprintf(fp,"Event\tDateTime\t\tAffects\tProperties\t\tValues\tT_interrupted\tL_interrupted\tN_interrupted\n");
00232 }
00233 fprintf(fp,"%d\t", totals.Nevents);
00234 gl_strtime(&dt,buffer,sizeof(buffer));
00235 fprintf(fp,"%s\t", buffer);
00236 double t = pEvent->ri/60;
00237 if (obj->name)
00238 fprintf(fp,"%s\t", obj->name);
00239 else
00240 fprintf(fp,"%s:%d\t", obj->oclass->name, obj->id);
00241 fprintf(fp,"%s\t\t", targets);
00242 fprintf(fp,"%s\t", event_values);
00243 fprintf(fp,"%7.2f\t\t", t);
00244 }
00245
00246 FINDLIST *load_meters = gl_findlist_copy(customers);
00247 OBJECT *interrupted_meters=NULL;
00248 complex *kva_in;
00249 complex *pload;
00250 totals.LT=0;
00251 while ((interrupted_meters = gl_find_next(load_meters,interrupted_meters))!=NULL)
00252 {
00253 kva_in = gl_get_complex_by_name(interrupted_meters,"measured_power");
00254 totals.LT += (*kva_in).Mag();
00255 pload = gl_get_complex_by_name(interrupted_meters,"pre_load");
00256 (*pload) = (*kva_in);
00257 }
00258
00259
00260
00261 return obj;
00262 }
00263 return NULL;
00264 }
00265
00267 void metrics::end_event(OBJECT* obj,
00268 const char *targets,
00269 char *old_values)
00270 {
00271
00272
00273
00274 if (strcmp(old_values,"")!=0)
00275 {
00276 gl_verbose("event ends");
00277 gl_verbose("old values = '%s'",old_values);
00278
00279 FINDLIST *candidates = gl_findlist_copy(customers);
00280
00281 FINDLIST *unserved = gl_find_objects(candidates,FT_PROPERTY,"voltage_A",EQ,"0.0",AND,FT_PROPERTY,"voltage_B",EQ,"0.0",AND,FT_PROPERTY,"voltage_C",EQ,"0.0",NULL);
00282
00283 FINDLIST *candidatessecond = gl_findlist_copy(customers);
00284
00285 FINDLIST *unservedtrip = gl_find_objects(candidatessecond,FT_PROPERTY,"voltage_1",EQ,"0.0",AND,FT_PROPERTY,"voltage_2",EQ,"0.0",NULL);
00286
00287 FINDLIST *pmetercounter = gl_findlist_copy(unserved);
00288
00289 FINDLIST *tmetercounter = gl_findlist_copy(unservedtrip);
00290
00291 double S_lost=0;
00292
00293 OBJECT *tripcust_mad=NULL;
00294 while ((tripcust_mad=gl_find_next(unservedtrip,tripcust_mad))!=NULL)
00295 gl_findlist_add(unserved,tripcust_mad);
00296
00297 OBJECT *int_load=NULL;
00298 complex *pload;
00299 while ((int_load = gl_find_next(unserved,int_load))!=NULL)
00300 {
00301 pload = gl_get_complex_by_name(int_load,"pre_load");
00302 S_lost += (*pload).Mag();
00303 }
00304 if (eventlist->ri>300){
00305
00306 totals.Li +=S_lost;
00307
00308 totals.LDI += S_lost * eventlist->ri/60;
00309 }
00310
00311 if (report_event_log){
00312 fprintf(fp,"%9.3f\t", S_lost);
00313 fprintf(fp,"%8d\n", unserved->hit_count);
00314 }
00315
00316 if (eventlist->ri > 300) {
00317
00318 totals.CI += unserved->hit_count;
00319
00320 if (eventlist!=NULL) {
00321 totals.CMI += unserved->hit_count * eventlist->ri/60;
00322 }
00323
00324
00325 OBJECT *sust_pmeter=NULL;
00326 int16 *sust_pcount;
00327 while ((sust_pmeter=gl_find_next(pmetercounter,sust_pmeter))!=NULL){
00328 sust_pcount = gl_get_int16_by_name(sust_pmeter,"sustained_count");
00329 (*sust_pcount)++;
00330 }
00331
00332
00333 OBJECT *sust_tmeter=NULL;
00334 int16 *sust_tcount;
00335 while ((sust_tmeter=gl_find_next(tmetercounter,sust_tmeter))!=NULL){
00336 sust_tcount = gl_get_int16_by_name(sust_tmeter,"sustained_count");
00337 (*sust_tcount)++;
00338 }
00339
00340
00341 } else {
00342
00343 totals.CMIE += unserved->hit_count;
00344
00345 OBJECT *momt_pmeter=NULL;
00346 int16 *momt_pcount;
00347 while ((momt_pmeter=gl_find_next(pmetercounter,momt_pmeter))!=NULL){
00348 momt_pcount = gl_get_int16_by_name(momt_pmeter,"momentary_count");
00349 (*momt_pcount)++;
00350 }
00351
00352
00353 OBJECT *momt_tmeter=NULL;
00354 int16 *momt_tcount;
00355 while ((momt_tmeter=gl_find_next(tmetercounter,momt_tmeter))!=NULL){
00356 momt_tcount = gl_get_int16_by_name(momt_tmeter,"momentary_count");
00357 (*momt_tcount)++;
00358 }
00359
00360 }
00361
00362
00363 OBJECT *totl_tmeter=NULL;
00364 int16 *totl_tcount;
00365 while ((totl_tmeter=gl_find_next(tmetercounter,totl_tmeter))!=NULL){
00366 totl_tcount = gl_get_int16_by_name(totl_tmeter,"total_count");
00367 (*totl_tcount)++;
00368 }
00369
00370
00371 OBJECT *totl_pmeter=NULL;
00372 int16 *totl_pcount;
00373 while ((totl_pmeter=gl_find_next(pmetercounter,totl_pmeter))!=NULL){
00374 totl_pcount = gl_get_int16_by_name(totl_pmeter,"total_count");
00375 (*totl_pcount)++;
00376 }
00377
00378 candidates = gl_findlist_copy(customers);
00379 FINDLIST *sust_intr_cust = gl_find_objects(candidates,FT_PROPERTY,"sustained_count",GT,"0.0",NULL);
00380
00381 totals.CN = sust_intr_cust->hit_count;
00382
00383
00384 candidates = gl_findlist_copy(customers);
00385 OBJECT *k_sust_cust=NULL;
00386 int16 *s_count;
00387 int16 *sust_flag;
00388 while ((k_sust_cust=gl_find_next(candidates,k_sust_cust))!=NULL){
00389 s_count = gl_get_int16_by_name(k_sust_cust,"sustained_count");
00390 if ((*s_count)>n){
00391 sust_flag = gl_get_int16_by_name(k_sust_cust,"s_flag");
00392 (*sust_flag)=1;
00393 }
00394 }
00395
00396 candidates = gl_findlist_copy(customers);
00397 OBJECT *k_totl_cust=NULL;
00398 int16 *t_count;
00399 int16 *totl_flag;
00400 while ((k_totl_cust=gl_find_next(candidates,k_totl_cust))!=NULL){
00401 t_count = gl_get_int16_by_name(k_totl_cust,"total_count");
00402 if ((*t_count)>n)
00403 {
00404 totl_flag = gl_get_int16_by_name(k_totl_cust,"t_flag");
00405 (*totl_flag)=1;
00406 }
00407 }
00408
00409
00410 candidates = gl_findlist_copy(customers);
00411 FINDLIST *cemi_cust = gl_find_objects(candidates,FT_PROPERTY,"s_flag",EQ,"1",NULL);
00412 candidates = gl_findlist_copy(customers);
00413 FINDLIST *cemsmi_cust = gl_find_objects(candidates,FT_PROPERTY,"t_flag",EQ,"1",NULL);
00414 totals.CNK=cemi_cust->hit_count;
00415 totals.CNT=cemsmi_cust->hit_count;
00416
00417
00418 double relay_ops = 0;
00419 if ((gl_object_isa(obj,"relay","powerflow")))
00420 {
00421 if (eventlist->ri<=300)
00422 {
00423 int16 *r_operations;
00424 r_operations = gl_get_int16_by_name(obj,"recloser_tries");
00425 relay_ops = (*r_operations);
00426 totals.IMi += relay_ops * unserved->hit_count;
00427 }
00428 }
00429 if (!(gl_object_isa(obj,"relay","powerflow")))
00430 {
00431 relay_ops = 0;
00432 totals.IMi += relay_ops * unserved->hit_count;
00433 }
00434
00435 SAIFI = saifi();
00436 SAIDI = saidi();
00437 CAIFI = caifi();
00438 CAIDI = caidi();
00439 CTAIDI = ctaidi();
00440 ASAI = asai();
00441 MAIFI = maifi();
00442 MAIFIE = maifie();
00443 CEMI = cemi();
00444 CEMSMI = cemsmi();
00445 ASIDI = asidi();
00446 ASIFI = asifi();
00447
00448
00449 gl_free(candidates);
00450 gl_free(candidatessecond);
00451 fflush(fp);
00452
00453
00454 set_values(obj,targets,old_values);
00455 strcpy(old_values,"");
00456 }
00457 }
00458
00459
00460 double metrics::asai(void)
00461 {
00462 return customers->hit_count>0 ? (customers->hit_count*8760 - (totals.CMI/60))/(customers->hit_count*8760) : 1.0;
00463 }
00464 double metrics::ctaidi(void)
00465 {
00466 return totals.CN>0 ? totals.CMI/totals.CN : 0;
00467 }
00468 double metrics::saidi(void)
00469 {
00470 return customers->hit_count>0 ? totals.CMI / customers->hit_count : 0;
00471 }
00472 double metrics::caifi(void)
00473 {
00474 return totals.CN>0 ? totals.CI/totals.CN : 0;
00475 }
00476 double metrics::saifi(void)
00477 {
00478 return customers->hit_count>0 ? totals.CI/customers->hit_count:0;
00479 }
00480 double metrics::caidi(void)
00481 {
00482 return totals.CI>0 ? saidi()/saifi() : 0;
00483 }
00484 double metrics::maifie(void)
00485 {
00486 return totals.CMIE>0 ? totals.CMIE/customers->hit_count : 0;
00487 }
00488 double metrics::cemi(void)
00489 {
00490 return totals.CNK>0 ? (double)totals.CNK/customers->hit_count : 0;
00491 }
00492 double metrics::cemsmi(void)
00493 {
00494 return totals.CNT>0 ? (double)totals.CNT/customers->hit_count : 0;
00495 }
00496 double metrics::asifi(void)
00497 {
00498 return totals.Li>0 ? totals.Li/totals.LT : 0;
00499 }
00500 double metrics::asidi(void)
00501 {
00502 return totals.LDI>0 ? totals.LDI/totals.LT : 0;
00503 }
00504 double metrics::maifi(void)
00505 {
00506 return totals.IMi>0 ? totals.IMi/customers->hit_count : 0;
00507 }
00509
00511
00512 EXPORT int create_metrics(OBJECT **obj, OBJECT *parent)
00513 {
00514 try
00515 {
00516 *obj = gl_create_object(metrics::oclass);
00517 if (*obj!=NULL)
00518 {
00519 metrics *my = OBJECTDATA(*obj,metrics);
00520 gl_set_parent(*obj,parent);
00521 return my->create();
00522 }
00523 }
00524 catch (char *msg)
00525 {
00526 gl_error("create_metrics: %s", msg);
00527 }
00528 return 1;
00529 }
00530
00531 EXPORT int init_metrics(OBJECT *obj, OBJECT *parent)
00532 {
00533 try
00534 {
00535 if (obj!=NULL)
00536 return OBJECTDATA(obj,metrics)->init(parent);
00537 }
00538 catch (char *msg)
00539 {
00540 gl_error("init_metrics(obj=%d;%s): %s", obj->id, obj->name?obj->name:"unnamed", msg);
00541 }
00542 return 1;
00543 }
00544
00545 EXPORT TIMESTAMP sync_metrics(OBJECT *obj, TIMESTAMP t1, PASSCONFIG pass)
00546 {
00547 TIMESTAMP t2 = TS_NEVER;
00548 metrics *my = OBJECTDATA(obj,metrics);
00549 try
00550 {
00551 switch (pass) {
00552 case PC_POSTTOPDOWN:
00553 t2 = my->postsync(obj->clock,t1);
00554 break;
00555 case PC_PRETOPDOWN:
00556 case PC_BOTTOMUP:
00557 default:
00558 GL_THROW("invalid pass request (%d)", pass);
00559 break;
00560 }
00561 if (pass==clockpass)
00562 obj->clock = t1;
00563 }
00564 catch (char *msg)
00565 {
00566 gl_error("sync_metrics(obj=%d;%s): %s", obj->id, obj->name?obj->name:"unnamed", msg);
00567 }
00568 return t2;
00569 }