Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify controls and polynomial controls #1078

Merged
merged 18 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions benchmark/benchmark_balanced_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ def _run_balanced_field_length_problem(tx=dm.GaussLobatto, timeseries=True, sim=
rotate.set_time_options(fix_initial=False, duration_bounds=(1.0, 5), duration_ref=1.0)
rotate.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0)
rotate.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0)
rotate.add_polynomial_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10,
ref=10, val=[0, 10])
rotate.add_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10,
ref=10, val=[0, 10], control_type='polynomial')

if timeseries:
rotate.add_timeseries_output('*')
Expand Down Expand Up @@ -229,7 +229,7 @@ def _run_balanced_field_length_problem(tx=dm.GaussLobatto, timeseries=True, sim=
p.set_val('traj.rotate.t_duration', 5)
p.set_val('traj.rotate.states:r', rotate.interp('r', [1750, 1800.0]))
p.set_val('traj.rotate.states:v', rotate.interp('v', [80, 85.0]))
p.set_val('traj.rotate.polynomial_controls:alpha', 0.0, units='deg')
p.set_val('traj.rotate.controls:alpha', 0.0, units='deg')

p.set_val('traj.climb.t_initial', 75)
p.set_val('traj.climb.t_duration', 15)
Expand Down
5 changes: 3 additions & 2 deletions docs/dymos_book/examples/balanced_field/balanced_field.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,8 @@
"rotate.set_time_options(fix_initial=False, duration_bounds=(1.0, 5), duration_ref=1.0)\n",
"rotate.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0)\n",
"rotate.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0)\n",
"rotate.add_polynomial_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10, val=[0, 10])\n",
"rotate.add_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10,\n",
" ref=10, val=[0, 10], control_type='polynomial')\n",
"rotate.add_timeseries_output('*')\n",
"\n",
"# Fifth Phase: Climb to target speed and altitude at end of runway.\n",
Expand Down Expand Up @@ -687,7 +688,7 @@
"rotate.set_time_val(initial=35.0, duration=5.0)\n",
"rotate.set_state_val('r', [1750, 1800.0])\n",
"rotate.set_state_val('v', [80, 85.0])\n",
"rotate.set_polynomial_control_val('alpha', 0.0, units='deg')\n",
"rotate.set_control_val('alpha', 0.0, units='deg')\n",
"\n",
"climb.set_time_val(initial=30.0, duration=20.0)\n",
"climb.set_state_val('r', [5000, 5500.0], units='ft')\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,19 +248,19 @@
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"active-ipynb",
"remove-input",
"remove-output"
]
},
"outputs": [],
"source": [
"%matplotlib inline"
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"active-ipynb",
"remove-input",
"remove-output"
]
},
"outputs": [],
"source": [
"%matplotlib inline"
]
},
{
"cell_type": "code",
Expand Down Expand Up @@ -328,7 +328,9 @@
"rotate.set_time_options(fix_initial=False, duration_bounds=(1.0, 5), duration_ref=1.0)\n",
"rotate.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0)\n",
"rotate.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0)\n",
"rotate.add_polynomial_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10, val=[0, 10])\n",
"rotate.add_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10,\n",
"ref=10,\n",
" val=[0, 10], control_type='polynomial')\n",
"rotate.add_timeseries_output('*')\n",
"\n",
"# Fifth Phase: Climb to target speed and altitude at end of runway.\n",
Expand Down Expand Up @@ -437,7 +439,7 @@
"rotate.set_time_val(initial=35.0, duration=5.0)\n",
"rotate.set_state_val('r', [1750, 1800.0])\n",
"rotate.set_state_val('v', [80, 85.0])\n",
"rotate.set_polynomial_control_val('alpha', 0.0, units='deg')\n",
"rotate.set_control_val('alpha', 0.0, units='deg')\n",
"\n",
"climb.set_time_val(initial=30.0, duration=20.0)\n",
"climb.set_state_val('r', [5000, 5500.0], units='ft')\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,19 @@
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"active-ipynb",
"remove-input",
"remove-output"
]
},
"outputs": [],
"source": [
"%matplotlib inline"
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"active-ipynb",
"remove-input",
"remove-output"
]
},
"outputs": [],
"source": [
"%matplotlib inline"
]
},
{
"cell_type": "code",
Expand Down Expand Up @@ -286,8 +286,8 @@
"#\n",
"# The tangent of theta is modeled as a linear polynomial over the duration of the phase.\n",
"#\n",
"phase.add_polynomial_control('tan_theta', order=1, units=None, opt=True,\n",
" targets=['guidance.tan_theta'])\n",
"phase.add_control('tan_theta', order=1, units=None, opt=True,\n",
" targets=['guidance.tan_theta'], control_type='polynomial')\n",
"\n",
"#\n",
"# Parameters values for thrust and specific impulse are design parameters. They are\n",
Expand Down Expand Up @@ -339,7 +339,7 @@
"phase.set_state_val('vx', [0, 1627.0])\n",
"phase.set_state_val('vy', [1.0E-6, 0.0])\n",
"phase.set_state_val('m', 50000)\n",
"phase.set_polynomial_control_val('tan_theta', [[0.5 * np.pi], [0.0]])\n",
"phase.set_control_val('tan_theta', [[0.5 * np.pi], [0.0]])\n",
"\n",
"#\n",
"# Solve the problem.\n",
Expand Down
28 changes: 2 additions & 26 deletions docs/dymos_book/features/phases/variables.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -245,37 +245,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Polynomial Controls\n",
"### Polynomial Controls\n",
"\n",
"Sometimes it can be easier to optimize a problem by reducing the freedom in the controls.\n",
"For instance, one might want the control to be linearly or quadratically varying throughout a phase, rather than having a different value specified at each node.\n",
"In Dymos, this role is filled by the PolynomialControl.\n",
"Polynomial controls are specified at some limited number of points throughout a _phase_, and then have their values interpolated to each node in each segment.\n",
"\n",
"### Options for Polynomial Control Variables"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"om.show_options_table(\"dymos.phase.options.PolynomialControlOptionsDictionary\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Polynomial values are connected to the ODE using the `targets` argument.\n",
"The values of this argument obey the same rules as those for states.\n",
"\n",
"The polynomial control first and second derivatives w.r.t. time may also be connected to the ODE.\n",
"First derivatives of controls in Dymos assume the name `<control_name>_rate`.\n",
"Second derivatives of controls in Dymos assume the name `<control_name>_rate2`.\n",
"Control rates are automatically connected if a top-level input of the ODE is named `<control_name>_rate` or `<control_name>_rate2`.\n",
"These variables are available in the timeseries output as `timeseries.polynomial_control_rates.<control_name>_rate` and `timeseries.polynomial_control_rates.<control_name>_rate2`, respectively."
"Controls added with `control_type='polynomial'` are added as polynomial controls."
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ def test_balanced_field_length_for_docs(self):
rotate.set_time_options(fix_initial=False, duration_bounds=(1.0, 5), duration_ref=1.0)
rotate.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0)
rotate.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0)
rotate.add_polynomial_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10, val=[0, 10])
rotate.add_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10, val=[0, 10],
control_type='polynomial')
rotate.add_timeseries_output('*')

