Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Fix voltage violation detection implementation errors #1170

Merged
merged 8 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/Use cases/Hosting capacity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[[/Use cases/Hosting capacity analysis]] - Hosting capacity analysis support

The `powerflow` module implements support for hosting capacity analysis in all objects derives from `link` and `node` classes.

# Limit violations

When limit violations are detected, they are flagged by setting the `violation_active` property of the object whose limits are violated. The `violation_count` variable is also incremented.

## Current violations

Current limit violations are detected on all links when the `violation_rating[A]` is exceeded by the `current_in_[ABC]` on the link. Current limits are only detected when the `violation_rating[A]` is positive. If the `violation_rating[A]` property of an object is zero, then the value of the module global `default_violation_rating[A]` is used. By default, this value is zero, which disables current violation detection.

## Voltage violations

Voltage limit violations are detected on all nodes when the `voltage_violation_threshold[pu]` is exceeded by the node voltages on any phases relative to the nominal voltage. If the `voltage_violation_threshold[pu]` property is zero, then the value of the module global `default_voltage_violation_threshold[pu]` is used. By default, this value is `0.05`, which enables voltage violation detection for all objects. Setting the global value to zero disables voltage violation checks.

If either the `undervoltage_violation_threshold[pu]` or `overvoltage_violation_threshold[pu]` are set and `undervoltage_violation_threshold[pu]` < `overvoltage_violation_threshold[pu]`, then these values are used instead. Similarly, the corresponding global values are used if these values are zero. The default `undervoltage_violation_threshold[pu]` and `overvoltage_violation_threshold[pu]` values are zero, which disables them.

## Voltage fluctuations

Voltage fluctuations are detected on all nodes when both `voltage_fluctuation_threshold[pu]` and `DER_value[W]` are non-zero. The solver compares the voltage of each phase of each object in the network with the DER on and off. If the difference in voltage exceeds `voltage_fluctuation_threshold[pu]`, then the violation is detected. The default value of `voltage_fluctuation_threshold[pu]` is given by `default_voltage_fluctuation_threshold[pu]`, which is zero by default.

# Violation recording

When limit violations are enables they are recorded in the filename specified by the `powerflow` module global `violation_record`. If this file is not specified, violations are not recorded when they are detected.

# Exhaustive violation checks

Normally, the voltage fluctuation tests stop checking if any violation is found, and no further tests are conducted. If the module global `DER_violation_tests` is set to `ALL`, then all violations will be reported.
12 changes: 6 additions & 6 deletions module/powerflow/autotest/test_node_voltage_fluctuation.csv
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
timestamp,object,type,description
2000-01-01 00:00:00,load_1,VOLTAGE,"load_1 phase A voltage magnitude 2252.6 V outside 3.0% violation threshold for load_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,load_1,VOLTAGE,"load_1 phase B voltage magnitude 2252.6 V outside 3.0% violation threshold for load_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,load_1,VOLTAGE,"load_1 phase C voltage magnitude 2252.6 V outside 3.0% violation threshold for load_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,load_2,VOLTAGE,"load_2 phase A voltage magnitude 2184.0 V outside 3.0% violation threshold for load_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,load_2,VOLTAGE,"load_2 phase B voltage magnitude 2184.0 V outside 3.0% violation threshold for load_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,load_2,VOLTAGE,"load_2 phase C voltage magnitude 2184.0 V outside 3.0% violation threshold for load_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,node_2,VOLTAGE,"node_2 phase A voltage magnitude 2184.0 V outside 4.0% violation threshold for node_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,node_2,VOLTAGE,"node_2 phase B voltage magnitude 2184.0 V outside 4.0% violation threshold for node_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,node_2,VOLTAGE,"node_2 phase C voltage magnitude 2184.0 V outside 4.0% violation threshold for node_2 DER_value 100000.0+0.0j kVA"
2000-01-01 00:00:00,node_2,VOLTAGE,"node phase A voltage 2257.7 V is outside 5.0% violation threshold"
2000-01-01 00:00:00,node_2,VOLTAGE,"node phase B voltage 2258.8 V is outside 5.0% violation threshold"
2000-01-01 00:00:00,node_2,VOLTAGE,"node phase C voltage 2259.5 V is outside 5.0% violation threshold"
13 changes: 12 additions & 1 deletion module/powerflow/autotest/test_node_voltage_fluctuation.glm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ module powerflow
{
solver_method NR;
violation_record "test_node_voltage_fluctuation.csv";
voltage_fluctuation_threshold 0.04;
DER_violation_test ALL;
}

