From 50ebe264e9e91891621f47a911cf5c85bb65f924 Mon Sep 17 00:00:00 2001 From: kdayday Date: Sat, 17 Aug 2024 11:14:36 -0600 Subject: [PATCH 01/43] Consolidate IS TS docs in API, add TimeSeriesKey, remove valuecurve page ref --- docs/src/api/public.md | 48 ++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/docs/src/api/public.md b/docs/src/api/public.md index 95fc0f4d66..59c2faa7cd 100644 --- a/docs/src/api/public.md +++ b/docs/src/api/public.md @@ -22,14 +22,13 @@ Pages = ["PowerSystems.jl", "static_injection_subsystem.jl", "dynamic_models.jl", "operational_cost.jl", - "cost_functions/ValueCurves.jl", "cost_function_timeseries.jl", "definitions.jl"] Public = true Private = false ``` -## TimeSeries +## Time Series ```@autodocs Modules = [InfrastructureSystems] @@ -38,7 +37,8 @@ Pages = ["abstract_time_series.jl", "probabilistic.jl", "scenarios.jl", "single_time_series.jl", - "forecasts.jl"] + "forecasts.jl", + ] Order = [:type, :function] Filter = t -> t ∉ [InfrastructureSystems.get_internal, InfrastructureSystems.set_internal!] @@ -46,10 +46,27 @@ Filter = t -> t ∉ [InfrastructureSystems.get_internal, ```@autodocs Modules = [InfrastructureSystems] -Pages = ["time_series_cache.jl"] +Pages = ["time_series_cache.jl", + "time_series_interface.jl", + "time_series_structs.jl", + "time_series_storage.jl", + "utils/print.jl"] Order = [:type, :function] -Filter = t -> t ∈ [InfrastructureSystems.get_next_time_series_array!, - InfrastructureSystems.get_next_time] +Filter = t -> t ∈ [InfrastructureSystems.ForecastCache, + InfrastructureSystems.StaticTimeSeriesCache, + InfrastructureSystems.TimeSeriesKey, + InfrastructureSystems.TimeSeriesAssociation, + InfrastructureSystems.CompressionSettings + InfrastructureSystems.get_next_time_series_array!, + InfrastructureSystems.get_next_time + InfrastructureSystems.get_time_series, + InfrastructureSystems.get_time_series_array, + InfrastructureSystems.reset!, + InfrastructureSystems.get_time_series_timestamps, + InfrastructureSystems.get_time_series_values, + InfrastructureSystems.show_time_series, + InfrastructureSystems.get_time_series_keys + ] ``` ## System @@ -98,25 +115,6 @@ Public = true Private = false ``` -```@autodocs -Modules = [InfrastructureSystems] -Pages = ["time_series_interface.jl", "time_series_structs.jl", - "time_series_storage.jl", "utils/print.jl", - "time_series_cache.jl"] -Filter = t -> t ∈ [InfrastructureSystems.get_time_series, - InfrastructureSystems.get_time_series_array, - InfrastructureSystems.reset!, - InfrastructureSystems.get_time_series_timestamps, - InfrastructureSystems.get_time_series_values, - InfrastructureSystems.show_time_series, - InfrastructureSystems.get_time_series_keys, - InfrastructureSystems.TimeSeriesAssociation, - InfrastructureSystems.ForecastCache, - InfrastructureSystems.StaticTimeSeriesCache, - InfrastructureSystems.CompressionSettings - ] -``` - ## Parsing ```@autodocs From 7e6e5a370c4c401713c72d97ddd0c5954d13c97f Mon Sep 17 00:00:00 2001 From: kdayday Date: Sat, 17 Aug 2024 11:58:56 -0600 Subject: [PATCH 02/43] Bug fix --- docs/src/api/public.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/api/public.md b/docs/src/api/public.md index 59c2faa7cd..deb2668ae7 100644 --- a/docs/src/api/public.md +++ b/docs/src/api/public.md @@ -56,9 +56,9 @@ Filter = t -> t ∈ [InfrastructureSystems.ForecastCache, InfrastructureSystems.StaticTimeSeriesCache, InfrastructureSystems.TimeSeriesKey, InfrastructureSystems.TimeSeriesAssociation, - InfrastructureSystems.CompressionSettings + InfrastructureSystems.CompressionSettings, InfrastructureSystems.get_next_time_series_array!, - InfrastructureSystems.get_next_time + InfrastructureSystems.get_next_time, InfrastructureSystems.get_time_series, InfrastructureSystems.get_time_series_array, InfrastructureSystems.reset!, From ac96a253f6ba2dc43fbe425422c3a8b134b5be51 Mon Sep 17 00:00:00 2001 From: kdayday Date: Sat, 17 Aug 2024 12:22:44 -0600 Subject: [PATCH 03/43] Add staticTimeSeries to API and remove IS TS filters to simplify tracking --- docs/src/api/public.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/docs/src/api/public.md b/docs/src/api/public.md index deb2668ae7..d86a66353b 100644 --- a/docs/src/api/public.md +++ b/docs/src/api/public.md @@ -36,6 +36,7 @@ Pages = ["abstract_time_series.jl", "deterministic.jl", "probabilistic.jl", "scenarios.jl", + "static_time_series.jl", "single_time_series.jl", "forecasts.jl", ] @@ -52,21 +53,6 @@ Pages = ["time_series_cache.jl", "time_series_storage.jl", "utils/print.jl"] Order = [:type, :function] -Filter = t -> t ∈ [InfrastructureSystems.ForecastCache, - InfrastructureSystems.StaticTimeSeriesCache, - InfrastructureSystems.TimeSeriesKey, - InfrastructureSystems.TimeSeriesAssociation, - InfrastructureSystems.CompressionSettings, - InfrastructureSystems.get_next_time_series_array!, - InfrastructureSystems.get_next_time, - InfrastructureSystems.get_time_series, - InfrastructureSystems.get_time_series_array, - InfrastructureSystems.reset!, - InfrastructureSystems.get_time_series_timestamps, - InfrastructureSystems.get_time_series_values, - InfrastructureSystems.show_time_series, - InfrastructureSystems.get_time_series_keys - ] ``` ## System From 28b84b18969b89e06c5880aedb375dd902e94fbd Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 19 Aug 2024 16:57:08 -0600 Subject: [PATCH 04/43] Move trouble-shooting serialization issues under custom components --- docs/make.jl | 2 +- docs/src/how_to/add_new_types.md | 23 +++++++++++++++++++++++ docs/src/how_to/serialize_data.md | 23 ----------------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index ed3561a2a3..e9cf5c24bf 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -25,7 +25,7 @@ pages = OrderedDict( "...add an Operating Cost" => "how_to/add_cost_curve.md", "...add a market bid" => "how_to/market_bid_cost.md", "...add additional data to a component" => "how_to/adding_additional_fields.md", - "...add a new Type" => "how_to/add_new_types.md", + "...customize or add a new Type" => "how_to/add_new_types.md", "...improve performance with time series data" => "how_to/improve_ts_performance.md", "...serialize data to a JSON" => "how_to/serialize_data.md", "...reduce REPL printing" => "how_to/reduce_repl_printing.md", diff --git a/docs/src/how_to/add_new_types.md b/docs/src/how_to/add_new_types.md index 0a4c8e2a4a..03e9780a04 100644 --- a/docs/src/how_to/add_new_types.md +++ b/docs/src/how_to/add_new_types.md @@ -111,6 +111,29 @@ Refer to `InfrastructureSystems.serialize_struct` for example behavior. New structs that are not subtypes of `InfrastructureSystemsType` may be able to call it directly. +#### How to trouble-shoot serialization issues + +Here are some examples of potential problems and solutions if you need to implement custom +`InfrastructureSystems.serialize` and `InfrastructureSystems.deserialize` methods +for your type.: + +**Problem**: Your struct contains a field defined as an abstract type. The +deserialization process doesn't know what concrete type to construct. + +*Solution*: Encode the concrete type into the serialized dictionary as a string. + +*Example*: `serialize` and `deserialize` methods for `DynamicBranch` in +`src/models/dynamic_branch.jl`. + +**Problem**: Similar to above in that a field is defined as an abstract type +but the struct is parameterized on the actual concrete type. + +*Solution*: Use the fact that the concrete type is encoded into the serialized +type of the struct and extract it in a customized `deserialze` method. + +*Example*: `deserialize` method for `OuterControl` in +`src/models/OuterControl.jl`. + ### Adding `PowerSystems.jl` as a dependency in a modeling package ```julia diff --git a/docs/src/how_to/serialize_data.md b/docs/src/how_to/serialize_data.md index 148b3fdf55..d0bb9c127b 100644 --- a/docs/src/how_to/serialize_data.md +++ b/docs/src/how_to/serialize_data.md @@ -26,26 +26,3 @@ to_json(sys, path) ```@repl serialize_data sys2 = System(path) ``` - -## How to trouble-shoot serialization issues - -If this doesn't work then you likely need to implement custom -`InfrastructureSystems.serialize` and `InfrastructureSystems.deserialize` methods -for your type. Here are some examples of potential problems and solutions: - -**Problem**: Your struct contains a field defined as an abstract type. The -deserialization process doesn't know what concrete type to construct. - -*Solution*: Encode the concrete type into the serialized dictionary as a string. - -*Example*: `serialize` and `deserialize` methods for `DynamicBranch` in -`src/models/dynamic_branch.jl`. - -**Problem**: Similar to above in that a field is defined as an abstract type -but the struct is parameterized on the actual concrete type. - -*Solution*: Use the fact that the concrete type is encoded into the serialized -type of the struct and extract it in a customized `deserialze` method. - -*Example*: `deserialize` method for `OuterControl` in -`src/models/OuterControl.jl`. From 9e0dba84a7b65f555f1336aa17108a1c1229c5ec Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 19 Aug 2024 17:58:47 -0600 Subject: [PATCH 05/43] Add valuecurves to API after move to IS --- docs/make.jl | 2 -- docs/src/api/public.md | 11 +++++++++++ docs/src/how_to/add_cost_curve.md | 2 +- docs/src/model_library/cost_curves.md | 9 --------- docs/src/model_library/value_curves.md | 9 --------- 5 files changed, 12 insertions(+), 21 deletions(-) delete mode 100644 docs/src/model_library/cost_curves.md delete mode 100644 docs/src/model_library/value_curves.md diff --git a/docs/make.jl b/docs/make.jl index e9cf5c24bf..687b755b8a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -81,8 +81,6 @@ pages["Model Library"] = make_model_library( "StorageCost" =>"model_library/storage_cost.md", "LoadCost" =>"model_library/load_cost.md", "MarketBidCost" =>"model_library/market_bid_cost.md"], - "Cost Curves" => ["Variable Cost Curves" => "model_library/cost_curves.md", - "Value Curves" => "model_library/value_curves.md"] ) ) diff --git a/docs/src/api/public.md b/docs/src/api/public.md index d86a66353b..ad614f7f2b 100644 --- a/docs/src/api/public.md +++ b/docs/src/api/public.md @@ -28,6 +28,17 @@ Public = true Private = false ``` +## Operating Costs + +```@autodocs +Modules = [InfrastructureSystems] +Pages = ["production_variable_cost_curve.jl", + "cost_aliases.jl", + "value_curve.jl", + ] +Order = [:type, :function] +``` + ## Time Series ```@autodocs diff --git a/docs/src/how_to/add_cost_curve.md b/docs/src/how_to/add_cost_curve.md index 6890739be4..40eee6124e 100644 --- a/docs/src/how_to/add_cost_curve.md +++ b/docs/src/how_to/add_cost_curve.md @@ -21,7 +21,7 @@ To begin, the user must make 2 or 3 decisions before defining the operating cost as a `CostCurve`. 2. Select a [`ValueCurve`](@ref) to represent the variable cost data by comparing the format of your variable cost data to the [Variable Cost Representations table](@ref curve_table) - and the [`ValueCurve`](@ref value_curve_library) options. + and the [`ValueCurve`](@ref) options. Then, the user defines the cost by working backwards: 1. Define the variable cost's `ValueCurve` diff --git a/docs/src/model_library/cost_curves.md b/docs/src/model_library/cost_curves.md deleted file mode 100644 index 1087224137..0000000000 --- a/docs/src/model_library/cost_curves.md +++ /dev/null @@ -1,9 +0,0 @@ -# Variable Cost Curves - -```@autodocs -Modules = [PowerSystems] -Pages = ["cost_functions/variable_cost.jl"] -Order = [:type, :function] -Public = true -Private = false -``` \ No newline at end of file diff --git a/docs/src/model_library/value_curves.md b/docs/src/model_library/value_curves.md deleted file mode 100644 index 47f331bbe5..0000000000 --- a/docs/src/model_library/value_curves.md +++ /dev/null @@ -1,9 +0,0 @@ -# [`ValueCurve`s](@id value_curve_library) - -```@autodocs -Modules = [PowerSystems] -Pages = ["cost_functions/cost_aliases.jl"] -Order = [:type, :function] -Public = true -Private = false -``` \ No newline at end of file From 7251b43e742445bdfd666b52ac3009e6c346b264 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 19 Aug 2024 18:26:03 -0600 Subject: [PATCH 06/43] Add DynamicInjection devices to model library --- docs/make.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 687b755b8a..22fcc6cda2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -59,14 +59,17 @@ pages["Model Library"] = make_model_library( Topology, StaticInjection, Service, - Branch + Branch, + DynamicInjection, ], exceptions = [PSY.DynamicComponent, PSY.ActivePowerControl, PSY.ReactivePowerControl, PSY.DynamicBranch, PSY.HybridSystem, - PSY.OperationalCost + PSY.OperationalCost, + PSY.DynamicInverter, + PSY.DynamicGenerator, ], manual_additions = Dict("Service" => ["Reserves" => "model_library/reserves.md"], From 2fadabf13976006ef7b58517901ce2a53fb0e279 Mon Sep 17 00:00:00 2001 From: kdayday Date: Tue, 20 Aug 2024 16:37:13 -0600 Subject: [PATCH 07/43] Update to Documenter 1.0 enforcing everything except doc size and missing docstrings --- docs/Project.toml | 2 +- docs/make.jl | 20 ++++++++++++++++++-- docs/src/tutorials/creating_system.md | 1 - 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index d0bcc61542..3bd9f31917 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -15,6 +15,6 @@ TypeTree = "04da0e3b-1cad-4b2c-a963-fc1602baf1af" [compat] CSV = "~0.10" -Documenter = "^0.27" +Documenter = "^1.5" InfrastructureSystems = "2" julia = "^1.6" diff --git a/docs/make.jl b/docs/make.jl index 22fcc6cda2..b3311add38 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -130,10 +130,26 @@ end makedocs( modules = [PowerSystems, InfrastructureSystems], - format = Documenter.HTML(prettyurls = haskey(ENV, "GITHUB_ACTIONS"),), + format = Documenter.HTML( + prettyurls = haskey(ENV, "GITHUB_ACTIONS"), + size_threshold = nothing,), sitename = "PowerSystems.jl", authors = "Jose Daniel Lara, Daniel Thom, Kate Doubleday, and Clayton Barrows", - pages = Any[p for p in pages] + pages = Any[p for p in pages], + warnonly = Documenter.except( + :autodocs_block, + :cross_references, + :docs_block, + :doctest, + :eval_block, + :example_block, + :footnote, + :linkcheck_remotes, + :linkcheck, + :meta_block, + :parse_error, + :setup_block), + draft = false, ) deploydocs( diff --git a/docs/src/tutorials/creating_system.md b/docs/src/tutorials/creating_system.md index 4730fd2aa1..cb7c056020 100644 --- a/docs/src/tutorials/creating_system.md +++ b/docs/src/tutorials/creating_system.md @@ -328,7 +328,6 @@ and modified the `System` per-unit settings. Next, you might want to: - [Add time series data to components in the `System`](@ref tutorial_time_series) -- [Learn more about how to retrieve components and their data from a `System`](@ref get_components_tutorial) - [Import a `System` from an existing Matpower or PSSE file instead of creating it manually](@ref parsing) - [Create your own `System` from .csv files instead of creating it manually](@ref table_data) - [Read more to understand per-unitization in PowerSystems.jl](@ref per_unit) From 3db9abd156be0909a92a11d520fffc99c7af0f02 Mon Sep 17 00:00:00 2001 From: kdayday Date: Fri, 30 Aug 2024 14:08:44 -0600 Subject: [PATCH 08/43] Add next steps links in ts tutorial --- docs/src/tutorials/working_with_time_series.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/tutorials/working_with_time_series.md b/docs/src/tutorials/working_with_time_series.md index 003bed3a34..72d4475c28 100644 --- a/docs/src/tutorials/working_with_time_series.md +++ b/docs/src/tutorials/working_with_time_series.md @@ -400,7 +400,7 @@ or component fields, as well as by referencing a `StaticTimeSeries` to address m forecast data. Next you might like to: -- Parse many timeseries data sets from CSV's -- See how to improve performance efficiency with your own time series data -- Review the available time series data formats -- Learn more about how times series data is stored +- [Parse many timeseries data sets from CSV's](@ref parsing_time_series) +- [See how to improve performance efficiency with your own time series data](@ref "Improve Performance with Time Series Data") +- [Review the available time series data formats](@ref ts_data) +- [Learn more about how times series data is stored](@ref "Data Storage") From 8839020cfc5501b80e61d9a6cd5c85e6499e5f87 Mon Sep 17 00:00:00 2001 From: kdayday Date: Fri, 30 Aug 2024 14:10:31 -0600 Subject: [PATCH 09/43] Update dynamic data tutorial by merging with tutorial from PSID --- docs/make.jl | 4 +- docs/src/explanation/dynamic_data.md | 83 ++++ docs/src/explanation/example_dynamic_data.md | 46 --- docs/src/index.md | 3 +- docs/src/tutorials/add_dynamic_data.md | 391 +++++++++++++++++++ docs/src/tutorials/add_dynamic_device.md | 311 --------------- docs/src/tutorials/creating_system.md | 1 + 7 files changed, 478 insertions(+), 361 deletions(-) create mode 100644 docs/src/explanation/dynamic_data.md delete mode 100644 docs/src/explanation/example_dynamic_data.md create mode 100644 docs/src/tutorials/add_dynamic_data.md delete mode 100644 docs/src/tutorials/add_dynamic_device.md diff --git a/docs/make.jl b/docs/make.jl index b3311add38..bd72aca01d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -12,7 +12,7 @@ pages = OrderedDict( "Tutorials" => Any[ "Create and Explore a Power `System`" => "tutorials/creating_system.md", "Working with Time Series" => "tutorials/working_with_time_series.md", - "Adding dynamic devices" => "tutorials/add_dynamic_device.md", + "Adding Data for Dynamic Simulations" => "tutorials/add_dynamic_data.md", ], "How to..." => Any[ "...install PowerSystems.jl" => "how_to/install.md", @@ -37,7 +37,7 @@ pages = OrderedDict( "explanation/type_structure.md", "explanation/per_unit.md", "explanation/time_series.md", - "explanation/example_dynamic_data.md", + "explanation/dynamic_data.md", ], "Model Library" => Any[], "Reference" => diff --git a/docs/src/explanation/dynamic_data.md b/docs/src/explanation/dynamic_data.md new file mode 100644 index 0000000000..b365b671ff --- /dev/null +++ b/docs/src/explanation/dynamic_data.md @@ -0,0 +1,83 @@ +# Dynamic Devices + +## Static and Dynamic Data Layers + +`PowerSystems.jl` uses two data layers to define data for dynamic simulations: +1. [Static](@ref S) components, which includes the data needed to run a power flow problem +2. [Dynamic](@ref D) components are those that define differential equations to run a transient simulation. These dyanamic + data are attached to the static components. + +Although `PowerSystems.jl` is not constrained to only PSS/e files, commonly the data for a +dynamic simulation comes in a pair of files: One for the static data power flow case (e.g., +`.raw` file) and a second one with the dynamic components information (e.g., `.dyr` file). +However, `PowerSystems.jl` is able to take any power flow case and specify dynamic +components to it. The two data layers in `PowerSystems.jl` are similar to the data +division between those two files. + +### Layer 1: Static Components + +The first data layer contains all the information necessary to run a power flow problem: + +- Vector of `Bus` elements, that define all the buses in the network. +- Vector of `Branch` elements, that define all the branches elements (that connect two buses) in the network. +- Vector of `StaticInjection` elements, that define all the devices connected to buses that can inject (or withdraw) power. These static devices, typically generators, in `PowerSimulationsDynamics` are used to solve the Power Flow problem that determines the active and reactive power provided for each device. +- Vector of `PowerLoad` elements, that define all the loads connected to buses that can withdraw current. These are also used to solve the Power Flow. +- Vector of `Source` elements, that define source components behind a reactance that can inject or withdraw current. +- The base of power used to define per unit values, in MVA as a `Float64` value. +- The base frequency used in the system, in Hz as a `Float64` value. + +### Layer 2: Dynamic Components + +The second data layer contains the *additional* information describing the dynamic response +of certain components in the `System`. This data is all attached to components defined in +the static data layer: + +- (Optional) Selecting which of the `Lines` (of the `Branch` vector) elements must be modeled of `DynamicLines` elements, that can be used to model lines with differential equations. +- Vector of `DynamicInjection` elements. These components must be attached to a `StaticInjection` that connects the power flow solution to the dynamic formulation of such device. + +`DynamicInjection` can be `DynamicGenerator` or `DynamicInverter`, and its specific formulation (i.e. differential equations) will depend on the specific components that define each device (see the sections below). As +a result, it is possible to flexibly define dynamic data models and methods according to +the analysis requirements. [`DynamicInjection`](@ref) components use a parametric +type pattern to materialize the full specification of the dynamic injection model with +parameters. This design enable the use of parametric methods to specify the mathematical +model of the dynamic components separately. + +[`DynamicInjection`](@ref) components also implement some additional information useful for +the modeling, like the usual states assumed by the model and the number of states. These values are +derived from the documentation associated with the model, for instance PSS/e models provide +parameters, states and variables. Although `PowerSystems.jl` doesn't assume a specific +mathematical model for the components, the default values for these parameters are derived +directly from the data model source. + +## Dynamic Generator Structure + +Each generator is a data structure that is defined by the following components: + +- [Machine](@ref Machine): That defines the stator electro-magnetic dynamics. +- [Shaft](@ref Shaft): That describes the rotor electro-mechanical dynamics. +- [Automatic Voltage Regulator](@ref AVR): Electromotive dynamics to model an AVR controller. +- [Power System Stabilizer](@ref PSS): Control dynamics to define an stabilization signal for the AVR. +- [Prime Mover and Turbine Governor](@ref TurbineGov): Thermo-mechanical dynamics and associated controllers. + +```@raw html + +``` + +## Dynamic Inverter Structure + +Each inverter is a data structure that is defined by the following components: + +- [DC Source](@ref DCSource): Defines the dynamics of the DC side of the converter. +- [Frequency Estimator](@ref FrequencyEstimator): That describes how the frequency of the grid + can be estimated using the grid voltages. Typically a phase-locked loop (PLL). +- [Outer Loop Control](@ref OuterControl): That describes the active and reactive power + control dynamics. +- [Inner Loop Control](@ref InnerControl): That can describe virtual impedance, + voltage control and current control dynamics. +- [Converter](@ref Converter): That describes the dynamics of the pulse width modulation (PWM) + or space vector modulation (SVM). +- [Filter](@ref Filter): Used to connect the converter output to the grid. + +```@raw html + +``` ⠀ diff --git a/docs/src/explanation/example_dynamic_data.md b/docs/src/explanation/example_dynamic_data.md deleted file mode 100644 index b64f61a3dd..0000000000 --- a/docs/src/explanation/example_dynamic_data.md +++ /dev/null @@ -1,46 +0,0 @@ -# Dynamic Devices - -Each sub-type is composed of the corresponding dynamic components that define the model. As -a result, it is possible to flexibly define dynamic data models and methods according to -the analysis requirements. [`DynamicInjection`](@ref) components use parametric a parametric -type pattern to materialize the full specification of the dynamic injection model with -parameters. This design enable the use of parametric methods to specify the mathematical -model of the dynamic components separately. - -[`DynamicInjection`](@ref) components also implement some additional information useful for -the modeling like the usual states assumed by the model and the number. These values are -derived from the documentation associated with the model, for instance PSS/e models provide -parameters, states and variables. Although `PowerSystems.jl` doesn't assume a specific -mathematical model for the components, the default values for these parameters are derived -directly from the data model source. - -## Dynamic Generator - -Each generator is a data structure that is defined by the following components: - -- [Machine](@ref Machine): That defines the stator electro-magnetic dynamics. -- [Shaft](@ref Shaft): That describes the rotor electro-mechanical dynamics. -- [Automatic Voltage Regulator](@ref AVR): Electromotive dynamics to model an AVR controller. -- [Power System Stabilizer](@ref PSS): Control dynamics to define an stabilization signal for the AVR. -- [Prime Mover and Turbine Governor](@ref TurbineGov): Thermo-mechanical dynamics and associated controllers. - -```@raw html - -``` - -Each inverter is a data structure that is defined by the following components: - -- [DC Source](@ref DCSource): Defines the dynamics of the DC side of the converter. -- [Frequency Estimator](@ref FrequencyEstimator): That describes how the frequency of the grid - can be estimated using the grid voltages. Typically a phase-locked loop (PLL). -- [Outer Loop Control](@ref OuterControl): That describes the active and reactive power - control dynamics. -- [Inner Loop Control](@ref InnerControl): That can describe virtual impedance, - voltage control and current control dynamics. -- [Converter](@ref Converter): That describes the dynamics of the pulse width modulation (PWM) - or space vector modulation (SVM). -- [Filter](@ref Filter): Used to connect the converter output to the grid. - -```@raw html - -``` ⠀ diff --git a/docs/src/index.md b/docs/src/index.md index 1d3f6cd192..999b1632e5 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -77,8 +77,7 @@ If you are new to `PowerSystems.jl`, here's how we suggest getting started: 3. Work through the other basic tutorials based on your interests - See [Working with Time Series Data](@ref tutorial_time_series) if you will be doing production cost modeling or working with time series - - See [Creating a System with Dynamic devices](@ref) and the - [tutorial in PowerSimulationsDynamics](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/tutorials/tutorial_dynamic_data/) + - See [Adding Data for Dynamic Simulations](@ref) if you are interested in [dynamic](@ref D) simulations 4. Then, see the how-to's on parsing [Matpower](@ref pm_data) or [PSS/e files](@ref dyr_data) or [CSV files](@ref table_data) to begin loading your own data into `PowerSystems.jl` diff --git a/docs/src/tutorials/add_dynamic_data.md b/docs/src/tutorials/add_dynamic_data.md new file mode 100644 index 0000000000..624a59cb30 --- /dev/null +++ b/docs/src/tutorials/add_dynamic_data.md @@ -0,0 +1,391 @@ +# Adding Data for Dynamic Simulations + +In this tutorial, we are going to add dynamic data to a power [`System`](@ref), including +a dynamic generator suitable for phasor-type simulations, as well as a dynamic inverter +and dynamic lines necessary for more complex EMT (electro-magnetic transient) +simulations. + +To run a dynamic simulation in Sienna\Dyn using +[`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/), +two data layers are required: +1. A base layer of [static](@ref S) components, which includes the data needed to run a + power flow problem +2. An additional layer of [dynamic](@ref D) components, which define differential equations + to run a transient simulation + +We'll define these two layers sequentially. + +## Defining the Static Data Layer + +Instead of defining the static data in the `System` manually, we will load an existing three-bus system using +[`PowerSystemCaseBuilder.jl`](https://github.com/NREL-Sienna/PowerSystemCaseBuilder.jl) +to use as a starting point. + +Start by importing these packages: + +```@repl dyn_data +using PowerSystems +using PowerSystemCaseBuilder +const PSY = PowerSystems; +``` + +To create the system, load pre-existing data for a 3-bus system using +`PowerSystemCaseBuilder.jl`: +```@repl dyn_data +threebus_sys = build_system(PSIDSystems, "3 Bus Inverter Base") +``` +See that there is a table of "Static Components", but no "Dynamic" data yet. + +Let's view the generators in the system with [`show_components`](@ref), +including which bus they are connected at: +```@repl dyn_data +show_components(ThermalStandard, threebus_sys, [:bus]) +``` +Notice that there are generators connected at Buses 2 and 3, but not Bus 1. + +Now, we are going to add the data needed to run an EMT simulation. +We will add an infinite voltage source to Bus 1, which is the last component we +need to complete the static data layer. Then, we will a dynamic +generator or inverter model to the two generators, as well as adding dynamic lines. + +## Add an Infinite Voltage Source + +Add a infinite voltage source with small impedance to Bus 1 (the reference bus). +First, retrieve the reference bus using [`get_components`](@ref): +```@repl dyn_data +slack_bus = first(get_components(x -> get_bustype(x) == ACBusTypes.REF, Bus, threebus_sys)) +``` +Notice we filtered by the [bus type](@ref acbustypes_list) to get the bus(es) we wanted. + +Next, manually define a [`Source`](@ref): +```@repl dyn_data +inf_source = Source( + name = "InfBus", #name + available = true, #availability + active_power = 0.0, + reactive_power = 0.0, + bus = slack_bus, #bus + R_th = 0.0, #Rth + X_th = 5e-6, #Xth +); +``` + +And add it to the system: +```@repl dyn_data +add_component!(threebus_sys, inf_source) +``` + +This completes the first layer of [static](@ref S) data, using components similar to those +we added manually in the [Create and Explore a Power `System`](@ref) tutorial. + +## Adding a Dynamic Generator + +Now, we will connect a classic machine model to the generator at bus 102. +Dynamic generator devices +are composed by 5 components: a [Machine](@ref Machine), +[Shaft](@ref Shaft), [Automatic Voltage Regulator](@ref AVR) (AVR), +[Power System Stabilizer](@ref PSS) (PSS), and +[Prime Mover and Turbine Governor](@ref TurbineGov). +For each of those 5 components, we will select a specific model that defines the data and +differential equations for that component, +and then use those 5 components to define the complete dynamic generator. + +```@raw html + +``` + +!!! note + When defining dynamic data, by convention `PowerSystems.jl` assumes that all data is + in [`DEVICE_BASE`](@ref per_unit). + +First, define a [Machine](@ref Machine) that describes the the stator electro-magnetic dynamics: +```@repl dyn_data +# Create the machine +machine_oneDoneQ = OneDOneQMachine( + R = 0.0, + Xd = 1.3125, + Xq = 1.2578, + Xd_p = 0.1813, + Xq_p = 0.25, + Td0_p = 5.89, + Tq0_p = 0.6, +) +``` +Notice that we selected a specific model, [`OneDOneQMachine`](@ref), with the parameters +tailored to a One-d-one-q dynamic machine model. + +Next, define a specific [Shaft](@ref Shaft) model, [`SingleMass`](@ref) that describes the +rotor electro-mechanical dynamics: +```@repl dyn_data +# Shaft +shaft_no_damping = SingleMass( + H = 3.01, #(M = 6.02 -> H = M/2) + D = 0.0, +) +``` + +Represent the electromotive dynamics of the AVR controller using a specific +[Automatic Voltage Regulator](@ref AVR) model, [`AVRTypeI`](@ref): +```@repl dyn_data +# AVR: Type I: Resembles a DC1 AVR +avr_type1 = AVRTypeI( + Ka = 20.0, + Ke = 0.01, + Kf = 0.063, + Ta = 0.2, + Te = 0.314, + Tf = 0.35, + Tr = 0.001, + Va_lim = (min = -5.0, max = 5.0), + Ae = 0.0039, #1st ceiling coefficient + Be = 1.555, #2nd ceiling coefficient +) +``` + +Define a fixed efficiency [Prime Mover and Turbine Governor](@ref TurbineGov) with +[`TGFixed`](@ref): +```@repl dyn_data +#No TG +tg_none = TGFixed(efficiency = 1.0) #efficiency +``` +See that we are modeling a machine that does not include a Turbine Governor +(or PSS below), but you must define components for them to build a +complete machine model. + +Similarly, define a PSS using [`PSSFixed`](@ref), which is used to describe the stabilization +signal for the AVR: +```@repl dyn_data +#No PSS +pss_none = PSSFixed(V_pss = 0.0) +``` + +Now, we are ready to add a dynamic generator to the static +generator at bus 102. First, let's get that static generator: +```@repl dyn_data +static_gen = get_component(Generator, threebus_sys, "generator-102-1") +``` +Notice that its `dynamic_injector` field is currently `nothing`. + +Use its name and the 5 components above to define its [`DynamicGenerator`](@ref) model: +```@repl dyn_data +dynamic_gen = DynamicGenerator( + name = get_name(static_gen), + ω_ref = 1.0, # frequency reference set-point + machine = machine_oneDoneQ, + shaft = shaft_no_damping, + avr = avr_type1, + prime_mover = tg_none, + pss = pss_none, +) +``` +See that the specific component models that we selected and defined above were used to +specify the states needed to model this generator in a dynamic simulation. + +Finally, use the dynamic version of [`add_component!`](@ref add_component!( + sys::System, + dyn_injector::DynamicInjection, + static_injector::StaticInjection; + kwargs..., +)) to add this data to the `System`: +```@repl dyn_data +add_component!(threebus_sys, dynamic_gen, static_gen) +``` +Notice that unlike static components, which are just added to the `System`, +this dynamic component is added to a specific static component within the `System`. + +!!! tip + To define identical dynamic devices for multiple generators at once, define the pieces of the + generator model as *functions*, such as: + ``` + avr_type1() = AVRTypeI(... + ``` + When called in the `DynamicGenerator` constructor, this will create a new AVR for each generator, so + they are different in memory. Later, if you decide to modify the AVR parameters for + a specific generator, it will not modify the AVR in another generator. + +Recall that you can print the system to see a summary of its data: +```@repl dyn_data +threebus_sys +``` +See that a new table has been added: "Dynamic Components." + +Also, print the static generator to double-check the dynamic layer has been added: +```@repl dyn_data +static_gen +``` +Verify that `dynamic_injector` now contains our dynamic generator model. + +Up to this point, you have added the dynamic data necessary to do a phaser-type simulation, +which focuses on machine behavior. Now we will also add dynamic inverters and lines to enable +EMT simulations. + +## Adding a Dynamic Inverter + +Next we will connect a Virtual Synchronous Generator Inverter at bus 103. + +An inverter is composed of [Converter](@ref), [OuterControl](@ref), [InnerControl](@ref), +[DCSource](@ref), [FrequencyEstimator](@ref), and [Filter](@ref) components: +```@raw html + +``` + +As we did for the generator, we will define each of these six components with a specific +model, which defines its differential equations. + +First, define an [`AverageConverter`](@ref) as the specific model for the [Converter](@ref) +component: +```@repl dyn_data +converter_high_power() = AverageConverter( + rated_voltage = 138.0, + rated_current = 100.0 + ) +``` +Recall from the tip above that we can define these components as *functions* instead of +objects for reusability across multiple generators, and notice that that is what we have +done here. + +Define [OuterControl](@ref) using [Virtual Inertia](@ref) for the active power control and +[ReactivePowerDroop](@ref) for the reactive power control: +```@repl dyn_data +outer_control() = OuterControl( + VirtualInertia(Ta = 2.0, kd = 400.0, kω = 20.0), + ReactivePowerDroop(kq = 0.2, ωf = 1000.0), +) +``` + +Define an [InnerControl](@ref) as a Voltage+Current Controller with Virtual Impedance, +using [`VoltageModeControl`](@ref): +```@repl dyn_data +inner_control() = VoltageModeControl( + kpv = 0.59, #Voltage controller proportional gain + kiv = 736.0, #Voltage controller integral gain + kffv = 0.0, #Binary variable enabling voltage feed-forward in current controllers + rv = 0.0, #Virtual resistance in pu + lv = 0.2, #Virtual inductance in pu + kpc = 1.27, #Current controller proportional gain + kic = 14.3, #Current controller integral gain + kffi = 0.0, #Binary variable enabling the current feed-forward in output of current controllers + ωad = 50.0, #Active damping low pass filter cut-off frequency + kad = 0.2, #Active damping gain +) +``` + +Define a [`FixedDCSource`](@ref) for the [DCSource](@ref): +```@repl dyn_data +dc_source_lv() = FixedDCSource(voltage = 600.0) +``` + +Define a [FrequencyEstimator](@ref) as a phase-locked loop (PLL) using [`KauraPLL`](@ref): +```@repl dyn_data +pll() = KauraPLL( + ω_lp = 500.0, #Cut-off frequency for LowPass filter of PLL filter. + kp_pll = 0.084, #PLL proportional gain + ki_pll = 4.69, #PLL integral gain +) +``` + +Finally, define an [`LCLFilter`](@ref) for the [Filter](@ref): +```@repl dyn_data +filt() = LCLFilter( + lf = 0.08, + rf = 0.003, + cf = 0.074, + lg = 0.2, + rg = 0.01, +) +``` + +Now, use those six functions to define a complete dynamic inverter +by getting the static component at bus 103: +```@repl dyn_data +gen_103 = get_component(Generator, threebus_sys, "generator-103-1"); +``` + +using it and our six functions to define a [`DynamicInverter`](@ref): +```@repl dyn_data +dynamic_inv = DynamicInverter( + name = get_name(gen_103), + ω_ref = 1.0, # frequency reference set-point + converter = converter_high_power(), + outer_control = outer_control(), + inner_control = inner_control(), + dc_source = dc_source_lv(), + freq_estimator = pll(), + filter = filt(), +) +``` + +and adding it to the `System`: +```@repl dyn_data +add_component!(threebus_sys, dynamic_inv, gen_103) +``` + +Both generators have now been updated with dynamic data. Let's complete the `System` +updates by adding dynamic lines. + +## Adding Dynamic Lines + +!!! warning + A `System` must have at least two buses and one branch to run a dynamic simulation in + [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/). + +Let's review the AC branches currently in the system: +```@repl dyn_data +get_components(ACBranch, threebus_sys) +``` +Notice that we have three static `Line` components. + +Let's also print the first line to review its format: +```@repl dyn_data +first(get_components(Line, threebus_sys)) +``` +See that these components do not have the fields for dynamic modeling, such as fields for +different [states](@ref S). + +Let's update that by cycling through these lines and using [`DynamicBranch`](@ref) to extend +each static line with the necessary fields: +```@repl dyn_data +for l in get_components(Line, threebus_sys) + # create a dynamic branch + dyn_branch = DynamicBranch(l) + # add dynamic branch to the system, replacing the static branch + add_component!(threebus_sys, dyn_branch) +end +``` + +Take a look at the AC branches in the system again: +```@repl dyn_data +branches = get_components(ACBranch, threebus_sys) +``` +Notice that now there are 3 `DynamicBranch` components instead the `Line` components. + +Let's take a look by printing first one: +```@repl dyn_data +first(branches) +``` +Observe that this is a wrapper around the static data, with the additional states +data for dynamic modeling. + +Finally, let's print the `System` again to summarize our additions: +```@repl dyn_data +threebus_sys +``` +Verify that the additions were successful, with an added voltage `Source`, `DynamicBranch`es +replacing the static `Lines`, and two new dynamic components with the generator and inverter models. + +## Next Steps + +In this tutorial, you have updated a static system with a second dynamic data layer. +The data you added can enable a phasor-based simulation using the dynamic generator, or +a more complex EMT simulation with the additional dynamic inverter and dynamic lines. + +Next, you might like to: +- Read more about the static and dynamic data layers and the dynamic data format in + [Dynamic Devices](@ref). +- Review the specific sub-system models available in `PowerSystems.jl` for [Machine](@ref), + [Shaft](@ref), [AVR](@ref), [PSS](@ref), + [Prime Mover and Turbine Governor](@ref TurbineGov), [Converter](@ref), + [OuterControl](@ref), [InnerControl](@ref), [DCSource](@ref), + [FrequencyEstimator](@ref), and [Filter](@ref) components +- Explore [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/) + for dynamics modeling in Sienna\Dyn \ No newline at end of file diff --git a/docs/src/tutorials/add_dynamic_device.md b/docs/src/tutorials/add_dynamic_device.md deleted file mode 100644 index c246c6b41b..0000000000 --- a/docs/src/tutorials/add_dynamic_device.md +++ /dev/null @@ -1,311 +0,0 @@ -# Creating a System with Dynamic devices - -You can access example data in the [Power Systems Test Data Repository](https://github.com/NREL-Sienna/PowerSystemsTestData). Most of these systems are available to use using [PowerSystemCaseBuilder.jl](@ref psb). - -```@repl dynamic_data -using PowerSystems -const PSY = PowerSystems -file_dir = joinpath(pkgdir(PowerSystems), "docs", "src", "tutorials", "tutorials_data") -``` - -Although `PowerSystems.jl` is not constrained to only PSS/e files, commonly the data available -comes in a pair of files: One for the static data power flow case and a second one with the -dynamic components information. However, `PowerSystems.jl` is able to take any power flow case -and specify dynamic components to it. - -The following describes the system creation for the one machine infinite bus case using custom -component specifications. - -## One Machine Infinite Bus Example - -First load data from any format (see [Constructing a System from RAW data](@ref parsing) for -details. In this example we will load a [PTI power flow data format](https://labs.ece.uw.edu/pstca/formats/pti.txt) -(`.raw` file) as follows: - -```raw -0, 100, 33, 0, 0, 60 / 24-Apr-2020 17:05:49 - MATPOWER 7.0.1-dev - - - 101, 'BUS 1 ', 230, 3, 1, 1, 1, 1.05, 0, 1.06, 0.94, 1.06, 0.94 - 102, 'BUS 2 ', 230, 2, 1, 1, 1, 1.04, 0, 1.06, 0.94, 1.06, 0.94 -0 / END OF BUS DATA, BEGIN LOAD DATA -0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA -0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA - 102, 1, 50, 0, 100, -100, 1.00, 0, 100, 0, 1, 0, 0, 1, 1, 100, 100, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1 -0 / END OF GENERATOR DATA, BEGIN BRANCH DATA - 101, 102, 1, 0.00, 0.05, 0.000, 100, 100, 100, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 -0 / END OF BRANCH DATA, BEGIN TRANSFORMER DATA -0 / END OF TRANSFORMER DATA, BEGIN AREA DATA -0 / END OF AREA DATA, BEGIN TWO-TERMINAL DC DATA -0 / END OF TWO-TERMINAL DC DATA, BEGIN VOLTAGE SOURCE CONVERTER DATA -0 / END OF VOLTAGE SOURCE CONVERTER DATA, BEGIN IMPEDANCE CORRECTION DATA -0 / END OF IMPEDANCE CORRECTION DATA, BEGIN MULTI-TERMINAL DC DATA -0 / END OF MULTI-TERMINAL DC DATA, BEGIN MULTI-SECTION LINE DATA -0 / END OF MULTI-SECTION LINE DATA, BEGIN ZONE DATA -0 / END OF ZONE DATA, BEGIN INTER-AREA TRANSFER DATA -0 / END OF INTER-AREA TRANSFER DATA, BEGIN OWNER DATA -0 / END OF OWNER DATA, BEGIN FACTS CONTROL DEVICE DATA -0 / END OF FACTS CONTROL DEVICE DATA, BEGIN SWITCHED SHUNT DATA -0 / END OF SWITCHED SHUNT DATA, BEGIN GNE DEVICE DATA -0 / END OF GNE DEVICE DATA, BEGIN INDUCTION MACHINE DATA -0 / END OF INDUCTION MACHINE DATA -Q -``` - -Based on the description provided in PTI files, this is a two-bus system, on which the bus -101 (bus 1) is the reference bus at 1.05 pu, and bus 102 (bus 2) is PV bus, to be set at -1.04 pu. There is one 100 MVA generator connected at bus 2, producing 50 MW. There is an -equivalent line connecting buses 1 and 2 with a reactance of ``0.05`` pu. - -We can load this data file first - -```@repl dynamic_data -omib_sys = System(joinpath(file_dir, "OMIB.raw"), runchecks = false) -``` - -### Dynamic Generator - -We are now interested in attaching to the system the dynamic component that will be modeling -our dynamic generator. The data can be added by directly passing a `.dyr` file, but in this -example we want to add custom dynamic data. - -Dynamic generator devices are composed by 5 components, namely, `machine`, `shaft`, `avr`, -`tg` and `pss` (see [`DynamicGenerator`](@ref)). So we will be adding functions to create all -of its components and the generator itself. The example code creates all the components -for a [`DynamicGenerator`](@ref) based on specific models for its components. This result -will be a classic machine model without AVR, Turbine Governor and PSS. - -```@repl dynamic_data -#Machine -machine_classic = BaseMachine( - R = 0.0, - Xd_p = 0.2995, - eq_p = 0.7087, -) - -#Shaft -shaft_damping = SingleMass( - H = 3.148, - D = 2.0, -) - -#AVR -avr_none = AVRFixed(Vf = 0.0) - -#TurbineGovernor -tg_none = TGFixed(efficiency = 1.0) - -#PSS -pss_none = PSSFixed(V_pss = 0.0); -``` - -Then we can collect all the dynamic components and create the dynamic generator and assign it -to a static generator of choice. In this example we will add it to the generator "generator-102-1" -as follows: - -```@repl dynamic_data -#Collect the static gen in the system -static_gen = get_component(Generator, omib_sys, "generator-102-1") -#Creates the dynamic generator -dyn_gen = DynamicGenerator( - name = get_name(static_gen), - ω_ref = 1.0, - machine = machine_classic, - shaft = shaft_damping, - avr = avr_none, - prime_mover = tg_none, - pss = pss_none, - ) -#Add the dynamic generator the system -add_component!(omib_sys, dyn_gen, static_gen) -``` - -Once the data is created, we can export our system data such that it can be reloaded later: - -```julia -to_json(omib_sys, "YOUR_DIR/omib_sys.json") -``` - -## Example with Dynamic Inverter - -We will now create a three bus system with one inverter and one generator. In order to do so, -we will parse the following file `ThreebusInverter.raw`: - -```raw -0, 100, 33, 0, 0, 60 / 24-Apr-2020 19:28:39 - MATPOWER 7.0.1-dev - - - 101, 'BUS 1 ', 138, 3, 1, 1, 1, 1.02, 0, 1.1, 0.9, 1.1, 0.9 - 102, 'BUS 2 ', 138, 2, 1, 1, 1, 1.0142, 0, 1.1, 0.9, 1.1, 0.9 - 103, 'BUS 3 ', 138, 2, 1, 1, 1, 1.0059, 0, 1.1, 0.9, 1.1, 0.9 -0 / END OF BUS DATA, BEGIN LOAD DATA - 101, 1, 1, 1, 1, 50, 10, 0, 0, 0, 0, 1, 1, 0 - 102, 1, 1, 1, 1, 100, 30, 0, 0, 0, 0, 1, 1, 0 - 103, 1, 1, 1, 1, 30, 10, 0, 0, 0, 0, 1, 1, 0 -0 / END OF LOAD DATA, BEGIN FIXED SHUNT DATA -0 / END OF FIXED SHUNT DATA, BEGIN GENERATOR DATA - 102, 1, 70, 0, 100, -100, 1.0142, 0, 100, 0, 1, 0, 0, 1, 1, 100, 318, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1 - 103, 1, 80, 0, 100, -100, 1.0059, 0, 100, 0, 1, 0, 0, 1, 1, 100, 318, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1 -0 / END OF GENERATOR DATA, BEGIN BRANCH DATA - 101, 103, 1, 0.01000, 0.12, 0.2, 250, 250, 250, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 - 101, 102, 1, 0.01000, 0.12, 0.2, 250, 250, 250, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 - 102, 103, 1, 0.02000, 0.9, 1.0, 250, 250, 250, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 -0 / END OF BRANCH DATA, BEGIN TRANSFORMER DATA -0 / END OF TRANSFORMER DATA, BEGIN AREA DATA -0 / END OF AREA DATA, BEGIN TWO-TERMINAL DC DATA -0 / END OF TWO-TERMINAL DC DATA, BEGIN VOLTAGE SOURCE CONVERTER DATA -0 / END OF VOLTAGE SOURCE CONVERTER DATA, BEGIN IMPEDANCE CORRECTION DATA -0 / END OF IMPEDANCE CORRECTION DATA, BEGIN MULTI-TERMINAL DC DATA -0 / END OF MULTI-TERMINAL DC DATA, BEGIN MULTI-SECTION LINE DATA -0 / END OF MULTI-SECTION LINE DATA, BEGIN ZONE DATA -0 / END OF ZONE DATA, BEGIN INTER-AREA TRANSFER DATA -0 / END OF INTER-AREA TRANSFER DATA, BEGIN OWNER DATA -0 / END OF OWNER DATA, BEGIN FACTS CONTROL DEVICE DATA -0 / END OF FACTS CONTROL DEVICE DATA, BEGIN SWITCHED SHUNT DATA -0 / END OF SWITCHED SHUNT DATA, BEGIN GNE DEVICE DATA -0 / END OF GNE DEVICE DATA, BEGIN INDUCTION MACHINE DATA -0 / END OF INDUCTION MACHINE DATA -Q -``` - -That describes a three bus connected system, with generators connected at bus 2 and 3, and -loads in three buses. We can load the system and attach an infinite source on the reference bus: - -```@repl dynamic_data -threebus_sys = System(joinpath(file_dir, "ThreeBusInverter.raw"), runchecks = false) -``` - -We will connect a [`OneDOneQMachine`](@ref) machine at bus 102, and a Virtual Synchronous Generator Inverter -at bus 103. An inverter is composed by a `converter`, `outer control`, `inner control`, -`dc source`, `frequency estimator` and a `filter` (see [`DynamicInverter`](@ref)). - -### Dynamic Inverter definition - -We will create specific components of the inverter as follows: - -```@repl dynamic_data -#Define converter as an AverageConverter -converter_high_power = AverageConverter(rated_voltage = 138.0, rated_current = 100.0) - -#Define Outer Control as a composition of Virtual Inertia + Reactive Power Droop -outer_cont = OuterControl( - active_power_control = VirtualInertia(Ta = 2.0, kd = 400.0, kω = 20.0), - reactive_power_control = ReactivePowerDroop(kq = 0.2, ωf = 1000.0), -) - -#Define an Inner Control as a Voltage+Current Controler with Virtual Impedance: -inner_cont = VoltageModeControl( - kpv = 0.59, #Voltage controller proportional gain - kiv = 736.0, #Voltage controller integral gain - kffv = 0.0, #Binary variable enabling the voltage feed-forward in output of current controllers - rv = 0.0, #Virtual resistance in pu - lv = 0.2, #Virtual inductance in pu - kpc = 1.27, #Current controller proportional gain - kic = 14.3, #Current controller integral gain - kffi = 0.0, #Binary variable enabling the current feed-forward in output of current controllers - ωad = 50.0, #Active damping low pass filter cut-off frequency - kad = 0.2, #Active damping gain -) - -#Define DC Source as a FixedSource: -dc_source_lv = FixedDCSource(voltage = 600.0) - -#Define a Frequency Estimator as a PLL based on Vikram Kaura and Vladimir Blaskoc 1997 paper: -pll = KauraPLL( - ω_lp = 500.0, #Cut-off frequency for LowPass filter of PLL filter. - kp_pll = 0.084, #PLL proportional gain - ki_pll = 4.69, #PLL integral gain -) - -#Define an LCL filter: -filt = LCLFilter(lf = 0.08, rf = 0.003, cf = 0.074, lg = 0.2, rg = 0.01) -``` - -Similarly we will construct a dynamic generator as follows: - -```@repl dynamic_data -#Create the machine -machine_oneDoneQ = OneDOneQMachine( - R = 0.0, - Xd = 1.3125, - Xq = 1.2578, - Xd_p = 0.1813, - Xq_p = 0.25, - Td0_p = 5.89, - Tq0_p = 0.6, -) - -#Shaft -shaft_no_damping = SingleMass( - H = 3.01, - D = 0.0, -) - -#AVR: Type I: Resembles a DC1 AVR -avr_type1 = AVRTypeI( - Ka = 20.0, - Ke = 0.01, - Kf = 0.063, - Ta = 0.2, - Te = 0.314, - Tf = 0.35, - Tr = 0.001, - Va_lim = (min = -5.0, max = 5.0), - Ae = 0.0039, #1st ceiling coefficient - Be = 1.555, #2nd ceiling coefficient -) - -#No TG -tg_none = TGFixed(efficiency = 1.0) - -#No PSS -pss_none = PSSFixed(V_pss = 0.0); -``` - -### Add the components to the System - -```@repl dynamic_data -for g in get_components(Generator, threebus_sys) - #Find the generator at bus 102 - if get_number(get_bus(g)) == 102 - #Create the dynamic generator - case_gen = DynamicGenerator( - name = get_name(g), - ω_ref = 1.0, - machine = machine_oneDoneQ, - shaft = shaft_no_damping, - avr = avr_type1, - prime_mover = tg_none, - pss = pss_none, - ) - #Attach the dynamic generator to the system - add_component!(threebus_sys, case_gen, g) - #Find the generator at bus 103 - elseif get_number(get_bus(g)) == 103 - #Create the dynamic inverter - case_inv = DynamicInverter( - name = get_name(g), - ω_ref = 1.0, - converter = converter_high_power, - outer_control = outer_cont, - inner_control = inner_cont, - dc_source = dc_source_lv, - freq_estimator = pll, - filter = filt, - ) - #Attach the dynamic inverter to the system - add_component!(threebus_sys, case_inv, g) - end -end - -# We can check that the system has the Dynamic Inverter and Generator -threebus_sys -``` - -Finally we can seraliaze the system data for later reloading - -```julia -to_json(threebus_sys, "YOUR_DIR/threebus_sys.json") -``` - -For more details to handle dynamic data, [check the tutorial in PowerSimulationsDynamics](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/tutorials/tutorial_dynamic_data/). diff --git a/docs/src/tutorials/creating_system.md b/docs/src/tutorials/creating_system.md index cb7c056020..b19a85a18f 100644 --- a/docs/src/tutorials/creating_system.md +++ b/docs/src/tutorials/creating_system.md @@ -328,6 +328,7 @@ and modified the `System` per-unit settings. Next, you might want to: - [Add time series data to components in the `System`](@ref tutorial_time_series) +- [Add necessary data for dynamic simulations](@ref "Adding Data for Dynamic Simulations") - [Import a `System` from an existing Matpower or PSSE file instead of creating it manually](@ref parsing) - [Create your own `System` from .csv files instead of creating it manually](@ref table_data) - [Read more to understand per-unitization in PowerSystems.jl](@ref per_unit) From 0938aeb38b51c2f7b041d2a5d0d3bca431be6f00 Mon Sep 17 00:00:00 2001 From: kdayday Date: Fri, 30 Aug 2024 17:51:20 -0600 Subject: [PATCH 10/43] Add branch abstract type docstrings --- src/models/branches.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/models/branches.jl b/src/models/branches.jl index 79aa09aec6..4ab6f67c00 100644 --- a/src/models/branches.jl +++ b/src/models/branches.jl @@ -1,8 +1,11 @@ +""" Supertype for all branches""" abstract type Branch <: Device end +""" Supertype for all AC branches (branches connecting at least one AC node)""" abstract type ACBranch <: Branch end +""" Supertype for all DC branches (branches that connect only DC nodes)""" abstract type DCBranch <: Branch end get_from_bus(b::T) where {T <: Branch} = b.arc.from From 607fb11502a8dfaa77365898e3ed8dbdb0ca5805 Mon Sep 17 00:00:00 2001 From: kdayday Date: Fri, 30 Aug 2024 17:52:23 -0600 Subject: [PATCH 11/43] Add links and default values to DynamicGenerator and Inverter docstrings --- src/PowerSystems.jl | 1 + src/models/dynamic_generator.jl | 37 ++++++++++++++++++----------- src/models/dynamic_inverter.jl | 41 ++++++++++++++++++++++----------- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/PowerSystems.jl b/src/PowerSystems.jl index 79097c2c34..21bfd9b65e 100644 --- a/src/PowerSystems.jl +++ b/src/PowerSystems.jl @@ -234,6 +234,7 @@ export CurrentModeControl export RECurrentControlB # OutputCurrentLimiters Export +export OutputCurrentLimiter export MagnitudeOutputCurrentLimiter export InstantaneousOutputCurrentLimiter export PriorityOutputCurrentLimiter diff --git a/src/models/dynamic_generator.jl b/src/models/dynamic_generator.jl index 208ee4d59c..3ee8e468cb 100644 --- a/src/models/dynamic_generator.jl +++ b/src/models/dynamic_generator.jl @@ -20,23 +20,34 @@ internal::InfrastructureSystemsInternal end -A dynamic generator is composed by 5 components, namely a Machine, a Shaft, an Automatic Voltage Regulator (AVR), -a Prime Mover (o Turbine Governor) and Power System Stabilizer (PSS). It requires a [`StaticInjection`](@ref) device -that is attached to it. +A dynamic generator with the necessary data for modeling the dynamic response of a generator +in a phasor or electromagnetic transient simulation. + +Dynamic generator is composed by 5 components, namely a [Machine](@ref), a [Shaft](@ref), +an Automatic Voltage Regulator ([AVR](@ref)), a +[Prime Mover and Turbine Governor](@ref "TurbineGov"), +and Power System Stabilizer ([PSS](@ref)). It must be attached to a +[`StaticInjection`](@ref) device using [`add_component!`](@ref add_component!( + sys::System, + dyn_injector::DynamicInjection, + static_injector::StaticInjection; + kwargs..., +)), which contains all the rest of the generator's data that isn't specific to its dynamic +response. # Arguments - `name::String`: Name of generator. - `ω_ref::Float64`: Frequency reference set-point in pu. -- `machine <: Machine`: Machine model for modeling the electro-magnetic phenomena. -- `shaft <: Shaft`: Shaft model for modeling the electro-mechanical phenomena. -- `avr <: AVR`: AVR model of the excitacion system. -- `prime_mover <: TurbineGov`: Prime Mover and Turbine Governor model for mechanical power. -- `pss <: PSS`: Power System Stabilizer model. -- `base_power::Float64`: Base power -- `n_states::Int`: Number of states (will depend on the components). -- `states::Vector{Symbol}`: Vector of states (will depend on the components). -- `ext::Dict{String, Any}` -- `internal::InfrastructureSystemsInternal`: power system internal reference, do not modify +- `machine <: Machine`: [Machine](@ref) model for modeling the electro-magnetic phenomena. +- `shaft <: Shaft`: [Shaft](@ref) model for modeling the electro-mechanical phenomena. +- `avr <: AVR`: [AVR](@ref) model of the excitacion system. +- `prime_mover <: TurbineGov`: [Prime Mover and Turbine Governor model](@ref "TurbineGov") for mechanical power. +- `pss <: PSS`: [PSS](@ref) model. +- `base_power::Float64`: (default: `100.0`) Base power of the `System` +- `n_states::Int`: (**Do not modify.**) Number of states (will depend on the inputs above). +- `states::Vector{Symbol}`: (**Do not modify.**) Vector of states (will depend on the inputs above). +- `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation +- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems.jl internal reference """ mutable struct DynamicGenerator{ M <: Machine, diff --git a/src/models/dynamic_inverter.jl b/src/models/dynamic_inverter.jl index ebe3cf1719..601b85f8cd 100644 --- a/src/models/dynamic_inverter.jl +++ b/src/models/dynamic_inverter.jl @@ -25,25 +25,38 @@ abstract type InverterComponent <: DynamicComponent end internal::InfrastructureSystemsInternal end -A dynamic inverter is composed by 6 components, namely a Converter, an Outer Control, an Inner Control, -a DC Source, a Frequency Estimator and a Filter. It requires a [`StaticInjection`](@ref) device that is -attached to it. +A dynamic inverter with the necessary data for modeling the dynamic response of an inverter +in a phasor or electromagnetic transient simulation. + +A dynamic inverter is composed by 6 components, namely a [Converter](@ref), +[Outer Loop Control](@ref OuterControl), [Inner Loop Control](@ref InnerControl), +a [DC Source](@ref DCSource), a [Frequency Estimator](@ref FrequencyEstimator) and a +[Filter](@ref). + +It must be attached to a +[`StaticInjection`](@ref) device using [`add_component!`](@ref add_component!( + sys::System, + dyn_injector::DynamicInjection, + static_injector::StaticInjection; + kwargs..., +)), which contains all the rest of the generator's data that isn't specific to its dynamic +response. # Arguments - `name::String`: Name of inverter. - `ω_ref::Float64`: Frequency reference set-point in pu. - `converter <: Converter`: Converter model for the PWM transformation. -- `outer_control <: OuterControl`: Outer-control controller model. -- `inner_control <: InnerControl`: Inner-control controller model. -- `dc_source <: DCSource`: DC Source model. -- `freq_estimator <: FrequencyEstimator`: Frequency Estimator (typically a PLL) model. -- `filter <: Filter`: Filter model. -- `limiter <: Union{nothing, OutputCurrentLimiter}`: Inverter Inner Control Limiter model -- `base_power::Float64`: Base power -- `n_states::Int`: Number of states (will depend on the components). -- `states::Vector{Symbol}`: Vector of states (will depend on the components). -- `ext::Dict{String, Any}` -- `internal::InfrastructureSystemsInternal`: power system internal reference, do not modify +- `outer_control <: OuterControl`: An [OuterControl](@ref) controller model. +- `inner_control <: InnerControl`: An [InnerControl](@ref) controller model. +- `dc_source <: DCSource`: [DCSource](@ref) model. +- `freq_estimator <: FrequencyEstimator`: a [FrequencyEstimator](@ref) (typically a [PLL](@ref P)) model. +- `filter <: Filter`: [Filter](@ref) model. +- `limiter <: Union{nothing, OutputCurrentLimiter}`: (default: nothing) Inner Control [Current Limiter](@ref OutputCurrentLimiter) model +- `base_power::Float64`: (default: `100.0`) Base power of the `System` +- `n_states::Int`: (**Do not modify.**) Number of states (will depend on the inputs above). +- `states::Vector{Symbol}`: (**Do not modify.**) Vector of states (will depend on the inputs above). +- `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation +- `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems.jl internal reference """ mutable struct DynamicInverter{ C <: Converter, From a24c8ed6fe2658386acd184ac1b8c9da30cd0456 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 2 Sep 2024 09:35:17 -0600 Subject: [PATCH 12/43] Change JSON how-to from temp to more realistic folder name --- docs/src/how_to/serialize_data.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/src/how_to/serialize_data.md b/docs/src/how_to/serialize_data.md index d0bb9c127b..6cbe2baef4 100644 --- a/docs/src/how_to/serialize_data.md +++ b/docs/src/how_to/serialize_data.md @@ -1,28 +1,37 @@ # Serialize Data to a JSON -PowerSystems.jl supports serializing/deserializing data with JSON. This provides an example of how to write and read a `System` to/from disk. +PowerSystems.jl supports serializing/deserializing data with JSON. This provides an example +of how to write and read a `System` to/from disk. -## Dependencies - -Let's use a dataset from the tabular data parsing tutorial: +You can do this to save your own custom system, but we'll use an existing +dataset from +[`PowerSystemCaseBuilder.jl`](https://github.com/NREL-Sienna/PowerSystemCaseBuilder.jl), +simply to illustrate the process. +First, load the dependencies and a `System` from `PowerSystemCaseBuilder`: ```@repl serialize_data using PowerSystems -file_dir = joinpath(pkgdir(PowerSystems), "docs", "src", "tutorials", "tutorials_data"); #hide -sys = System(joinpath(file_dir, "case5_re.m")) +using PowerSystemCaseBuilder +sys = build_system(PSISystems, "c_sys5_pjm") ``` -## Write data to a temporary directory +## Write data to a JSON +Set up your target path, for example in a "mysystems" subfolder: ```@repl serialize_data -folder = mktempdir(); +folder = mkdir("mysystems"); path = joinpath(folder, "system.json") -println("Serializing to $path") +``` + +Now write the system to JSON: +```@repl serialize_data to_json(sys, path) ``` ## Read the JSON file and create a new `System` +Now, you can read the file back in, and verify the new system has the same data as above: ```@repl serialize_data sys2 = System(path) +rm(folder, recursive=true); #hide ``` From ed24940217f006565b7bcd112726dbbd1c723ab2 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 2 Sep 2024 11:01:47 -0600 Subject: [PATCH 13/43] Add markdown files to JuliaFormatter script and run formatter on all documentation --- docs/src/api/citation.md | 1 - docs/src/api/developer_guidelines.md | 4 +- docs/src/api/enumerated_types.md | 142 +++++------ docs/src/api/glossary.md | 187 +++++++------- docs/src/api/valuecurve_options.md | 24 +- docs/src/explanation/dynamic_data.md | 56 ++--- docs/src/explanation/per_unit.md | 2 +- docs/src/explanation/system.md | 20 +- docs/src/explanation/time_series.md | 28 +-- docs/src/explanation/type_structure.md | 28 ++- docs/src/generate_input_config_table.jl | 23 +- docs/src/generate_validation_table.jl | 14 +- docs/src/how_to/add_cost_curve.md | 59 +++-- docs/src/how_to/add_new_types.md | 58 ++--- docs/src/how_to/adding_additional_fields.md | 4 + docs/src/how_to/extend_tabular_parsing.md | 34 +-- docs/src/how_to/get_available_generators.md | 10 +- docs/src/how_to/get_buses.md | 12 +- docs/src/how_to/improve_ts_performance.md | 52 ++-- docs/src/how_to/install.md | 14 +- docs/src/how_to/market_bid_cost.md | 73 +++--- docs/src/how_to/migrating_to_psy4.md | 117 +++++---- docs/src/how_to/parsing.md | 93 +++---- docs/src/how_to/reduce_repl_printing.md | 4 +- docs/src/how_to/serialize_data.md | 6 +- docs/src/index.md | 50 ++-- .../model_library/hydro_generation_cost.md | 2 +- docs/src/model_library/load_cost.md | 2 +- docs/src/model_library/market_bid_cost.md | 2 +- .../renewable_generation_cost.md | 2 +- docs/src/model_library/storage_cost.md | 2 +- .../model_library/thermal_generation_cost.md | 2 +- docs/src/tutorials/add_dynamic_data.md | 132 +++++++--- docs/src/tutorials/creating_system.md | 216 ++++++++++------- docs/src/tutorials/get_component_data.md | 3 +- docs/src/tutorials/utils/docs_utils.jl | 2 +- .../src/tutorials/working_with_time_series.md | 229 +++++++++++------- scripts/formatter/formatter_code.jl | 6 +- src/parsers/im_io/LICENCE.md | 19 +- src/parsers/pm_io/LICENSE.md | 9 +- 40 files changed, 991 insertions(+), 752 deletions(-) diff --git a/docs/src/api/citation.md b/docs/src/api/citation.md index 2d65377658..b2741f8e21 100644 --- a/docs/src/api/citation.md +++ b/docs/src/api/citation.md @@ -17,7 +17,6 @@ author = {José Daniel Lara and Clayton Barrows and Daniel Thom and Dheepak Kris keywords = {Power Systems, Julia, Energy}, ``` ------------- PowerSystems has been developed as part of the [Sienna modeling framework](https://www.nrel.gov/analysis/sienna.html) by the U.S. Department of Energy's National Renewable Energy Laboratory ([NREL](https://www.nrel.gov/)). diff --git a/docs/src/api/developer_guidelines.md b/docs/src/api/developer_guidelines.md index 11613f313c..1dbd541088 100644 --- a/docs/src/api/developer_guidelines.md +++ b/docs/src/api/developer_guidelines.md @@ -4,8 +4,8 @@ In order to contribute to `PowerSystems.jl` repository please read the following [`InfrastructureSystems.jl`](https://github.com/NREL-Sienna/InfrastructureSystems.jl) documentation in detail: -1. [Style Guide](https://nrel-sienna.github.io/InfrastructureSystems.jl/stable/style/) -2. [Contributing Guidelines](https://github.com/NREL-Sienna/PowerSystems.jl/blob/main/CONTRIBUTING.md) + 1. [Style Guide](https://nrel-sienna.github.io/InfrastructureSystems.jl/stable/style/) + 2. [Contributing Guidelines](https://github.com/NREL-Sienna/PowerSystems.jl/blob/main/CONTRIBUTING.md) Pull requests are always welcome to fix bugs or add additional modeling capabilities. diff --git a/docs/src/api/enumerated_types.md b/docs/src/api/enumerated_types.md index e73c140ba9..5e0832b9e5 100644 --- a/docs/src/api/enumerated_types.md +++ b/docs/src/api/enumerated_types.md @@ -1,10 +1,10 @@ - # Specifying the type of... Some fields in PowerSystems.jl are specified with an option from a pre-defined list -(Specified with [`IS.scoped_enums`](https://nrel-sienna.github.io/InfrastructureSystems.jl/stable/InfrastructureSystems/#InfrastructureSystems.@scoped_enum-Tuple{Any,%20Vararg{Any,%20N}%20where%20N})). +(Specified with [`IS.scoped_enums`](https://nrel-sienna.github.io/InfrastructureSystems.jl/stable/InfrastructureSystems/#InfrastructureSystems.@scoped_enum-Tuple%7BAny,%20Vararg%7BAny,%20N%7D%20where%20N%7D)). Example syntax: + ``` set_fuel!(gen, ThermalFuels.COAL) ``` @@ -16,13 +16,13 @@ These predefined lists are below: `ACBusTypes` categorize buses for modeling activities and denote which quantities are specified for load flow calculations. `ACBusTypes` has the options: -| Name | Description | -|:----------|:-------------| -| `ISOLATED` | Disconnected from network | -| `PQ` | Active and reactive power defined (load bus)| -| `PV` | Active power and voltage magnitude defined (generator bus)| -| `REF` | Reference bus (θ = 0)| -| `SLACK` | Slack bus | +| Name | Description | +|:---------- |:---------------------------------------------------------- | +| `ISOLATED` | Disconnected from network | +| `PQ` | Active and reactive power defined (load bus) | +| `PV` | Active power and voltage magnitude defined (generator bus) | +| `REF` | Reference bus (θ = 0) | +| `SLACK` | Slack bus | ## [Prime Movers](@id pm_list) @@ -30,31 +30,31 @@ Each generator contains a field for `prime_mover::PrimeMovers`, based on the opt [EIA form 923](https://www.eia.gov/survey/form/eia_923/instructions.pdf). `PrimeMovers` has the options: -| Name | Description | -|:----------|:-------------| -| `BA` | Energy Storage, Battery | -| `BT` | Turbines Used in a Binary Cycle (including those used for geothermal applications) | -| `CA` | Combined-Cycle – Steam Part | -| `CC` | Combined-Cycle - Aggregated Plant *augmentation of EIA | -| `CE` | Energy Storage, Compressed Air | -| `CP` | Energy Storage, Concentrated Solar Power | -| `CS` | Combined-Cycle Single-Shaft Combustion turbine and steam turbine share a single generator | -| `CT` | Combined-Cycle Combustion Turbine Part | -| `ES` | Energy Storage, Other | -| `FC` | Fuel Cell | -| `FW` | Energy Storage, Flywheel | -| `GT` | Combustion (Gas) Turbine (including jet engine design) | -| `HA` | Hydrokinetic, Axial Flow Turbine | -| `HB` | Hydrokinetic, Wave Buoy | -| `HK` | Hydrokinetic, Other | -| `HY` | Hydraulic Turbine (including turbines associated with delivery of water by pipeline) | -| `IC` | Internal Combustion (diesel, piston, reciprocating) Engine | -| `PS` | Energy Storage, Reversible Hydraulic Turbine (Pumped Storage) | -| `OT` | Other | -| `ST` | Steam Turbine (including nuclear, geothermal and solar steam; does not include combined-cycle turbine) | -| `PVe` | Photovoltaic \(*Note*: renaming from EIA PV to PVe to avoid conflict with `ACBusType.PV`\) | -| `WT` | Wind Turbine, Onshore | -| `WS` | Wind Turbine, Offshore | +| Name | Description | +|:----- |:------------------------------------------------------------------------------------------------------ | +| `BA` | Energy Storage, Battery | +| `BT` | Turbines Used in a Binary Cycle (including those used for geothermal applications) | +| `CA` | Combined-Cycle – Steam Part | +| `CC` | Combined-Cycle - Aggregated Plant *augmentation of EIA | +| `CE` | Energy Storage, Compressed Air | +| `CP` | Energy Storage, Concentrated Solar Power | +| `CS` | Combined-Cycle Single-Shaft Combustion turbine and steam turbine share a single generator | +| `CT` | Combined-Cycle Combustion Turbine Part | +| `ES` | Energy Storage, Other | +| `FC` | Fuel Cell | +| `FW` | Energy Storage, Flywheel | +| `GT` | Combustion (Gas) Turbine (including jet engine design) | +| `HA` | Hydrokinetic, Axial Flow Turbine | +| `HB` | Hydrokinetic, Wave Buoy | +| `HK` | Hydrokinetic, Other | +| `HY` | Hydraulic Turbine (including turbines associated with delivery of water by pipeline) | +| `IC` | Internal Combustion (diesel, piston, reciprocating) Engine | +| `PS` | Energy Storage, Reversible Hydraulic Turbine (Pumped Storage) | +| `OT` | Other | +| `ST` | Steam Turbine (including nuclear, geothermal and solar steam; does not include combined-cycle turbine) | +| `PVe` | Photovoltaic \(*Note*: renaming from EIA PV to PVe to avoid conflict with `ACBusType.PV`\) | +| `WT` | Wind Turbine, Onshore | +| `WS` | Wind Turbine, Offshore | ## [Fuels for Thermal Generators](@id tf_list) @@ -63,22 +63,22 @@ are intended to reflect the options in the [Aggregated Fuel Codes](https://www.eia.gov/survey/form/eia_923/instructions.pdf) from the EIA Annual Energy Review. `ThermalFuels` has the options: -| Name | EIA Fuel Code | Description | -|:----------|:---------------|:-------------| -| `COAL` | COL | Anthracite Coal and Bituminous Coal | -| `WASTE_COAL` | WOC | Waste/Other Coal (includes anthracite culm, gob, fine coal, lignite waste, waste coal) | -| `DISTILLATE_FUEL_OIL` | DFO | Distillate Fuel Oil (Diesel, No. 1, No. 2, and No. 4) | -| `WASTE_OIL` | WOO | Waste Oil Kerosene and JetFuel Butane, Propane | -| `PETROLEUM_COKE` | PC | Petroleum Coke | -| `RESIDUAL_FUEL_OIL` | RFO | Residual Fuel Oil (No. 5, No. 6 Fuel Oils, and Bunker Oil) | -| `NATURAL_GAS` | NG | Natural Gas | -| `OTHER_GAS` | OOG | Other Gas and blast furnace gas | -| `NUCLEAR` | NUC | Nuclear Fission (Uranium, Plutonium, Thorium) | -| `AG_BIPRODUCT` | ORW | Agricultural Crop Byproducts/Straw/Energy Crops | -| `MUNICIPAL_WASTE` | MLG | Municipal Solid Waste – Biogenic component | -| `WOOD_WASTE` | WWW | Wood Waste Liquids excluding Black Liquor (BLQ) (Includes red liquor, sludge wood, spent sulfite liquor, and other wood-based liquids) | -| `GEOTHERMAL` | GEO | Geothermal | -| `OTHER` | OTH | Other | +| Name | EIA Fuel Code | Description | +|:--------------------- |:------------- |:-------------------------------------------------------------------------------------------------------------------------------------- | +| `COAL` | COL | Anthracite Coal and Bituminous Coal | +| `WASTE_COAL` | WOC | Waste/Other Coal (includes anthracite culm, gob, fine coal, lignite waste, waste coal) | +| `DISTILLATE_FUEL_OIL` | DFO | Distillate Fuel Oil (Diesel, No. 1, No. 2, and No. 4) | +| `WASTE_OIL` | WOO | Waste Oil Kerosene and JetFuel Butane, Propane | +| `PETROLEUM_COKE` | PC | Petroleum Coke | +| `RESIDUAL_FUEL_OIL` | RFO | Residual Fuel Oil (No. 5, No. 6 Fuel Oils, and Bunker Oil) | +| `NATURAL_GAS` | NG | Natural Gas | +| `OTHER_GAS` | OOG | Other Gas and blast furnace gas | +| `NUCLEAR` | NUC | Nuclear Fission (Uranium, Plutonium, Thorium) | +| `AG_BIPRODUCT` | ORW | Agricultural Crop Byproducts/Straw/Energy Crops | +| `MUNICIPAL_WASTE` | MLG | Municipal Solid Waste – Biogenic component | +| `WOOD_WASTE` | WWW | Wood Waste Liquids excluding Black Liquor (BLQ) (Includes red liquor, sludge wood, spent sulfite liquor, and other wood-based liquids) | +| `GEOTHERMAL` | GEO | Geothermal | +| `OTHER` | OTH | Other | ## [Energy Storage](@id storagetech_list) @@ -86,19 +86,19 @@ EIA Annual Energy Review. `ThermalFuels` has the options: on the options in [EIA form 923](https://www.eia.gov/survey/form/eia_923/instructions.pdf). `StorageTech` has the options: -| Name | Description | -|:----------|:-------------| -| `PTES` | Pumped thermal energy storage | -| `LIB` | LiON Battery | -| `LAB` | Lead Acid Battery | -| `FLWB` | Redox Flow Battery | -| `SIB` | Sodium Ion Battery | -| `ZIB` | Zinc Ion Battery | -| `HGS` | Hydrogen Gas Storage | -| `LAES` | Liquid Air Storage | -| `OTHER_CHEM` | Other Chemical Storage | -| `OTHER_MECH` | Other Mechanical Storage | -| `OTHER_THERM` | Other Thermal Storage | +| Name | Description | +|:------------- |:----------------------------- | +| `PTES` | Pumped thermal energy storage | +| `LIB` | LiON Battery | +| `LAB` | Lead Acid Battery | +| `FLWB` | Redox Flow Battery | +| `SIB` | Sodium Ion Battery | +| `ZIB` | Zinc Ion Battery | +| `HGS` | Hydrogen Gas Storage | +| `LAES` | Liquid Air Storage | +| `OTHER_CHEM` | Other Chemical Storage | +| `OTHER_MECH` | Other Mechanical Storage | +| `OTHER_THERM` | Other Thermal Storage | ## [Dynamic States](@id states_list) @@ -106,17 +106,17 @@ on the options in [EIA form 923](https://www.eia.gov/survey/form/eia_923/instruc to in [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/). `StateTypes` has the options: -| Name | Description | -|:----------|:-------------| -| `Differential` | State evolves over time via a differential equation ``\dot{x} = f(x)`` | -| `Algebraic` | State evolves over time by satisfying an algebraic equation ``0 = g(x)`` | -| `Hybrid` | Depending on specific parameters, the state can be `Differential` or `Algebraic` | +| Name | Description | +|:-------------- |:-------------------------------------------------------------------------------- | +| `Differential` | State evolves over time via a differential equation ``\dot{x} = f(x)`` | +| `Algebraic` | State evolves over time by satisfying an algebraic equation ``0 = g(x)`` | +| `Hybrid` | Depending on specific parameters, the state can be `Differential` or `Algebraic` | ## [Angle Units](@id angleunits_list) `AngleUnits` can be specified in: -| Name | -|----------| +| Name | +|:--------- | | `DEGREES` | -| `RADIANS` | \ No newline at end of file +| `RADIANS` | diff --git a/docs/src/api/glossary.md b/docs/src/api/glossary.md index de54a51e18..a286efdeeb 100644 --- a/docs/src/api/glossary.md +++ b/docs/src/api/glossary.md @@ -1,156 +1,135 @@ # Glossary and Acronyms -[A](@ref) | [D](@ref) | [E](@ref) | [F](@ref) | [H](@ref) | [I](@ref) | [O](@ref) | [P](@ref) | [R](@ref) | +[A](@ref) | [D](@ref) | [E](@ref) | [F](@ref) | [H](@ref) | [I](@ref) | [O](@ref) | [P](@ref) | [R](@ref) | [S](@ref) | [V](@ref) | [W](@ref) | [Z](@ref) ### A -* *AC*: Alternating current + - *AC*: Alternating current -* *ACE*: Area control error - -* *AGC*: Automatic generation control - -* *AVR*: Automatic Voltage Regulator + - *ACE*: Area control error + - *AGC*: Automatic generation control + - *AVR*: Automatic Voltage Regulator ### D -* *DC*: Direct current - -* *DERA1*: + - *DC*: Direct current -* *Dynamic*: Refers to data and simulations for power system transient simulations using differential - equations. Common examples include signal stability analysis to verify the power system will - maintain stability in the few seconds following an unexpected fault or generator trip. For contrast, - see the definition for [Static](@ref S) data. + - *DERA1*: + - *Dynamic*: Refers to data and simulations for power system transient simulations using differential + equations. Common examples include signal stability analysis to verify the power system will + maintain stability in the few seconds following an unexpected fault or generator trip. For contrast, + see the definition for [Static](@ref S) data. ### E -* *EMF*: Electromotive force - -* *ESAC*: IEEE Type AC Excitation System model - -* *ESDC*: IEEE Type DC Excitation System model - -* *EXAC*: IEEE Type AC Excitation System (modified) model + - *EMF*: Electromotive force -* *EXPIC*: Proportional/Integral Excitation System from PSS/E - -* *EXST*: IEEE Type ST (Static) Excitation System model - -* *EX4VSA*: IEEE Excitation System for Voltage Security Assessment with Over-Excitation Limits. + - *ESAC*: IEEE Type AC Excitation System model + - *ESDC*: IEEE Type DC Excitation System model + - *EXAC*: IEEE Type AC Excitation System (modified) model + - *EXPIC*: Proportional/Integral Excitation System from PSS/E + - *EXST*: IEEE Type ST (Static) Excitation System model + - *EX4VSA*: IEEE Excitation System for Voltage Security Assessment with Over-Excitation Limits. ### F -* *Forecast*: Predicted values of a time-varying quantity that commonly features - a look-ahead and can have multiple data values representing each time period. - This data is used in simulation with receding horizons or data generated from - forecasting algorithms. See the article on [`Time Series Data`](@ref ts_data). + - *Forecast*: Predicted values of a time-varying quantity that commonly features + a look-ahead and can have multiple data values representing each time period. + This data is used in simulation with receding horizons or data generated from + forecasting algorithms. See the article on [`Time Series Data`](@ref ts_data). -* *Forecast window*: Represents the forecasted value starting at a particular initial time. + - *Forecast window*: Represents the forecasted value starting at a particular initial time. See the article on [`Time Series Data`](@ref ts_data). ### H -* *Horizon*: Is the duration of all time steps in one forecast. As of PowerSystems.jl - version 4.0, all horizons in `PowerSystems.jl` are represented as a `Dates.Period`. - For instance, many Day-ahead markets will have an hourly-[resolution](@ref R) forecast - for the next day, which would have a horizon of `Dates.Hour(24)` or `Dates.Day(1)`. If the - forecast included the next day plus a 24-hour lookahead window, the horizon would be - `Dates.Hour(48)` or `Dates.Day(2)`. See the article on [`Time Series Data`](@ref ts_data). + - *Horizon*: Is the duration of all time steps in one forecast. As of PowerSystems.jl + version 4.0, all horizons in `PowerSystems.jl` are represented as a `Dates.Period`. + For instance, many Day-ahead markets will have an hourly-[resolution](@ref R) forecast + for the next day, which would have a horizon of `Dates.Hour(24)` or `Dates.Day(1)`. If the + forecast included the next day plus a 24-hour lookahead window, the horizon would be + `Dates.Hour(48)` or `Dates.Day(2)`. See the article on [`Time Series Data`](@ref ts_data). -* *HVDC*: High-voltage DC + - *HVDC*: High-voltage DC ### I -* *IEEET*: IEEE Type I Excitation System. - -* *Injector* or *Injection*: Injectors refer to models that represent how a generator or storage - device *injects* power or current into the power system. Loads are negative injectors. In - `PowerSystems.jl`, some components can accept data for both [`StaticInjection`](@ref) and - [`DynamicInjection`](@ref) models for both [static](@ref S) and [dynamic](@ref D) modeling. - -* *Interval*: The period of time between forecast initial times. In `PowerSystems.jl` all - intervals are represented using `Dates.Period` types. For instance, in a Day-Ahead market - simulation, the interval is usually `Hour(24)`. - -* *Initial time*: The first time-stamp in a forecast window. See the article on - [`Time Series Data`](@ref ts_data). + - *IEEET*: IEEE Type I Excitation System. -* *IPC*: Interconnecting power converter + - *Injector* or *Injection*: Injectors refer to models that represent how a generator or storage + device *injects* power or current into the power system. Loads are negative injectors. In + `PowerSystems.jl`, some components can accept data for both [`StaticInjection`](@ref) and + [`DynamicInjection`](@ref) models for both [static](@ref S) and [dynamic](@ref D) modeling. + - *Interval*: The period of time between forecast initial times. In `PowerSystems.jl` all + intervals are represented using `Dates.Period` types. For instance, in a Day-Ahead market + simulation, the interval is usually `Hour(24)`. + - *Initial time*: The first time-stamp in a forecast window. See the article on + [`Time Series Data`](@ref ts_data). + - *IPC*: Interconnecting power converter ### O -* *OEL*: + - *OEL*: ### P -* *PLL*: Phase-locked loop + - *PLL*: Phase-locked loop -* *PSS*: Power System Stabilizer - -* *PSSE* or *PSS/E*: Siemen's PSS®E Power Simulator - -* *PPA*: Power purchase agreement - -* *PSID*: - -* *PSLF*: - -* *pu* or *p.u.*: Per-unit + - *PSS*: Power System Stabilizer + - *PSSE* or *PSS/E*: Siemen's PSS®E Power Simulator + - *PPA*: Power purchase agreement + - *PSID*: + - *PSLF*: + - *pu* or *p.u.*: Per-unit ### R -* *REECB1*: - -* *REPCA1*: + - *REECB1*: -* *Resolution*: The period of time between each discrete value in a time series. All resolutions - are represented using `Dates.Period` types. For instance, a Day-ahead market data set usually - has a resolution of `Hour(1)`, a Real-Time market data set usually has a resolution of `Minute(5)`. + - *REPCA1*: + - *Resolution*: The period of time between each discrete value in a time series. All resolutions + are represented using `Dates.Period` types. For instance, a Day-ahead market data set usually + has a resolution of `Hour(1)`, a Real-Time market data set usually has a resolution of `Minute(5)`. ### S -* *SCRX*: Bus Fed or Solid Fed Static Exciter - -* *SEXS*: Simplified Excitation System model from PSS/E - -* *SIL*: Surge impedance loading - -* *States*: Correspond to the set of inputs, outputs or variables, that evolve dynamically in - [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/), - commonly via a differential-algebraic system of equations. In `PowerSystems.jl`, a component - associated to a `DynamicInjector` (for example an AVR) specifies the set of states that specific - component requires to be modeled accurately. - -* *Static*: Typically refers to steady state data or models where the power system - and each of its components are assumed to be operating at a steady state equilibrium point. This - includes both power flow data for a single time point simulation as well as quasi-static time - series data and models, where the power system is at an equilibrium point at each time step. - Static data can be used as the input to single time point power flow models and production - cost models with, for example, 5-minute, 15-minute, or 1-hour [Resolution](@ref R). - For contrast, see the definition for [Dynamic](@ref D) data. - -* *STAB*: Speed Sensitive Stabilizing PSS Model + - *SCRX*: Bus Fed or Solid Fed Static Exciter + + - *SEXS*: Simplified Excitation System model from PSS/E + - *SIL*: Surge impedance loading + - *States*: Correspond to the set of inputs, outputs or variables, that evolve dynamically in + [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/), + commonly via a differential-algebraic system of equations. In `PowerSystems.jl`, a component + associated to a `DynamicInjector` (for example an AVR) specifies the set of states that specific + component requires to be modeled accurately. + - *Static*: Typically refers to steady state data or models where the power system + and each of its components are assumed to be operating at a steady state equilibrium point. This + includes both power flow data for a single time point simulation as well as quasi-static time + series data and models, where the power system is at an equilibrium point at each time step. + Static data can be used as the input to single time point power flow models and production + cost models with, for example, 5-minute, 15-minute, or 1-hour [Resolution](@ref R). + For contrast, see the definition for [Dynamic](@ref D) data. + - *STAB*: Speed Sensitive Stabilizing PSS Model ### V -* *VSCDCLine*: Voltage-Source Converter Direct Current Line + - *VSCDCLine*: Voltage-Source Converter Direct Current Line -* *VSM*: + - *VSM*: ### W -* *Window*: A forecast window is one forecast run that starts at one [initial time](@ref I) - and extends through the forecast [horizon](@ref H). Typically, a forecast data set - contains multiple forecast windows, with sequential initial times. For example, a - year-long data set of day-ahead forecasts contains 365 forecast windows + - *Window*: A forecast window is one forecast run that starts at one [initial time](@ref I) + and extends through the forecast [horizon](@ref H). Typically, a forecast data set + contains multiple forecast windows, with sequential initial times. For example, a + year-long data set of day-ahead forecasts contains 365 forecast windows ### Z -* *ZIP load*: A ZIP load model accounts for the voltage-dependency of a load and is primarily used - for dynamics modeling. It includes three kinds of load: constant impedance (Z), constant current (I), - and constant power (P), though many dynamics models just use the constant impedance model. - [`StandardLoad`](@ref) and [`ExponentialLoad`](@ref) are both ZIP load models: - [`StandardLoad`](@ref) breaks up the load into each of its three components, while - [`ExponentialLoad`](@ref) expresses the load as an exponential equation. \ No newline at end of file + - *ZIP load*: A ZIP load model accounts for the voltage-dependency of a load and is primarily used + for dynamics modeling. It includes three kinds of load: constant impedance (Z), constant current (I), + and constant power (P), though many dynamics models just use the constant impedance model. + [`StandardLoad`](@ref) and [`ExponentialLoad`](@ref) are both ZIP load models: + [`StandardLoad`](@ref) breaks up the load into each of its three components, while + [`ExponentialLoad`](@ref) expresses the load as an exponential equation. diff --git a/docs/src/api/valuecurve_options.md b/docs/src/api/valuecurve_options.md index d66dc31606..85c8f501ec 100644 --- a/docs/src/api/valuecurve_options.md +++ b/docs/src/api/valuecurve_options.md @@ -1,23 +1,23 @@ # [`ValueCurve` Options](@id curve_table) + Operating cost data typically includes both fixed and variable costs. See the how-to on [Adding an Operating Cost](@ref cost_how_to) for a demonstration of defining an operating cost. In PowerSystems.jl, the *variable* portion of the operating cost can be represented as linear, quadratic, or piecewise input-output curves; potentially piecewise marginal heat rates; average heat rates; and more, as best fits the input data. This is done by constructing various subtypes of [`ValueCurve`](@ref). This summary table shows each way to construct a `ValueCurve` with the user-friendly subtype aliases. The `ValueCurve`s make no assumption about units; the example interpretation given here assumes that the variable cost `ValueCurve` will be wrapped in a [`CostCurve`](@ref) with natural units. Note that all four `Piecewise` options here fundamentally represent the same curve. More information and explanatory plots are provided for each subtype alias in the subheadings below. -| Description | Example | Example interpretation | -| :--- | :--- | :--- | -| Linear input-output curve with *zero* no-load cost (constant average rate) | `LinearCurve(5.0)` | \$5/MWh | -| Linear input-output curve with potentially *nonzero* no-load cost (constant marginal rate) | `LinearCurve(5.0, 15.0)` | \$5/MWh + \$15/hr | -| Quadratic input-output curve with potentially nonzero no-load cost | `QuadraticCurve(1.0, 1.0, 18.0)` | $C(P) = 1 P^2 + 1 P + 18$ where $C$ is \$/hr, $P$ is MW | -| Piecewise linear curve specified by cost values at production points | `PiecewisePointCurve([(1.0, 20.0), (2.0, 24.0), (3.0, 30.0)])` | \$20/hr @ 1 MW, \$24/hr @ 2 MW, \$30/hr @ 3 MW, linear \$/hr interpolation between these points | -| Piecewise linear curve specified by initial value and marginal rates (slopes) between production points | `PiecewiseIncrementalCurve(20.0, [1.0, 2.0, 3.0], [4.0, 6.0])` | \$20/hr @ 1 MW plus additional \$4/MWh from 1 MW to 2 MW plus additional \$6/MWh from 2 MW to 3 MW | +| Description | Example | Example interpretation | +|:------------------------------------------------------------------------------------------------------------------------- |:-------------------------------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------ | +| Linear input-output curve with *zero* no-load cost (constant average rate) | `LinearCurve(5.0)` | \$5/MWh | +| Linear input-output curve with potentially *nonzero* no-load cost (constant marginal rate) | `LinearCurve(5.0, 15.0)` | \$5/MWh + \$15/hr | +| Quadratic input-output curve with potentially nonzero no-load cost | `QuadraticCurve(1.0, 1.0, 18.0)` | $C(P) = 1 P^2 + 1 P + 18$ where $C$ is \$/hr, $P$ is MW | +| Piecewise linear curve specified by cost values at production points | `PiecewisePointCurve([(1.0, 20.0), (2.0, 24.0), (3.0, 30.0)])` | \$20/hr @ 1 MW, \$24/hr @ 2 MW, \$30/hr @ 3 MW, linear \$/hr interpolation between these points | +| Piecewise linear curve specified by initial value and marginal rates (slopes) between production points | `PiecewiseIncrementalCurve(20.0, [1.0, 2.0, 3.0], [4.0, 6.0])` | \$20/hr @ 1 MW plus additional \$4/MWh from 1 MW to 2 MW plus additional \$6/MWh from 2 MW to 3 MW | | No-load cost plus piecewise linear curve specified by initial value and marginal rates (slopes) between production points | `PiecewiseIncrementalCurve(18.0, 20.0, [1.0, 2.0, 3.0], [4.0, 6.0])` | \$18/hr no-load cost; \$20/hr @ 1 MW plus additional \$4/MWh from 1 MW to 2 MW plus additional \$6/MWh from 2 MW to 3 MW | -| Piecewise linear curve specified by initial value and average rates between production points | `PiecewiseAverageCurve(20.0, [1.0, 2.0, 3.0], [12.0, 10.0])` | \$20/hr @ 1 MW, \$12/MWh @ 2 MW, \$10/MWh @ 3 MW, linear \$/hr interpolation between these points | - ---- +| Piecewise linear curve specified by initial value and average rates between production points | `PiecewiseAverageCurve(20.0, [1.0, 2.0, 3.0], [12.0, 10.0])` | \$20/hr @ 1 MW, \$12/MWh @ 2 MW, \$10/MWh @ 3 MW, linear \$/hr interpolation between these points | ## [`LinearCurve`](@ref) + Specify the marginal cost of production $M$ and optionally the no-load cost $C$, which defaults to zero. Here is a graphical representation: ```@raw html @@ -25,6 +25,7 @@ Specify the marginal cost of production $M$ and optionally the no-load cost $C$, ``` ## [`QuadraticCurve`](@ref) + Specify the quadratic ($Q$), proportional ($M$), and constant ($C$) terms of a function that defines the input-output curve: ```@raw html @@ -32,6 +33,7 @@ Specify the quadratic ($Q$), proportional ($M$), and constant ($C$) terms of a f ``` ## [`PiecewisePointCurve`](@ref) + Specify a vector of $K$ (production, cost) pairs (i.e., $(P_k, C_k)$ for $k = 1, \dots, K$) to define the input-output curve: ```@raw html @@ -39,6 +41,7 @@ Specify a vector of $K$ (production, cost) pairs (i.e., $(P_k, C_k)$ for $k = 1, ``` ## [`PiecewiseIncrementalCurve`](@ref) + Specify the cost $C_1$ at the least production point given (NOT the cost at zero production), a vector of $K$ production points $P_1, \dots, P_k$, and a vector of $K-1$ marginal rates $M_1, \dots, M_{k-1}$, that represent the slopes of the curve segments between the points. $C_1$ may be `nothing`, which results in a not-fully-defined curve. The no-load cost $C_0$ can optionally be specified as a first argument; it is not part of the representation of the curve, just another piece of data that may be stored: ```@raw html @@ -46,6 +49,7 @@ Specify the cost $C_1$ at the least production point given (NOT the cost at zero ``` ## [`PiecewiseAverageCurve`](@ref) + Specify the cost $C_1$ at the least production point given (NOT the cost at zero production), a vector of $K$ production points $P_1, \dots, P_k$, and a vector of $K-1$ average rates $R_1, \dots, R_{k-1}$ at the $K-1$ latter production points: ```@raw html diff --git a/docs/src/explanation/dynamic_data.md b/docs/src/explanation/dynamic_data.md index b365b671ff..99eceb073a 100644 --- a/docs/src/explanation/dynamic_data.md +++ b/docs/src/explanation/dynamic_data.md @@ -3,9 +3,10 @@ ## Static and Dynamic Data Layers `PowerSystems.jl` uses two data layers to define data for dynamic simulations: -1. [Static](@ref S) components, which includes the data needed to run a power flow problem -2. [Dynamic](@ref D) components are those that define differential equations to run a transient simulation. These dyanamic - data are attached to the static components. + + 1. [Static](@ref S) components, which includes the data needed to run a power flow problem + 2. [Dynamic](@ref D) components are those that define differential equations to run a transient simulation. These dyanamic + data are attached to the static components. Although `PowerSystems.jl` is not constrained to only PSS/e files, commonly the data for a dynamic simulation comes in a pair of files: One for the static data power flow case (e.g., @@ -18,13 +19,13 @@ division between those two files. The first data layer contains all the information necessary to run a power flow problem: -- Vector of `Bus` elements, that define all the buses in the network. -- Vector of `Branch` elements, that define all the branches elements (that connect two buses) in the network. -- Vector of `StaticInjection` elements, that define all the devices connected to buses that can inject (or withdraw) power. These static devices, typically generators, in `PowerSimulationsDynamics` are used to solve the Power Flow problem that determines the active and reactive power provided for each device. -- Vector of `PowerLoad` elements, that define all the loads connected to buses that can withdraw current. These are also used to solve the Power Flow. -- Vector of `Source` elements, that define source components behind a reactance that can inject or withdraw current. -- The base of power used to define per unit values, in MVA as a `Float64` value. -- The base frequency used in the system, in Hz as a `Float64` value. + - Vector of `Bus` elements, that define all the buses in the network. + - Vector of `Branch` elements, that define all the branches elements (that connect two buses) in the network. + - Vector of `StaticInjection` elements, that define all the devices connected to buses that can inject (or withdraw) power. These static devices, typically generators, in `PowerSimulationsDynamics` are used to solve the Power Flow problem that determines the active and reactive power provided for each device. + - Vector of `PowerLoad` elements, that define all the loads connected to buses that can withdraw current. These are also used to solve the Power Flow. + - Vector of `Source` elements, that define source components behind a reactance that can inject or withdraw current. + - The base of power used to define per unit values, in MVA as a `Float64` value. + - The base frequency used in the system, in Hz as a `Float64` value. ### Layer 2: Dynamic Components @@ -32,8 +33,8 @@ The second data layer contains the *additional* information describing the dynam of certain components in the `System`. This data is all attached to components defined in the static data layer: -- (Optional) Selecting which of the `Lines` (of the `Branch` vector) elements must be modeled of `DynamicLines` elements, that can be used to model lines with differential equations. -- Vector of `DynamicInjection` elements. These components must be attached to a `StaticInjection` that connects the power flow solution to the dynamic formulation of such device. + - (Optional) Selecting which of the `Lines` (of the `Branch` vector) elements must be modeled of `DynamicLines` elements, that can be used to model lines with differential equations. + - Vector of `DynamicInjection` elements. These components must be attached to a `StaticInjection` that connects the power flow solution to the dynamic formulation of such device. `DynamicInjection` can be `DynamicGenerator` or `DynamicInverter`, and its specific formulation (i.e. differential equations) will depend on the specific components that define each device (see the sections below). As a result, it is possible to flexibly define dynamic data models and methods according to @@ -53,11 +54,11 @@ directly from the data model source. Each generator is a data structure that is defined by the following components: -- [Machine](@ref Machine): That defines the stator electro-magnetic dynamics. -- [Shaft](@ref Shaft): That describes the rotor electro-mechanical dynamics. -- [Automatic Voltage Regulator](@ref AVR): Electromotive dynamics to model an AVR controller. -- [Power System Stabilizer](@ref PSS): Control dynamics to define an stabilization signal for the AVR. -- [Prime Mover and Turbine Governor](@ref TurbineGov): Thermo-mechanical dynamics and associated controllers. + - [Machine](@ref Machine): That defines the stator electro-magnetic dynamics. + - [Shaft](@ref Shaft): That describes the rotor electro-mechanical dynamics. + - [Automatic Voltage Regulator](@ref AVR): Electromotive dynamics to model an AVR controller. + - [Power System Stabilizer](@ref PSS): Control dynamics to define an stabilization signal for the AVR. + - [Prime Mover and Turbine Governor](@ref TurbineGov): Thermo-mechanical dynamics and associated controllers. ```@raw html @@ -67,17 +68,18 @@ Each generator is a data structure that is defined by the following components: Each inverter is a data structure that is defined by the following components: -- [DC Source](@ref DCSource): Defines the dynamics of the DC side of the converter. -- [Frequency Estimator](@ref FrequencyEstimator): That describes how the frequency of the grid - can be estimated using the grid voltages. Typically a phase-locked loop (PLL). -- [Outer Loop Control](@ref OuterControl): That describes the active and reactive power - control dynamics. -- [Inner Loop Control](@ref InnerControl): That can describe virtual impedance, - voltage control and current control dynamics. -- [Converter](@ref Converter): That describes the dynamics of the pulse width modulation (PWM) - or space vector modulation (SVM). -- [Filter](@ref Filter): Used to connect the converter output to the grid. + - [DC Source](@ref DCSource): Defines the dynamics of the DC side of the converter. + - [Frequency Estimator](@ref FrequencyEstimator): That describes how the frequency of the grid + can be estimated using the grid voltages. Typically a phase-locked loop (PLL). + - [Outer Loop Control](@ref OuterControl): That describes the active and reactive power + control dynamics. + - [Inner Loop Control](@ref InnerControl): That can describe virtual impedance, + voltage control and current control dynamics. + - [Converter](@ref Converter): That describes the dynamics of the pulse width modulation (PWM) + or space vector modulation (SVM). + - [Filter](@ref Filter): Used to connect the converter output to the grid. ```@raw html ``` ⠀ +``` diff --git a/docs/src/explanation/per_unit.md b/docs/src/explanation/per_unit.md index 28c9042e0e..fcbb434efe 100644 --- a/docs/src/explanation/per_unit.md +++ b/docs/src/explanation/per_unit.md @@ -27,4 +27,4 @@ The units of the parameter values stored in each struct are defined in the stored parameter values. Instead, unit system conversions are made when accessing parameters using the [accessor functions](@ref dot_access), thus making it imperative to utilize the accessor functions instead of the "dot" accessor methods to -ensure the return of the correct values. \ No newline at end of file +ensure the return of the correct values. diff --git a/docs/src/explanation/system.md b/docs/src/explanation/system.md index 4e46c4bea0..598facc637 100644 --- a/docs/src/explanation/system.md +++ b/docs/src/explanation/system.md @@ -90,9 +90,9 @@ to_json(system, "system.json") The serialization process stores 3 files -1. System data file (`*.json` file) -2. Validation data file (`*.json` file) -3. Time Series data file (`*.h5` file) + 1. System data file (`*.json` file) + 2. Validation data file (`*.json` file) + 3. Time Series data file (`*.h5` file) To deserialize: @@ -106,7 +106,7 @@ UUIDs. If you will modify the System or components after deserialization then it is recommended that you set this flag to generate new UUIDs. ```julia -system2 = System("system.json", assign_new_uuids = true) +system2 = System("system.json"; assign_new_uuids = true) ``` ## Viewing PowerSystems Data in JSON Format @@ -127,38 +127,38 @@ online in a browser. The command line utility [jq](https://stedolan.github.io/jq/) offers even more features. The rest of this document provides example commands. -- View the entire file pretty-printed + - View the entire file pretty-printed ```zsh jq . system.json ``` -- View the PowerSystems component types + - View the PowerSystems component types ```zsh jq '.data.components | .[] | .__metadata__ | .type' system.json | sort | uniq ``` -- View specific components + - View specific components ```zsh jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard")' system.json ``` -- Get the count of a component type + - Get the count of a component type ```zsh # There is almost certainly a better way. jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard")' system.json | grep -c ThermalStandard ``` -- View specific component by name + - View specific component by name ```zsh jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard" and .name == "107_CC_1")' system.json ``` -- Filter on a field value + - Filter on a field value ```zsh jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard" and .active_power > 2.3)' system.json diff --git a/docs/src/explanation/time_series.md b/docs/src/explanation/time_series.md index fdc92725a4..4bce2c96d2 100644 --- a/docs/src/explanation/time_series.md +++ b/docs/src/explanation/time_series.md @@ -9,9 +9,8 @@ enable consistent modeling. `PowerSystems.jl` supports two categories of time series data depending on the process to obtain the data and its interpretation: -- [Static Time Series Data](@ref) -- [Forecasts](@ref) - + - [Static Time Series Data](@ref) + - [Forecasts](@ref) These categories are are all subtypes of `TimeSeriesData` and fall within this time series type hierarchy: @@ -34,10 +33,10 @@ Static time series usually comes in the following format, with a set [resolution between the time-stamps: | DateTime | Value | -|---------------------|:-----:| +|:------------------- |:-----:| | 2020-09-01T00:00:00 | 100.0 | | 2020-09-01T01:00:00 | 101.0 | -| 2020-09-01T02:00:00 | 99.0 | +| 2020-09-01T02:00:00 | 99.0 | This example is a 1-hour resolution static time-series. @@ -58,22 +57,22 @@ Forecast data usually comes in the following format, where a column represents t stamp associated with the [initial time](@ref I) of the forecast, and the remaining columns represent the forecasted values at each step in the forecast [horizon](@ref H). -| DateTime | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | -|---------------------|:-----:|:-----:|:-----:|:----:|:-----:|:-----:|:-----:|:------| +| DateTime | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +|:------------------- |:-----:|:-----:|:-----:|:----:|:-----:|:-----:|:-----:|:----- | | 2020-09-01T00:00:00 | 100.0 | 101.0 | 101.3 | 90.0 | 98.0 | 87.0 | 88.0 | 67.0 | | 2020-09-01T01:00:00 | 101.0 | 101.3 | 99.0 | 98.0 | 88.9 | 88.3 | 67.1 | 89.4 | -| 2020-09-01T02:00:00 | 99.0 | 67.0 | 89.0 | 99.9 | 100.0 | 101.0 | 112.0 | 101.3 | +| 2020-09-01T02:00:00 | 99.0 | 67.0 | 89.0 | 99.9 | 100.0 | 101.0 | 112.0 | 101.3 | This example forecast has a [interval](@ref I) of 1 hour and a [horizon](@ref H) of 8. PowerSystems defines the following Julia structs to represent forecasts: -- [`Deterministic`](@ref): Point forecast without any uncertainty representation. -- [`Probabilistic`](@ref): Stores a discretized cumulative distribution functions - (CDFs) or probability distribution functions (PDFs) at each time step in the - look-ahead window. -- [`Scenarios`](@ref): Stores a set of probable trajectories for forecasted quantity - with equal probability. + - [`Deterministic`](@ref): Point forecast without any uncertainty representation. + - [`Probabilistic`](@ref): Stores a discretized cumulative distribution functions + (CDFs) or probability distribution functions (PDFs) at each time step in the + look-ahead window. + - [`Scenarios`](@ref): Stores a set of probable trajectories for forecasted quantity + with equal probability. ## Data Storage @@ -100,6 +99,5 @@ maximum active power field in the struct describing the generator. In this way, store a scaling factor time series that will get multiplied by the maximum active power rather than the magnitudes of the maximum active power time series. - Examples of how to create and add time series to system can be found in the [Add Time Series Example](https://nrel-sienna.github.io/PowerSystems.jl/stable/tutorials/add_forecasts/) diff --git a/docs/src/explanation/type_structure.md b/docs/src/explanation/type_structure.md index 7a554d7811..0594b1d4c7 100644 --- a/docs/src/explanation/type_structure.md +++ b/docs/src/explanation/type_structure.md @@ -23,18 +23,21 @@ the data represents. A type hierarchy has been defined with several levels of abstract types starting with `InfrastructureSystemsType`. There are a bunch of subtypes of `InfrastructureSystemsType`, but the important ones to know about are: -- `System`: overarching `struct` that collects all of the `Component`s -- `Component`: includes all elements of power system data - - `Topology`: includes non physical elements describing network connectivity - - `Service`: includes descriptions of system requirements (other than energy balance) - - `Device`: includes descriptions of all the physical devices in a power system -- `InfrastructureSystems.DeviceParameter`: includes structs that hold data describing the - dynamic, or economic capabilities of `Device`. -- `TimeSeriesData`: Includes all time series types - - `Forecast`: includes structs to define time series of forecasted data where multiple -values can represent each time stamp - - `StaticTimeSeries`: includes structs to define time series with a single value for each -time stamp + - `System`: overarching `struct` that collects all of the `Component`s + + - `Component`: includes all elements of power system data + + + `Topology`: includes non physical elements describing network connectivity + + `Service`: includes descriptions of system requirements (other than energy balance) + + `Device`: includes descriptions of all the physical devices in a power system + - `InfrastructureSystems.DeviceParameter`: includes structs that hold data describing the + dynamic, or economic capabilities of `Device`. + - `TimeSeriesData`: Includes all time series types + + + `Forecast`: includes structs to define time series of forecasted data where multiple + values can represent each time stamp + + `StaticTimeSeries`: includes structs to define time series with a single value for each + time stamp The abstract hierarchy enables categorization of the devices by their operational characteristics and modeling requirements. @@ -57,3 +60,4 @@ implemented in the package. ```@raw html ``` ⠀ +``` diff --git a/docs/src/generate_input_config_table.jl b/docs/src/generate_input_config_table.jl index 50f8b7a255..dc89055770 100644 --- a/docs/src/generate_input_config_table.jl +++ b/docs/src/generate_input_config_table.jl @@ -1,10 +1,12 @@ @info "Generating Input Configuration Descriptor Table" function create_md() - descriptor = PowerSystems._read_config_file(joinpath( - dirname(pathof(PowerSystems)), - "descriptors", - "power_system_inputs.json", - )) + descriptor = PowerSystems._read_config_file( + joinpath( + dirname(pathof(PowerSystems)), + "descriptors", + "power_system_inputs.json", + ), + ) columns = [ "name", @@ -25,7 +27,7 @@ function create_md() ) s = string( s, - "`PowerSystemeTableData` parser defined by `src/descriptors/power_system_inputs.json`:\n\n" + "`PowerSystemeTableData` parser defined by `src/descriptors/power_system_inputs.json`:\n\n", ) for (cat, items) in descriptor csv = "" @@ -64,6 +66,9 @@ end txt = create_md() -open("/Users/cbarrows/Documents/repos/PowerSystems.jl/docs/src/modeler_guide/markdown.txt", "w") do f - write(f, txt) -end \ No newline at end of file +open( + "/Users/cbarrows/Documents/repos/PowerSystems.jl/docs/src/modeler_guide/markdown.txt", + "w", +) do f + write(f, txt) +end diff --git a/docs/src/generate_validation_table.jl b/docs/src/generate_validation_table.jl index 54b838926c..a1598c49b2 100644 --- a/docs/src/generate_validation_table.jl +++ b/docs/src/generate_validation_table.jl @@ -1,10 +1,18 @@ @info "Generating Validation Table" function generate_validation_table(filepath::AbstractString) - descriptor = InfrastructureSystems.read_validation_descriptor(joinpath(PSYPATH,"descriptors","power_system_structs.json")) + descriptor = InfrastructureSystems.read_validation_descriptor( + joinpath(PSYPATH, "descriptors", "power_system_structs.json"), + ) open(filepath, "w") do io write(io, "# Data Requirements\n\n") - write(io, "| Struct Name | Field Name | DataType | Min | Max | Action |\n") - write(io, "|---------------|--------------|------------|-------|-------|----------|\n") + write( + io, + "| Struct Name | Field Name | DataType | Min | Max | Action |\n", + ) + write( + io, + "|---------------|--------------|------------|-------|-------|----------|\n", + ) for item in descriptor for field in item["fields"] write(io, "|$(item["struct_name"])|$(field["name"])|$(field["data_type"])|") diff --git a/docs/src/how_to/add_cost_curve.md b/docs/src/how_to/add_cost_curve.md index 40eee6124e..0ae405d4c1 100644 --- a/docs/src/how_to/add_cost_curve.md +++ b/docs/src/how_to/add_cost_curve.md @@ -8,40 +8,47 @@ using PowerSystems #hide ``` To begin, the user must make 2 or 3 decisions before defining the operating cost: -1. Select an appropriate [`OperationalCost`](@ref) from the [`OperationalCost`](@ref) + + 1. Select an appropriate [`OperationalCost`](@ref) from the [`OperationalCost`](@ref) options. In general, each operating cost has parameters to define fixed and variable costs. To be able to define an `OperationalCost`, you must first select a curve to represent the variable cost(s). - 1. If you selected [`ThermalGenerationCost`](@ref) or [`HydroGenerationCost`](@ref), + + 1. If you selected [`ThermalGenerationCost`](@ref) or [`HydroGenerationCost`](@ref), select either a [`FuelCurve`](@ref) or [`CostCurve`](@ref) to represent the variable cost, based on the units of the generator's data. - - If you have data in terms of heat rate or water flow, use [`FuelCurve`](@ref). - - If you have data in units of currency, such as \$/MWh, use [`CostCurve`](@ref). - If you selected another `OperationalCost` type, the variable cost is represented - as a `CostCurve`. -2. Select a [`ValueCurve`](@ref) to represent the variable cost data by comparing the format + + * If you have data in terms of heat rate or water flow, use [`FuelCurve`](@ref). + * If you have data in units of currency, such as \$/MWh, use [`CostCurve`](@ref). + If you selected another `OperationalCost` type, the variable cost is represented + as a `CostCurve`. + + 2. Select a [`ValueCurve`](@ref) to represent the variable cost data by comparing the format of your variable cost data to the [Variable Cost Representations table](@ref curve_table) and the [`ValueCurve`](@ref) options. Then, the user defines the cost by working backwards: - 1. Define the variable cost's `ValueCurve` - 2. Use the `ValueCurve` to define the selected `CostCurve` or `FuelCurve` - 3. Use the `CostCurve` or `FuelCurve` to define the `OperationalCost` -Let's look at a few examples. + 1. Define the variable cost's `ValueCurve` + 2. Use the `ValueCurve` to define the selected `CostCurve` or `FuelCurve` + 3. Use the `CostCurve` or `FuelCurve` to define the `OperationalCost` + +Let's look at a few examples. ## Example 1: A Renewable Generator -We have a renewable unit that produces at \$22/MWh. +We have a renewable unit that produces at \$22/MWh. Following the decision steps above: -1. We select [`RenewableGenerationCost`](@ref) to represent this renewable generator. -2. We select a [`LinearCurve`](@ref) to represent the \$22/MWh variable cost. + + 1. We select [`RenewableGenerationCost`](@ref) to represent this renewable generator. + 2. We select a [`LinearCurve`](@ref) to represent the \$22/MWh variable cost. Following the implementation steps, we define `RenewableGenerationCost` by nesting the definitions: + ```@repl costcurve -RenewableGenerationCost(variable = CostCurve(value_curve = LinearCurve(22.0))) +RenewableGenerationCost(; variable = CostCurve(; value_curve = LinearCurve(22.0))) ``` ## Example 2: A Thermal Generator @@ -51,25 +58,35 @@ We have a thermal generating unit that has a heat rate of 7 GJ/MWh at 100 MW and \$1000. Its fuel cost is \$20/GJ. Following the decision steps above: -1. We select [`ThermalGenerationCost`](@ref) to represent this thermal generator. -2. We select [`FuelCurve`](@ref) because we have consumption in units of fuel (GJ/MWh) + + 1. We select [`ThermalGenerationCost`](@ref) to represent this thermal generator. + 2. We select [`FuelCurve`](@ref) because we have consumption in units of fuel (GJ/MWh) instead of currency. -3. We select a [`PiecewisePointCurve`](@ref) to represent the piecewise linear heat rate + 3. We select a [`PiecewisePointCurve`](@ref) to represent the piecewise linear heat rate curve. This time, we'll define each step individually, beginning with the heat rate curve: + ```@repl costcurve heat_rate_curve = PiecewisePointCurve([(100.0, 7.0), (200.0, 9.0)]) ``` Use the heat rate to define the fuel curve, including the cost of fuel: + ```@repl costcurve -fuel_curve = FuelCurve(value_curve = heat_rate_curve, fuel_cost = 20.0) +fuel_curve = FuelCurve(; value_curve = heat_rate_curve, fuel_cost = 20.0) ``` Finally, define the full operating cost: + ```@repl costcurve -cost = ThermalGenerationCost(variable = fuel_curve, fixed = 6.0, start_up = 2000.0, shut_down = 1000.0) +cost = ThermalGenerationCost(; + variable = fuel_curve, + fixed = 6.0, + start_up = 2000.0, + shut_down = 1000.0, +) ``` + This `OperationalCost` can be used when defining a component or added to an existing component using -`set_operation_cost!`. \ No newline at end of file +`set_operation_cost!`. diff --git a/docs/src/how_to/add_new_types.md b/docs/src/how_to/add_new_types.md index 03e9780a04..564ed1465e 100644 --- a/docs/src/how_to/add_new_types.md +++ b/docs/src/how_to/add_new_types.md @@ -54,10 +54,10 @@ the parent's reference to that child. The source file `src/base.jl` provides functions that you can implement for your new type to manage these scenarios. -- `check_component_addition(sys::System, component::Component; kwargs...)` -- `handle_component_addition!(sys::System, component::Component; kwargs...)` -- `check_component_removal(sys::System, component::Component; kwargs...)` -- `handle_component_removal!(sys::System, component::Component; kwargs...)` + - `check_component_addition(sys::System, component::Component; kwargs...)` + - `handle_component_addition!(sys::System, component::Component; kwargs...)` + - `check_component_removal(sys::System, component::Component; kwargs...)` + - `handle_component_removal!(sys::System, component::Component; kwargs...)` The functions `add_component!()` and `remove_component!()` call the check function before performing actions and then call the handle function @@ -76,9 +76,9 @@ and `throw_if_not_attached(component, system)`. You can implement three methods to perform custom validation or correction for your type. PowerSystems calls all of these functions in `add_component!`. -- `sanitize_component!(component::Component, sys::System)`: intended to make standard data corrections (e.g. voltage angle in degrees -> radians) -- `validate_component(component::Component)`: intended to check component field values for internal consistency -- `validate_component_with_system(component::Component, sys::System)`: intended to check component field values for consistency with system + - `sanitize_component!(component::Component, sys::System)`: intended to make standard data corrections (e.g. voltage angle in degrees -> radians) + - `validate_component(component::Component)`: intended to check component field values for internal consistency + - `validate_component_with_system(component::Component, sys::System)`: intended to check component field values for consistency with system ### Struct Requirements for Serialization of custom components @@ -89,23 +89,23 @@ fields become dictionary keys. The code imposes these requirements: -1. The InfrastructureSystems methods `serialize` and `deserialize` must be - implemented for the struct. InfrastructureSystems implements a method that - covers all subtypes of `InfrastructureSystemsType`. All PowerSystems - components should be subtypes of `PowerSystems.Component` which is a subtype - `InfrastructureSystemsType`, so any new structs should be covered as well. -2. All struct fields must be able to be encoded in JSON format or be covered be - covered by `serialize` and `deserialize` methods. Basic types, such as - numbers and strings or arrays and dictionaries of numbers and strings, - should just work. Complex containers with symbols may not. -3. Structs relying on the default `deserialize` method must have a kwarg-only - constructor. The deserialization code constructs objects by splatting the - dictionary key/value pairs into the constructor. -4. Structs that contain other PowerSystem components (like a generator contains - a bus) must serialize those components as UUIDs instead of actual values. - The deserialization code uses the UUIDs as a mechanism to restore a - reference to the actual object rather a new object with identical values. It - also significantly reduces the size of the JSON file. + 1. The InfrastructureSystems methods `serialize` and `deserialize` must be + implemented for the struct. InfrastructureSystems implements a method that + covers all subtypes of `InfrastructureSystemsType`. All PowerSystems + components should be subtypes of `PowerSystems.Component` which is a subtype + `InfrastructureSystemsType`, so any new structs should be covered as well. + 2. All struct fields must be able to be encoded in JSON format or be covered be + covered by `serialize` and `deserialize` methods. Basic types, such as + numbers and strings or arrays and dictionaries of numbers and strings, + should just work. Complex containers with symbols may not. + 3. Structs relying on the default `deserialize` method must have a kwarg-only + constructor. The deserialization code constructs objects by splatting the + dictionary key/value pairs into the constructor. + 4. Structs that contain other PowerSystem components (like a generator contains + a bus) must serialize those components as UUIDs instead of actual values. + The deserialization code uses the UUIDs as a mechanism to restore a + reference to the actual object rather a new object with identical values. It + also significantly reduces the size of the JSON file. Refer to `InfrastructureSystems.serialize_struct` for example behavior. New structs that are not subtypes of `InfrastructureSystemsType` may be able to @@ -164,9 +164,9 @@ end ## [Auto-generating Structs](@id autogen) Most `PowerSystems.jl` structs are auto-generated from the JSON descriptor file -`src/descriptors/power_system_structs.json`. +`src/descriptors/power_system_structs.json`. You can add your new struct -here or write it manually when contributing code to the repository. +here or write it manually when contributing code to the repository. If all you need is the basic struct definition and getter/setter functions then you will likely find the auto-generation helpful. @@ -182,9 +182,9 @@ and `generate_structs`. Full details are in the InfrastructureSystems documentat In order to merge new structs to the code base, your struct needs to pass several tests. -1. addition to `System` -2. retrieval from `System` -3. serialization/de-serialization + 1. addition to `System` + 2. retrieval from `System` + 3. serialization/de-serialization The following code block is an example of the code that the new struct needs to pass diff --git a/docs/src/how_to/adding_additional_fields.md b/docs/src/how_to/adding_additional_fields.md index 6172804f0c..0f19357286 100644 --- a/docs/src/how_to/adding_additional_fields.md +++ b/docs/src/how_to/adding_additional_fields.md @@ -14,23 +14,27 @@ system = build_system(PSISystems, "modified_RTS_GMLC_DA_sys"); #hide ``` __Step 1:__ Use `get_ext` to get the `ext` field of the desired components and assign your data: + ```@repl generated_adding_additional_fields for g in get_components(ThermalStandard, system) external_field = get_ext(g) external_field["my_data"] = 1.0 end ``` + Here, we added additional data called `my_data` to the [`ThermalStandard`](@ref) generators in a previously defined [`System`](@ref). __Step 2:__ Retrieve your data using `get_ext` again First, retrieve the first ThermalStandard generator: + ```@repl generated_adding_additional_fields gen = collect(get_components(ThermalStandard, system))[1]; ``` Then, retrieve `my_data` from the generator and verify it is 1.0, as assigned. + ```@repl generated_adding_additional_fields retrieved_data = get_ext(gen)["my_data"] ``` diff --git a/docs/src/how_to/extend_tabular_parsing.md b/docs/src/how_to/extend_tabular_parsing.md index 881876cb56..0e729b656d 100644 --- a/docs/src/how_to/extend_tabular_parsing.md +++ b/docs/src/how_to/extend_tabular_parsing.md @@ -11,23 +11,23 @@ names. ### Procedure -1. Add an entry to the array of parameters for your category in - `src/descriptors/power_system_inputs.json` according to the following: - 1. Use `snake_case` for the name field. - 2. The fields `name` and `description` are required. - 3. Try to use a name that is generic and not specific to one dataset. - 4. It is recommended that you define `unit`. - 5. If PowerSystems expects the value to be per-unit then you must specify - `system_per_unit=true`. - -2. PowerSystems has two commonly-used datasets with customized user config - files: - [PowerSystemsTestData](https://github.com/NREL/PowerSystemsTestData/blob/main/RTS_GMLC/user_descriptors.yaml) - and - [RTS_GMLC](https://github.com/GridMod/RTS-GMLC/blob/master/RTS_Data/FormattedData/SIIP/user_descriptors.yaml). - Update both of these files and submit pull requests. - -3. Parse the raw data like in this example: + 1. Add an entry to the array of parameters for your category in + `src/descriptors/power_system_inputs.json` according to the following: + + 1. Use `snake_case` for the name field. + 2. The fields `name` and `description` are required. + 3. Try to use a name that is generic and not specific to one dataset. + 4. It is recommended that you define `unit`. + 5. If PowerSystems expects the value to be per-unit then you must specify + `system_per_unit=true`. + + 2. PowerSystems has two commonly-used datasets with customized user config + files: + [PowerSystemsTestData](https://github.com/NREL/PowerSystemsTestData/blob/main/RTS_GMLC/user_descriptors.yaml) + and + [RTS_GMLC](https://github.com/GridMod/RTS-GMLC/blob/master/RTS_Data/FormattedData/SIIP/user_descriptors.yaml). + Update both of these files and submit pull requests. + 3. Parse the raw data like in this example: ```julia function demo_bus_csv_parser!(data::PowerSystemTableData) diff --git a/docs/src/how_to/get_available_generators.md b/docs/src/how_to/get_available_generators.md index 6388e5614f..c069bda85b 100644 --- a/docs/src/how_to/get_available_generators.md +++ b/docs/src/how_to/get_available_generators.md @@ -14,19 +14,23 @@ to access all the available generators in an existing [`system`](@ref System). Use [`get_available_components`](@ref) to get an iterator of all the available generators in an existing [`system`](@ref System), which also prints a summary: + ```@repl get_gens gen_iter = get_available_components(Generator, system) ``` The iterator avoids unnecessary memory allocations if there are many generators, and it can be used to view or update the generator data, such as seeing each of the names: + ```@repl get_gens get_name.(gen_iter) ``` !!! tip + Above, we use the abstract supertype [`Generator`](@ref) to get all components that are subtypes of it. You can instead get all the components of a concrete type, such as: + ```@repl get_gens gen_iter = get_available_components(RenewableDispatch, system) ``` @@ -35,6 +39,7 @@ get_name.(gen_iter) Use `collect` to get a vector of the generators instead of an iterator, which could require a lot of memory: + ```@repl get_gens gens = collect(get_available_components(Generator, system)); ``` @@ -48,7 +53,8 @@ with a filter to check for availability: gen_iter = get_components(get_available, Generator, system) ``` -`collect` can also be used to turn this iterator into a vector. +`collect` can also be used to turn this iterator into a vector. #### See Also -- How to: [Get the buses in a System](@ref) \ No newline at end of file + + - How to: [Get the buses in a System](@ref) diff --git a/docs/src/how_to/get_buses.md b/docs/src/how_to/get_buses.md index 6529165aaa..d08cb6116b 100644 --- a/docs/src/how_to/get_buses.md +++ b/docs/src/how_to/get_buses.md @@ -21,6 +21,7 @@ bus_iter = get_components(ACBus, system) The iterator avoids unnecessary memory allocations if there are many buses, and it can be used to view or update the bus data. For example, we can set the base voltage of all buses to 330 kV: + ```@repl get_buses for b in bus_iter set_base_voltage!(b, 330.0) @@ -31,6 +32,7 @@ end Use `collect` to get a vector of the buses instead of an iterator, which could require a lot of memory: + ```@repl get_buses buses = collect(get_components(ACBus, system)) ``` @@ -41,31 +43,37 @@ Use [`get_buses`](@ref) to get a vector of buses when you know which [`Area`](@r [`LoadZone`](@ref) you are interested in. First, we select an Area: + ```@repl get_buses show_components(Area, system) # See available Areas area2 = get_component(Area, system, "2"); # Get Area named 2 ``` Then call `get_buses` for that Area: + ```@repl get_buses area_buses = get_buses(system, area2) ``` -#### Option 2b: Get buses by ID number +#### Option 2b: Get buses by ID number Finally, use [`get_buses`](@ref get_buses(sys::System, bus_numbers::Set{Int})) to get a vector of buses by their ID numbers. Example getting buses with ID numbers from 101 to 110: + ```@repl get_buses buses_by_ID = get_buses(system, Set(101:110)) ``` !!! note + You can combine this with Option 1 to first view all the bus numbers if needed: + ```@repl get_buses get_number.(get_components(ACBus, system)) ``` #### See Also -- How to: [Get the available generators in a System](@ref) + + - How to: [Get the available generators in a System](@ref) diff --git a/docs/src/how_to/improve_ts_performance.md b/docs/src/how_to/improve_ts_performance.md index 0d8a5fcf1d..4e6686d5c9 100644 --- a/docs/src/how_to/improve_ts_performance.md +++ b/docs/src/how_to/improve_ts_performance.md @@ -3,14 +3,14 @@ Use the steps here to improve performance with small or large data sets, but particularly large data sets. These improvements can help handle adding large numbers of data sets or reduce overhead when accessing time series data -multiple times. +multiple times. ## Choosing the Storage Location By default, time series data is stored in an HDF5 file in the tmp file system to prevent large datasets from overwhelming system memory. However, you can change its location. -**Small data sets** +### Small data sets If your dataset will fit in your computer's memory, then you can increase performance by storing it in memory: @@ -19,13 +19,15 @@ performance by storing it in memory: sys = System(100.0; time_series_in_memory = true) ``` -**Large data sets** +### Large data sets If the system's time series data will be larger than the amount of tmp space available, use the `time_series_directory` parameter to change its location. + ```julia sys = System(100.0; time_series_directory = "bigger_directory") ``` + You can also override the location by setting the environment variable `SIENNA_TIME_SERIES_DIRECTORY` to another directory. @@ -35,11 +37,14 @@ it with `enable_compression` to get significant storage savings at the cost of C ```julia sys = System(100.0; enable_compression = true) -sys = System(100.0; compression = CompressionSettings( - enabled = true, - type = CompressionTypes.DEFLATE, # BLOSC is also supported - level = 3, - shuffle = true) +sys = System( + 100.0; + compression = CompressionSettings(; + enabled = true, + type = CompressionTypes.DEFLATE, # BLOSC is also supported + level = 3, + shuffle = true, + ), ) ``` @@ -51,7 +56,7 @@ components then can call `add_time_series!`, passing the collection of components that share the time series data. Time series data can also be shared on a component level. Suppose a time series array applies to both the `max_active_power` and `max_reactive_power` attributes of a generator. You can share the -Data. +data. ```julia resolution = Dates.Hour(1) @@ -63,14 +68,14 @@ data = Dict( forecast_max_active_power = Deterministic( "max_active_power", data, - resolution, + resolution; scaling_factor_multiplier = get_max_active_power, ) add_time_series!(sys, generator, forecast_max_active_power) # Reuse time series for second attribute forecast_max_reactive_power = Deterministic( forecast_max_active_power, - "max_reactive_power" + "max_reactive_power"; scaling_factor_multiplier = get_max_reactive_power, ) add_time_series!(sys, generator, forecast_max_reactive_power) @@ -90,10 +95,10 @@ resolution = Dates.Hour(1) associations = ( IS.TimeSeriesAssociation( gen, - Deterministic( + Deterministic(; data = read_time_series(get_name(gen) * ".csv"), name = "get_max_active_power", - resolution=resolution), + resolution = resolution), ) for gen in get_components(ThermalStandard, sys) ) @@ -112,19 +117,16 @@ into the system memory with large reads in order to mitigate this potential prob It is highly recommended that you use this interface for modeling implementations. This is particularly relevant for models using large datasets. For example: + ```julia - cache = ForecastCache(Deterministic, component, "max_active_power") - window1 = get_next_time_series_array!(cache) - window2 = get_next_time_series_array!(cache) - # or - for window in cache - @show window - end +cache = ForecastCache(Deterministic, component, "max_active_power") +window1 = get_next_time_series_array!(cache) +window2 = get_next_time_series_array!(cache) +# or +for window in cache + @show window +end ``` + Each iteration of on the cache object will deliver the next forecast window (see [`get_next_time_series_array!`](@ref)). - - - - - diff --git a/docs/src/how_to/install.md b/docs/src/how_to/install.md index abac48b5d3..aee7672760 100644 --- a/docs/src/how_to/install.md +++ b/docs/src/how_to/install.md @@ -1,10 +1,14 @@ -### [Install PowerSystems.jl](@id install) +## [Install PowerSystems.jl](@id install) PowerSystems.jl is a command line tool written in the Julia programming language. To install: -**Step 1:** [Install Julia](https://julialang.org/downloads/) +### Step 1: Install Julia -**Step 2:** Start the [Julia REPL](https://docs.julialang.org/en/v1/stdlib/REPL/) from a command line: +[Follow the instructions here](https://julialang.org/downloads/) + +### Step 2: Open Julia + +Start the [Julia REPL](https://docs.julialang.org/en/v1/stdlib/REPL/) from a command line: ``` $ julia ``` @@ -25,7 +29,9 @@ julia> If not, go back to check the Julia installation steps. -**Step 3:** Install the latest stable release of PowerSystems.jl using the +### Step 3: Install `PowerSystems.jl` + +Install the latest stable release of `PowerSystems.jl` using the [Julia package manager](https://docs.julialang.org/en/v1/stdlib/Pkg/#Pkg) with: ```julia diff --git a/docs/src/how_to/market_bid_cost.md b/docs/src/how_to/market_bid_cost.md index 7ec034c9fc..4a1bd8ce13 100644 --- a/docs/src/how_to/market_bid_cost.md +++ b/docs/src/how_to/market_bid_cost.md @@ -17,27 +17,27 @@ The code below shows an example how we can create a thermal device with MarketBi using PowerSystems, Dates bus = ACBus(1, "nodeE", "REF", 0, 1.0, (min = 0.9, max = 1.05), 230, nothing, nothing) -generator = ThermalStandard( - name = "Brighton", - available = true, - status = true, - bus = bus, - active_power = 6.0, - reactive_power = 1.50, - rating = 0.75, - prime_mover_type = PrimeMovers.ST, - fuel = ThermalFuels.COAL, - active_power_limits = (min = 0.0, max = 6.0), - reactive_power_limits = (min = -4.50, max = 4.50), - time_limits = (up = 0.015, down = 0.015), - ramp_limits = (up = 5.0, down = 3.0), - operation_cost = MarketBidCost( - no_load_cost = 0.0, - start_up = (hot = 0.0, warm = 0.0, cold = 0.0), - shut_down = 0.0, - ), - base_power = 100.0, - ) +generator = ThermalStandard(; + name = "Brighton", + available = true, + status = true, + bus = bus, + active_power = 6.0, + reactive_power = 1.50, + rating = 0.75, + prime_mover_type = PrimeMovers.ST, + fuel = ThermalFuels.COAL, + active_power_limits = (min = 0.0, max = 6.0), + reactive_power_limits = (min = -4.50, max = 4.50), + time_limits = (up = 0.015, down = 0.015), + ramp_limits = (up = 5.0, down = 3.0), + operation_cost = MarketBidCost(; + no_load_cost = 0.0, + start_up = (hot = 0.0, warm = 0.0, cold = 0.0), + shut_down = 0.0, + ), + base_power = 100.0, +) ``` ### Step 2: Creating the `TimeSeriesData` for the Market Bid @@ -52,14 +52,15 @@ ThreePartCost. Code below shows an example of how to build a TimeSeriesData. ```@repl market_bid_cost data = - Dict(Dates.DateTime("2020-01-01") => [ - [(0.0, 0.05), (290.1, 0.0733), (582.72, 0.0967), (894.1, 0.120)], - [(0.0, 0.05), (300.1, 0.0733), (600.72, 0.0967), (900.1, 0.120)],] + Dict( + Dates.DateTime("2020-01-01") => [ + [(0.0, 0.05), (290.1, 0.0733), (582.72, 0.0967), (894.1, 0.120)], + [(0.0, 0.05), (300.1, 0.0733), (600.72, 0.0967), (900.1, 0.120)]], ) -time_series_data = Deterministic( +time_series_data = Deterministic(; name = "variable_cost", data = data, - resolution = Dates.Hour(1) + resolution = Dates.Hour(1), ) ``` @@ -70,11 +71,11 @@ discern the types properly in the constructor and will return `SortedDict{Any,An the `Dict` with the data as follows: ```julia - # Very verbose dict definition - data = Dict{DateTime,Array{Array{Tuple{Float64,Float64},1},1}}() - for t in range(initial_time_sys; step = Hour(1), length = window_count) - data[t] = MY_BID_DATA - end +# Very verbose dict definition +data = Dict{DateTime, Array{Array{Tuple{Float64, Float64}, 1}, 1}}() +for t in range(initial_time_sys; step = Hour(1), length = window_count) + data[t] = MY_BID_DATA +end ``` ### Step 3a: Adding Energy Bid TimeSeriesData to the device @@ -82,9 +83,9 @@ the `Dict` with the data as follows: To add energy market bids time-series to the `MarketBidCost`, use `set_variable_cost!`. The arguments for `set_variable_cost!` are: -- `sys::System`: PowerSystem System -- `component::StaticInjection`: Static injection device -- `time_series_data::TimeSeriesData`: TimeSeriesData + - `sys::System`: PowerSystem System + - `component::StaticInjection`: Static injection device + - `time_series_data::TimeSeriesData`: TimeSeriesData ```@repl market_bid_cost sys = System(100.0, [bus], [generator]) @@ -101,10 +102,10 @@ service = VariableReserve{ReserveUp}("example_reserve", true, 0.6, 2.0) add_service!(sys, service, get_component(ThermalStandard, sys, "Brighton")) data = Dict(Dates.DateTime("2020-01-01") => [650.3, 750.0]) -time_series_data = Deterministic( +time_series_data = Deterministic(; name = get_name(service), data = data, - resolution = Dates.Hour(1) + resolution = Dates.Hour(1), ) set_service_bid!(sys, generator, service, time_series_data) ``` diff --git a/docs/src/how_to/migrating_to_psy4.md b/docs/src/how_to/migrating_to_psy4.md index 727a81dd12..ef46e9f09f 100644 --- a/docs/src/how_to/migrating_to_psy4.md +++ b/docs/src/how_to/migrating_to_psy4.md @@ -3,51 +3,59 @@ This guide outlines the code updates required to upgrade from PowerSystems.jl version 3.0 to 4.0, which was released in June 2024 and includes breaking changes. These are: -- [Renamed Types and Parameters](@ref) -- [New and Eliminated Types](@ref) -- [Updates to Energy Storage Parameters](@ref esr_migration) -- [Hydropower `status` added](@ref) -- [New Cost Functions](@ref) -- [New Time Series Horizon Format](@ref) -- [Minor Type Hierarchy Change](@ref) -- [(Temporary) Use Version 3.0 for `HybridSystem` (+ new parameter)](@ref) + - [Renamed Types and Parameters](@ref) + - [New and Eliminated Types](@ref) + - [Updates to Energy Storage Parameters](@ref esr_migration) + - [Hydropower `status` added](@ref) + - [New Cost Functions](@ref) + - [New Time Series Horizon Format](@ref) + - [Minor Type Hierarchy Change](@ref) + - [(Temporary) Use Version 3.0 for `HybridSystem` (+ new parameter)](@ref) ## Renamed Types and Parameters + Some `Types` and fields were renamed, which should require a trivial search and replace: Renamed `Types`: -- `RenewableFix` is now named [`RenewableNonDispatch`](@ref) -- `StaticReserve` is now named [`ConstantReserve`](@ref) -- `StaticReserveGroup` is now named [`ConstantReserveGroup`](@ref) -- `StaticReserveNonSpinning` is now named [`ConstantReserveNonSpinning`](@ref) -- `PriorityCurrentLimiter` is now named [`PriorityOutputCurrentLimiter`](@ref) -- `MagnitudeCurrentLimiter` is now named [`MagnitudeOutputCurrentLimiter`](@ref) -- `InstantaneousCurrentLimiter` is now named [`InstantaneousOutputCurrentLimiter`](@ref) + + - `RenewableFix` is now named [`RenewableNonDispatch`](@ref) + - `StaticReserve` is now named [`ConstantReserve`](@ref) + - `StaticReserveGroup` is now named [`ConstantReserveGroup`](@ref) + - `StaticReserveNonSpinning` is now named [`ConstantReserveNonSpinning`](@ref) + - `PriorityCurrentLimiter` is now named [`PriorityOutputCurrentLimiter`](@ref) + - `MagnitudeCurrentLimiter` is now named [`MagnitudeOutputCurrentLimiter`](@ref) + - `InstantaneousCurrentLimiter` is now named [`InstantaneousOutputCurrentLimiter`](@ref) Renamed parameters: -- The `rate` parameter is now named `rating` for subtypes of `Branch`, for + + - The `rate` parameter is now named `rating` for subtypes of `Branch`, for consistency with other Types. Affected Types are: - - [`Line`](@ref) - - [`MonitoredLine`](@ref) - - [`PhaseShiftingTransformer`](@ref) - - [`TapTransformer`](@ref) - - [`Transformer2W`](@ref) + + + [`Line`](@ref) + + [`MonitoredLine`](@ref) + + [`PhaseShiftingTransformer`](@ref) + + [`TapTransformer`](@ref) + + [`Transformer2W`](@ref) ## New and Eliminated Types + In addition to cost-related types detailed in [New Cost Functions](@ref), these new types have been added: -- [`AreaInterchange`](@ref) -- [`HybridOutputCurrentLimiter`](@ref) -- [`SaturationOutputCurrentLimiter`](@ref) + + - [`AreaInterchange`](@ref) + - [`HybridOutputCurrentLimiter`](@ref) + - [`SaturationOutputCurrentLimiter`](@ref) These types are no longer part of PowerSystems.jl, although there are future plans to rework some of them: -- `RegulationDevice` -- `Transfer` -- `BatteryEMS` -- `GenericBattery` (see [Updates to Energy Storage Parameters](@ref esr_migration)) + + - `RegulationDevice` + - `Transfer` + - `BatteryEMS` + - `GenericBattery` (see [Updates to Energy Storage Parameters](@ref esr_migration)) ## [Updates to Energy Storage Parameters](@id esr_migration) + [`EnergyReservoirStorage`](@ref) is now the default battery and energy storage model, replacing `GenericBattery`. @@ -55,61 +63,74 @@ There are also changes to the data fields compared to `GenericBattery` to improv and modeling flexibility. New data fields: -- `storage_capacity` for the maximum storage capacity (can be in units of, + + - `storage_capacity` for the maximum storage capacity (can be in units of, e.g., MWh for batteries or liters for hydrogen) - - Example: 10000.0 for 10,000 liters hydrogen -- `storage_level_limits` for the minimum and maximum allowable storage levels + + + Example: 10000.0 for 10,000 liters hydrogen + + - `storage_level_limits` for the minimum and maximum allowable storage levels on [0, 1], which can be used to model derates or other restrictions, such as state-of-charge restrictions on battery cycling - - Example: Minimum of 0.2 and maximum of 1.0 to restrict the storage from dropping below + + + Example: Minimum of 0.2 and maximum of 1.0 to restrict the storage from dropping below 20% capacity to keep some reserve margin available at all times -- `initial_storage_capacity_level` for the initial storage capacity level as + - `initial_storage_capacity_level` for the initial storage capacity level as a ratio [0, 1.0] of `storage_capacity` - - Example: 0.5 to start the storage at 50% full -- `conversion_factor` is the (optional) conversion factor of `storage_capacity` to MWh, if + + + Example: 0.5 to start the storage at 50% full + - `conversion_factor` is the (optional) conversion factor of `storage_capacity` to MWh, if different than 1.0 (i.e., no conversion is needed if the `storage_capacity` is in MWh) - - Example: 0.0005 for 0.5 kWh/l hydrogen + + + Example: 0.0005 for 0.5 kWh/l hydrogen Removed data fields: -- `state_of_charge_limits` with units of p.u.-hr -- `initial_energy` with units of p.u.-hr + + - `state_of_charge_limits` with units of p.u.-hr + - `initial_energy` with units of p.u.-hr ## Hydropower `status` added + A new required parameter, `status`, was added to [`HydroEnergyReservoir`](@ref) and [`HydroPumpedStorage`](@ref), for the initial condition of the generator. -- For [`HydroEnergyReservoir`](@ref), `status` can be `true` = on or `false` = off. -- For [`HydroPumpedStorage`](@ref), `status` can be `PumpHydroStatus.PUMP`, + + - For [`HydroEnergyReservoir`](@ref), `status` can be `true` = on or `false` = off. + - For [`HydroPumpedStorage`](@ref), `status` can be `PumpHydroStatus.PUMP`, `PumpHydroStatus.GEN`, or `PumpHydroStatus.OFF` ## New Cost Functions ## New Time Series Horizon Format + The [horizon](@ref H) for a forecast has changed from a **count** of time steps (as an - `Int`) to a **duration**, as a - [`Dates.Period`](https://docs.julialang.org/en/v1/stdlib/Dates/#Period-Types) +`Int`) to a **duration**, as a +[`Dates.Period`](https://docs.julialang.org/en/v1/stdlib/Dates/#Period-Types) **Example day-ahead forecast:** A forecast with hourly [resolution](@ref R) for the next 24 hours, with a new forecast available every 24 hours (i.e., 24-hour [interval](@ref I)) -- The horizon is now `Dates.Hour(24)` or `Dates.Day(1)` -- Previously in version 3.0, the horizon would have been `24` for the 24 1-hour time-steps + + - The horizon is now `Dates.Hour(24)` or `Dates.Day(1)` + - Previously in version 3.0, the horizon would have been `24` for the 24 1-hour time-steps in each forecast **Example hour-ahead forecast:** A forecast with 5-minute [resolution](@ref R) for the next 1 hour, with a new forecast available every hour (i.e., 1-hour [interval](@ref I)) -- The horizon is now `Dates.Hour(1)` -- Previously in version 3.0, the horizon would have been `12` for the 12 5-minute time-steps + + - The horizon is now `Dates.Hour(1)` + - Previously in version 3.0, the horizon would have been `12` for the 12 5-minute time-steps in each forecast ## Minor Type Hierarchy Change -- [`ControllableLoad`](@ref) is now a subtype of [`StaticLoad`](@ref) rather than + - [`ControllableLoad`](@ref) is now a subtype of [`StaticLoad`](@ref) rather than `ElectricLoad` The vast majority of users are not expected to be impacted by this change. ## (Temporary) Use Version 3.0 for `HybridSystem` (+ new parameter) + The [`HybridSystem`](@ref) is currently not supported in the rest of the Sienna ecosystem, such as PowerSimulations.jl. To use `HybridSystem` in simulation, revert to version 3.0. There are plans to update `HybridSystem` for version 4.0, but they have not been completed. -In addition, `HybridSystem` has a new required parameter: `interconnection_efficiency` \ No newline at end of file +In addition, `HybridSystem` has a new required parameter: `interconnection_efficiency` diff --git a/docs/src/how_to/parsing.md b/docs/src/how_to/parsing.md index a7d22a3513..870fabb442 100644 --- a/docs/src/how_to/parsing.md +++ b/docs/src/how_to/parsing.md @@ -1,12 +1,11 @@ - # [Parsing Data](@id parsing) `PowerSystems.jl` supports the creation of a `System` from a variety of common data formats: -- [MATPOWER](@ref pm_data) (code copied with permission from [`PowerModels.jl`](https://github.com/lanl-ansi/PowerModels.jl)) -- [PSS/e RAW Files](@ref pm_data) (code copied with permission from [`PowerModels.jl`](https://github.com/lanl-ansi/PowerModels.jl)) -- [PSS/e DYR Files](@ref dyr_data) -- [PowerSystems table data (CSV Files)](@ref table_data) + - [MATPOWER](@ref pm_data) (code copied with permission from [`PowerModels.jl`](https://github.com/lanl-ansi/PowerModels.jl)) + - [PSS/e RAW Files](@ref pm_data) (code copied with permission from [`PowerModels.jl`](https://github.com/lanl-ansi/PowerModels.jl)) + - [PSS/e DYR Files](@ref dyr_data) + - [PowerSystems table data (CSV Files)](@ref table_data) ## [MATPOWER / PSS/e](@id pm_data) @@ -24,7 +23,7 @@ PSS/e's dynamic model library is extensive, we currently support parsing a limit of models out of the box. | Machine models | AVR Models | Prime Movers | PSS models | -|----------------|------------|--------------|------------| +|:-------------- |:---------- |:------------ |:---------- | | GENSAE | IEEET1 | HYGOV | IEEEST | | GENSAL | ESDC1A | IEEEG1 | | | GENROE | ESAC1A | GGOV1 | | @@ -95,16 +94,22 @@ To create the system we can do it passing both files directories: ```@repl raw_dyr_system RAW_dir = joinpath(file_dir, "ThreeBusNetwork.raw") DYR_dir = joinpath(file_dir, "TestGENCLS.dyr") -dyn_system = System(RAW_dir, DYR_dir, runchecks = false) +dyn_system = System(RAW_dir, DYR_dir; runchecks = false) ``` + ### Common Issues Please note that while PSS/e does not enforce unique bus names, `PowerSystems.jl` does. To reparse bus names to comply with this requirement the `bus_name_formatter` *kwarg can be used in `System()` as shown in the example below: ```@repl raw_dyr_system -dyn_system = System(RAW_dir, DYR_dir; bus_name_formatter = x -> strip(string(x["name"])) * "-" * string(x["index"])) +dyn_system = System( + RAW_dir, + DYR_dir; + bus_name_formatter = x -> strip(string(x["name"])) * "-" * string(x["index"]), +) ``` -In this example the anonymous function `x -> strip(string(x["name"])) * "-" * string(x["index"])` takes the bus name and index from PSSe and concatenates them to produce the name. + +In this example the anonymous function `x -> strip(string(x["name"])) * "-" * string(x["index"])` takes the bus name and index from PSSe and concatenates them to produce the name. ## [PowerSystems Table Data](@id table_data) @@ -116,16 +121,18 @@ by category and column with custom names, types, and units. Components for each category must be defined in their own CSV file. The following categories are currently supported: -- branch.csv -- bus.csv (required) - - columns specifying `area` and `zone` will create a corresponding set of `Area` and `LoadZone` objects. - - columns specifying `max_active_power` or `max_reactive_power` will create `PowerLoad` objects when nonzero values are encountered and will contribute to the `peak_active_power` and `peak_reactive_power` values for the - corresponding `LoadZone` object. -- dc_branch.csv -- gen.csv -- load.csv -- reserves.csv -- storage.csv + - branch.csv + + - bus.csv (required) + + + columns specifying `area` and `zone` will create a corresponding set of `Area` and `LoadZone` objects. + + columns specifying `max_active_power` or `max_reactive_power` will create `PowerLoad` objects when nonzero values are encountered and will contribute to the `peak_active_power` and `peak_reactive_power` values for the + corresponding `LoadZone` object. + - dc_branch.csv + - gen.csv + - load.csv + - reserves.csv + - storage.csv These must reside in the directory passed when constructing PowerSystemTableData. @@ -135,28 +142,28 @@ PowerSystems requires a metadata file that maps components to their time series data in order to be able to automatically construct time_series from raw data files. The following fields are required for each time array: -- `simulation`: User description of simulation -- `resolution`: Resolution of time series in seconds -- `module`: Module that defines the abstract type of the component -- `category`: Type of component. Must map to abstract types defined by the "module" - entry (Bus, ElectricLoad, Generator, LoadZone, Reserve) -- `component_name`: Name of component -- `name`: User-defined name for the time series data. -- `normalization_factor`: Controls normalization of the data. Use 1.0 for - pre-normalized data. Use 'Max' to divide the time series by the max value in the - column. Use any float for a custom scaling factor. -- `scaling_factor_multiplier_module`: Module that defines the accessor function for the -scaling factor -- `scaling_factor_multiplier`: Accessor function of the scaling factor -- `data_file`: Path to the time series data file + - `simulation`: User description of simulation + - `resolution`: Resolution of time series in seconds + - `module`: Module that defines the abstract type of the component + - `category`: Type of component. Must map to abstract types defined by the "module" + entry (Bus, ElectricLoad, Generator, LoadZone, Reserve) + - `component_name`: Name of component + - `name`: User-defined name for the time series data. + - `normalization_factor`: Controls normalization of the data. Use 1.0 for + pre-normalized data. Use 'Max' to divide the time series by the max value in the + column. Use any float for a custom scaling factor. + - `scaling_factor_multiplier_module`: Module that defines the accessor function for the + scaling factor + - `scaling_factor_multiplier`: Accessor function of the scaling factor + - `data_file`: Path to the time series data file Notes: -- The "module", "category", and "component_name" entries must be valid arguments to retrieve -a component using `get_component(${module}.${category}, sys, $name)`. -- The "scaling_factor_multiplier_module" and the "scaling_factor_multiplier" entries must -be sufficient to return the scaling factor data using -`${scaling_factor_multiplier_module}.${scaling_factor_multiplier}(component)`. + - The "module", "category", and "component_name" entries must be valid arguments to retrieve + a component using `get_component(${module}.${category}, sys, $name)`. + - The "scaling_factor_multiplier_module" and the "scaling_factor_multiplier" entries must + be sufficient to return the scaling factor data using + `${scaling_factor_multiplier_module}.${scaling_factor_multiplier}(component)`. PowerSystems supports this metadata in either CSV or JSON formats. Refer to [RTS_GMLC](https://github.com/GridMod/RTS-GMLC/blob/master/RTS_Data/FormattedData/SIIP/timeseries_pointers.json) @@ -182,8 +189,8 @@ SIENNA_TIME_SERIES_DIRECTORY to another directory. The tabular data parser in `PowerSystems.jl` can be customized to read a variety of datasets by configuring: - - [which type of generator (`<:Generator`) to create based on the fuel and prime mover specifications](@ref csv_genmap) - - [property names](@ref csv_columns), [units](@ref csv_units), and per units conversions](@ref csv_per_unit) in *.csv files + - [which type of generator (`<:Generator`) to create based on the fuel and prime mover specifications](@ref csv_genmap) + - [property names](@ref csv_columns), [units](@ref csv_units), and per units conversions](@ref csv_per_unit) in *.csv files Here is an example of how to construct a System with all customizations listed in this section: @@ -200,13 +207,13 @@ data = PowerSystemTableData( timeseries_metadata_file = timeseries_metadata_file, generator_mapping_file = generator_mapping_file, ) -sys = System(data, time_series_in_memory = true) +sys = System(data; time_series_in_memory = true) ``` Examples configuration files can be found in the [RTS-GMLC](https://github.com/GridMod/RTS-GMLC/) repo: - - [user_descriptors.yaml](https://github.com/GridMod/RTS-GMLC/blob/master/RTS_Data/FormattedData/SIIP/user_descriptors.yaml) - - [generator_mapping.yaml](https://github.com/GridMod/RTS-GMLC/blob/master/RTS_Data/FormattedData/SIIP/generator_mapping.yaml) + - [user_descriptors.yaml](https://github.com/GridMod/RTS-GMLC/blob/master/RTS_Data/FormattedData/SIIP/user_descriptors.yaml) + - [generator_mapping.yaml](https://github.com/GridMod/RTS-GMLC/blob/master/RTS_Data/FormattedData/SIIP/generator_mapping.yaml) #### [CSV Data Configurations](@id csv_data) diff --git a/docs/src/how_to/reduce_repl_printing.md b/docs/src/how_to/reduce_repl_printing.md index 6922df6bf7..5d2f031709 100644 --- a/docs/src/how_to/reduce_repl_printing.md +++ b/docs/src/how_to/reduce_repl_printing.md @@ -1,7 +1,7 @@ # Reduce REPL printing By default `PowerSystems.jl` outputs to the REPL all Logging statements, which can be -overwhelming in some cases. +overwhelming in some cases. Use [`configure_logging`](@ref) to create a logger with your preferences for which logging statements should be printed to the console or a log file: @@ -11,7 +11,7 @@ statements should be printed to the console or a log file: ```julia using PowerSystems using Logging -configure_logging(console_level = Logging.Error) +configure_logging(; console_level = Logging.Error) ``` **Note:** log messages are not automatically flushed to files. Call diff --git a/docs/src/how_to/serialize_data.md b/docs/src/how_to/serialize_data.md index 6cbe2baef4..fcb2f91eae 100644 --- a/docs/src/how_to/serialize_data.md +++ b/docs/src/how_to/serialize_data.md @@ -9,6 +9,7 @@ dataset from simply to illustrate the process. First, load the dependencies and a `System` from `PowerSystemCaseBuilder`: + ```@repl serialize_data using PowerSystems using PowerSystemCaseBuilder @@ -18,12 +19,14 @@ sys = build_system(PSISystems, "c_sys5_pjm") ## Write data to a JSON Set up your target path, for example in a "mysystems" subfolder: + ```@repl serialize_data folder = mkdir("mysystems"); path = joinpath(folder, "system.json") ``` Now write the system to JSON: + ```@repl serialize_data to_json(sys, path) ``` @@ -31,7 +34,8 @@ to_json(sys, path) ## Read the JSON file and create a new `System` Now, you can read the file back in, and verify the new system has the same data as above: + ```@repl serialize_data sys2 = System(path) -rm(folder, recursive=true); #hide +rm(folder; recursive = true); #hide ``` diff --git a/docs/src/index.md b/docs/src/index.md index 999b1632e5..b264c676c7 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -5,11 +5,13 @@ CurrentModule = PowerSystems ``` !!! tip "Announcement" + PowerSystems.jl upgraded to version 4.0 in June 2024, which included breaking changes. Visit the [v4.0 migration guide](@ref psy4_migration) for information on how to update your existing code. !!! warning "Under Construction" + The PowerSystems.jl is being actively being rewritten for version 4.0 using the new format described in [How To Use This Documentation](@ref). Your patience is appreciated as we make this change! For now, some documentation is not located in its final home. @@ -21,11 +23,12 @@ CurrentModule = PowerSystems [Sienna ecosystem](https://www.nrel.gov/analysis/sienna.html), an open source framework for scheduling problems and dynamic simulations for power systems. The Sienna ecosystem can be [found on github](https://github.com/NREL-Sienna/Sienna). It contains three applications: -- [Sienna\Data](https://github.com/NREL-Sienna/Sienna?tab=readme-ov-file#siennadata) enables + + - [Sienna\Data](https://github.com/NREL-Sienna/Sienna?tab=readme-ov-file#siennadata) enables efficient data input, analysis, and transformation -- [Sienna\Ops](https://github.com/NREL-Sienna/Sienna?tab=readme-ov-file#siennaops) enables + - [Sienna\Ops](https://github.com/NREL-Sienna/Sienna?tab=readme-ov-file#siennaops) enables enables system scheduling simulations by formulating and solving optimization problems -- [Sienna\Dyn](https://github.com/NREL-Sienna/Sienna?tab=readme-ov-file#siennadyn) enables + - [Sienna\Dyn](https://github.com/NREL-Sienna/Sienna?tab=readme-ov-file#siennadyn) enables system transient analysis including small signal stability and full system dynamic simulations @@ -40,29 +43,29 @@ agnostic to a specific mathematical model and can be used for many model categor `PowerSystems.jl` provides tools to prepare and process data useful for electric energy systems modeling. This package serves two purposes: -1. It facilitates the development and open sharing of large data sets for Power Systems modeling -2. It provides a data model that imposes discipline on model specification, addressing the challenge of design and terminology choices when sharing code and data. + 1. It facilitates the development and open sharing of large data sets for Power Systems modeling + 2. It provides a data model that imposes discipline on model specification, addressing the challenge of design and terminology choices when sharing code and data. The main features include: -- Comprehensive and extensible library of data structures for electric systems modeling. -- Large scale data set development tools based on common text based data formats - (PSS/e `.raw` and `.dyr`, and `MATPOWER`) and configurable tabular data (e.g. CSV) - parsing capabilities. -- Optimized container for component data and time series supporting serialization to - portable file formats and configurable validation routines. + - Comprehensive and extensible library of data structures for electric systems modeling. + - Large scale data set development tools based on common text based data formats + (PSS/e `.raw` and `.dyr`, and `MATPOWER`) and configurable tabular data (e.g. CSV) + parsing capabilities. + - Optimized container for component data and time series supporting serialization to + portable file formats and configurable validation routines. ## How To Use This Documentation There are five main sections containing different information: -- **Tutorials** - Detailed walk-throughs to help you *learn* how to use + - **Tutorials** - Detailed walk-throughs to help you *learn* how to use `PowerSystems.jl` -- **How to...** - Directions to help *guide* your work for a particular task -- **Explanation** - Additional details and background information to help you *understand* + - **How to...** - Directions to help *guide* your work for a particular task + - **Explanation** - Additional details and background information to help you *understand* `PowerSystems.jl`, its structure, and how it works behind the scenes -- **Reference** - Technical references and API for a quick *look-up* during your work -- **Model Library** - Technical references of the data types and their functions that + - **Reference** - Technical references and API for a quick *look-up* during your work + - **Model Library** - Technical references of the data types and their functions that `PowerSystems.jl` uses to model power system components `PowerSystems.jl` strives to follow the [Diataxis](https://diataxis.fr/) documentation @@ -71,13 +74,16 @@ framework. ## Getting Started If you are new to `PowerSystems.jl`, here's how we suggest getting started: -1. [Install](@ref install) -2. Work through the introductory tutorial: [Create and Explore a Power `System`](@ref) to + + 1. [Install](@ref install) + 2. Work through the introductory tutorial: [Create and Explore a Power `System`](@ref) to familiarize yourself with how `PowerSystems.jl` works -3. Work through the other basic tutorials based on your interests + 3. Work through the other basic tutorials based on your interests + - See [Working with Time Series Data](@ref tutorial_time_series) if you will be doing - production cost modeling or working with time series + production cost modeling or working with time series - See [Adding Data for Dynamic Simulations](@ref) - if you are interested in [dynamic](@ref D) simulations -4. Then, see the how-to's on parsing [Matpower](@ref pm_data) or [PSS/e files](@ref dyr_data) or + if you are interested in [dynamic](@ref D) simulations + + 4. Then, see the how-to's on parsing [Matpower](@ref pm_data) or [PSS/e files](@ref dyr_data) or [CSV files](@ref table_data) to begin loading your own data into `PowerSystems.jl` diff --git a/docs/src/model_library/hydro_generation_cost.md b/docs/src/model_library/hydro_generation_cost.md index 489567c52e..aa53da2b55 100644 --- a/docs/src/model_library/hydro_generation_cost.md +++ b/docs/src/model_library/hydro_generation_cost.md @@ -6,4 +6,4 @@ Pages = ["cost_functions/HydroGenerationCost.jl"] Order = [:type, :function] Public = true Private = false -``` \ No newline at end of file +``` diff --git a/docs/src/model_library/load_cost.md b/docs/src/model_library/load_cost.md index ce6d10b50a..777483d23e 100644 --- a/docs/src/model_library/load_cost.md +++ b/docs/src/model_library/load_cost.md @@ -6,4 +6,4 @@ Pages = ["cost_functions/LoadCost.jl"] Order = [:type, :function] Public = true Private = false -``` \ No newline at end of file +``` diff --git a/docs/src/model_library/market_bid_cost.md b/docs/src/model_library/market_bid_cost.md index 3128ec0e9c..18c01aa700 100644 --- a/docs/src/model_library/market_bid_cost.md +++ b/docs/src/model_library/market_bid_cost.md @@ -6,4 +6,4 @@ Pages = ["cost_functions/MarketBidCost.jl"] Order = [:type, :function] Public = true Private = false -``` \ No newline at end of file +``` diff --git a/docs/src/model_library/renewable_generation_cost.md b/docs/src/model_library/renewable_generation_cost.md index 81d45016cf..0bd50fb297 100644 --- a/docs/src/model_library/renewable_generation_cost.md +++ b/docs/src/model_library/renewable_generation_cost.md @@ -6,4 +6,4 @@ Pages = ["cost_functions/RenewableGenerationCost.jl"] Order = [:type, :function] Public = true Private = false -``` \ No newline at end of file +``` diff --git a/docs/src/model_library/storage_cost.md b/docs/src/model_library/storage_cost.md index 10253abfd8..9e2249b60d 100644 --- a/docs/src/model_library/storage_cost.md +++ b/docs/src/model_library/storage_cost.md @@ -6,4 +6,4 @@ Pages = ["cost_functions/StorageCost.jl"] Order = [:type, :function] Public = true Private = false -``` \ No newline at end of file +``` diff --git a/docs/src/model_library/thermal_generation_cost.md b/docs/src/model_library/thermal_generation_cost.md index 9ee740b884..9dc66d0883 100644 --- a/docs/src/model_library/thermal_generation_cost.md +++ b/docs/src/model_library/thermal_generation_cost.md @@ -6,4 +6,4 @@ Pages = ["cost_functions/ThermalGenerationCost.jl"] Order = [:type, :function] Public = true Private = false -``` \ No newline at end of file +``` diff --git a/docs/src/tutorials/add_dynamic_data.md b/docs/src/tutorials/add_dynamic_data.md index 624a59cb30..383cba4cfb 100644 --- a/docs/src/tutorials/add_dynamic_data.md +++ b/docs/src/tutorials/add_dynamic_data.md @@ -8,9 +8,10 @@ simulations. To run a dynamic simulation in Sienna\Dyn using [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/), two data layers are required: -1. A base layer of [static](@ref S) components, which includes the data needed to run a + + 1. A base layer of [static](@ref S) components, which includes the data needed to run a power flow problem -2. An additional layer of [dynamic](@ref D) components, which define differential equations + 2. An additional layer of [dynamic](@ref D) components, which define differential equations to run a transient simulation We'll define these two layers sequentially. @@ -31,35 +32,42 @@ const PSY = PowerSystems; To create the system, load pre-existing data for a 3-bus system using `PowerSystemCaseBuilder.jl`: + ```@repl dyn_data threebus_sys = build_system(PSIDSystems, "3 Bus Inverter Base") ``` -See that there is a table of "Static Components", but no "Dynamic" data yet. + +See that there is a table of "Static Components", but no "Dynamic" data yet. Let's view the generators in the system with [`show_components`](@ref), including which bus they are connected at: + ```@repl dyn_data show_components(ThermalStandard, threebus_sys, [:bus]) ``` + Notice that there are generators connected at Buses 2 and 3, but not Bus 1. -Now, we are going to add the data needed to run an EMT simulation. +Now, we are going to add the data needed to run an EMT simulation. We will add an infinite voltage source to Bus 1, which is the last component we need to complete the static data layer. Then, we will a dynamic -generator or inverter model to the two generators, as well as adding dynamic lines. +generator or inverter model to the two generators, as well as adding dynamic lines. ## Add an Infinite Voltage Source Add a infinite voltage source with small impedance to Bus 1 (the reference bus). First, retrieve the reference bus using [`get_components`](@ref): + ```@repl dyn_data slack_bus = first(get_components(x -> get_bustype(x) == ACBusTypes.REF, Bus, threebus_sys)) ``` + Notice we filtered by the [bus type](@ref acbustypes_list) to get the bus(es) we wanted. Next, manually define a [`Source`](@ref): + ```@repl dyn_data -inf_source = Source( +inf_source = Source(; name = "InfBus", #name available = true, #availability active_power = 0.0, @@ -71,12 +79,13 @@ inf_source = Source( ``` And add it to the system: + ```@repl dyn_data add_component!(threebus_sys, inf_source) ``` This completes the first layer of [static](@ref S) data, using components similar to those -we added manually in the [Create and Explore a Power `System`](@ref) tutorial. +we added manually in the [Create and Explore a Power `System`](@ref) tutorial. ## Adding a Dynamic Generator @@ -85,7 +94,7 @@ Dynamic generator devices are composed by 5 components: a [Machine](@ref Machine), [Shaft](@ref Shaft), [Automatic Voltage Regulator](@ref AVR) (AVR), [Power System Stabilizer](@ref PSS) (PSS), and -[Prime Mover and Turbine Governor](@ref TurbineGov). +[Prime Mover and Turbine Governor](@ref TurbineGov). For each of those 5 components, we will select a specific model that defines the data and differential equations for that component, and then use those 5 components to define the complete dynamic generator. @@ -95,13 +104,15 @@ and then use those 5 components to define the complete dynamic generator. ``` !!! note + When defining dynamic data, by convention `PowerSystems.jl` assumes that all data is in [`DEVICE_BASE`](@ref per_unit). First, define a [Machine](@ref Machine) that describes the the stator electro-magnetic dynamics: + ```@repl dyn_data # Create the machine -machine_oneDoneQ = OneDOneQMachine( +machine_oneDoneQ = OneDOneQMachine(; R = 0.0, Xd = 1.3125, Xq = 1.2578, @@ -111,14 +122,16 @@ machine_oneDoneQ = OneDOneQMachine( Tq0_p = 0.6, ) ``` + Notice that we selected a specific model, [`OneDOneQMachine`](@ref), with the parameters tailored to a One-d-one-q dynamic machine model. Next, define a specific [Shaft](@ref Shaft) model, [`SingleMass`](@ref) that describes the rotor electro-mechanical dynamics: + ```@repl dyn_data # Shaft -shaft_no_damping = SingleMass( +shaft_no_damping = SingleMass(; H = 3.01, #(M = 6.02 -> H = M/2) D = 0.0, ) @@ -126,9 +139,10 @@ shaft_no_damping = SingleMass( Represent the electromotive dynamics of the AVR controller using a specific [Automatic Voltage Regulator](@ref AVR) model, [`AVRTypeI`](@ref): + ```@repl dyn_data # AVR: Type I: Resembles a DC1 AVR -avr_type1 = AVRTypeI( +avr_type1 = AVRTypeI(; Ka = 20.0, Ke = 0.01, Kf = 0.063, @@ -144,31 +158,37 @@ avr_type1 = AVRTypeI( Define a fixed efficiency [Prime Mover and Turbine Governor](@ref TurbineGov) with [`TGFixed`](@ref): + ```@repl dyn_data #No TG -tg_none = TGFixed(efficiency = 1.0) #efficiency +tg_none = TGFixed(; efficiency = 1.0) #efficiency ``` + See that we are modeling a machine that does not include a Turbine Governor (or PSS below), but you must define components for them to build a complete machine model. Similarly, define a PSS using [`PSSFixed`](@ref), which is used to describe the stabilization signal for the AVR: + ```@repl dyn_data #No PSS -pss_none = PSSFixed(V_pss = 0.0) +pss_none = PSSFixed(; V_pss = 0.0) ``` Now, we are ready to add a dynamic generator to the static generator at bus 102. First, let's get that static generator: + ```@repl dyn_data static_gen = get_component(Generator, threebus_sys, "generator-102-1") ``` + Notice that its `dynamic_injector` field is currently `nothing`. Use its name and the 5 components above to define its [`DynamicGenerator`](@ref) model: + ```@repl dyn_data -dynamic_gen = DynamicGenerator( +dynamic_gen = DynamicGenerator(; name = get_name(static_gen), ω_ref = 1.0, # frequency reference set-point machine = machine_oneDoneQ, @@ -178,53 +198,64 @@ dynamic_gen = DynamicGenerator( pss = pss_none, ) ``` + See that the specific component models that we selected and defined above were used to specify the states needed to model this generator in a dynamic simulation. Finally, use the dynamic version of [`add_component!`](@ref add_component!( - sys::System, - dyn_injector::DynamicInjection, - static_injector::StaticInjection; - kwargs..., +sys::System, +dyn_injector::DynamicInjection, +static_injector::StaticInjection; +kwargs..., )) to add this data to the `System`: + ```@repl dyn_data add_component!(threebus_sys, dynamic_gen, static_gen) ``` + Notice that unlike static components, which are just added to the `System`, this dynamic component is added to a specific static component within the `System`. !!! tip + To define identical dynamic devices for multiple generators at once, define the pieces of the - generator model as *functions*, such as: + generator model as *functions*, such as: + ``` avr_type1() = AVRTypeI(... ``` + When called in the `DynamicGenerator` constructor, this will create a new AVR for each generator, so they are different in memory. Later, if you decide to modify the AVR parameters for a specific generator, it will not modify the AVR in another generator. Recall that you can print the system to see a summary of its data: + ```@repl dyn_data threebus_sys ``` + See that a new table has been added: "Dynamic Components." Also, print the static generator to double-check the dynamic layer has been added: + ```@repl dyn_data static_gen ``` + Verify that `dynamic_injector` now contains our dynamic generator model. Up to this point, you have added the dynamic data necessary to do a phaser-type simulation, which focuses on machine behavior. Now we will also add dynamic inverters and lines to enable EMT simulations. -## Adding a Dynamic Inverter +## Adding a Dynamic Inverter Next we will connect a Virtual Synchronous Generator Inverter at bus 103. An inverter is composed of [Converter](@ref), [OuterControl](@ref), [InnerControl](@ref), [DCSource](@ref), [FrequencyEstimator](@ref), and [Filter](@ref) components: + ```@raw html ``` @@ -234,29 +265,33 @@ model, which defines its differential equations. First, define an [`AverageConverter`](@ref) as the specific model for the [Converter](@ref) component: + ```@repl dyn_data -converter_high_power() = AverageConverter( +converter_high_power() = AverageConverter(; rated_voltage = 138.0, - rated_current = 100.0 - ) + rated_current = 100.0, +) ``` + Recall from the tip above that we can define these components as *functions* instead of objects for reusability across multiple generators, and notice that that is what we have done here. Define [OuterControl](@ref) using [Virtual Inertia](@ref) for the active power control and [ReactivePowerDroop](@ref) for the reactive power control: + ```@repl dyn_data outer_control() = OuterControl( - VirtualInertia(Ta = 2.0, kd = 400.0, kω = 20.0), - ReactivePowerDroop(kq = 0.2, ωf = 1000.0), + VirtualInertia(; Ta = 2.0, kd = 400.0, kω = 20.0), + ReactivePowerDroop(; kq = 0.2, ωf = 1000.0), ) ``` Define an [InnerControl](@ref) as a Voltage+Current Controller with Virtual Impedance, using [`VoltageModeControl`](@ref): + ```@repl dyn_data -inner_control() = VoltageModeControl( +inner_control() = VoltageModeControl(; kpv = 0.59, #Voltage controller proportional gain kiv = 736.0, #Voltage controller integral gain kffv = 0.0, #Binary variable enabling voltage feed-forward in current controllers @@ -271,13 +306,15 @@ inner_control() = VoltageModeControl( ``` Define a [`FixedDCSource`](@ref) for the [DCSource](@ref): + ```@repl dyn_data -dc_source_lv() = FixedDCSource(voltage = 600.0) +dc_source_lv() = FixedDCSource(; voltage = 600.0) ``` Define a [FrequencyEstimator](@ref) as a phase-locked loop (PLL) using [`KauraPLL`](@ref): + ```@repl dyn_data -pll() = KauraPLL( +pll() = KauraPLL(; ω_lp = 500.0, #Cut-off frequency for LowPass filter of PLL filter. kp_pll = 0.084, #PLL proportional gain ki_pll = 4.69, #PLL integral gain @@ -285,8 +322,9 @@ pll() = KauraPLL( ``` Finally, define an [`LCLFilter`](@ref) for the [Filter](@ref): + ```@repl dyn_data -filt() = LCLFilter( +filt() = LCLFilter(; lf = 0.08, rf = 0.003, cf = 0.074, @@ -297,13 +335,15 @@ filt() = LCLFilter( Now, use those six functions to define a complete dynamic inverter by getting the static component at bus 103: + ```@repl dyn_data gen_103 = get_component(Generator, threebus_sys, "generator-103-1"); ``` using it and our six functions to define a [`DynamicInverter`](@ref): + ```@repl dyn_data -dynamic_inv = DynamicInverter( +dynamic_inv = DynamicInverter(; name = get_name(gen_103), ω_ref = 1.0, # frequency reference set-point converter = converter_high_power(), @@ -316,6 +356,7 @@ dynamic_inv = DynamicInverter( ``` and adding it to the `System`: + ```@repl dyn_data add_component!(threebus_sys, dynamic_inv, gen_103) ``` @@ -326,24 +367,30 @@ updates by adding dynamic lines. ## Adding Dynamic Lines !!! warning - A `System` must have at least two buses and one branch to run a dynamic simulation in + + A `System` must have at least two buses and one branch to run a dynamic simulation in [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/). Let's review the AC branches currently in the system: + ```@repl dyn_data get_components(ACBranch, threebus_sys) ``` -Notice that we have three static `Line` components. -Let's also print the first line to review its format: +Notice that we have three static `Line` components. + +Let's also print the first line to review its format: + ```@repl dyn_data first(get_components(Line, threebus_sys)) ``` + See that these components do not have the fields for dynamic modeling, such as fields for different [states](@ref S). Let's update that by cycling through these lines and using [`DynamicBranch`](@ref) to extend each static line with the necessary fields: + ```@repl dyn_data for l in get_components(Line, threebus_sys) # create a dynamic branch @@ -354,38 +401,45 @@ end ``` Take a look at the AC branches in the system again: + ```@repl dyn_data branches = get_components(ACBranch, threebus_sys) ``` + Notice that now there are 3 `DynamicBranch` components instead the `Line` components. Let's take a look by printing first one: + ```@repl dyn_data first(branches) ``` + Observe that this is a wrapper around the static data, with the additional states data for dynamic modeling. Finally, let's print the `System` again to summarize our additions: + ```@repl dyn_data threebus_sys ``` + Verify that the additions were successful, with an added voltage `Source`, `DynamicBranch`es replacing the static `Lines`, and two new dynamic components with the generator and inverter models. ## Next Steps -In this tutorial, you have updated a static system with a second dynamic data layer. +In this tutorial, you have updated a static system with a second dynamic data layer. The data you added can enable a phasor-based simulation using the dynamic generator, or -a more complex EMT simulation with the additional dynamic inverter and dynamic lines. +a more complex EMT simulation with the additional dynamic inverter and dynamic lines. Next, you might like to: -- Read more about the static and dynamic data layers and the dynamic data format in + + - Read more about the static and dynamic data layers and the dynamic data format in [Dynamic Devices](@ref). -- Review the specific sub-system models available in `PowerSystems.jl` for [Machine](@ref), + - Review the specific sub-system models available in `PowerSystems.jl` for [Machine](@ref), [Shaft](@ref), [AVR](@ref), [PSS](@ref), [Prime Mover and Turbine Governor](@ref TurbineGov), [Converter](@ref), [OuterControl](@ref), [InnerControl](@ref), [DCSource](@ref), [FrequencyEstimator](@ref), and [Filter](@ref) components -- Explore [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/) - for dynamics modeling in Sienna\Dyn \ No newline at end of file + - Explore [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/) + for dynamics modeling in Sienna\Dyn diff --git a/docs/src/tutorials/creating_system.md b/docs/src/tutorials/creating_system.md index b19a85a18f..132753e10b 100644 --- a/docs/src/tutorials/creating_system.md +++ b/docs/src/tutorials/creating_system.md @@ -12,11 +12,13 @@ system settings. To get started, ensure you have followed the [installation instructions](@ref install). Start Julia from the command line if you haven't already: + ``` $ julia ``` Load the PowerSystems.jl package: + ```@repl basics using PowerSystems ``` @@ -27,7 +29,7 @@ In PowerSystems.jl, data is held in a [`System`](@ref) that holds all of the ind along with some metadata about the power system itself. There are many ways to define a `System`, but let's start with an empty system. -All we need to define is a base power of 100 MVA for [per-unitization](@ref per_unit). +All we need to define is a base power of 100 MVA for [per-unitization](@ref per_unit). ```@repl basics sys = System(100.0) @@ -42,93 +44,106 @@ Now, let's add some components to our system. We'll start by creating some buses. By referring to the documentation for [ACBus](@ref), notice that we need define some basic data, including the bus's unique identifier and name, base voltage, and whether it's a [load, generator, -or reference bus](@ref acbustypes_list). +or reference bus](@ref acbustypes_list). Let's start with a reference bus: ```@repl basics -bus1 = ACBus( - number = 1, - name = "bus1", - bustype = ACBusTypes.REF, - angle = 0.0, - magnitude = 1.0, - voltage_limits = (min = 0.9, max = 1.05), - base_voltage = 230.0 - ); +bus1 = ACBus(; + number = 1, + name = "bus1", + bustype = ACBusTypes.REF, + angle = 0.0, + magnitude = 1.0, + voltage_limits = (min = 0.9, max = 1.05), + base_voltage = 230.0, +); ``` + This bus is on a 230 kV AC transmission network, with an allowable voltage range of 0.9 to 1.05 p.u. We are assuming it is currently operating at 1.0 p.u. voltage and -an angle of 0 radians. +an angle of 0 radians. Let's add this bus to our `System` with `add_component!`: + ```@repl basics add_component!(sys, bus1) ``` We can see the impact this has on the `System` simply by printing it: + ```@repl basics sys ``` + Notice that `System` now shows a summary of components in the system. Let's create a second bus: + ```@repl basics -bus2 = ACBus( - number = 2, - name = "bus2", - bustype = ACBusTypes.PV, - angle = 0.0, - magnitude = 1.0, - voltage_limits = (min = 0.9, max = 1.05), - base_voltage = 230.0 - ); +bus2 = ACBus(; + number = 2, + name = "bus2", + bustype = ACBusTypes.PV, + angle = 0.0, + magnitude = 1.0, + voltage_limits = (min = 0.9, max = 1.05), + base_voltage = 230.0, +); ``` + Notice that we've defined this bus with [power and voltage variables](@ref acbustypes_list), suitable for power flow studies. Let's also add this to our `System`: + ```@repl basics add_component!(sys, bus2) ``` Now, let's use [`show_components`](@ref) to quickly see some basic information about the buses: + ```@repl basics show_components(sys, ACBus) ``` ## Adding a Transmission Line -Let's connect our buses. We'll add a transmission [`Line`](@ref) between `bus1` and `bus2`. + +Let's connect our buses. We'll add a transmission [`Line`](@ref) between `bus1` and `bus2`. !!! warning + When defining a line that isn't attached to a `System` yet, you must define the thermal rating of the transmission line in per-unit using the base power of the `System` you plan to connect it to -- in this case, 100 MVA. ```@repl basics -line = Line( - name = "line1", - available = true, - active_power_flow = 0.0, - reactive_power_flow = 0.0, - arc = Arc(from = bus1, to = bus2), - r = 0.00281, # Per-unit - x = 0.0281, # Per-unit - b = (from = 0.00356, to = 0.00356), # Per-unit - rating = 2.0, # Line rating of 200 MVA / System base of 100 MVA - angle_limits = (min = -0.7, max = 0.7), - ); +line = Line(; + name = "line1", + available = true, + active_power_flow = 0.0, + reactive_power_flow = 0.0, + arc = Arc(; from = bus1, to = bus2), + r = 0.00281, # Per-unit + x = 0.0281, # Per-unit + b = (from = 0.00356, to = 0.00356), # Per-unit + rating = 2.0, # Line rating of 200 MVA / System base of 100 MVA + angle_limits = (min = -0.7, max = 0.7), +); ``` + Note that we also had to define an [`Arc`](@ref) in the process to define the connection between the two buses. Let's also add this to our `System`: + ```@repl basics add_component!(sys, line) ``` Finally, let's check our `System` summary to see all the network topology components we have added are attached: + ```@repl basics sys ``` @@ -136,31 +151,35 @@ sys ## Adding Loads and Generators Now that our network topology is complete, we'll start adding components that [inject](@ref I) or -withdraw power from the network. +withdraw power from the network. !!! warning + When you define components that aren't attached to a `System` yet, you must define all fields related to power (with units such as MW, MVA, MVAR, or MW/min) in per-unit using the `base_power` of the component. We'll start with defining a 10 MW [load](@ref PowerLoad) to `bus1`: + ```@repl basics -load = PowerLoad( - name = "load1", - available = true, - bus = bus1, - active_power = 0.0, # Per-unitized by device base_power - reactive_power = 0.0, # Per-unitized by device base_power - base_power = 10.0, # MVA - max_active_power = 1.0, # 10 MW per-unitized by device base_power - max_reactive_power = 0.0 - ); +load = PowerLoad(; + name = "load1", + available = true, + bus = bus1, + active_power = 0.0, # Per-unitized by device base_power + reactive_power = 0.0, # Per-unitized by device base_power + base_power = 10.0, # MVA + max_active_power = 1.0, # 10 MW per-unitized by device base_power + max_reactive_power = 0.0, +); ``` + Notice that we defined the `max_active_power`, which is 10 MW, as 1.0 in per-unit using the `base_power` of 10 MVA. We've also used the `bus1` component itself to define where this load is located in the network. Now add the load to the system: + ```@repl basics add_component!(sys, load) ``` @@ -168,48 +187,52 @@ add_component!(sys, load) Finally, we'll add two generators: one renewable and one thermal. We'll add a 5 MW solar power plant to `bus2`: + ```@repl basics -solar = RenewableDispatch( - name = "solar1", - available = true, - bus = bus2, - active_power = 0.0, # Per-unitized by device base_power - reactive_power = 0.0, # Per-unitized by device base_power - rating = 1.0, # 5 MW per-unitized by device base_power - prime_mover_type = PrimeMovers.PVe, - reactive_power_limits = (min=0.0, max=0.05), # 0 MVAR to 0.25 MVAR per-unitized by device base_power - power_factor = 1.0, - operation_cost = RenewableGenerationCost(nothing), - base_power = 5.0 # MVA - ); +solar = RenewableDispatch(; + name = "solar1", + available = true, + bus = bus2, + active_power = 0.0, # Per-unitized by device base_power + reactive_power = 0.0, # Per-unitized by device base_power + rating = 1.0, # 5 MW per-unitized by device base_power + prime_mover_type = PrimeMovers.PVe, + reactive_power_limits = (min = 0.0, max = 0.05), # 0 MVAR to 0.25 MVAR per-unitized by device base_power + power_factor = 1.0, + operation_cost = RenewableGenerationCost(nothing), + base_power = 5.0, # MVA +); ``` + Note that we've used a generic [renewable generator](@ref RenewableDispatch) to model -solar, but we can specify that it is solar through the [prime mover](@ref pm_list). +solar, but we can specify that it is solar through the [prime mover](@ref pm_list). Finally, we'll also add a 30 MW gas [thermal generator](@ref ThermalStandard): + ```@repl basics -gas = ThermalStandard( - name = "gas1", - available = true, - status = true, - bus = bus2, - active_power = 0.0, # Per-unitized by device base_power - reactive_power = 0.0, # Per-unitized by device base_power - rating = 1.0, # 30 MW per-unitized by device base_power - active_power_limits = (min=0.2, max=1.0), # 6 MW to 30 MW per-unitized by device base_power - reactive_power_limits = nothing, # Per-unitized by device base_power - ramp_limits = (up=0.2, down=0.2), # 6 MW/min up or down, per-unitized by device base_power - operation_cost = ThermalGenerationCost(nothing), - base_power = 30.0, # MVA - time_limits = (up=8.0, down=8.0), # Hours - must_run = false, - prime_mover_type = PrimeMovers.CC, - fuel = ThermalFuels.NATURAL_GAS - ); +gas = ThermalStandard(; + name = "gas1", + available = true, + status = true, + bus = bus2, + active_power = 0.0, # Per-unitized by device base_power + reactive_power = 0.0, # Per-unitized by device base_power + rating = 1.0, # 30 MW per-unitized by device base_power + active_power_limits = (min = 0.2, max = 1.0), # 6 MW to 30 MW per-unitized by device base_power + reactive_power_limits = nothing, # Per-unitized by device base_power + ramp_limits = (up = 0.2, down = 0.2), # 6 MW/min up or down, per-unitized by device base_power + operation_cost = ThermalGenerationCost(nothing), + base_power = 30.0, # MVA + time_limits = (up = 8.0, down = 8.0), # Hours + must_run = false, + prime_mover_type = PrimeMovers.CC, + fuel = ThermalFuels.NATURAL_GAS, +); ``` This time, let's add these components to our `System` using [`add_components!`](@ref) to add them both at the same time: + ```@repl basics add_components!(sys, [solar, gas]) ``` @@ -217,41 +240,49 @@ add_components!(sys, [solar, gas]) ## Explore the System and its Components Congratulations! You have built a power system including buses, a transmission line, a -load, and different types of generators. Now let's take a look around. +load, and different types of generators. Now let's take a look around. Remember that we can see a summary of our `System` using the print statement: + ```@repl basics sys ``` Now, let's double-check some of our data by retrieving it from the `System`. Let's use [`show_components`](@ref) again to get an overview of our renewable generators: + ```@repl basics show_components(sys, RenewableDispatch) ``` We just have the one renewable generator named `solar1`. Use `get_component` to retrieve it by name: + ```@repl basics retrieved_component = get_component(RenewableDispatch, sys, "solar1"); ``` Let's double-check what type of renewable generator this is using a `get_` function: + ```@repl basics get_prime_mover_type(retrieved_component) ``` + Verify that this a `PVe`, or solar photovoltaic, generator. Let's also use a `get_` function to double-check where this generator is connected in the transmission network: + ```@repl basics get_bus(retrieved_component) ``` + See that the generator's bus is linked to the actual `bus2` component in our `System`. These "getter" functions are available for all the data fields in a component. !!! tip + **Always use the `get_*` functions to retrieve the data within a component.** While in Julia a user can use `.` to access the fields of a component, we make no guarantees on the stability of field names and locations. We do however promise to @@ -262,29 +293,34 @@ These "getter" functions are available for all the data fields in a component. ## Changing `System` Per-Unit Settings Now, let's use a getter function to look up the solar generator's `rating`: + ```@repl basics get_rating(retrieved_component) ``` !!! tip "Important" + When we defined the solar generator, we defined the rating as 1.0 per-unit with a device `base_power` of 5.0 MVA. Notice that the rating now reads 0.05. After we attached this component to our `System`, its power data is being returned to us in the `System`'s units base. -Let's double-check the `System`'s units base: +Let's double-check the `System`'s units base: + ```@repl basics get_units_base(sys) ``` `SYSTEM_BASE` means all power-related (MW, MVA, MVAR, MW/min) component data in the `System`, except for each component's `base_power`, is per-unitized by the -system base power for consistency. +system base power for consistency. Check the `System`'s base_power again: + ```@repl basics get_base_power(sys) ``` + Notice that when we called `get_rating` above, the solar generator's rating, 5.0 MW, is being returned as 0.05 = (5 MVA)/(100 MVA) using the system base power. @@ -292,33 +328,41 @@ Instead of using the `System` base power, let's view everything in MW or MVA -- call "NATURAL_UNITS" in PowerSystems. Change the `System`'s unit system: + ```@repl basics set_units_base_system!(sys, "NATURAL_UNITS") ``` Now retrieve the solar generator's rating again: + ```@repl basics get_rating(retrieved_component) ``` + Notice that the value is now its "natural" value, 5.0 MVA. Finally, let's change the `System`'s unit system to the final option, "DEVICE_BASE": + ```@repl basics set_units_base_system!(sys, "DEVICE_BASE") ``` And retrieve the solar generator's rating once more: + ```@repl basics get_rating(retrieved_component) ``` + See that now the data is now 1.0 (5.0 MVA per-unitized by the generator (i.e., the device's) -`base_power` of 5.0 MVA), which is the format we used to originally define the device. +`base_power` of 5.0 MVA), which is the format we used to originally define the device. Recall that if you ever need to check a `System`'s settings, including the unit system being used by all the getter functions, you can always just print the `System`: + ```@repl basics sys ``` + See the units base is printed as one of the `System` properties. ## Next Steps @@ -327,9 +371,9 @@ In this tutorial, you manually created a power `System`, added and then retrieve and modified the `System` per-unit settings. Next, you might want to: -- [Add time series data to components in the `System`](@ref tutorial_time_series) -- [Add necessary data for dynamic simulations](@ref "Adding Data for Dynamic Simulations") -- [Import a `System` from an existing Matpower or PSSE file instead of creating it manually](@ref parsing) -- [Create your own `System` from .csv files instead of creating it manually](@ref table_data) -- [Read more to understand per-unitization in PowerSystems.jl](@ref per_unit) + - [Add time series data to components in the `System`](@ref tutorial_time_series) + - [Add necessary data for dynamic simulations](@ref "Adding Data for Dynamic Simulations") + - [Import a `System` from an existing Matpower or PSSE file instead of creating it manually](@ref parsing) + - [Create your own `System` from .csv files instead of creating it manually](@ref table_data) + - [Read more to understand per-unitization in PowerSystems.jl](@ref per_unit) diff --git a/docs/src/tutorials/get_component_data.md b/docs/src/tutorials/get_component_data.md index b511dba75f..efbf3e73e0 100644 --- a/docs/src/tutorials/get_component_data.md +++ b/docs/src/tutorials/get_component_data.md @@ -6,8 +6,7 @@ time-series data, which we will explore more in the tutorial on [Working with Time Series Data](@ref tutorial_time_series). In [Create and Explore a Power `System`](@ref), we created a basic `System` with nodes, a transmission -line, and a few generators. Let's recreate that system if you don't have it already: - +line, and a few generators. Let's recreate that system if you don't have it already: ```@setup get_component_data using PowerSystems; diff --git a/docs/src/tutorials/utils/docs_utils.jl b/docs/src/tutorials/utils/docs_utils.jl index b160c32487..5950f5c495 100644 --- a/docs/src/tutorials/utils/docs_utils.jl +++ b/docs/src/tutorials/utils/docs_utils.jl @@ -10,4 +10,4 @@ function print_struct(type) println(" $fn::$ft") end println("end") -end \ No newline at end of file +end diff --git a/docs/src/tutorials/working_with_time_series.md b/docs/src/tutorials/working_with_time_series.md index 72d4475c28..49c131468c 100644 --- a/docs/src/tutorials/working_with_time_series.md +++ b/docs/src/tutorials/working_with_time_series.md @@ -2,7 +2,7 @@ In this tutorial, we will manually add, retrieve, and inspect time-series data in different formats, including identifying which components in a power `System` have time -series data. Along the way, we will also use workarounds for missing forecast data and +series data. Along the way, we will also use workarounds for missing forecast data and reuse identical time series profiles to avoid unnecessary memory usage. ## Example Data and Setup @@ -11,18 +11,21 @@ We will make an example `System` with a wind generator and two loads, and add the time series needed to model, for example, the impacts of wind forecast uncertainty. Here is the available data: + ```@raw html ``` + For the wind generator, we have the historical point (deterministic) forecasts of power output. The forecasts were generated every 30 minutes with a 5-minute [resolution](@ref R) and 1-hour [horizon](@ref H). We also have measurements what actually happened at 5-minute resolution over the 2 hours. For the loads, note that the forecast data is missing. We only have the historical -measurements of total load for the system, which is normalized to the system's peak load. +measurements of total load for the system, which is normalized to the system's peak load. + +Load the `PowerSystems`, `Dates`, and `TimeSeries` packages to get started: -Load the `PowerSystems`, `Dates`, and `TimeSeries` packages to get started: ```@repl timeseries using PowerSystems using Dates @@ -31,158 +34,177 @@ using TimeSeries As usual, we need to define a power [`System`](@ref) that holds all our data. Let's define a simple system with a bus, a wind generator, and two loads: + ```@repl timeseries system = System(100.0); # 100 MVA base power -bus1 = ACBus( - number = 1, - name = "bus1", - bustype = ACBusTypes.REF, - angle = 0.0, - magnitude = 1.0, - voltage_limits = (min = 0.9, max = 1.05), - base_voltage = 230.0 - ); - -wind1 = RenewableDispatch( - name = "wind1", - available = true, - bus = bus1, - active_power = 0.0, # Per-unitized by device base_power - reactive_power = 0.0, # Per-unitized by device base_power - rating = 1.0, # 10 MW per-unitized by device base_power - prime_mover_type = PrimeMovers.WT, - reactive_power_limits = (min=0.0, max=0.0), # per-unitized by device base_power - power_factor = 1.0, - operation_cost = RenewableGenerationCost(nothing), - base_power = 10.0 # MVA - ); - -load1 = PowerLoad( - name = "load1", - available = true, - bus = bus1, - active_power = 0.0, # Per-unitized by device base_power - reactive_power = 0.0, # Per-unitized by device base_power - base_power = 10.0, # MVA - max_active_power = 1.0, # 10 MW per-unitized by device base_power - max_reactive_power = 0.0 - ); - -load2 = PowerLoad( - name = "load2", - available = true, - bus = bus1, - active_power = 0.0, # Per-unitized by device base_power - reactive_power = 0.0, # Per-unitized by device base_power - base_power = 30.0, # MVA - max_active_power = 1.0, # 10 MW per-unitized by device base_power - max_reactive_power = 0.0 - ); +bus1 = ACBus(; + number = 1, + name = "bus1", + bustype = ACBusTypes.REF, + angle = 0.0, + magnitude = 1.0, + voltage_limits = (min = 0.9, max = 1.05), + base_voltage = 230.0, +); + +wind1 = RenewableDispatch(; + name = "wind1", + available = true, + bus = bus1, + active_power = 0.0, # Per-unitized by device base_power + reactive_power = 0.0, # Per-unitized by device base_power + rating = 1.0, # 10 MW per-unitized by device base_power + prime_mover_type = PrimeMovers.WT, + reactive_power_limits = (min = 0.0, max = 0.0), # per-unitized by device base_power + power_factor = 1.0, + operation_cost = RenewableGenerationCost(nothing), + base_power = 10.0, # MVA +); + +load1 = PowerLoad(; + name = "load1", + available = true, + bus = bus1, + active_power = 0.0, # Per-unitized by device base_power + reactive_power = 0.0, # Per-unitized by device base_power + base_power = 10.0, # MVA + max_active_power = 1.0, # 10 MW per-unitized by device base_power + max_reactive_power = 0.0, +); + +load2 = PowerLoad(; + name = "load2", + available = true, + bus = bus1, + active_power = 0.0, # Per-unitized by device base_power + reactive_power = 0.0, # Per-unitized by device base_power + base_power = 30.0, # MVA + max_active_power = 1.0, # 10 MW per-unitized by device base_power + max_reactive_power = 0.0, +); add_components!(system, [bus1, wind1, load1, load2]) ``` Recall that we can also set the `System`'s unit base to natural units (MW) to make it easier to inspect results: + ```@repl timeseries set_units_base_system!(system, "NATURAL_UNITS") ``` -Before we get started, print `wind1` to see its data: +Before we get started, print `wind1` to see its data: + ```@repl timeseries wind1 ``` -See the `has_time_series` field at the bottom is `false`. + +See the `has_time_series` field at the bottom is `false`. Recall that we also can see a summary of the system by printing it: + ```@repl timeseries system ``` + Observe that there is no mention of time series data in the system yet. # Add and Retrieve a Single Time Series Let's start by defining and attaching the wind measurements shown in the data above. -This is a single time series profile, so we will use a [`SingleTimeSeries`](@ref). +This is a single time series profile, so we will use a [`SingleTimeSeries`](@ref). First, define a `TimeSeries.TimeArray` of input data, using the 5-minute [resolution](@ref R) to define the time-stamps in the example data: + ```@repl timeseries wind_values = [6.0, 7, 7, 6, 7, 9, 9, 9, 8, 8, 7, 6, 5, 5, 5, 5, 5, 6, 6, 6, 7, 6, 7, 7]; resolution = Dates.Minute(5); -timestamps = range(DateTime("2020-01-01T08:00:00"), step = resolution, length = 24); +timestamps = range(DateTime("2020-01-01T08:00:00"); step = resolution, length = 24); wind_timearray = TimeArray(timestamps, wind_values); ``` Now, use the input data to define a Single Time Series in PowerSystems: + ```@repl timeseries -wind_time_series = SingleTimeSeries( +wind_time_series = SingleTimeSeries(; name = "max_active_power", - data = wind_timearray - ); + data = wind_timearray, +); ``` + Note that we've chosen the name `max_active_power`, which is the default time series profile name when using [PowerSimulations.jl](https://nrel-sienna.github.io/PowerSimulations.jl/stable/formulation_library/RenewableGen/) for simulations. So far, this time series has been defined, but not attached to our `System` in any way. Now, -attach it to `wind1` using [`add_time_series!`](@ref add_time_series!(sys::System, component::Component, time_series::TimeSeriesData; features...)): +attach it to `wind1` using [`add_time_series!`](@ref add_time_series!(sys::System, component::Component, time_series::TimeSeriesData; features...)): + ```@repl timeseries add_time_series!(system, wind1, wind_time_series); ``` Let's double-check this worked by calling [`show_time_series`](@ref): + ```@repl timeseries show_time_series(wind1) ``` + Now `wind1` has the first time-series data set. Recall that you can also print `wind1` and -check the `has_time_series` field like we did above. +check the `has_time_series` field like we did above. Finally, let's retrieve and inspect the new timeseries, using `get_time_series_array`: + ```@repl timeseries get_time_series_array(SingleTimeSeries, wind1, "max_active_power") ``` -Verify this matches your expectation based on the input data. + +Verify this matches your expectation based on the input data. # Add and Retrieve a Forecast Next, let's add the wind power forecasts. We will use a [`Deterministic`](@ref) format for -the point forecasts. +the point forecasts. Because we have forecasts with at different [initial times](@ref I), the input data must be a dictionary where the keys are the initial times and the values are vectors or `TimeSeries.TimeArray`s of the forecast data. Set up the example input data: + ```@repl timeseries wind_forecast_data = Dict( DateTime("2020-01-01T08:00:00") => [5.0, 6, 7, 7, 7, 8, 9, 10, 10, 9, 7, 5], DateTime("2020-01-01T08:30:00") => [9.0, 9, 9, 9, 8, 7, 6, 5, 4, 5, 4, 4], - DateTime("2020-01-01T09:00:00") => [6.0, 6, 5, 5, 4, 5, 6, 7, 7, 7, 6, 6] + DateTime("2020-01-01T09:00:00") => [6.0, 6, 5, 5, 4, 5, 6, 7, 7, 7, 6, 6], ); ``` Define the `Deterministic` forecast and attach it to `wind1`: + ```@repl timeseries wind_forecast = Deterministic("max_active_power", wind_forecast_data, resolution); add_time_series!(system, wind1, wind_forecast); ``` Let's call `show_time_series` once again: + ```@repl timeseries show_time_series(wind1) ``` + Notice that we now have two types of time series listed -- the single time series and -the forecasts. +the forecasts. Finally, let's retrieve the forecast data to double check it was added properly, specifying -the initial time to get the 2nd forecast window starting at 8:30: +the initial time to get the 2nd forecast window starting at 8:30: + ```@repl timeseries get_time_series_array( Deterministic, wind1, - "max_active_power", + "max_active_power"; start_time = DateTime("2020-01-01T08:30:00"), ) ``` @@ -195,22 +217,25 @@ power, so we'll use it to scale both of our loads. We call normalized time serie First, let's create our input data `TimeSeries.TimeArray` with the example data and the same time stamps we used in the wind time series: + ```@repl timeseries load_values = [0.3, 0.3, 0.3, 0.3, 0.4, 0.4, 0.4, 0.4, 0.5, 0.5, 0.6, 0.6, - 0.7, 0.8, 0.8, 0.8, 0.8, 0.8, 0.9, 0.8, 0.8, 0.8, 0.8, 0.8]; + 0.7, 0.8, 0.8, 0.8, 0.8, 0.8, 0.9, 0.8, 0.8, 0.8, 0.8, 0.8]; load_timearray = TimeArray(timestamps, load_values); ``` Again, define a [`SingleTimeSeries`](@ref), but this time use the `scaling_factor_multiplier`parameter to scale this time series from -normalized values to power values: +normalized values to power values: + ```@repl timeseries -load_time_series = SingleTimeSeries( +load_time_series = SingleTimeSeries(; name = "max_active_power", - data = load_timearray; - scaling_factor_multiplier = get_max_active_power - ); + data = load_timearray, + scaling_factor_multiplier = get_max_active_power, +); ``` + Notice that we assigned the [`get_max_active_power`](@ref get_max_active_power(value::PowerLoad)) *function* to scale the time series, rather than a value, making the time series reusable for multiple @@ -218,100 +243,116 @@ components or multiple fields in a component. Now, add the scaling factor time series to both loads to save memory and avoid data duplication: + ```@repl timeseries add_time_series!(system, [load1, load2], load_time_series); ``` Let's take a look at `load1`, including printing its parameters... + ```@repl timeseries load1 ``` ...as well as its time series: + ```@repl timeseries show_time_series(load1) ``` !!! tip "Important" + Notice that each load now has two references to `max_active_power`. This is intentional. - There is the parameter, `max_active_power`, which is the + There is the parameter, `max_active_power`, which is the maximum demand of each load at any time (10 MW). There is also `max_active_power` the time series, which is the time varying demand over the 2-hour window, calculated using the scaling factors and the `max_active_power` parameter. - + This means that if we change the `max_active_power` parameter, the time series will also change when we retrieve it! This is also true when we apply the same scaling factors to multiple components or parameters. Let's check the impact that these two `max_active_power` data sources have on the times series data when we retrieve it. Get the `max_active_power` time series for `load1`: + ```@repl timeseries get_time_series_array(SingleTimeSeries, load1, "max_active_power") # in MW ``` + See that the normalized values have been scaled up by 10 MW. Now let's at `load2`. First check its `max_active_power` parameter: + ```@repl timeseries get_max_active_power(load2) ``` + This has a higher peak maximum demand of 30 MW. Next, retrieve it's `max_active_power` time series: + ```@repl timeseries get_time_series_array(SingleTimeSeries, load2, "max_active_power") # in MW ``` -Observe the difference compared to `load1`'s time series. + +Observe the difference compared to `load1`'s time series. Finally, retrieve the underlying time series data with no scaling factor multiplier applied: + ```@repl timeseries get_time_series_array(SingleTimeSeries, load2, - "max_active_power", - ignore_scaling_factors = true + "max_active_power"; + ignore_scaling_factors = true, ) ``` + Notice that this is the normalized input data, which is still being stored underneath. Each load is using a reference to that data when we call `get_time_series_array` to avoid -unnecessary data duplication. +unnecessary data duplication. # Transform a `SingleTimeSeries` into a Forecast Finally, let's use a workaround to handle the missing load forecast data. We will assume a -perfect forecast where the forecast is based on the `SingleTimeSeries` we just added. +perfect forecast where the forecast is based on the `SingleTimeSeries` we just added. Rather than unnecessarily duplicating and reformatting data, use PowerSystems.jl's dedicated [`transform_single_time_series!`](@ref) function to generate a [`DeterministicSingleTimeSeries`](@ref), -which saves memory while behaving just like a `Deterministic` forecast: +which saves memory while behaving just like a `Deterministic` forecast: ```@repl timeseries transform_single_time_series!( system, Dates.Hour(1), # horizon - Dates.Minute(30) # interval + Dates.Minute(30), # interval ); ``` Let's see the results for `load1`'s time series summary: + ```@repl timeseries show_time_series(load1) ``` + Notice we now have a load forecast data set with the resolution, horizon, and, interval matching our wind forecasts. Retrieve the first forecast window: + ```@repl timeseries get_time_series_array( DeterministicSingleTimeSeries, load1, - "max_active_power", + "max_active_power"; start_time = DateTime("2020-01-01T08:00:00"), ) ``` + See that `load1`'s scaling factor multiplier is still being applied as expected. Continue to the next section to address one more impact of calling -`transform_single_time_series!` on the entire `System`. +`transform_single_time_series!` on the entire `System`. # Finding, Retrieving, and Inspecting Time Series @@ -320,14 +361,17 @@ where are we will also examine components with time series and retrieve the time series data in a few more ways. First, recall that we can print a component to check its `has_time_series` field: + ```@repl timeseries load1 ``` Also, recall we can print the `System` to summarize the data in our system: + ```@repl timeseries system ``` + Notice that a new table has been added -- the Time Series Summary, showing the count of each Type of component that has a given time series type. @@ -339,40 +383,48 @@ forecasts for our `RenewableDispatch` generator (`wind1`). This was a side effec Let's remove it with [`remove_time_series!`](@ref). Since we have one wind generator, we could easily do it for that component, but let's do programmatically instead by its Type: + ```@repl timeseries for g in get_components(x -> has_time_series(x), RenewableDispatch, system) remove_time_series!(system, DeterministicSingleTimeSeries, g, "max_active_power") end ``` + Notice that we also filtered for components where `has_time_series` is `true`, which is a simple way to find and manipulate components with time series. Let's double check `wind1` now: + ```@repl timeseries show_time_series(wind1) ``` + See the unnecessary data is gone. Finally, let's do a last data sanity check on the forecasts. Since we defined the wind time series in MW instead of scaling factors, let's make sure none of our forecasts exceeds -the `max_active_power` parameter. +the `max_active_power` parameter. Instead of using `get_time_series_array` where we need to remember some details of the time series we're looking up, let's use [`get_time_series_keys`](@ref) to refresh our memories: + ```@repl timeseries keys = get_time_series_keys(wind1) ``` See the forecast key is first, so let's retrieve it using [`get_time_series`](@ref): + ```@repl timeseries forecast = get_time_series(wind1, keys[1]) ``` + See that unlike when we used `get_time_series_array`, this returns an object we can manipulate. Use [`iterate_windows`](@ref) to cycle through the 3 forecast windows and inspect the peak value: + ```@repl timeseries for window in iterate_windows(forecast) @show values(maximum(window)) @@ -381,18 +433,22 @@ end Finally, use [`get_max_active_power`](@ref get_max_active_power(d::RenewableGen)) to check the expected maximum: + ```@repl timeseries get_max_active_power(wind1) ``` + See that the forecasts are not exceeding this maximum -- sanity check complete. !!! tip + Unlike `PowerLoad` components, `RenewableDispatch` components do not have a `max_active_power` field, so check - [`get_max_active_power`](@ref get_max_active_power(d::RenewableGen)) + [`get_max_active_power`](@ref get_max_active_power(d::RenewableGen)) to see how its calculated. # Next Steps + In this tutorial, you defined, added, and retrieved four time series data sets, including static time series and deterministic forecasts. Along the way, we reduced data duplication using normalized scaling factors for reuse by multiple components @@ -400,7 +456,8 @@ or component fields, as well as by referencing a `StaticTimeSeries` to address m forecast data. Next you might like to: -- [Parse many timeseries data sets from CSV's](@ref parsing_time_series) -- [See how to improve performance efficiency with your own time series data](@ref "Improve Performance with Time Series Data") -- [Review the available time series data formats](@ref ts_data) -- [Learn more about how times series data is stored](@ref "Data Storage") + + - [Parse many timeseries data sets from CSV's](@ref parsing_time_series) + - [See how to improve performance efficiency with your own time series data](@ref "Improve Performance with Time Series Data") + - [Review the available time series data formats](@ref ts_data) + - [Learn more about how times series data is stored](@ref "Data Storage") diff --git a/scripts/formatter/formatter_code.jl b/scripts/formatter/formatter_code.jl index 080b7f2585..be04d5948f 100644 --- a/scripts/formatter/formatter_code.jl +++ b/scripts/formatter/formatter_code.jl @@ -5,12 +5,12 @@ Pkg.update() using JuliaFormatter -main_paths = ["./src", "./test"] +main_paths = ["./src", "./test", "./docs/src"] for main_path in main_paths for (root, dir, files) in walkdir(main_path) for f in files @show file_path = abspath(root, f) - !occursin(".jl", f) && continue + !((occursin(".jl", f) || occursin(".md", f))) && continue format(file_path; whitespace_ops_in_indices = true, remove_extra_newlines = true, @@ -20,6 +20,8 @@ for main_path in main_paths conditional_to_if = true, join_lines_based_on_source = true, separate_kwargs_with_semicolon = true, + format_markdown = true, + ignore = ["how_to/install.md"] # complicated formatting, formatter fails # always_use_return = true. # Disabled since it throws a lot of false positives ) diff --git a/src/parsers/im_io/LICENCE.md b/src/parsers/im_io/LICENCE.md index 35bb6daccf..a12e2bb2f7 100644 --- a/src/parsers/im_io/LICENCE.md +++ b/src/parsers/im_io/LICENCE.md @@ -1,9 +1,10 @@ -Copyright (c) 2016, Los Alamos National Security, LLC -All rights reserved. -Copyright 2016. Los Alamos National Security, LLC. This software was produced under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National Laboratory (LANL), which is operated by Los Alamos National Security, LLC for the U.S. Department of Energy. The U.S. Government has rights to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce derivative works, such modified software should be clearly marked, so as not to confuse it with the version available from LANL. - -Additionally, redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of Los Alamos National Security, LLC, Los Alamos National Laboratory, LANL, the U.S. Government, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2016, Los Alamos National Security, LLC +All rights reserved. +Copyright 2016. Los Alamos National Security, LLC. This software was produced under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National Laboratory (LANL), which is operated by Los Alamos National Security, LLC for the U.S. Department of Energy. The U.S. Government has rights to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce derivative works, such modified software should be clearly marked, so as not to confuse it with the version available from LANL. + +Additionally, redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + 3. Neither the name of Los Alamos National Security, LLC, Los Alamos National Laboratory, LANL, the U.S. Government, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/parsers/pm_io/LICENSE.md b/src/parsers/pm_io/LICENSE.md index 13369844c6..a12e2bb2f7 100644 --- a/src/parsers/pm_io/LICENSE.md +++ b/src/parsers/pm_io/LICENSE.md @@ -3,7 +3,8 @@ All rights reserved. Copyright 2016. Los Alamos National Security, LLC. This software was produced under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National Laboratory (LANL), which is operated by Los Alamos National Security, LLC for the U.S. Department of Energy. The U.S. Government has rights to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce derivative works, such modified software should be clearly marked, so as not to confuse it with the version available from LANL. Additionally, redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of Los Alamos National Security, LLC, Los Alamos National Laboratory, LANL, the U.S. Government, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + 3. Neither the name of Los Alamos National Security, LLC, Los Alamos National Laboratory, LANL, the U.S. Government, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 9ab25cb010f3e9b8dfd89e35a72b96023de1b8c5 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 2 Sep 2024 11:23:14 -0600 Subject: [PATCH 14/43] Bug fix getting started list format --- docs/src/index.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index b264c676c7..2d87abcec4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -79,11 +79,10 @@ If you are new to `PowerSystems.jl`, here's how we suggest getting started: 2. Work through the introductory tutorial: [Create and Explore a Power `System`](@ref) to familiarize yourself with how `PowerSystems.jl` works 3. Work through the other basic tutorials based on your interests - - - See [Working with Time Series Data](@ref tutorial_time_series) if you will be doing - production cost modeling or working with time series - - See [Adding Data for Dynamic Simulations](@ref) - if you are interested in [dynamic](@ref D) simulations - + + + See [Working with Time Series Data](@ref tutorial_time_series) if you will be doing + production cost modeling or working with time series + + See [Adding Data for Dynamic Simulations](@ref) + if you are interested in [dynamic](@ref D) simulations 4. Then, see the how-to's on parsing [Matpower](@ref pm_data) or [PSS/e files](@ref dyr_data) or [CSV files](@ref table_data) to begin loading your own data into `PowerSystems.jl` From 43abcd4b9b1bda3d3f4d58a50e30fdd6bf9f10c1 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 2 Sep 2024 20:07:25 -0600 Subject: [PATCH 15/43] Move examples from system.md to json how-to and draft tutorial on getting and setting --- docs/make.jl | 2 +- docs/src/explanation/system.md | 150 ++--------------------- docs/src/how_to/serialize_data.md | 84 +++++++++++-- docs/src/tutorials/get_component_data.md | 69 +++++++++++ 4 files changed, 159 insertions(+), 146 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index bd72aca01d..5befc20519 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -27,7 +27,7 @@ pages = OrderedDict( "...add additional data to a component" => "how_to/adding_additional_fields.md", "...customize or add a new Type" => "how_to/add_new_types.md", "...improve performance with time series data" => "how_to/improve_ts_performance.md", - "...serialize data to a JSON" => "how_to/serialize_data.md", + "...write and read data with a JSON" => "how_to/serialize_data.md", "...reduce REPL printing" => "how_to/reduce_repl_printing.md", "...migrate from version 3.0 to 4.0" => "how_to/migrating_to_psy4.md" ], diff --git a/docs/src/explanation/system.md b/docs/src/explanation/system.md index 598facc637..08281d7bdc 100644 --- a/docs/src/explanation/system.md +++ b/docs/src/explanation/system.md @@ -10,40 +10,21 @@ at time of the query, and so avoids overwhelming the memory resources. ``` -## Accessing components stored in the system +## Accessing components stored in the `System` `PowerSystems.jl` implements a wide variety of methods to search for components to -aid in the development of models. The code block shows an example of -retrieving components through the type hierarchy with the [`get_components`](@ref) -function and exploiting the type hierarchy for modeling purposes. +aid in data manipulation. Most of these use the [Type Structure](@ref type_structure) to +retrieve all components of a certain `Type`. -The default implementation of the function [`get_components`](@ref) takes the desired device -type (concrete or abstract) and the system and it also accepts filter functions for a more -refined search. The container is optimized for iteration over abstract or concrete component -types as described by the [Type Structure](@ref type_structure). Given the potential size of the return, -`PowerSystems.jl` returns Julia iterators in order to avoid unnecessary memory allocations. - -```@repl system -using PowerSystems -file_dir = joinpath(pkgdir(PowerSystems), "docs", "src", "tutorials", "tutorials_data") -system = System(joinpath(file_dir, "RTS_GMLC.m")); -thermal_gens = get_components(ThermalStandard, system) -``` - -It is also possible to execute [`get_components`](@ref) with abstract types from the -[abstract tree](@ref type_structure). For instance, it is possible to retrieve all renewable -generators - -```@repl system -thermal_gens = get_components(RenewableGen, system) -``` - -The most common filtering requirement is by component name and for this case the method -[`get_component`](@ref) returns a single component taking the device type, system and name as arguments. +For example, the most common search function is [`get_components`](@ref), which +takes a desired device `Type` (concrete or abstract) and retrieves all components in that +category from the `System`. It also accepts filter functions for a more +refined search. -```@repl system -my_thermal_gen = get_component(ThermalStandard, system, "323_CC_1") -``` +Given the potential size of the return, +`PowerSystems.jl` returns Julia iterators in order to avoid unnecessary memory allocations. +The container is optimized for iteration over abstract or concrete component +types as described by the [Type Structure](@ref type_structure). ## [Accessing data stored in a component](@id dot_access) @@ -55,111 +36,4 @@ access to this data using the "dot" access (e.g. `component.field`), however _this is actively discouraged_ for two reasons: 1. We make no guarantees on the stability of component structure definitions. We will maintain version stability on the accessor methods. - 2. Per-unit conversions are made in the return of data from the accessor functions. (see the [per-unit section](@ref per_unit) for more details) - -For example, the `my_thermal_gen.active_power_limits` parameter of a thermal generator should be accessed as follows: - -```@repl system -get_active_power_limits(my_thermal_gen) -``` - -You can also view data from all instances of a concrete type in one table with the function `show_components`. It provides a few options: - - 1. View the standard fields by accepting the defaults. - 2. Pass a dictionary where the keys are column names and the values are functions that accept a component as a single argument. - 3. Pass a vector of symbols that are field names of the type. - -```@repl system -show_components(system, ThermalStandard) -show_components(system, ThermalStandard, Dict("has_time_series" => x -> has_time_series(x))) -show_components(system, ThermalStandard, [:active_power, :reactive_power]) -``` - -## JSON Serialization - -`PowerSystems.jl` provides functionality to serialize an entire system to a JSON -file and then deserialize it back to a system. The main benefit is that -deserializing is significantly faster than reconstructing the system from raw -data files. - -The function that serializes the system [`to_json`](@ref) requires the system and a file name - -```julia -to_json(system, "system.json") -``` - -The serialization process stores 3 files - - 1. System data file (`*.json` file) - 2. Validation data file (`*.json` file) - 3. Time Series data file (`*.h5` file) - -To deserialize: - -```julia -system2 = System("system.json") -``` - -PowerSystems generates UUIDs for the System and all components in order to have -a way to uniquely identify objects. During deserialization it restores the same -UUIDs. If you will modify the System or components after deserialization then -it is recommended that you set this flag to generate new UUIDs. - -```julia -system2 = System("system.json"; assign_new_uuids = true) -``` - -## Viewing PowerSystems Data in JSON Format - -PowerSystems data can be serialized and deserialized in JSON. This section shows how to -explore the data outside of Julia using. - -```julia -system = System("system.json") -``` - -It can be useful to view and filter the PowerSystems data in this format. There -are many tools available to browse JSON data. - -Here is an example [GUI tool](http://jsonviewer.stack.hu) that is available -online in a browser. - -The command line utility [jq](https://stedolan.github.io/jq/) offers even more -features. The rest of this document provides example commands. - - - View the entire file pretty-printed - -```zsh -jq . system.json -``` - - - View the PowerSystems component types - -```zsh -jq '.data.components | .[] | .__metadata__ | .type' system.json | sort | uniq -``` - - - View specific components - -```zsh -jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard")' system.json -``` - - - Get the count of a component type - -```zsh -# There is almost certainly a better way. -jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard")' system.json | grep -c ThermalStandard -``` - - - View specific component by name - -```zsh -jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard" and .name == "107_CC_1")' system.json -``` - - - Filter on a field value - -```zsh -jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard" and .active_power > 2.3)' system.json -``` + 2. Per-unit conversions are made in the return of data from the accessor functions. (see the [per-unit section](@ref per_unit) for more details) \ No newline at end of file diff --git a/docs/src/how_to/serialize_data.md b/docs/src/how_to/serialize_data.md index fcb2f91eae..2ee9cd31d3 100644 --- a/docs/src/how_to/serialize_data.md +++ b/docs/src/how_to/serialize_data.md @@ -1,9 +1,16 @@ -# Serialize Data to a JSON +# Write, View, and Load Data with a JSON -PowerSystems.jl supports serializing/deserializing data with JSON. This provides an example -of how to write and read a `System` to/from disk. +`PowerSystems.jl` provides functionality to serialize an entire [`System`](@ref) to a JSON +file and then deserialize it back to a `System`. The main benefit is that +deserializing is significantly faster than reconstructing the `System` from raw +data files. -You can do this to save your own custom system, but we'll use an existing +The sections below show how to write data to a JSON, explore the data while it is in +JSON format, and load Data saved in a JSON back into `PowerSystems.jl`. + +## Write data to a JSON + +You can do this to save your own custom `System`, but we'll use an existing dataset from [`PowerSystemCaseBuilder.jl`](https://github.com/NREL-Sienna/PowerSystemCaseBuilder.jl), simply to illustrate the process. @@ -16,8 +23,6 @@ using PowerSystemCaseBuilder sys = build_system(PSISystems, "c_sys5_pjm") ``` -## Write data to a JSON - Set up your target path, for example in a "mysystems" subfolder: ```@repl serialize_data @@ -30,12 +35,77 @@ Now write the system to JSON: ```@repl serialize_data to_json(sys, path) ``` +Notice in the `Info` statements that the serialization process stores 3 files: + + 1. System data file (`*.json` file) + 2. Validation data file (`*.json` file) + 3. Time Series data file (`*.h5` file) + +## Viewing `PowerSystems` Data in JSON Format + +Some users prefer to view and filter the `PowerSystems.jl` data while it is in JSON format. +There are many tools available to browse JSON data. + +Here is an example [GUI tool](http://jsonviewer.stack.hu) that is available +online in a browser. + +The command line utility [jq](https://stedolan.github.io/jq/) offers even more +features. Below are some example commands, called from the command line within the +"mysystems" subfolder: + +View the entire file pretty-printed: + +```zsh +jq . system.json +``` + +View the `PowerSystems` component types: + +```zsh +jq '.data.components | .[] | .__metadata__ | .type' system.json | sort | uniq +``` + +View specific components: + +```zsh +jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard")' system.json +``` + +Get the count of a component type: + +```zsh +# There is almost certainly a better way. +jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard")' system.json | grep -c ThermalStandard +``` + +View specific component by name: + +```zsh +jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard" and .name == "107_CC_1")' system.json +``` + +Filter on a field value: + +```zsh +jq '.data.components | .[] | select(.__metadata__.type == "ThermalStandard" and .active_power > 2.3)' system.json +``` ## Read the JSON file and create a new `System` -Now, you can read the file back in, and verify the new system has the same data as above: +Finally, you can read the file back in, and verify the new system has the same data as above: ```@repl serialize_data sys2 = System(path) rm(folder; recursive = true); #hide ``` + +!!! tip + + PowerSystems generates UUIDs for the `System` and all components in order to have + a way to uniquely identify objects. During deserialization it restores the same + UUIDs. If you will modify the `System` or components after deserialization then + it is recommended that you set this flag to generate new UUIDs. + + ```julia + system2 = System(path; assign_new_uuids = true) + ``` diff --git a/docs/src/tutorials/get_component_data.md b/docs/src/tutorials/get_component_data.md index efbf3e73e0..9d851bc202 100644 --- a/docs/src/tutorials/get_component_data.md +++ b/docs/src/tutorials/get_component_data.md @@ -54,3 +54,72 @@ bus1 = get_component(ACBus, sys, "nodeA") @show get_magnitude(bus1); nothing #hide ``` + +# copied over from the System + +## Accessing components stored in the system + + +```@repl system +using PowerSystems +file_dir = joinpath(pkgdir(PowerSystems), "docs", "src", "tutorials", "tutorials_data") +system = System(joinpath(file_dir, "RTS_GMLC.m")); +thermal_gens = get_components(ThermalStandard, system) +``` + +It is also possible to execute [`get_components`](@ref) with abstract types from the +[abstract tree](@ref type_structure). For instance, it is possible to retrieve all renewable +generators + +```@repl system +thermal_gens = get_components(RenewableGen, system) +``` + +The most common filtering requirement is by component name and for this case the method +[`get_component`](@ref) returns a single component taking the device type, system and name as arguments. + +```@repl system +my_thermal_gen = get_component(ThermalStandard, system, "323_CC_1") +``` + +## Accessing data stored in a component + +__Using the "dot" access to get a parameter value from a component is actively discouraged, use "getter" functions instead__ + + + +For example, the `my_thermal_gen.active_power_limits` parameter of a thermal generator should be accessed as follows: + +```@repl system +get_active_power_limits(my_thermal_gen) +``` + +You can also view data from all instances of a concrete type in one table with the function `show_components`. It provides a few options: + + 1. View the standard fields by accepting the defaults. + 2. Pass a dictionary where the keys are column names and the values are functions that accept a component as a single argument. + 3. Pass a vector of symbols that are field names of the type. + +```@repl system +show_components(system, ThermalStandard) +show_components(system, ThermalStandard, Dict("has_time_series" => x -> has_time_series(x))) +show_components(system, ThermalStandard, [:active_power, :reactive_power]) +``` + +# to do: add a link in the system that MD explanation to these examples \ No newline at end of file From 14c6349caeb0e4fbe724a22670ffed1142cca391 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 2 Sep 2024 20:59:19 -0600 Subject: [PATCH 16/43] Run formatter --- docs/src/explanation/system.md | 6 +++--- docs/src/how_to/serialize_data.md | 5 +++-- docs/src/index.md | 1 + docs/src/tutorials/creating_system.md | 3 ++- docs/src/tutorials/get_component_data.md | 5 ++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/src/explanation/system.md b/docs/src/explanation/system.md index 08281d7bdc..559e30d460 100644 --- a/docs/src/explanation/system.md +++ b/docs/src/explanation/system.md @@ -19,12 +19,12 @@ retrieve all components of a certain `Type`. For example, the most common search function is [`get_components`](@ref), which takes a desired device `Type` (concrete or abstract) and retrieves all components in that category from the `System`. It also accepts filter functions for a more -refined search. +refined search. Given the potential size of the return, `PowerSystems.jl` returns Julia iterators in order to avoid unnecessary memory allocations. The container is optimized for iteration over abstract or concrete component -types as described by the [Type Structure](@ref type_structure). +types as described by the [Type Structure](@ref type_structure). ## [Accessing data stored in a component](@id dot_access) @@ -36,4 +36,4 @@ access to this data using the "dot" access (e.g. `component.field`), however _this is actively discouraged_ for two reasons: 1. We make no guarantees on the stability of component structure definitions. We will maintain version stability on the accessor methods. - 2. Per-unit conversions are made in the return of data from the accessor functions. (see the [per-unit section](@ref per_unit) for more details) \ No newline at end of file + 2. Per-unit conversions are made in the return of data from the accessor functions. (see the [per-unit section](@ref per_unit) for more details) diff --git a/docs/src/how_to/serialize_data.md b/docs/src/how_to/serialize_data.md index 2ee9cd31d3..7542202f98 100644 --- a/docs/src/how_to/serialize_data.md +++ b/docs/src/how_to/serialize_data.md @@ -35,6 +35,7 @@ Now write the system to JSON: ```@repl serialize_data to_json(sys, path) ``` + Notice in the `Info` statements that the serialization process stores 3 files: 1. System data file (`*.json` file) @@ -100,12 +101,12 @@ rm(folder; recursive = true); #hide ``` !!! tip - + PowerSystems generates UUIDs for the `System` and all components in order to have a way to uniquely identify objects. During deserialization it restores the same UUIDs. If you will modify the `System` or components after deserialization then it is recommended that you set this flag to generate new UUIDs. - + ```julia system2 = System(path; assign_new_uuids = true) ``` diff --git a/docs/src/index.md b/docs/src/index.md index 2d87abcec4..dc086e4b80 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -76,6 +76,7 @@ framework. If you are new to `PowerSystems.jl`, here's how we suggest getting started: 1. [Install](@ref install) + 2. Work through the introductory tutorial: [Create and Explore a Power `System`](@ref) to familiarize yourself with how `PowerSystems.jl` works 3. Work through the other basic tutorials based on your interests diff --git a/docs/src/tutorials/creating_system.md b/docs/src/tutorials/creating_system.md index 132753e10b..26b9a4de2a 100644 --- a/docs/src/tutorials/creating_system.md +++ b/docs/src/tutorials/creating_system.md @@ -157,7 +157,8 @@ withdraw power from the network. When you define components that aren't attached to a `System` yet, you must define all fields related to power (with units such as MW, MVA, MVAR, or MW/min) in - per-unit using the `base_power` of the component. + per-unit using the `base_power` of the component (with the exception of `base_power` + itself, which is in MVA). We'll start with defining a 10 MW [load](@ref PowerLoad) to `bus1`: diff --git a/docs/src/tutorials/get_component_data.md b/docs/src/tutorials/get_component_data.md index 9d851bc202..2b0fdf7d26 100644 --- a/docs/src/tutorials/get_component_data.md +++ b/docs/src/tutorials/get_component_data.md @@ -58,6 +58,7 @@ nothing #hide # copied over from the System ## Accessing components stored in the system + - ```@repl system using PowerSystems file_dir = joinpath(pkgdir(PowerSystems), "docs", "src", "tutorials", "tutorials_data") @@ -103,7 +103,6 @@ _this is actively discouraged_ for two reasons: 1. We make no guarantees on the stability of component structure definitions. We will maintain version stability on the accessor methods. 2. Per-unit conversions are made in the return of data from the accessor functions. (see the [per-unit section](@ref per_unit) for more details) --> - For example, the `my_thermal_gen.active_power_limits` parameter of a thermal generator should be accessed as follows: ```@repl system @@ -122,4 +121,4 @@ show_components(system, ThermalStandard, Dict("has_time_series" => x -> has_time show_components(system, ThermalStandard, [:active_power, :reactive_power]) ``` -# to do: add a link in the system that MD explanation to these examples \ No newline at end of file +# to do: add a link in the system that MD explanation to these examples From ee56ad5d92303b1fdd7f3e1f5352e00e5b4c004e Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 2 Sep 2024 21:00:31 -0600 Subject: [PATCH 17/43] Expand per unit explanation and update Branch rating docstrings --- docs/src/explanation/per_unit.md | 46 +++++++++++++------ src/descriptors/power_system_structs.json | 10 ++-- src/models/generated/Line.jl | 4 +- src/models/generated/MonitoredLine.jl | 4 +- .../generated/PhaseShiftingTransformer.jl | 4 +- src/models/generated/TapTransformer.jl | 4 +- src/models/generated/Transformer2W.jl | 4 +- 7 files changed, 47 insertions(+), 29 deletions(-) diff --git a/docs/src/explanation/per_unit.md b/docs/src/explanation/per_unit.md index fcbb434efe..db68630dec 100644 --- a/docs/src/explanation/per_unit.md +++ b/docs/src/explanation/per_unit.md @@ -7,24 +7,42 @@ It is often useful to express power systems data in relative terms using per-uni 2. `"SYSTEM_BASE"`: Parameter values are divided by the system `base_power`. 3. `"DEVICE_BASE"`: Parameter values are divided by the device `base_mva`. -To see the unit system setting of a `System`: +`PowerSystems.jl` supports these unit systems because different power system tools and data +sets use different units systems by convention, such as: -```@repl per-unit -using PowerSystems; #hide -file_dir = joinpath(pkgdir(PowerSystems), "docs", "src", "tutorials", "tutorials_data") #hide -system = System(joinpath(file_dir, "RTS_GMLC.m")); #hide -get_units_base(system) -``` + - Dynamics data is often defined in device base + - Network data (e.g., reactance, resistance) is often defined in system base + - Production cost modeling data is often gathered from variety of data sources, + which are typically defined in natural units -To change the unit system setting of a `System`: +These three unit bases allow easy conversion between unit systems. +This allows `PowerSystems.jl` users to input data in the formats they have available, +as well as view data in the unit system that is most intuitive to them. -```@repl per-unit -set_units_base_system!(system, "DEVICE_BASE") -``` +You can get and set the unit system setting of a `System` with [`get_units_base`](@ref) +and [`set_units_base_system!`](@ref). -The units of the parameter values stored in each struct are defined in -`src/descriptors/power_system_structs.json`. Conversion between unit systems does not change +Conversion between unit systems does not change the stored parameter values. Instead, unit system conversions are made when accessing parameters using the [accessor functions](@ref dot_access), thus making it imperative to utilize the accessor functions instead of the "dot" accessor methods to -ensure the return of the correct values. +ensure the return of the correct values. The units of the parameter values stored in each +struct are defined in `src/descriptors/power_system_structs.json`. + +There are some unit system conventions in `PowerSystems.jl` when defining new components. +Currently, when you define components that aren't attached to a `System`, +you must define all fields in `"DEVICE_BASE"`, except for certain components that don't +have their own `base_power` rating, such as [`Line`](@ref)s, where the `rating` must be +defined in `"SYSTEM_BASE"`. + +In the future, `PowerSystems.jl` hopes to support defining components in natural units. +For now, if you want to define data in natural units, you must first +set the system units to `"NATURAL_UNITS"`, define an empty component, and then use the +[accessor functions](@ref dot_access) (e.g., getters and setters), to define each field +within the component. The accessor functions will then do the data conversion from your +input data in natural units (e.g., MW or MVA) to per-unit. + +By default, `PowerSystems.jl` uses `"SYSTEM_BASE"` because many optimization problems won't +converge when using natural units. If you change the unit setting, it's suggested that you +switch back to `"SYSTEM_BASE"` before solving an optimization problem (for example in +[`PowerSimulations.jl`](https://nrel-sienna.github.io/PowerSimulations.jl/stable/)). diff --git a/src/descriptors/power_system_structs.json b/src/descriptors/power_system_structs.json index ec2e777dad..1983d7375b 100644 --- a/src/descriptors/power_system_structs.json +++ b/src/descriptors/power_system_structs.json @@ -465,7 +465,7 @@ { "null_value": "0.0", "name": "rating", - "comment": "Thermal rating (MVA). Flow on the line must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to", + "comment": "Thermal rating (MVA). Flow on the line must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to", "data_type": "Float64", "needs_conversion": true }, @@ -585,7 +585,7 @@ "null_value": "0.0", "name": "rating", "data_type": "Float64", - "comment": "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to", + "comment": "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to", "needs_conversion": true }, { @@ -717,7 +717,7 @@ }, { "name": "rating", - "comment": "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to", + "comment": "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to", "null_value": "0.0", "data_type": "Union{Nothing, Float64}", "valid_range": { @@ -846,7 +846,7 @@ }, { "name": "rating", - "comment": "Thermal rating (MVA). Flow through the transformer must be between -`rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to", + "comment": "Thermal rating (MVA). Flow through the transformer must be between -`rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to", "null_value": "0.0", "data_type": "Union{Nothing, Float64}", "valid_range": { @@ -952,7 +952,7 @@ }, { "name": "rating", - "comment": "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to", + "comment": "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to", "null_value": "nothing", "data_type": "Union{Nothing, Float64}", "valid_range": { diff --git a/src/models/generated/Line.jl b/src/models/generated/Line.jl index 2a638e096d..86c3b5c663 100644 --- a/src/models/generated/Line.jl +++ b/src/models/generated/Line.jl @@ -32,7 +32,7 @@ An AC transmission line - `r::Float64`: Resistance in pu ([`SYSTEM_BASE`](@ref per_unit)), validation range: `(0, 4)` - `x::Float64`: Reactance in pu ([`SYSTEM_BASE`](@ref per_unit)), validation range: `(0, 4)` - `b::FromTo`: Shunt susceptance in pu ([`SYSTEM_BASE`](@ref per_unit)), specified both on the `from` and `to` ends of the line. These are commonly modeled with the same value, validation range: `(0, 100)` -- `rating::Float64`: Thermal rating (MVA). Flow on the line must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to +- `rating::Float64`: Thermal rating (MVA). Flow on the line must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to - `angle_limits::MinMax`: Minimum and maximum angle limits (radians), validation range: `(-1.571, 1.571)` - `services::Vector{Service}`: (default: `Device[]`) Services that this device contributes to - `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation, such as latitude and longitude. @@ -55,7 +55,7 @@ mutable struct Line <: ACBranch x::Float64 "Shunt susceptance in pu ([`SYSTEM_BASE`](@ref per_unit)), specified both on the `from` and `to` ends of the line. These are commonly modeled with the same value" b::FromTo - "Thermal rating (MVA). Flow on the line must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to" + "Thermal rating (MVA). Flow on the line must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to" rating::Float64 "Minimum and maximum angle limits (radians)" angle_limits::MinMax diff --git a/src/models/generated/MonitoredLine.jl b/src/models/generated/MonitoredLine.jl index 3ba2d6e195..44dabafffd 100644 --- a/src/models/generated/MonitoredLine.jl +++ b/src/models/generated/MonitoredLine.jl @@ -36,7 +36,7 @@ For example, monitored lines can be used to restrict line flow following a conti - `x::Float64`: Reactance in pu ([`SYSTEM_BASE`](@ref per_unit)), validation range: `(0, 4)` - `b::FromTo`: Shunt susceptance in pu ([`SYSTEM_BASE`](@ref per_unit)), specified both on the `from` and `to` ends of the line. These are commonly modeled with the same value, validation range: `(0, 2)` - `flow_limits::FromTo_ToFrom`: Minimum and maximum permissable flow on the line (MVA), if different from the thermal rating defined in `rating` -- `rating::Float64`: Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to +- `rating::Float64`: Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to - `angle_limits::MinMax`: Minimum and maximum angle limits (radians), validation range: `(-1.571, 1.571)` - `services::Vector{Service}`: (default: `Device[]`) Services that this device contributes to - `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation, such as latitude and longitude. @@ -61,7 +61,7 @@ mutable struct MonitoredLine <: ACBranch b::FromTo "Minimum and maximum permissable flow on the line (MVA), if different from the thermal rating defined in `rating`" flow_limits::FromTo_ToFrom - "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to" + "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a line before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to" rating::Float64 "Minimum and maximum angle limits (radians)" angle_limits::MinMax diff --git a/src/models/generated/PhaseShiftingTransformer.jl b/src/models/generated/PhaseShiftingTransformer.jl index 380827ae14..2147b9598c 100644 --- a/src/models/generated/PhaseShiftingTransformer.jl +++ b/src/models/generated/PhaseShiftingTransformer.jl @@ -38,7 +38,7 @@ The model uses an equivalent circuit assuming the impedance is on the High Volta - `primary_shunt::Float64`:, validation range: `(0, 2)` - `tap::Float64`: Normalized tap changer position for voltage control, varying between 0 and 2, with 1 centered at the nominal voltage, validation range: `(0, 2)` - `α::Float64`: Initial condition of phase shift (radians) between the `from` and `to` buses , validation range: `(-1.571, 1.571)` -- `rating::Union{Nothing, Float64}`: Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to, validation range: `(0, nothing)` +- `rating::Union{Nothing, Float64}`: Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to, validation range: `(0, nothing)` - `phase_angle_limits::MinMax`: (default: `(min=-1.571, max=1.571)`) Minimum and maximum phase angle limits (radians), validation range: `(-1.571, 1.571)` - `services::Vector{Service}`: (default: `Device[]`) Services that this device contributes to - `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation, such as latitude and longitude. @@ -64,7 +64,7 @@ mutable struct PhaseShiftingTransformer <: ACBranch tap::Float64 "Initial condition of phase shift (radians) between the `from` and `to` buses " α::Float64 - "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to" + "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to" rating::Union{Nothing, Float64} "Minimum and maximum phase angle limits (radians)" phase_angle_limits::MinMax diff --git a/src/models/generated/TapTransformer.jl b/src/models/generated/TapTransformer.jl index cfa9303488..9fcb566226 100644 --- a/src/models/generated/TapTransformer.jl +++ b/src/models/generated/TapTransformer.jl @@ -35,7 +35,7 @@ The model uses an equivalent circuit assuming the impedance is on the High Volta - `x::Float64`: Reactance in p.u. ([`SYSTEM_BASE`](@ref per_unit)), validation range: `(-2, 4)` - `primary_shunt::Float64`: Shunt reactance in p.u. ([`SYSTEM_BASE`](@ref per_unit)), validation range: `(0, 2)` - `tap::Float64`: Normalized tap changer position for voltage control, varying between 0 and 2, with 1 centered at the nominal voltage, validation range: `(0, 2)` -- `rating::Union{Nothing, Float64}`: Thermal rating (MVA). Flow through the transformer must be between -`rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to, validation range: `(0, nothing)` +- `rating::Union{Nothing, Float64}`: Thermal rating (MVA). Flow through the transformer must be between -`rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to, validation range: `(0, nothing)` - `services::Vector{Service}`: (default: `Device[]`) Services that this device contributes to - `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation, such as latitude and longitude. - `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems.jl internal reference @@ -59,7 +59,7 @@ mutable struct TapTransformer <: ACBranch primary_shunt::Float64 "Normalized tap changer position for voltage control, varying between 0 and 2, with 1 centered at the nominal voltage" tap::Float64 - "Thermal rating (MVA). Flow through the transformer must be between -`rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to" + "Thermal rating (MVA). Flow through the transformer must be between -`rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to" rating::Union{Nothing, Float64} "Services that this device contributes to" services::Vector{Service} diff --git a/src/models/generated/Transformer2W.jl b/src/models/generated/Transformer2W.jl index 2147edea36..20c87dbffd 100644 --- a/src/models/generated/Transformer2W.jl +++ b/src/models/generated/Transformer2W.jl @@ -33,7 +33,7 @@ The model uses an equivalent circuit assuming the impedance is on the High Volta - `r::Float64`: Resistance in pu ([`SYSTEM_BASE`](@ref per_unit)), validation range: `(-2, 4)` - `x::Float64`: Reactance in pu ([`SYSTEM_BASE`](@ref per_unit)), validation range: `(-2, 4)` - `primary_shunt::Float64`: Shunt reactance in pu ([`SYSTEM_BASE`](@ref per_unit)), validation range: `(0, 2)` -- `rating::Union{Nothing, Float64}`: Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to, validation range: `(0, nothing)` +- `rating::Union{Nothing, Float64}`: Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to, validation range: `(0, nothing)` - `services::Vector{Service}`: (default: `Device[]`) Services that this device contributes to - `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation, such as latitude and longitude. - `internal::InfrastructureSystemsInternal`: (**Do not modify.**) PowerSystems.jl internal reference @@ -55,7 +55,7 @@ mutable struct Transformer2W <: ACBranch x::Float64 "Shunt reactance in pu ([`SYSTEM_BASE`](@ref per_unit))" primary_shunt::Float64 - "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in per-unit divided by the base power of the `System` it will be attached to" + "Thermal rating (MVA). Flow through the transformer must be between -`rating` and `rating`. When defining a transformer before it is attached to a `System`, `rating` must be in pu ([`SYSTEM_BASE`](@ref per_unit)) using the base power of the `System` it will be attached to" rating::Union{Nothing, Float64} "Services that this device contributes to" services::Vector{Service} From 40f6fcc80288db4829f95952dbfedf6c8cb29160 Mon Sep 17 00:00:00 2001 From: kdayday Date: Tue, 3 Sep 2024 13:40:57 -0600 Subject: [PATCH 18/43] Correct base power docstring for dynamic_inverter and _generator --- src/models/dynamic_generator.jl | 2 +- src/models/dynamic_inverter.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/dynamic_generator.jl b/src/models/dynamic_generator.jl index 3ee8e468cb..897a64b749 100644 --- a/src/models/dynamic_generator.jl +++ b/src/models/dynamic_generator.jl @@ -43,7 +43,7 @@ response. - `avr <: AVR`: [AVR](@ref) model of the excitacion system. - `prime_mover <: TurbineGov`: [Prime Mover and Turbine Governor model](@ref "TurbineGov") for mechanical power. - `pss <: PSS`: [PSS](@ref) model. -- `base_power::Float64`: (default: `100.0`) Base power of the `System` +- `base_power::Float64`: (default: `100.0`) Base power of the unit (MVA) for [per unitization](@ref per_unit). Although this has a default, in almost all cases `base_power` should be updated to equal the `base_power` field of the [`StaticInjection`](@ref) device that this dynamic generator will be attached to. - `n_states::Int`: (**Do not modify.**) Number of states (will depend on the inputs above). - `states::Vector{Symbol}`: (**Do not modify.**) Vector of states (will depend on the inputs above). - `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation diff --git a/src/models/dynamic_inverter.jl b/src/models/dynamic_inverter.jl index 601b85f8cd..cc1162fec5 100644 --- a/src/models/dynamic_inverter.jl +++ b/src/models/dynamic_inverter.jl @@ -52,7 +52,7 @@ response. - `freq_estimator <: FrequencyEstimator`: a [FrequencyEstimator](@ref) (typically a [PLL](@ref P)) model. - `filter <: Filter`: [Filter](@ref) model. - `limiter <: Union{nothing, OutputCurrentLimiter}`: (default: nothing) Inner Control [Current Limiter](@ref OutputCurrentLimiter) model -- `base_power::Float64`: (default: `100.0`) Base power of the `System` +- `base_power::Float64`: (default: `100.0`) Base power of the unit (MVA) for [per unitization](@ref per_unit). Although this has a default, in almost all cases `base_power` should be updated to equal the `base_power` field of the [`StaticInjection`](@ref) device that this dynamic generator will be attached to. - `n_states::Int`: (**Do not modify.**) Number of states (will depend on the inputs above). - `states::Vector{Symbol}`: (**Do not modify.**) Vector of states (will depend on the inputs above). - `ext::Dict{String, Any}`: (default: `Dict{String, Any}()`) An [*ext*ra dictionary](@ref additional_fields) for users to add metadata that are not used in simulation From 318eeba54957e1e904a81c5feb418be7207bc448 Mon Sep 17 00:00:00 2001 From: kdayday Date: Tue, 3 Sep 2024 13:44:40 -0600 Subject: [PATCH 19/43] Correct per unit page --- docs/src/explanation/per_unit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/explanation/per_unit.md b/docs/src/explanation/per_unit.md index db68630dec..e4e78793e1 100644 --- a/docs/src/explanation/per_unit.md +++ b/docs/src/explanation/per_unit.md @@ -5,7 +5,7 @@ It is often useful to express power systems data in relative terms using per-uni 1. `"NATURAL_UNITS"`: The naturally defined units of each parameter (typically MW). 2. `"SYSTEM_BASE"`: Parameter values are divided by the system `base_power`. - 3. `"DEVICE_BASE"`: Parameter values are divided by the device `base_mva`. + 3. `"DEVICE_BASE"`: Parameter values are divided by the device `base_power`. `PowerSystems.jl` supports these unit systems because different power system tools and data sets use different units systems by convention, such as: From 2d63ee621d578acdc377fe3af9ee01c6f3db05c8 Mon Sep 17 00:00:00 2001 From: kdayday Date: Tue, 3 Sep 2024 14:18:41 -0600 Subject: [PATCH 20/43] Remove under construction warning --- docs/src/index.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index dc086e4b80..cca5fd277f 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -9,13 +9,9 @@ CurrentModule = PowerSystems PowerSystems.jl upgraded to version 4.0 in June 2024, which included breaking changes. Visit the [v4.0 migration guide](@ref psy4_migration) for information on how to update your existing code. - -!!! warning "Under Construction" - The PowerSystems.jl is being actively being rewritten for version 4.0 using the new - format described in [How To Use This Documentation](@ref). Your patience is appreciated - as we make this change! For now, some documentation is not located in its final home. - Please reach out with questions and suggestions. + We also have re-organized and updated this documentation, including new tutorials! Check + out [How To Use This Documentation](@ref) below to orient yourself. ## About From 77771b31d1edcf066c7ebd00ed0605f703bbddf9 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 9 Sep 2024 12:57:27 -0600 Subject: [PATCH 21/43] Add how to add component in natural units --- docs/make.jl | 1 + .../src/how_to/add_component_natural_units.md | 90 +++++++++++++++++++ docs/src/tutorials/creating_system.md | 1 + 3 files changed, 92 insertions(+) create mode 100644 docs/src/how_to/add_component_natural_units.md diff --git a/docs/make.jl b/docs/make.jl index 5befc20519..2004144c69 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -18,6 +18,7 @@ pages = OrderedDict( "...install PowerSystems.jl" => "how_to/install.md", "...load a `system` from `PowerSystemCaseBuilder`" => "how_to/powersystembuilder.md", "...parse data from Matpower, PSSE, or CSV files" => "how_to/parsing.md", + "...add a component using natural units (MW)" => "how_to/add_component_natural_units.md", "...add time series data from CSVs" => "how_to/add_ts_from_csvs.md", "...customize the tabular data parser" => "how_to/extend_tabular_parsing.md", "...get all the buses in a system" => "how_to/get_buses.md", diff --git a/docs/src/how_to/add_component_natural_units.md b/docs/src/how_to/add_component_natural_units.md new file mode 100644 index 0000000000..3b29d4e3d6 --- /dev/null +++ b/docs/src/how_to/add_component_natural_units.md @@ -0,0 +1,90 @@ +# Add a Component in Natural Units + +```@setup add_in_nu +using PowerSystems; #hide +using PowerSystemCaseBuilder #hide +system = build_system(PSISystems, "modified_RTS_GMLC_DA_sys"); #hide +``` + +`PowerSystems.jl` has [three per-unitization options](@ref per_unit) for getting, setting +and displaying data. + +Currently, only one of these options -- `"DEVICE_BASE"` -- is supported when using a +constructor function define a component. You can see +[an example of the default capabilities using `"DEVICE_BASE"` here](@ref "Adding Loads and Generators"). + +We hope to add capability to define components in +`"NATURAL_UNITS"` with constructors in the future, but for now, below is a workaround +for users who prefer to define data using `"NATURAL_UNITS"` (e.g., MW, MVA, MVAR, or MW/min): + +### Step 1: Set Units Base + +Set your (previously-defined) `System`'s units base to `"NATURAL_UNITS"`: + +```@repl add_in_nu +set_units_base_system!(system, "NATURAL_UNITS") +``` + +Now, the "setter" functions have been switched to define data using natural units (MW, MVA, +etc.), taking care of the necessary data conversions behind the scenes. + +### Step 2: Define Empty Component + +Define an empty component with `0.0` or `nothing` for all the power-related fields except +`base_power`, which is always in MVA. + +For example: + +```@repl add_in_nu +gas1 = ThermalStandard(; + name = "gas1", + available = true, + status = true, + bus = get_component(ACBus, system, "Cobb"), # Attach to a previously-defined bus named Cobb + active_power = 0.0, + reactive_power = 0.0, + rating = 0.0, + active_power_limits = (min = 0.0, max = 0.0), + reactive_power_limits = nothing, + ramp_limits = nothing, + operation_cost = ThermalGenerationCost(nothing), + base_power = 30.0, # MVA + time_limits = (up = 8.0, down = 8.0), # Hours, unaffected by per-unitization + must_run = false, + prime_mover_type = PrimeMovers.CC, + fuel = ThermalFuels.NATURAL_GAS, +); +``` + +### Step 3: Attach the Component + +Attach the component to your `System`: + +```@repl add_in_nu +add_component!(system, gas1) +``` + +### Step 4: Add Data with "setter" Functions + +Use individual "setter" functions to set each the value of each numeric field in natural +units: + +```@repl add_in_nu +set_rating!(gas1, 30.0) #MVA +set_active_power_limits!(gas1, (min = 6.0, max = 30.0)) # MW +set_reactive_power_limits!(gas1, (min = 6.0, max = 30.0)) # MVAR +set_ramp_limits!(gas1, (up = 6.0, down = 6.0)) #MW/min +``` +Notice the return values are divided by the `base_power` of 30 MW, showing the setters have +done the per-unit conversion into `"DEVICE_BASE"` behind the scenes. + +!!! tip + + Steps 2-4 can be called within a `for` loop to define many components at once (or step 3 + can be replaced with [`add_components!`](@ref) to add all components at once). + +#### See Also + + - [Read more to understand per-unitization in PowerSystems.jl](@ref per_unit) + - Learn how to use the default constructors and explore the per-unitization settings in + [Create and Explore a Power `System`](@ref) diff --git a/docs/src/tutorials/creating_system.md b/docs/src/tutorials/creating_system.md index 26b9a4de2a..3e5b12d233 100644 --- a/docs/src/tutorials/creating_system.md +++ b/docs/src/tutorials/creating_system.md @@ -378,3 +378,4 @@ Next, you might want to: - [Import a `System` from an existing Matpower or PSSE file instead of creating it manually](@ref parsing) - [Create your own `System` from .csv files instead of creating it manually](@ref table_data) - [Read more to understand per-unitization in PowerSystems.jl](@ref per_unit) + - See a workaround for how to [Add a Component in Natural Units](@ref) From a7ada48979a7cdbb7bb1369761fcde313e90ab71 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 9 Sep 2024 16:26:23 -0600 Subject: [PATCH 22/43] Run formatter --- docs/src/how_to/add_component_natural_units.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/how_to/add_component_natural_units.md b/docs/src/how_to/add_component_natural_units.md index 3b29d4e3d6..aa58eda902 100644 --- a/docs/src/how_to/add_component_natural_units.md +++ b/docs/src/how_to/add_component_natural_units.md @@ -75,6 +75,7 @@ set_active_power_limits!(gas1, (min = 6.0, max = 30.0)) # MW set_reactive_power_limits!(gas1, (min = 6.0, max = 30.0)) # MVAR set_ramp_limits!(gas1, (up = 6.0, down = 6.0)) #MW/min ``` + Notice the return values are divided by the `base_power` of 30 MW, showing the setters have done the per-unit conversion into `"DEVICE_BASE"` behind the scenes. From 3d78fbbda3f035c6eefc8bf6fe9ffb2e15c13ade Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 9 Sep 2024 20:53:25 -0600 Subject: [PATCH 23/43] Add missing PSY pages to api --- docs/src/api/public.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/src/api/public.md b/docs/src/api/public.md index 95fc0f4d66..a5c147549a 100644 --- a/docs/src/api/public.md +++ b/docs/src/api/public.md @@ -19,6 +19,7 @@ Pages = ["PowerSystems.jl", "topological_elements.jl", "dynamic_models.jl", "static_models.jl", + "subsystems.jl", "static_injection_subsystem.jl", "dynamic_models.jl", "operational_cost.jl", @@ -67,7 +68,8 @@ Modules = [PowerSystems] Pages = ["parsers/power_system_table_data.jl", "parsers/power_models_data.jl", "parsers/TAMU_data.jl", - "parsers/psse_dynamic_data.jl"] + "parsers/psse_dynamic_data.jl", + "pm_io/common.jl"] Public = true Private = false Filter = t -> t ∈ [System] @@ -83,7 +85,8 @@ Filter = t -> t ∉ [System] ```@autodocs Modules = [PowerSystems] -Pages = ["utils/print.jl"] +Pages = ["utils/print.jl", + "utils/generate_struct_files.jl"] Public = true Private = false Filter = t -> t ∉ [System] From 7c064273a04e3c7030f6ca7f766138b8482efeb2 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 9 Sep 2024 20:31:47 -0700 Subject: [PATCH 24/43] add docstrings to outer control --- src/models/OuterControl.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/models/OuterControl.jl b/src/models/OuterControl.jl index d55fef3e3f..da1ca873c5 100644 --- a/src/models/OuterControl.jl +++ b/src/models/OuterControl.jl @@ -56,8 +56,13 @@ function OuterControl(; return OuterControl(active_power_control, reactive_power_control, ext, states, n_states) end +"""Get Active Power Control from OuterControl.""" get_active_power_control(value::OuterControl) = value.active_power_control +"""Get Reactive Power Control from OuterControl.""" get_reactive_power_control(value::OuterControl) = value.reactive_power_control +"""Get `ext` from OuterControl.""" get_ext(value::OuterControl) = value.ext +"""Get `states` from OuterControl.""" get_states(value::OuterControl) = value.states +"""Get `n_states` from OuterControl.""" get_n_states(value::OuterControl) = value.n_states From f60d7a3e091c5a16ba22edb2c0701e9787b84cf2 Mon Sep 17 00:00:00 2001 From: rodrigomha Date: Mon, 9 Sep 2024 20:31:59 -0700 Subject: [PATCH 25/43] update outer control manual markdown --- docs/src/model_library/outer_control.md | 74 +++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/docs/src/model_library/outer_control.md b/docs/src/model_library/outer_control.md index ae75255c96..4780821676 100644 --- a/docs/src/model_library/outer_control.md +++ b/docs/src/model_library/outer_control.md @@ -10,6 +10,8 @@ Public = true Private = false ``` +## Active Power Controllers + ### Virtual Inertia ```@autodocs @@ -20,6 +22,48 @@ Public = true Private = false ``` +### Active Power Droop + +```@autodocs +Modules = [PowerSystems] +Pages = ["generated/ActivePowerDroop.jl"] +Order = [:type, :function] +Public = true +Private = false +``` + +### Active Power PI + +```@autodocs +Modules = [PowerSystems] +Pages = ["generated/ActivePowerPI.jl"] +Order = [:type, :function] +Public = true +Private = false +``` + +### Active Virtual Oscillator + +```@autodocs +Modules = [PowerSystems] +Pages = ["generated/ActiveVirtualOscillator.jl"] +Order = [:type, :function] +Public = true +Private = false +``` + +### Active Renewable Controller Type AB + +```@autodocs +Modules = [PowerSystems] +Pages = ["generated/ActiveRenewableControllerAB.jl"] +Order = [:type, :function] +Public = true +Private = false +``` + +## Reactive Power Controllers + ### Reactive Power Droop ```@autodocs @@ -29,3 +73,33 @@ Order = [:type, :function] Public = true Private = false ``` + +### Reactive Power PI + +```@autodocs +Modules = [PowerSystems] +Pages = ["generated/ReactivePowerPI.jl"] +Order = [:type, :function] +Public = true +Private = false +``` + +### Reactive Virtual Oscillator + +```@autodocs +Modules = [PowerSystems] +Pages = ["generated/ReactiveVirtualController.jl"] +Order = [:type, :function] +Public = true +Private = false +``` + +### Reactive Renewable Controller Type AB + +```@autodocs +Modules = [PowerSystems] +Pages = ["generated/ReactiveRenewableControllerAB.jl"] +Order = [:type, :function] +Public = true +Private = false +``` \ No newline at end of file From f3a3d03656c10bbdc9b43fa97b217b6869eb31c7 Mon Sep 17 00:00:00 2001 From: kdayday Date: Wed, 11 Sep 2024 14:01:11 -0600 Subject: [PATCH 26/43] Add Outercontrol setters --- src/models/OuterControl.jl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/models/OuterControl.jl b/src/models/OuterControl.jl index da1ca873c5..d4a0a92b14 100644 --- a/src/models/OuterControl.jl +++ b/src/models/OuterControl.jl @@ -56,13 +56,21 @@ function OuterControl(; return OuterControl(active_power_control, reactive_power_control, ext, states, n_states) end -"""Get Active Power Control from OuterControl.""" +"""Get `active_power_control` from [`OuterControl`](@ref).""" get_active_power_control(value::OuterControl) = value.active_power_control -"""Get Reactive Power Control from OuterControl.""" +"""Get `reactive_power_control` from [`OuterControl`](@ref).""" get_reactive_power_control(value::OuterControl) = value.reactive_power_control -"""Get `ext` from OuterControl.""" +"""Get `ext` from [`OuterControl`](@ref).""" get_ext(value::OuterControl) = value.ext -"""Get `states` from OuterControl.""" +"""Get `states` from [`OuterControl`](@ref).""" get_states(value::OuterControl) = value.states -"""Get `n_states` from OuterControl.""" +"""Get `n_states` from [`OuterControl`](@ref).""" get_n_states(value::OuterControl) = value.n_states +"""Set [`OuterControl`](@ref) `active_power_control`.""" +set_active_power_control!(value::OuterControl, val) = + value.active_power_control = val +"""Set [`OuterControl`](@ref) `reactive_power_control`.""" +set_reactive_power_control!(value::OuterControl, val) = + value.reactive_power_control = val +"""Set [`OuterControl`](@ref) `ext`.""" +set_ext!(value::OuterControl, val) = value.ext = val \ No newline at end of file From b8deb42fe8466a4061c6c2c76e04c88821d03dbd Mon Sep 17 00:00:00 2001 From: kdayday Date: Wed, 11 Sep 2024 14:01:51 -0600 Subject: [PATCH 27/43] Add parse_file kwargs in docstring --- src/parsers/pm_io/common.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/parsers/pm_io/common.jl b/src/parsers/pm_io/common.jl index 9ebf8d732e..858fa0666c 100644 --- a/src/parsers/pm_io/common.jl +++ b/src/parsers/pm_io/common.jl @@ -1,5 +1,10 @@ """ - parse_file(file; import_all) + parse_file( + file; + import_all = false, + validate = true, + correct_branch_rating = true, + ) Parses a Matpower .m `file` or PTI (PSS(R)E-v33) .raw `file` into a PowerModels data structure. All fields from PTI files will be imported if From d090c583adb3490f18f596ee795b1b22663550cc Mon Sep 17 00:00:00 2001 From: kdayday Date: Wed, 11 Sep 2024 14:02:46 -0600 Subject: [PATCH 28/43] Remove duplicate autodocs in public api --- docs/src/api/public.md | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/docs/src/api/public.md b/docs/src/api/public.md index a5c147549a..3f0bcda0a4 100644 --- a/docs/src/api/public.md +++ b/docs/src/api/public.md @@ -63,18 +63,6 @@ Private = false Filter = t -> t ∈ [System] ``` -```@autodocs -Modules = [PowerSystems] -Pages = ["parsers/power_system_table_data.jl", - "parsers/power_models_data.jl", - "parsers/TAMU_data.jl", - "parsers/psse_dynamic_data.jl", - "pm_io/common.jl"] -Public = true -Private = false -Filter = t -> t ∈ [System] -``` - ```@autodocs Modules = [PowerSystems] Pages = ["base.jl"] @@ -127,7 +115,8 @@ Modules = [PowerSystems] Pages = ["parsers/power_system_table_data.jl", "parsers/power_models_data.jl", "parsers/TAMU_data.jl", - "parsers/psse_dynamic_data.jl"] + "parsers/psse_dynamic_data.jl", + "parsers/pm_io/common.jl"] Public = true Private = false Filter = t -> t ∉ [System] From ecab80fd39dc092ac726166eedc7e6bed6a384b5 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 16 Sep 2024 12:09:16 -0600 Subject: [PATCH 29/43] Typo fix --- docs/src/model_library/outer_control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/model_library/outer_control.md b/docs/src/model_library/outer_control.md index 4780821676..0f9205bb59 100644 --- a/docs/src/model_library/outer_control.md +++ b/docs/src/model_library/outer_control.md @@ -88,7 +88,7 @@ Private = false ```@autodocs Modules = [PowerSystems] -Pages = ["generated/ReactiveVirtualController.jl"] +Pages = ["generated/ReactiveVirtualOscillator.jl"] Order = [:type, :function] Public = true Private = false From 74be27b4f4962985990029c51684739b64ed40b5 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 16 Sep 2024 14:01:12 -0600 Subject: [PATCH 30/43] Run formatter --- docs/src/model_library/outer_control.md | 2 +- src/models/OuterControl.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/model_library/outer_control.md b/docs/src/model_library/outer_control.md index 0f9205bb59..ef2c45a82f 100644 --- a/docs/src/model_library/outer_control.md +++ b/docs/src/model_library/outer_control.md @@ -102,4 +102,4 @@ Pages = ["generated/ReactiveRenewableControllerAB.jl"] Order = [:type, :function] Public = true Private = false -``` \ No newline at end of file +``` diff --git a/src/models/OuterControl.jl b/src/models/OuterControl.jl index d4a0a92b14..10fb9d0cc8 100644 --- a/src/models/OuterControl.jl +++ b/src/models/OuterControl.jl @@ -73,4 +73,4 @@ set_active_power_control!(value::OuterControl, val) = set_reactive_power_control!(value::OuterControl, val) = value.reactive_power_control = val """Set [`OuterControl`](@ref) `ext`.""" -set_ext!(value::OuterControl, val) = value.ext = val \ No newline at end of file +set_ext!(value::OuterControl, val) = value.ext = val From 98e384ff119db9397d71519f774bd473098b1cd8 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 16 Sep 2024 18:21:31 -0600 Subject: [PATCH 31/43] Move and collapse internal API --- docs/make.jl | 6 +++--- docs/src/api/internal.md | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 2004144c69..fe03cf168d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -43,13 +43,13 @@ pages = OrderedDict( "Model Library" => Any[], "Reference" => Any["Public API" => "api/public.md", - "Internal API Reference" => "api/internal.md", "Glossary and Acronyms" => "api/glossary.md", "Type Hierarchy" => "api/type_tree.md", "`ValueCurve` Options" => "api/valuecurve_options.md", "Specifying the category of..." => "api/enumerated_types.md", - "Developer Guidelines" => "api/developer_guidelines.md", - "Citation" => "api/citation.md" + "Citation" => "api/citation.md", + "Developers" => ["Developer Guidelines" => "api/developer_guidelines.md", + "Internals" => "api/internal.md"] ] diff --git a/docs/src/api/internal.md b/docs/src/api/internal.md index 89b96d83b6..e284104ae7 100644 --- a/docs/src/api/internal.md +++ b/docs/src/api/internal.md @@ -1,3 +1,7 @@ +```@meta +CollapsedDocStrings = true +``` + # Internal API ```@autodocs From b8c044129b67120b8706fb5ea269eb44c259b9d0 Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 16 Sep 2024 20:12:33 -0600 Subject: [PATCH 32/43] Add Rodrigo to authors --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index fe03cf168d..d8b3cac102 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -135,7 +135,7 @@ makedocs( prettyurls = haskey(ENV, "GITHUB_ACTIONS"), size_threshold = nothing,), sitename = "PowerSystems.jl", - authors = "Jose Daniel Lara, Daniel Thom, Kate Doubleday, and Clayton Barrows", + authors = "Jose Daniel Lara, Daniel Thom, Kate Doubleday, Rodrigo Henriquez-Auba, and Clayton Barrows", pages = Any[p for p in pages], warnonly = Documenter.except( :autodocs_block, From c3113be7aeac48cf7338908ad3674050ea88d64a Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 16 Sep 2024 20:13:16 -0600 Subject: [PATCH 33/43] Reorder PowerSystems.jl to track down IS files --- src/PowerSystems.jl | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/PowerSystems.jl b/src/PowerSystems.jl index da9138ff82..3532c7dd43 100644 --- a/src/PowerSystems.jl +++ b/src/PowerSystems.jl @@ -40,6 +40,7 @@ export Transformer2W export TapTransformer export PhaseShiftingTransformer +# from IS function_data.jl export FunctionData export LinearFunctionData export QuadraticFunctionData @@ -56,15 +57,17 @@ export get_points export get_x_coords export get_y_coords +# from IS value_curve.jl, cost_aliases.jl, and production_variable_cost_curve.jl export ValueCurve export InputOutputCurve, IncrementalCurve, AverageRateCurve export LinearCurve, QuadraticCurve export PiecewisePointCurve, PiecewiseIncrementalCurve, PiecewiseAverageCurve export ProductionVariableCostCurve, CostCurve, FuelCurve -export OperationalCost, MarketBidCost, LoadCost, StorageCost -export HydroGenerationCost, RenewableGenerationCost, ThermalGenerationCost export get_function_data, get_initial_input, get_input_at_zero export get_value_curve, get_power_units + +export OperationalCost, MarketBidCost, LoadCost, StorageCost +export HydroGenerationCost, RenewableGenerationCost, ThermalGenerationCost export get_fuel_cost, set_fuel_cost!, get_vom_cost export is_market_bid_curve, make_market_bid_curve export get_no_load_cost, set_no_load_cost!, get_start_up, set_start_up! @@ -281,24 +284,27 @@ export ThermalFuels export StorageTech export StateTypes +# from IS time_series_structs.jl, time_series_cache.jl export TimeSeriesAssociation -export TimeSeriesData -export StaticTimeSeries -export Forecast -export AbstractDeterministic -export Deterministic -export Probabilistic -export SingleTimeSeries -export DeterministicSingleTimeSeries export TimeSeriesKey export StaticTimeSeriesKey export ForecastKey -export Scenarios +export TimeSeriesCounts export ForecastCache export StaticTimeSeriesCache +# from IS time_series_parser.jl export NormalizationFactor export NormalizationTypes -export TimeSeriesCounts +# from IS forecasts.jl +export Forecast +export AbstractDeterministic +export TimeSeriesData # abstract_time_series.jl +export StaticTimeSeries # static_time_series.jl +export Deterministic # deterministic.jl +export Probabilistic # Probabilistic.jl +export SingleTimeSeries # Single_Time_Series.jl +export DeterministicSingleTimeSeries # deterministic_single_time_series.jl +export Scenarios # scenarios.jl export get_dynamic_components @@ -466,16 +472,17 @@ export check_component export check_components export check_sil_values +# From IS logging.jl, generate_struct_files.jl export configure_logging export open_file_logger export make_logging_config_file export MultiLogger export LogEventTracker -export UnitSystem export StructField export StructDefinition export generate_struct_file export generate_struct_files +export UnitSystem # internal.jl ################################################################################# # Imports From fb41e06f59601c4e50d149f83d2fbed05102ed6a Mon Sep 17 00:00:00 2001 From: kdayday Date: Mon, 16 Sep 2024 20:14:47 -0600 Subject: [PATCH 34/43] Filter IS docs by what's exported in PSY, remove IS from makedocs, remove warnonly exceptions --- docs/make.jl | 15 +-------------- docs/src/api/public.md | 7 +++++-- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index d8b3cac102..acd95ff137 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -130,26 +130,13 @@ for (section, folder) in folders end makedocs( - modules = [PowerSystems, InfrastructureSystems], + modules = [PowerSystems], format = Documenter.HTML( prettyurls = haskey(ENV, "GITHUB_ACTIONS"), size_threshold = nothing,), sitename = "PowerSystems.jl", authors = "Jose Daniel Lara, Daniel Thom, Kate Doubleday, Rodrigo Henriquez-Auba, and Clayton Barrows", pages = Any[p for p in pages], - warnonly = Documenter.except( - :autodocs_block, - :cross_references, - :docs_block, - :doctest, - :eval_block, - :example_block, - :footnote, - :linkcheck_remotes, - :linkcheck, - :meta_block, - :parse_error, - :setup_block), draft = false, ) diff --git a/docs/src/api/public.md b/docs/src/api/public.md index 34bcba0088..c8676b9578 100644 --- a/docs/src/api/public.md +++ b/docs/src/api/public.md @@ -38,6 +38,7 @@ Pages = ["production_variable_cost_curve.jl", "value_curve.jl", ] Order = [:type, :function] +Filter = t -> nameof(t) in names(PowerSystems) ``` ## Time Series @@ -46,6 +47,7 @@ Order = [:type, :function] Modules = [InfrastructureSystems] Pages = ["abstract_time_series.jl", "deterministic.jl", + "deterministic_single_time_series.jl", "probabilistic.jl", "scenarios.jl", "static_time_series.jl", @@ -53,8 +55,7 @@ Pages = ["abstract_time_series.jl", "forecasts.jl", ] Order = [:type, :function] -Filter = t -> t ∉ [InfrastructureSystems.get_internal, - InfrastructureSystems.set_internal!] +Filter = t -> nameof(t) in names(PowerSystems) ``` ```@autodocs @@ -63,8 +64,10 @@ Pages = ["time_series_cache.jl", "time_series_interface.jl", "time_series_structs.jl", "time_series_storage.jl", + "time_series_parser.jl", "utils/print.jl"] Order = [:type, :function] +Filter = t -> nameof(t) in names(PowerSystems) ``` ## System From 181b9712620584cdb172ace05c8d53cc7cece90a Mon Sep 17 00:00:00 2001 From: kdayday Date: Thu, 19 Sep 2024 10:09:25 -0600 Subject: [PATCH 35/43] Fix parsing.md text formatting --- docs/src/how_to/parsing.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/src/how_to/parsing.md b/docs/src/how_to/parsing.md index 870fabb442..513509917b 100644 --- a/docs/src/how_to/parsing.md +++ b/docs/src/how_to/parsing.md @@ -138,7 +138,7 @@ These must reside in the directory passed when constructing PowerSystemTableData ### [Adding Time Series Data](@id parsing_time_series) -PowerSystems requires a metadata file that maps components to their time series +`PowerSystems` requires a metadata file that maps components to their time series data in order to be able to automatically construct time_series from raw data files. The following fields are required for each time array: @@ -159,30 +159,30 @@ files. The following fields are required for each time array: Notes: - - The "module", "category", and "component_name" entries must be valid arguments to retrieve + - The `module`, `category`, and `component_name` entries must be valid arguments to retrieve a component using `get_component(${module}.${category}, sys, $name)`. - - The "scaling_factor_multiplier_module" and the "scaling_factor_multiplier" entries must + - The `scaling_factor_multiplier_module` and the `scaling_factor_multiplier` entries must be sufficient to return the scaling factor data using `${scaling_factor_multiplier_module}.${scaling_factor_multiplier}(component)`. -PowerSystems supports this metadata in either CSV or JSON formats. Refer to +`PowerSystems` supports this metadata in either CSV or JSON formats. Refer to [RTS_GMLC](https://github.com/GridMod/RTS-GMLC/blob/master/RTS_Data/FormattedData/SIIP/timeseries_pointers.json) for an example. #### Performance considerations -By default PowerSystems stores time series data in HDF5 files. It does not keep -all of the data in memory. This means that every time you access a time_series -PowerSystems will have to read the data from storage, which will add latency. If +By default `PowerSystems` stores time series data in HDF5 files. It does not keep +all of the data in memory. This means that every time you access a time series +`PowerSystems` will have to read the data from storage, which will add latency. If you know ahead of time that all of your data will fit in memory then you can change this behavior by passing `time_series_in_memory = true` when you create -the System. +the `System`. -If the time series data is stored in HDF5 then PowerSystems will use the tmp filesystem by +If the time series data is stored in HDF5 then `PowerSystems` will use the tmp filesystem by default. You can change this by passing `time_series_directory = X` when you create the -System. This is required if the time series data is larger than the amount of tmp space +`System`. This is required if the time series data is larger than the amount of tmp space available. You can also override the location by setting the environment variable -SIENNA_TIME_SERIES_DIRECTORY to another directory. +`SIENNA_TIME_SERIES_DIRECTORY` to another directory. ### Customization @@ -190,7 +190,7 @@ The tabular data parser in `PowerSystems.jl` can be customized to read a variety datasets by configuring: - [which type of generator (`<:Generator`) to create based on the fuel and prime mover specifications](@ref csv_genmap) - - [property names](@ref csv_columns), [units](@ref csv_units), and per units conversions](@ref csv_per_unit) in *.csv files + - [property names](@ref csv_columns), [units](@ref csv_units), and [per units conversions](@ref csv_per_unit) in *.csv files Here is an example of how to construct a System with all customizations listed in this section: @@ -219,15 +219,15 @@ Examples configuration files can be found in the [RTS-GMLC](https://github.com/G ##### [Custom construction of generators](@id csv_genmap) -PowerSystems supports custom construction of subtypes of the abstract type Generator based +`PowerSystems` supports custom construction of subtypes of the abstract type Generator based on `fuel` and `type`. The parsing code detects these fields in the raw data and then constructs the concrete type listed in the passed generator mapping file. The default file is `src/parsers/generator_mapping.yaml`. You can override this behavior by specifying your -own file when constructing PowerSystemTableData. +own file when constructing `PowerSystemTableData`. ##### [Column names](@id csv_columns) -PowerSystems provides am input mapping capability that allows you to keep your own +`PowerSystems` provides am input mapping capability that allows you to keep your own column names. For example, when parsing raw data for a generator the code expects a column @@ -254,13 +254,13 @@ make to this YAML file. Do not edit the default JSON file. For more info on the per-unit conventions in `PowerSystems.jl`, refer to the [per-unit section of the system documentation](@ref per_unit). -PowerSystems defines whether it expects a column value to be per-unit system base, +`PowerSystems` defines whether it expects a column value to be per-unit system base, per-unit device base, or natural units in `power_system_inputs.json`. If it expects a per-unit convention that differs from your values then you can set the `unit_system` in -`user_descriptors.yaml` and PowerSystems will automatically convert the values. For +`user_descriptors.yaml` and `PowerSystems` will automatically convert the values. For example, if you have a `max_active_power` value stored in natural units (MW), but `power_system_inputs.json` specifies `unit_system: device_base`, you can enter -`unit_system: natural_units` in `user_descriptors.yaml` and PowerSystems will divide +`unit_system: natural_units` in `user_descriptors.yaml` and `PowerSystems` will divide the value by the value of the corresponding entry in the column identified by the `base_reference` field in `power_system_inputs.json`. You can also override the `base_reference` setting by adding `base_reference: My Column` to make device base @@ -270,7 +270,7 @@ instantiated when constructing a `System`. ##### [Unit conversion](@id csv_units) -PowerSystems provides a limited set of unit conversions. For example, if +`PowerSystems` provides a limited set of unit conversions. For example, if `power_system_inputs.json` indicates that a value's unit is degrees but your values are in radians then you can set `unit: radian` in your YAML file. Other valid `unit` entries include `GW`, `GWh`, `MW`, `MWh`, `kW`, From 68534a0e4b82d8b7f49aefe88a4660048563f914 Mon Sep 17 00:00:00 2001 From: kdayday Date: Thu, 19 Sep 2024 10:21:04 -0600 Subject: [PATCH 36/43] Add missing glossary entries --- docs/src/api/glossary.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/api/glossary.md b/docs/src/api/glossary.md index a286efdeeb..6dd40f2ca7 100644 --- a/docs/src/api/glossary.md +++ b/docs/src/api/glossary.md @@ -24,7 +24,6 @@ ### E - *EMF*: Electromotive force - - *ESAC*: IEEE Type AC Excitation System model - *ESDC*: IEEE Type DC Excitation System model - *EXAC*: IEEE Type AC Excitation System (modified) model @@ -70,24 +69,25 @@ ### O - - *OEL*: + - *OEL*: Over Excitation Limiter ### P - *PLL*: Phase-locked loop - *PSS*: Power System Stabilizer - - *PSSE* or *PSS/E*: Siemen's PSS®E Power Simulator + - *PSSE* or *PSS/E*: Siemens' PSS®E Power System Simulator for Engineering - *PPA*: Power purchase agreement - - *PSID*: - - *PSLF*: + - *PSI*: [`PowerSimulations.jl`](https://nrel-sienna.github.io/PowerSimulations.jl/latest/) + - *PSID*: [`PowerSimulationsDynamics.jl`](https://nrel-sienna.github.io/PowerSimulationsDynamics.jl/stable/) + - *PSLF*: GE Vernova's Positive Sequence Load Flow Software + - *PSY*: `PowerSystems.jl` (this package) - *pu* or *p.u.*: Per-unit ### R - - *REECB1*: - - - *REPCA1*: + - *REECB1*: Renewable Energy Electric Controller Type B1 + - *REPCA1*: REPCA1: Renewable Energy Power Controller Type A1 - *Resolution*: The period of time between each discrete value in a time series. All resolutions are represented using `Dates.Period` types. For instance, a Day-ahead market data set usually has a resolution of `Hour(1)`, a Real-Time market data set usually has a resolution of `Minute(5)`. @@ -116,7 +116,7 @@ - *VSCDCLine*: Voltage-Source Converter Direct Current Line - - *VSM*: + - *VSM*: Virtual Synchronous Machine ### W From 0b9a5bab1051afdf59086f6470633aae367b2492 Mon Sep 17 00:00:00 2001 From: kdayday Date: Thu, 19 Sep 2024 10:22:13 -0600 Subject: [PATCH 37/43] Update docs/src/explanation/dynamic_data.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rodrigo Henríquez-Auba --- docs/src/explanation/dynamic_data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/explanation/dynamic_data.md b/docs/src/explanation/dynamic_data.md index 99eceb073a..2bf5e0f684 100644 --- a/docs/src/explanation/dynamic_data.md +++ b/docs/src/explanation/dynamic_data.md @@ -5,7 +5,7 @@ `PowerSystems.jl` uses two data layers to define data for dynamic simulations: 1. [Static](@ref S) components, which includes the data needed to run a power flow problem - 2. [Dynamic](@ref D) components are those that define differential equations to run a transient simulation. These dyanamic + 2. [Dynamic](@ref D) components are those that define differential equations to run a transient simulation. These dynamic data are attached to the static components. Although `PowerSystems.jl` is not constrained to only PSS/e files, commonly the data for a From f7cfb8c5194a5678edf97c57337fafa2daf1dc0e Mon Sep 17 00:00:00 2001 From: kdayday Date: Thu, 19 Sep 2024 10:22:25 -0600 Subject: [PATCH 38/43] Update docs/src/api/citation.md Co-authored-by: Jose Daniel Lara --- docs/src/api/citation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/api/citation.md b/docs/src/api/citation.md index b2741f8e21..5320dde37a 100644 --- a/docs/src/api/citation.md +++ b/docs/src/api/citation.md @@ -17,6 +17,6 @@ author = {José Daniel Lara and Clayton Barrows and Daniel Thom and Dheepak Kris keywords = {Power Systems, Julia, Energy}, ``` -PowerSystems has been developed as part of the [Sienna modeling framework](https://www.nrel.gov/analysis/sienna.html) +PowerSystems has been developed as part of the [Sienna platform](https://www.nrel.gov/analysis/sienna.html) by the U.S. Department of Energy's National Renewable Energy Laboratory ([NREL](https://www.nrel.gov/)). From e091e169be3c31f04dc244925f194646a6dabf10 Mon Sep 17 00:00:00 2001 From: kdayday Date: Thu, 19 Sep 2024 10:22:43 -0600 Subject: [PATCH 39/43] Update docs/src/explanation/dynamic_data.md Co-authored-by: Jose Daniel Lara --- docs/src/explanation/dynamic_data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/explanation/dynamic_data.md b/docs/src/explanation/dynamic_data.md index 2bf5e0f684..591864bc10 100644 --- a/docs/src/explanation/dynamic_data.md +++ b/docs/src/explanation/dynamic_data.md @@ -2,7 +2,7 @@ ## Static and Dynamic Data Layers -`PowerSystems.jl` uses two data layers to define data for dynamic simulations: +`PowerSystems.jl` uses two categories to define data for dynamic simulations: 1. [Static](@ref S) components, which includes the data needed to run a power flow problem 2. [Dynamic](@ref D) components are those that define differential equations to run a transient simulation. These dynamic From 605d34c96b452e2ef6dd70ab9be34ea8c8dace41 Mon Sep 17 00:00:00 2001 From: kdayday Date: Thu, 19 Sep 2024 10:26:18 -0600 Subject: [PATCH 40/43] Remove comment in draft tutorial --- docs/src/tutorials/get_component_data.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/src/tutorials/get_component_data.md b/docs/src/tutorials/get_component_data.md index 2b0fdf7d26..8aa4403795 100644 --- a/docs/src/tutorials/get_component_data.md +++ b/docs/src/tutorials/get_component_data.md @@ -55,8 +55,6 @@ bus1 = get_component(ACBus, sys, "nodeA") nothing #hide ``` -# copied over from the System - ## Accessing components stored in the system