Prepared by: Prof. Alexander Dowling, Molly Dougher (mdoughe6@nd.edu, 2023)
# Install Pyomo and solvers for Google Colab
import sys
if "google.colab" in sys.modules:
!wget "https://raw.githubusercontent.com/ndcbe/optimization/main/notebooks/helper.py"
import helper
helper.easy_install()
else:
sys.path.insert(0, "../")
import helper
helper.set_plotting_style()
import numpy as np
import matplotlib.pyplot as pltGuide to the transcription¶
Use this page for the full derivation behind Lecture 7: polynomial coordinates, integrated derivative basis and Runge-Kutta equations, continuity and counting, time scaling and implementation, and sparse structure.
The existing quadrature implementation below remains useful for experiments. The orthogonality proof is taught in the numerical-integration lecture and developed in Numeric Integration for DAEs. For exact coefficient construction, see Collocation Points and Runge-Kutta Coefficients; for a solved model, see Pyomo.DAE Example: Race Car.
Primary sources: Biegler (2010), Chapter 8, p. 216, and Chapter 10, pp. 289–298, with the Chapter 10 errata; Ascher & Petzold (1998), Section 4.7, pp. 101–104.
We now turn a DAE-constrained dynamic optimization problem into a sparse nonlinear program. We approximate state and control profiles with piecewise polynomials and enforce the DAE at collocation points. This simultaneous, or direct-transcription, approach is closely related to the implicit Runge--Kutta methods developed in the numerical-integration lecture.
Quadrature Methods¶
We begin with the quadrature ideas needed to derive collocation. The next notebooks apply the method to dynamic optimization examples. For more detail, see:
Chapter 9 in Biegler (2010)
Chapter 16, Gauss Quadrature and Multi-dimensional Integrals, in McClarren (2018)
Main Idea¶
The quadrature rule approximates the integral with weighted sum of function evaluations:
where:
are nodes (also known as abscissas)
are weights
number of nodes and weights
Central questions:
How to choose nodes and weights?
How does this choice impact approximation error?
How does impact approximation error?
Polynomial degree and coefficient count¶
A polynomial has coefficients and degree at most , with equality when . Some older texts call that polynomial “order .” Here degree denotes the highest power; order denotes the rate at which a numerical error decreases.
Gauss-Legendre quadrature¶
With the Gauss-Legendre nodes and weights, the -point rule is exact for every polynomial of degree at most . No -node rule with real nodes can be exact through degree : the squared node polynomial is zero at all nodes but has a positive integral. This is optimal polynomial exactness, rather than a claim of minimum error for every nonpolynomial integrand.
The table gives nodes and weights for .
| 1 | 0 | 2.0 |
| 2 | ±0.5773502691896257645091488 | 1.0 |
| 3 | 0 | 0.8888888888888888888888889 |
| ±0.7745966692414833770358531 | 0.5555555555555555555555556 | |
| 4 | ±0.3399810435848562648026658 | 0.6521451548625461426269361 |
| ±0.8611363115940525752239465 | 0.3478548451374538573730639 | |
| 5 | 0 | 0.5688888888888888888888889 |
| ±0.5384693101056830910363144 | 0.4786286704993664680412915 | |
| ±0.9061798459386639927976269 | 0.2369268850561890875142640 | |
| 6 | ±0.2386191860831969086305017 | 0.4679139345726910473898703 |
| ±0.6612093864662645136613996 | 0.3607615730481386075698335 | |
| ±0.9324695142031520278123016 | 0.1713244923791703450402961 | |
| 7 | 0 | 0.4179591836734693877551020 |
| ±0.4058451513773971669066064 | 0.3818300505051189449503698 | |
| ±0.7415311855993944398638648 | 0.2797053914892766679014678 | |
| ±0.9491079123427585245261897 | 0.1294849661688696932706114 | |
| 8 | ±0.1834346424956498049394761 | 0.3626837833783619829651504 |
| ±0.5255324099163289858177390 | 0.3137066458778872873379622 | |
| ±0.7966664774136267395915539 | 0.2223810344533744705443560 | |
| ±0.9602898564975362316835609 | 0.1012285362903762591525314 |
Why ?¶
Let
Exactness through degree imposes moment equations (one for each monomial ). These match the unknown node and weight values in an -point rule. This count motivates the attainable degree; orthogonal-polynomial theory supplies the nodes and proves exactness.
How to determine the nodes and weights?¶
One way to derive the Gauss-Legendre quadrature rules is by looking at the integral generic monomials of degree 0 up to and setting each equal to the point Gauss-Legendre quadrature rule:
Notice that the constants cancel out of each equation so they do not matter. This system is equations with weights, , and nodes, . We could solve these equations to get the weights and nodes, though this is not how it is done in practice generally---this is accomplished by using the theory of orthogonal polynomials.
Code¶
def GLQuad(f, L=8, dataReturn=False):
"""Compute the Gauss-Legendre Quadrature estimate
of the integral of f(x) from -1 to 1
Inputs:
f: name of function to integrate
L: Number of quadrature nodes. Tabulated here for L <= 8;
larger L falls through to numpy.polynomial.legendre.leggauss.
dataReturn: determines whether weights and xs are returned
Returns:
G-L Quadrature estimate"""
# Set bound on L
assert L >= 1
# Enumerate over L to define weights and x in each scenario
if L == 1:
weights = np.ones(1) * 2
xs = np.array([0])
elif L == 2:
weights = np.ones(2)
xs = np.array([-np.sqrt(1.0 / 3.0), np.sqrt(1.0 / 3.0)])
elif L == 3:
weights = np.array(
[
0.8888888888888888888888889,
0.5555555555555555555555556,
0.5555555555555555555555556,
]
)
xs = np.array([0.0, -0.7745966692414833770358531, 0.7745966692414833770358531])
elif L == 4:
weights = np.array(
[
0.6521451548625461426269361,
0.6521451548625461426269361,
0.3478548451374538573730639,
0.3478548451374538573730639,
]
)
xs = np.array(
[
-0.3399810435848562648026658,
0.3399810435848562648026658,
-0.8611363115940525752239465,
0.8611363115940525752239465,
]
)
elif L == 5:
weights = np.array(
[
0.5688888888888888888888889,
0.4786286704993664680412915,
0.4786286704993664680412915,
0.2369268850561890875142640,
0.2369268850561890875142640,
]
)
xs = np.array(
[
0.0,
-0.5384693101056830910363144,
0.5384693101056830910363144,
-0.9061798459386639927976269,
0.9061798459386639927976269,
]
)
elif L == 6:
weights = np.array(
[
0.4679139345726910473898703,
0.4679139345726910473898703,
0.3607615730481386075698335,
0.3607615730481386075698335,
0.1713244923791703450402961,
0.1713244923791703450402961,
]
)
xs = np.array(
[
-0.2386191860831969086305017,
0.2386191860831969086305017,
-0.6612093864662645136613996,
0.6612093864662645136613996,
-0.9324695142031520278123016,
0.9324695142031520278123016,
]
)
elif L == 7:
weights = np.array(
[
0.4179591836734693877551020,
0.3818300505051189449503698,
0.3818300505051189449503698,
0.2797053914892766679014678,
0.2797053914892766679014678,
0.1294849661688696932706114,
0.1294849661688696932706114,
]
)
xs = np.array(
[
0.0,
-0.4058451513773971669066064,
0.4058451513773971669066064,
-0.7415311855993944398638648,
0.7415311855993944398638648,
-0.9491079123427585245261897,
0.9491079123427585245261897,
]
)
elif L == 8:
weights = np.array(
[
0.3626837833783619829651504,
0.3626837833783619829651504,
0.3137066458778872873379622,
0.3137066458778872873379622,
0.2223810344533744705443560,
0.2223810344533744705443560,
0.1012285362903762591525314,
0.1012285362903762591525314,
]
)
xs = np.array(
[
-0.1834346424956498049394761,
0.1834346424956498049394761,
-0.5255324099163289858177390,
0.5255324099163289858177390,
-0.7966664774136267395915539,
0.7966664774136267395915539,
-0.9602898564975362316835609,
0.9602898564975362316835609,
]
)
else: # use numpy's function
xs, weights = np.polynomial.legendre.leggauss(L)
# Calculate the quadrature estimate
quad_estimate = np.sum(weights * f(xs))
if dataReturn:
return quad_estimate, weights, xs
else:
return quad_estimateVisualize Weights¶
# Establish L
L = np.arange(1, 15)
# Define a lambda function
f = lambda x: x
# Define the figure
plt.figure(figsize=(4, 4))
# Loop over L
for l in L:
# Calculate quadrature estimate
quad_est, weights, xs = GLQuad(f, l, dataReturn=True)
levels = weights * 0 + l
plt.scatter(xs, levels, s=weights * 100)
# Format the plot
plt.xlabel("x", fontsize=16, fontweight="bold")
plt.ylabel("L", fontsize=16, fontweight="bold")
plt.tick_params(direction="in", labelsize=15)
plt.title(
"Gauss-Legendre Quadrature Points\nSize of Point is Weight",
fontsize=16,
fontweight="bold",
)
plt.show()
Polynomial Example¶
As a simple demonstration of the Gauss-Legendre quadrature, let’s show that it integrates polynomials of degree exactly. Consider the integral
We can use the GLQuad function to evaluate the error.
L = np.arange(1, 12)
for l in L:
# Create f
f = lambda x: (x + 1) ** (2 * l - 1)
# Evaluate exact (analytic) solution
integral = 2 ** (2 * l - 1) / l
# Evaluate quadrature rule
GLintegral = GLQuad(f, l)
# Print results
print(
"L =",
l,
"\t Estimate is",
GLintegral,
"Exact value is",
integral,
"\nAbs. Relative Error is",
np.abs(GLintegral - integral) / integral,
)L = 1 Estimate is 2.0 Exact value is 2.0
Abs. Relative Error is 0.0
L = 2 Estimate is 3.9999999999999996 Exact value is 4.0
Abs. Relative Error is 1.1102230246251565e-16
L = 3 Estimate is 10.666666666666668 Exact value is 10.666666666666666
Abs. Relative Error is 1.6653345369377348e-16
L = 4 Estimate is 31.99999999999999 Exact value is 32.0
Abs. Relative Error is 3.3306690738754696e-16
L = 5 Estimate is 102.39999999999995 Exact value is 102.4
Abs. Relative Error is 5.551115123125783e-16
L = 6 Estimate is 341.33333333333337 Exact value is 341.3333333333333
Abs. Relative Error is 1.6653345369377348e-16
L = 7 Estimate is 1170.2857142857135 Exact value is 1170.2857142857142
Abs. Relative Error is 5.828670879282072e-16
L = 8 Estimate is 4096.000000000003 Exact value is 4096.0
Abs. Relative Error is 6.661338147750939e-16
L = 9 Estimate is 14563.555555555482 Exact value is 14563.555555555555
Abs. Relative Error is 4.9960036108132044e-15
L = 10 Estimate is 52428.79999999968 Exact value is 52428.8
Abs. Relative Error is 6.106226635438361e-15
L = 11 Estimate is 190650.18181818203 Exact value is 190650.18181818182
Abs. Relative Error is 1.0685896612017132e-15
Generalization¶
We are generally interested in integrals not just over the domain . We’ll now make a function that does Gauss-Legendre quadrature over a general range using the formula
def generalGL(f, a, b, L):
"""Compute the Gauss-Legendre Quadrature estimate
of the integral of f(x) from a to b
Inputs:
f: name of function to integrate
a: lower bound of integral
b: upper bound of integral
L: Number of quadrature nodes (see GLQuad)
Returns:
G-L Quadrature estimate"""
# Set bound on L
assert L >= 1
# Define a re-scaled f
f_rescaled = lambda z: f(0.5 * (b - a) * z + 0.5 * (a + b))
# Define the integral
integral = GLQuad(f_rescaled, L)
# Calculate the G-L quadrature estimate
return integral * (b - a) * 0.5Let’s show that this version integrates polynomials of degree exactly. Consider the integral
L = np.arange(1, 12)
# Loop over L to calculate the integral and the G-L estimate
for l in L:
# Define a lambda function
f = lambda x: (x + 1) ** (2 * l - 1)
# Calculate the integral
integral = (9**l - 4**l) / (2 * l)
# Calculate the estimate
GLintegral = generalGL(f, -3, 2, l)
# Print statement for the error comparison
print(
"L =",
l,
"\t Estimate is",
GLintegral,
"Exact value is",
integral,
"\nAbs. Relative Error is",
np.abs(GLintegral - integral) / integral,
)L = 1 Estimate is 2.5 Exact value is 2.5
Abs. Relative Error is 0.0
L = 2 Estimate is 16.249999999999996 Exact value is 16.25
Abs. Relative Error is 2.1862853408003084e-16
L = 3 Estimate is 110.83333333333336 Exact value is 110.83333333333333
Abs. Relative Error is 2.5643647606379557e-16
L = 4 Estimate is 788.1249999999999 Exact value is 788.125
Abs. Relative Error is 1.4424975444455643e-16
L = 5 Estimate is 5802.500000000002 Exact value is 5802.5
Abs. Relative Error is 3.1348374037843283e-16
L = 6 Estimate is 43945.4166666667 Exact value is 43945.416666666664
Abs. Relative Error is 8.278403262589113e-16
L = 7 Estimate is 340470.35714285733 Exact value is 340470.35714285716
Abs. Relative Error is 5.128874777992276e-16
L = 8 Estimate is 2686324.0625 Exact value is 2686324.0625
Abs. Relative Error is 0.0
L = 9 Estimate is 21508796.944444306 Exact value is 21508796.944444444
Abs. Relative Error is 6.408342660870799e-15
L = 10 Estimate is 174286791.24999845 Exact value is 174286791.25
Abs. Relative Error is 8.891785505059933e-15
L = 11 Estimate is 1426221150.227277 Exact value is 1426221150.2272727
Abs. Relative Error is 3.0090245283098317e-15
Radau Quadrature¶
Sometimes we desire either:
The first node is at the beginning of the interval, i.e., or
The last node is at the end of the interval, i.e., .
This is known as Gauss-Radau quadrature. The remaining nodes and weights are chosen optimally; thus, Radau quadrature exactly integrates all polynomials of degree or less. Specifying one of the nodes leaves only degrees of freedom.
More info: https://
Differential Equations¶
Consider solving the differential equation
to determine . This can be expressed as an integral:
Thus, Runge-Kutta methods can be interpreted as a quadrature rule.
Collocation Methods¶
See the following resources for more information on collocation:
Chapter 10 in Biegler (2010)
What is a Collocation Method?¶
A collocation method approximates the state with a polynomial and enforces the differential and algebraic equations at selected collocation points. The resulting finite system of algebraic equations can be solved directly or embedded in an optimization problem.
Derivation of Collocation Methods¶
Derivations are followed from Section 10.2 of Biegler (2010).
Consider the following multiperiod dynamic optimization problem (8.5).
Each physical period may contain many finite elements. Period junctions use the coupling function ; finite-element boundaries within a period use state continuity. The decision variables include the nodal states, control coefficients and any free parameters. A running cost can be included by adding an accumulator state, , and minimizing plus terminal cost.
This source-controlled course schematic was re-authored from the mathematical layout of Biegler (2010), Figure 10.1; it is not a reproduction of the textbook artwork.
State coordinates on one element¶
The collocation method can be developed by solving the differential equation () at specific time points. For example, Figure 10.1 shows the state variable approximation for a single finite element:
The coefficients are vectors when the state is a vector. A power basis is mathematically valid, but nodal Lagrange coefficients are directly interpretable as state values. This profile can also be represented as a Lagrange interpolation polynomial. For Gauss-Legendre or right Radau, choose distinct interpolation points within element , the state can be represented as:
The key property of this polynomial is that where .
At , every with contains a zero numerator; . Thus . The basis also satisfies : both sides interpolate the constant polynomial at distinct nodes. These identities justify interpreting coefficients as nodal values.