object node {
Expand Down Expand Up @@ -77,13 +79,21 @@ object overhead_line
{
phases ABCN;
from "load_1";
to "load_2";
to "node_2";
length 1 mile;
configuration "lc";
}

object node
{
name "node_2";
phases ABCN;
nominal_voltage 2400 V;
}

object load
{
parent "node_2";
phases ABCN;
nominal_voltage 2400 V;
name "load_2";
Expand All @@ -97,3 +107,4 @@ object load
#on_exit 0 diff ../test_node_voltage_fluctuation.csv test_node_voltage_fluctuation.csv >gridlabd.diff
#endif

#set savefile=test_node_voltage_fluctuation.json
6 changes: 6 additions & 0 deletions module/powerflow/link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ CLASS* link_object::pclass = NULL;

double link_object::default_continuous_rating = 1000;
double link_object::default_emergency_rating = 2000;
double link_object::default_violation_rating = 0.0;

EXPORT_COMMIT_C(link,link_object)

Expand Down Expand Up @@ -157,6 +158,7 @@ link_object::link_object(MODULE *mod) : powerflow_object(mod)

gl_global_create("powerflow::default_continuous_rating[A]",PT_double,&default_continuous_rating,NULL);
gl_global_create("powerflow::default_emergency_rating[A]",PT_double,&default_emergency_rating,NULL);
gl_global_create("powerflow::default_violation_rating[A]",PT_double,&default_violation_rating,NULL);

//Publish deltamode functions
if (gl_publish_function(oclass, "interupdate_pwr_object", (FUNCTIONADDR)interupdate_link)==NULL)
Expand Down Expand Up @@ -879,6 +881,10 @@ int link_object::init(OBJECT *parent)
error("negative violation_rating is not valid");
return 0;
}
else if ( violation_rating == 0 )
{
violation_rating = default_violation_rating;
}

return 1;
}
Expand Down
1 change: 1 addition & 0 deletions module/powerflow/link.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class link_object : public powerflow_object
int protect_locations[3]; ///< Links to protection object for different phase faults - part of reliability
FUNCTIONADDR link_recalc_fxn; ///< Function address for link recalculation function - frequency dependence
double violation_rating; ///< maximum line flow before a violation occurs
static double default_violation_rating; /// default maximum line flow before a violation occurs

int create(void);
int init(OBJECT *parent);
Expand Down
119 changes: 107 additions & 12 deletions module/powerflow/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,12 @@ CLASS *node::pclass = NULL;

unsigned int node::n = 0;
double node::default_voltage_violation_threshold = 0.05;
double node::voltage_fluctuation_threshold = 0.03;
double node::default_overvoltage_violation_threshold = 0.00;
double node::default_undervoltage_violation_threshold = 0.00;
double node::default_voltage_fluctuation_threshold = 0.03;
OBJECT* *node::DER_objectlist = NULL;
unsigned int node::DER_nodecount = 0;
enumeration node::DER_violation_test = DVT_ANY;

node::node(MODULE *mod) : powerflow_object(mod)
{
Expand Down Expand Up @@ -353,14 +356,28 @@ node::node(MODULE *mod) : powerflow_object(mod)
PT_DESCRIPTION,"topological parent as per GLM configuration",

PT_double, "voltage_violation_threshold[pu]", PADDR(voltage_violation_threshold),
PT_DEFAULT, "+0.0 pu",
PT_DESCRIPTION,"voltage violation threshold (per unit nominal voltage)",
PT_double, "undervoltage_violation_threshold[pu]", PADDR(undervoltage_violation_threshold),
PT_DEFAULT, "+0.0 pu",
PT_DESCRIPTION,"under-voltage violation threshold (per unit nominal voltage)",
PT_double, "overvoltage_violation_threshold[pu]", PADDR(overvoltage_violation_threshold),
PT_DEFAULT, "+0.0 pu",
PT_DESCRIPTION,"over-voltage violation threshold (per unit nominal voltage)",
PT_double, "voltage_fluctuation_threshold[pu]", PADDR(voltage_fluctuation_threshold),
PT_DEFAULT, "+0.0 pu",
PT_DESCRIPTION,"voltage fluctuation violation threshold (per unit nominal voltage)",

PT_complex, "DER_value[VA]", PADDR(DER_value),
PT_DESCRIPTION,"DER power fluctuation value (per phase)",

NULL) < 1) GL_THROW("unable to publish properties in %s",__FILE__);

