Repository: OpenMDAO/dymos (language: python)

Issue:
Undefined function reference
### Description

The Dymos code base still has two references to the `get_target_metadata` function, which was removed in PR #656


### Example

https://github.com/search?q=repo%3AOpenMDAO%2Fdymos%20get_target_metadata&type=code


### Dymos Version

1.11.0-dev

### Relevant environment information

_No response_


Discussion / hints:

Fix patch:
diff --git a/dymos/transcriptions/__init__.py b/dymos/transcriptions/__init__.py
index 14426a392..d4e2a1fc1 100644
--- a/dymos/transcriptions/__init__.py
+++ b/dymos/transcriptions/__init__.py
@@ -3,4 +3,3 @@
 from .pseudospectral.gauss_lobatto import GaussLobatto
 from .pseudospectral.radau_pseudospectral import Radau
 from .pseudospectral.birkhoff import Birkhoff
-from .solve_ivp.solve_ivp import SolveIVP
diff --git a/dymos/transcriptions/solve_ivp/__init__.py b/dymos/transcriptions/solve_ivp/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/dymos/transcriptions/solve_ivp/components/__init__.py b/dymos/transcriptions/solve_ivp/components/__init__.py
deleted file mode 100644
index 19cc475a8..000000000
--- a/dymos/transcriptions/solve_ivp/components/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from .segment_state_mux_comp import SegmentStateMuxComp
-from .segment_simulation_comp import SegmentSimulationComp
-from .ode_integration_interface import ODEIntegrationInterface
-from .odeint_control_interpolation_comp import ODEIntControlInterpolationComp
-from .solve_ivp_control_group import SolveIVPControlGroup
-from .solve_ivp_timeseries_comp import SolveIVPTimeseriesOutputComp
-from .state_rate_collector_comp import StateRateCollectorComp
diff --git a/dymos/transcriptions/solve_ivp/components/ode_integration_interface.py b/dymos/transcriptions/solve_ivp/components/ode_integration_interface.py
deleted file mode 100644
index fcd2e2061..000000000
--- a/dymos/transcriptions/solve_ivp/components/ode_integration_interface.py
+++ /dev/null
@@ -1,163 +0,0 @@
-from collections import OrderedDict
-
-import numpy as np
-from .ode_integration_interface_system import ODEIntegrationInterfaceSystem
-import openmdao.api as om
-
-
-class ODEIntegrationInterface(object):
-    """
-    Given a system class, create a callable object with the signature required by scipy.integrate.ode.
-
-    Internally, this is accomplished by constructing an OpenMDAO problem using the ODE with
-    a single node.  The interface populates the values of the time, states, and controls,
-    and then calls `run_model()` on the problem.  The state rates generated by the ODE
-    are then returned back to scipy ode, which continues the integration.
-
-    Parameters
-    ----------
-    ode_class : class
-        The ODEClass belonging to the phase being simulated.
-    time_options : dict of {str: TimeOptionsDictionary}
-        The time options for the phase being simulated.
-    state_options : dict of {str: StateOptionsDictionary}
-        The state options for the phase being simulated.
-    control_options : dict of {str: ControlOptionsDictionary}
-        The control options for the phase being simulated.
-    parameter_options : dict of {str: ParameterOptionsDictionary}
-        The parameter options for the phase being simulated.
-    ode_init_kwargs : dict
-        Keyword argument dictionary passed to the ODE at initialization.
-    reports : bool or None or str or Sequence
-        The reports argument to be passed to the subproblems.  By default, no subproblem reports are generated.
-    """
-    def __init__(self, ode_class, time_options, state_options, control_options,
-                 parameter_options, ode_init_kwargs=None,
-                 reports=False):
-
-        # Get the state vector.  This isn't necessarily ordered
-        # so just pick the default ordering and go with it.
-        self.state_options = OrderedDict()
-        self.time_options = time_options
-        self.control_options = control_options
-        self.parameter_options = parameter_options
-        self.control_interpolants = {}
-        self.polynomial_control_interpolants = {}
-
-        pos = 0
-
-        for state, options in state_options.items():
-            self.state_options[state] = {'rate_source': options['rate_source'],
-                                         'pos': pos,
-                                         'shape': options['shape'],
-                                         'size': np.prod(options['shape']),
-                                         'units': options['units'],
-                                         'targets': options['targets']}
-            pos += self.state_options[state]['size']
-
-        self._state_vec = np.zeros(pos, dtype=float)
-        self._state_rate_vec = np.zeros(pos, dtype=float)
-
-        #
-        # Build odeint problem interface
-        #
-        self.prob = om.Problem(model=ODEIntegrationInterfaceSystem(ode_class=ode_class,
-                                                                   time_options=time_options,
-                                                                   state_options=state_options,
-                                                                   control_options=control_options,
-                                                                   parameter_options=parameter_options,
-                                                                   ode_init_kwargs=ode_init_kwargs),
-                               reports=reports)
-
-    def _unpack_state_vec(self, x):
-        """
-        Given the state vector in 1D, extract the values corresponding to
-        each state into the ode integrators problem states.
-
-        Parameters
-        ----------
-        x : np.array
-            The 1D state vector.
-
-        Returns
-        -------
-        None
-
-        """
-        for state_name, state_options in self.state_options.items():
-            pos = state_options['pos']
-            size = state_options['size']
-            self.prob[f'states:{state_name}'][0, ...] = x[pos:pos + size]
-
-    def _pack_state_rate_vec(self):
-        """
-        Pack the state rates into a 1D vector for use by scipy odeint.
-
-        Returns
-        -------
-        dXdt : np.array
-            The 1D state-rate vector.
-
-        """
-        for state_name, state_options in self.state_options.items():
-            pos = state_options['pos']
-            size = state_options['size']
-            self._state_rate_vec[pos:pos + size] = \
-                np.ravel(self.prob[f'state_rate_collector.state_rates:{state_name}_rate'])
-        return self._state_rate_vec
-
-    def set_interpolant(self, name, interp):
-        """
-        Set the control and/or polynomial control interpolants in the underlying system.
-
-        Parameters
-        ----------
-        name : str
-            The name of the control or polynomial control whose interpolant is being set.
-        interp : LagrangeBarycentricInterpolant
-            The LagrangeBarycentricInterpolant for the given control or polynomial control.
-        """
-        self.prob.model.set_interpolant(name, interp)
-
-    def setup_interpolant(self, name, x0, xf, f_j):
-        """
-        Setup the values to be interpolated in an existing interpolant.
-
-        Parameters
-        ----------
-        name : str
-            The name of the control or polynomial control.
-        x0 : float
-            The initial time (or independent variable) of the segment (for controls) or phase (for polynomial controls).
-        xf : float
-            The final time (or independent variable) of the segment (for controls) or phase (for polynomial controls).
-        f_j : float
-            The value of the control at the nodes in the segment or phase.
-        """
-        if name in self.prob.model.options['control_options']:
-            self.prob.model.setup_interpolant(name, x0, xf, f_j)
-        else:
-            raise KeyError(f'Unable to set control interpolant of unknown control: {name}')
-
-    def __call__(self, t, x):
-        """
-        The function interface used by scipy.ode
-
-        Parameters
-        ----------
-        t : float
-            The current time, t.
-        x : np.array
-            The 1D state vector.
-
-        Returns
-        -------
-        xdot : np.array
-            The 1D vector of state time-derivatives.
-        """
-        self.prob['t'] = t
-        self.prob['t_phase'] = t - self.prob['t_initial']
-        self._unpack_state_vec(x)
-        self.prob.run_model()
-        xdot = self._pack_state_rate_vec()
-        return xdot
diff --git a/dymos/transcriptions/solve_ivp/components/ode_integration_interface_system.py b/dymos/transcriptions/solve_ivp/components/ode_integration_interface_system.py
deleted file mode 100644
index 3cdefe60e..000000000
--- a/dymos/transcriptions/solve_ivp/components/ode_integration_interface_system.py
+++ /dev/null
@@ -1,190 +0,0 @@
-import numpy as np
-from .odeint_control_interpolation_comp import ODEIntControlInterpolationComp
-from .state_rate_collector_comp import StateRateCollectorComp
-from ....phase.options import TimeOptionsDictionary
-from ....utils.introspection import get_promoted_vars
-import openmdao.api as om
-
-
-class ODEIntegrationInterfaceSystem(om.Group):
-    """
-    Class definition for ODEIntegrationInterfaceSystem.
-
-    Parameters
-    ----------
-    **kwargs : dict
-        Dictionary of optional arguments.
-    """
-    def initialize(self):
-        """
-        Declare group options.
-        """
-        self.options.declare('time_options', types=TimeOptionsDictionary,
-                             desc='Time options for the phase')
-
-        self.options.declare('state_options', types=dict,
-                             desc='Dictionary of state names/options for the segments parent Phase')
-
-        self.options.declare('control_options', default=None, types=dict, allow_none=True,
-                             desc='Dictionary of control names/options for the segments parent Phase.')
-
-        self.options.declare('parameter_options', default=None, types=dict, allow_none=True,
-                             desc='Dictionary of parameter names/options for the segments '
-                                  'parent Phase.')
-
-        self.options.declare('ode_class',
-                             desc='System defining the ODE')
-
-        self.options.declare('ode_init_kwargs', types=dict, default={},
-                             desc='Keyword arguments provided when initializing the ODE System')
-
-    def setup(self):
-        """
-        Create the group hierarchy.
-        """
-        ivc = om.IndepVarComp()
-        time_options = self.options['time_options']
-        time_units = time_options['units']
-        ivc.add_output('t', val=0.0, units=time_units)
-        ivc.add_output('t_phase', val=-88.0, units=time_units)
-        ivc.add_output('t_initial', val=-99.0, units=time_units)
-        ivc.add_output('t_duration', val=-111.0, units=time_units)
-
-        self.add_subsystem('ivc', ivc, promotes_outputs=['*'])
-
-        self.connect('t', [f'ode.{tgt}' for tgt in time_options['targets']])
-
-        self.connect('t_phase', [f'ode.{tgt}' for tgt in time_options['time_phase_targets']])
-
-        self.connect('t_initial', [f'ode.{tgt}' for tgt in time_options['t_initial_targets']])
-
-        self.connect('t_duration', [f'ode.{tgt}' for tgt in time_options['t_duration_targets']])
-
-        if self.options['control_options']:
-            self._interp_comp = \
-                ODEIntControlInterpolationComp(time_units=time_units,
-                                               control_options=self.options['control_options'])
-
-            self.add_subsystem('indep_controls', self._interp_comp, promotes_outputs=['*'])
-            self.connect('t', ['indep_controls.time'])
-
-        # The ODE System
-        if self.options['ode_class'] is not None:
-            self.add_subsystem('ode', subsys=self.options['ode_class'](num_nodes=1,
-                                                                       **self.options['ode_init_kwargs']))
-
-        # The state rate collector comp
-        self.add_subsystem('state_rate_collector',
-                           StateRateCollectorComp(state_options=self.options['state_options'],
-                                                  time_units=time_options['units']))
-
-    def configure(self):
-        """
-        Issue connections after introspection.
-        """
-        ivc = self._get_subsystem('ivc')
-        ode = self._get_subsystem('ode')
-        ode_inputs = get_promoted_vars(ode, 'input')
-
-        # Configure states
-        for name, options in self.options['state_options'].items():
-            size = np.prod(options['shape'])
-            ivc.add_output(f'states:{name}',
-                           shape=(1, size),
-                           units=options['units'])
-
-            rate_src = self._get_rate_source_path(name)
-
-            self.connect(rate_src, f'state_rate_collector.state_rates_in:{name}_rate')
-
-            if options['targets']:
-                for tgt in options['targets']:
-                    tgt_shape = ode_inputs[tgt]['shape']
-                    if len(tgt_shape) == 1 and tgt_shape[0] == 1:
-                        src_idxs = np.arange(size, dtype=int).reshape(tgt_shape)
-                    else:
-                        src_idxs = np.arange(size, dtype=int).reshape((1,) + tgt_shape)
-                    self.connect(f'states:{name}', f'ode.{tgt}',
-                                 src_indices=(src_idxs,), flat_src_indices=True)
-
-        # Configure controls
-        if self.options['control_options']:
-            for name, options in self.options['control_options'].items():
-                if options['targets']:
-                    self.connect(f'controls:{name}',
-                                 [f'ode.{tgt}' for tgt in options['targets']])
-                if options['rate_targets']:
-                    self.connect(f'control_rates:{name}_rate',
-                                 [f'ode.{tgt}' for tgt in options['rate_targets']])
-                if options['rate2_targets']:
-                    self.connect(f'control_rates:{name}_rate2',
-                                 [f'ode.{tgt}' for tgt in options['rate2_targets']])
-
-        # Parameters
-        if self.options['parameter_options']:
-            for name, options in self.options['parameter_options'].items():
-                ivc.add_output(f'parameters:{name}', shape=options['shape'], units=options['units'])
-                if options['targets']:
-                    self.connect(f'parameters:{name}',
-                                 [f'ode.{tgt}' for tgt in options['targets']])
-
-    def _get_rate_source_path(self, state_var):
-        var = self.options['state_options'][state_var]['rate_source']
-        time_name = self.options['time_options']['name']
-
-        rate_path = f'ode.{var}'
-
-        if var == time_name:
-            rate_path = 't'
-        elif var == f'{time_name}_phase':
-            rate_path = 't_phase'
-        elif self.options['state_options'] is not None and var in self.options['state_options']:
-            rate_path = f'states:{var}'
-        elif self.options['control_options'] is not None and var in self.options['control_options']:
-            rate_path = f'controls:{var}'
-        elif self.options['parameter_options'] is not None and var in self.options['parameter_options']:
-            rate_path = f'parameters:{var}'
-        elif var.endswith('_rate') and self.options['control_options'] is not None and \
-                var[:-5] in self.options['control_options']:
-            rate_path = f'control_rates:{var}'
-        elif var.endswith('_rate2') and self.options['control_options'] is not None and \
-                var[:-6] in self.options['control_options']:
-            rate_path = f'control_rates:{var}'
-
-        return rate_path
-
-    def set_interpolant(self, name, interp):
-        """
-        Set the control and/or polynomial control interpolants in the underlying system.
-
-        Parameters
-        ----------
-        name : str
-            The name of the control or polynomial control whose interpolant is being set.
-        interp : LagrangeBarycentricInterpolant
-            The LagrangeBarycentricInterpolant for the given control or polynomial control.
-        """
-        if name in self.options['control_options']:
-            self._interp_comp.options['control_interpolants'][name] = interp
-        else:
-            raise KeyError(f'Unable to set control interpolant of unknown control: {name}')
-
-    def setup_interpolant(self, name, x0, xf, f_j):
-        """
-        Setup the values to be interpolated in an existing interpolant.
-
-        Parameters
-        ----------
-        name : str
-            The name of the control or polynomial control.
-        x0 : float
-            The initial time (or independent variable) of the segment (for controls).
-        xf : float
-            The final time (or independent variable) of the segment (for controls).
-        f_j : float
-            The value of the control at the nodes in the segment or phase.
-        """
-        if name in self.options['control_options']:
-            self._interp_comp.options['control_interpolants'][name].setup(x0, xf, f_j)
-        else:
-            raise KeyError(f'Unable to setup control interpolant of unknown control: {name}')
diff --git a/dymos/transcriptions/solve_ivp/components/odeint_control_interpolation_comp.py b/dymos/transcriptions/solve_ivp/components/odeint_control_interpolation_comp.py
deleted file mode 100644
index 475c8c4e1..000000000
--- a/dymos/transcriptions/solve_ivp/components/odeint_control_interpolation_comp.py
+++ /dev/null
@@ -1,78 +0,0 @@
-from dymos.utils.misc import get_rate_units
-import openmdao.api as om
-
-
-class ODEIntControlInterpolationComp(om.ExplicitComponent):
-    """
-    Provides the interpolated value and rate of a control variable during explicit integration.
-
-    For each control handled by ODEIntControlInterpolationComp, the user must provide an object
-    with methods `eval(t)` and `eval_deriv(t)` which return the interpolated value and
-    derivative of the control at time `t`, respectively.
-
-    Parameters
-    ----------
-    **kwargs : dict
-        Dictionary of optional arguments.
-    """
-    def initialize(self):
-        """
-        Declare component options.
-        """
-        self.options.declare('time_units', default='s', allow_none=True, types=str,
-                             desc='Units of time')
-        self.options.declare('control_options', types=dict, allow_none=True, default=None,
-                             desc='Dictionary of options for the dynamic controls')
-        self.options.declare('control_interpolants', types=dict, allow_none=True, default={},
-                             desc='Dictionary of interpolants for the dynamic controls',
-                             recordable=False)
-        self.options.declare('polynomial_control_interpolants', types=dict, allow_none=True,
-                             default={}, recordable=False,
-                             desc='Dictionary of interpolants for the polynomial controls')
-
-    def setup(self):
-        """
-        Create inputs/outputs for this component.
-        """
-        time_units = self.options['time_units']
-
-        self.add_input('time', val=1.0, units=time_units)
-
-        for control_name, options in self.options['control_options'].items():
-            shape = options['shape']
-            units = options['units']
-            rate_units = get_rate_units(units, time_units, deriv=1)
-            rate2_units = get_rate_units(units, time_units, deriv=2)
-
-            self.add_output('controls:{0}'.format(control_name), shape=shape, units=units)
-
-            self.add_output('control_rates:{0}_rate'.format(control_name), shape=shape,
-                            units=rate_units)
-
-            self.add_output('control_rates:{0}_rate2'.format(control_name), shape=shape,
-                            units=rate2_units)
-
-    def compute(self, inputs, outputs):
-        """
-        Compute component outputs.
-
-        Parameters
-        ----------
-        inputs : `Vector`
-            `Vector` containing inputs.
-        outputs : `Vector`
-            `Vector` containing outputs.
-        """
-        time = inputs['time']
-
-        for name in self.options['control_options']:
-            if name not in self.options['control_interpolants']:
-                raise ValueError('No interpolant has been specified for {0}'.format(name))
-
-            interp = self.options['control_interpolants'][name]
-
-            outputs['controls:{0}'.format(name)] = interp.eval(time)
-
-            outputs['control_rates:{0}_rate'.format(name)] = interp.eval_deriv(time)
-
-            outputs['control_rates:{0}_rate2'.format(name)] = interp.eval_deriv(time, der=2)
diff --git a/dymos/transcriptions/solve_ivp/components/segment_simulation_comp.py b/dymos/transcriptions/solve_ivp/components/segment_simulation_comp.py
deleted file mode 100644
index 4c05ee9a7..000000000
--- a/dymos/transcriptions/solve_ivp/components/segment_simulation_comp.py
+++ /dev/null
@@ -1,256 +0,0 @@
-"""
-SimulationPhase is an instance that resembles a Phase in structure but is intended for
-use with scipy.solve_ivp to verify the accuracy of the implicit solutions of Dymos.
-"""
-import numpy as np
-
-from scipy.integrate import solve_ivp
-
-import openmdao.api as om
-from ....utils.interpolate import LagrangeBarycentricInterpolant
-from ....utils.lgl import lgl
-from .ode_integration_interface import ODEIntegrationInterface
-from ....phase.options import TimeOptionsDictionary, SimulateOptionsDictionary
-
-
-class SegmentSimulationComp(om.ExplicitComponent):
-    """
-    Class definition for SegmentSimulationComp.
-
-    SegmentSimulationComp is a component which, given values for time, states, and controls
-    within a given segment, explicitly simulates the segment using scipy.integrate.solve_ivp.
-
-    The resulting states are captured at all nodes within the segment.
-
-    Parameters
-    ----------
-    **kwargs : dict
-        Dictionary of optional arguments.
-    """
-    def initialize(self):
-        """
-        Declare component options.
-        """
-        self.options.declare('index', desc='the index of this segment in the parent phase.')
-
-        self.options.declare('grid_data', desc='the grid data of the corresponding phase.')
-
-        self.options.declare('simulate_options', types=SimulateOptionsDictionary,
-                             desc='The simulation options of the parent phase.')
-
-        self.options.declare('ode_class', recordable=False,
-                             desc='System defining the ODE')
-
-        self.options.declare('ode_init_kwargs', types=dict, default={},
-                             desc='Keyword arguments provided when initializing the ODE System')
-
-        self.options.declare('time_options', types=TimeOptionsDictionary,
-                             desc='Time options for the phase')
-
-        self.options.declare('state_options', types=dict,
-                             desc='Dictionary of state names/options for the segments parent Phase')
-
-        self.options.declare('control_options', default=None, types=dict, allow_none=True,
-                             desc='Dictionary of control names/options for the segments parent Phase.')
-
-        self.options.declare('parameter_options', default=None, types=dict, allow_none=True,
-                             desc='Dictionary of parameter names/options for the segments '
-                                  'parent Phase.')
-
-        self.options.declare('ode_integration_interface', default=None, allow_none=True,
-                             types=ODEIntegrationInterface, recordable=False,
-                             desc='The instance of the ODE integration interface used to provide '
-                                  'the ODE to scipy.integrate.solve_ivp in the segment.  If None,'
-                                  ' a new one will be instantiated for this segment.')
-
-        self.options.declare('output_nodes_per_seg', default=None, types=(int,), allow_none=True,
-                             desc='If None, results are provided at the all nodes within each'
-                                  'segment.  If an int (n) then results are provided at n '
-                                  'equally distributed points in time within each segment.')
-
-        self.options.declare('reports', default=False, desc='Reports setting for the subproblem.')
-
-        self.recording_options['options_excludes'] = ['ode_integration_interface']
-
-    def configure_io(self):
-        """
-        I/O creation is delayed until configure so we can determine variable shape and units.
-        """
-        idx = self.options['index']
-        gd = self.options['grid_data']
-
-        if self.options['output_nodes_per_seg'] is None:
-            nnps_i = gd.subset_num_nodes_per_segment['all'][idx]
-        else:
-            nnps_i = self.options['output_nodes_per_seg']
-
-        # Number of control discretization nodes per segment
-        ncdsps = gd.subset_num_nodes_per_segment['control_disc'][idx]
-
-        # Indices of the control disc nodes belonging to the current segment
-        control_disc_seg_idxs = gd.subset_segment_indices['control_disc'][idx]
-
-        # Segment tau values for the control disc nodes in the phase
-        control_disc_stau = gd.node_stau[gd.subset_node_indices['control_disc']]
-
-        # Segment tau values for the control disc nodes in the current segment
-        control_disc_seg_stau = control_disc_stau[control_disc_seg_idxs[0]:control_disc_seg_idxs[1]]
-
-        if self.options['ode_integration_interface'] is None:
-            self.options['ode_integration_interface'] = ODEIntegrationInterface(
-                ode_class=self.options['ode_class'],
-                time_options=self.options['time_options'],
-                state_options=self.options['state_options'],
-                control_options=self.options['control_options'],
-                parameter_options=self.options['parameter_options'],
-                ode_init_kwargs=self.options['ode_init_kwargs'],
-                reports=self.options['reports'])
-
-        self.add_input(name=self.options['time_options']['name'], val=np.ones(nnps_i),
-                       units=self.options['time_options']['units'],
-                       desc='Time at all nodes within the segment.')
-
-        self.add_input(name='t_phase', val=np.ones(nnps_i),
-                       units=self.options['time_options']['units'],
-                       desc='Phase elapsed time at all nodes within the segment.')
-
-        self.add_input(name='t_initial', val=0.0, units=self.options['time_options']['units'],
-                       desc='Initial time value in the phase.')
-
-        self.add_input(name='t_duration', val=1.0, units=self.options['time_options']['units'],
-                       desc='Total time duration of the phase.')
-
-        # Setup the initial state vector for integration
-        self.state_vec_size = 0
-        for name, options in self.options['state_options'].items():
-            self.state_vec_size += np.prod(options['shape'])
-            self.add_input(name='initial_states:{0}'.format(name), val=np.ones((1,) + options['shape']),
-                           units=options['units'], desc='initial values of state {0} '
-                                                        'in the segment'.format(name))
-            self.add_output(name='states:{0}'.format(name),
-                            val=np.ones((nnps_i,) + options['shape']),
-                            units=options['units'],
-                            desc='Values of state {0} at all nodes in the segment.'.format(name))
-
-        self.initial_state_vec = np.zeros(self.state_vec_size)
-
-        self.options['ode_integration_interface'].prob.setup(check=False)
-
-        # Setup the control interpolants
-        if self.options['control_options']:
-            for name, options in self.options['control_options'].items():
-                if options['control_type'] == 'full':
-                    self.add_input(name='controls:{0}'.format(name),
-                                   val=np.ones(((ncdsps,) + options['shape'])),
-                                   units=options['units'],
-                                   desc='Values of control {0} at control discretization '
-                                        'nodes within the segment.'.format(name))
-                    interp = LagrangeBarycentricInterpolant(control_disc_seg_stau, options['shape'])
-                    self.options['ode_integration_interface'].set_interpolant(name, interp)
-                else:
-                    poly_control_disc_ptau, _ = lgl(options['order'] + 1)
-                    self.add_input(name='controls:{0}'.format(name),
-                                   val=np.ones(((options['order'] + 1,) + options['shape'])),
-                                   units=options['units'],
-                                   desc='Values of polynomial control {0} at control discretization '
-                                        'nodes within the phase.'.format(name))
-                    interp = LagrangeBarycentricInterpolant(poly_control_disc_ptau, options['shape'])
-                    self.options['ode_integration_interface'].set_interpolant(name, interp)
-
-        self.declare_partials(of='*', wrt='*', method='fd')
-
-    def compute(self, inputs, outputs):
-        """
-        Compute component outputs.
-
-        Parameters
-        ----------
-        inputs : `Vector`
-            `Vector` containing inputs.
-        outputs : `Vector`
-            `Vector` containing outputs.
-        """
-        idx = self.options['index']
-        gd = self.options['grid_data']
-        iface_prob = self.options['ode_integration_interface'].prob
-        time_name = self.options['time_options']['name']
-
-        # Create the vector of initial state values
-        self.initial_state_vec[:] = 0.0
-        pos = 0
-        for name, options in self.options['state_options'].items():
-            size = np.prod(options['shape'])
-            self.initial_state_vec[pos:pos + size] = \
-                np.ravel(inputs['initial_states:{0}'.format(name)])
-            pos += size
-
-        # Setup the control interpolants
-        if self.options['control_options']:
-            t0_seg = inputs[time_name][0]
-            tf_seg = inputs[time_name][-1]
-            for name, options in self.options['control_options'].items():
-                if options['control_type'] == 'full':
-                    ctrl_vals = inputs[f'controls:{name}']
-                    self.options['ode_integration_interface'].setup_interpolant(name,
-                                                                                x0=t0_seg,
-                                                                                xf=tf_seg,
-                                                                                f_j=ctrl_vals)
-                else:
-                    t0_phase = inputs['t_initial']
-                    tf_phase = inputs['t_initial'] + inputs['t_duration']
-                    ctrl_vals = inputs[f'controls:{name}']
-                    self.options['ode_integration_interface'].setup_interpolant(name,
-                                                                                x0=t0_phase,
-                                                                                xf=tf_phase,
-                                                                                f_j=ctrl_vals)
-
-        # Set the values of t_initial and t_duration
-        iface_prob.set_val('t_initial',
-                           val=inputs['t_initial'],
-                           units=self.options['time_options']['units'])
-
-        iface_prob.set_val('t_duration',
-                           val=inputs['t_duration'],
-                           units=self.options['time_options']['units'])
-
-        # Set the values of the phase parameters
-        if self.options['parameter_options']:
-            for param_name, options in self.options['parameter_options'].items():
-                val = inputs['parameters:{0}'.format(param_name)]
-                iface_prob.set_val('parameters:{0}'.format(param_name),
-                                   val=val,
-                                   units=options['units'])
-
-        # Setup the evaluation times.
-        if self.options['output_nodes_per_seg'] is None:
-            # Output nodes given as subset, convert segment tau of nodes to time
-            i1, i2 = gd.subset_segment_indices['all'][idx, :]
-            indices = gd.subset_node_indices['all'][i1:i2]
-            nodes_eval = gd.node_stau[indices]  # evaluation nodes in segment tau space
-            t_initial = inputs['time'][0]
-            t_duration = inputs['time'][-1] - t_initial
-            t_eval = t_initial + 0.5 * (nodes_eval + 1) * t_duration
-        else:
-            # Output nodes given as number, linspace them across the segment
-            t_eval = np.linspace(inputs[time_name][0], inputs[time_name][-1],
-                                 self.options['output_nodes_per_seg'])
-
-        # Perform the integration using solve_ivp
-        sim_options = {key: val for key, val in self.options['simulate_options'].items()}
-
-        sol = solve_ivp(fun=self.options['ode_integration_interface'],
-                        t_span=(inputs[time_name][0], inputs[time_name][-1]),
-                        y0=self.initial_state_vec,
-                        t_eval=t_eval,
-                        **sim_options)
-
-        if not sol.success:
-            raise om.AnalysisError(f'solve_ivp failed: {sol.message} Dynamics changing '
-                                   f'too dramatically')
-
-        # Extract the solution
-        pos = 0
-        for name, options in self.options['state_options'].items():
-            size = np.prod(options['shape'])
-            outputs[f'states:{name}'] = sol.y[pos:pos+size, :].T
-            pos += size
diff --git a/dymos/transcriptions/solve_ivp/components/segment_state_mux_comp.py b/dymos/transcriptions/solve_ivp/components/segment_state_mux_comp.py
deleted file mode 100644
index 781b8f6bb..000000000
--- a/dymos/transcriptions/solve_ivp/components/segment_state_mux_comp.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import numpy as np
-
-import openmdao.api as om
-
-
-class SegmentStateMuxComp(om.ExplicitComponent):
-    """
-    Class definition for SegmentStateMuxComp.
-
-    Parameters
-    ----------
-    **kwargs : dict
-        Dictionary of optional arguments.
-    """
-    def initialize(self):
-        """
-        Declare component options.
-        """
-        self.options.declare('grid_data', desc='the grid data of the corresponding phase.')
-        self.options.declare('state_options', types=dict)
-
-        self.options.declare('output_nodes_per_seg', default=None, types=(int,), allow_none=True,
-                             desc='If None, results are provided at the all nodes within each'
-                                  'segment.  If an int (n) then results are provided at n '
-                                  'equally distributed points in time within each segment.')
-
-    def setup(self):
-        """
-        Define the independent variables as output variables.
-        """
-        gd = self.options['grid_data']
-        num_seg = gd.num_segments
-
-        if self.options['output_nodes_per_seg'] is None:
-            num_nodes = gd.subset_num_nodes['all']
-        else:
-            num_nodes = num_seg * self.options['output_nodes_per_seg']
-
-        for name, options in self.options['state_options'].items():
-            oname = f'states:{name}'
-
-            for i in range(num_seg):
-                if self.options['output_nodes_per_seg'] is None:
-                    nnps_i = gd.subset_num_nodes_per_segment['all'][i]
-                else:
-                    nnps_i = self.options['output_nodes_per_seg']
-
-                iname = f'segment_{i}_states:{name}'
-                shape = (nnps_i,) + options['shape']
-
-                self.add_input(name=iname, val=np.ones(shape), units=options['units'])
-
-                self.declare_partials(of=oname,  wrt=iname, method='fd')
-
-            self.add_output(name=oname, val=np.ones((num_nodes,) + options['shape']),
-                            units=options['units'])
-
-    def compute(self, inputs, outputs):
-        """
-        Compute component outputs.
-
-        Parameters
-        ----------
-        inputs : `Vector`
-            `Vector` containing inputs.
-        outputs : `Vector`
-            `Vector` containing outputs.
-        """
-        outputs.set_val(inputs.asarray())
diff --git a/dymos/transcriptions/solve_ivp/components/solve_ivp_control_group.py b/dymos/transcriptions/solve_ivp/components/solve_ivp_control_group.py
deleted file mode 100644
index 8547a1c02..000000000
--- a/dymos/transcriptions/solve_ivp/components/solve_ivp_control_group.py
+++ /dev/null
@@ -1,273 +0,0 @@
-import numpy as np
-from scipy.linalg import block_diag
-
-import openmdao.api as om
-
-from ...grid_data import GridData
-from dymos.utils.misc import get_rate_units
-from ....utils.lagrange import lagrange_matrices
-
-
-class SolveIVPControlInterpComp(om.ExplicitComponent):
-    """
-    Class definition for SolveIVPControlInterpComp.
-
-    Compute the approximated control values and rates given the values of a control at output nodes
-    and the approximated values at output nodes, given values at the control input nodes.
-
-    Parameters
-    ----------
-    **kwargs : dict
-        Dictionary of optional arguments.
-
-    Notes
-    -----
-    .. math::
-
-        u = \\left[ L \\right] u_d
-
-        \\dot{u} = \\frac{d\\tau_s}{dt} \\left[ D \\right] u_d
-
-        \\ddot{u} = \\left( \\frac{d\\tau_s}{dt} \\right)^2 \\left[ D_2 \\right] u_d
-
-    where
-    :math:`u_d` are the values of the control at the control discretization nodes,
-    :math:`u` are the values of the control at all nodes,
-    :math:`\\dot{u}` are the time-derivatives of the control at all nodes,
-    :math:`\\ddot{u}` are the second time-derivatives of the control at all nodes,
-    :math:`L` is the Lagrange interpolation matrix,
-    :math:`D` is the Lagrange differentiation matrix,
-    and :math:`\\frac{d\\tau_s}{dt}` is the ratio of segment duration in segment tau space
-    [-1 1] to segment duration in time.
-    """
-    def initialize(self):
-        """
-        Declare component options.
-        """
-        self.options.declare('control_options', types=dict,
-                             desc='Dictionary of options for the dynamic controls')
-        self.options.declare('time_units', default=None, allow_none=True, types=str,
-                             desc='Units of time')
-        self.options.declare('grid_data', types=GridData, desc='Container object for grid info')
-        self.options.declare('output_nodes_per_seg', default=None, types=(int,), allow_none=True,
-                             desc='If None, results are provided at the all nodes within each'
-                                  'segment.  If an int (n) then results are provided at n '
-                                  'equally distributed points in time within each segment.')
-
-        # Save the names of the dynamic controls/parameters
-        self._dynamic_names = []
-        self._input_names = {}
-        self._output_val_names = {}
-        self._output_val_all_names = {}
-        self._output_rate_names = {}
-        self._output_rate2_names = {}
-
-    def _configure_controls(self):
-        control_options = self.options['control_options']
-        num_nodes_all = self.num_nodes_all
-        num_nodes_output = self.num_nodes_output
-        num_control_input_nodes = self.options['grid_data'].subset_num_nodes['control_input']
-        time_units = self.options['time_units']
-
-        for name, options in control_options.items():
-            self._input_names[name] = f'controls:{name}'
-            self._output_val_all_names[name] = f'control_values_all:{name}'
-            self._output_val_names[name] = f'control_values:{name}'
-            self._output_rate_names[name] = f'control_rates:{name}_rate'
-            self._output_rate2_names[name] = f'control_rates:{name}_rate2'
-            shape = options['shape']
-            input_shape = (num_control_input_nodes,) + shape
-            all_shape = (num_nodes_all,) + shape
-            output_shape = (num_nodes_output,) + shape
-
-            units = options['units']
-            rate_units = get_rate_units(units, time_units)
-            rate2_units = get_rate_units(units, time_units, deriv=2)
-
-            self._dynamic_names.append(name)
-
-            self.add_input(self._input_names[name], val=np.ones(input_shape), units=units)
-
-            self.add_output(self._output_val_all_names[name], shape=all_shape, units=units)
-
-            self.add_output(self._output_val_names[name], shape=output_shape, units=units)
-
-            self.add_output(self._output_rate_names[name], shape=output_shape, units=rate_units)
-
-            self.add_output(self._output_rate2_names[name], shape=output_shape,
-                            units=rate2_units)
-
-    def configure_io(self):
-        """
-        I/O creation is delayed until configure so we can determine variable shape and units.
-        """
-        output_nodes_per_seg = self.options['output_nodes_per_seg']
-        time_units = self.options['time_units']
-        gd = self.options['grid_data']
-        num_seg = gd.num_segments
-        num_nodes_all = gd.subset_num_nodes['all']
-
-        if output_nodes_per_seg is None:
-            num_nodes_output = num_nodes_all
-        else:
-            num_nodes_output = num_seg * output_nodes_per_seg
-
-        self.add_input('dt_dstau', shape=num_nodes_output, units=time_units)
-
-        self.val_jacs = {}
-        self.rate_jacs = {}
-        self.rate2_jacs = {}
-        self.val_jac_rows = {}
-        self.val_jac_cols = {}
-        self.rate_jac_rows = {}
-        self.rate_jac_cols = {}
-        self.rate2_jac_rows = {}
-        self.rate2_jac_cols = {}
-        self.sizes = {}
-        self.num_nodes_all = num_nodes_all
-        self.num_nodes_output = num_nodes_output
-
-        num_disc_nodes = gd.subset_num_nodes['control_disc']
-        num_input_nodes = gd.subset_num_nodes['control_input']
-
-        # Find the indexing matrix that, multiplied by the values at the input nodes,
-        # gives the values at the discretization nodes
-        L_id = np.zeros((num_disc_nodes, num_input_nodes), dtype=float)
-        L_id[np.arange(num_disc_nodes, dtype=int),
-             gd.input_maps['dynamic_control_input_to_disc']] = 1.0
-
-        # Matrices L_do and D_do interpolate values and rates (respectively) at output nodes from
-        # values specified at control discretization nodes.
-        L_da, _ = gd.phase_lagrange_matrices('control_disc', 'all')
-
-        L_do_blocks = []
-        D_do_blocks = []
-
-        for iseg in range(num_seg):
-            i1, i2 = gd.subset_segment_indices['control_disc'][iseg, :]
-            indices = gd.subset_node_indices['control_disc'][i1:i2]
-            nodes_given = gd.node_stau[indices]
-
-            if output_nodes_per_seg is None:
-                i1, i2 = gd.subset_segment_indices['all'][iseg, :]
-                indices = gd.subset_node_indices['all'][i1:i2]
-                nodes_eval = gd.node_stau[indices]
-            else:
-                nodes_eval = np.linspace(-1, 1, output_nodes_per_seg)
-
-            L_block, D_block = lagrange_matrices(nodes_given, nodes_eval)
-
-            L_do_blocks.append(L_block)
-            D_do_blocks.append(D_block)
-
-        L_do = block_diag(*L_do_blocks)
-        D_do = block_diag(*D_do_blocks)
-
-        self.L = np.dot(L_do, L_id)
-        self.L_all = np.dot(L_da, L_id)
-        self.D = np.dot(D_do, L_id)
-
-        # Matrix D_dd interpolates rates at discretization nodes from values given at control
-        # discretization nodes.
-        _, D_dd = gd.phase_lagrange_matrices('control_disc', 'control_disc')
-
-        # Matrix D2 provides second derivatives at output nodes given values at input nodes.
-        self.D2 = np.dot(D_do, np.dot(D_dd, L_id))
-
-        self._configure_controls()
-
-        self.set_check_partial_options('*', method='cs')
-
-    def compute(self, inputs, outputs):
-        """
-        Compute component outputs.
-
-        Parameters
-        ----------
-        inputs : `Vector`
-            `Vector` containing inputs.
-        outputs : `Vector`
-            `Vector` containing outputs.
-        """
-        control_options = self.options['control_options']
-
-        for name in control_options:
-
-            u = inputs[self._input_names[name]]
-
-            a = np.tensordot(self.D, u, axes=(1, 0)).T
-            b = np.tensordot(self.D2, u, axes=(1, 0)).T
-
-            # divide each "row" by dt_dstau or dt_dstau**2
-            outputs[self._output_val_names[name]] = np.tensordot(self.L, u, axes=(1, 0))
-            outputs[self._output_val_all_names[name]] = np.tensordot(self.L_all, u, axes=(1, 0))
-            outputs[self._output_rate_names[name]] = (a / inputs['dt_dstau']).T
-            outputs[self._output_rate2_names[name]] = (b / inputs['dt_dstau'] ** 2).T
-
-
-class SolveIVPControlGroup(om.Group):
-    """
-    Group containing the SolveIVPControlInterpComp.
-
-    Parameters
-    ----------
-    **kwargs : dict
-        Dictionary of optional arguments.
-    """
-    def initialize(self):
-        """
-        Declare group options.
-        """
-        self.options.declare('control_options', types=dict,
-                             desc='Dictionary of options for the dynamic controls')
-        self.options.declare('time_units', default=None, allow_none=True, types=str,
-                             desc='Units of time')
-        self.options.declare('grid_data', types=GridData, desc='Container object for grid info')
-        self.options.declare('output_nodes_per_seg', default=None, types=(int,), allow_none=True,
-                             desc='If None, results are provided at the all nodes within each'
-                                  'segment.  If an int (n) then results are provided at n '
-                                  'equally distributed points in time within each segment.')
-
-    def setup(self):
-        """
-        Build the group hierarchy.
-        """
-        gd = self.options['grid_data']
-        control_options = self.options['control_options']
-        time_units = self.options['time_units']
-
-        if len(control_options) < 1:
-            return
-
-        opt_controls = [name for (name, opts) in control_options.items() if opts['opt']]
-
-        if len(opt_controls) > 0:
-            self.add_subsystem('indep_controls', subsys=om.IndepVarComp(),
-                               promotes_outputs=['*'])
-
-        self.add_subsystem(
-            'control_interp_comp',
-            subsys=SolveIVPControlInterpComp(time_units=time_units, grid_data=gd,
-                                             control_options=control_options,
-                                             output_nodes_per_seg=self.options['output_nodes_per_seg']),
-            promotes_inputs=['*'],
-            promotes_outputs=['*'])
-
-    def configure_io(self):
-        """
-        I/O creation is delayed until configure so we can determine variable shape and units.
-        """
-        gd = self.options['grid_data']
-        control_options = self.options['control_options']
-
-        self.control_interp_comp.configure_io()
-
-        for name, options in control_options.items():
-            if options['opt']:
-                num_input_nodes = gd.subset_num_nodes['control_input']
-
-                val = np.outer(np.ones(num_input_nodes), options['val'])
-                self.indep_controls.add_output(name=f'controls:{name}',
-                                               val=val,
-                                               shape=(num_input_nodes, np.prod(options['shape'])),
-                                               units=options['units'])
diff --git a/dymos/transcriptions/solve_ivp/components/solve_ivp_timeseries_comp.py b/dymos/transcriptions/solve_ivp/components/solve_ivp_timeseries_comp.py
deleted file mode 100644
index 8b27a60b5..000000000
--- a/dymos/transcriptions/solve_ivp/components/solve_ivp_timeseries_comp.py
+++ /dev/null
@@ -1,177 +0,0 @@
-import numpy as np
-import openmdao.api as om
-import scipy.sparse as sp
-
-from openmdao.utils.units import unit_conversion
-
-from ....utils.lagrange import lagrange_matrices
-from ...common import TimeseriesOutputComp
-
-
-class SolveIVPTimeseriesOutputComp(TimeseriesOutputComp):
-    """
-    Class definition for SolveIVPTimeseriesOutputComp.
-
-    Parameters
-    ----------
-    **kwargs : dict
-        Dictionary of optional arguments.
-    """
-    def initialize(self):
-        """
-        Declare component options.
-        """
-        super().initialize()
-
-        self.options.declare('output_nodes_per_seg', default=None, types=(int,), allow_none=True,
-                             desc='If None, results are provided at the all nodes within each'
-                                  'segment.  If an int (n) then results are provided at n '
-                                  'equally distributed points in time within each segment.')
-
-    def setup(self):
-        """
-        Define the independent variables as output variables.
-        """
-        igd = self.options['input_grid_data']
-
-        if self.options['output_nodes_per_seg'] is None:
-            self.output_num_nodes = self.input_num_nodes = igd.num_nodes
-            ogd = igd
-        else:
-            self.output_num_nodes = self.input_num_nodes = igd.num_segments * self.options['output_nodes_per_seg']
-            output_nodes_stau = np.linspace(-1, 1, self.options['output_nodes_per_seg'])
-            output_nodes_ptau = igd.node_ptau[igd.subset_node_indices['all']]
-            ogd = None
-
-        # Build the interpolation matrix which maps from the input grid to the output grid.
-        # Rather than a single phase-wide interpolating polynomial, map each segment.
-        # To do this, find the nodes in the output grid which fall in each segment of the input
-        # grid.  Then build a Lagrange interpolating polynomial for that segment
-        # L_blocks = []
-        D_blocks = []
-
-        for iseg in range(igd.num_segments):
-            # Create the interpolation matrix and add it to the blocks
-            if self.options['output_nodes_per_seg'] is None:
-                i1, i2 = igd.segment_indices[iseg]
-                iptau_segi = igd.node_ptau[i1:i2]
-                istau_segi = igd.node_stau[i1:i2]
-
-                # The indices of the output grid that fall within this segment of the input grid
-                if ogd is igd:
-                    optau_segi = iptau_segi
-                else:
-                    ptau_hi = igd.segment_ends[iseg + 1]
-                    if iseg < igd.num_segments - 1:
-                        optau_segi = output_nodes_ptau[output_nodes_ptau <= ptau_hi]
-                    else:
-                        optau_segi = output_nodes_ptau
-
-                    # Remove the captured nodes so we don't accidentally include them again
-                    output_nodes_ptau = output_nodes_ptau[len(optau_segi):]
-
-                # # Now get the output nodes which fall in iseg in iseg's segment tau space.
-                ostau_segi = 2.0 * (optau_segi - iptau_segi[0]) / (iptau_segi[-1] - iptau_segi[0]) - 1
-
-                _, D = lagrange_matrices(istau_segi, ostau_segi)
-            else:
-                _, D = lagrange_matrices(output_nodes_stau, output_nodes_stau)
-            D_blocks.append(D)
-
-        self.differentiation_matrix = sp.block_diag(D_blocks, format='csr')
-
-        for (name, kwargs) in self._vars:
-            units = kwargs['units']
-            desc = kwargs['desc']
-            shape = kwargs['shape']
-            src = kwargs['src']
-            rate = kwargs['rate']
-            self._add_output_configure(name, units, shape, desc, src, rate)
-
-        self.add_input('dt_dstau', shape=(self.output_num_nodes,), units=self.options['time_units'])
-
-    def _add_output_configure(self, name, units, shape, desc, src=None, rate=False):
-        """
-        Add a single timeseries output.
-
-        Can be called by parent groups in configure.
-
-        Parameters
-        ----------
-        name : str
-            name of the variable in this component's namespace.
-        shape : int or tuple or list or None
-            Shape of this variable, only required if val is not an array.
-            Default is None.
-        units : str or None
-            Units in which the output variables will be provided to the component during execution.
-            Default is None, which means it has no units.
-        desc : str
-            description of the timeseries output variable.
-        src : str or None
-            The source of the timeseries output.
-        rate : bool
-            If True, timeseries output is a rate.
-        """
-        if rate:
-            om.issue_warning(f'Timeseries rate outputs not currently supported in simulate: {name} being skipped.')
-
-        input_num_nodes = self.input_num_nodes
-        output_num_nodes = self.output_num_nodes
-        added_source = False
-
-        if name in self._vars:
-            return False
-
-        if src in self._sources:
-            # If we're already pulling the source into this timeseries, use that as the
-            # input for this output.
-            input_name = self._sources[src]
-            input_units = self._units[input_name]
-        else:
-            input_name = f'input_values:{name}'
-            self.add_input(input_name,
-                           shape=(input_num_nodes,) + shape,
-                           units=units, desc=desc)
-            self._sources[src] = input_name
-            input_units = self._units[input_name] = units
-            added_source = True
-
-        output_name = name
-        self.add_output(output_name, shape=(output_num_nodes,) + shape, units=units, desc=desc)
-
-        self._vars[name] = (input_name, output_name, shape, rate)
-
-        # There's a chance that the input for this output was pulled from another variable with
-        # different units, so account for that with a conversion.
-        if input_units is None or units is None:
-            pass
-        else:
-            scale, offset = unit_conversion(input_units, units)
-            self._conversion_factors[output_name] = scale, offset
-
-        return added_source
-
-    def compute(self, inputs, outputs):
-        """
-        Compute component outputs.
-
-        Parameters
-        ----------
-        inputs : `Vector`
-            `Vector` containing inputs.
-        outputs : `Vector`
-            `Vector` containing outputs.
-        """
-        dt_dstau = inputs['dt_dstau'][:, np.newaxis]
-        for (input_name, output_name, _, is_rate) in self._vars.values():
-            interp_vals = inputs[input_name]
-
-            if is_rate:
-                interp_vals = self.differentiation_matrix.dot(interp_vals) / dt_dstau
-
-            if output_name in self._conversion_factors:
-                scale, offset = self._conversion_factors[output_name]
-                outputs[output_name] = scale * (interp_vals + offset)
-            else:
-                outputs[output_name] = interp_vals
diff --git a/dymos/transcriptions/solve_ivp/components/state_rate_collector_comp.py b/dymos/transcriptions/solve_ivp/components/state_rate_collector_comp.py
deleted file mode 100644
index 4c96bccaa..000000000
--- a/dymos/transcriptions/solve_ivp/components/state_rate_collector_comp.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import numpy as np
-from ....utils.misc import get_rate_units
-from ...._options import options as dymos_options
-import openmdao.api as om
-
-
-class StateRateCollectorComp(om.ExplicitComponent):
-    """
-    Class definition for StateRateCollectorComp.
-
-    Collects the state rates and outputs them in the units specified in the state options.
-    For explicit integration this is necessary when the output providing the state rate has
-    different units than those defined in the state_options/time_options.
-
-    Parameters
-    ----------
-    **kwargs : dict
-        Dictionary of optional arguments.
-    """
-    def initialize(self):
-        """
-        Declare component options.
-        """
-        self.options.declare(
-            'state_options', types=dict,
-            desc='Dictionary of options for the ODE state variables.')
-        self.options.declare(
-            'time_units', default=None, allow_none=True, types=str,
-            desc='Units of time')
-
-        self._no_check_partials = not dymos_options['include_check_partials']
-
-    def setup(self):
-        """
-        Create inputs/outputs on this component.
-        """
-        state_options = self.options['state_options']
-        time_units = self.options['time_units']
-
-        for name, options in state_options.items():
-            shape = options['shape']
-            units = options['units']
-
-            rate_units = get_rate_units(units, time_units)
-
-            self.add_input(f'state_rates_in:{name}_rate', val=np.ones(shape), units=rate_units)
-            self.add_output(f'state_rates:{name}_rate', shape=shape, units=rate_units)
-
-    def compute(self, inputs, outputs):
-        """
-        Compute component outputs.
-
-        Parameters
-        ----------
-        inputs : `Vector`
-            `Vector` containing inputs.
-        outputs : `Vector`
-            `Vector` containing outputs.
-        """
-        outputs.set_val(inputs.asarray())
diff --git a/dymos/transcriptions/solve_ivp/solve_ivp.py b/dymos/transcriptions/solve_ivp/solve_ivp.py
deleted file mode 100644
index f68727e1d..000000000
--- a/dymos/transcriptions/solve_ivp/solve_ivp.py
+++ /dev/null
@@ -1,629 +0,0 @@
-import numpy as np
-
-import openmdao.api as om
-
-from ..transcription_base import TranscriptionBase
-from .components import SegmentSimulationComp, SegmentStateMuxComp, \
-    SolveIVPControlGroup, SolveIVPTimeseriesOutputComp
-from ..common import TimeComp, TimeseriesOutputGroup
-from ...utils.misc import get_rate_units
-from ...utils.introspection import get_promoted_vars, get_targets, get_source_metadata
-from ...utils.indexing import get_src_indices_by_row
-
-
-class SolveIVP(TranscriptionBase):
-    """
-    The SolveIVP Transcription class.
-
-    SolveIVP transcription in Dymos uses the scipy.simulate.solve_ivp method to explicitly integrate
-    the states from the phase initial time to the phase final time.
-
-    SolveIVP transcription does not currently support optimization since it does not propagate
-    analytic derivatives through the ODE.
-
-    Parameters
-    ----------
-    grid_data : GridData
-        Grid data for this phases.
-    **kwargs : dict
-        Dictionary of optional arguments.
-    """
-    def __init__(self, grid_data=None, **kwargs):
-        om.issue_warning('The SolveIVP transcription is deprecated. The simulate methods in Dymos now uses '
-                         'the ExplicitShooting transcription without derivative propagation to achieve the same'
-                         'functionality. SolveIVP will be removed in a future version of Dymos.',
-                         category=om.OMDeprecationWarning)
-        super(SolveIVP, self).__init__(**kwargs)
-        self.grid_data = grid_data
-        self._rhs_source = 'ode'
-
-    def initialize(self):
-        """
-        Declare transcription options.
-        """
-        super(SolveIVP, self).initialize()
-
-        self.options.declare('output_nodes_per_seg', default=None, types=(int,), allow_none=True,
-                             desc='If None, results are provided at the all nodes within each'
-                                  'segment.  If an int (n) then results are provided at n '
-                                  'equally distributed points in time within each segment.')
-
-        self.options.declare('reports', default=False, desc='Reports setting for the subproblem.')
-
-    def init_grid(self):
-        """
-        Setup the GridData object for the Transcription.
-        """
-        pass
-
-    def setup_time(self, phase):
-        """
-        Setup the time component.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        time_options = phase.time_options
-        time_units = time_options['units']
-        num_seg = self.grid_data.num_segments
-        grid_data = self.grid_data
-        output_nodes_per_seg = self.options['output_nodes_per_seg']
-
-        super(SolveIVP, self).setup_time(phase)
-
-        if output_nodes_per_seg is None:
-            # Case 1:  Compute times at 'all' node set.
-            num_nodes = grid_data.num_nodes
-            node_ptau = grid_data.node_ptau
-            node_dptau_dstau = grid_data.node_dptau_dstau
-        else:
-            # Case 2:  Compute times at n equally distributed points per segment.
-            num_nodes = num_seg * output_nodes_per_seg
-            node_stau = np.linspace(-1, 1, output_nodes_per_seg)
-            node_ptau = np.empty(0, )
-            node_dptau_dstau = np.empty(0, )
-            # Append our nodes in phase tau space
-            for iseg in range(num_seg):
-                v0 = grid_data.segment_ends[iseg]
-                v1 = grid_data.segment_ends[iseg + 1]
-                node_ptau = np.concatenate((node_ptau, v0 + 0.5 * (node_stau + 1) * (v1 - v0)))
-                node_dptau_dstau = np.concatenate((node_dptau_dstau,
-                                                   0.5 * (v1 - v0) * np.ones_like(node_stau)))
-
-        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=['*'])
-
-    def configure_time(self, phase):
-        """
-        Configure the inputs/outputs on the time component.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        super(SolveIVP, self).configure_time(phase)
-        num_seg = self.grid_data.num_segments
-        grid_data = self.grid_data
-        output_nodes_per_seg = self.options['output_nodes_per_seg']
-        ode = self._get_ode(phase)
-        time_name = phase.time_options['name']
-
-        phase.time.configure_io()
-
-        for i in range(num_seg):
-            if output_nodes_per_seg is None:
-                i1, i2 = grid_data.subset_segment_indices['all'][i, :]
-                src_idxs = grid_data.subset_node_indices['all'][i1:i2]
-            else:
-                src_idxs = np.arange(i * output_nodes_per_seg, output_nodes_per_seg * (i + 1),
-                                     dtype=int)
-            phase.connect('t', f'segment_{i}.{time_name}', src_indices=src_idxs, flat_src_indices=True)
-            phase.connect('t_phase', f'segment_{i}.t_phase', src_indices=src_idxs,
-                          flat_src_indices=True)
-
-            phase.segments.promotes(f'segment_{i}', inputs=['t_initial', 't_duration'])
-
-        options = phase.time_options
-
-        # The tuples here are (name, user_specified_targets, dynamic)
-        for name, targets, dynamic in [('t', options['targets'], True),
-                                       ('t_phase', options['time_phase_targets'], True)]:
-            if targets:
-                phase.connect(name, [f'ode.{t}' for t in targets])
-
-        for name, targets, dynamic in [('t_initial', options['t_initial_targets'], False),
-                                       ('t_duration', options['t_duration_targets'], False)]:
-
-            shape, units, static_target = get_target_metadata(ode, name=name,
-                                                              user_targets=targets,
-                                                              user_units=options['units'],
-                                                              user_shape=(1,))
-            if shape == (1,):
-                src_idxs = None
-                flat_src_idxs = None
-                src_shape = None
-            else:
-                src_idxs = np.zeros(self.grid_data.subset_num_nodes['all'])
-                flat_src_idxs = True
-                src_shape = (1,)
-
-            for t in targets:
-                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=options['units'])
-
-        phase.connect('dt_dstau', 'timeseries.dt_dstau')
-
-    def setup_states(self, phase):
-        """
-        Setup the states for this transcription.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        phase.add_subsystem('indep_states', om.IndepVarComp(),
-                            promotes_outputs=['*'])
-
-    def configure_states(self, phase):
-        """
-        Configure state connections post-introspection.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        super().configure_states(phase)
-        num_seg = self.grid_data.num_segments
-
-        for state_name, options in phase.state_options.items():
-            phase.indep_states.add_output(f'initial_states:{state_name}',
-                                          val=np.ones(((1,) + options['shape'])),
-                                          units=options['units'])
-
-        for state_name, options in phase.state_options.items():
-            # Connect the initial state to the first segment
-            src_idxs = get_src_indices_by_row([0], options['shape'])
-
-            phase.connect(f'initial_states:{state_name}',
-                          f'segment_0.initial_states:{state_name}',
-                          src_indices=(src_idxs,), flat_src_indices=True)
-
-            phase.connect(f'segment_0.states:{state_name}',
-                          f'state_mux_comp.segment_0_states:{state_name}')
-
-            if options['targets']:
-                phase.connect(f'state_mux_comp.states:{state_name}',
-                              [f'ode.{t}' for t in options['targets']])
-
-            # Connect the final state in segment n to the initial state in segment n + 1
-            for i in range(1, num_seg):
-                if self.options['output_nodes_per_seg'] is None:
-                    nnps_i = self.grid_data.subset_num_nodes_per_segment['all'][i]
-                else:
-                    nnps_i = self.options['output_nodes_per_seg']
-
-                src_idxs = get_src_indices_by_row([nnps_i-1], shape=options['shape'])
-                phase.connect(f'segment_{i - 1}.states:{state_name}',
-                              f'segment_{i}.initial_states:{state_name}',
-                              src_indices=(src_idxs,), flat_src_indices=True)
-
-                phase.connect(f'segment_{i}.states:{state_name}',
-                              f'state_mux_comp.segment_{i}_states:{state_name}')
-
-    def setup_ode(self, phase):
-        """
-        Setup the ode for this transcription.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        gd = self.grid_data
-        num_seg = gd.num_segments
-
-        segments_group = phase.add_subsystem(name='segments', subsys=om.Group(),
-                                             promotes_outputs=['*'], promotes_inputs=['*'])
-
-        for i in range(num_seg):
-            seg_i_comp = SegmentSimulationComp(
-                index=i,
-                simulate_options=phase.simulate_options,
-                grid_data=self.grid_data,
-                ode_class=phase.options['ode_class'],
-                ode_init_kwargs=phase.options['ode_init_kwargs'],
-                time_options=phase.time_options,
-                state_options=phase.state_options,
-                control_options=phase.control_options,
-                parameter_options=phase.parameter_options,
-                output_nodes_per_seg=self.options['output_nodes_per_seg'],
-                reports=self.options['reports'])
-
-            segments_group.add_subsystem(f'segment_{i}', subsys=seg_i_comp)
-
-        # scipy.integrate.solve_ivp does not actually evaluate the ODE at the desired output points,
-        # but just returns the time and interpolated integrated state values there instead. We need
-        # to instantiate a second ODE group that will call the ODE at those points so that we can
-        # accurately obtain timeseries for ODE outputs.
-        phase.add_subsystem('state_mux_comp',
-                            SegmentStateMuxComp(grid_data=gd, state_options=phase.state_options,
-                                                output_nodes_per_seg=self.options['output_nodes_per_seg']))
-
-        if self.options['output_nodes_per_seg'] is None:
-            self.num_output_nodes = gd.subset_num_nodes['all']
-        else:
-            self.num_output_nodes = num_seg * self.options['output_nodes_per_seg']
-
-        phase.add_subsystem('ode', phase.options['ode_class'](num_nodes=self.num_output_nodes,
-                                                              **phase.options['ode_init_kwargs']))
-
-    def configure_ode(self, phase):
-        """
-        Create connections to the introspected states.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        gd = self.grid_data
-        num_seg = gd.num_segments
-
-        for i in range(num_seg):
-            seg_comp = phase.segments._get_subsystem(f'segment_{i}')
-            seg_comp.configure_io()
-
-    def setup_controls(self, phase):
-        """
-        Setup the control group.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        output_nodes_per_seg = self.options['output_nodes_per_seg']
-
-        phase._check_control_options()
-
-        if phase.control_options:
-            control_group = SolveIVPControlGroup(control_options=phase.control_options,
-                                                 time_units=phase.time_options['units'],
-                                                 grid_data=self.grid_data,
-                                                 output_nodes_per_seg=output_nodes_per_seg)
-
-            phase.add_subsystem('control_group',
-                                subsys=control_group,
-                                promotes=['*controls:*', '*control_values:*', '*control_values_all:*',
-                                          '*control_rates:*'])
-
-    def configure_controls(self, phase):
-        """
-        Configure the inputs/outputs for the controls.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        grid_data = self.grid_data
-
-        if phase.control_options:
-            phase.control_group.configure_io()
-            phase.connect('dt_dstau', 'control_group.dt_dstau')
-
-        for name, options in phase.control_options.items():
-            for i in range(grid_data.num_segments):
-                i1, i2 = grid_data.subset_segment_indices['control_disc'][i, :]
-                seg_idxs = grid_data.subset_node_indices['control_disc'][i1:i2]
-                src_idxs = get_src_indices_by_row(row_idxs=seg_idxs, shape=options['shape'])
-                phase.connect(src_name=f'control_values_all:{name}',
-                              tgt_name=f'segment_{i}.controls:{name}',
-                              src_indices=(src_idxs,), flat_src_indices=True)
-
-            if options['targets']:
-                phase.connect(f'control_values:{name}', [f'ode.{t}' for t in options['targets']])
-
-            if options['rate_targets']:
-                phase.connect(f'control_rates:{name}_rate', [f'ode.{t}' for t in options['rate_targets']])
-
-            if options['rate2_targets']:
-                phase.connect(f'control_rates:{name}_rate2', [f'ode.{t}' for t in options['rate2_targets']])
-
-    def configure_parameters(self, phase):
-        """
-        Configure parameter promotion.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        super(SolveIVP, self).configure_parameters(phase)
-
-        gd = self.grid_data
-
-        # We also need to take care of the segments.
-        segs = phase._get_subsystem('segments')
-
-        for name, options in phase.parameter_options.items():
-            # prom_name = f'parameters:{name}'
-            shape = options['shape']
-            units = options['units']
-
-            for i in range(gd.num_segments):
-                seg_comp = segs._get_subsystem(f'segment_{i}')
-                seg_comp.add_input(name=f'parameters:{name}', val=np.ones(shape), units=units,
-                                   desc=f'values of parameter {name}.')
-                # phase.connect(f'parameter_vals:{name}', f'segment_{i}.{prom_name}')
-
-    def setup_defects(self, phase):
-        """
-        Not used in SolveIVP.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        pass
-
-    def configure_defects(self, phase):
-        """
-        Not used in SolveIVP.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        pass
-
-    def configure_objective(self, phase):
-        """
-        Not used in SolveIVP.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        pass
-
-    def configure_path_constraints(self, phase):
-        """
-        Not used in SolveIVP.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        pass
-
-    def configure_boundary_constraints(self, phase):
-        """
-        Not used in SolveIVP.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        pass
-
-    def setup_solvers(self, phase):
-        """
-        Not used in SolveIVP.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        pass
-
-    def configure_solvers(self, phase):
-        """
-        Not used in SolveIVP.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        pass
-
-    def setup_timeseries_outputs(self, phase):
-        """
-        Setup the timeseries for this transcription.
-
-        Parameters
-        ----------
-        phase : dymos.Phase
-            The phase object to which this transcription instance applies.
-        """
-        gd = self.grid_data
-        # Check if timeseries contains an expression that needs to be evaluated
-        for _, output_options in phase._timeseries['timeseries']['outputs'].items():
-            if output_options['is_expr']:
-                has_expr = True
-                break
-            else:
-                has_expr = False
-
-        timeseries_comp = \
-            SolveIVPTimeseriesOutputComp(input_grid_data=gd,
-                                         output_nodes_per_seg=self.options['output_nodes_per_seg'],
-                                         time_units=phase.time_options['units'])
-
-        timeseries_group = TimeseriesOutputGroup(has_expr=has_expr, timeseries_output_comp=timeseries_comp)
-        phase.add_subsystem('timeseries', subsys=timeseries_group)
-
-        # Remove all subsequent timeseries
-        for ts_name in list(phase._timeseries.keys()):
-            if ts_name != 'timeseries':
-                phase._timeseries.pop(ts_name)
-
-    def get_parameter_connections(self, name, phase):
-        """
-        Returns info about a parameter's target connections in the phase.
-
-        Parameters
-        ----------
-        name : str
-            The name of the parameter for which connection information is desired.
-        phase : dymos.Phase
-            The phase object to which this transcription applies.
-
-        Returns
-        -------
-        list of (paths, indices)
-            A list containing a tuple of target paths and corresponding src_indices to which the
-            given design variable is to be connected.
-        """
-        connection_info = []
-        num_seg = self.grid_data.num_segments
-        output_nodes_per_seg = self.options['output_nodes_per_seg']
-        num_final_ode_nodes = self.grid_data.subset_num_nodes['all'] \
-            if output_nodes_per_seg is None else num_seg * output_nodes_per_seg
-
-        if name in phase.parameter_options:
-            options = phase.parameter_options[name]
-
-            static = options['static_target']
-            shape = options['shape']
-
-            # Get connections to each segment
-            gd = self.grid_data
-
-            for i in range(gd.num_segments):
-                phase.connect(f'parameter_vals:{name}', f'segment_{i}.parameters:{name}')
-
-            # Connections to the final ODE
-            ode_tgts = get_targets(ode=phase.ode, name=name, user_targets=options['targets'])
-            if not static:
-                src_idxs_raw = np.zeros(num_final_ode_nodes, dtype=int)
-                src_idxs = get_src_indices_by_row(src_idxs_raw, shape)
-                if shape == (1,):
-                    src_idxs = src_idxs.ravel()
-            else:
-                src_idxs_raw = np.zeros(1, dtype=int)
-                src_idxs = get_src_indices_by_row(src_idxs_raw, shape)
-                src_idxs = np.squeeze(src_idxs, axis=0)
-
-            connection_info.append(([f'ode.{tgt}' for tgt in ode_tgts], (src_idxs,)))
-
-        return connection_info
-
-    def _get_timeseries_var_source(self, var, output_name, phase):
-        """
-        Return the source path and indices for a given variable to be connected to a timeseries.
-
-        Parameters
-        ----------
-        var : str
-            Name of the timeseries variable whose source is desired.
-        output_name : str
-            Name of the timeseries output whose source is desired.
-        phase : dymos.Phase
-            Phase object containing the variable, either as state, time, control, etc., or as an ODE output.
-
-        Returns
-        -------
-        meta : dict
-            Metadata pertaining to the variable at the given path. This dict contains 'src' (the path to the
-            timeseries source), 'src_idxs' (an array of the
-            source indices), 'units' (the units of the source variable), and 'shape' (the shape of the variable at
-            a given node).
-        """
-        var_type = phase.classify_var(var)
-        time_units = phase.time_options['units']
-
-        transcription = phase.options['transcription']
-        ode = transcription._get_ode(phase)
-        ode_outputs = get_promoted_vars(ode, 'output')
-
-        # The default for node_idxs, applies to everything except states and parameters.
-        node_idxs = None
-        meta = {}
-
-        # Determine the path to the variable
-        if var_type == 't':
-            path = 't'
-            src_units = time_units
-            src_shape = (1,)
-        elif var_type == 't_phase':
-            path = 't_phase'
-            src_units = time_units
-            src_shape = (1,)
-        elif var_type == 'state':
-            path = f'state_mux_comp.states:{var}'
-            src_units = phase.state_options[var]['units']
-            src_shape = phase.state_options[var]['shape']
-        elif var_type in ['indep_control', 'input_control']:
-            path = f'control_values:{var}'
-            src_units = phase.control_options[var]['units']
-            src_shape = phase.control_options[var]['shape']
-        elif var_type == 'control_rate':
-            control_name = var[:-5]
-            path = f'control_rates:{control_name}_rate'
-            control_name = var[:-5]
-            src_units = get_rate_units(phase.control_options[control_name]['units'], time_units, deriv=1)
-            src_shape = phase.control_options[control_name]['shape']
-        elif var_type == 'control_rate2':
-            control_name = var[:-6]
-            path = f'control_rates:{control_name}_rate2'
-            src_units = get_rate_units(phase.control_options[control_name]['units'], time_units, deriv=2)
-            src_shape = phase.control_options[control_name]['shape']
-        elif var_type == 'parameter':
-            path = f'parameter_vals:{var}'
-            num_seg = self.grid_data.num_segments
-            node_idxs = np.zeros(num_seg * self.options['output_nodes_per_seg'], dtype=int)
-            src_units = phase.parameter_options[var]['units']
-            src_shape = phase.parameter_options[var]['shape']
-        else:
-            # Failed to find variable, assume it is in the ODE
-            path = f'ode.{var}'
-            meta = get_source_metadata(ode_outputs, src=var)
-            src_shape = meta['shape']
-            src_units = meta['units']
-            src_tags = meta['tags']
-            if 'dymos.static_output' in src_tags:
-                raise RuntimeError(f'ODE output {var} is tagged with "dymos.static_output" and cannot be a '
-                                   f'timeseries output.')
-
-        src_idxs = None if node_idxs is None else om.slicer[node_idxs, ...]
-
-        meta['src'] = path
-        meta['src_idxs'] = src_idxs
-        meta['units'] = src_units
-        meta['shape'] = src_shape
-
-        return meta
-
-    def _get_num_timeseries_nodes(self):
-        """
-        Returns the number of nodes in the default timeseries for this transcription.
-
-        Returns
-        -------
-        int
-            The number of nodes in the default timeseries for this transcription.
-        """
-        if self.options['output_nodes_per_seg'] is None:
-            output_nodes = self.grid_data.num_segments
-        else:
-            output_nodes = self.grid_data.num_segments * self.options['output_nodes_per_seg']
-        return output_nodes
diff --git a/dymos/transcriptions/transcription_base.py b/dymos/transcriptions/transcription_base.py
index 751f413e9..b01ed31de 100644
--- a/dymos/transcriptions/transcription_base.py
+++ b/dymos/transcriptions/transcription_base.py
@@ -104,16 +104,6 @@ def configure_time(self, phase):
         """
         time_options = phase.time_options
 
-        # Determine the time unit.
-        if time_options['units'] in _none_or_unspecified:
-            if time_options['targets']:
-                ode = phase._get_subsystem(self._rhs_source)
-
-                _, time_options['units'] = get_target_metadata(ode, name='time',
-                                                               user_targets=time_options['targets'],
-                                                               user_units=time_options['units'],
-                                                               user_shape='')
-
         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


Test patch:
diff --git a/dymos/examples/min_time_climb/test/test_ex_min_time_climb.py b/dymos/examples/min_time_climb/test/test_ex_min_time_climb.py
index 6fd096779..c8addd537 100644
--- a/dymos/examples/min_time_climb/test/test_ex_min_time_climb.py
+++ b/dymos/examples/min_time_climb/test/test_ex_min_time_climb.py
@@ -239,7 +239,7 @@ def _test_mach_rate(self, p, plot=False, time_name='time'):
             assert_near_equal(mach_rate_nodes, deriv(time_nodes), tolerance=1.0E-9)
 
         # Comparing the mach rate over the entire trajectory since it is expected to be off at some points due to
-        # the equidistant time-spacing of nodes in SolveIVP's timeseries outputs.
+        # the equidistant time-spacing of nodes in ExplicitShooting's timeseries outputs.
         assert_timeseries_near_equal(t_ref=time, x_ref=mach_rate, t_check=sim_time, x_check=sim_mach_rate,
                                      abs_tolerance=0.02, rel_tolerance=0.02)
 
diff --git a/dymos/phase/test/test_timeseries.py b/dymos/phase/test/test_timeseries.py
index 346cd262f..0b3290555 100644
--- a/dymos/phase/test/test_timeseries.py
+++ b/dymos/phase/test/test_timeseries.py
@@ -97,7 +97,7 @@ def test_timeseries_gl(self, test_smaller_timeseries=False):
                     assert_near_equal(p.get_val(f'phase0.parameter_vals:{dp}')[0],
                                       p.get_val(f'phase0.timeseries.{dp}')[i])
 
-        # call simulate to test SolveIVP transcription
+        # test simulation
         exp_out = phase.simulate()
         if test_smaller_timeseries:
             with self.assertRaises(KeyError):
@@ -185,7 +185,7 @@ def test_timeseries_radau(self, test_smaller_timeseries=False):
                     assert_near_equal(p.get_val(f'phase0.parameters:{dp}')[0],
                                       p.get_val(f'phase0.timeseries.{dp}')[i])
 
-        # call simulate to test SolveIVP transcription
+        # test simulation
         exp_out = phase.simulate()
         if test_smaller_timeseries:
             with self.assertRaises(KeyError):
diff --git a/dymos/transcriptions/solve_ivp/components/test/__init__.py b/dymos/transcriptions/solve_ivp/components/test/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/dymos/transcriptions/solve_ivp/components/test/test_segment_simulation_comp.py b/dymos/transcriptions/solve_ivp/components/test/test_segment_simulation_comp.py
deleted file mode 100644
index b8bc96ab7..000000000
--- a/dymos/transcriptions/solve_ivp/components/test/test_segment_simulation_comp.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import unittest
-
-import numpy as np
-import openmdao.api as om
-from openmdao.utils.assert_utils import assert_near_equal
-from openmdao.utils.testing_utils import use_tempdirs
-
-from dymos.transcriptions.solve_ivp.components.segment_simulation_comp import SegmentSimulationComp
-from dymos.phase.options import TimeOptionsDictionary, StateOptionsDictionary, \
-    SimulateOptionsDictionary
-from dymos.transcriptions.grid_data import GridData
-
-# Modify class so we can run it standalone.
-from dymos.utils.misc import CompWrapperConfig
-SegmentSimulationComp = CompWrapperConfig(SegmentSimulationComp)
-
-
-class TestODE(om.ExplicitComponent):
-
-    def initialize(self):
-        self.options.declare('num_nodes', types=int)
-
-    def setup(self):
-        self.add_input('t', val=np.ones(self.options['num_nodes']), units='s')
-        self.add_input('y', val=np.ones(self.options['num_nodes']), units='m')
-        self.add_output('ydot', val=np.ones(self.options['num_nodes']), units='m/s')
-        self.declare_coloring(wrt='*', method='cs')
-
-    def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
-        t = inputs['t']
-        y = inputs['y']
-        outputs['ydot'] = y - t ** 2 + 1
-
-
-@use_tempdirs
-class TestSegmentSimulationComp(unittest.TestCase):
-
-    def test_simple_integration(self):
-
-        p = om.Problem(model=om.Group())
-
-        time_options = TimeOptionsDictionary()
-        time_options['units'] = 's'
-        time_options['targets'] = 't'
-        time_options['time_phase_targets'] = []
-
-        state_options = {}
-        state_options['y'] = StateOptionsDictionary()
-        state_options['y']['units'] = 'm'
-        state_options['y']['targets'] = 'y'
-        state_options['y']['rate_source'] = 'ydot'
-
-        # Non-standard way to assign state options, so we need this
-        state_options['y']['shape'] = (1, )
-
-        gd = GridData(num_segments=4, transcription='gauss-lobatto', transcription_order=3)
-
-        sim_options = SimulateOptionsDictionary()
-        sim_options['rtol'] = 1.0E-9
-        sim_options['atol'] = 1.0E-9
-
-        seg0_comp = SegmentSimulationComp(index=0, grid_data=gd, simulate_options=sim_options,
-                                          ode_class=TestODE, time_options=time_options,
-                                          state_options=state_options)
-
-        p.model.add_subsystem('segment_0', subsys=seg0_comp)
-
-        p.setup(check=True)
-
-        p.set_val('segment_0.time', [0, 0.25, 0.5])
-        p.set_val('segment_0.initial_states:y', 0.5)
-
-        p.run_model()
-
-        assert_near_equal(p.get_val('segment_0.states:y', units='m')[-1, ...],
-                          1.425639364649936,
-                          tolerance=1.0E-6)
-
-
-if __name__ == '__main__':  # pragma: no cover
-    unittest.main()