Skip to content

Commit 195bceb

Browse files
authored
Merge pull request #292 from control-toolbox/grids
feat: add default component for time_grid() with MultipleTimeGridModel
2 parents e39c430 + 15e33ce commit 195bceb

8 files changed

Lines changed: 106 additions & 52 deletions

File tree

BREAKING.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,38 @@
44

55
This document describes breaking changes in CTModels releases and how to migrate your code.
66

7+
## [0.9.12-beta] - 2026-04-03
8+
9+
### No Breaking Changes
10+
11+
This release introduces enhancements without breaking existing functionality:
12+
13+
#### New Default Behavior (Non-Breaking)
14+
15+
- **Enhanced `time_grid()` function**: `time_grid(sol)` now works for `MultipleTimeGridModel` solutions without explicit component
16+
- **Default component**: Automatically uses `:state` when no component specified
17+
- **Full backward compatibility**: All existing code continues to work unchanged
18+
19+
#### What Changed
20+
21+
```julia
22+
# These still work exactly as before
23+
time_grid(sol_unified) # UnifiedTimeGridModel (unchanged)
24+
time_grid(sol_multi, :state) # MultipleTimeGridModel with explicit component
25+
time_grid(sol_multi, :control) # MultipleTimeGridModel with explicit component
26+
27+
# This now works (previously threw IncorrectArgument)
28+
time_grid(sol_multi) # MultipleTimeGridModel without component (NEW)
29+
```
30+
31+
#### Migration
32+
33+
- **No action required**: Existing code continues to work
34+
- **Optional simplification**: Can use `time_grid(sol)` instead of `time_grid(sol, :state)` for state grid access
35+
- **Explicit still preferred**: Use explicit component specification when accessing non-state grids
36+
37+
---
38+
739
## [0.9.11] - 2026-03-31
840

941
### No Breaking Changes

CHANGELOG.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,58 @@ All notable changes to this project will be documented in this file.
77
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
88
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
99

10+
## [0.9.12-beta] - 2026-04-03
11+
12+
### 🚀 Enhancements
13+
14+
#### Default Component for Multiple Time Grid Solutions
15+
16+
- **Default time grid access**: `time_grid(sol)` now works for `MultipleTimeGridModel` without explicit component specification
17+
- **Sensible default**: When no component is specified, defaults to `:state` grid (most commonly used)
18+
- **Consistent API**: Same `time_grid(sol)` syntax works for both unified and multiple time grid solutions
19+
- **Backward compatibility**: All existing code with explicit component specification continues to work unchanged
20+
21+
#### Improved Developer Experience
22+
23+
- **Reduced verbosity**: No need to specify `:state` component for most common use case
24+
- **Intuitive behavior**: State trajectory is the natural default for optimal control problems
25+
- **Cleaner code**: Simplified access to time grids in multi-grid solutions
26+
27+
### 📊 API Changes
28+
29+
```julia
30+
# Before (required component specification)
31+
time_grid(sol_multi, :state) # Required for MultipleTimeGridModel
32+
time_grid(sol_unified) # Worked for UnifiedTimeGridModel
33+
34+
# After (consistent behavior)
35+
time_grid(sol_multi) # Now works! Defaults to :state
36+
time_grid(sol_multi, :state) # Still works (explicit)
37+
time_grid(sol_unified) # Still works (unchanged)
38+
```
39+
40+
### 🔧 Internal Changes
41+
42+
- **Default function**: Added `__time_grid_default_component()::Symbol = :state` in `defaults.jl`
43+
- **Method signature**: Updated `time_grid` method for `MultipleTimeGridModel` with default parameter
44+
- **Removed exception**: Eliminated method that threw `IncorrectArgument` for missing component
45+
- **Enhanced tests**: Updated test suites to verify new default behavior
46+
47+
### 🧪 Testing
48+
49+
- **Comprehensive coverage**: All existing tests pass with new behavior
50+
- **Default behavior tests**: Added tests for automatic component selection
51+
- **Compatibility verification**: Confirmed backward compatibility with explicit specifications
52+
- **Integration testing**: End-to-end testing of multi-grid workflows
53+
54+
### 📝 Migration Notes
55+
56+
- **No breaking changes**: Existing code continues to work unchanged
57+
- **Optional enhancement**: Can adopt new shorter syntax when convenient
58+
- **Explicit still supported**: `time_grid(sol, :component)` syntax remains fully functional
59+
60+
---
61+
1062
## [0.9.11] - 2026-03-31
1163

1264
### 🔧 Internal Improvements

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "CTModels"
22
uuid = "34c4fa32-2049-4079-8329-de33c2a22e2d"
3-
version = "0.9.11"
3+
version = "0.9.12-beta"
44
authors = ["Olivier Cots <olivier.cots@toulouse-inp.fr>"]
55

66
[deps]

