Skip to content

Commit

Permalink
Refactored time_extents away in favor of housing extents in param_comp (
Browse files Browse the repository at this point in the history
#952)

* time_extents removed in favor of keeping t_intial, t_duration, t_initial_val, and t_duration val in param_comp

* Added test to verify that model data for visualization report hasn't changed. Made test for parameter connections more specific to avoid picking up t_initial and t_duration.

* Fixed the tandem phase brachistochrone example in docs to connect from t_duration_val.
Included model_data.dat in the package data for the linkage report test.

* Cleanup per kens review

* Update oldest supported OpenMDAO version to 3.26.0

* unique artifact name per matrix entry
  • Loading branch information
robfalck authored Jul 21, 2023
1 parent 9a370ad commit 79ca083
Show file tree
Hide file tree
Showing 16 changed files with 358 additions and 75 deletions.
12 changes: 10 additions & 2 deletions .github/workflows/dymos_tests_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ jobs:
OPENMPI: '4.0'
MPI4PY: '3.0'
PETSc: 3.13
PYOPTSPARSE: 'v2.4.0'
PYOPTSPARSE: 'v2.6.1'
SNOPT: 7.2
OPENMDAO: 3.19.0
OPENMDAO: 3.26.0
OPTIONAL: '[test]'

steps:
Expand Down Expand Up @@ -254,6 +254,7 @@ jobs:
run: |
conda info
conda list
conda env export --name test --file ${{ matrix.NAME }}_environment.yml
echo "============================================================="
echo "Check installed versions of Python, Numpy and Scipy"
Expand All @@ -267,6 +268,13 @@ jobs:
python -c "import scipy; assert str(scipy.__version__).startswith(str(${{ matrix.SCIPY }})), \
f'Scipy version {scipy.__version__} is not the requested version (${{ matrix.SCIPY }})'"
- name: 'Upload environment artifact'
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.NAME }}_environment
path: ${{ matrix.NAME }}_environment.yml
retention-days: 5

- name: Run tests
if: env.RUN_BUILD
env:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@
"#\n",
"# Connect the two phases\n",
"#\n",
"p.model.connect('phase0.t_duration', 'phase1.t_duration')\n",
"p.model.connect('phase0.t_duration_val', 'phase1.t_duration')\n",
"\n",
"p.model.connect('phase0.timeseries2.theta', 'phase1.controls:theta')\n",
"p.model.connect('phase0.timeseries2.v', 'phase1.controls:v')\n",
Expand Down Expand Up @@ -357,7 +357,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
"version": "3.11.4"
}
},
"nbformat": 4,
Expand Down
4 changes: 2 additions & 2 deletions docs/dymos_book/faq/tandem_phases.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@
"#\n",
"# Connect the two phases\n",
"#\n",
"p.model.connect('phase0.t_duration', 'phase1.t_duration')\n",
"p.model.connect('phase0.t_duration_val', 'phase1.t_duration')\n",
"\n",
"p.model.connect('phase0.timeseries2.theta', 'phase1.controls:theta')\n",
"p.model.connect('phase0.timeseries2.v', 'phase1.controls:v')\n",
Expand Down Expand Up @@ -320,7 +320,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
"version": "3.11.4"
}
},
"nbformat": 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def test_brachistochrone_tandem_phases(self):
#
# Connect the two phases
#
p.model.connect('phase0.t_duration', 'phase1.t_duration')
p.model.connect('phase0.t_duration_val', 'phase1.t_duration')

p.model.connect('phase0.timeseries2.theta', 'phase1.controls:theta')
p.model.connect('phase0.timeseries2.v', 'phase1.controls:v')
Expand Down
2 changes: 1 addition & 1 deletion dymos/examples/brachistochrone/test/test_tandem_phases.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def _run_transcription(self, transcription):
#
# Connect the two phases
#
p.model.connect('phase0.t_duration', 'phase1.t_duration')
p.model.connect('phase0.t_duration_val', 'phase1.t_duration')

p.model.connect('phase0.timeseries2.theta', 'phase1.controls:theta')
p.model.connect('phase0.timeseries2.v', 'phase1.controls:v')
Expand Down
14 changes: 5 additions & 9 deletions dymos/phase/test/test_time_targets.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import unittest