# Fifth Phase: Climb to target speed and altitude at end of runway.
Expand Down Expand Up @@ -225,7 +226,7 @@ def test_balanced_field_length_for_docs(self):
rotate.set_time_val(initial=70.0, duration=5.0)
rotate.set_state_val('r', [1750, 1800.0])
rotate.set_state_val('v', [80, 85.0])
rotate.set_polynomial_control_val('alpha', 0.0, units='deg')
rotate.set_control_val('alpha', 0.0, units='deg')

climb.set_time_val(initial=75.0, duration=15.0)
climb.set_state_val('r', [5000, 5500.0], units='ft')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ def _run_problem(self, tx):
rotate.set_time_options(fix_initial=False, duration_bounds=(1.0, 5), duration_ref=1.0)
rotate.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0)
rotate.add_state('v', fix_initial=False, lower=0, ref=100.0, defect_ref=100.0)
rotate.add_polynomial_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10, val=[0, 10])
rotate.add_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10,
val=[0, 10], control_type='polynomial')
rotate.add_timeseries_output('*')

# Fifth Phase: Climb to target speed and altitude at end of runway.
Expand Down Expand Up @@ -306,7 +307,7 @@ def _run_problem(self, tx):
rotate.set_time_val(initial=35.0, duration=5.0)
rotate.set_state_val('r', [1750, 1800.0])
rotate.set_state_val('v', [80, 85.0])
rotate.set_polynomial_control_val('alpha', 0.0, units='deg')
rotate.set_control_val('alpha', 0.0, units='deg')