Why change coordinates? Power coefficients are not nodal states, so componentwise bounds on those coefficients do not directly impose physical state bounds. Monomial Vandermonde systems can also become poorly conditioned as the degree grows or the time interval is badly scaled; nearly dependent columns make coefficients sensitive even when the represented profile changes little. Normalizing each element and using nodal coordinates makes the variables easier to interpret. It does not remove every conditioning problem, and nodal bounds still need the between-node check below.
Bounds are nodal. Imposing does not bound the whole polynomial. Lagrange basis functions can be negative between nodes. Check additional points, extrema of the interpolation polynomial, or the independently integrated trajectory. For a continuous path constraint, mesh refinement and a residual check are part of validation.
The counterexample below uses at the initial point and three right-Radau nodes. Every nodal value lies in , but .
radau = np.array([(4-np.sqrt(6))/10,(4+np.sqrt(6))/10,1.0])
nodes = np.r_[0.0,radau]
values = 4.2*nodes*(1-nodes)
assert np.all((values>=0)&(values<=1))
assert np.isclose(4.2*0.5*(1-0.5),1.05)
print("Nodal values:",values,"; value at tau=1/2:",1.05)Nodal values: [0. 0.55024286 0.96175714 0. ] ; value at tau=1/2: 1.05
Integrated derivative basis gives Runge-Kutta equations¶
Let distinct derivative nodes be . Define
The derivative polynomial has degree at most ; integrating it with the initial value gives
Here denotes the physical-time derivative, not the state. Differentiating cancels the factor , and gives . The factor arises from .

