5. Extensions¶
This section describes how to add custom functions and constraints to PFNET. The general principle is that PFNET asks functions and constraints to count the number of matrix rows and non-zero entries, then to analyze the structural and constant parts, and finally to evaluate the parts that depend on variable values. For each of these operations (count, analyze, and evaluate), PFNET loops through buses and time periods once, and asks constraints to perform these operations gradually in steps for each bus and time.
5.1. Adding a Function¶
To add a new function to PFNET, one should create a subclass of the CustomFunction class and provide four methods:
init- This method initializes any custom function data.
count_step- This method is called for every bus and time period, and is responsible for updating the counter
Hphi_nnzof non-zero entries of the Hessian matrix of the function (only lower or upper triangular part).
- This method is called for every bus and time period, and is responsible for updating the counter
analyze_step- This method is called for every bus and time period, and is responsible for storing the structural or constant part of the Hessian matrix
Hphi. Only one element of each off-diagonal pair should be stored. After all buses and time periods have been processed, PFNET automatically makes the Hessian lower triangular by swapping elements as necessary.
- This method is called for every bus and time period, and is responsible for storing the structural or constant part of the Hessian matrix
eval_step
A template for creating a custom function is provided below. The argument bus is an AC bus whereas the argument busdc is an HVDC bus. For each function call, exactly one of them is given and the other is None.
from pfnet import CustomFunction
class DummyFunction(CustomFunction):
def init(self):
self.name = "dummy function"
def count_step(self, bus, busdc, t):
pass
def analyze_step(self, bus, busdc, t):
pass
def eval_step(self, bus, busdc, t, x):
pass
An example of a custom function that computes the quadratic active power generation cost can be found here.
5.2. Adding a Constraint¶
To add a new constraint to PFNET, one should create a subclass of the CustomConstraint class. The subclass needs to define the following five methods:
init- This method initializes any custom constraint data.
count_step- This method is called for every bus and time period, and is responsible for updating the counters
A_row,A_nnz,G_row,G_nnz,J_row, andJ_nnz, which count the number of rows and non-zero elements of the matricesA,G, and JacobianJ, respectively, and for updating the entries of the arrayH_nnz, which count the number of non-zeros of each nonlinear constraint Hessian (only lower or upper triangular part). If the constraint has extra variables, this method is responsible for updating the counternum_extra_vars.
- This method is called for every bus and time period, and is responsible for updating the counters
analyze_step- This method is called for every bus and time period, and is responsible for storing the structural or constant parts of the matrices
A,G, JacobianJ, and the Hessians of the nonlinear equality constraint functions. The latter can be extracted using the methodget_H_single(). For these Hessian matrices, only one element of each off-diagonal pair should be stored. After all buses and time periods have been processed, PFNET automatically makes these constraint Hessians lower triangular by swapping elements as necessary. If the constraint has extra variables, this method is responsible for updating the contents of the vectorsinit_extra_vars,u_extra_vars, andl_extra_vars, which provide initial values, upper bounds, and lower bounds for the extra variables.
- This method is called for every bus and time period, and is responsible for storing the structural or constant parts of the matrices
eval_stepstore_sens_step- This method is called for every bus and time period, and is used for storing constraint sensitivity information in the network components and will be documented in the near future. It can be left empty for now.
A template for creating a custom constraint is provided below. The argument bus is an AC bus whereas the argument busdc is an HVDC bus. For each function call, exactly one of them is given and the other is None.
from pfnet import CustomConstraint
class DummyConstraint(CustomConstraint):
def init(self):
self.name = "dummy constraint"
def count_step(self, bus, busdc, t):
pass
def analyze_step(self, bus, busdc, t):
pass
def eval_step(self, bus, busdc, t, x, y):
pass
def store_sens_step(self, bus, busdc, t, sA, sf, sGu, sGl):
pass
An example of a custom constraint that constructs the DC power balance equations can be found here.
Note
Nonlinear constraints implemented in Python will likely be very slow. Therefore, it is recommended to write such constraints directly in C. The procedure of adding constraints in C is similar to the one outlined above. In particular, the same methods need to be provided. Examples of constraints written in C can be found in this folder.