import numpy as np
import matplotlib

import openmdao.api as om
from openmdao.utils.assert_utils import assert_near_equal
Expand All @@ -10,9 +9,6 @@
import dymos as dm


matplotlib.use('Agg')


class _BrachistochroneTestODE(om.ExplicitComponent):

def initialize(self):
Expand Down Expand Up @@ -138,10 +134,10 @@ def _make_problem(self, transcription, num_seg, transcription_order=3, input_ini
if input_duration:
p.model.add_design_var('phase0.t_duration', lower=0, upper=3, scaler=1.0)

p.setup(check=True)
p.setup(check=True, force_alloc_complex=True)

p['phase0.t_initial'] = 1.0
p['phase0.t_duration'] = 5.0
p['phase0.t_duration'] = 2.0

if transcription == 'explicit-shooting':
p['phase0.initial_states:x'] = 0
Expand All @@ -151,7 +147,7 @@ def _make_problem(self, transcription, num_seg, transcription_order=3, input_ini
p['phase0.states:x'] = phase.interp('x', [0, 10])
p['phase0.states:y'] = phase.interp('y', [10, 5])
p['phase0.states:v'] = phase.interp('v', [0, 9.9])
p['phase0.controls:theta'] = phase.interp('theta', [5, 100.5])
p['phase0.controls:theta'] = phase.interp('theta', [0.01, 100.5])

return p

Expand Down Expand Up @@ -287,9 +283,9 @@ def test_explicit_shooting(self):

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

assert_near_equal(p['phase0.t_initial'], p['phase0.t_initial'])
assert_near_equal(p['phase0.t_initial'], p['phase0.integrator.t_initial'])

assert_near_equal(p['phase0.t_duration'], p['phase0.t_duration'])
assert_near_equal(p['phase0.t_duration'], p['phase0.integrator.t_duration'])

assert_near_equal(np.atleast_2d(p['phase0.t_phase']).T, time_phase_all)

Expand Down
4 changes: 2 additions & 2 deletions dymos/trajectory/test/test_trajectory_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,8 @@ def test_parameter_shape_definition(self):

p.setup()

p['traj.phase0.t_initial'] = 0.0
p['traj.phase0.t_duration'] = 2.0
p['traj.t_initial'] = 0.0
p['traj.t_duration'] = 2.0

p['traj.phase0.states:x'] = phase.interp('x', ys=[0, 10])
p['traj.phase0.states:y'] = phase.interp('y', ys=[10, 5])
Expand Down
26 changes: 24 additions & 2 deletions dymos/transcriptions/common/parameter_comp.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class ParameterComp(ExplicitComponent):
Parameters
----------
time_options : TimeOptionsDictionary or None
If None, specify time options for the creation of t_initial and t_duration inputs and outputs.
**kwargs : dict
Arguments to be passed to the component initialization method.
Expand All @@ -25,18 +27,38 @@ class ParameterComp(ExplicitComponent):
Container mapping name of variables to be muxed with associated inputs.
"""

def __init__(self, **kwargs):
def __init__(self, time_options=None, **kwargs):
"""
Instantiate MuxComp and populate private members.
"""
super().__init__(**kwargs)
self.time_options = time_options

self._no_check_partials = not dymos_options['include_check_partials']

def setup(self):
"""
Add time-related I/O to the ParameterComp at setup, if provided.
"""
time_options = self.time_options

if time_options:
ti_val = self.time_options['initial_val']
td_val = self.time_options['duration_val']
units = self.time_options['units']

self.add_input(name='t_initial', val=ti_val, shape=(1,), units=units)
self.add_output(name='t_initial_val', val=ti_val, shape=(1,), units=units)
self.declare_partials(of='t_initial_val', wrt='t_initial', val=1.0)

self.add_input(name=f't_duration', val=td_val, shape=(1,), units=units)
self.add_output(name='t_duration_val', val=td_val, shape=(1,), units=units)
self.declare_partials(of='t_duration_val', wrt='t_duration', val=1.0)

def add_parameter(self, name, val=1.0, shape=None, output_name=None,
units=None, desc='', tags=None, input_tags=None, output_tags=None, input_shape_by_conn=False,
input_copy_shape=None, output_shape_by_conn=False, output_copy_shape=None,
distributed=None, res_units=None, lower=None, upper=None, ref=1.0, ref0=0.0, res_ref=1.0, ):
distributed=None, res_units=None, lower=None, upper=None, ref=1.0, ref0=0.0, res_ref=1.0,):
"""
Add an input/output pair for a variable to this component.
Expand Down
41 changes: 20 additions & 21 deletions dymos/transcriptions/explicit_shooting/explicit_shooting.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
from ..._options import options as dymos_options
from ...utils.misc import get_rate_units, CoerceDesvar
from ...utils.indexing import get_src_indices_by_row
from ...utils.introspection import get_promoted_vars, get_source_metadata, get_targets
from ...utils.introspection import get_promoted_vars, get_source_metadata, get_targets, get_target_metadata
from ...utils.constants import INF_BOUND
from ..common import TimeComp, TimeseriesOutputGroup, ControlGroup, PolynomialControlGroup
from ..common import TimeComp, TimeseriesOutputGroup, ControlGroup, PolynomialControlGroup, \
ParameterComp


class ExplicitShooting(TranscriptionBase):
Expand Down Expand Up @@ -152,7 +153,13 @@ def setup_time(self, phase):
time_comp = TimeComp(num_nodes=num_nodes, node_ptau=node_ptau,
node_dptau_dstau=node_dptau_dstau, units=time_units)

phase.add_subsystem('time', time_comp, promotes=['*'])
phase.add_subsystem('param_comp', subsys=ParameterComp(time_options=time_options),
promotes_inputs=['*'], promotes_outputs=['*'])

phase.add_subsystem('time', time_comp, promotes_outputs=['*'])

phase.connect('t_initial_val', ['time.t_initial', 'integrator.t_initial'])
phase.connect('t_duration_val', ['time.t_duration', 'integrator.t_duration'])

def configure_time(self, phase):
"""
Expand All @@ -175,12 +182,6 @@ def configure_time(self, phase):
time_comp.configure_io()

ode = phase._get_subsystem('ode')
ode_inputs = get_promoted_vars(ode, 'input')

phase.set_input_defaults('t_initial', val=0.0)
phase.set_input_defaults('t_duration', val=1.0)

phase.promotes('integrator', inputs=['t_initial', 't_duration'])

if not time_options['fix_initial']:
lb, ub = time_options['initial_bounds']
Expand Down Expand Up @@ -219,9 +220,10 @@ def configure_time(self, phase):
for name, targets in [('t_initial', time_options['t_initial_targets']),
('t_duration', time_options['t_duration_targets'])]:
for t in targets:
shape = ode_inputs[t]['shape']

if shape == (1,):
tgt_shape, _, static_tgt = get_target_metadata(ode, name=name,
user_targets=t,
user_units=time_options['units'])
if tgt_shape == (1,):
src_idxs = None
flat_src_idxs = None
src_shape = None
Expand All @@ -230,12 +232,9 @@ def configure_time(self, phase):
flat_src_idxs = True
src_shape = (1,)

phase.promotes('ode', inputs=[(t, name)], src_indices=src_idxs,
flat_src_indices=flat_src_idxs, src_shape=src_shape)
if targets:
phase.set_input_defaults(name=name,
val=np.ones((1,)),
units=t_units)
phase.connect(f'{name}_val', f'ode.{name}',
src_indices=src_idxs,
flat_src_indices=flat_src_idxs)

def setup_states(self, phase):
"""
Expand Down Expand Up @@ -564,6 +563,9 @@ def setup_defects(self, phase):
control_options=phase.control_options,
time_units=phase.time_options['units']))

