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

Added ability to allow the user to change the name of the integration variable. #874

Merged
merged 24 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
14c5a70
Started the ability to rename the integration variable from time to t…
robfalck Nov 21, 2022
fb49b6a
removed noisy warning about duplicate entries in timeseries
robfalck Nov 21, 2022
5b2f7a5
Fixed issue in Trajectory._update_linkage_options_configure that was …
robfalck Nov 22, 2022
36a0f00
Refinement fix for renamed time_phase and a fix for _get_objective_src
robfalck Nov 22, 2022
a07ea98
benchmark cases are working. Added allowance for time_phase in constr…
robfalck Nov 22, 2022
f5d8c77
cleanup of AnalyticPhase constraint handling with new names
robfalck Nov 22, 2022
e7e0bb9
working on getting explicit shooting working here
robfalck Nov 22, 2022
2ef3d96
more progress on the ExplicitShooting failures
robfalck Nov 22, 2022
d2c2d8b
knocking out more test failures
robfalck Nov 22, 2022
7b511e8
More changes to tests to account for phase.time -> phase.t change.
robfalck Nov 22, 2022
93e6b22
more test failures fixed
robfalck Nov 23, 2022
176a839
All test fixed except the linkage diagram test on some platforms
robfalck Nov 23, 2022
ff6fe32
Missed one
robfalck Nov 23, 2022
f12acbe
Include phase elapsed time in the timeseries as {time_name}_phase. Up…
robfalck Nov 23, 2022
3bbd815
Fixing tests to expect time_phase in the timeseries instead of t_phase
robfalck Nov 23, 2022
969ed9c
Demonstrated changing time variable name in the racecar example.
robfalck Nov 23, 2022
de1cb7e
Adding tests of renamed time as objectives and constraints.
robfalck Nov 23, 2022
9418484
more tests
robfalck Nov 23, 2022
278c156
missed a test
robfalck Nov 23, 2022
e88ec63
edits per Johns review
robfalck Nov 25, 2022
b0680d0
Fixed timeseries introspection to jump to the next output if one is n…
robfalck Nov 29, 2022
80ac162
changed default time and time_phaset targets to _unspecified to make …
robfalck Nov 29, 2022
62cff45
Fixed a test for segment simulation comp where no time introspection …
robfalck Nov 29, 2022
a81c083
workaround of pygments ipython issue
robfalck Nov 29, 2022
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
2 changes: 2 additions & 0 deletions docs/dymos_book/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ launch_buttons:
# Enable use of sphinx autodoc
sphinx:
extra_extensions:
# This line is a workaround for https://github.com/jupyter/nbconvert/issues/528
- 'IPython.sphinxext.ipython_console_highlighting'
- 'sphinx.ext.autodoc'
- 'sphinx.ext.autosummary'
- 'sphinx.ext.viewcode'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,10 @@
"soc1m = prob['traj.phase1_mfail.states:state_of_charge']\n",
"\n",
"# Plot Results\n",
"t0 = prob['traj.phases.phase0.time.time']/3600\n",
"t1 = prob['traj.phases.phase1.time.time']/3600\n",
"t1b = prob['traj.phases.phase1_bfail.time.time']/3600\n",
"t1m = prob['traj.phases.phase1_mfail.time.time']/3600\n",
"t0 = prob['traj.phases.phase0.timeseries.time']/3600\n",
"t1 = prob['traj.phases.phase1.timeseries.time']/3600\n",
"t1b = prob['traj.phases.phase1_bfail.timeseries.time']/3600\n",
"t1m = prob['traj.phases.phase1_mfail.timeseries.time']/3600\n",
"\n",
"plt.subplot(2, 1, 1)\n",
"plt.plot(t0, soc0, 'b')\n",
Expand Down Expand Up @@ -515,7 +515,7 @@
}
},
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -529,7 +529,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
"version": "3.10.6"
}
},
"nbformat": 4,
Expand Down
10 changes: 6 additions & 4 deletions docs/dymos_book/examples/racecar/racecar.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"(content:examples:race_car)=\n",
"# Race car Lap Simulation\n",
"\n",
"```{admonition} Things you'll learn through this example\n",
Expand Down Expand Up @@ -263,7 +264,7 @@
"# timeODE.py\n",
"phase.set_time_options(fix_initial=True, fix_duration=True, duration_val=s_final,\n",
" targets=['curv.s'], units='m', duration_ref=s_final,\n",
" duration_ref0=10)\n",
" duration_ref0=10, name='s')\n",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first glance, this name='s' confuses me. I will review the PR and see if it makes more sense. I'm leaving this note here so I remember to check back in here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left this alone because it was an external contribution. In my mind 's' is often used for arclength so it makes sense to me.

