network/node.cpp

00001 
00054 // #define HYBRID // enables hybrid solver that can be used to perform state estimation (DPC 10/07: this is not fully functional)
00055 
00056 #include <stdlib.h>
00057 #include <stdio.h>
00058 #include <errno.h>
00059 #include <math.h>
00060 #include "network.h"
00061 
00062 
00063 
00065 // node CLASS FUNCTIONS
00067 
00068 CLASS* node::oclass = NULL;
00069 CLASS* node::pclass = NULL;
00070 node *node::defaults = NULL;
00071 //CLASS *node_class = NULL;
00072 
00073 node::node(MODULE *mod) 
00074 {
00075 
00076     // first time init
00077     if (oclass==NULL)
00078     {
00079         // register the class definition
00080         node_class = oclass = gl_register_class(mod,"node",PC_BOTTOMUP);
00081         if (oclass==NULL)
00082             GL_THROW("unable to register object class implemented by %s",__FILE__);
00083 
00084         // publish the class properties
00085         if (gl_publish_variable(oclass,
00086             PT_complex, "V", PADDR(V),
00087             PT_complex, "S", PADDR(S),
00088             PT_double, "G", PADDR(G),
00089             PT_double, "B", PADDR(B),
00090             PT_double, "Qmax_MVAR", PADDR(Qmax_MVAR),
00091             PT_double, "Qmin_MVAR", PADDR(Qmin_MVAR),
00092             PT_enumeration,"type",PADDR(type),
00093                 PT_KEYWORD,"PQ",PQ,
00094                 PT_KEYWORD,"PQV",PQV,
00095                 PT_KEYWORD,"PV",PV,
00096                 PT_KEYWORD,"SWING",SWING,
00097             PT_int16, "bus_id", PADDR(bus_id),
00098             PT_char32, "busname", PADDR(name),
00099             PT_int16, "flow_area_num", PADDR(flow_area_num),
00100             PT_double, "base_kV", PADDR(base_kV),
00101             PT_complex, "Vobs", PADDR(Vobs),
00102             PT_double, "Vstdev", PADDR(Vstdev),
00103             NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
00104 
00105         // setup the default values
00106         defaults = this;
00107         type = PQ;
00108         V = complex(1,0,A);
00109         S = complex(0,0,J);
00110         G = 0;
00111         B = 0;
00112         Qmax_MVAR = 0.0;
00113         Qmin_MVAR = 0.0;
00114         Ys = 0.0;
00115         YVs = 0.0;
00116         type = PQ;
00117         name[0] = '\0';
00118         bus_id = 0;
00119         flow_area_num=1;
00120         loss_zone_num=1;
00121         base_kV=0;
00122         desired_kV=0;
00123         remote_bus_id=NULL;
00124 
00125         Vobs = complex(0,0,A); /* default observation is 0+0j */
00126         Vstdev = 0.0; /* default is no observation */
00127         r2 = 0.0;
00128         n_obs = 0;
00129         Sr2 = 0.0;
00130         n_inj = 0;
00131     }
00132 }
00133 
00134 node::~node()
00135 {
00136 }
00137 
00138 int node::create() 
00139 {
00140 /*
00141     V = complex(1,0,A);
00142     S = complex(0,0,J);
00143     G = 0;
00144     B = 0;
00145     Qmax_MVAR = 0.0;
00146     Qmin_MVAR = 0.0;
00147     Ys = 0.0;
00148     YVs = 0.0;
00149     type = PQ;
00150     name[0] = '\0';
00151     bus_id = 0;
00152     flow_area_num=1;
00153     loss_zone_num=1;
00154     base_kV=0;
00155     desired_kV=0;
00156     remote_bus_id=NULL;
00157 
00158     Vobs = complex(0,0,A); // default observation is 0+0j 
00159     Vstdev = 0.0; // default is no observation 
00160     r2 = 0.0;
00161     n_obs = 0;
00162     Sr2 = 0.0;
00163     n_inj = 0;
00164 */
00165     memcpy(this,defaults,sizeof(*this));
00166     return 1;
00167 }
00168 
00169 int node::init(OBJECT *parent)
00170 {
00171     // check that parent is swing bus
00172     OBJECT *hdr = OBJECTHDR(this);
00173     node *swing = parent?OBJECTDATA(parent,node):this;
00174     OBJECT *swing_hdr = OBJECTHDR(swing);
00175     if (swing_hdr->oclass!=hdr->oclass || swing->type!=SWING)
00176     {
00177         gl_error("node %s:%d parent is not a swing bus",hdr->oclass->name,hdr->id);
00178         return 0;
00179     }
00180 #ifdef HYBRID
00181     // add observation residual
00182     if (Vstdev>0)
00183         swing->add_obs_residual(this);
00184 
00185     // add injection error
00186     swing->add_inj_residual(this);
00187 #endif
00188 
00189     YVs=complex(0,0);
00190     Ys=complex(0,0);
00191     return 1;
00192 }
00193 
00194 #ifdef HYBRID
00195 void node::add_inj_residual(node *pNode)
00196 {
00197     if (pNode!=this)
00198     {
00199         pNode->Sr2 = (~(~pNode->V*((pNode->Ys + complex(pNode->G,pNode->B))*pNode->V + pNode->YVs)) - pNode->S).Mag();
00200         Sr2 += pNode->Sr2*pNode->Sr2;
00201         n_inj++;
00202     }
00203 }
00204 
00205 void node::del_inj_residual(node *pNode)
00206 {
00207     if (pNode!=this)
00208     {
00209         Sr2 -= pNode->Sr2*pNode->Sr2;
00210         n_inj--;
00211     }
00212 }
00213 
00214 double node::get_inj_residual(void) const
00215 {
00216     if (Sr2>0 && n_inj>0)
00217         return sqrt(Sr2/n_inj);
00218     else
00219         return Sr2;
00220 }
00221 
00222 void node::add_obs_residual(node *pNode)
00223 {
00224     double r = (pNode->V - pNode->Vobs).Mag() / pNode->Vstdev;
00225     r*=r;
00226     if (pNode!=this)
00227     {
00228         pNode->r2 = r;
00229         pNode->n_obs = 1;
00230     }
00231     r2 += r;
00232     n_obs++;
00233 }
00234 
00235 void node::del_obs_residual(node *pNode)
00236 {
00237     if (pNode!=this)
00238         r2 -= pNode->r2;
00239     else
00240     {
00241         double r = (pNode->V - pNode->Vobs).Mag() / pNode->Vstdev;
00242         r2 -= r*r;
00243     }
00244     n_obs--;
00245 }
00246 
00247 double node::get_obs_residual(void) const
00248 {
00249     if (r2>0 && n_obs>0)
00250         return sqrt(r2/n_obs);
00251     else
00252         return r2;
00253 }
00254 
00255 double node::get_obs_probability(void) const
00256 {
00257     if (r2<0)
00258         return 1;
00259     double pr = exp(-0.5*r2); 
00260     if (pr>1)
00261         gl_warning("node:%d observation probability exceeds 1!", OBJECTHDR(this)->id);
00262     return pr;
00263 }
00264 #endif
00265 
00266 TIMESTAMP node::sync(TIMESTAMP t0) 
00267 {
00268     OBJECT *hdr = OBJECTHDR(this);
00269     node *swing = hdr->parent?OBJECTDATA(hdr->parent,node):this;
00270     complex dV(0.0);
00271     complex YY = Ys + complex(G,B);
00272 #ifdef HYBRID
00273     swing->del_inj_residual(this);
00274 #endif
00275     if (!YY.IsZero() || type==SWING)
00276     {
00277         switch (type) {
00278         case PV:
00279             S.Im() = -((~V*(YY*V+YVs)).Im());
00280             if (Qmin_MVAR<Qmax_MVAR && S.Im()<Qmin_MVAR) 
00281                 S.Im() = Qmin_MVAR;
00282             else if (Qmax_MVAR>Qmin_MVAR && S.Im()>Qmax_MVAR) 
00283                 S.Im() = Qmax_MVAR;
00284             else
00285             {
00286                 complex Vnew = (~S/~V - YVs) / YY;
00287                 Vnew.SetPolar(V.Mag(),Vnew.Arg());
00288 #ifdef HYBRID
00289                 if (Vstdev>0)
00290                 {
00291                     double pr = swing->get_obs_probability();
00292                     swing->del_obs_residual(this);
00293                     dV = Vobs*(1-pr) + (Vnew)*pr - V;
00294                     V += dV;
00295                     swing->add_obs_residual(this);
00296                 }
00297                 else
00298 #endif
00299                 {
00300                     dV = Vnew - V;
00301                     V = Vnew;
00302                 }
00303                 break;
00304             }
00305             /* continue with PQ solution */
00306         case PQ:
00307             if (!V.IsZero())
00308             {
00309                 complex Vnew = (~S/~V - YVs) / YY;
00310 #ifdef HYBRID
00311                 if (Vstdev>0) // need to consider observation
00312                 {
00313                     double pr = swing->get_obs_probability();
00314                     swing->del_obs_residual(this);
00315                     dV = Vobs*(1-pr) + (Vnew)*pr - V;
00316                     V += dV;
00317                     swing->add_obs_residual(this);
00318                 }
00319                 else // no observation 
00320 #endif
00321                 {
00322                     dV = (Vnew - V)*acceleration_factor;
00323                     V += dV;
00324                 }
00325                 V.Notation() = A;
00326             }
00327             break;
00328         case SWING:
00329             S = ~(~V*(YY*V + YVs));
00330             S.Notation() = J;
00331             break;
00332         default:
00333             /* unknown type fails */
00334             gl_error("invalid bus type");
00335             return TS_ZERO;
00336         }
00337     }
00338 #ifdef HYBRID
00339     swing->add_inj_residual(this);
00340 #endif
00341 
00342 #ifdef _DEBUG
00343     // node debugging
00344     if (debug_node>0)
00345     {
00346         OBJECT* obj = OBJECTHDR(this);
00347         static int first=-1;
00348         if (first==-1) first = obj->id;
00349         if (obj->id==first)
00350         {
00351             printf("\n");
00352             printf("Node           Type  V                 Vobs              Stdev    Power             G        B        dV       Pr{Vobs} r2     Sr2\n");
00353             printf("============== ===== ================= ================= ======== ================= ======== ======== ======== ======== ====== ======\n");
00354         }
00355         if (((debug_node&1)==1 && dV.Mag()>convergence_limit )  // only on dV
00356 #ifdef HYBRID
00357             || ((debug_node&2)==2 && Vstdev>0 ) // only on observation
00358             || ((debug_node&4)==4 && get_inj_residual()>0.001)// non-zero power residual
00359 #endif
00360             )
00361         {
00362             printf("%2d (%-9.9s) %5s %+8.4f%+8.3fd %+8.4f%+8.3fd %8.5f %+8.4f%+8.4fj %+8.5f ", 
00363                 obj->id, name, type==SWING?"SWING":(type==PQ?"PQ   ":"PV"),
00364                 V.Mag(),V.Arg()*180/3.1416, 
00365                 Vobs.Mag(), Vobs.Arg()*180/3.1416, Vstdev,
00366                 S.Re(), S.Im(), 
00367                 G, B,
00368                 dV.Mag());
00369 #ifdef HYBRID
00370             printf("%+8.5f %8.5f ", get_obs_probability(), r2);
00371             if (Vstdev>0)
00372                 printf("%8.5f %6.3f %6.3f\n", get_obs_probability(), r2, get_inj_residual());
00373             else
00374                 printf("   --      --   %6.3f\n",get_inj_residual());
00375 #else
00376             printf("\n");
00377 #endif
00378         }
00379     }
00380 #endif // _DEBUG
00381 
00382     Ys = 0;
00383     YVs = 0;
00384     if (dV.Mag()>convergence_limit)
00385         return t0; /* did not converge, stop clock */
00386     else
00387         return TS_NEVER; /* converged, no further updates needed */
00388 }
00389 
00391 // IMPLEMENTATION OF CORE LINKAGE: node
00393 
00394 EXPORT int create_node(OBJECT **obj, OBJECT *parent)
00395 {
00396     *obj = gl_create_object(node_class,sizeof(node));
00397     if (*obj!=NULL)
00398     {
00399         last_node = *obj;
00400         node *my = OBJECTDATA(*obj,node);
00401         gl_set_parent(*obj,parent);
00402         my->create();
00403         return 1;
00404     }
00405     return 0;
00406 }
00407 
00408 EXPORT int init_node(OBJECT *obj)
00409 {
00410     return OBJECTDATA(obj,node)->init(obj->parent);
00411 }
00412 
00413 EXPORT TIMESTAMP sync_node(OBJECT *obj, TIMESTAMP t0)
00414 {
00415     TIMESTAMP t1 = OBJECTDATA(obj,node)->sync(t0);
00416     obj->clock = t0;
00417     return t1;
00418 }
00419 

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