I would like to be able to pass string values between components. Is this possible in the latest version of OpenMDAO or will this be possible in future releases?

If I understand correctly, then the passing of strings was supported in earlier releases of OpenMDAO (V<=1) with pass-by-object. Currently I use a workaround where string inputs and outputs of my tools are written to separate files and taken from those files when required. This is then outside OpenMDAO model.

Here's a small example for the type of model that would be supported if strings can be used as inputs and outputs. This is of course just a demonstration case.

from openmdao.core.explicitcomponent import ExplicitComponent
from openmdao.core.group import Group
from openmdao.core.indepvarcomp import IndepVarComp
from openmdao.core.problem import Problem
from openmdao.devtools.problem_viewer.problem_viewer import view_model


class PreProcessor(ExplicitComponent):

    def setup(self):
        self.add_input('a', val=0.)

        self.add_output('para_shape', val='hill')

    def compute(self, inputs, outputs):
        if inputs['a'] <= 0.:
            outputs['para_shape'] = 'hill'
        else:
            outputs['para_shape'] = 'canyon'


class Parabola(ExplicitComponent):

    def setup(self):
        self.add_input('x', val=0.)
        self.add_input('para_shape', val='hill')

        self.add_output('y', val=0.)

    def compute(self, inputs, outputs):
        if inputs['para_shape'] == 'hill':
            outputs['y'] = -inputs['x']**2
        elif inputs['para_shape'] == 'canyon':
            outputs['y'] = inputs['x']**2
        else:
            raise IOError('Invalid "para_shape" value "{}" provided.'.format(inputs['para_shape']))


if __name__ == "__main__":

    model = Group()
    ivc = IndepVarComp()
    ivc.add_output('a', 2.)
    ivc.add_output('x', 4.)
    model.add_subsystem('vars', ivc, promotes=['*'])
    model.add_subsystem('preprocessor', PreProcessor(), promotes=['*'])
    model.add_subsystem('parabola', Parabola(), promotes=['*'])

    prob = Problem(model)
    prob.setup()
    view_model(prob, outfile='n2_pass_by_object_example.html')

    prob.run_model()

If I run this I get the ValueError that could not convert string to float: hill, which is to be expected. I'm just wondering if there is a way to make this work while keeping para_shape as a string output of PreProcessor and a string input for Parabola.

Apart from that it would simply be convenient to pass strings around in my case, I thought this might also be helpful when optimization algorithms are used that support discrete values, with a genetic algorithm for example. In those algorithms para_shape could be a design variable with the possible values hill or canyon. Behind the scenes, such a string variable would probably be mapped to an integer value, like 0:hill, 1:canyon.

In summary, my question is: will the pass-by-object (or a similar capability that allows me to define string input/output) be implemented for OpenMDAO 2 or is there a way for doing this already?

1 Answers

1
Bret Naylor On Best Solutions

The good news is that the current version of OpenMDAO does support discrete variables. I've updated your example to use the current syntax for declaring discrete variables and passing them to the compute function. Basically, the changes are that you have to declare discrete variables using add_discrete_input and add_discrete_output. In addition, you have to add discrete_inputs and discrete_outputs as args to your compute function, so the new version looks like: def compute(self, inputs, outputs, discrete_inputs, discrete_outputs). Also note that other functions like compute_partials, compute_jacvec_product and others also require additional discrete args if your component happens to use them.

The bad news is that your example uncovered a bug in view_model. It appears that view_model doesn't currently support discrete variables properly. I've put a bug into our bug tracker describing the problem so hopefully we'll have it fixed shortly.

from openmdao.core.explicitcomponent import ExplicitComponent
from openmdao.core.group import Group
from openmdao.core.indepvarcomp import IndepVarComp
from openmdao.core.problem import Problem
from openmdao.devtools.problem_viewer.problem_viewer import view_model


class PreProcessor(ExplicitComponent):

    def setup(self):
        self.add_input('a', val=0.)

        self.add_discrete_output('para_shape', val='hill')

    def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):
        if inputs['a'] <= 0.:
            discrete_outputs['para_shape'] = 'hill'
        else:
            discrete_outputs['para_shape'] = 'canyon'


class Parabola(ExplicitComponent):

    def setup(self):
        self.add_input('x', val=0.)
        self.add_discrete_input('para_shape', val='hill')

        self.add_output('y', val=0.)

    def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):
        if discrete_inputs['para_shape'] == 'hill':
            outputs['y'] = -inputs['x']**2
        elif discrete_inputs['para_shape'] == 'canyon':
            outputs['y'] = inputs['x']**2
        else:
            raise IOError('Invalid "para_shape" value "{}" provided.'.format(inputs['para_shape']))


if __name__ == "__main__":

    model = Group()
    ivc = IndepVarComp()
    ivc.add_output('a', 2.)
    ivc.add_output('x', 4.)
    model.add_subsystem('vars', ivc, promotes=['*'])
    model.add_subsystem('preprocessor', PreProcessor(), promotes=['*'])
    model.add_subsystem('parabola', Parabola(), promotes=['*'])

    prob = Problem(model)
    prob.setup()
    view_model(prob, outfile='n2_pass_by_object_example.html')
    prob.run_model()