"\n",
"# Define states\n",
"phase.add_state('t', fix_initial=True, fix_final=False, units='s', lower=0,\n",
Expand Down Expand Up @@ -329,6 +330,7 @@
"\n",
"# Add output timeseries\n",
"phase.add_timeseries_output('*')\n",
"phase.add_timeseries_output('t', output_name='time')\n",
"\n",
"# Link the states at the start and end of the phase in order to ensure a continous lap\n",
"traj.link_phases(phases=['phase0', 'phase0'],\n",
Expand Down Expand Up @@ -383,7 +385,7 @@
"\n",
"# Get optimized time series\n",
"n = p.get_val('traj.phase0.timeseries.states:n')\n",
"s = p.get_val('traj.phase0.timeseries.time')\n",
"s = p.get_val('traj.phase0.timeseries.s')\n",
"V = p.get_val('traj.phase0.timeseries.states:V')\n",
"thrust = p.get_val('traj.phase0.timeseries.controls:thrust')\n",
"delta = p.get_val('traj.phase0.timeseries.controls:delta')\n",
Expand All @@ -399,7 +401,7 @@
"source": [
"# Get optimized time series\n",
"n = p.get_val('traj.phase0.timeseries.states:n')\n",
"s = p.get_val('traj.phase0.timeseries.time')\n",
"s = p.get_val('traj.phase0.timeseries.s')\n",
"V = p.get_val('traj.phase0.timeseries.states:V')\n",
"thrust = p.get_val('traj.phase0.timeseries.controls:thrust')\n",
"delta = p.get_val('traj.phase0.timeseries.controls:delta')\n",
Expand Down Expand Up @@ -612,7 +614,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.5"
"version": "3.10.6"
}
},
"nbformat": 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,12 @@
"\n",
"a = sol.get_val('traj.phase0.parameters:a_ctrl')\n",
"b = sol.get_val('traj.phase0.parameters:b_ctrl')\n",
"t = sol.get_val('traj.phase0.time_phase')\n",
"t = sol.get_val('traj.phase0.timeseries.time_phase')\n",
"tan_theta_sol = a + b * t\n",
"\n",
"a = sim.get_val('traj.phase0.parameters:a_ctrl')\n",
"b = sim.get_val('traj.phase0.parameters:b_ctrl')\n",
"t = sim.get_val('traj.phase0.time_phase')\n",
"t = sim.get_val('traj.phase0.timeseries.time_phase')\n",
"tan_theta_sim = a + b * t\n",
"\n",
"param_ax.plot(sol.get_val('traj.phase0.timeseries.time'),\n",
Expand Down Expand Up @@ -482,7 +482,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.7"
"version": "3.10.6"
}
},
"nbformat": 4,
Expand Down
6 changes: 3 additions & 3 deletions docs/dymos_book/examples/water_rocket/water_rocket.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,8 @@
"\n",
"def plot_propelled_ascent(p, exp_out):\n",
" fig, ax = plt.subplots(2, 2, sharex=True, figsize=(12, 6))\n",
" t_imp = p.get_val('traj.propelled_ascent.time', 's')\n",
" t_exp = exp_out.get_val('traj.propelled_ascent.time', 's')\n",
" t_imp = p.get_val('traj.propelled_ascent.timeseries.time', 's')\n",
" t_exp = exp_out.get_val('traj.propelled_ascent.timeseries.time', 's')\n",
" \n",
" c = colors['pa']\n",
"\n",
Expand Down Expand Up @@ -875,7 +875,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.7"
"version": "3.10.6"
}
},
"nbformat": 4,
Expand Down
40 changes: 38 additions & 2 deletions docs/dymos_book/features/phases/variables.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,42 @@
"- `t_initial` - The initial time of the current phase (this value is the same at all nodes within the phase).\n",
"- `t_duration` - The phase time duration of the current phase (this value is the same at all nodes within the phase).\n",
"\n",
"### Renaming the integration variable\n",
"\n",
"In some casees the integration in a phase might take place over some non-time variable. For instance, in the [race car example](content:examples:race_car), thee dynamics are integrated over the track length `s`. In such cases it might be preferable to rename the time variable to make the outputs somewhat more understandable. This can be achieved by using the `name` parameter to `set_time_options`, for instance:\n",
"\n",
"```\n",
"phase.set_time_options(name='s', ...)\n",
"```\n",
"\n",
"When doing so, the timeseries will replace `time` with the given name `s` and `time_phase` with `s_phase`.\n",
"If one wishes to use the renamed integration variablee (or the phase-relative version), it can be used in the `add_objective` and `add_boundary_constraint` and `add_path_constraint` methods on `Phase`. For instance:\n",
"\n",
"```\n",
"phase.set_time_options(name='W', ...)\n",
".\n",
".\n",
".\n",
"phase.add_objective('W', loc='final')\n",
"```\n",
"\n",
"### Recovering time for the timeseries outputs\n",
"\n",
"When creating timeseries plots in dymos, it expects the `phase_name.timeseries.time` to be present. In a multi-phase trajectory where some phases use a different variable of integratoin, it may be helpful to to add an expression for time to the timeseries manually.\n",
"\n",
"For instance, the [race car example](content:examples:race_car) integrates the expression `dt_ds` to accumulate the elapsed time of the phase as a state named `t`. This can be manulally added to the timeseries outputs\n",
"\n",
"```\n",
"phase.add_timeseries_output('t', output_name='time')\n",
"```\n",
"\n",
"Similarly, we can form linkage constraints that would link two phases where time might be the integration variable in one phase and a state variable in a subsequent phase.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Options for Time Variables"
]
},
Expand Down Expand Up @@ -289,7 +325,7 @@
}
},
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -303,7 +339,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.1"
"version": "3.10.6"
}
},
"nbformat": 4,
Expand Down
1 change: 0 additions & 1 deletion docs/dymos_book/test/test_jupyter_output_linting.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ class LintJupyterOutputsTestCase(unittest.TestCase):
"""

def test_output(self):
this_file = pathlib.PurePath(__file__)

for file in _get_files():
print(file)
Expand Down
2 changes: 1 addition & 1 deletion dymos/examples/brachistochrone/test/ex_brachistochrone.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,6 @@ def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, tran


if __name__ == '__main__':
p = brachistochrone_min_time(transcription='radau-ps', num_segments=20, run_driver=True,
p = brachistochrone_min_time(transcription='gauss-lobatto', num_segments=5, run_driver=True,
transcription_order=5, compressed=False, optimizer='SNOPT',
solve_segments=False, force_alloc_complex=True)
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ def _make_problem(transcription='gauss-lobatto', num_segments=8, transcription_o
class TestBrachistochroneVectorStatesExampleSolveSegments(unittest.TestCase):

def assert_results(self, p):
t_initial = p.get_val('traj0.phase0.time')[0]
t_final = p.get_val('traj0.phase0.time')[-1]
t_initial = p.get_val('traj0.phase0.t')[0]
t_final = p.get_val('traj0.phase0.t')[-1]

x0 = p.get_val('traj0.phase0.timeseries.states:pos')[0, 0]
xf = p.get_val('traj0.phase0.timeseries.states:pos')[0, -1]
Expand Down Expand Up @@ -210,8 +210,8 @@ def test_ex_brachistochrone_vs_gl_single_segment(self):
class TestBrachistochroneSolveSegments(unittest.TestCase):

def assert_results(self, p):
t_initial = p.get_val('traj0.phase0.time')[0]
t_final = p.get_val('traj0.phase0.time')[-1]
t_initial = p.get_val('traj0.phase0.t')[0]
t_final = p.get_val('traj0.phase0.t')[-1]

x0 = p.get_val('traj0.phase0.timeseries.states:x')[0]
xf = p.get_val('traj0.phase0.timeseries.states:x')[-1]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_brachistochrone_vector_boundary_constraints_radau_no_indices(self):

p.run_driver()

assert_near_equal(p.get_val('phase0.time')[-1], 1.8016, tolerance=1.0E-3)
assert_near_equal(p.get_val('phase0.t')[-1], 1.8016, tolerance=1.0E-3)

# Plot results
if SHOW_PLOTS:
Expand Down Expand Up @@ -150,7 +150,7 @@ def test_brachistochrone_vector_boundary_constraints_radau_full_indices(self):

p.run_driver()

assert_near_equal(p.get_val('phase0.time')[-1], 1.8016, tolerance=1.0E-3)
assert_near_equal(p.get_val('phase0.t')[-1], 1.8016, tolerance=1.0E-3)

# Plot results
if SHOW_PLOTS:
Expand Down Expand Up @@ -236,48 +236,48 @@ def test_brachistochrone_vector_boundary_constraints_radau_partial_indices(self)

p.run_driver()

assert_near_equal(p.get_val('phase0.time')[-1], 1.8016, tolerance=1.0E-3)

# # Plot results
# if SHOW_PLOTS:
# p.run_driver()
# exp_out = phase.simulate(times_per_seg=20)
#
# fig, ax = plt.subplots()
# fig.suptitle('Brachistochrone Solution')
#
# x_imp = p.get_val('phase0.timeseries.states:pos')[:, 0]
# y_imp = p.get_val('phase0.timeseries.states:pos')[:, 1]
#
# x_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 0]
# y_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 1]
#
# ax.plot(x_imp, y_imp, 'ro', label='implicit')
# ax.plot(x_exp, y_exp, 'b-', label='explicit')
#
# ax.set_xlabel('x (m)')
# ax.set_ylabel('y (m)')
# ax.grid(True)
# ax.legend(loc='upper right')
#
# fig, ax = plt.subplots()
# fig.suptitle('Brachistochrone Solution')
#
# x_imp = p.get_val('phase0.timeseries.time')
# y_imp = p.get_val('phase0.timeseries.control_rates:theta_rate2')
#
# x_exp = exp_out.get_val('phase0.timeseries.time')
# y_exp = exp_out.get_val('phase0.timeseries.control_rates:theta_rate2')
#
# ax.plot(x_imp, y_imp, 'ro', label='implicit')
# ax.plot(x_exp, y_exp, 'b-', label='explicit')
#
# ax.set_xlabel('time (s)')
# ax.set_ylabel('theta rate2 (rad/s**2)')
# ax.grid(True)
# ax.legend(loc='lower right')
#
# plt.show()
assert_near_equal(p.get_val('phase0.t')[-1], 1.8016, tolerance=1.0E-3)

# Plot results
if SHOW_PLOTS:
p.run_driver()
exp_out = phase.simulate(times_per_seg=20)

fig, ax = plt.subplots()
fig.suptitle('Brachistochrone Solution')

x_imp = p.get_val('phase0.timeseries.states:pos')[:, 0]
y_imp = p.get_val('phase0.timeseries.states:pos')[:, 1]

x_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 0]
y_exp = exp_out.get_val('phase0.timeseries.states:pos')[:, 1]

ax.plot(x_imp, y_imp, 'ro', label='implicit')
ax.plot(x_exp, y_exp, 'b-', label='explicit')

ax.set_xlabel('x (m)')
ax.set_ylabel('y (m)')
ax.grid(True)
ax.legend(loc='upper right')

fig, ax = plt.subplots()
fig.suptitle('Brachistochrone Solution')

x_imp = p.get_val('phase0.timeseries.time')
y_imp = p.get_val('phase0.timeseries.control_rates:theta_rate2')

x_exp = exp_out.get_val('phase0.timeseries.time')
y_exp = exp_out.get_val('phase0.timeseries.control_rates:theta_rate2')

ax.plot(x_imp, y_imp, 'ro', label='implicit')
ax.plot(x_exp, y_exp, 'b-', label='explicit')

ax.set_xlabel('time (s)')
ax.set_ylabel('theta rate2 (rad/s**2)')
ax.grid(True)
ax.legend(loc='lower right')

plt.show()

return p

Expand Down
41 changes: 0 additions & 41 deletions dymos/examples/brachistochrone/test/test_duplicate_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,44 +574,3 @@ def test_duplicate_path_constraint_different_constraint_name(self):

assert_near_equal(check_calc, check_1)
assert_near_equal(check_calc, check_2)

def test_duplicate_timeseries_output(self):
p = om.Problem(model=om.Group())

p.driver = om.ScipyOptimizeDriver(tol=1.0E-6)
p.driver.declare_coloring(tol=1.0E-12)

tx = dm.Radau(num_segments=10, order=3)

traj = dm.Trajectory()
phase = dm.Phase(ode_class=BrachistochroneODE, transcription=tx)
p.model.add_subsystem('traj0', traj)
traj.add_phase('phase0', phase)

phase.set_time_options(fix_initial=True, duration_bounds=(.5, 10))

phase.add_state('x', fix_initial=True, fix_final=True)
phase.add_state('y', fix_initial=True, fix_final=True)

# Note that by omitting the targets here Dymos will automatically attempt to connect
# to a top-level input named 'v' in the ODE, and connect to nothing if it's not found.
phase.add_state('v', fix_initial=True, fix_final=False)

phase.add_control('theta', units='rad', lower=1E-6, upper=np.pi)

phase.add_parameter('g', units='m/s**2')

phase.add_timeseries_output('check', output_name='bounded_check')
# First make sure this causes a warning.
msg = "Output name `bounded_check` is already in timeseries `timeseries`. New output ignored."
with assert_warning(UserWarning, msg):
phase.add_timeseries_output('check', output_name='bounded_check')

phase.add_boundary_constraint('check', loc='final', lower=-50, upper=50)

# We need to make sure this doesn't cause a warning or error.
with warnings.catch_warnings():
warnings.simplefilter("error")
phase.add_path_constraint('check', constraint_name='bounded_check', upper=100, lower=-100)

self.assertIn('bounded_check', [op['output_name'] for op in phase._timeseries['timeseries']['outputs'].values()])
Loading