Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,26 @@
[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://sintefore.github.io/TimeStruct.jl/stable/)
[![In Development](https://img.shields.io/badge/docs-dev-blue.svg)](https://sintefore.github.io/TimeStruct.jl/dev/)

TimeStruct is a Julia package that supports the efficient development of optimization models with multi-horizon time modelling and possible uncertainty.
The package is designed to be used in combination with the JuMP package for optimization modeling in Julia.
TimeStruct is a Julia package that supports the efficient development of optimization models with multi-horizon time modeling and possible uncertainty.
The package is designed to be used in combination with [JuMP](https://jump.dev/) for optimization modeling in Julia.

## Installation

TimeStruct.jl is a registered Julia package. Install it using the package manager:

```julia
using Pkg
Pkg.add("TimeStruct")
```

Or in the Julia REPL package mode (press `]`):
```
] add TimeStruct
pkg> add TimeStruct
```

## Example

The following shows a simple example of usage. For further details we refer to the documentation.
The following shows a simple example of usage. For further details, refer to the [documentation](https://sintefore.github.io/TimeStruct.jl/stable/).

```julia
using JuMP
Expand All @@ -32,9 +40,8 @@ model = Model()
@objective(model, Min, sum(income[t] * x[t] for t in periods))
```

## Cite
If you find TimeStruct useful in your work, we kindly request that you cite the
following [paper](https://doi.org/10.21105/joss.07578):
## Citation
If you find TimeStruct useful in your work, we kindly request that you cite the following [paper](https://doi.org/10.21105/joss.07578):
```
@article{Flatberg2025,
doi = {10.21105/joss.07578},
Expand Down
34 changes: 17 additions & 17 deletions docs/src/contribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,33 @@ Contributing to `TimeStruct` can be achieved in several different ways.

## [File a bug report](@id con-bug_rep)

One approach to contributing to `TimeStruct` is through filing a bug report as an *[issue](https://github.com/sintefore/TimeStruct.jl/issues/new)* when unexpected behaviour is occurring.
One approach to contributing to `TimeStruct` is by filing a bug report as an *[issue](https://github.com/sintefore/TimeStruct.jl/issues/new)* when unexpected behavior is occurring.

When filing a bug report, please follow the following guidelines:
When filing a bug report, please follow these guidelines:

1. be certain that the bug is a bug and originating in `TimeStruct`:
1. Be certain that the bug is a bug and originates in `TimeStruct`:
- If the problem is within the results of your optimization problem, please be certain that your optimization model is correctly set up.
- If the problem is only appearing for specific solvers, it is most likely not a bug in `TimeStruct`, but instead a problem of the solver wrapper for `MathOptInterface`.
- If the problem only appears for specific solvers, it is most likely not a bug in `TimeStruct`, but instead a problem with the solver wrapper for `MathOptInterface`.
In this case, please contact the developers of the corresponding solver wrapper.
2. label the issue as bug, and
3. provide a minimum working example of a case in which the bug occurs.
This minimum working example _**should**_ not be based on a potential application of `TimeStruct`.
Instead, it is important to focus purely on how `TimeStruct` is behaving when the bug occurs.
2. Label the issue as a bug, and
3. Provide a minimum working example of a case in which the bug occurs.
This minimum working example _**should not**_ be based on a potential application of `TimeStruct`.
Instead, it is important to focus purely on how `TimeStruct` behaves when the bug occurs.

## [Feature requests](@id con-feat_req)

Although `TimeStruct` was designed with the aim of flexibility with respect to incorporation of different time structures, it sometimes still requires additional features.
Although `TimeStruct` was designed with the aim of flexibility with respect to incorporating different time structures, it sometimes still requires additional features.
Feature requests can be achieved through two approaches:

1. create an issue describing the aim of the feature request and
2. incorporate the feature request through a fork of the repository and open a pull request.
1. Create an issue describing the aim of the feature request, and
2. Incorporate the feature request through a fork of the repository and open a pull request.

### [Create an Issue](@id con-feat_req-issue)

Creating a new *[issue](https://github.com/sintefore/TimeStruct.jl/issues/new)* for a feature request is our standard approach for contributing to `TimeStruct`.
Due to the modularity of `TimeStruct`'s individual time structures, it is not necessarily straight forward to understand how to best incorporate required features into the framework without breaking existing time structures.
Due to the modularity of `TimeStruct`'s individual time structures, it is not necessarily straightforward to understand how to best incorporate required features into the framework without breaking existing time structures.

When creating a new issue as feature request, please follow the the following guidelines.
When creating a new issue as a feature request, please follow these guidelines:

1. **Reason for the feature**: Please describe the reasoning for the feature request. What functionality do you require in `TimeStruct`?
2. **Required outcome**: What should be the outcome when including the feature and what should be the minimum requirements of the outcome?
Expand All @@ -39,18 +39,18 @@ When creating a new issue as feature request, please follow the the following gu
### [Incorporating the feature requests through a fork](@id con-feat_req-fork)

!!! note
The approach used for providing code is based on the excellent description of the [JuMP](https://jump.dev/JuMP.jl/stable/developers/contributing/#Contribute-code-to-JuMP) package.
The approach used for providing code is based on the excellent description from the [JuMP](https://jump.dev/JuMP.jl/stable/developers/contributing/#Contribute-code-to-JuMP) package.
We essentially follow the same approach with minor changes.

If you would like to work directly in `TimeStruct`, you can also incorporate your changes directly.
In this case, it is beneficial to follow the outlined steps:
In this case, it is beneficial to follow these outlined steps:

#### [Step 1: Create an issue](@id con-feat_req-fork-step_1)

Even if you plan to incorporate the code directly, we advise you to first follow the steps outlined in *[Create an Issue](@ref con-feat_req-issue)*.
This way, it is possible for us to comment on the solution approach(es) and assess potential problems with other time structures.

Through creating an issue first, it is possible for us to comment directly on the proposed changes and assess, whether we consider the proposed changes to follow the philosophy of the framework.
By creating an issue first, it is possible for us to comment directly on the proposed changes and assess whether we consider the proposed changes to follow the philosophy of the framework.

#### [Step 2: Create a fork of `TimeStruct`](@id con-feat_req-fork-step_2)

Expand All @@ -74,7 +74,7 @@ New tests should be based on a minimum working example in which the new concept
Aside from the individual tests, it is required to use [`JuliaFormatter`](https://domluna.github.io/JuliaFormatter.jl/stable/) on the code.

It is not necessary to provide changes directly in the documentation.
It can be easier to include these changes after the pull request is accepted in principal.
It can be easier to include these changes after the pull request is accepted in principle.

#### [Step 5: Create a pull request](@id con-feat_req-fork-step_5)

Expand Down
53 changes: 27 additions & 26 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,53 @@ Welcome to the documentation of the TimeStruct package!

## What is TimeStruct?

TimeStruct is a Julia package that supports the efficient development of optimization models with multi-horizon time modelling. The package is designed to be used in combination with the JuMP package for optimization modeling in Julia.
TimeStruct is a Julia package that supports the efficient development of optimization models with multi-horizon time modeling. The package is designed to be used in combination with [JuMP](https://jump.dev/) for optimization modeling in Julia.

The main concept is a [`TimeStructure`](@ref) which is an abstract type that enables iteration over a sequence of time periods. These time periods can serve as indices for optimization variables and can also facilitate the lookup of associated data values from time-varying profiles. By having a well-defined interface that is supported by all time structures, optimization models that are valid for arbitrary time structures can be written. The following example shows how a small optimization model can be set up in a function that accepts a general time structure and cost profile.

```@example
using JuMP, TimeStruct

```@ex
using JuMP, TimeStruct

function(periods::TimeStructure, cost::TimeProfile)
m = Model()
@variable(m, x[periods] >= 0)
@constraint(m, sum(x[t] for t in periods) >= 10)
@objective(m, Min, sum(cost[t] * x[t] for t in periods))
end
function optimize_model(periods::TimeStructure, cost::TimeProfile)
m = Model()
@variable(m, x[periods] >= 0)
@constraint(m, sum(x[t] for t in periods) >= 10)
@objective(m, Min, sum(cost[t] * x[t] for t in periods))
return m
end
```

## Why use TimeStruct?
In complex optimization models, tracking relationships between time periods often requires substantial coding, especially with stochastic versions. Time constraints can introduce subtle bugs, particularly with linking constraints between periods or scenarios, like managing storage inventory or dispatch constraints. Extra indices for time and scenarios complicate the code, making it harder to read, maintain, and adapt.

TimeStruct abstracts time structures, providing a common interface that simplifies the code and supports various time structures (operational, strategic/investment periods, and uncertainties). This abstraction allows modellers to focus on other model properties and facilitates running a single model for different time structures, and aids in the development of decomposition techniques.
TimeStruct abstracts time structures, providing a common interface that simplifies the code and supports various time structures (operational, strategic/investment periods, and uncertainties). This abstraction allows modelers to focus on other model properties and facilitates running a single model for different time structures, and aids in the development of decomposition techniques.

## How to get started

The package is registered in the general registry and can be installed in standard fashion
The package is registered in the general registry and can be installed in the standard fashion:

```julia
] add TimeStruct
```

This documentation consists of a manual explaining concepts and giving examples as well as a complete
API reference.
This documentation consists of a manual explaining concepts and giving examples, as well as a complete API reference.

## Cite
## Citation

If you find TimeStruct useful in your work, we kindly request that you cite the
following:
```
If you find TimeStruct useful in your work, we kindly request that you cite the following:

@misc{TimeStruct.jl,
author = {Flatberg, Truls and Hellemo, Lars},
title = {{TimeStruct.jl: Flexible time structures in optimization modelling}},
month = Jan,
year = 2024,
doi = {10.5281/zenodo.10511399},
publisher = {Zenodo},
url = {https://zenodo.org/records/10511399}
```bibtex
@article{Flatberg2025,
doi = {10.21105/joss.07578},
url = {https://doi.org/10.21105/joss.07578},
year = {2025},
publisher = {The Open Journal},
volume = {10},
number = {107},
pages = {7578},
author = {Truls Flatberg and Julian Straus and Lars Hellemo},
title = {TimeStruct.jl -- flexible multi-horizon time modeling in optimization models},
journal = {Journal of Open Source Software}
}
```

Expand Down
31 changes: 10 additions & 21 deletions docs/src/manual/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,18 @@ durations = [duration(t) for t in periods]

## [Calendar based](@id man-oper-calendar)

For some applications it is required to relate the time periods to actual calendar dates.
This is supported by the time structure [`CalendarTimes`](@ref) that allows for creation and iteration of a calendar based sequence of periods in combination with calendar arithmetic.
For some applications, it is required to relate the time periods to actual calendar dates.
This is supported by the time structure [`CalendarTimes`](@ref) that allows for creation and iteration of a calendar-based sequence of periods in combination with calendar arithmetic.

The following example shows the creation of a time structure with 12 months starting from
the first of January 2024. The duration of each time period is given in hours by default, but it is possible to specify the time units to use by providing the period type to use:
The following example shows the creation of a time structure with 12 months starting from January 1, 2024. The duration of each time period is given in hours by default, but it is possible to specify the time units to use by providing the period type:

```@repl ts
using Dates
year = CalendarTimes(DateTime(2024, 1, 1), 12, Month(1));
duration(first(year); dfunc = Dates.Day)
```

You can also make the time structure for a specific time zone as shown in the following
example with 3 days in the end of March with a transition to summer time on the second day:
You can also make the time structure for a specific time zone, as shown in the following example with 3 days in late March with a transition to daylight saving time on the second day:

```@repl ts
using TimeZones
Expand All @@ -42,8 +40,7 @@ duration.(periods)
## [Operational scenarios](@id man-oper-osc)

Operations often face uncertain operating conditions. In energy systems modeling, a typical example is the availability of wind and solar power.
One method for accounting for this uncertainty is to have multiple operational scenarios that are used to evaluate the cost and feasibility of
operations, where each scenario has a given probability of occurring.
One method for accounting for this uncertainty is to have multiple operational scenarios that are used to evaluate the cost and feasibility of operations, where each scenario has a given probability of occurring.

The time structure [`OperationalScenarios`](@ref) represents an unordered collection of
operational scenarios where each scenario has a separate time structure and an associated
Expand All @@ -60,21 +57,17 @@ scenarios = OperationalScenarios(

![Illustration of OperationalScenarios](./../figures/scenario.png)

Similar to representative periods, each period has a [`multiple`](@ref) that is defined
relative to the maximum duration for all scenarios. In addition, each time period
has a [`probability`](@ref)equal to the probability of its scenario. Thus we have that:
Similar to representative periods, each period has a [`multiple`](@ref) that is defined relative to the maximum duration for all scenarios. In addition, each time period has a [`probability`](@ref) equal to the probability of its scenario. Thus, we have that:

```@repl os
sum(duration(t) * probability(t) * multiple(t) for t in scenarios)
```

## [Representative periods](@id man-oper-repr)

In some cases, a fine-scale representation for the operations of the infrastructure of the whole time horizon, is not feasible. A possible strategy is then to select one or more representative periods and use them to evaluate operational cost and feasibility. The time structure [`RepresentativePeriods`](@ref) consists of an ordered sequence of representative periods that represents a longer period of time. Each
representative period covers a specified share of the whole time period.
In some cases, a fine-scale representation of the operations of the infrastructure for the whole time horizon is not feasible. A possible strategy is then to select one or more representative periods and use them to evaluate operational cost and feasibility. The time structure [`RepresentativePeriods`](@ref) consists of an ordered sequence of representative periods that represents a longer period of time. Each representative period covers a specified share of the whole time period.

The following example shows an example with a year with daily resolution represented by two weeks
with a share of 0.7 and 0.3 respectively.
The following example shows a year with daily resolution represented by two weeks with shares of 0.7 and 0.3, respectively.

```@repl rp
using JuMP, TimeStruct
Expand All @@ -87,8 +80,7 @@ periods = RepresentativePeriods(
);
```

The time periods can be iterated both for the whole time structure and individually by each representative period using the [`repr_periods`](@ref) function. This is illustrated here
when setting up a JuMP model with a separate constraint for each representative period:
The time periods can be iterated both for the whole time structure and individually by each representative period using the [`repr_periods`](@ref) function. This is illustrated here when setting up a JuMP model with a separate constraint for each representative period:

```@repl rp
m = Model();
Expand All @@ -101,10 +93,7 @@ end
@constraint(m, sum(prod[t] * multiple(t) for t in periods) <= 1);
```

For each time period the [`multiple`](@ref) function returns how many times the given period
should be counted when aggregating to the whole [`RepresentativePeriods`](@ref) structure. This
will take into account both the duration and share of each representative period, thus
we have that:
For each time period, the [`multiple`](@ref) function returns how many times the given period should be counted when aggregating to the whole [`RepresentativePeriods`](@ref) structure. This will take into account both the duration and share of each representative period. Thus, we have that:

```@repl rp
sum(duration(t) * multiple(t) for t in periods)
Expand Down
Loading