Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/OpenMDAO/dymos into radau…
Browse files Browse the repository at this point in the history
…_refactor
  • Loading branch information
robfalck committed Jan 8, 2025
2 parents 8bada94 + efb22b7 commit 9587358
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, transcription_order=3,
grid_type='lgl', compressed=True, optimizer='SLSQP',
dynamic_simul_derivs=True, force_alloc_complex=False,
solve_segments=False, run_driver=True):
solve_segments=False, run_driver=True, simulate=False,
make_plots=False):
p = om.Problem(model=om.Group())

if optimizer == 'SNOPT':
Expand Down Expand Up @@ -78,9 +79,11 @@ def brachistochrone_min_time(transcription='gauss-lobatto', num_segments=8, tran
phase.set_state_val('v', [0, 9.9])
phase.set_control_val('theta', [5, 100])
phase.set_parameter_val('g', 9.80665)
p.run_model()
if run_driver:
p.run_driver()

dm.run_problem(p,
run_driver=run_driver,
simulate=simulate,
make_plots=make_plots)

return p

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import importlib
import os
import pathlib
import unittest
from numpy.testing import assert_almost_equal

from openmdao.utils.general_utils import set_pyoptsparse_opt, printoptions
from openmdao.utils.testing_utils import use_tempdirs
import sys

import dymos
import dymos.examples.brachistochrone.test.ex_brachistochrone_vector_states as ex_brachistochrone_vs
from dymos.utils.testing_utils import assert_check_partials
from dymos.utils.testing_utils import assert_check_partials, _get_reports_dir

bokeh_available = importlib.util.find_spec('bokeh') is not None

OPT, OPTIMIZER = set_pyoptsparse_opt('SNOPT')


@use_tempdirs
class TestBrachistochroneVectorStatesExample(unittest.TestCase):

def setUp(self):
self.testflo_running = os.environ.pop('TESTFLO_RUNNING', None)

def tearDown(self):
# restore what was there before running the test
if self.testflo_running is not None:
os.environ['TESTFLO_RUNNING'] = self.testflo_running

def assert_results(self, p):
t_initial = p.get_val('traj0.phase0.timeseries.time')[0]
t_final = p.get_val('traj0.phase0.timeseries.time')[-1]
Expand Down Expand Up @@ -107,6 +119,37 @@ def test_ex_brachistochrone_vs_birkhoff(self):
self.assert_results(p)
self.assert_partials(p)

@unittest.skipIf(not bokeh_available, 'bokeh unavailable')
@hooks_active
def test_bokeh_plots(self):
with set_env_vars_context(OPENMDAO_REPORTS='1'):
with dm.options.temporary(plots='bokeh'):
p = ex_brachistochrone_vs.brachistochrone_min_time(transcription='radau-ps',
compressed=False,
force_alloc_complex=True,
run_driver=True,
simulate=True,
make_plots=True)

self.assert_results(p)
self.assert_partials(p)

html_file = pathlib.Path(_get_reports_dir(p)) / 'traj0_results_report.html'
self.assertTrue(html_file.exists(), msg=f'{html_file} does not exist!')

with open(html_file) as f:
html_data = f.read()

expected_labels = ['"axis_label":"pos[0] (m)"',
'"axis_label":"pos[1] (m)"',
'"axis_label":"v (m/s)"',
'"axis_label":"theta (deg)"']

for label in expected_labels:
self.assertIn(label, html_data)

self.assertNotIn('"axis_label":"pos (m)"', html_data)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from openmdao.utils.general_utils import set_pyoptsparse_opt
from openmdao.utils.testing_utils import use_tempdirs, set_env_vars_context


import dymos as dm
from dymos.examples.finite_burn_orbit_raise.finite_burn_orbit_raise_problem import two_burn_orbit_raise_problem
from dymos.utils.testing_utils import _get_reports_dir
Expand Down
100 changes: 80 additions & 20 deletions dymos/examples/min_time_climb/test/test_ex_min_time_climb.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import importlib
import os
import unittest
import numpy as np
from numpy.polynomial import Polynomial as P
Expand All @@ -9,18 +11,21 @@

import openmdao.api as om
from openmdao.utils.assert_utils import assert_near_equal
from dymos.utils.testing_utils import assert_timeseries_near_equal
from dymos.utils.testing_utils import assert_timeseries_near_equal, _get_reports_dir
from dymos.utils.introspection import get_promoted_vars

import dymos as dm
from dymos.examples.min_time_climb.min_time_climb_ode import MinTimeClimbODE
from dymos.utils.misc import om_version
from openmdao.utils.testing_utils import use_tempdirs, require_pyoptsparse
from openmdao.utils.testing_utils import use_tempdirs, require_pyoptsparse, set_env_vars_context


bokeh_available = importlib.util.find_spec('bokeh') is not None


def min_time_climb(optimizer='SLSQP', num_seg=3, transcription='gauss-lobatto',
transcription_order=3, force_alloc_complex=False, add_rate=False, time_name='time',
simulate=True, path_constraints=True):
simulate=True, path_constraints=True, make_plots=False):

