Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added capability for IVCs to be instantiated with a list of variables #3197

Closed
wants to merge 9 commits into from
34 changes: 30 additions & 4 deletions openmdao/core/indepvarcomp.py
Expand Up @@ -14,13 +14,17 @@ class IndepVarComp(ExplicitComponent):

Parameters
----------
name : str or None
Name of the variable.
name : str, list, tuple, or None
Name of the variable or list/tuple of variables.
If a string, defines a single variable with the specified name.
If a list or tuple, each element should be a tuple with the format (name, value) or
(name, value, kwargs) where `name` is a string, `value` can be any type compatible with val,
and `kwargs` is a dictionary of keyword arguments.
If None, variables should be defined external to this class by calling add_output.
val : float or ndarray
Value of the variable if a single variable is being defined.
Initial value of the variable if a single variable is being defined.
**kwargs : dict
Keyword arguments.
Additional keyword arguments.
"""

def __init__(self, name=None, val=1.0, **kwargs):
Expand All @@ -39,6 +43,28 @@ def __init__(self, name=None, val=1.0, **kwargs):
if isinstance(name, str):
super().add_output(name, val, **kwargs)

elif isinstance(name, (list, tuple)):
for tup in name:
if not isinstance(tup, tuple):
raise TypeError("Each entry in the list of tuples must be of type tuple.")
if len(tup) == 2:
Copy link
Member

Choose a reason for hiding this comment

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

You might want to verify here that tup is actually a tuple, or at least not a string. Otherwise in cases where the user gives you a list/tuple of strings, you'll get weird error messages. I could see a user just providing a list of strings and assuming those variables would all have default values.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call, I've modified this and pushed up!

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the docstring for name needs to be updated for these new possibilities...

Also maybe some kind of error checking on the tup values (i.e. tup[0] must be a string, tup[2] must be a dict?)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks Steve, I've updated the docstring and added type checking!

if not isinstance(tup[0], str):
raise TypeError("The first element of the tuple must be a string representing the variable name.")
if not isinstance(tup[1], (int, float, list, tuple, np.ndarray)):
raise TypeError("The second element of the tuple must be the initial value of the variable.")
super().add_output(tup[0], tup[1], **kwargs)
elif len(tup) == 3:
if not isinstance(tup[0], str):
raise TypeError("The first element of the tuple must be a string representing the variable name.")
if not isinstance(tup[1], (int, float, list, tuple, np.ndarray)):
raise TypeError("The second element of the tuple must be the initial value of the variable.")
if not isinstance(tup[2], dict):
raise TypeError("The third element of the tuple must be a dictionary of keyword arguments.")
super().add_output(tup[0], tup[1], **tup[2])
else:
raise ValueError("Each entry in the list of tuples must be of the form "
"(name, value) or (name, value, keyword_dict).")

elif name is None:
pass

Expand Down
46 changes: 46 additions & 0 deletions openmdao/core/tests/test_indep_var_comp.py
Expand Up @@ -105,6 +105,52 @@ def test_add_output(self):
assert_near_equal(prob.get_val('indep_var_1'), 1.0)
assert_near_equal(prob.get_val('indep_var_2'), 2.0)

def test_tuple_ivc(self):
"""Define one independent variable using a tuple."""

ivcs = [
('indep_var', 1.0),
('indep_var2', 2.0),
]

comp = om.IndepVarComp(ivcs)

prob = om.Problem()
prob.model.add_subsystem('comp', comp, promotes=['*'])
prob.setup(check=False)

assert_near_equal(prob.get_val('indep_var'), 1.0)
assert_near_equal(prob.get_val('indep_var2'), 2.0)

def test_tuple_ivc_kwargs(self):
"""Define one independent variable using a tuple with additional options."""

ivcs = [
('indep_var', 1.0, {'units': 'm'}),
('indep_var2', 2.0, {'units': 'm'}),
]

comp = om.IndepVarComp(ivcs)

prob = om.Problem()
prob.model.add_subsystem('comp', comp, promotes=['*'])
prob.setup(check=False)

assert_near_equal(prob.get_val('indep_var', units='m'), 1.0)
assert_near_equal(prob.get_val('indep_var2', units='m'), 2.0)

def test_tuple_error(self):
"""Test to see if the objects in the list are actually tuples."""

ivcs = ['indep_var', 'indep_var2']

try:
om.IndepVarComp(ivcs)
except TypeError as err:
self.assertEqual(str(err), "Each entry in the list of tuples must be of type tuple.")
else:
self.fail('Exception expected.')

def test_promote_glob_no_inputs(self):
p = om.Problem()
p.model.add_subsystem('indep',
Expand Down