powerflow/meter.cpp

00001 
00022 #include <stdlib.h>
00023 #include <stdio.h>
00024 #include <errno.h>
00025 #include <math.h>
00026 
00027 #include "meter.h"
00028 #include "timestamp.h"
00029 
00030 // specify the sync calls needed
00031 #define SYNCS (PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN)
00032 
00033 // useful macros
00034 #define TO_HOURS(t) (((double)t) / (3600 * TS_SECOND))
00035 
00037 // meter CLASS FUNCTIONS
00039 // class management data
00040 CLASS* meter::oclass = NULL;
00041 CLASS* meter::pclass = NULL;
00042 meter *meter::defaults = NULL;
00043 
00044 // the constructor registers the class and properties and sets the defaults
00045 meter::meter(MODULE *mod) : node(mod)
00046 {
00047     // first time init
00048     if (oclass==NULL)
00049     {
00050         // link to parent class (used by isa)
00051         pclass = node::oclass;
00052 
00053         // register the class definition
00054         oclass = gl_register_class(mod,"meter",SYNCS);
00055         if (oclass==NULL)
00056             GL_THROW("unable to register object class implemented by %s",__FILE__);
00057 
00058         // publish the class properties
00059         if (gl_publish_variable(oclass,
00060             PT_enumeration,"type",PADDR(type),
00061                 PT_KEYWORD,"UNKNOWN",UNKNOWN,
00062                 PT_KEYWORD,"SINGLEPHASE",SINGLEPHASE,
00063                 PT_KEYWORD,"POLYPHASE",POLYPHASE,
00064             PT_double, "energy[kWh]", PADDR(energy),
00065             PT_double, "power[kW]", PADDR(power),
00066             PT_double, "demand[kW]", PADDR(demand),
00067             PT_complex, "line12_voltage[V]", PADDR(phaseAtoB_V),
00068             PT_complex, "line23_voltage[V]", PADDR(phaseBtoC_V),
00069             PT_complex, "line31_voltage[V]", PADDR(phaseCtoA_V),
00070             PT_complex, "line1_voltage[V]", PADDR(phaseA_V),
00071             PT_complex, "line2_voltage[V]", PADDR(phaseB_V),
00072             PT_complex, "line3_voltage[V]", PADDR(phaseC_V),
00073             PT_complex, "line1_current[A]", PADDR(phaseA_I),
00074             PT_complex, "line2_current[A]", PADDR(phaseB_I),
00075             PT_complex, "line3_current[A]", PADDR(phaseC_I),
00076             PT_set, "phases", PADDR(phases),
00077                 PT_KEYWORD, "A",PHASE_A,
00078                 PT_KEYWORD, "B",PHASE_B,
00079                 PT_KEYWORD, "C",PHASE_C,
00080                 PT_KEYWORD, "N",PHASE_N,
00081                 PT_KEYWORD, "S",PHASE_S,
00082             PT_enumeration, "status", PADDR(status),
00083                 PT_KEYWORD, "NOMINAL", NOMINAL,
00084                 PT_KEYWORD, "UNDERVOLT", UNDERVOLT,
00085                 PT_KEYWORD, "OVERVOLT", OVERVOLT,
00086             PT_double, "nominal_voltage[V]", PADDR(nominal_voltage),
00087             NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
00088 
00089         // setup the default values
00090         memset(this,0,sizeof(meter));
00091         defaults = this;
00092         nominal_voltage = 240;
00093     }
00094 }
00095 
00096 int meter::isa(char *classname)
00097 {
00098     return strcmp(classname,"meter")==0 || node::isa(classname);
00099 }
00100 
00101 // Create a distribution meter from the defaults template, return 1 on success
00102 int meter::create()
00103 {
00104     int result = node::create();
00105     memcpy(this,defaults,sizeof(meter));
00106     return result;
00107 }
00108 
00109 // Initialize a distribution meter, return 1 on success
00110 int meter::init(OBJECT *parent)
00111 {
00112     if (type==UNKNOWN)
00113     {
00114         OBJECT *obj = OBJECTHDR(this);
00115         throw("meter type is not specified");
00116     }
00117     return node::init(parent);
00118 }
00119 
00120 // Synchronize a distribution meter
00121 TIMESTAMP meter::presync(TIMESTAMP t0, TIMESTAMP t1)
00122 {
00123     // compute demand peak
00124     if (power>demand) demand=power;
00125     if (t0>0)
00126         energy += power * TO_HOURS(t1 - t0);
00127     if (type==SINGLEPHASE)
00128     {
00129             phaseAtoB_V = phaseA_V - (-phaseB_V);
00130             phaseBtoC_V = -phaseB_V - (phaseC_V);
00131             phaseCtoA_V = phaseC_V - (phaseA_V);
00132 
00133     }
00134 
00135     return node::presync(t0);
00136 
00137 
00138     /*
00139     complex true_power;
00140     // compute energy consumption after clock starts
00141     if (t0>0)
00142         energy += power * TO_HOURS(t1 - t0);
00143         if (type==1){
00144             true_power = phaseA_V * phaseA_I -phaseB_V * phaseB_I +phaseC_V * phaseC_I;
00145             power = true_power.Re()/1000;
00146         }
00147         else if (type==2){
00148             true_power = phaseA_V * phaseA_I_in +phaseB_V * phaseB_I_in +phaseC_V * phaseC_I_in;
00149             power = true_power.Mag()/1000;
00150         }
00151         else{ //if (type ==0 )//if type not known, assume it measure nothing.
00152             //true_power = phaseA_V * phaseA_I -phaseB_V * phaseB_I +phaseC_V * phaseC_I;
00153             true_power = 0.0;
00154             power = true_power.Mag /1000;
00155         }
00156 
00157     return TS_NEVER;
00158     */
00159 }
00160 
00161 TIMESTAMP meter::sync(TIMESTAMP t0)
00162 {
00163     complex true_power;
00164     /* add the load to the node injection */
00165     phaseA_I_in += phaseA_I;
00166     phaseB_I_in += phaseB_I;
00167     phaseC_I_in += phaseC_I;
00168 
00169     /* update the node */
00170     TIMESTAMP t1 = node::sync(t0);
00171 
00172     /* check the voltage status for loads */
00173     double VApu = phaseA_V.Mag()/nominal_voltage;
00174     double VBpu = phaseB_V.Mag()/nominal_voltage;
00175     double VCpu = phaseC_V.Mag()/nominal_voltage;
00176     if (type==SINGLEPHASE)
00177     {
00178             /* compute phase-to-phase voltages */
00179             phaseAtoB_V = phaseA_V - (-phaseB_V);
00180             phaseBtoC_V = -phaseB_V - (phaseC_V);
00181             phaseCtoA_V = phaseC_V - (phaseA_V);
00182 
00183         if (VApu<0.4 || VBpu<0.4 || VApu+VBpu<0.8)
00184             status=UNDERVOLT;
00185         else if (VApu>0.6 || VBpu>0.6 || VApu+VBpu>1.2)
00186             status=OVERVOLT;
00187         else
00188             status=NOMINAL;
00189 
00190     }
00191     else if (type==POLYPHASE)
00192     {
00193 
00194         /*      if (type==2){
00195             complex true_power = phaseA_V * phaseA_I_in +phaseB_V * phaseB_I_in +phaseC_V * phaseC_I_in;
00196             power = true_power.Mag()/1000;
00197         }
00198 */
00199         if (VApu<0.8 || VBpu<0.8 || VCpu<0.8)
00200             status=UNDERVOLT;
00201         else if (VApu>1.2 || VBpu>1.2 || VCpu>1.2)
00202             status=OVERVOLT;
00203         else
00204             status=NOMINAL;
00205 
00206     }
00207 /*      if (type==1){
00208             true_power = phaseA_V * phaseA_I -phaseB_V * phaseB_I +phaseC_V * phaseC_I;
00209             power = true_power.Re()/1000;
00210         }
00211         else if (type==2){
00212             true_power = phaseA_V * phaseA_I_in +phaseB_V * phaseB_I_in +phaseC_V * phaseC_I_in;
00213             power = true_power.Mag()/1000;
00214         }
00215         else{ //if (type ==0 )//if type not known, assume it measure nothing.
00216             //true_power = phaseA_V * phaseA_I -phaseB_V * phaseB_I +phaseC_V * phaseC_I;
00217             true_power = 0.0;
00218             power = true_power.Mag /1000;
00219         }
00220 */
00221     return t1;
00222 }
00223 
00224 TIMESTAMP meter::postsync(TIMESTAMP t0)
00225 {
00226 /*  // compute demand peak
00227     if (power>demand) demand=power;
00228 
00229     return node::postsync(t0);
00230 */
00231     complex true_power;
00232     // compute energy consumption after clock starts
00233 //  if (t0>0)
00234 //      energy += power * TO_HOURS(t1 - t0);
00235         if (type==1){
00236             true_power = phaseA_V * phaseA_I_in -phaseB_V * phaseB_I_in +phaseC_V * phaseC_I_in;
00237             power = true_power.Re()/1000;
00238                 if (type=SINGLEPHASE)
00239             {
00240             phaseAtoB_V = phaseA_V - (-phaseB_V);
00241             phaseBtoC_V = -phaseB_V - (phaseC_V);
00242             phaseCtoA_V = phaseC_V - (phaseA_V);
00243             }
00244         }
00245         else if (type==2){
00246             true_power = phaseA_V * phaseA_I_in +phaseB_V * phaseB_I_in +phaseC_V * phaseC_I_in;
00247             power = true_power.Mag()/1000;
00248         }
00249         else{ //if (type ==0 )//if type not known, assume it measure nothing.
00250             //true_power = phaseA_V * phaseA_I -phaseB_V * phaseB_I +phaseC_V * phaseC_I;
00251             true_power = 0.0;
00252             power = true_power.Mag()/1000;
00253         }
00254 
00255     return TS_NEVER;
00256     }
00257 
00259 // IMPLEMENTATION OF CORE LINKAGE
00261 
00262 EXPORT int isa_meter(OBJECT *obj, char *classname)
00263 {
00264     return OBJECTDATA(obj,meter)->isa(classname);
00265 }
00266 
00267 EXPORT int create_meter(OBJECT **obj, OBJECT *parent)
00268 {
00269     *obj = gl_create_object(meter::oclass,sizeof(meter));
00270     if (*obj!=NULL)
00271     {
00272         meter *my = OBJECTDATA(*obj,meter);
00273         gl_set_parent(*obj,parent);
00274         my->create();
00275         return 1;
00276     }
00277     return 0;
00278 }
00279 
00280 EXPORT int init_meter(OBJECT *obj)
00281 {
00282     meter *my = OBJECTDATA(obj,meter);
00283     try {
00284         return my->init(obj->parent);
00285     }
00286     catch (char *msg)
00287     {
00288         GL_THROW("%s (meter:%d): %s", my->get_name(), my->get_id(), msg);
00289         return 0; 
00290     }
00291 }
00292 
00293 EXPORT TIMESTAMP sync_meter(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
00294 {
00295     meter *pObj = OBJECTDATA(obj,meter);
00296     try {
00297         TIMESTAMP t1 = TS_NEVER;
00298         if (pass==PC_PRETOPDOWN)
00299         {
00300             t1 = pObj->presync(obj->clock,t0);
00301             obj->clock = t0;
00302             return t1;
00303         }
00304         else if (pass==PC_BOTTOMUP)
00305             return pObj->sync(t0);
00306         else if (pass=PC_POSTTOPDOWN)
00307             return pObj->postsync(t0);
00308         else
00309             throw "invalid pass request";
00310     } catch (const char *error) {
00311         GL_THROW("%s (meter:%d): %s", pObj->get_name(), pObj->get_id(), error);
00312         return 0; 
00313     } catch (...) {
00314         GL_THROW("%s (meter:%d): %s", pObj->get_name(), pObj->get_id(), "unknown exception");
00315         return 0;
00316     }
00317 }
00318 

GridLAB-DTM Version 1.0
An open-source project initiated by the US Department of Energy