For a semi-explicit DAE, enforce
Evaluating the state at the stages and endpoint gives
These are the Runge-Kutta stage and update equations. For a state-only ODE they are standard RK; for a DAE the algebraic stage equations are additional. A degree- state polynomial is fixed by its initial value and its derivative at distinct points, so this derivative representation is equivalent to the state-coordinate equations when the state interpolation nodes are distinct.
Not every node set is implicit. For one node , and . The choices give forward Euler, implicit midpoint and backward Euler. Standard Gauss, Radau IIA and Lobatto IIIA are implicit families (the first Lobatto stage is an explicit evaluation). For Lobatto the derivative nodes include zero; do not append a duplicate zero to the -point state formula.
Partition of unity gives stage times. Since , . Hence and . For this reproduces and . With normalized local coordinate, , not 1. Always use for the endpoint update; the last row of equals only when the last node is 1.
Continuity and degree-of-freedom counting¶
The incoming value on element equals the endpoint reconstructed on element :
The state remains continuous when its derivative has a finite jump. Right Radau has , so its endpoint is the last stage. Gauss-Legendre has ; it needs the endpoint evaluation, which generally differs from the last stage.
Controls may be piecewise constant and jump at a boundary. Do not impose continuity unless the physical actuator or chosen approximation requires it. Algebraic variables require a separate check. If defines a unique regular local branch and its inputs are continuous, then is continuous. Index 1 alone does not ensure this if jumps. In , , switching from 1 to 2 makes jump while stays continuous. At a discontinuity, distinguish the left and right values at the shared time.