climb.set_time_val(initial=30.0, duration=20.0)
climb.set_state_val('r', [5000, 5500.0], units='ft')
Expand Down
10 changes: 6 additions & 4 deletions dymos/examples/balanced_field/test/test_balanced_field_length.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ def _make_problem(self):
rotate.set_time_options(fix_initial=False, duration_bounds=(1.0, 5), duration_ref=1.0)
rotate.add_state('r', fix_initial=False, lower=0, ref=1000.0, defect_ref=1000.0)
rotate.add_state('v', fix_initial=False, lower=0.0001, ref=100.0, defect_ref=100.0)
rotate.add_polynomial_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10, val=[0, 10])
rotate.add_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10,
val=[0, 10], control_type='polynomial')
rotate.add_timeseries_output('*')

# Fifth Phase: Climb to target speed and altitude at end of runway.
Expand Down Expand Up @@ -204,7 +205,7 @@ def _make_problem(self):
rotate.set_time_val(initial=35.0, duration=5.0)
rotate.set_state_val('r', [1750, 1800.0])
rotate.set_state_val('v', [80, 85.0])
rotate.set_polynomial_control_val('alpha', 0.0, units='deg')
rotate.set_control_val('alpha', 0.0, units='deg')

climb.set_time_val(initial=30.0, duration=20.0)
climb.set_state_val('r', [5000, 5500.0], units='ft')
Expand Down Expand Up @@ -321,7 +322,8 @@ def test_default_vals_stick(self):
val=rotate.interp(ys=[1750, 1800.0], nodes='state_input'))
rotate.add_state('v', fix_initial=False, lower=0.0001, ref=100.0, defect_ref=100.0,
val=rotate.interp(ys=[80, 85.0], nodes='state_input'))
rotate.add_polynomial_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10, val=[0, 10])
rotate.add_control('alpha', order=1, opt=True, units='deg', lower=0, upper=10, ref=10,
val=[0, 10], control_type='polynomial')
rotate.add_timeseries_output('*')

# Fifth Phase: Climb to target speed and altitude at end of runway.
Expand Down Expand Up @@ -456,7 +458,7 @@ def test_default_vals_stick(self):

assert_near_equal(p.get_val('traj.rotate.t_initial'), 70)
assert_near_equal(p.get_val('traj.rotate.t_duration'), 5)
assert_near_equal(p.get_val('traj.rotate.polynomial_controls:alpha'), np.array([[0, 10]]).T)
assert_near_equal(p.get_val('traj.rotate.controls:alpha'), np.array([[0, 10]]).T)
assert_near_equal(p.get_val('traj.climb.controls:alpha'),
p.model.traj.phases.climb.interp('', [0.01, 0.01], nodes='control_input'))
assert_near_equal(p.get_val('traj.climb.states:gam'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,8 @@ def test_brachistochrone_polynomial_control_rate_targets_gauss_lobatto(self):
units='m/s',
fix_initial=True, fix_final=False, solve_segments=False)

phase.add_polynomial_control('theta', order=3, units='deg*s', lower=0.01, upper=179.9,
fix_initial=True)
phase.add_control('theta', order=3, units='deg*s', lower=0.01, upper=179.9,
fix_initial=True, control_type='polynomial')

phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665)

Expand All @@ -547,7 +547,7 @@ def test_brachistochrone_polynomial_control_rate_targets_gauss_lobatto(self):
phase.set_state_val('x', [0, 10])
phase.set_state_val('y', [10, 5])
phase.set_state_val('v', [0, 9.9])
phase.set_polynomial_control_val('theta', [0, 100])
phase.set_control_val('theta', [0, 100])

# Solve for the optimal trajectory
p.run_driver()
Expand Down Expand Up @@ -624,8 +624,8 @@ def test_brachistochrone_polynomial_control_rate_targets_radau(self):
units='m/s',
fix_initial=True, fix_final=False, solve_segments=False)

