Repository: OpenMDAO/dymos
Pull request #970 (2023-08-24 17:54:56)

Allow parameter shape to be given as `None`, rather than just `_unspecified`
### Summary

The previous parameter introspection update changed parameters so that a shape of `None` is not allowed. This is added back in as dymos can handle this situation just fine.

### Related Issues

- Resolves #

### Backwards incompatibilities

None

### New Dependencies

None



Patch:
diff --git a/dymos/examples/water_rocket/phases.py b/dymos/examples/water_rocket/phases.py
index 626671c2..ffd0fc6b 100644
--- a/dymos/examples/water_rocket/phases.py
+++ b/dymos/examples/water_rocket/phases.py
@@ -53,14 +53,14 @@ def new_ballistic_ascent_phase(transcription):
         duration_ref=1, units='s')
 
     ballistic_ascent.add_state('r', units='m', rate_source='eom.r_dot', fix_initial=False,
-                               fix_final=False, ref=100.0, defect_ref=100.0)
+                               fix_final=False, ref=1.0, defect_ref=1.0)
     ballistic_ascent.add_state('h', units='m', rate_source='eom.h_dot', targets=['atmos.h'],
-                               fix_initial=False, fix_final=False, ref=100.0, defect_ref=100.0)
+                               fix_initial=False, fix_final=False, ref=1.0)
     ballistic_ascent.add_state('gam', units='deg', rate_source='eom.gam_dot', targets=['eom.gam'],
-                               fix_initial=False, fix_final=True, upper=89, ref=90, defect_ref=90)
+                               fix_initial=False, fix_final=True, upper=89, ref=90)
     ballistic_ascent.add_state('v', units='m/s', rate_source='eom.v_dot',
                                targets=['dynamic_pressure.v', 'eom.v'], fix_initial=False,
-                               fix_final=False, ref=100, defect_ref=100)
+                               fix_final=False, ref=1.)
 
     ballistic_ascent.add_parameter('S', targets=['aero.S'], units='m**2')
     ballistic_ascent.add_parameter('m_empty', targets=['eom.m'], units='kg')
@@ -85,7 +85,7 @@ def new_descent_phase(transcription):
     descent.add_state('v', units='m/s', rate_source='eom.v_dot',
                       targets=['dynamic_pressure.v', 'eom.v'],
                       fix_initial=False, fix_final=False,
-                      ref=100, defect_ref=100)
+                      ref=1)
 
     descent.add_parameter('S', targets=['aero.S'], units='m**2')
     descent.add_parameter('mass', targets=['eom.m'], units='kg')
diff --git a/dymos/phase/options.py b/dymos/phase/options.py
index 6f343806..5da83ffb 100644
--- a/dymos/phase/options.py
+++ b/dymos/phase/options.py
@@ -3,7 +3,7 @@ from numbers import Number
 import numpy as np
 
 import openmdao.api as om
-from ..utils.misc import _unspecified
+from ..utils.misc import _unspecified, _none_or_unspecified
 
 
 class ControlOptionsDictionary(om.OptionsDictionary):
@@ -224,7 +224,7 @@ def check_valid_shape(name, value):
         Shape to check, should be a Iterable, Number, list, or tuple.
     """
     if name == 'shape':
-        if value is not _unspecified and not isinstance(value, (Iterable, Number, list, tuple)):
+        if value not in _none_or_unspecified and not isinstance(value, (Iterable, Number, list, tuple)):
             raise ValueError(f"Option '{name}' with value {value} is not valid.")
 
 
@@ -284,7 +284,7 @@ class ParameterOptionsDictionary(om.OptionsDictionary):
         self.declare(name='val', types=(Iterable, np.ndarray, Number), default=0.0,
                      desc='The default value of the parameter in the phase.')
 
-        self.declare(name='shape', check_valid=check_valid_shape, default=_unspecified,
+        self.declare(name='shape', check_valid=check_valid_shape, default=_unspecified, allow_none=True,
                      desc='The shape of the parameter.')
 
         self.declare(name='lower', types=(Iterable, Number), default=None,


Test patch:
diff --git a/dymos/examples/brachistochrone/test/test_brachistochrone_path_constraint.py b/dymos/examples/brachistochrone/test/test_brachistochrone_path_constraint.py
index bdbec751..723cb5da 100644
--- a/dymos/examples/brachistochrone/test/test_brachistochrone_path_constraint.py
+++ b/dymos/examples/brachistochrone/test/test_brachistochrone_path_constraint.py
@@ -40,7 +40,7 @@ class TestBrachistochroneExprPathConstraint(unittest.TestCase):
         phase.add_boundary_constraint('x', loc='final', equals=10.0)
         phase.add_path_constraint('pc = y-x/2-1', lower=0.0)
 
-        phase.add_parameter('g', opt=False, units='m/s**2', val=9.80665, include_timeseries=True)
+        phase.add_parameter('g', opt=False, units='m/s**2', val=9.80665, shape=None, include_timeseries=True)
 
         phase.add_objective('time_phase', loc='final', scaler=10)
 
diff --git a/dymos/examples/water_rocket/test/test_water_rocket.py b/dymos/examples/water_rocket/test/test_water_rocket.py
index ab2d26a2..541eff5b 100644
--- a/dymos/examples/water_rocket/test/test_water_rocket.py
+++ b/dymos/examples/water_rocket/test/test_water_rocket.py
@@ -26,7 +26,7 @@ class TestWaterRocketForDocs(unittest.TestCase):
 
         p.driver = om.pyOptSparseDriver(optimizer='IPOPT', print_results=False)
         p.driver.opt_settings['print_level'] = 0
-        p.driver.opt_settings['max_iter'] = 1000
+        p.driver.opt_settings['max_iter'] = 500
         p.driver.opt_settings['mu_strategy'] = 'monotone'
         p.driver.declare_coloring(tol=1.0E-12)
 
@@ -65,7 +65,7 @@ class TestWaterRocketForDocs(unittest.TestCase):
 
         p.driver = om.pyOptSparseDriver(optimizer='IPOPT')
         p.driver.opt_settings['print_level'] = 0
-        p.driver.opt_settings['max_iter'] = 1000
+        p.driver.opt_settings['max_iter'] = 300
         p.driver.opt_settings['mu_strategy'] = 'monotone'
         p.driver.declare_coloring(tol=1.0E-12)