src/OCP/Building/solution.jl

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ function time_grid(
985985
<:AbstractDualModel,
986986
<:AbstractSolverInfos,
987987
},
988-
component::Symbol,
988+
component::Symbol = __time_grid_default_component(),
989989
)::TimesDisc
990990
# Clean and validate component symbol
991991
component_clean = clean_component_symbols((component,))[1]
@@ -1011,50 +1011,6 @@ end
10111011
"""
10121012
$(TYPEDSIGNATURES)
10131013
1014-
Return the time grid for solutions with multiple time grids (component must be specified).
1015-
1016-
# Throws
1017-
- `IncorrectArgument`: Always thrown for MultipleTimeGridModel without component specification
1018-
1019-
# Notes
1020-
This method enforces explicit component specification for solutions with multiple time grids
1021-
to avoid ambiguity about which grid is being accessed.
1022-
1023-
# Examples
1024-
```julia-repl
1025-
julia> time_grid(sol) # ❌ Error for MultipleTimeGridModel
1026-
julia> time_grid(sol, :state) # ✅ Correct usage
1027-
```
1028-
"""
1029-
function time_grid(
1030-
sol::Solution{
1031-
<:MultipleTimeGridModel,
1032-
<:AbstractTimesModel,
1033-
<:AbstractStateModel,
1034-
<:AbstractControlModel,
1035-
<:AbstractVariableModel,
1036-
<:AbstractModel,
1037-
<:Function,
1038-
<:ctNumber,
1039-
<:AbstractDualModel,
1040-
<:AbstractSolverInfos,
1041-
},
1042-
)
1043-
# ⚠️ Applying Exception Rule: Missing component specification
1044-
throw(
1045-
CTBase.Exceptions.IncorrectArgument(
1046-
"Component must be specified for solutions with multiple time grids";
1047-
got="no component specified",
1048-
expected="time_grid(sol, :component) where component ∈ {:state, :control, :path}",
1049-
suggestion="Specify which time grid to access, e.g., time_grid(sol, :state)",
1050-
context="time_grid for MultipleTimeGridModel",
1051-
),
1052-
)
1053-
end
1054-
1055-
"""
1056-
$(TYPEDSIGNATURES)
1057-
10581014
Return the objective value.
10591015
10601016
"""

src/OCP/Core/defaults.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,13 @@ The default value is `:constant` for piecewise constant interpolation (direct me
112112
The other possible value is `:linear` for piecewise linear interpolation (indirect methods).
113113
"""
114114
__control_interpolation()::Symbol = :constant
115+
116+
"""
117+
$(TYPEDSIGNATURES)
118+
119+
Return the default component for time grid access in multiple time grid solutions.
120+
121+
The default value is `:state` since the state trajectory is typically the most
122+
commonly accessed component in optimal control problems.
123+
"""
124+
__time_grid_default_component()::Symbol = :state

test/suite/ocp/test_defaults.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ function test_defaults()
5252
Test.@testset "time and criterion defaults" begin
5353
Test.@test CTModels.OCP.__time_name() == "t"
5454
Test.@test CTModels.OCP.__criterion_type() == :min
55+
Test.@test CTModels.OCP.__time_grid_default_component() == :state
5556
end
5657

5758
Test.@testset "variable naming defaults" begin

test/suite/ocp/test_solution_multi_grids.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,8 +356,8 @@ function test_solution_multi_grids()
356356
successful=true,
357357
)
358358

359-
# Should require component specification
360-
Test.@test_throws Exceptions.IncorrectArgument CTModels.time_grid(sol)
359+
# Should work with default component (state)
360+
Test.@test CTModels.time_grid(sol) == T_state
361361

362362
# Should work with component specification
363363
Test.@test CTModels.time_grid(sol, :state) == T_state
@@ -605,8 +605,10 @@ function test_solution_multi_grids()
605605
)
606606
end
607607

608-
Test.@testset "Missing component specification" begin
609-
Test.@test_throws Exceptions.IncorrectArgument CTModels.time_grid(sol)
608+
Test.@testset "Default component specification" begin
609+
# Should default to state grid
610+
Test.@test CTModels.time_grid(sol) == T_state
611+
Test.@test CTModels.time_grid(sol) == CTModels.time_grid(sol, :state)
610612
end
611613
end
612614

test/suite/serialization/test_multi_grids.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,9 @@ function test_multi_grids()
234234
successful=CTModels.successful(sol_unified),
235235
)
236236

237-
# time_grid without component should throw error
238-
Test.@test_throws Exceptions.IncorrectArgument CTModels.time_grid(sol_multi)
237+
# time_grid without component should return state grid (default behavior)
238+
Test.@test CTModels.time_grid(sol_multi) == T_state
239+
Test.@test CTModels.time_grid(sol_multi) == CTModels.time_grid(sol_multi, :state)
239240

240241
# Invalid component should throw error
241242
Test.@test_throws Exceptions.IncorrectArgument CTModels.time_grid(

0 commit comments

Comments
 (0)