Skip to content

Commit

Permalink
Review docs (#572)
Browse files Browse the repository at this point in the history
  • Loading branch information
clizbe committed Mar 29, 2024
1 parent d43c1ba commit 42d9768
Show file tree
Hide file tree
Showing 14 changed files with 192 additions and 191 deletions.
4 changes: 2 additions & 2 deletions docs/make.jl
Expand Up @@ -16,10 +16,10 @@ makedocs(;
),
pages = [
"Home" => "index.md",
"Features" => "features.md",
"Mathematical Formulation" => "formulation.md",
"How to Use" => "how-to-use.md",
"Tutorials" => "tutorials.md",
"Concepts" => "concepts.md",
"Mathematical Formulation" => "formulation.md",
"API" => "api.md",
"Reference" => "reference.md",
],
Expand Down
154 changes: 72 additions & 82 deletions docs/src/features.md → docs/src/concepts.md

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions docs/src/formulation.md
@@ -1,7 +1,7 @@
# [Mathematical Formulation](@id formulation)

This section shows the mathematical formulation of _TulipaEnergyModel.jl_, assuming that the temporal definition of time steps is the same for all the elements in the model.\
The complete mathematical formulation, including variable temporal resolutions, is also freely available in the [preprint](https://arxiv.org/abs/2309.07711). In addition, the feature section has an example of how the model handles the [`flexible time resolution`](@ref flex-time-res).
This section shows the mathematical formulation of _TulipaEnergyModel.jl_, assuming that the temporal definition of timesteps is the same for all the elements in the model.\
The complete mathematical formulation, including variable temporal resolutions, is also freely available in the [preprint](https://arxiv.org/abs/2309.07711). In addition, the [concepts section](@ref seasonal-storage) has an example of how the model handles the [`flexible time resolution`](@ref flex-time-res).

## [Sets](@id math-sets)

Expand Down Expand Up @@ -160,7 +160,7 @@ v^{\text{flow}}_{f,k,b_k} \geq 0 \quad \forall f \notin \mathcal{F}^{\text{t}},

### Constraints for Energy Storage Assets

There are two types of constraints for energy storage assets: intra-temporal and inter-temporal. Intra-temporal constraints impose limits within the representative periods, while inter-temporal constraints restrict storage between representative periods. Inter-temporal constraints allow us to model seasonal storage by mapping the representative periods $\mathcal{K}$ to the periods $\mathcal{P}$ in the model's timeframe. For more information on this topic, refer to [Tejada-Arango et al. (2018)](https://ieeexplore.ieee.org/document/8334256) and [Tejada-Arango et al. (2019)](https://www.sciencedirect.com/science/article/pii/S0360544219317748).
There are two types of constraints for energy storage assets: intra-temporal and inter-temporal. Intra-temporal constraints impose limits within the representative periods, while inter-temporal constraints restrict storage between representative periods. Inter-temporal constraints allow us to model seasonal storage by mapping the representative periods $\mathcal{K}$ to the periods $\mathcal{P}$ in the model's timeframe. For more information on this topic, refer to the [concepts section](@ref seasonal-storage) or [Tejada-Arango et al. (2018)](https://ieeexplore.ieee.org/document/8334256) and [Tejada-Arango et al. (2019)](https://www.sciencedirect.com/science/article/pii/S0360544219317748).

#### [Intra-temporal Constraint for Storage Balance](@id intra-storage-balance)

Expand All @@ -185,7 +185,7 @@ v^{\text{intra-storage}}_{a,k,b_k} \geq p^{\text{min intra level}}_{a,k,b_k} \cd
\\ \\ \forall a \in \mathcal{A}^{\text{s}} \setminus \mathcal{A}^{\text{ss}}, \forall k \in \mathcal{K},\forall b_k \in \mathcal{B_k}
```

#### Intra-temporal Constraint for Cycling Constraint
#### Intra-temporal Cycling Constraint

The cycling constraint for the intra-temporal constraints links the first timestep block ($b^{\text{first}}_k$) and the last one ($b^{\text{last}}_k$) in each representative period. The parameter $p^{\text{init storage level}}_{a}$ determines the considered equations in the model for this constraint:

Expand Down Expand Up @@ -241,7 +241,7 @@ v^{\text{inter-storage}}_{a,p} \geq p^{\text{min inter level}}_{a,p} \cdot (p^{\
\\ \\ \forall a \in \mathcal{A}^{\text{ss}}, \forall p \in \mathcal{P}
```

#### Inter-temporal Constraint for Cycling Constraint
#### Inter-temporal Cycling Constraint

The cycling constraint for the inter-temporal constraints links the first-period block ($p^{\text{first}}$) and the last one ($p^{\text{last}}$) in the timeframe. The parameter $p^{\text{init storage level}}_{a}$ determines the considered equations in the model for this constraint:

Expand Down
114 changes: 63 additions & 51 deletions docs/src/how-to-use.md

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions docs/src/index.md
Expand Up @@ -12,8 +12,7 @@ This content is released under the [Apache License 2.0](https://www.apache.org/l

## [Bug reports and discussions](@id bugs-and-discussions)

If you think you found a bug, feel free to open an [issue](https://github.com/TulipaEnergy/TulipaEnergyModel.jl/issues).
Focused suggestions and requests can also be opened as issues. Before opening a pull request, please start an issue or a discussion on the topic.
If you think you have found a bug, feel free to open an [issue](https://github.com/TulipaEnergy/TulipaEnergyModel.jl/issues). Before opening a pull request, please read our [CONTRIBUTING.md](https://github.com/TulipaEnergy/TulipaEnergyModel.jl/blob/main/CONTRIBUTING.md) and follow the guidelines in the [README.dev.md](https://github.com/TulipaEnergy/TulipaEnergyModel.jl/blob/main/README.dev.md) file.

Start a discussion [here](https://github.com/TulipaEnergy/TulipaEnergyModel.jl/discussions) if you want to ask a question unsuitable for a bug report. This forum is for general discussion about the repository [TulipaEnergyModel](https://github.com/TulipaEnergy/TulipaEnergyModel.jl).

Expand Down
30 changes: 16 additions & 14 deletions docs/src/tutorials.md
Expand Up @@ -10,29 +10,31 @@ Depth = 5
## [Basic example](@id basic-example)

For our first example, let's use a tiny existing dataset.
Inside the code for this package, you can find the folder [`test/inputs/Tiny`](https://github.com/TulipaEnergy/TulipaEnergyModel.jl/tree/main/test/inputs/Tiny), which includes all the files necessary to create a TulipaEnergyModel and solve it.
Inside the code for this package, you can find the folder [`test/inputs/Tiny`](https://github.com/TulipaEnergy/TulipaEnergyModel.jl/tree/main/test/inputs/Tiny), which includes all the files necessary to create a model and solve it.

There are 8 relevant¹ files inside the "Tiny" folder. They define the assets and flows data, their profiles, and their time resolution, as well as two files to define the representative periods and which periods in the full problem formulation they stand for.
The files inside the "Tiny" folder define the assets and flows data, their profiles, and their time resolution, as well as define the representative periods and which periods in the full problem formulation they represent.¹

For more details about these files, see [Input](@ref).
For more details about these files, see [Input](@ref input).

¹ _Ignore the 9th file, bad-assets-data.csv, which is used for testing._
¹ _Ignore bad-assets-data.csv, which is used for testing._

### Run scenario

To read all data from the Tiny folder, perform all necessary steps to create a model, and solve the model, use the following snippet:
To read all data from the Tiny folder, perform all necessary steps to create a model, and solve the model, run the following in a Julia terminal:

```@example
using TulipaEnergyModel
input_dir = "../../test/inputs/Tiny" # hide
# input_dir should be the path to Tiny
# input_dir should be the path to Tiny as a string (something like "test/inputs/Tiny")
energy_problem = run_scenario(input_dir)
```

The `energy_problem` variable is of type `EnergyProblem`.
For more details, see the [documentation for that type](@ref TulipaEnergyModel.EnergyProblem) or the section [Structures](@ref).

That's all it takes to run a scenario! To learn about the data required to run your own scenario, see the [Input section](@ref input) of [How to Use](@ref how-to-use).

### Manually running each step

If we need more control, we can create the energy problem first, then the optimization model inside it, and finally ask for it to be solved.
Expand Down Expand Up @@ -237,13 +239,13 @@ graph[:ocgt, :demand]

The type of the flow struct is [GraphFlowData](@ref).

We can easily find all assets `v` for which a flow `(a, v)` exists:
We can easily find all assets `v` for which a flow `(a, v)` exists for a given asset `a` (in this case, demand):

```@example manual
inneighbor_labels(graph, :demand) |> collect
```

Similarly, all assets `u` for which a flow `(u, a)` exists:
Similarly, all assets `u` for which a flow `(u, a)` exists for a given asset `a` (in this case, ocgt):

```@example manual
outneighbor_labels(graph, :ocgt) |> collect
Expand Down Expand Up @@ -328,7 +330,7 @@ df = filter(

### The solution inside the graph

In addition to the solution object, the solution is also stored by the individual assets and flows when [`solve_model!`](@ref) is called - i.e., when using a [EnergyProblem](@ref) object.
In addition to the solution object, the solution is also stored by the individual assets and flows when [`solve_model!`](@ref) is called (i.e., when using an [EnergyProblem](@ref) object).

They can be accessed like any other value from [GraphAssetData](@ref) or [GraphFlowData](@ref), which means that we recreate the values from the previous section in a new way:

Expand Down Expand Up @@ -419,7 +421,7 @@ df.solution
By accessing the model directly, we can query the values of constraints and expressions.
We need to know the name of the constraint and how it is indexed, and for that, you will need to check the model.

For instance, we can get all incoming flow in the lowest resolution for a given asset for a given representative period with the following:
For instance, we can get all incoming flows in the lowest resolution for a given asset for a given representative period with the following:

```@example solution
using JuMP
Expand Down Expand Up @@ -461,7 +463,7 @@ df = filter(
df_consumers,
view = true,
)
value.(energy_problem.model[:consumer_balance][df.index]);
value.(energy_problem.model[:consumer_balance][df.index])
```

Here `value.` (i.e., broadcasting) was used instead of the vector comprehension from previous examples just to show that it also works.
Expand All @@ -473,15 +475,15 @@ The value of the constraint is obtained by looking only at the part with variabl
To save the solution to CSV files, you can use [`save_solution_to_file`](@ref):

```@example solution
mkdir("output")
save_solution_to_file("output", energy_problem)
mkdir("outputs")
save_solution_to_file("outputs", energy_problem)
```

### Plotting

In the previous sections, we have shown how to create vectors such as the one for flows. If you want simple plots, you can plot the vectors directly using any package you like.

If you would like more custom plots, there is a separate repository [TulipaPlots[.jl](https://github.com/TulipaEnergy/TulipaPlots.jl), under development, which provides tailored-made plots for _TulipaEnergyModel.jl_. Check it out for inspiration.
If you would like more custom plots, check out [TulipaPlots.jl](https://github.com/TulipaEnergy/TulipaPlots.jl), under development, which provides tailor-made plots for _TulipaEnergyModel.jl_.

## [Hydrothermal Dispatch example](@id hydrothermal-example)

Expand Down
8 changes: 4 additions & 4 deletions src/create-model.jl
Expand Up @@ -90,7 +90,7 @@ end
)
Computes the incoming and outgoing expressions per row of df_cons for the constraints
that are within (intra) the representative period.
that are within (intra) the representative periods.
This function is only used internally in the model.
Expand Down Expand Up @@ -176,7 +176,7 @@ end
)
Computes the incoming and outgoing expressions per row of df_inter for the constraints
that are between (inter) the representative period.
that are between (inter) the representative periods.
This function is only used internally in the model.
Expand Down Expand Up @@ -228,7 +228,7 @@ If the profile does not exist, uses `default_value` instead of **each** profile
`profiles` should be a dictionary of profiles, for instance `graph[a].profiles` or `graph[u, v].profiles`.
If `profiles[key]` exists, then this function computes the aggregation of `profiles[key]`
over the range `block` using the aggregator `agg`, i.e., `agg(profiles[key][block])`.
If `profiles[key]` does not exist, then this substitutes it by a vector of `default_value`s.
If `profiles[key]` does not exist, then this substitutes it with a vector of `default_value`s.
"""
function profile_aggregation(agg, profiles, key, block, default_value)
if haskey(profiles, key)
Expand Down Expand Up @@ -290,7 +290,7 @@ function create_model(graph, representative_periods, dataframes, timeframe; writ
Ai = filter_assets(:investable, true)
Fi = filter_flows(:investable, true)

# Maximum time step
# Maximum timestep
Tmax = maximum(last(rp.timesteps) for rp in representative_periods)
expression_workspace = Vector{JuMP.AffExpr}(undef, Tmax)

Expand Down
6 changes: 3 additions & 3 deletions src/input-schemas.jl
Expand Up @@ -92,8 +92,8 @@ const schemas = (
rep_periods = (
data = OrderedDict(
:id => Int, # Representative period ID
:num_timesteps => Int, # Numer of time steps
:resolution => Float64, # Duration of each time steps (hours)
:num_timesteps => Int, # Numer of timesteps
:resolution => Float64, # Duration of each timestep (hours)
),

# Schema for the rep-periods-mapping.csv file.
Expand All @@ -107,7 +107,7 @@ const schemas = (
profiles_data = OrderedDict(
:profile_name => Symbol, # Asset ID
:rep_period => Int, # Representative period ID
:timestep => Int, # Time step ID
:timestep => Int, # Timestep ID
:value => Float64, # p.u. (per unit)
),
),
Expand Down
26 changes: 13 additions & 13 deletions src/io.jl
Expand Up @@ -11,7 +11,7 @@ Returns the [`TulipaEnergyModel.EnergyProblem`](@ref) reading all data from CSV
in the `input_folder`.
This is a wrapper around `create_graph_and_representative_periods_from_csv_folder` that creates
the `EnergyProblem` structure.
Set strict = true to error if assets are missing from partition data.
Set `strict = true` to error if assets are missing from partition data.
"""
function create_energy_problem_from_csv_folder(input_folder::AbstractString; strict = false)
graph, representative_periods, timeframe =
Expand All @@ -23,7 +23,7 @@ end
graph, representative_periods, timeframe = create_graph_and_representative_periods_from_csv_folder(input_folder; strict = false)
Returns the `graph` structure that holds all data, and the `representative_periods` array.
Set strict = true to error if assets are missing from partition data.
Set `strict = true` to error if assets are missing from partition data.
The following files are expected to exist in the input folder:
Expand Down Expand Up @@ -261,7 +261,7 @@ end
"""
read_csv_with_schema(file_path, schema; csvargs...)
Reads the csv at `file_path` validating the data using the `schema`.
Reads the csv at `file_path` and validates the data using the `schema`.
It assumes that the file's header is at the second row.
The first row of the file contains some metadata information that is not used.
Additional keywords arguments can be passed to `CSV.read`.
Expand All @@ -275,9 +275,9 @@ end
"""
read_csv_with_implicit_schema(dir, filename; csvargs...)
Reads the csv at direcory `dir` named `filename` validating the data using a schema
chosen based on `filename`.
The function [`read_csv_with_schema`](@ref) is responsible for actually reading the file.
Reads the csv at direcory `dir` named `filename` and validates the data using a schema
based on `filename`.
The function [`read_csv_with_schema`](@ref) reads the file.
Additional keywords arguments can be passed to `CSV.read`.
"""
function read_csv_with_implicit_schema(dir, filename; csvargs...)
Expand Down Expand Up @@ -324,10 +324,10 @@ The following files are created:
capacity value. Only investable assets are included.
- `flows-investment.csv`: Similar to `assets-investment.csv`, but for flows.
- `flows.csv`: The value of each flow, per `(from, to)` flow, `rp` representative period
and `timestep`. Since the flow is in power, the value at a time step is equal to the value
and `timestep`. Since the flow is in power, the value at a timestep is equal to the value
at the corresponding time block, i.e., if flow[1:3] = 30, then flow[1] = flow[2] = flow[3] = 30.
- `storage-level.csv`: The value of each storage level, per `asset`, `rp` representative period,
and `timestep`. Since the storage level is in energy, the value at a time step is a
and `timestep`. Since the storage level is in energy, the value at a timestep is a
proportional fraction of the value at the corresponding time block, i.e., if level[1:3] = 30,
then level[1] = level[2] = level[3] = 10.
"""
Expand Down Expand Up @@ -438,7 +438,7 @@ end
"""
_interpolate_storage_level!(df, time_column::Symbol)
Tranform the storage level dataframe from grouped timesteps or periods to incremental ones by interpolation.
Transform the storage level dataframe from grouped timesteps or periods to incremental ones by interpolation.
The starting value is the value of the previous grouped timesteps or periods or the initial value.
The ending value is the value for the grouped timesteps or periods.
"""
Expand All @@ -462,8 +462,8 @@ end
"""
_parse_rp_partition(Val(specification), timestep_string, rp_timesteps)
Parses the timestep_string according to the specification.
The representative period time steps (`rp_timesteps`) might not be used in the computation,
Parses the `timestep_string` according to the specification.
The representative period timesteps (`rp_timesteps`) might not be used in the computation,
but it will be used for validation.
The specification defines what is expected from the `timestep_string`:
Expand Down Expand Up @@ -578,7 +578,7 @@ input `partitions`.
possibly empty.
`timesteps_per_rp` must be a dictionary indexed by `rp` and its values are the
time steps of that `rp`.
timesteps of that `rp`.
To obtain the partitions, the columns `specification` and `partition` from `df`
are passed to the function [`_parse_rp_partition`](@ref).
Expand Down Expand Up @@ -608,7 +608,7 @@ input `partitions`.
possibly empty.
`timesteps_per_rp` must be a dictionary indexed by `rp` and its values are the
time steps of that `rp`.
timesteps of that `rp`.
To obtain the partitions, the columns `specification` and `partition` from `df`
are passed to the function [`_parse_rp_partition`](@ref).
Expand Down
4 changes: 2 additions & 2 deletions src/run-scenario.jl
Expand Up @@ -6,8 +6,8 @@ export run_scenario
Run the scenario in the given `input_folder` and return the energy problem.
The `output_folder` is optional. If it is specified, save the sets, parameters, and solution to the `output_folder`.
The `optimizer` and `parameters` keyword arguments can be used to change the default optimizer
(which is HiGHS) and its parameters. The variables are passed to the [`solve_model`](@ref) function.
The `optimizer` and `parameters` keyword arguments can be used to change the optimizer
(the default is HiGHS) and its parameters. The variables are passed to the [`solve_model`](@ref) function.
"""
function run_scenario(
input_folder::AbstractString,
Expand Down

0 comments on commit 42d9768

Please sign in to comment.