gl_global_create("powerflow::voltage_violation_threshold[pu]",PT_double,&default_voltage_violation_threshold,NULL);
gl_global_create("powerflow::undervoltage_violation_threshold[pu]",PT_double,&default_undervoltage_violation_threshold,NULL);
gl_global_create("powerflow::overvoltage_violation_threshold[pu]",PT_double,&default_overvoltage_violation_threshold,NULL);
gl_global_create("powerflow::voltage_fluctuation_threshold[pu]",PT_double,&default_voltage_fluctuation_threshold,NULL);
gl_global_create("powerflow::DER_violation_test",PT_enumeration,&DER_violation_test,PT_KEYWORD,"ANY",DVT_ANY,PT_KEYWORD,"ALL",DVT_ALL,NULL);

if (gl_publish_function(oclass, "delta_linkage_node", (FUNCTIONADDR)delta_linkage)==NULL)
GL_THROW("Unable to publish node delta_linkage function");
Expand Down Expand Up @@ -494,10 +511,38 @@ int node::create(void)
//Multi-island tracking
reset_island_state = false; //Reset is disabled, by default

if ( voltage_violation_threshold == 0.0 )
if ( voltage_violation_threshold == 0.0 ) // 0.0 --> use global default threshold
{
voltage_violation_threshold = default_voltage_violation_threshold;
}
else if ( voltage_violation_threshold < 0.0 ) // <0.0 --> disable threshold
{
voltage_violation_threshold = 0.0;
}
if ( undervoltage_violation_threshold == 0.0 ) // 0.0 --> use global default threshold
{
undervoltage_violation_threshold = default_undervoltage_violation_threshold;
}
else if ( undervoltage_violation_threshold < 0.0 ) // <0.0 --> disable threshold
{
undervoltage_violation_threshold = 0.0;
}
if ( overvoltage_violation_threshold == 0.0 ) // 0.0 --> use global default threshold
{
overvoltage_violation_threshold = default_overvoltage_violation_threshold;
}
else if ( overvoltage_violation_threshold < 0.0 ) // <0.0 --> disable threshold
{
overvoltage_violation_threshold = 0.0;
}
if ( voltage_fluctuation_threshold == 0.0 ) // 0.0 --> use global default threshold
{
voltage_fluctuation_threshold = default_voltage_fluctuation_threshold;
}
else if ( voltage_fluctuation_threshold < 0.0 ) // <0.0 --> disable threshold
{
voltage_fluctuation_threshold = 0.0;
}

return result;
}
Expand All @@ -519,6 +564,14 @@ int node::init(OBJECT *parent)
DER_objectlist = (OBJECT**)malloc(sizeof(OBJECT*)*n);
}
DER_objectlist[DER_nodecount++] = obj;

// add child DER_value to parent node
if ( gl_object_isa(parent,"node") )
{
gld_property parent_DER_value(parent,"DER_value");
complex value = parent_DER_value.get_complex() + DER_value;
parent_DER_value.setp(value);
}
}