For the common degree- control and algebraic profile representations, use the -node derivative basis:
These profiles need not match across elements. A lower-order control parameterization can instead set all equal on an element. For a regular algebraic branch, an endpoint algebraic value may also be recovered by solving from the endpoint differential state and the appropriate left/right control; this does not justify equating both sides when the control jumps. Nodal bounds on either interpolated profile still need a between-node check.
For one differential state, fixed mesh and prescribed controls/parameters, an expanded derivative transcription has:
| Quantity | Count |
|---|---|
| stage states | |
| stage derivatives | |
| boundary states | |
| stage reconstruction equations | |
| stage ODE equations | |
| endpoint update equations |
There are variables and equations before fixing . The initial condition removes the remaining degree of freedom. Each regular algebraic stage variable adds a variable and its equation. Free controls, parameters, durations and terminal constraints change the count. Pyomo may eliminate boundary/stage duplicates or use a different derivative representation; its raw counts need not equal this pedagogical expanded formulation. A square count is necessary for a regular simulation, but does not prove independence or numerical accuracy.
A free final time on a fixed domain¶
Let and . Then
The transformed derivative is with respect to . The duration multiplies every physical-time right-hand side, and any explicit time argument must also be transformed. Minimize for minimum arrival time with fixed . In Biegler’s notation is this duration; the car code calls it tf because .
Normalization fixes the domain on which Pyomo creates points; it does not impose a positive duration. Add a physically meaningful lower bound, initialize within the bounds, and use units to check both sides. The car example uses tf >= 0.001 s. For moving normalized mesh fractions , require and use physical lengths ; do not multiply by the duration twice. Alternatively optimize physical with .
Pyomo implementation choices¶
| Task | Pyomo object or operation | Modeling consequence |
|---|---|---|
| Fixed normalized domain | dae.ContinuousSet(bounds=(0, 1)) | the set endpoints do not become time decision variables |
| Derivative w.r.t. that domain | dae.DerivativeVar(z, wrt=sigma) | write the scaled differential equation explicitly |
| Finite differences | dae.finite_difference, nfe, scheme | inspect which endpoint derivative rows the stencil omits |
| Orthogonal collocation | dae.collocation, nfe, ncp, scheme="LAGRANGE-RADAU" or "LAGRANGE-LEGENDRE" | more stages per element or more elements are distinct refinements |
| One control per element | transformation’s reduce_collocation_points(..., var=u, ncp=1, contset=sigma) | ties within-element control values; it does not make element lengths variables |
| Explicit mesh locations | ContinuousSet(initialize=[...]) | fixed locations; an NLP cannot move members of the set |
| Moving element lengths | explicit collocation equations with variables | couple stage equations, physical times and the sum-of-lengths constraint |
Transformation scheme reference¶
| Transformation | scheme | Mesh arguments | Interpretation |
|---|---|---|---|
dae.finite_difference | BACKWARD | nfe | backward/implicit Euler in time |
dae.finite_difference | FORWARD | nfe | forward/explicit Euler in time |
dae.finite_difference | CENTRAL | nfe | central finite differences; inspect endpoint coverage |
dae.collocation | LAGRANGE-RADAU | nfe, ncp | right Radau IIA (default) |
dae.collocation | LAGRANGE-LEGENDRE | nfe, ncp | Gauss-Legendre |
Here nfe is the number of finite elements and ncp the stages per element. The finite-difference transformation has no ncp option, and there is no Pyomo Lobatto scheme. These are spatial derivative formulas too; Euler interpretations refer to discretizing time, not to a time integrator created merely by discretizing space.
The official Pyomo.DAE documentation describes these transformations. Read the generated constraints when a derivative appears at an endpoint: declaration alone does not create its defining equation. For boundary-value problems, use the heat-conduction stencil and diagnostics examples. For initialization of a nonlinear process model, see Pyomo.DAE Example: Temperature Control Lab; use a simulated or coarse solution as a starting profile and check structural diagnostics before relying on a solver message.
Node families and endpoint accuracy¶
For Gauss-Legendre, the nodes are the roots of the shifted degree- Legendre polynomial. The corresponding quadrature integrates all degrees through . The orthogonality proof and its assumptions are in Numeric Integration for DAEs.
Right Radau adds the endpoint 1 to the roots of a degree- polynomial orthogonal with weight . Lobatto adds 0 and 1 to the roots of a degree- polynomial orthogonal with weight . These are free-node Jacobi constructions, with the fixed endpoints added afterward. Exact symbolic tables and verification are in Collocation Points and Runge-Kutta Coefficients.