if rate_cont:
phase.connect('t_duration_val', 'continuity_comp.t_duration')

def configure_defects(self, phase):
"""
Not used in ExplicitShooting.
Expand Down Expand Up @@ -604,9 +606,6 @@ def configure_defects(self, phase):
control_rates_to_enforce=control_rates_to_enforce,
control_rates2_to_enforce=control_rates2_to_enforce)

if any_rate_cnty:
phase.promotes('continuity_comp', inputs=['t_duration'])

def setup_timeseries_outputs(self, phase):
"""
Setup the timeseries for this transcription.
Expand Down
2 changes: 1 addition & 1 deletion dymos/transcriptions/pseudospectral/pseudospectral_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ def configure_defects(self, phase):
src_indices=(flattened_src_idxs,), flat_src_indices=True)

if any_control_rate_cnty:
phase.promotes('continuity_comp', inputs=['t_duration'])
phase.connect('t_duration_val', 'continuity_comp.t_duration')

for name, options in phase.control_options.items():
control_src_name = f'control_values:{name}'
Expand Down
34 changes: 7 additions & 27 deletions dymos/transcriptions/transcription_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from ..utils.misc import _unspecified
from ..utils.introspection import configure_states_introspection, get_promoted_vars, get_target_metadata, \
configure_states_discovery
from .._options import options as dymos_options


