diff --git a/docs/src/index.md b/docs/src/index.md index 318afcd77..89a42dc29 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,7 +1,7 @@ JuliaGrid ============= -JuliaGrid is a fast, flexible and easy-to-use open-source tool for analysis and modification of power system configurations and measurement data. It represents a comprehensive framework for steady-state power system analysis written in the Julia programming language. The framework is available as a Julia package under MIT License. JuliaGrid is primarily designed for researchers and academics, providing various state-of-the-art algorithms. +JuliaGrid is a fast, flexible, and easy-to-use open-source tool for analyzing and modifying power system configurations and measurement data. It represents a comprehensive framework for steady-state power system analysis written in the Julia programming language. The framework is available as a Julia package under MIT License. JuliaGrid is primarily designed for researchers and academics, providing various state-of-the-art algorithms. The framework's architecture centres around code-reusability paradigm, allowing users a high level of customization for their experiments. To simplify, the overall logic for setting the experiments and its analysis can be as follows: * Users define a power system with/without measurement data. diff --git a/docs/src/manual/acOptimalPowerFlow.md b/docs/src/manual/acOptimalPowerFlow.md index 0c44dbead..e63f66b38 100644 --- a/docs/src/manual/acOptimalPowerFlow.md +++ b/docs/src/manual/acOptimalPowerFlow.md @@ -13,20 +13,8 @@ After obtaining the AC optimal power flow solution, JuliaGrid offers post-proces * [`power!`](@ref power!(::PowerSystem, ::ACPowerFlow)), * [`current!`](@ref current!(::PowerSystem, ::ACPowerFlow)). -Furthermore, there are specialized functions dedicated to calculating specific types of powers related to particular buses and branches: -* [`injectionPower`](@ref injectionPower(::PowerSystem, ::AC)), -* [`supplyPower`](@ref supplyPower(::PowerSystem, ::ACPowerFlow)), -* [`shuntPower`](@ref shuntPower(::PowerSystem, ::AC)), -* [`fromPower`](@ref fromPower(::PowerSystem, ::AC)), -* [`toPower`](@ref toPower(::PowerSystem, ::AC)), -* [`seriesPower`](@ref seriesPower(::PowerSystem, ::AC)), -* [`chargingPower`](@ref chargingPower(::PowerSystem, ::AC)). +Additionally, specialized functions are available for calculating specific types of [powers](@ref ACPowerAnalysisAPI) or [currents](@ref ACCurrentAnalysisAPI) for individual buses, branches, or generators. -Likewise, there are specialized functions dedicated to calculating specific types of currents related to particular buses or branches: -* [`injectionCurrent`](@ref injectionCurrent(::PowerSystem, ::AC)), -* [`fromCurrent`](@ref fromCurrent(::PowerSystem, ::AC)), -* [`toCurrent`](@ref toCurrent(::PowerSystem, ::AC)), -* [`seriesCurrent`](@ref seriesCurrent(::PowerSystem, ::AC)). --- @@ -86,6 +74,15 @@ fieldnames(typeof(analysis.method.variable)) --- +##### Variable Names +Users have the option to define custom variable names for printing and writing equations, which can help present them in a more compact form. For example: +```@example ACOptimalPowerFlow +analysis = acOptimalPowerFlow(system, Ipopt.Optimizer; magnitude = "V", angle = "θ") +nothing # hide +``` + +--- + ##### Add Variables The user has the ability to easily add new variables to the defined AC optimal power flow model by using the [`@variable`](https://jump.dev/JuMP.jl/stable/api/JuMP/#JuMP.@variable) macro from the JuMP package. Here is an example: ```@example ACOptimalPowerFlow diff --git a/docs/src/manual/acPowerFlow.md b/docs/src/manual/acPowerFlow.md index 48f831102..14293e0ab 100644 --- a/docs/src/manual/acPowerFlow.md +++ b/docs/src/manual/acPowerFlow.md @@ -19,21 +19,7 @@ After obtaining the AC power flow solution, JuliaGrid offers post-processing ana * [`power!`](@ref power!(::PowerSystem, ::ACPowerFlow)), * [`current!`](@ref current!(::PowerSystem, ::AC)). -Furthermore, there are specialized functions dedicated to calculating specific types of powers related to particular buses, branches, or generators: -* [`injectionPower`](@ref injectionPower(::PowerSystem, ::AC)), -* [`supplyPower`](@ref supplyPower(::PowerSystem, ::ACPowerFlow)), -* [`shuntPower`](@ref shuntPower(::PowerSystem, ::AC)), -* [`fromPower`](@ref fromPower(::PowerSystem, ::AC)), -* [`toPower`](@ref toPower(::PowerSystem, ::AC)), -* [`seriesPower`](@ref seriesPower(::PowerSystem, ::AC)), -* [`chargingPower`](@ref chargingPower(::PowerSystem, ::AC)), -* [`generatorPower`](@ref generatorPower(::PowerSystem, ::ACPowerFlow)). - -Likewise, there are specialized functions dedicated to calculating specific types of currents related to particular buses or branches: -* [`injectionCurrent`](@ref injectionCurrent(::PowerSystem, ::AC)), -* [`fromCurrent`](@ref fromCurrent(::PowerSystem, ::AC)), -* [`toCurrent`](@ref toCurrent(::PowerSystem, ::AC)), -* [`seriesCurrent`](@ref seriesCurrent(::PowerSystem, ::AC)). +Additionally, specialized functions are available for calculating specific types of [powers](@ref ACPowerAnalysisAPI) or [currents](@ref ACCurrentAnalysisAPI) for individual buses, branches, or generators. --- @@ -50,13 +36,12 @@ addBus!(system; label = "Bus 3", type = 2) addGenerator!(system; bus = "Bus 2") -acModel!(system) analysis = newtonRaphson(system) ``` Initially, `Bus 1` is set as the slack bus (`type = 3`), and `Bus 2` and `Bus 3` are generator buses (`type = 2`). However, `Bus 3` does not have a generator, and JuliaGrid considers this a mistake and changes the corresponding bus to a demand bus (`type = 1`). -After this step, JuliaGrid verifies the slack bus. Initially, the slack bus (`type = 3`) corresponds to `Bus 1`, but since it does not have an in-service generator connected to it, JuliaGrid recognizes it as an error. Therefore, JuliaGrid assigns a new slack bus from the available generator buses (`type = 2`) that have connected in-service generators. In this specific example, `Bus 2` becomes the new slack bus. +After this step, JuliaGrid verifies the slack bus. Initially, the slack bus (`type = 3`) corresponds to `Bus 1`, but since it does not have an in-service generator connected to it, JuliaGrid recognizes it as another mistake. Therefore, JuliaGrid assigns a new slack bus from the available generator buses (`type = 2`) that have connected in-service generators. In this specific example, `Bus 2` becomes the new slack bus. ```@setup busType using JuliaGrid @@ -90,7 +75,7 @@ Note that, if a bus is initially defined as the demand bus (`type = 1`) and late --- ## [Setup Starting Voltages](@id SetupStartingVoltagesManual) -To begin analyzing the AC power flow in JuliaGrid, we must first establish the `PowerSystem` type and define the AC model by calling the [`acModel!`](@ref acModel!) function. Once the power system is set up, we can select one of the available methods for solving the AC power flow problem, such as [`newtonRaphson`](@ref newtonRaphson), [`fastNewtonRaphsonBX`](@ref fastNewtonRaphsonBX), [`fastNewtonRaphsonXB`](@ref fastNewtonRaphsonXB), or [`gaussSeidel`](@ref gaussSeidel). +To begin analyzing the AC power flow in JuliaGrid, we must first establish the `PowerSystem` type. Once the power system is set up, we can select one of the available methods for solving the AC power flow problem, such as [`newtonRaphson`](@ref newtonRaphson), [`fastNewtonRaphsonBX`](@ref fastNewtonRaphsonBX), [`fastNewtonRaphsonXB`](@ref fastNewtonRaphsonXB), or [`gaussSeidel`](@ref gaussSeidel). Assuming we have selected the Newton-Raphson method, we can use the following code snippet: ```@example initializeACPowerFlow @@ -113,7 +98,7 @@ analysis = newtonRaphson(system) nothing # hide ``` -Here, in this code snippet, the function [`newtonRaphson`](@ref newtonRaphson) generates starting voltage vectors in polar coordinates. +Here, the function [`newtonRaphson`](@ref newtonRaphson) generates starting voltage vectors in polar coordinates. The starting voltage magnitudes are set to: ```@repl initializeACPowerFlow @@ -185,7 +170,7 @@ Consequently, the iteration begins with a fixed set of voltage magnitude values --- ## [Power Flow Solution](@id ACPowerFlowSolutionManual) -To solve the AC power flow problem using JuliaGrid, we first need to create the `PowerSystem` type and define the AC model by calling the [`acModel!`](@ref acModel!) function. Here is an example: +To start, we will create a power system and define the AC model by invoking the [`acModel!`](@ref acModel!) function: ```@example ACPowerFlowSolution using JuliaGrid # hide @default(unit) # hide @@ -223,7 +208,7 @@ nothing # hide ```julia DCPowerFlowSolution analysis = newtonRaphson(system, QR) ``` - It is important to note that the capability to change the factorization method is exclusively available for the Newton-Raphson and fast Newton-Raphson methods. + The capability to change the factorization method is exclusively available for the Newton-Raphson and fast Newton-Raphson methods. This function sets up the desired method for an iterative process based on two functions: [`mismatch!`](@ref mismatch!(::PowerSystem, ::ACPowerFlow{NewtonRaphson})) and [`solve!`](@ref solve!(::PowerSystem, ::ACPowerFlow{NewtonRaphson})). The [`mismatch!`](@ref mismatch!(::PowerSystem, ::ACPowerFlow{NewtonRaphson})) function calculates the active and reactive power injection mismatches using the given voltage magnitudes and angles, while [`solve!`](@ref solve!(::PowerSystem, ::ACPowerFlow{NewtonRaphson})) computes the voltage magnitudes and angles. @@ -275,7 +260,7 @@ nothing # hide The [`mismatch!`](@ref mismatch!(::PowerSystem, ::ACPowerFlow{NewtonRaphson})) function returns the maximum absolute values of active and reactive power injection mismatches, which are commonly used as a convergence criterion in iterative AC power flow algorithms. Note that the function can also be used to terminate the loop when using the Gauss-Seidel method, even though it is not required. !!! tip "Tip" - To ensure an accurate count of iterations, it is important for the user to place the iteration counter after the condition expressions within the if construct. Counting the iterations before this point can result in an incorrect number of iterations, as it leads to an additional iteration being performed. + To ensure an accurate count of iterations, the user should place the iteration counter after the condition expressions within the if construct. --- diff --git a/docs/src/manual/dcPowerFlow.md b/docs/src/manual/dcPowerFlow.md index c4bcf436c..78c09e359 100644 --- a/docs/src/manual/dcPowerFlow.md +++ b/docs/src/manual/dcPowerFlow.md @@ -10,12 +10,7 @@ To solve the DC power flow problem and acquire bus voltage angles, make use of t After obtaining the solution for DC power flow, JuliaGrid offers a post-processing analysis function to compute active powers associated with buses, branches, and generators: * [`power!`](@ref power!(::PowerSystem, ::DCPowerFlow)). -Additionally, there are specialized functions dedicated to calculating specific types of active powers related to particular buses, branches, or generators: -* [`injectionPower`](@ref injectionPower(::PowerSystem, ::DCPowerFlow)), -* [`supplyPower`](@ref supplyPower(::PowerSystem, ::DCPowerFlow)), -* [`fromPower`](@ref fromPower(::PowerSystem, ::DCPowerFlow)), -* [`toPower`](@ref toPower(::PowerSystem, ::DCPowerFlow)), -* [`generatorPower`](@ref generatorPower(::PowerSystem, ::DCPowerFlow)). +Additionally, specialized functions are available for calculating specific types of [powers](@ref DCPowerAnalysisAPI) for individual buses, branches, or generators. --- @@ -37,7 +32,7 @@ dcModel!(system) analysis = dcPowerFlow(system) ``` -In this example, the slack bus (`type = 3`) corresponds to the `Bus 1`. However, this bus does not have an in-service generator connected to it. Consequently, JuliaGrid recognizes this as an error and attempts to assign a new slack bus from the available generator buses (`type = 2`) that have connected in-service generators. In this particular example, the `Bus 3` will become the new slack bus. As a result, we can observe the updated array of bus types: +In this example, the slack bus (`type = 3`) corresponds to the `Bus 1`. However, this bus does not have an in-service generator connected to it. JuliaGrid considers this a mistake and attempts to assign a new slack bus from the available generator buses (`type = 2`) that have connected in-service generators. In this particular example, the `Bus 3` will become the new slack bus. As a result, we can observe the updated array of bus types: ```@setup busType using JuliaGrid # hide @default(unit) # hide diff --git a/docs/src/manual/measurementModel.md b/docs/src/manual/measurementModel.md index c3792caa0..45a471c46 100644 --- a/docs/src/manual/measurementModel.md +++ b/docs/src/manual/measurementModel.md @@ -665,7 +665,7 @@ This procedure is applicable to all measurement devices, including voltmeters, a --- -##### Load and Save Power System Data +##### Load and Save Measurement Data When saving the measurements to an HDF5 file, the label type (strings or integers) will match the type chosen during system setup. Likewise, when loading data from an HDF5 file, the label type will be preserved as saved, regardless of what is set by the [`@labels`](@ref @labels) macro. diff --git a/docs/src/manual/powerSystemModel.md b/docs/src/manual/powerSystemModel.md index 059b2b4fa..7f840dedd 100644 --- a/docs/src/manual/powerSystemModel.md +++ b/docs/src/manual/powerSystemModel.md @@ -77,7 +77,7 @@ The `PowerSystem` type stores all electrical quantities in per-units and radians --- ##### Change Base Unit Prefixes -As an example, the user can retrieve the base power and base voltage values along with their respective units, as shown below: +The user can retrieve the base power and base voltage values along with their respective units: ```@repl buildModelScratch system.base.power.value, system.base.power.unit system.base.voltage.value, system.base.voltage.unit @@ -95,17 +95,15 @@ system.base.power.value, system.base.power.unit system.base.voltage.value, system.base.voltage.unit ``` -Therefore, by using the [`@base`](@ref @base) macro to modify the prefixes of the base units, users can convert the output data from various analyses to specific units with the desired prefixes. - --- ## [Save Model](@id SaveModelManual) -Once the `PowerSystem` type has been created using one of the methods outlined in [Build Model](@ref BuildModelManual), the current data can be stored in the HDF5 file by using the [`savePowerSystem`](@ref savePowerSystem) function: +Once the `PowerSystem` type has been created using one of the methods outlined in [Build Model](@ref BuildModelManual), the data can be stored in the HDF5 file by using the [`savePowerSystem`](@ref savePowerSystem) function: ```julia savePowerSystem(system; path = "C:/matpower/case14.h5", reference = "IEEE 14-bus test case") ``` -All electrical quantities saved in the HDF5 file are in per-units and radians, except for base values for power and voltages, which are given in volt-amperes and volts. It is important to note that even if the user modifies the base units using the [`@base`](@ref @base) macro, the units will still be saved in the default settings. +All electrical quantities saved in the HDF5 file are in per-units and radians, except for base values for power and voltages, which are given in volt-amperes and volts. Note that even if the user modifies the base units using the [`@base`](@ref @base) macro, the units will still be saved with the default settings. --- @@ -247,7 +245,7 @@ nothing # hide In the above code, we add the generator to the `Bus 2`, with active and reactive power outputs set to: ```@repl addGenerator -[system.generator.output.active system.generator.output.reactive] +system.generator.output.active, system.generator.output.reactive ``` Similar to buses and branches, the input units can be changed to units other than per-units using different macros. @@ -298,7 +296,8 @@ nothing # hide This code example involves two uses of the [`addBus!`](@ref addBus!) and [`addBranch!`](@ref addBranch!) functions. In the first use, the functions rely on the default values set by the templates created with the [`@bus`](@ref @bus) and [`@branch`](@ref @branch) macros. In contrast, the second use passes specific values that match the keywords used in the templates. As a result, the templates are ignored: ```@repl CreateBusTemplate system.bus.layout.type -[system.bus.demand.active system.branch.parameter.reactance] +system.bus.demand.active +system.branch.parameter.reactance ``` In the given example, the [`@generator`](@ref @generator) macro is utilized instead of repeatedly specifying the `magnitude` keyword in the [`addGenerator!`](@ref addGenerator!) function. This macro creates a generator template with a default value for `magnitude`, which is automatically applied every time the [`addGenerator!`](@ref addGenerator!) function is called. Therefore, it eliminates the requirement to set the magnitude value for each individual generator: @@ -309,7 +308,7 @@ system.generator.voltage.magnitude --- ##### Customizing Input Units for Keywords -JuliaGrid requires users to specify electrical quantity-related keywords in per-units (pu) and radians (rad) by default. However, it provides macros, such as [`@power`](@ref @power), that allow users to specify other units: +Templates can also be defined using a custom unit system, for example: ```@example CreateBusTemplateUnits using JuliaGrid # hide @@ -326,7 +325,8 @@ nothing # hide In this example, we create the bus template and one bus using SI power units, and then we switch to per-units and add the second bus. It is important to note that once the template is defined in any unit system, it remains valid regardless of subsequent unit system changes. The resulting power values are: ```@repl CreateBusTemplateUnits -[system.bus.demand.active system.bus.demand.reactive] +system.bus.demand.active +system.bus.demand.reactive ``` Thus, JuliaGrid automatically tracks the unit system used to create templates and provides the appropriate conversion to per-units and radians. Even if the user switches to a different unit system later on, the previously defined template will still be valid. @@ -359,7 +359,7 @@ nothing # hide As we have shown, JuliaGrid mandates a distinctive label for every bus, branch, or generator. These labels are stored in ordered dictionaries, functioning as pairs of strings and integers. The string signifies the exclusive label for the specific component, whereas the integer maintains an internal numbering of buses, branches, or generators. !!! tip "Tip" - String labels improve readability, but in larger models, the overhead from using strings can become substantial. To reduce memory usage and the number of allocations, users can configure ordered dictionaries to accept and store integers as labels: + String labels improve readability, but in larger models, the overhead from using strings can become substantial. To reduce memory usage, users can configure ordered dictionaries to accept and store integers as labels: ```julia DCPowerFlowSolution @labels(Integer) ``` @@ -462,7 +462,7 @@ For instance, the bus labels can be accessed using the variable: system.bus.label ``` -If the objective is to obtain labels in the same order as the bus definitions sequence, users can utilize the following: +If the objective is to obtain only labels, users can utilize the following: ```@repl RetrieveLabels label = collect(keys(system.bus.label)) ``` @@ -474,7 +474,7 @@ Moreover, the `from` and `to` keywords associated with branches are stored based [system.branch.layout.from system.branch.layout.to] ``` -To recover the original `from` and `to` labels, we can utilize the following method: +To recover the original `from` and `to` labels, we can utilize: ```@repl RetrieveLabels [label[system.branch.layout.from] label[system.branch.layout.to]] ``` @@ -484,7 +484,7 @@ Similarly, the `bus` keywords related to generators are saved based on internall system.generator.layout.bus ``` -To recover the original `bus` labels, we can utilize the following method: +To recover the original `bus` labels, we can utilize: ```@repl RetrieveLabels label[system.generator.layout.bus] ``` @@ -497,7 +497,7 @@ label[system.generator.layout.bus] --- -##### Load and Save Power System Data +##### Loading and Saving Labels When a user loads a power system from a Matpower file, the default behavior is to store labels as strings. However, this can be overridden by using the [`@labels`](@ref @labels) macro to store labels as integers. When saving the power system to an HDF5 file, the label type (strings or integers) will match the type chosen during system setup. Likewise, when loading data from an HDF5 file, the label type will be preserved as saved, regardless of what is set by the [`@labels`](@ref @labels) macro. @@ -710,7 +710,7 @@ In essence, what we have accomplished is the establishment of a cost function de The default input units are in per-units (pu), with coefficients of the cost function having units of currency/pu²-hr for 1100, currency/pu-hr for 500, and currency/hr for 150. Therefore, the coefficients are stored exactly as entered: ```@repl addActiveCost -system.generator.cost.active.polynomial +system.generator.cost.active.polynomial[1] ``` By setting `active = 2` within the function, we express our intent to specify the active power cost using the `active` key. By using a value of `2`, we signify our preference for employing a polynomial cost model for the associated generator. This flexibility is neccessary when we have also previously defined a piecewise linear cost function for the same generator. In such cases, we can set `active = 1` to utilize the piecewise linear cost function to represent the cost of the corresponding generators. Thus, we retain the freedom to choose between these two cost functions according to the requirements of our simulation. Additionally, users have the option to define both piecewise and polynomial costs within a single function call, further enhancing the versatility of the implementation. @@ -726,13 +726,13 @@ nothing # hide The first column denotes the generator's output reactive powers in per-units, while the second column specifies the corresponding costs for the specified reactive power in currency/hr. Thus, the data is stored exactly as entered: ```@repl addActiveCost -system.generator.cost.reactive.piecewise +system.generator.cost.reactive.piecewise[1] ``` --- ##### Customizing Input Units for Keywords -Changing input units from per-units (pu) can be particularly useful since cost functions are usually related to SI units of powers. Let us set active powers in megawatts (MW) and reactive powers in megavolt-amperes reactive (MVAr) : +Changing input units from per-units (pu) can be particularly useful since cost functions are usually related to SI units. Let us set active powers in megawatts (MW) and reactive powers in megavolt-amperes reactive (MVAr) : ```@example addActiveCost @power(MW, MVAr, pu) nothing # hide @@ -745,7 +745,7 @@ cost!(system; label = "Generator 1", active = 2, polynomial = [0.11; 5.0; 150.0] After inspecting the resulting cost data, we can see that it is the same as before: ```@repl addActiveCost -system.generator.cost.active.polynomial +system.generator.cost.active.polynomial[1] ``` Similarly, we can define the linear piecewise cost using megavolt-amperes reactive: @@ -756,7 +756,7 @@ nothing # hide Upon inspection, we can see that the stored data is the same as before: ```@repl addActiveCost -system.generator.cost.reactive.piecewise +system.generator.cost.reactive.piecewise[1] ``` !!! tip "Tip" diff --git a/src/optimalPowerFlow/acOptimalPowerFlow.jl b/src/optimalPowerFlow/acOptimalPowerFlow.jl index 814a217dc..e16e1d2a9 100644 --- a/src/optimalPowerFlow/acOptimalPowerFlow.jl +++ b/src/optimalPowerFlow/acOptimalPowerFlow.jl @@ -1,5 +1,6 @@ """ - acOptimalPowerFlow(system::PowerSystem, optimizer; bridge, name) + acOptimalPowerFlow(system::PowerSystem, optimizer; bridge, name, + magnitude, angle, active, reactive) The function sets up the optimization model for solving the AC optimal power flow problem. @@ -21,6 +22,10 @@ However, certain configurations may require different method calls, such as: - `bridge`: manage the bridging mechanism (default: `false`), - `name`: manage the creation of string names (default: `true`). +Additionally, users can modify variable names used for printing and writing through the +keywords `magnitude`, `angle`, `active`, and `reactive`. For instance, users can choose +`magnitude = "V"` and `angle = "θ"` to display equations in a more readable format. + # Returns The function returns an instance of the `ACOptimalPowerFlow` type, which includes the following fields: @@ -41,7 +46,11 @@ function acOptimalPowerFlow( system::PowerSystem, @nospecialize optimizerFactory; bridge::Bool = false, - name::Bool = true + name::Bool = true, + magnitude::String = "magnitude", + angle::String = "angle", + active::String = "active", + reactive::String = "reactive" ) branch = system.branch bus = system.bus @@ -57,10 +66,12 @@ function acOptimalPowerFlow( jump = JuMP.Model(optimizerFactory; add_bridges = bridge) set_string_names_on_creation(jump, name) - active = @variable(jump, active[i = 1:gen.number]) - reactive = @variable(jump, reactive[i = 1:gen.number]) - magnitude = @variable(jump, magnitude[i = 1:bus.number]) - angle = @variable(jump, angle[i = 1:bus.number]) + active = @variable(jump, active[i = 1:gen.number], base_name = active) + reactive = @variable(jump, reactive[i = 1:gen.number], base_name = reactive) + magnitude = @variable(jump, magnitude[i = 1:bus.number], base_name = magnitude) + @time angle = @variable(jump, angle[i = 1:bus.number], base_name = angle) + + fix(angle[bus.layout.slack], bus.voltage.angle[bus.layout.slack]) slack = Dict(bus.layout.slack => FixRef(angle[bus.layout.slack])) diff --git a/src/optimalPowerFlow/dcOptimalPowerFlow.jl b/src/optimalPowerFlow/dcOptimalPowerFlow.jl index feec7224f..ef469eda1 100644 --- a/src/optimalPowerFlow/dcOptimalPowerFlow.jl +++ b/src/optimalPowerFlow/dcOptimalPowerFlow.jl @@ -1,5 +1,5 @@ """ - dcOptimalPowerFlow(system::PowerSystem, optimizer; bridge = false, name = true) + dcOptimalPowerFlow(system::PowerSystem, optimizer; bridge, name, angle, active) The function sets up the optimization model for solving the DC optimal power flow problem. @@ -21,6 +21,10 @@ However, certain configurations may require different method calls, such as: - `bridge`: manage the bridging mechanism (default: `false`), - `name`: manage the creation of string names (default: `true`). +Additionally, users can modify variable names used for printing and writing through the +keywords `angle` and `active`. For instance, users can choose `angle = "θ"` to display +equations in a more readable format. + # Returns The function returns an instance of the `DCOptimalPowerFlow` type, which includes the following fields: @@ -40,7 +44,9 @@ function dcOptimalPowerFlow( system::PowerSystem, @nospecialize optimizerFactory; bridge::Bool = false, - name::Bool = true + name::Bool = true, + angle::String = "angle", + active::String = "active", ) bus = system.bus gen = system.generator @@ -53,8 +59,8 @@ function dcOptimalPowerFlow( jump = JuMP.Model(optimizerFactory; add_bridges = bridge) set_string_names_on_creation(jump, name) - active = @variable(jump, active[i = 1:gen.number]) - angle = @variable(jump, angle[i = 1:bus.number]) + active = @variable(jump, active[i = 1:gen.number], base_name = active) + angle = @variable(jump, angle[i = 1:bus.number], base_name = angle) fix(angle[bus.layout.slack], bus.voltage.angle[bus.layout.slack]) slack = Dict(bus.layout.slack => FixRef(angle[bus.layout.slack])) diff --git a/src/powerSystem/bus.jl b/src/powerSystem/bus.jl index bfc635ef5..71a00c8ac 100644 --- a/src/powerSystem/bus.jl +++ b/src/powerSystem/bus.jl @@ -104,12 +104,12 @@ function addBus!(system::PowerSystem; label::IntStrMiss = missing, kwargs...) if !isempty(system.model.ac.nodalMatrix) acModelEmpty!(system.model.ac) - @info("The current AC model has been completely erased.") + @info("The AC model has been completely erased.") end if !isempty(system.model.dc.nodalMatrix) dcModelEmpty!(system.model.dc) - @info("The current DC model has been completely erased.") + @info("The DC model has been completely erased.") end end