For a sufficiently smooth ODE, the standard Gauss, Radau IIA and Lobatto IIIA endpoint orders are , and . An order- method has an unnormalized one-step defect , or normalized local truncation error . Stage errors and DAE algebraic-variable errors need not have that same order. Quadrature moment tests alone do not prove every nonlinear Runge-Kutta order condition.
Gauss and Radau IIA are A-stable and algebraically stable. Algebraic stability implies AN-stability; the converse applies for distinct abscissas. Radau IIA also has homogeneous stiff decay. A-stability bounds amplification for left-half-plane eigenvalues; it does not ensure accurate transient decay or convergence of the nonlinear stage solve. Gauss large-step oscillations can be reduced by refining the mesh until the transient is resolved. For a very stiff problem, that can require far more elements than a method with stiff decay.
See Biegler, Section 10.2.2, pp. 290–293; Ascher & Petzold, pp. 101–104 and Exercise 4.13, p. 119. See the numerical-integration guide for the formal stability definitions, rather than treating their names as interchangeable.
A practical choice. Start with right Radau for a stiff model when damping fast homogeneous transients matters. For a smooth nonstiff problem, compare Gauss-Legendre: it has one higher endpoint order at the same stage count. That is a reason to measure error and work, not a universal speed ranking. Gauss oscillations can be resolved by sufficiently fine elements. Lobatto is useful for understanding endpoint choices but is not supplied by Pyomo.DAE.
Local equations and sparse structure¶
Order the variables by element. A collocation row involves stages in its own element, the incoming boundary state, that element’s control coefficients, and any shared parameters. Continuity connects neighboring elements. The Jacobian therefore has local blocks and a border for shared parameters or a free final time. It need not be a plain banded matrix. A stage derivative can couple every state component, so a block can itself be dense. Sparse NLP solvers exploit the incidence pattern and compute derivatives of the transcribed equations.

