TIMESTAMP house::sync_panel(TIMESTAMP t0, TIMESTAMP t1) { TIMESTAMP sync_time = TS_NEVER; OBJECT *obj = OBJECTHDR(this); // clear accumulators for panel currents complex I[3]; I[X12] = I[X23] = I[X13] = complex(0,0); // clear heatgain accumulator double heatgain = 0; // gather load power and compute current for each circuit CIRCUIT *c; for (c=panel.circuits; c!=NULL; c=c->next) { // get circuit type int n = (int)c->type; if (n<0 || n>2) GL_THROW("%s:%d circuit %d has an invalid circuit type (%d)", obj->oclass->name, obj->id, c->id, (int)c->type); /* TROUBLESHOOT Invalid circuit types are an internal error for the house panel. Please report this error. The likely causes include an object that is not a house is being processed by the house model, or the panel was not correctly initialized. */ // if breaker is open and reclose time has arrived if (c->status==BRK_OPEN && t1>=c->reclose) { c->status = BRK_CLOSED; c->reclose = TS_NEVER; sync_time = t1; // must immediately reevaluate devices affected gl_debug("house:%d panel breaker %d closed", obj->id, c->id); } // if breaker is closed if (c->status==BRK_CLOSED) { // compute circuit current if ((c->pV)->Mag() == 0) { gl_debug("house:%d circuit %d (enduse %s) voltage is zero", obj->id, c->id, c->pLoad->name); break; } complex current = ~(c->pLoad->total*1000 / *(c->pV)); // check breaker if (c->max_amps>0 && current.Mag()>c->max_amps) { // probability of breaker failure increases over time if (c->tripsleft>0 && gl_random_bernoulli(RNGSTATE,1/(c->tripsleft--))==0) { // breaker opens c->status = BRK_OPEN; // average five minutes before reclosing, exponentially distributed c->reclose = t1 + (TIMESTAMP)(gl_random_exponential(RNGSTATE,1/300.0)*TS_SECOND); gl_debug("house:%d circuit breaker %d tripped - enduse %s overload at %.0f A", obj->id, c->id, c->pLoad->name, current.Mag()); } // breaker fails from too frequent operation else { c->status = BRK_FAULT; c->reclose = TS_NEVER; gl_debug("house:%d circuit breaker %d failed", obj->id, c->id); } // must immediately reevaluate everything sync_time = t1; } // add to panel current else { tload.power += c->pLoad->power; // reminder: |a| + |b| != |a+b| tload.current += c->pLoad->current; tload.admittance += c->pLoad->admittance; // should this be additive? I don't buy t.a = c->pL->a ... -MH tload.total += c->pLoad->total; tload.heatgain += c->pLoad->heatgain; tload.energy += c->pLoad->power * gl_tohours(t1-t0); I[n] += current; c->reclose = TS_NEVER; } } // sync time if (sync_time > c->reclose) sync_time = c->reclose; } // compute line currents and post to meter if (obj->parent != NULL) LOCK_OBJECT(obj->parent); pLine_I[0] = I[X13]; pLine_I[1] = I[X23]; pLine_I[2] = 0; *pLine12 = I[X12]; if (obj->parent != NULL) UNLOCK_OBJECT(obj->parent); return sync_time; }
/** * Fuse checking function * Hold over code from previous functionality - lets FBS work as-is, for now. * * functionalized so don't have to change 4 entries in 3 different sets every time * * @param phase_to_check - the current phase to check fusing action for * @param fcurr - array of from (line input) currents */ void fuse::fuse_check(set phase_to_check, complex *fcurr) { char indexval; char phase_verbose; unsigned char work_phase; FUSESTATE *valstate; TIMESTAMP *fixtime; OBJECT *hdr = OBJECTHDR(this); if (phase_to_check == PHASE_A) { indexval = 0; valstate = (FUSESTATE*)&phase_A_state; phase_verbose='A'; fixtime = &fix_time[0]; } else if (phase_to_check == PHASE_B) { indexval = 1; valstate = (FUSESTATE*)&phase_B_state; phase_verbose='B'; fixtime = &fix_time[1]; } else if (phase_to_check == PHASE_C) { indexval = 2; valstate = (FUSESTATE*)&phase_C_state; phase_verbose='C'; fixtime = &fix_time[2]; } else { GL_THROW("Unknown phase to check in fuse:%d",OBJECTHDR(this)->id); /* TROUBLESHOOT An invalid phase was specified for the phase check in a fuse. Please check your code and continue. If it persists, submit your code and a bug using the trac website. */ } //See which phases we need to check if ((phases & phase_to_check) == phase_to_check) //Check phase { work_phase = 0x04 >> indexval; //Working variable, primarily for NR if (*valstate == GOOD) //Only bother if we are in service { //Check both directions, that way if we are reverse flowed it doesn't matter if (fcurr[indexval].Mag() > current_limit) //We've exceeded the limit { *valstate = BLOWN; //Trip us //Set us up appropriately A_mat[indexval][indexval] = d_mat[indexval][indexval] = 0.0; //Get an update time *fixtime = prev_fuse_time + (int64)(3600*gl_random_exponential(RNGSTATE,1.0/mean_replacement_time)); //Announce it for giggles gl_warning("Phase %c of fuse:%d (%s) just blew",phase_verbose,hdr->id,hdr->name); } else //Still good { //Ensure matrices are up to date in case someone manually set things A_mat[indexval][indexval] = d_mat[indexval][indexval] = 1.0; } } else //We're blown { if (*fixtime <= prev_fuse_time) //Technician has arrived and replaced us!! { //Fix us A_mat[indexval][indexval] = d_mat[indexval][indexval] = 1.0; *valstate = GOOD; *fixtime = TS_NEVER; //Update the time check just in case //Send an announcement for giggles gl_warning("Phase %c of fuse:%d (%s) just returned to service",phase_verbose,hdr->id,hdr->name); } else //Still driving there or on break, no fixed yet { //Ensure matrices are up to date in case someone manually blew us (or a third, off state is implemented) A_mat[indexval][indexval] = d_mat[indexval][indexval] = 0.0; } } }
TIMESTAMP relay::sync(TIMESTAMP t0) { TIMESTAMP t1 = TS_NEVER; node *f; node *t; set reverse = get_flow(&f,&t); #ifdef SUPPORT_OUTAGES //Handle explicit trips differently if (recloser_event) { //Make sure we aren't locked out if ((recloser_reset_time != 0) && (recloser_reset_time<=t0)) //We're done { //Reset all of the variables recloser_reset_time = 0; recloser_delay_time = 0; current_recloser_tries = 0; status = LS_CLOSED; t1 = TS_NEVER; //Just flag us as something small to continue gl_verbose("Recloser:%d just unlocked and rejoined service",OBJECTHDR(this)->id); } else if ((recloser_reset_time != 0 ) && (recloser_reset_time>t0)) //Not done being locked out { status=LS_OPEN; //Make sure we are still open t1 = recloser_reset_time; } else //Should be normal area { if (status==LS_OPEN) //Open operations - only if a reliability event is occurring { if (recloser_delay_time<=t0) //Time delay has passed - but we are obviously still "in fault" { recloser_tries++; //Increment the tries counter current_recloser_tries++; //Increment the current tries if (current_recloser_tries>recloser_limit) //Now we need to lock out { recloser_delay_time = 0; recloser_reset_time = t0+(TIMESTAMP)(gl_random_exponential(3600)*TS_SECOND); //Figure out how long to lock out gl_verbose("Recloser:%d just reached its limit and locked out for a while",OBJECTHDR(this)->id); t1 = recloser_reset_time; } else //We're still OK to flicker { recloser_delay_time = t0+(TIMESTAMP)(recloser_delay*TS_SECOND); //Get a new time gl_verbose("Recloser:%d just tried to reclose and failed",OBJECTHDR(this)->id); t1 = recloser_delay_time; } } else //still in delay { t1 = recloser_delay_time; } } else //Closed operations - only if a reliability event is occurring { status=LS_OPEN; //Open us up, we are in an event current_recloser_tries = 0; //Reset out count, just in case recloser_reset_time = 0; //Reset the lockout timer, just in case recloser_delay_time = t0+(TIMESTAMP)(recloser_delay*TS_SECOND); //Get a new time gl_verbose("Recloser:%d detected a fault and opened",OBJECTHDR(this)->id); t1 = recloser_delay_time; } } } else //Older method (and catch if reliabilty ends while in lockout) { set trip = (f->is_contact_any() || t->is_contact_any()); //Make sure aren't in overall lockout if ((recloser_reset_time != 0 ) && (recloser_reset_time>=t0)) //We're done being locked out { //Reset all of the variables recloser_reset_time = 0; recloser_delay_time = 0; recloser_tries = 0; status = LS_CLOSED; t1 = TS_NEVER; //Just flag us as something small to continue gl_verbose("Recloser:%d just unlocked and rejoined service",OBJECTHDR(this)->id); } else if ((recloser_reset_time != 0 ) && (recloser_reset_time>t0)) //Not done being locked out { t1 = recloser_reset_time; } else //Should be normal area { /* perform relay operation if any line contact has occurred */ if (status==LS_CLOSED && trip) { status = LS_OPEN; /* schedule recloser operation */ recloser_delay_time=t0+(TIMESTAMP)(recloser_delay*TS_SECOND); gl_verbose("Recloser:%d detected a fault and opened",OBJECTHDR(this)->id); t1 = recloser_delay_time; } /* recloser time has arrived */ else if (status==LS_OPEN && t0>=recloser_delay_time) { /* still have contact */ if (trip) { /* reschedule automatic recloser if retries permits */ if (recloser_limit>recloser_tries) { recloser_tries++; gl_verbose("Recloser:%d just tried to reclose and failed",OBJECTHDR(this)->id); recloser_delay_time = t0+(TIMESTAMP)(recloser_delay*TS_SECOND); t1 = recloser_delay_time; } /* automatic retries exhausted, manual takes an average of an hour */ else { gl_verbose("Recloser:%d just reached its limit and locked out for a while",OBJECTHDR(this)->id); recloser_reset_time = t0+(TIMESTAMP)(gl_random_exponential(3600)*TS_SECOND); t1 = recloser_reset_time; } } else status = LS_CLOSED; } else if ((recloser_delay_time != 0) && (recloser_delay_time>t0)) //Still in delay { t1 = recloser_delay_time; } else //Recover { if (status==LS_OPEN) { gl_verbose("Recloser:%d recovered from a fault",OBJECTHDR(this)->id); } current_recloser_tries = 0; //Reset count variables recloser_tries = 0; status=LS_CLOSED; } } } #endif TIMESTAMP t2=link::sync(t0); return t1<t2?t1:t2; }
TIMESTAMP fuse::sync(TIMESTAMP t0) { OBJECT *obj = OBJECTHDR(this); unsigned char work_phases; bool fuse_blew; TIMESTAMP replacement_time; TIMESTAMP replacement_duration; TIMESTAMP t2; char fault_val[9]; int result_val; //Try to map the event_schedule function address, if we haven't tried yet if (event_schedule_map_attempt == false) { //First check to see if a fault_check object even exists if (fault_check_object != NULL) { //It exists, good start! - now see if the proper variable is populated! eventgen_obj = get_object(fault_check_object, "eventgen_object"); //See if it worked - if not, assume it doesn't exist if (*eventgen_obj != NULL) { //It's not null, map up the scheduler function event_schedule = (FUNCTIONADDR)(gl_get_function(*eventgen_obj,"add_event")); //Make sure it was found if (event_schedule == NULL) { gl_warning("Unable to map add_event function in eventgen:%s",*(*eventgen_obj)->name); /* TROUBLESHOOT While attempting to map the "add_event" function from an eventgen object, the function failed to be found. Ensure the target object in fault_check is an eventgen object and this function exists. If the error persists, please submit your code and a bug report via the trac website. */ } } //Defaulted elses - just leave things as is :( } //Defaulted else - doesn't exist, so leave function address empty //Flag the attempt as having occurred event_schedule_map_attempt = true; } //Update time variable if (prev_fuse_time != t0) //New timestep prev_fuse_time = t0; //Code below only applies to NR right now - FBS legacy code has no sync values //May need to be appropriately adjusted once FBS supports reliability if (solver_method == SM_NR) { //Put any fuses back in service, if they're ready if (((fix_time[0] <= t0) || (fix_time[1] <= t0) || (fix_time[2] <= t0)) && (event_schedule == NULL)) //Only needs to be done if reliability isn't present { //Bring the phases back that are necessary if ((fix_time[0] <= t0) && ((NR_branchdata[NR_branch_reference].origphases & 0x04) == 0x04)) //Phase A ready and had a phase A { //Update status phase_A_state = GOOD; //Pop in the variables for the reliability update (if it exists) fix_time[0] = TS_NEVER; //Reset variables } if ((fix_time[1] <= t0) && ((NR_branchdata[NR_branch_reference].origphases & 0x02) == 0x02)) //Phase B ready and had a phase B { //Update status phase_B_state = GOOD; //Pop in the variables for the reliability update (if it exists) fix_time[1] = TS_NEVER; //Reset variables } if ((fix_time[2] <= t0) && ((NR_branchdata[NR_branch_reference].origphases & 0x01) == 0x01)) //Phase C ready and had a phase C { //Update status phase_C_state = GOOD; //Pop in the variables for the reliability update (if it exists) fix_time[2] = TS_NEVER; //Reset variables } }//End back in service //Call syncing function fuse_sync_function(); //Call overlying link sync t2=link_object::sync(t0); //Always execute check code now //Start with no assumed outages fuse_blew = false; work_phases = 0x00; replacement_time = 0; //Check them if ((NR_branchdata[NR_branch_reference].phases & 0x04) == 0x04) //Phase A valid - check it { //Link::sync is where current in is calculated. Convert the values current_current_values[0] = current_in[0].Mag(); if ((current_current_values[0] > current_limit) && (phase_A_state == GOOD)) { phase_A_state = BLOWN; //Blow the fuse gl_warning("Phase A of fuse:%s just blew!",obj->name); /* TROUBLESHOOT The current through phase A of the fuse just exceeded the maximum rated value. Use a larger value, or otherwise change your system and try again. */ fuse_blew = true; //Flag a change work_phases |= 0x04; //Flag A change //See if an update is needed (it's A and first, so yes, but just to be generic) if (replacement_time == 0) { //Get length of outage if (restore_dist_type == EXPONENTIAL) { //Update mean repair time mean_repair_time = gl_random_exponential(RNGSTATE,1.0/mean_replacement_time); replacement_duration = (TIMESTAMP)(mean_repair_time); } else { //Update mean repair time - fuse always overrides link mean_repair_time = mean_replacement_time; replacement_duration = (TIMESTAMP)(mean_repair_time); } //Figure out when it is replacement_time = prev_fuse_time + replacement_duration; } } //Else is leave as is - either blown, or reliability hit it } if ((NR_branchdata[NR_branch_reference].phases & 0x02) == 0x02) //Phase B valid - check it { //Link::sync is where current in is calculated. Convert the values current_current_values[1] = current_in[1].Mag(); if ((current_current_values[1] > current_limit) && (phase_B_state == GOOD)) { phase_B_state = BLOWN; //Blow the fuse gl_warning("Phase B of fuse:%s just blew!",obj->name); /* TROUBLESHOOT The current through phase B of the fuse just exceeded the maximum rated value. Use a larger value, or otherwise change your system and try again. */ fuse_blew = true; //Flag a change work_phases |= 0x02; //Flag B change //See if an update is needed if (replacement_time == 0) { //Get length of outage if (restore_dist_type == EXPONENTIAL) { //Update mean repair time mean_repair_time = gl_random_exponential(RNGSTATE,1.0/mean_replacement_time); replacement_duration = (TIMESTAMP)(mean_repair_time); } else { //Update mean repair time - fuse always overrides link mean_repair_time = mean_replacement_time; replacement_duration = (TIMESTAMP)(mean_repair_time); } //Figure out when it is replacement_time = prev_fuse_time + replacement_duration; } } //Else is leave as is - either blown, or reliability hit it } if ((NR_branchdata[NR_branch_reference].phases & 0x01) == 0x01) //Phase C valid - check it { //Link::sync is where current in is calculated. Convert the values current_current_values[2] = current_in[2].Mag(); if ((current_current_values[2] > current_limit) && (phase_C_state == GOOD)) { phase_C_state = BLOWN; //Blow the fuse gl_warning("Phase C of fuse:%s just blew!",obj->name); /* TROUBLESHOOT The current through phase C of the fuse just exceeded the maximum rated value. Use a larger value, or otherwise change your system and try again. */ fuse_blew = true; //Flag a change work_phases |= 0x01; //Flag C change //See if an update is needed if (replacement_time == 0) { //Get length of outage if (restore_dist_type == EXPONENTIAL) { //Update mean repair time mean_repair_time = gl_random_exponential(RNGSTATE,1.0/mean_replacement_time); replacement_duration = (TIMESTAMP)(mean_repair_time); } else { //Update mean repair time - fuse always overrides link mean_repair_time = mean_replacement_time; replacement_duration = (TIMESTAMP)(mean_repair_time); } //Figure out when it is replacement_time = prev_fuse_time + replacement_duration; } } //Else is leave as is - either blown, or reliability hit it } if (fuse_blew == true) { //Set up fault type fault_val[0] = 'F'; fault_val[1] = 'U'; fault_val[2] = 'S'; fault_val[3] = '-'; //Determine who blew and store the time (assumes fuses can be replaced in parallel) switch (work_phases) { case 0x00: //No fuses blown !?? GL_THROW("fuse:%s supposedly blew, but doesn't register the right phases",obj->name); /* TROUBLESHOOT A fuse reported an over-current condition and blew the appropriate link. However, it did not appear to fully propogate this condition. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ break; case 0x01: //Phase C blew fix_time[2] = replacement_time; fault_val[4] = 'C'; fault_val[5] = '\0'; break; case 0x02: //Phase B blew fix_time[1] = replacement_time; fault_val[4] = 'B'; fault_val[5] = '\0'; break; case 0x03: //Phase B and C blew fix_time[1] = replacement_time; fix_time[2] = replacement_time; fault_val[4] = 'B'; fault_val[5] = 'C'; fault_val[6] = '\0'; break; case 0x04: //Phase A blew fix_time[0] = replacement_time; fault_val[4] = 'A'; fault_val[5] = '\0'; break; case 0x05: //Phase A and C blew fix_time[0] = replacement_time; fix_time[2] = replacement_time; fault_val[4] = 'A'; fault_val[5] = 'C'; fault_val[6] = '\0'; break; case 0x06: //Phase A and B blew fix_time[0] = replacement_time; fix_time[1] = replacement_time; fault_val[4] = 'A'; fault_val[5] = 'B'; fault_val[6] = '\0'; break; case 0x07: //All three went fix_time[0] = replacement_time; fix_time[1] = replacement_time; fix_time[2] = replacement_time; fault_val[4] = 'A'; fault_val[5] = 'B'; fault_val[6] = 'C'; fault_val[7] = '\0'; break; default: GL_THROW("fuse:%s supposedly blew, but doesn't register the right phases",obj->name); //Defined above }//End switch if (event_schedule != NULL) //Function was mapped - go for it! { //Call the function result_val = ((int (*)(OBJECT *, OBJECT *, char *, TIMESTAMP, TIMESTAMP, int, bool))(*event_schedule))(*eventgen_obj,obj,fault_val,t0,0,-1,false); //Make sure it worked if (result_val != 1) { GL_THROW("Attempt to blow fuse:%s failed in a reliability manner",obj->name); /* TROUBLESHOOT While attempting to propagate a blown fuse's impacts, an error was encountered. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ } //Ensure we don't go anywhere yet t2 = t0; } //End fault object present else //No object, just fail us out - save the iterations { gl_warning("No fault_check object present - Newton-Raphson solver may fail!"); /* TROUBLESHOOT A fuse blew and created an open link. If the system is not meshed, the Newton-Raphson solver will likely fail. Theoretically, this should be a quick fail due to a singular matrix. However, the system occasionally gets stuck and will exhaust iteration cycles before continuing. If the fuse is blowing and the NR solver still iterates for a long time, this may be the case. */ } } }//End NR-only reliability calls else //FBS t2 = link_object::sync(t0); if (t2==TS_NEVER) return(t2); else return(-t2); //Soft limit it }