class TranscriptionBase(object):
Expand Down Expand Up @@ -84,13 +83,8 @@ def setup_time(self, phase):
if phase.time_options['t_duration_balance_options']:
self._implicit_duration = True

if not time_options['input_initial']:
phase.add_subsystem('time_extents', om.IndepVarComp(),
promotes_outputs=['*'])
else:
if not time_options['input_duration'] and not self._implicit_duration:
phase.add_subsystem('time_extents', om.IndepVarComp(),
promotes_outputs=['*'])
phase.add_subsystem('param_comp', subsys=ParameterComp(time_options=time_options),
promotes_inputs=['*'], promotes_outputs=['*'])

for ts_name, ts_options in phase._timeseries.items():
if t_name not in ts_options['outputs']:
Expand Down Expand Up @@ -120,20 +114,6 @@ def configure_time(self, phase):
user_units=time_options['units'],
user_shape='')

time_units = time_options['units']
indeps = []
default_vals = {'t_initial': phase.time_options['initial_val'],
't_duration': phase.time_options['duration_val']}

if not time_options['input_initial']:
indeps.append('t_initial')

if not time_options['input_duration'] and not self._implicit_duration:
indeps.append('t_duration')

for var in indeps:
phase.time_extents.add_output(var, val=default_vals[var], units=time_units)

if not (time_options['input_initial'] or time_options['fix_initial']):
lb, ub = time_options['initial_bounds']
lb = -INF_BOUND if lb is None else lb
Expand Down Expand Up @@ -221,7 +201,11 @@ def setup_polynomial_controls(self, phase):
polynomial_control_options=phase.polynomial_control_options,
time_units=phase.time_options['units'])
phase.add_subsystem('polynomial_control_group', subsys=sys,
promotes_inputs=['*'], promotes_outputs=['*'])
promotes_inputs=['polynomial_controls:*'],
promotes_outputs=['polynomial_control_values:*',
'polynomial_control_rates:*'])

phase.connect('t_duration_val', 'polynomial_control_group.t_duration')

prefix = 'polynomial_controls:' if phase.timeseries_options['use_prefix'] else ''
rate_prefix = 'polynomial_control_rates:' if phase.timeseries_options['use_prefix'] else ''
Expand Down Expand Up @@ -265,10 +249,6 @@ def setup_parameters(self, phase):
param_prefix = 'parameters:' if phase.timeseries_options['use_prefix'] else ''
include_params = phase.timeseries_options['include_parameters']

if phase.parameter_options:
param_comp = ParameterComp()
phase.add_subsystem('param_comp', subsys=param_comp, promotes_inputs=['*'], promotes_outputs=['*'])

for name, options in phase.parameter_options.items():
if (options['include_timeseries'] is None and include_params) or options['include_timeseries']:
for ts_name, ts_options in phase._timeseries.items():
Expand Down
2 changes: 2 additions & 0 deletions dymos/visualization/linkage/js/DmLinkageModelData.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class DmLinkageModelData extends ModelData {
* @returns {DmLinkageTreeNode} The newly-created object.
*/
_newNode(element, attribNames, parent) {
console.log(element)
console.log(attribNames)
return new DmLinkageTreeNode(element, attribNames, parent);
}
}
Loading

0 comments on commit 79ca083

Please sign in to comment.