A simultaneous NLP iterate can violate the dynamic equations. At a converged point it satisfies the discrete residual tolerance, which still leaves a discretization error. In a sequential architecture, successful integration supplies a dynamic trajectory for a trial control; it does not automatically enforce path constraints, state bounds or terminal conditions. The architecture comparison is taught in Differential Algebraic Equations (DAEs).
Difficult cases and further reading¶
Free element lengths add products of with stage derivatives. They let a discontinuity align with an element boundary, but require positive lengths and sensible movement bounds. Inactive pieces can make many meshes nearly equivalent, leaving weak curvature in the mesh variables. See the executed moving-element car example, and Biegler, pp. 296–298.
Scale both variables and residuals. A typical-state scale changes ; a residual scale changes the nonlinear solver’s numerical balance. Preserve units before choosing either. A duration bound is a physical domain constraint, not a substitute for scaling.
An index-1 semi-explicit DAE has locally nonsingular . Higher-index DAEs and active path constraints can require differentiated consistency conditions or index reduction; general smooth-ODE order claims cannot be transferred unchanged. Biegler, Section 10.4.5, pp. 317–322, develops high-index path constraints.
Spatial semi-discretization can generate a stiff ODE or DAE. The discretization, boundary equations and constraint index determine which integrator assumptions apply; the fact that the result is finite-dimensional is insufficient. See the heat-conduction guide.
Sources¶
Biegler, Nonlinear Programming (2010), single/multiperiod formulation p. 216; interpolation and integrated derivative representation pp. 289–290, corrected by errata2.pdf; nodes, continuity and RK coefficients pp. 291–293; state/control/algebraic profiles and free-element lengths pp. 296–298; sparse NLP considerations pp. 302–304. Ascher & Petzold, Computer Methods for Ordinary Differential Equations and Differential-Algebraic Equations (1998), Section 4.7, pp. 101–104, and method of lines pp. 12–13. The source books and their errata, rather than an unchecked transcription, determine the formulas.
Additional topic map¶
| Question | Biegler (2010) locator or course guide |
|---|---|
| Choosing or optimizing element lengths | Section 10.3.1, pp. 296–298; moving-element car experiment |
| Unstable dynamic systems | Section 10.3.2, pp. 299–302 |
| Scaling and large sparse NLPs | Section 10.3.3, pp. 302–304 |
| Parameter estimation and parallel computation | Section 10.3.4, pp. 304–309 |
| Discretize then optimize versus optimize then discretize | Sections 10.4.1–10.4.2, pp. 312–314 |
| Singular optimal-control problems | Section 10.4.4, pp. 315–317 |
| High-index active path constraints | Section 10.4.5, pp. 317–322 |
| FD, FV, FE and spatial refinement | heat-conduction guide; adaptive spatial meshes are an extension |