p = om.Problem(model=om.Group())

Expand Down Expand Up @@ -139,7 +144,7 @@ def min_time_climb(optimizer='SLSQP', num_seg=3, transcription='gauss-lobatto',
with np.printoptions(linewidth=1024, edgeitems=1024):
p.check_partials(compact_print=False, method='cs', show_only_incorrect=True, out_stream=f)

dm.run_problem(p, simulate=simulate, make_plots=False)
dm.run_problem(p, simulate=simulate, make_plots=make_plots, plot_kwargs={'x_name': time_name})

return p

Expand Down Expand Up @@ -293,35 +298,90 @@ def test_results_birkhoff(self):

self._test_timeseries_units(p)


@use_tempdirs
class TestMinTimeClimbWithReports(TestMinTimeClimb):

def setUp(self):
self.testflo_running = os.environ.pop('TESTFLO_RUNNING', None)

def tearDown(self):
# restore what was there before running the test
if self.testflo_running is not None:
os.environ['TESTFLO_RUNNING'] = self.testflo_running

def _test_traj_results_report(self, p):
html_file = _get_reports_dir(p) / 'traj_results_report.html'
self.assertTrue(html_file.exists(), msg=f'{html_file} does not exist!')

with open(html_file) as f:
html_data = f.read()

expected_labels = ['"axis_label":"alpha (deg)"',
'"axis_label":"t (s)"',
'"axis_label":"CD (None)"',
'"axis_label":"CD0 (None)"',
'"axis_label":"CL (None)"',
'"axis_label":"CLa (None)"',
'"axis_label":"f_drag (N)"',
'"axis_label":"f_lift (lbf)"',
'"axis_label":"gam (rad)"',
'"axis_label":"h (m)"',
'"axis_label":"kappa (None)"',
'"axis_label":"m (kg)"',
'"axis_label":"m_dot (kg/s)"',
'"axis_label":"mach (None)"',
'"axis_label":"mach_rate (None)"',
'"axis_label":"q (N/m**2)"',
'"axis_label":"r (m)"',
'"axis_label":"thrust (lbf)"',
'"axis_label":"v (m/s)"']

for label in expected_labels:
self.assertIn(label, html_data)

@require_pyoptsparse(optimizer='IPOPT')
@unittest.skipIf(not bokeh_available, 'bokeh is not available')
def test_results_gauss_lobatto_renamed_time(self):
NUM_SEG = 12
ORDER = 3
p = min_time_climb(optimizer='IPOPT', num_seg=NUM_SEG, transcription_order=ORDER,
transcription='gauss-lobatto', add_rate=True, time_name='t')
with set_env_vars_context(OPENMDAO_REPORTS='1'):
with dm.options.temporary(plots='bokeh'):
NUM_SEG = 12
ORDER = 3
p = min_time_climb(optimizer='IPOPT', num_seg=NUM_SEG, transcription_order=ORDER,
force_alloc_complex=True,
transcription='gauss-lobatto', add_rate=True, time_name='t',
make_plots=True)

self._test_results(p, time_name='t')
self._test_results(p, time_name='t')

self._test_wilcard_outputs(p)
self._test_wilcard_outputs(p)

self._test_timeseries_units(p)
self._test_timeseries_units(p)

self._test_mach_rate(p, time_name='t')

self._test_mach_rate(p, time_name='t')
self._test_traj_results_report(p)

@require_pyoptsparse(optimizer='IPOPT')
@unittest.skipIf(not bokeh_available, 'bokeh is not available')
def test_results_radau_renamed_time(self):
NUM_SEG = 15
ORDER = 3
p = min_time_climb(optimizer='IPOPT', num_seg=NUM_SEG, transcription_order=ORDER,
transcription='radau-ps', add_rate=True, time_name='t', force_alloc_complex=True)
with set_env_vars_context(OPENMDAO_REPORTS='1'):
with dm.options.temporary(plots='bokeh'):
NUM_SEG = 15
ORDER = 3
p = min_time_climb(optimizer='IPOPT', num_seg=NUM_SEG, transcription_order=ORDER,
transcription='radau-ps', add_rate=True, time_name='t',
force_alloc_complex=True, make_plots=True)

self._test_results(p, time_name='t')
self._test_results(p, time_name='t')

self._test_wilcard_outputs(p)
self._test_wilcard_outputs(p)

self._test_timeseries_units(p)
self._test_timeseries_units(p)

self._test_mach_rate(p, plot=False, time_name='t')

self._test_mach_rate(p, plot=False, time_name='t')
self._test_traj_results_report(p)


if __name__ == '__main__': # pragma: no cover
Expand Down
7 changes: 4 additions & 3 deletions dymos/run_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ def run_problem(problem, refine_method='hp', refine_iteration_limit=0, run_drive
sims[subsys.pathname] = sim_prob

if make_plots:

if om_version()[0] > (3, 34, 2):
outputs_dir = problem.get_outputs_dir()
if os.sep in str(solution_record_file):
Expand All @@ -142,13 +141,15 @@ def run_problem(problem, refine_method='hp', refine_iteration_limit=0, run_drive
_sol_record_file = solution_record_file
_sim_record_file = None if not simulate else simulation_record_file

_plot_kwargs = plot_kwargs if plot_kwargs is not None else {}

if dymos_options['plots'] == 'bokeh':
from dymos.visualization.timeseries.bokeh_timeseries_report import make_timeseries_report
make_timeseries_report(prob=problem,
solution_record_file=_sol_record_file,
simulation_record_file=_sim_record_file)
simulation_record_file=_sim_record_file,
**_plot_kwargs)
else:
_plot_kwargs = plot_kwargs if plot_kwargs is not None else {}
plots_dir = problem.get_reports_dir() / 'plots'
timeseries_plots(_sol_record_file,
simulation_record_file=_sim_record_file,
Expand Down
33 changes: 32 additions & 1 deletion dymos/test/test_pycodestyle.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import os
import pathlib
import subprocess
import sys
import unittest

Expand Down Expand Up @@ -33,6 +35,33 @@ def _discover_python_files(path):
return python_files


def _get_tracked_python_files(git_root):
"""
Given a git root directory
Parameters
----------
git_root : str or Path
The root directory of the git repository.
Returns
-------
set
Python files in the given git directory that a tracked by git.
"""
_git_root = str(git_root)
file_list = _discover_python_files(_git_root)
untracked_set = set(
subprocess.run(
['git', 'ls-files', _git_root, '--exclude-standard', '--others'],
stdout=subprocess.PIPE,
text=True
).stdout.splitlines()
)
untracked_set = {str(pathlib.Path(f).absolute()) for f in untracked_set if f.endswith('.py')}
return set(file_list) - untracked_set


@unittest.skipIf(pycodestyle is None, "This test requires pycodestyle")
class TestPyCodeStyle(unittest.TestCase):

Expand All @@ -47,6 +76,8 @@ def test_pycodestyle(self):
dymos_path = os.path.split(dymos.__file__)[0]
pyfiles = _discover_python_files(dymos_path)

files_to_check = _get_tracked_python_files(dymos_path)

style = pycodestyle.StyleGuide(ignore=['E226', # missing whitespace around arithmetic operator
'E241', # multiple spaces after ','
'W504', # line break after binary operator
Expand All @@ -65,7 +96,7 @@ def test_pycodestyle(self):
try:
sys.stdout = buff_out = StringIO()
sys.stdout = buff_err = StringIO()
report = style.check_files(pyfiles)
report = style.check_files(files_to_check)
finally:
sys.stdout = save_out
sys.stderr = save_err
Expand Down
Loading

0 comments on commit 9587358

Please sign in to comment.