00001
00076 #include <stdlib.h>
00077 #include <stdio.h>
00078 #include <errno.h>
00079 #include <math.h>
00080 #include <ctype.h>
00081
00082 #include <gridlabd.h>
00083
00084 #include "house_a.h"
00085 #include "evcharger.h"
00086
00090 typedef struct s_evdemandprofilelist {
00091 EVDEMAND *data;
00092 struct s_evdemandprofilelist *next;
00093 } EVPROFILEITEM;
00094
00095 EVPROFILEITEM *first_demand_profile = NULL;
00096
00099 EVDEMAND *find_demand_profile(char *name)
00100 {
00101 EVPROFILEITEM *item = first_demand_profile;
00102 while (item!=NULL)
00103 {
00104 if (strcmp(name,item->data->name)==0)
00105 break;
00106 else
00107 item = item->next;
00108 }
00109 return item?item->data:NULL;
00110 }
00113 EVPROFILEITEM *add_demand_profile(EVDEMAND *data)
00114 {
00115 EVPROFILEITEM *item = new EVPROFILEITEM;
00116 if (item==NULL)
00117 throw "evcharter.cpp: add_demand_profile() memory allocation failed";
00118 item->data = data;
00119 item->next = first_demand_profile;
00120 return first_demand_profile = item;
00121 }
00122 EVDEMAND *load_demand_profile(char *filename)
00123 {
00124 char *filepath = gl_findfile(filename,NULL,FF_READ);
00125 if (filepath==NULL)
00126 {
00127 gl_error("searching for demand profile file '%s'",filename);
00128 throw "unable to find demand profile";
00129 }
00130 EVDEMAND *data = new EVDEMAND;
00131 if (data==NULL)
00132 throw "unable to allocate memory for new demand profile";
00133 memset(data,0,sizeof(EVDEMAND));
00134
00135 int daytype = WEEKDAY;
00136 int linenum=0;
00137 int hour=0;
00138
00139 strcpy(data->name,filepath);
00140 FILE *fp = fopen(filepath,"r");
00141 if (fp==NULL)
00142 {
00143 gl_error("reading file %s line %d",filename,linenum);
00144 throw "unable to open demand profile";
00145 }
00146 char line[1024];
00147 double *hdr[8]; memset(hdr,0,sizeof(hdr));
00148 while (!feof(fp) && fgets(line,sizeof(line),fp)!=NULL)
00149 {
00150 linenum++;
00151
00152
00153 if (isspace(line[0]))
00154 continue;
00155 if (line[0]=='#')
00156 continue;
00157 if (line[0]=='[')
00158 {
00159 char blockname[8];
00160 if (sscanf(line,"[%7s]",blockname)==1)
00161 {
00162 if (strcmp(blockname,"WEEKDAY")==0)
00163 daytype = WEEKDAY;
00164 else if (strcmp(blockname,"WEEKEND")==0)
00165 daytype = WEEKEND;
00166 else
00167 {
00168 gl_output("%s(%d): '%s' is an invalid daytype name",filename,linenum,blockname);
00169 throw "invalid block name";
00170 }
00171 data->n_daytypes++;
00172 }
00173 else
00174 {
00175 gl_output("%s(%d): daytype name syntax is not recognized",filename,linenum,blockname);
00176 throw "unable to read block name";
00177 }
00178 }
00179 else if (isalpha(line[0]))
00180 {
00181 char dir[4], trip[5];
00182 int event;
00183 int nhdr = 0;
00184 int offset=0;
00185 while (sscanf(line+offset,"%3s%4s",dir,trip)==2)
00186 {
00187 if (nhdr>sizeof(hdr)/sizeof(hdr[0]))
00188 {
00189 gl_output("%s(%d): too many heading items listed (max is %d)",filename,linenum,sizeof(hdr)/sizeof(hdr[0]));
00190 throw "too many heading items in demand profile";
00191 }
00192 if (strcmp(dir,"ARR")==0)
00193 event = ARRIVE;
00194 else if (strcmp(dir,"DEP")==0)
00195 event = DEPART;
00196 else
00197 {
00198 gl_output("%s(%d): '%s' is not an appropriate event direction (e.g., ARR, DEP)",filename,linenum,dir);
00199 throw "unrecognized event direction in demand profile";
00200 }
00201 if (strcmp(trip,"HOME")==0)
00202 hdr[nhdr++] = data->home[daytype][event];
00203 else if (strcmp(trip,"WORK")==0)
00204 hdr[nhdr++] = data->work[daytype][event];
00205 else if (strcmp(trip,"SHRT")==0)
00206 hdr[nhdr++] = data->shorttrip[daytype][event];
00207 else if (strcmp(trip,"LONG")==0)
00208 hdr[nhdr++] = data->longtrip[daytype][event];
00209 else
00210 {
00211 gl_output("%s(%d): '%s' is not an appropriate trip location (e.g., HOME, WORK)",filename,linenum,dir);
00212 throw "unrecognized trip location in demand profile";
00213 }
00214 offset += 8;
00215 }
00216 }
00217 else if (isdigit(line[0]))
00218 {
00219 double prob;
00220 int offset = 0;
00221 int col = 0;
00222 while (sscanf(line+offset,"%lf",&prob)==1)
00223 {
00224 hdr[col++][hour] = prob;
00225 while (line[offset]!='\0' && line[offset++]!=',') {}
00226 }
00227 hour++;
00228 }
00229 else
00230 {
00231 }
00232 }
00233 if (ferror(fp))
00234 {
00235 gl_error("%s(%d): %s",filename,linenum,strerror(errno));
00236 throw "unable to read demand profile";
00237 }
00238 fclose(fp);
00239 return data;
00240 }
00241 EVDEMAND *get_demand_profile(char *name)
00242 {
00243 EVDEMAND *profile = find_demand_profile(name);
00244 if (profile==NULL)
00245 {
00246 profile = load_demand_profile(name);
00247 if (profile!=NULL)
00248 add_demand_profile(profile);
00249 }
00250 return profile;
00251 }
00252
00254
00256 CLASS* evcharger::oclass = NULL;
00257 CLASS* evcharger::pclass = NULL;
00258 evcharger *evcharger::defaults = NULL;
00259
00260 evcharger::evcharger(MODULE *module) : residential_enduse(module)
00261 {
00262
00263 if (oclass==NULL)
00264 {
00265
00266 oclass = gl_register_class(module,"evcharger",sizeof(evcharger),PC_BOTTOMUP);
00267 if (oclass==NULL)
00268 GL_THROW("unable to register object class implemented by %s",__FILE__);
00269
00270
00271 if (gl_publish_variable(oclass,
00272 PT_INHERIT, "residential_enduse",
00273 PT_enumeration,"charger_type",PADDR(charger_type),
00274 PT_KEYWORD,"LOW",CT_LOW,
00275 PT_KEYWORD,"MEDIUM",CT_MEDIUM,
00276 PT_KEYWORD,"HIGH",CT_HIGH,
00277 PT_enumeration,"vehicle_type",PADDR(vehicle_type),
00278 PT_KEYWORD,"ELECTRIC",VT_ELECTRIC,
00279 PT_KEYWORD,"HYBRID",VT_HYBRID,
00280 PT_enumeration,"state",PADDR(vehicle_state),
00281 PT_KEYWORD,"UNKNOWN",VS_UNKNOWN,
00282 PT_KEYWORD,"HOME",VS_HOME,
00283 PT_KEYWORD,"WORK",VS_WORK,
00284
00285
00286
00287 PT_double,"p_go_home[unit/h]",PADDR(demand.home),
00288 PT_double,"p_go_work[unit/h]",PADDR(demand.work),
00289
00290
00291
00292 PT_double,"work_dist[mile]",PADDR(distance.work),
00293
00294
00295
00296 PT_double,"capacity[kWh]",PADDR(capacity),
00297 PT_double,"charge[unit]",PADDR(charge),
00298 PT_bool,"charge_at_work",PADDR(charge_at_work),
00299 PT_double,"charge_throttle[unit]", PADDR(charge_throttle),
00300 PT_char1024,"demand_profile", PADDR(demand_profile),
00301 NULL)<1)
00302 GL_THROW("unable to publish properties in %s",__FILE__);
00303 }
00304 }
00305
00306 evcharger::~evcharger()
00307 {
00308 }
00309
00310 int evcharger::create()
00311 {
00312 int res = residential_enduse::create();
00313
00314
00315 load.name = oclass->name;
00316 load.power = load.admittance = load.current = load.total = complex(0,0,J);
00317 vehicle_type = VT_HYBRID;
00318
00319 return res;
00320 }
00321
00322
00323 static double fuse[] = {15,35,70};
00324 static double amps[] = {12,28,55};
00325 static bool hiV[] = {false,true,true};
00326
00327 int evcharger::init(OBJECT *parent)
00328 {
00329 static double sizes[] = {20,30,30,40,40,40,50,50,50,50,60,60,60,70,70,80};
00330 if (capacity==0) capacity = gl_random_sampled(sizeof(sizes)/sizeof(sizes[0]),sizes);
00331 if (power_factor==0) power_factor = 0.95;
00332 if (charge==0) charge = gl_random_uniform(0.25,1.0);
00333 if (charge_throttle==0) charge_throttle = 1.0;
00334 if (mileage==0) mileage = gl_random_uniform(0.8,1.2);
00335 if (distance.work==0) distance.work = gl_random_lognormal(3,1);
00336
00337
00338
00339
00340 OBJECT *hdr = OBJECTHDR(this);
00341 hdr->flags |= OF_SKIPSAFE;
00342
00343
00344 if (strcmp(demand_profile,"")!=0)
00345 pDemand = get_demand_profile(demand_profile);
00346
00347 update_state();
00348
00349 return residential_enduse::init(parent);
00350 }
00351
00352 int evcharger::isa(char *classname)
00353 {
00354 return (strcmp(classname,"evcharger")==0 || residential_enduse::isa(classname));
00355 }
00356
00357 double evcharger::update_state(double dt )
00358 {
00359 OBJECT *obj = OBJECTHDR(this);
00360 if (obj->clock>TS_ZERO && dt>0)
00361 {
00362 DATETIME now;
00363 if (!gl_localtime(obj->clock,&now))
00364 throw "unable to obtain current time";
00365
00366
00367
00368
00369
00370 int hour = now.hour;
00371 int daytype = 0;
00372 if(pDemand != NULL){
00373 pDemand->n_daytypes>0 ? (now.weekday>0&&now.weekday<6) : 0;
00374
00375 demand.home = pDemand->home[daytype][DEPART][hour];
00376 demand.work = pDemand->home[daytype][ARRIVE][hour];
00377 }
00378
00379
00380 switch (vehicle_state) {
00381 case VS_HOME:
00382 if (gl_random_bernoulli(demand.home*dt/3600))
00383 {
00384 gl_debug("%s (%s:%d) leaves for work with %.0f%% charge",obj->name?obj->name:"anonymous",obj->oclass->name,obj->id,charge*100);
00385 vehicle_state = VS_WORK;
00386 }
00387 break;
00388 case VS_WORK:
00389 if (gl_random_bernoulli(demand.work*dt/3600))
00390 {
00391 vehicle_state = VS_HOME;
00392 if (charge_at_work)
00393 charge -= (distance.work / mileage)/capacity;
00394 else
00395 charge -= 2 * (distance.work / mileage)/capacity;
00396 if (charge<0.25 && vehicle_type==VT_HYBRID)
00397 charge = 0.25;
00398 if (charge<=0)
00399 gl_warning("%s (%s:%d) vehicle has run out of charge while away from home", obj->name?obj->name:"anonymous",obj->oclass->name,obj->id);
00400 else
00401 gl_debug("%s (%s:%d) returns from work with %.0f%% charge",obj->name?obj->name:"anonymous",obj->oclass->name,obj->id, charge*100);
00402 }
00403 break;
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413 default:
00414 throw "invalid state";
00415
00416
00417
00418
00419
00420
00421 break;
00422 }
00423 }
00424
00425
00426 switch (vehicle_state) {
00427 case VS_HOME:
00428 if (charge<1.0 && obj->clock>0)
00429 {
00430
00431 double charge_kw;
00432 switch (charger_type) {
00433 case CT_LOW:
00434 case CT_MEDIUM:
00435 case CT_HIGH:
00436 charge_kw = amps[(int)charger_type] * pCircuit->pV->Mag() * charge_throttle /1000;
00437 break;
00438 default:
00439 throw "invalid charger_type";
00440
00441
00442
00443
00444
00445
00446
00447 break;
00448 }
00449
00450
00451 double d_charge_kWh = charge_kw*dt/3600;
00452
00453
00454 charge += d_charge_kWh/capacity;
00455 if (charge>1.0)
00456 {
00457 gl_warning("%s (%s:%d) overcharge by %.0f%% truncated",obj->name?obj->name:"anonymous",obj->oclass->name,obj->id, (charge-1)*100);
00458
00459 charge = 1.0;
00460 }
00461 else if (charge<0.0)
00462 {
00463 gl_warning("%s (%s:%d) 100%% discharged", obj->name?obj->name:"anonymous",obj->oclass->name,obj->id);
00464
00465 charge = 0.0;
00466 }
00467 else if (charge>0.999)
00468 {
00469 gl_debug("%s (%s:%d) charge complete",obj->name?obj->name:"anonymous",obj->oclass->name,obj->id);
00470 charge = 1.0;
00471 }
00472
00473
00474
00475 if (charge_kw>0)
00476 {
00477 dt = (capacity*(1-charge))/charge_kw*3600;
00478 if (dt<1)
00479 {
00480 charge = 1.0;
00481 dt = -1;
00482 }
00483 }
00484
00485 else
00486 dt = -1;
00487
00488 load.power.SetPowerFactor(charge_kw,power_factor,J);
00489 }
00490 else
00491 {
00492 load.power = complex(0,0,J);
00493 dt = -1;
00494 }
00495 break;
00496 case VS_WORK:
00497 load.power = complex(0,0,J);
00498 dt = -1;
00499 break;
00500
00501
00502
00503
00504
00505
00506
00507 default:
00508 throw "invalid state";
00509
00510
00511
00512
00513
00514
00515
00516 break;
00517 }
00518
00519 if (!isfinite(charge))
00520 throw "charge state error (not a number)";
00521
00522
00523
00524
00525
00526 load.total = load.power;
00527 load.heatgain = load.total.Mag() * heat_fraction;
00528
00529
00530 return dt<3600&&dt>=0?dt:3600;
00531 }
00532
00533 TIMESTAMP evcharger::sync(TIMESTAMP t0, TIMESTAMP t1)
00534 {
00535 OBJECT *obj = OBJECTHDR(this);
00536
00537 if (t0>TS_ZERO && t1>t0)
00538 load.energy += (load.total * gl_tohours(t1-t0));
00539 double dt = update_state(gl_toseconds(t1-t0));
00540 if (dt==0)
00541 gl_warning("%s (%s:%d) didn't advance the clock",obj->name?obj->name:"anonymous",obj->oclass->name,obj->id);
00542
00543
00544 return dt<0 ? TS_NEVER : (TIMESTAMP)(t1+dt*TS_SECOND);
00545 }
00546
00547 void evcharger::load_demand_profile(void)
00548 {
00549
00550 }
00551
00553
00555
00556 EXPORT int create_evcharger(OBJECT **obj, OBJECT *parent)
00557 {
00558 *obj = gl_create_object(evcharger::oclass);
00559 if (*obj!=NULL)
00560 {
00561 evcharger *my = OBJECTDATA(*obj,evcharger);;
00562 gl_set_parent(*obj,parent);
00563 my->create();
00564 return 1;
00565 }
00566 return 0;
00567 }
00568
00569 EXPORT int init_evcharger(OBJECT *obj)
00570 {
00571 try {
00572 evcharger *my = OBJECTDATA(obj,evcharger);
00573 return my->init(obj->parent);
00574 }
00575 catch (char *msg)
00576 {
00577 gl_error("%s (%s:%d): %s", obj->name?obj->name:"anonymous object",obj->oclass->name,obj->id,msg);
00578 return 0;
00579 }
00580 }
00581
00582 EXPORT int isa_evcharger(OBJECT *obj, char *classname)
00583 {
00584 if(obj != 0 && classname != 0){
00585 return OBJECTDATA(obj,evcharger)->isa(classname);
00586 } else {
00587 return 0;
00588 }
00589 }
00590
00591 EXPORT TIMESTAMP sync_evcharger(OBJECT *obj, TIMESTAMP t0)
00592 {
00593 try {
00594 evcharger *my = OBJECTDATA(obj, evcharger);
00595 TIMESTAMP t1 = my->sync(obj->clock, t0);
00596 obj->clock = t0;
00597 return t1;
00598 }
00599 catch (char *msg)
00600 {
00601 gl_error("%s (%s:%d): %s", obj->name?obj->name:"anonymous object",obj->oclass->name,obj->id,msg);
00602 return TS_INVALID;
00603 }
00604 }
00605