diff --git a/README.md b/README.md index 08d846f..4a25c31 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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}, diff --git a/docs/src/contribute.md b/docs/src/contribute.md index 0c746e4..6af92a8 100644 --- a/docs/src/contribute.md +++ b/docs/src/contribute.md @@ -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? @@ -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) @@ -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) diff --git a/docs/src/index.md b/docs/src/index.md index 5cc7fff..9c6c739 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -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} } ``` diff --git a/docs/src/manual/basic.md b/docs/src/manual/basic.md index c8641d9..bbe12bb 100644 --- a/docs/src/manual/basic.md +++ b/docs/src/manual/basic.md @@ -18,11 +18,10 @@ 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 @@ -30,8 +29,7 @@ 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 @@ -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 @@ -60,9 +57,7 @@ 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) @@ -70,11 +65,9 @@ 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 @@ -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(); @@ -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) diff --git a/docs/src/manual/discount.md b/docs/src/manual/discount.md index f24ec7d..ebfee58 100644 --- a/docs/src/manual/discount.md +++ b/docs/src/manual/discount.md @@ -1,17 +1,9 @@ # [Discounting](@id man-disc) -For multi-year investment optimization models it is common practice to use an -objective that is discounted to get the net present value of the investment. -Since investment decisions usually are done on a strategic level, discount -factors are also calculated based on strategic periods. - -The discount factor for a time period `t` is found by the [`discount`](@ref) -function. There are two strategies for calculating the discount factor, -either all discounting is calculated based on the start of the strategic period -or it is based on finding an approximation of the average value over the -strategic period. The following example shows how these two types will -differ for a planning period of 50 years, consisting of 5 periods of -10 years: +For multi-year investment optimization models, it is common practice to use an objective that is discounted to get the net present value of the investment. +Since investment decisions are usually made on a strategic level, discount factors are also calculated based on strategic periods. + +The discount factor for a time period `t` is found by the [`discount`](@ref) function. There are two strategies for calculating the discount factor: either all discounting is calculated based on the start of the strategic period, or it is based on finding an approximation of the average value over the strategic period. The following example shows how these two types will differ for a planning period of 50 years, consisting of 5 periods of 10 years: ```@repl ts using TimeStruct @@ -20,9 +12,7 @@ df_start = [discount(t, ts, 0.05; type = "start") for t in ts] df_avg = [discount(t, ts, 0.05; type = "avg") for t in ts] ``` -While it is often normal to assume investments at the start of each -strategic period, it can be more correct to average the discount factor -for operational costs that are accrued throughout the strategic period. +While it is often normal to assume investments at the start of each strategic period, it can be more accurate to average the discount factor for operational costs that are accrued throughout the strategic period. We also provide a method in which the average discount factor is calculated for the beginning of the years within a strategic period: @@ -34,9 +24,6 @@ df_start = [discount(sp, ts, 0.05; type = "avg") for sp in sps] df_avg = [discount(sp, ts, 0.05; type = "avg_year") for sp in sps] ``` -This approach results in a slighly higher discount factor. +This approach results in a slightly higher discount factor. -To help setting up the objective function in a typical optimization problem, -there is a utility function [`objective_weight`](@ref) that returns -the weight to give a time period in the objective, considering both -discount factor, probability and possible multiplicity. +To help set up the objective function in a typical optimization problem, there is a utility function [`objective_weight`](@ref) that returns the weight to give a time period in the objective, considering both discount factor, probability, and possible multiplicity. diff --git a/docs/src/manual/iteration.md b/docs/src/manual/iteration.md index abffdc3..ef6894d 100644 --- a/docs/src/manual/iteration.md +++ b/docs/src/manual/iteration.md @@ -2,7 +2,7 @@ ## [Basic iteration](@id man-iter-basic) -All time structures are iterable over their operational time periods +All time structures are iterable over their operational time periods: ```@repl ts using TimeStruct @@ -15,10 +15,7 @@ end ## [Iteration with previous](@id man-iter-prev) -In many settings, e.g. tracking of storage, it is convenient to have -access to the previous time period. By using the custom iterator -[`withprev`](@ref) it is possible to return both the previous and -current time period as a tuple when iterating: +In many settings, e.g., tracking of storage, it is convenient to have access to the previous time period. By using the custom iterator [`withprev`](@ref), it is possible to return both the previous and current time period as a tuple when iterating: ```@repl ts using TimeStruct periods = SimpleTimes(5, 1); @@ -30,22 +27,15 @@ the current and next period (or `nothing` if none). ## [Iteration with chunks of time periods](@id man-iter-chunk) -Sometimes it is convenient to iterate through the time periods -as chunks of a fixed number of periods or minimum duration, e.g. in production planning -with minimum production runs. To simplify this process -there are several iterator wrappers that allows this kind of iteration pattern. +Sometimes it is convenient to iterate through the time periods as chunks of a fixed number of periods or minimum duration, e.g., in production planning with minimum production runs. To simplify this process, there are several iterator wrappers that allow this kind of iteration pattern. - -The [`chunk`](@ref) function iterates through a time structure returning -subsequences of length at most `n` starting at each time period. +The [`chunk`](@ref) function iterates through a time structure, returning subsequences of length at most `n` starting at each time period. ```@repl ts periods = SimpleTimes(5,1) collect(collect(ts) for ts in chunk(periods, 3)) ``` -This wrapper can be used for e.g. modelling of startup modelling with a minimum -uptime. The following example shows how this can be implemented as part of -a JuMP model: +This wrapper can be used for, e.g., modeling of startup processes with a minimum uptime. The following example shows how this can be implemented as part of a JuMP model: ```@example using JuMP, TimeStruct @@ -63,9 +53,7 @@ for cref in all_constraints(m, AffExpr, MOI.LessThan{Float64}) # hide println(constraint_string(MIME("text/plain"), cref; in_math_mode = true)) # hide end # hide ``` -Similarly, if modelling startup decisions with a minimum downtime, -it is possible to reverse the original time periods and then -chunk: +Similarly, if modeling startup decisions with a minimum downtime, it is possible to reverse the original time periods and then chunk: ```@example using JuMP, TimeStruct # hide periods = SimpleTimes(5,1) # hide @@ -82,9 +70,8 @@ for cref in all_constraints(m, AffExpr, MOI.LessThan{Float64}) # hide end # hide ``` -It is also possible to get cyclic behaviour by setting the `cyclic` argument to `true`. -If reaching the end before the required number of time periods, the chunk will continue -from the first time period. +It is also possible to get cyclic behavior by setting the `cyclic` argument to `true`. +If reaching the end before the required number of time periods, the chunk will continue from the first time period. ```@example using JuMP, TimeStruct # hide periods = SimpleTimes(5,1) # hide @@ -102,11 +89,9 @@ end # hide ## [Chunks based on duration](@id man-iter-chunk_dur) -If working with a time structure that has varying duration for its time periods, -it can be more convenient with chunks based on their combined duration. +If working with a time structure that has varying duration for its time periods, it can be more convenient to use chunks based on their combined duration. -The [`chunk_duration`](@ref) function iterates through a time structure returning -subsequences of duration at least `dur` starting at each time period. +The [`chunk_duration`](@ref) function iterates through a time structure, returning subsequences with a duration of at least `dur` starting at each time period. ```@repl ts periods = SimpleTimes(5,[1, 2, 1, 1.5, 0.5, 2]) collect(collect(ts) for ts in chunk_duration(periods, 3)) @@ -114,9 +99,7 @@ collect(collect(ts) for ts in chunk_duration(periods, 3)) ## [Indexing of operational time structures](@id man-iter-index) -It is possible to use indices for operational time structures, either directly -using [`SimpleTimes`](@ref) or [`CalendarTimes`](@ref) or by accessing an -operational scenario. +It is possible to use indices for operational time structures, either directly using [`SimpleTimes`](@ref) or [`CalendarTimes`](@ref), or by accessing an operational scenario. ```@repl ts periods = TwoLevel(3, 100, SimpleTimes(10,1)); diff --git a/docs/src/manual/multi.md b/docs/src/manual/multi.md index 01074b2..d03aa24 100644 --- a/docs/src/manual/multi.md +++ b/docs/src/manual/multi.md @@ -2,9 +2,7 @@ ## [TwoLevel structure](@id man-multi-twolevel) -The main motivation for the `TimeStruct` package is to support -multi-horizon optimization models. The time structure [`TwoLevel`](@ref) allows for a two level -approach, combining an ordered sequence of strategic periods with given duration and an associated operational time structure. +The main motivation for the `TimeStruct` package is to support multi-horizon optimization models. The time structure [`TwoLevel`](@ref) allows for a two-level approach, combining an ordered sequence of strategic periods with given duration and an associated operational time structure. ```@repl ts using TimeStruct @@ -15,9 +13,7 @@ periods = TwoLevel( ![Illustration of TwoLevel](./../figures/twolevel.png) -The following example shows a typical usage of a [`TwoLevel`](@ref) structure with investment -decisions on a strategic level and operational decision variables. It is possible to iterate -through each strategic period using the [`strat_periods`](@ref)function. +The following example shows a typical usage of a [`TwoLevel`](@ref) structure with investment decisions on a strategic level and operational decision variables. It is possible to iterate through each strategic period using the [`strat_periods`](@ref) function. ```@repl ts using JuMP @@ -30,9 +26,7 @@ for sp in strat_periods(periods) end ``` -It is also possible to combine a [`TwoLevel`](@ref) time structure with more complex -operational structures like [`RepresentativePeriods`](@ref) and [`OperationalScenarios`](@ref), -alone or in combination, as shown in the following example and illustrated the figure below. +It is also possible to combine a [`TwoLevel`](@ref) time structure with more complex operational structures like [`RepresentativePeriods`](@ref) and [`OperationalScenarios`](@ref), alone or in combination, as shown in the following example and illustrated in the figure below. ```@repl ts oper = SimpleTimes(5,1); @@ -45,21 +39,13 @@ periods = TwoLevel([scen, repr, repr_scen]); ![Complex TwoLevel](./../figures/two_complex.png) -In the above examples, the duration of the operational time structures have -been equal to the duration of the strategic periods, but this is not required. -If the duration of the operational time structure is shorter than the strategic -period, this will be accounted for with the [`multiple`](@ref) function. +In the above examples, the duration of the operational time structures has been equal to the duration of the strategic periods, but this is not required. +If the duration of the operational time structure is shorter than the strategic period, this will be accounted for with the [`multiple`](@ref) function. -It is also sometimes convenient to use a different time unit for -the strategic periods than the operational time periods. This is -controlled by the `op_per_strat` field of the [`TwoLevel`](@ref) structure -that holds the number of operational periods per strategic period. +It is also sometimes convenient to use a different time unit for the strategic periods than the operational time periods. This is controlled by the `op_per_strat` field of the [`TwoLevel`](@ref) structure, which holds the number of operational periods per strategic period. -A typical use case is an investment problem where one uses years -to measure duration at the strategic level and hours/days on the operational level. -Below is an example with 3 strategic periods of duration 5, 5, and 10 years -respectively, while the operational time structure is given by -representative periods with duration in days. The `op_per_strat` is then set to 365. +A typical use case is an investment problem where one uses years to measure duration at the strategic level and hours/days on the operational level. +Below is an example with 3 strategic periods of duration 5, 5, and 10 years, respectively, while the operational time structure is given by representative periods with duration in days. The `op_per_strat` is then set to 365. ```@repl ts week = SimpleTimes(7,1); @@ -69,12 +55,11 @@ periods = TwoLevel(3, [5, 5, 10], [repr, repr, repr], 365.0); ## [TwoLevelTree structure](@id man-multi-twoleveltree) -If there is uncertainty at a strategic level, this can be incorporated using the [`TwoLevelTree`](@ref) -time structure. This structure is represented by a tree, with each node corresponding to a strategic period that contains an operational time structure. +If there is uncertainty at a strategic level, this can be incorporated using the [`TwoLevelTree`](@ref) time structure. This structure is represented by a tree, with each node corresponding to a strategic period that contains an operational time structure. The operational time structure can be any combination of the *[described structures](@ref man-oper)*. The following example demonstrates how to create a regular tree through a constructor (`TwoLevelTree(duration::S, branching::Vector, ts::OP; op_per_strat::Float64 = 1.0) where {S,T,OP<:TimeStructure{T}}`) where each strategic period spans 3 years and is represented by a week with daily resolution. -The second argument of the constructor function specifies the number of branches at each stage of the tree, excluding the first stage. +The second argument of the constructor function specifies the number of branches at each stage of the tree, excluding the first stage. ```@repl ts using TimeStruct @@ -84,10 +69,10 @@ two_level_tree = TwoLevelTree(3, [3, 2], week; op_per_strat = 52.0); ![Illustration of TwoLevelTree](./../figures/two_level_tree.png) -The branching probabilities are equal for all branches as indicated in green in the figure. +The branching probabilities are equal for all branches, as indicated in green in the figure. We also provide the possibility of having differing tree structures through the application of the [`TreeNode`](@ref) type. -A [`TreeNode`](@ref) approach for above's time structure would be given by +A [`TreeNode`](@ref) approach for the above time structure would be given by: ```@repl ts using TimeStruct @@ -109,9 +94,9 @@ two_level_tree = TwoLevelTree( ) ``` -Similar as for [`TwoLevel`](@ref), the strategic nodes can be iterated using [`strat_periods`](@ref). -It is possible to connect the nodes to their predecessor by iterating using the [`withprev`](@ref) iterator that returns a tuple with the parent or nothing if no parent, together with the node itself. -This provides the flexibility to track decisions in the tree as shown by the following example that allows investment into new capacity in each strategic node while tracking the accumulated capacity. +Similar to [`TwoLevel`](@ref), the strategic nodes can be iterated using [`strat_periods`](@ref). +It is possible to connect the nodes to their predecessor by iterating using the [`withprev`](@ref) iterator, which returns a tuple with the parent (or `nothing` if no parent) together with the node itself. +This provides the flexibility to track decisions in the tree, as shown by the following example that allows investment into new capacity in each strategic node while tracking the accumulated capacity. ```@repl ts using JuMP @@ -128,7 +113,7 @@ end ``` To ensure consistency across the tree, it is possible to iterate through all strategic scenarios in the tree using [`strategic_scenarios`](@ref). -Here each scenario is a path from the root node to one of the leaves of the tree. +Here, each scenario is a path from the root node to one of the leaves of the tree. In the example above, if we only allow one investment in the planning period, this can be added by restricting the number of investments in each scenario: ```@repl ts diff --git a/docs/src/manual/profiles.md b/docs/src/manual/profiles.md index a58be34..d8fc0ff 100644 --- a/docs/src/manual/profiles.md +++ b/docs/src/manual/profiles.md @@ -1,17 +1,15 @@ # [Time profiles](@id man-prof) -To provide data for different time structures there is a flexible system of different time -profiles that can be indexed by time periods. +To provide data for different time structures, there is a flexible system of different time profiles that can be indexed by time periods. - [`FixedProfile`](@ref): Time profile with the same value for all time periods - [`OperationalProfile`](@ref): Time profile with values varying with operational time periods - [`ScenarioProfile`](@ref): Holds a separate time profile for each operational scenario - [`RepresentativeProfile`](@ref): Holds a separate time profile for each representative period -- [`StrategicProfile`](@ref) : Holds a separate time profile for each strategic period -- [`StrategicStochasticProfile`](@ref) : Holds a separate time profile for each strategic node in a strategic tree +- [`StrategicProfile`](@ref): Holds a separate time profile for each strategic period +- [`StrategicStochasticProfile`](@ref): Holds a separate time profile for each strategic node in a strategic tree -The following code example shows how these profile types can be combined in a flexible -manner to produce different overall profiles. +The following code example shows how these profile types can be combined in a flexible manner to produce different overall profiles. ```julia rep_periods = RepresentativePeriods(2, 365, [0.6, 0.4], [SimpleTimes(7,1), SimpleTimes(7,1)]) @@ -30,5 +28,5 @@ cost = StrategicProfile( ) ``` -Illustration of profile values for the various time periods as defined in the profile example +Illustration of profile values for the various time periods as defined in the profile example: ![Time profile values](./../figures/profiles.png) diff --git a/docs/src/reference/api.md b/docs/src/reference/api.md index 418494d..fac9347 100644 --- a/docs/src/reference/api.md +++ b/docs/src/reference/api.md @@ -1,4 +1,4 @@ -# [Interface for using `TimeStruct](@id api) +# [Interface for using `TimeStruct`](@id api) ## [Available time structures](@id api-ts) @@ -8,7 +8,7 @@ TimeStructure ``` -### [Conecrete types](@id api-ts-con) +### [Concrete types](@id api-ts-con) ```@docs SimpleTimes @@ -92,7 +92,7 @@ StrategicStochasticProfile Discounter ``` -### [Functions](@id api-disc-type) +### [Functions](@id api-disc-func) ```@docs discount diff --git a/docs/src/showcases/emx.md b/docs/src/showcases/emx.md index 05d77fe..852dd79 100644 --- a/docs/src/showcases/emx.md +++ b/docs/src/showcases/emx.md @@ -2,11 +2,11 @@ **[EnergyModelsX (EMX)](https://github.com/EnergyModelsX)** is a flexible multi-horizon energy system optimization framework using [`JuMP`](https://jump.dev/JuMP.jl/stable/). It utilizes `TimeStruct` from the beginning and influenced some of the added features of `TimeStruct`. -Its implemtation is outlined in the following sections. +Its implementation is outlined in the following sections. ## [Implementation of the core structures](@id show-emx-core) -The core package `EnergyModelsBase` differentiates between variables indexed over strategic periods and variables indexed over operational period, similar to the battery sizing example. +The core package `EnergyModelsBase` differentiates between variables indexed over strategic periods and variables indexed over operational periods, similar to the battery sizing example. As an example, consider the following two functions for variable declaration for a given `𝒯::TimeStructure`: - [`variables_capacity(m, 𝒩::Vector{<:Node}, 𝒳ᵛᵉᶜ, 𝒯, modeltype::EnergyModel)`](https://github.com/EnergyModelsX/EnergyModelsBase.jl/blob/10036d91949e69cae0fbdb8e1652b85d7f82742a/src/model.jl#L175) with, among others, @@ -23,11 +23,11 @@ As an example, consider the following two functions for variable declaration for @variable(m, opex_var[𝒩ᵒᵖᵉˣ, 𝒯ᴵⁿᵛ]) ``` -These two functions highlight the simplicity of using `TimeStruct` as the individual time structures allow for iterations within `JuMP` macros. -In addition, it simplifies the index sets as changing the number of intermediate time structures through incorporating, *e.g.*, [`OperationalScenarios`](@ref man-oper-osc), does not require changes to the variable declarations. +These two functions highlight the simplicity of using `TimeStruct`, as the individual time structures allow for iterations within `JuMP` macros. +In addition, it simplifies the index sets, as changing the number of intermediate time structures through incorporating, *e.g.*, [`OperationalScenarios`](@ref man-oper-osc), does not require changes to the variable declarations. The change in the structure through the incorporation of the operational scenarios is instead available within the individual `TimePeriod` type. -The same is also true for constraint functions as shown in [`constraints_capacity(m, n::Node, 𝒯::TimeStructure, modeltype::EnergyModel)`](https://github.com/EnergyModelsX/EnergyModelsBase.jl/blob/10036d91949e69cae0fbdb8e1652b85d7f82742a/src/constraint_functions.jl#L17): +The same is also true for constraint functions, as shown in [`constraints_capacity(m, n::Node, 𝒯::TimeStructure, modeltype::EnergyModel)`](https://github.com/EnergyModelsX/EnergyModelsBase.jl/blob/10036d91949e69cae0fbdb8e1652b85d7f82742a/src/constraint_functions.jl#L17): ```julia @constraint(m, [t ∈ 𝒯], m[:cap_use][n, t] <= m[:cap_inst][n, t]) @@ -48,11 +48,11 @@ where ``t`` corresponds to an operational period and ``t_{inv}`` to a strategic ### [`withprev` and its application](@id show-emx-adv-withprev) -As outlined, neither [`OperationalScenarios`](@ref man-oper-osc) or [`RepresentativePeriods`](@ref man-oper-repr) where available in the initial development of **EMX**. -However, the adjustments towards including these were limited to constraints that require the previous periods, that is constraints declared through the [`withprev`](@ref man-iter-prev) functionality. -All other constraints did not require any changes as the respective `TimePeriod`s included the required information. +As outlined, neither [`OperationalScenarios`](@ref man-oper-osc) nor [`RepresentativePeriods`](@ref man-oper-repr) were available in the initial development of **EMX**. +However, the adjustments toward including these were limited to constraints that require the previous periods, that is, constraints declared through the [`withprev`](@ref man-iter-prev) functionality. +All other constraints did not require any changes, as the respective `TimePeriod`s included the required information. -The implementation of the the *[`Storage` level balance](https://github.com/EnergyModelsX/EnergyModelsBase.jl/blob/10036d91949e69cae0fbdb8e1652b85d7f82742a/src/constraint_functions.jl#L194C10-L195C1)* provides an example on how `TimeStruct` can be used. +The implementation of the *[`Storage` level balance](https://github.com/EnergyModelsX/EnergyModelsBase.jl/blob/10036d91949e69cae0fbdb8e1652b85d7f82742a/src/constraint_functions.jl#L194C10-L195C1)* provides an example of how `TimeStruct` can be used. It iterates through all potential subtypes of the given `TimeStructure`. Multiple dispatch is included to identify whether the `TimeStructure` includes `OperationalScenarios` or `RepresentativePeriods`. If this is the case, additional constraints can be incorporated. @@ -60,24 +60,23 @@ Due to the iteration (and storing) of all previous periods, it is possible to id !!! tip "Proposed workflow" While it can be tempting to design models from the initial stage to include both operational scenarios and representative periods, it is beneficial to avoid including these. - `TimeStruct` simplifies the introduction of either `TimeStructure` in a latter stage. + `TimeStruct` simplifies the introduction of either `TimeStructure` at a later stage. It is hence significantly easier to develop a model without considering complex time structures. - If you do not have any constraints depending on the previous periods, you even do not have to do any changes to your model when including `OperationalScenarios` or `RepresentativePeriods`. + If you do not have any constraints depending on the previous periods, you do not even have to make any changes to your model when including `OperationalScenarios` or `RepresentativePeriods`. ### [Chunks for unit commitment](@id show-emx-adv-chunk) The functionality of `TimeStruct` for chunks based on the minimum required time is utilized in the *[unit commitment constraints of `Reformer` nodes](https://github.com/EnergyModelsX/EnergyModelsHydrogen.jl/blob/88a21ffae88b7ce199752aa5465313ad549718b6/src/constraints/reformer.jl#L259)* in [`EnergyModelsHydrogen`](https://energymodelsx.github.io/EnergyModelsHydrogen.jl/stable/). In this context, we require a minimum time for starting the node, shutting the node down, and when the node is offline due to limitations in the dynamics of the chemical plant. - -The used approach is similar to the `Storage` level balance utilizing the [`withprev`](@ref man-iter-prev) functionality for the majority of the time structures. -However, once on the lowest level, [`chunk_duration`](@ref man-iter-chunk_dur) is used in addition to provide limits on changes between the different states. -The `eltype`s of the iterator allows for further iterations to have access to the number of operational periods. +The used approach is similar to the `Storage` level balance, utilizing the [`withprev`](@ref man-iter-prev) functionality for the majority of the time structures. +However, once at the lowest level, [`chunk_duration`](@ref man-iter-chunk_dur) is used in addition to provide limits on changes between the different states. +The `eltype`s of the iterator allow for further iterations to have access to the number of operational periods. ### [Including strategic uncertainty](@id show-emx-adv-strat) **EMX** was not tested to also include strategic uncertainty as described in *[TwoLevelTree structure](@ref man-multi-twoleveltree)*. -The basic functionalities of `EnergyModelsBase` and the majority of the developed packages do however not have any problems with incorporating strategic uncertainty as the strategic periods are not linked. +The basic functionalities of `EnergyModelsBase` and the majority of the developed packages do, however, not have any problems with incorporating strategic uncertainty, as the strategic periods are not linked. In this case, the internal structure of `TimeStruct` allows the direct inclusion of strategic uncertainty without changes to the model. -The exception if when using the [`withprev`](@ref man-iter-prev) functionality on strategic periods as it is the case for the investments in [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/stable/). +The exception is when using the [`withprev`](@ref man-iter-prev) functionality on strategic periods, as is the case for the investments in [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/stable/). However, it is expected that the inclusion does not require any changes directly to the code structure due to the function overload on the [`withprev`](@ref man-iter-prev) functionality. diff --git a/src/discount.jl b/src/discount.jl index 5e6e7b6..4719c22 100644 --- a/src/discount.jl +++ b/src/discount.jl @@ -11,7 +11,7 @@ convert the time units of strategic periods in the time structure to years (defa As an example, consider the following time structure: ```julia -# Modelling of a day with hourly resolution for 50 years with a resolution of 5 years +# Modeling of a day with hourly resolution for 50 years with a resolution of 5 years periods = TwoLevel(10, 5 * 8760, SimpleTimes(24, 1)) # The parameter `timeunit_to_year` must in this case be 1 year / 8760 h @@ -137,7 +137,7 @@ end Calculates the overall objective weight for a time period `t` using a fixed `discount_rate`. -The weight consideres both discounting, the probability and potential multiplicity of `t` +The weight considers both discounting, the probability, and potential multiplicity of `t`. The function can be either called using a [`Discounter`](@ref) type or by specifying the parameters (time structure `ts`, `discount_rate` and potentially `timeunit_to_year`) directly. @@ -155,7 +155,7 @@ the time structure to years (default value = 1.0). !!! tip "Comparison with `discount`" Both [`discount`](@ref) and `objective_weight` can serve similar purposes. Compared to - [`discount`](@ref), `objective_weight` includes as well the probablity and multiplicity + [`discount`](@ref), `objective_weight` includes as well the probability and multiplicity of a given time period. If `t` is an [`AbstractStrategicPeriod`](@ref), both are equivalent. """ diff --git a/src/op_scenarios/core_types.jl b/src/op_scenarios/core_types.jl index 9e7d923..64f7573 100644 --- a/src/op_scenarios/core_types.jl +++ b/src/op_scenarios/core_types.jl @@ -13,11 +13,11 @@ and an associated probability. These scenarios are in general represented as !!! note - The `TimeStructure`s of all operational scenarios must use the same type for the - duration, *.i.e.*, either Integer or Float. + duration, *i.e.*, either Integer or Float. - If the `probability` is not specified, it assigns the same probability to each scenario. - It is possible that `sum(probability)` is larger or smaller than 1. This can lead to problems in your application. Hence, it is advised to scale it. Currently, a warning - will be given if the period shares do not sum to one as an automatic scaling will + will be given if the period shares do not sum to one, as an automatic scaling will correspond to a breaking change. ## Example @@ -55,8 +55,8 @@ struct OperationalScenarios{T,OP<:TimeStructure{T}} <: TimeStructure{T} ) elseif !isapprox(sum(probability), 1; atol = 1e-6) @warn( - "The sum of the probablity vector is given by $(sum(probability)). " * - "This can lead to unexpected behaviour." + "The sum of the probability vector is given by $(sum(probability)). " * + "This can lead to unexpected behavior." ) end return new{T,OP}(len, scenarios, convert(Vector{Float64}, probability)) diff --git a/src/op_scenarios/opscenarios.jl b/src/op_scenarios/opscenarios.jl index 0ea8d79..3a1da24 100644 --- a/src/op_scenarios/opscenarios.jl +++ b/src/op_scenarios/opscenarios.jl @@ -78,7 +78,7 @@ function Base.eachindex(osc::SingleScenario) return eachindex(osc.ts) end Base.last(osc::SingleScenario) = last(osc.ts) -function Base.last( # TODO: Considering removing the function as the the structure is opposite +function Base.last( # TODO: Considering removing the function as the structure is opposite osc::SingleScenario{T,RepresentativePeriod{T,OP}}, ) where {T,OP} period = last(osc.ts.operational) diff --git a/src/representative/core_types.jl b/src/representative/core_types.jl index 4e01fb2..0f11586 100644 --- a/src/representative/core_types.jl +++ b/src/representative/core_types.jl @@ -20,12 +20,12 @@ is attributed to it. !!! note - The `TimeStructure`s of all representative periods must use the same type for the - duration, *.i.e.*, either Integer or Float. + duration, *i.e.*, either Integer or Float. - If the field `period_share` is not specified, it assigns the same probability to each representative period. - It is possible that `sum(period_share)` is larger or smaller than 1. This can lead to problems in your application. Hence, it is advised to scale it. Currently, a warning - will be given if the period shares do not sum to one as an automatic scaling will + will be given if the period shares do not sum to one, as an automatic scaling will correspond to a breaking change. - If you include [`OperationalScenarios`](@ref) in your time structure, it is important that the scenarios are within the representative periods, and not the other way. @@ -68,7 +68,7 @@ struct RepresentativePeriods{S<:Duration,T,OP<:TimeStructure{T}} <: TimeStructur elseif !isapprox(sum(period_share), 1; atol = 1e-6) @warn( "The sum of the `period_share` vector is given by $(sum(period_share)). " * - "This can lead to unexpected behaviour." + "This can lead to unexpected behavior." ) end return new{S,T,OP}( diff --git a/src/simple.jl b/src/simple.jl index f6c6c28..9db9b98 100644 --- a/src/simple.jl +++ b/src/simple.jl @@ -6,9 +6,9 @@ SimpleTimes(dur::Vector{T}) where {T<:Duration} A simple time structure consisting of consecutive time periods of varying duration. -`SimpleTimes` is always the lowest level in a `TimeStruct` time structure. if used. +`SimpleTimes` is always the lowest level in a `TimeStruct` time structure, if used. -An alternative for `SimpleTimes` is [`CalendarTimes`](@ref) +An alternative to `SimpleTimes` is [`CalendarTimes`](@ref) ## Example ```julia diff --git a/src/strat_scenarios/core_types.jl b/src/strat_scenarios/core_types.jl index 561d465..6f5bac7 100644 --- a/src/strat_scenarios/core_types.jl +++ b/src/strat_scenarios/core_types.jl @@ -5,19 +5,19 @@ TwoLevelTree(duration::S, branching::Vector, ts::OP; op_per_strat::Float64 = 1.0) where {S,T,OP<:TimeStructure{T}} Time structure allowing for a tree structure for the strategic level. For each strategic -node in the tree a separate time structure is used for operational decisions. Iterating the +node in the tree, a separate time structure is used for operational decisions. Iterating the structure will go through all operational periods. -The default approach for creating a `TwoLevelTree` is by providing the root `[TreeNode`](@ref) -with all its children nodes. In the case of a regular structure, that is all children nodes +The default approach for creating a `TwoLevelTree` is by providing the root [`TreeNode`](@ref) +with all its children nodes. In the case of a regular structure, that is, all children nodes have the same `duration`, time structure `ts`, probability, and children itself, you can use a simplified constructor with the `branching` vector. The vector `branching` specifies the number of branchings at each stage of the tree, excluding the first stage. The branches at each stage will all have equal probability, duration, and time structure. -!!! warning "Additional iteratores" - `TwoLevelTree` utilize a separate [`withprev`](@ref) method which is equivalent to the - existing method for the other time structures. [`withnext`](@ref), [`chunk](@ref) and +!!! warning "Additional iterators" + `TwoLevelTree` utilizes a separate [`withprev`](@ref) method which is equivalent to the + existing method for the other time structures. [`withnext`](@ref), [`chunk`](@ref), and [`chunk_duration`](@ref) are not implemented and will result in an error when used. ## Example @@ -26,8 +26,8 @@ each stage will all have equal probability, duration, and time structure. # Declare the individual time structure day = SimpleTimes(24, 1) -# Regular tree with 3 strategic periods of duration 5, 3 branches for the second strategic -# period, and 6 branchs in the thirdand forth strategic period +# Regular tree with 4 strategic periods of duration 5, 3 branches for the second strategic +# period, and 6 branches in the third and fourth strategic periods regtree_1 = TwoLevelTree(5, [3, 2, 1], day) # Equivalent structure using `TreeNode` and the different constructors @@ -279,7 +279,7 @@ Type for iterating through the individual strategic nodes of a [`TwoLevelTree`]( It is automatically created through the function [`strat_periods`](@ref), and hence, [`strategic_periods`](@ref). -Iterating through `StratTreeNodes` using the `WithPrev` iterator changes the behaviour, +Iterating through `StratTreeNodes` using the `WithPrev` iterator changes the behavior, although the meaning remains unchanged. """ struct StratTreeNodes{S,T,OP<:TimeStructure{T}} <: AbstractStratPers{T} diff --git a/src/strat_scenarios/tree_periods.jl b/src/strat_scenarios/tree_periods.jl index a91f149..3b0024a 100644 --- a/src/strat_scenarios/tree_periods.jl +++ b/src/strat_scenarios/tree_periods.jl @@ -157,8 +157,8 @@ struct TreeNode{S,T,OP<:TimeStructure{T},COP} ) elseif !isapprox(sum(probability), 1; atol = 1e-6) @warn( - "The sum of the probablity vector is given by $(sum(probability)). " * - "This can lead to unexpected behaviour." + "The sum of the probability vector is given by $(sum(probability)). " * + "This can lead to unexpected behavior." ) end return new{S,T,OP,COP}(duration, ts, probability, children) diff --git a/src/strategic/core_types.jl b/src/strategic/core_types.jl index 2f1ad22..c413856 100644 --- a/src/strategic/core_types.jl +++ b/src/strategic/core_types.jl @@ -13,10 +13,10 @@ A time structure with two levels of time periods. On the top level it has a sequence of strategic periods of varying duration. -For each strategic period a separate time structure is used for +For each strategic period, a separate time structure is used for operational decisions. Iterating the structure will go through all operational periods. It is possible to use different time units for the two levels by providing the number -of operational time units per strategic time unit through the kewyord argument `op_per_strat`. +of operational time units per strategic time unit through the keyword argument `op_per_strat`. Potential time structures are [`SimpleTimes`](@ref), [`CalendarTimes`](@ref), [`OperationalScenarios`](@ref), or [`RepresentativePeriods`](@ref), as well as combinations @@ -24,7 +24,7 @@ of these. !!! note - The `TimeStructure`s of all strategic periods must use the same type for the - duration, *.i.e.*, either Integer or Float. + duration, *i.e.*, either Integer or Float. !!! danger "Usage of op_per_strat" The optional keyword `op_per_strat` is important for the overall calculations. diff --git a/src/structures.jl b/src/structures.jl index dc7b0da..db615e4 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -72,7 +72,7 @@ duration(t::TimePeriod) = error("duration() not implemented for $(typeof(t))") Returns true if the time period is the first in a sequence and has no previous time period """ -isfirst(t::TimePeriod) = error("isfirst() not implemented for$(typeof(t))") +isfirst(t::TimePeriod) = error("isfirst() not implemented for $(typeof(t))") """ multiple(t::TimePeriod) diff --git a/test/runtests.jl b/test/runtests.jl index 9771742..5418619 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -162,8 +162,8 @@ end @test_throws ArgumentError OperationalScenarios(2, [day, week], [1.0]) @test_throws ArgumentError OperationalScenarios(2, [day], [0.5, 0.5]) msg = - "The sum of the probablity vector is given by $(2.0). " * - "This can lead to unexpected behaviour." + "The sum of the probability vector is given by $(2.0). " * + "This can lead to unexpected behavior." @test_logs (:warn, msg) OperationalScenarios([day, day], [0.5, 1.5]) end @@ -224,7 +224,7 @@ end @test_throws ArgumentError RepresentativePeriods(2, 8760, [0.5, 0.5], [day]) msg = "The sum of the `period_share` vector is given by $(2.0). " * - "This can lead to unexpected behaviour." + "This can lead to unexpected behavior." @test_logs (:warn, msg) RepresentativePeriods(8760, [0.5, 1.5], [day, day]) # Testing of the external constructors providing the same case @@ -965,8 +965,8 @@ end @test_throws ArgumentError TreeNode(25, day, [0.3], [leaf_node, lin_node]) msg = - "The sum of the probablity vector is given by 0.8. " * - "This can lead to unexpected behaviour." + "The sum of the probability vector is given by 0.8. " * + "This can lead to unexpected behavior." @test_logs (:warn, msg) TreeNode(25, day, [0.5, 0.3], lin_node) end