//Put the phase_S check right on the top, since it will apply to both solvers
Expand Down Expand Up @@ -2828,6 +2881,8 @@ TIMESTAMP node::sync(TIMESTAMP t0)
{
BUSDATA *der_bus = NR_busdata + der;
node *der_data = OBJECTDATA(der_bus->obj,node);
if ( der_bus->obj->parent != NULL && gl_object_isa(der_bus->obj->parent,"node") )
continue; // ignore child nodes because they're already included in parent node DER_value
der_data->clear_violation();

// DER is present on this bus
Expand Down Expand Up @@ -2863,19 +2918,30 @@ TIMESTAMP node::sync(TIMESTAMP t0)
debug("phase A voltage fluctuation violation detected on '%s' due to '%s' DER_value %.1f%+.1fj",check_name, der_name, der_data->DER_value.r, der_data->DER_value.i);
check_data->add_violation(VF_VOLTAGE,"%s phase A voltage magnitude %.1f V outside %.1f%% violation threshold for %s DER_value %.1f%+.1fj kVA",
check_name, check_data->voltage[0].Mag(), voltage_fluctuation_threshold*100, der_name, der_data->DER_value.r, der_data->DER_value.i);
if ( DER_violation_test == DVT_ANY )
{
break;
}
}
if ( has_phase(PHASE_B) && fabs((Vb[check_bus][1]-check_data->voltage[1]).Mag()) / Vb[check_bus][1].Mag() > voltage_fluctuation_threshold )
{
debug("phase B voltage fluctuation violation detected on '%s' due to '%s' DER_value %.1f%+.1fj",check_name, der_name, der_data->DER_value.r, der_data->DER_value.i);
check_data->add_violation(VF_VOLTAGE,"%s phase B voltage magnitude %.1f V outside %.1f%% violation threshold for %s DER_value %.1f%+.1fj kVA",
check_name, check_data->voltage[0].Mag(), voltage_fluctuation_threshold*100, der_name, der_data->DER_value.r, der_data->DER_value.i);

if ( DER_violation_test == DVT_ANY )
{
break;
}
}
if ( has_phase(PHASE_C) && fabs((Vb[check_bus][2]-check_data->voltage[2]).Mag()) / Vb[check_bus][2].Mag() > voltage_fluctuation_threshold )
{
debug("phase C voltage fluctuation violation detected on '%s' due to '%s' DER_value %.1f%+.1fj",check_name, der_name, der_data->DER_value.r, der_data->DER_value.i);
check_data->add_violation(VF_VOLTAGE,"%s phase C voltage magnitude %.1f V outside %.1f%% violation threshold for %s DER_value %.1f%+.1fj kVA",
check_name, check_data->voltage[0].Mag(), voltage_fluctuation_threshold*100, der_name, der_data->DER_value.r, der_data->DER_value.i);
if ( DER_violation_test == DVT_ANY )
{
break;
}
}
}
}
Expand Down Expand Up @@ -3480,19 +3546,48 @@ EXPORT int create_node(OBJECT **obj, OBJECT *parent)
EXPORT TIMESTAMP commit_node(OBJECT *obj, TIMESTAMP t1, TIMESTAMP t2)
{
node *pNode = OBJECTDATA(obj,node);
if ( pNode->has_phase(PHASE_A) && fabs(pNode->voltage[0].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->voltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase A voltage %.1f V is outside %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[0].Mag(), pNode->voltage_violation_threshold*100);
}
if ( pNode->has_phase(PHASE_B) && fabs(pNode->voltage[1].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->voltage_violation_threshold )
if ( pNode->undervoltage_violation_threshold < pNode->overvoltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase B voltage %.1f V is outside %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[1].Mag(), pNode->voltage_violation_threshold*100);
if ( pNode->has_phase(PHASE_A) && (pNode->voltage[0].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->overvoltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase A voltage %.1f V is above %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[0].Mag(), pNode->overvoltage_violation_threshold*100);
}
if ( pNode->has_phase(PHASE_A) && (pNode->voltage[0].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage < -pNode->undervoltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase A voltage %.1f V is below %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[0].Mag(), pNode->undervoltage_violation_threshold*100);
}
if ( pNode->has_phase(PHASE_B) && (pNode->voltage[1].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->overvoltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase B voltage %.1f V is above %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[1].Mag(), pNode->overvoltage_violation_threshold*100);
}
if ( pNode->has_phase(PHASE_B) && (pNode->voltage[1].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage < -pNode->undervoltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase B voltage %.1f V is below %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[1].Mag(), pNode->undervoltage_violation_threshold*100);
}
if ( pNode->has_phase(PHASE_C) && (pNode->voltage[2].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->overvoltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase C voltage %.1f V is above %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[2].Mag(), pNode->overvoltage_violation_threshold*100);
}
if ( pNode->has_phase(PHASE_C) && (pNode->voltage[2].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage < -pNode->undervoltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase C voltage %.1f V is below %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[2].Mag(), pNode->undervoltage_violation_threshold*100);
}
}
if ( pNode->has_phase(PHASE_C) && fabs(pNode->voltage[2].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->voltage_violation_threshold )
else
{
pNode->add_violation(VF_VOLTAGE,"%s phase C voltage %.1f V is outside %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[2].Mag(), pNode->voltage_violation_threshold*100);
if ( pNode->has_phase(PHASE_A) && fabs(pNode->voltage[0].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->voltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase A voltage %.1f V is outside %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[0].Mag(), pNode->voltage_violation_threshold*100);
}
if ( pNode->has_phase(PHASE_B) && fabs(pNode->voltage[1].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->voltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase B voltage %.1f V is outside %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[1].Mag(), pNode->voltage_violation_threshold*100);
}
if ( pNode->has_phase(PHASE_C) && fabs(pNode->voltage[2].Mag()-pNode->nominal_voltage)/pNode->nominal_voltage > pNode->voltage_violation_threshold )
{
pNode->add_violation(VF_VOLTAGE,"%s phase C voltage %.1f V is outside %.1f%% violation threshold", pNode->oclass->name, pNode->voltage[2].Mag(), pNode->voltage_violation_threshold*100);
}
}

try {
// This zeroes out all of the unused phases at each node in the FBS method
if (solver_method==SM_FBS)
Expand Down
Loading