phase.add_polynomial_control('theta', order=3, units='deg*s', lower=0.01, upper=179.9,
fix_initial=True)
phase.add_control('theta', order=3, units='deg*s', lower=0.01, upper=179.9,
fix_initial=True, control_type='polynomial')

phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665)

Expand All @@ -641,7 +641,7 @@ def test_brachistochrone_polynomial_control_rate_targets_radau(self):
phase.set_state_val('x', [0, 10])
phase.set_state_val('y', [10, 5])
phase.set_state_val('v', [0, 9.9])
phase.set_polynomial_control_val('theta', [0, 100])
phase.set_control_val('theta', [0, 100])

# Solve for the optimal trajectory
p.run_driver()
Expand Down Expand Up @@ -721,8 +721,8 @@ def test_brachistochrone_polynomial_control_rate_targets_gauss_lobatto(self):
units='m/s',
fix_initial=True, fix_final=False, solve_segments=False)

phase.add_polynomial_control('theta', order=3, units='deg*s', lower=0.01, upper=179.9,
rate_targets=['theta_rate'], fix_initial=True)
phase.add_control('theta', order=3, units='deg*s', lower=0.01, upper=179.9,
rate_targets=['theta_rate'], fix_initial=True, control_type='polynomial')

phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665)

Expand All @@ -738,7 +738,7 @@ def test_brachistochrone_polynomial_control_rate_targets_gauss_lobatto(self):
phase.set_state_val('x', [0, 10])
phase.set_state_val('y', [10, 5])
phase.set_state_val('v', [0, 9.9])
phase.set_polynomial_control_val('theta', [0, 100])
phase.set_control_val('theta', [0, 100])

# Solve for the optimal trajectory
p.run_driver()
Expand Down Expand Up @@ -815,8 +815,8 @@ def test_brachistochrone_polynomial_control_rate_targets_radau(self):
units='m/s',
fix_initial=True, fix_final=False, solve_segments=False)

phase.add_polynomial_control('theta', order=3, units='deg*s', lower=0.01, upper=179.9,
rate_targets=['theta_rate'], fix_initial=True)
phase.add_control('theta', order=3, units='deg*s', lower=0.01, upper=179.9,
rate_targets=['theta_rate'], fix_initial=True, control_type='polynomial')

phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665)

Expand All @@ -832,7 +832,7 @@ def test_brachistochrone_polynomial_control_rate_targets_radau(self):
phase.set_state_val('x', [0, 10])
phase.set_state_val('y', [10, 5])
phase.set_state_val('v', [0, 9.9])
phase.set_polynomial_control_val('theta', [0, 100])
phase.set_control_val('theta', [0, 100])

# Solve for the optimal trajectory
p.run_driver()
Expand Down Expand Up @@ -912,8 +912,8 @@ def test_brachistochrone_polynomial_control_rate_targets_gauss_lobatto(self):
units='m/s',
fix_initial=True, fix_final=False, solve_segments=False)

phase.add_polynomial_control('theta', order=5, units='deg*s**2', lower=0.01, upper=179.9,
rate_targets=None, rate2_targets=['theta_rate'], fix_initial=True)
phase.add_control('theta', order=5, units='deg*s**2', lower=0.01, upper=179.9,
rate_targets=None, rate2_targets=['theta_rate'], fix_initial=True, control_type='polynomial')

phase.add_parameter('g', units='m/s**2', opt=False, val=9.80665)

Expand All @@ -929,8 +929,8 @@ def test_brachistochrone_polynomial_control_rate_targets_gauss_lobatto(self):
phase.set_state_val('x', [0, 10])
phase.set_state_val('y', [10, 5])
phase.set_state_val('v', [0, 9.9])
phase.set_polynomial_control_val('theta', [0, 10, 40, 60, 80, 100],
time_vals=[0, 0.4, 0.8, 1.2, 1.6, 2.0])
phase.set_control_val('theta', [0, 10, 40, 60, 80, 100],
time_vals=[0, 0.4, 0.8, 1.2, 1.6, 2.0])

# Solve for the optimal trajectory
dm.run_problem(p, simulate=True)
Expand Down
Loading
Loading