Ejemplo n.º 1
0
def instantiate_component(component, component_name, mdao_config, root,
                          subproblem_output_meta):
    component_type = component.get('type', 'TestBenchComponent')
    if component_type == 'IndepVarComp':

        def get_unknown_val(unknown):
            if unknown.get('type') is None:
                return unknown['value']
            return {
                'double': float,
                'int': int,
                'string': six.text_type,
                'array': numpy.array
            }[unknown['type']](unknown['value'])

        def get_unknown_meta(unknown):
            ret = {'pass_by_obj': True}
            units = unknown.get('units')
            if units:
                ret['units'] = str(units)
            return ret

        vars = ((name, get_unknown_val(unknown), get_unknown_meta(unknown))
                for name, unknown in six.iteritems(component['unknowns']))
        return IndepVarComp(vars)
    elif component_type == 'TestBenchComponent':
        tb = TestBenchComponent(component_name, mdao_config, root,
                                subproblem_output_meta)
        # FIXME verify this works properly
        tb.solve_nonlinear = _memoize_solve(tb, tb.solve_nonlinear)

        return tb
    elif component_type == 'EnumMap':
        return EnumMapper(component['details']['config'],
                          param_name=_get_param_name('input'))
    elif component_type == 'FMU':
        return FmuWrapper(component['details']['fmu'])
    else:
        if '.' in component_type:
            mod_name = '.'.join(component_type.split('.')[:-1])
            class_name = component_type.split('.')[-1]
            component_instance = getattr(importlib.import_module(mod_name),
                                         class_name)(**component['details'])
        else:
            component_instance = locals()[component_type](
                **component['details'])
        return component_instance
Ejemplo n.º 2
0
def instantiate_component(component, component_name, mdao_config, root):
    component_type = component.get('type', 'TestBenchComponent')
    if component_type == 'IndepVarComp':
        def get_unknown_val(unknown):
            if unknown.get('type') is None:
                return unknown['value']
            return {'double': float,
                    'int': int,
                    'string': six.text_type,
                    'array': numpy.array}[unknown['type']](unknown['value'])

        def get_unknown_meta(unknown):
            ret = {'pass_by_obj': True}
            units = unknown.get('units')
            if units:
                ret['units'] = str(units)
            return ret
        vars = ((name, get_unknown_val(unknown), get_unknown_meta(unknown)) for name, unknown in six.iteritems(component['unknowns']))
        return IndepVarComp(vars)
    elif component_type == 'TestBenchComponent':
        tb = TestBenchComponent(component_name, mdao_config, root)
        # FIXME verify this works properly
        tb.solve_nonlinear = _memoize_solve(tb, tb.solve_nonlinear)

        return tb
    elif component_type == 'EnumMap':
        return EnumMapper(component['details']['config'], param_name=_get_param_name('input'))
    elif component_type == 'FMU':
        return FmuWrapper(component['details']['fmu'])
    else:
        if '.' in component_type:
            mod_name = '.'.join(component_type.split('.')[:-1])
            class_name = component_type.split('.')[-1]
            component_instance = getattr(importlib.import_module(mod_name), class_name)(**component['details'])
        else:
            component_instance = locals()[component_type](**component['details'])
        return component_instance
Ejemplo n.º 3
0
def with_problem(mdao_config, original_dir, override_driver=None):
    # TODO: can we support more than one driver
    driver = next(iter(mdao_config['drivers'].values()))

    top = Problem(impl=impl)
    root = top.root = Group()
    recorder = None
    driver_params = {'original_dir': original_dir}
    eval(compile(driver['details'].get('Code', ''), '<driver Code>', 'exec'), globals(), driver_params)

    def get_desvar_path(designVariable):
        return 'designVariable.{}'.format(designVariable)

    if driver['type'] == 'optimizer':
        top.driver = ScipyOptimizer()
        top.driver.options['optimizer'] = str(driver.get('details', {}).get('OptimizationFunction', 'SLSQP'))

        for key, value in six.iteritems(driver_params):
            try:
                top.driver.options[key] = value
            except KeyError:
                pass # Ignore options that aren't valid for driver
    elif driver['type'] == 'parameterStudy':
        drivers = {
            "Uniform": UniformDriver,
            "Full Factorial": FullFactorialDriver,
            "Latin Hypercube": LatinHypercubeDriver,
            "Opt Latin Hypercube": OptimizedLatinHypercubeDriver,
        }
        driver_type = drivers.get(driver['details']['DOEType'])
        if driver_type is None:
            raise Exception('DOEType "{}" is unsupported'.format(driver['details']['DOEType']))
        if override_driver is None:
            top.driver = driver_type(**driver_params)
        else:
            top.driver = override_driver
        seed = getattr(top.driver, 'seed', None)
        if seed is not None:
            print('Using random seed {}'.format(seed))
    elif driver['type'] == 'PCCDriver':
        import PCC.pcc_driver
        driver_params.update(driver['details'])
        top.driver = PCC.pcc_driver.PCCdriver(**driver_params)
    else:
        raise ValueError('Unsupported driver type %s' % driver['type'])

    def add_recorders():
        recorders = []
        design_var_map = {get_desvar_path(designVariable): designVariable for designVariable in driver['designVariables']}
        objective_map = {'{}.{}'.format(objective['source'][0], objective['source'][1]): objective_name for objective_name, objective in six.iteritems(driver['objectives'])}
        intermediate_var_map = {'{}.{}'.format(intermediate_var['source'][0], intermediate_var['source'][1]): intermediate_var_name for intermediate_var_name, intermediate_var in six.iteritems(driver.get('intermediateVariables', {}))}
        constants_map = {}
        for name, constant in (c for c in six.iteritems(mdao_config['components']) if c[1].get('type', 'TestBenchComponent') == 'IndepVarComp'):
            constants_map.update({'{}.{}'.format(name, unknown): unknown for unknown in constant['unknowns']})

        constraints_map = {'{}.{}'.format(constraint['source'][0], constraint['source'][1]): constraint_name for constraint_name, constraint in six.iteritems(driver.get('constraints', {})) if constraint['source'][0] not in mdao_config['drivers']} # All constraints that don't point back to design variables

        unknowns_map = design_var_map
        unknowns_map.update(objective_map)
        unknowns_map.update(intermediate_var_map)
        unknowns_map.update(constants_map)
        unknowns_map.update(constraints_map)
        for recorder in mdao_config.get('recorders', [{'type': 'DriverCsvRecorder', 'filename': 'output.csv'}]):
            if recorder['type'] == 'DriverCsvRecorder':
                mode = 'wb'
                if RestartRecorder.is_restartable(original_dir):
                    mode = 'ab'
                recorder = MappingCsvRecorder({}, unknowns_map, io.open(recorder['filename'], mode))
                if mode == 'ab':
                    recorder._wrote_header = True
            elif recorder['type'] == 'AllCsvRecorder':
                mode = 'wb'
                recorder = CsvRecorder(out=open(recorder['filename'], mode))
            elif recorder['type'] == 'CouchDBRecorder':
                recorder = CouchDBRecorder(recorder.get('url', 'http://localhost:5984/'), recorder['run_id'])
                recorder.options['record_params'] = True
                recorder.options['record_unknowns'] = True
                recorder.options['record_resids'] = False
                recorder.options['includes'] = list(unknowns_map.keys())
            else:
                mod_name = '.'.join(recorder['type'].split('.')[:-1])
                class_name = recorder['type'].split('.')[-1]
                recorder = getattr(importlib.import_module(mod_name), class_name)()

            top.driver.add_recorder(recorder)
        return recorders

    recorders = add_recorders()

    try:
        driver_vars = []
        for var_name, var in six.iteritems(driver['designVariables']):
            if var.get('type', 'double') == 'double':
                default = 0.0
                range_min = var.get('RangeMin')
                range_max = var.get('RangeMax')
                if range_min is not None and range_max is not None:
                    default = range_min + (range_max - range_min) / 2
                driver_vars.append((var_name, default))
            elif var['type'] == 'enum':
                driver_vars.append((var_name, var['items'][0], {"pass_by_obj": True}))
            elif var['type'] == 'int':
                driver_vars.append((var_name, 0))
            else:
                raise ValueError('Unimplemented designVariable type "{}"'.format(var['type']))

        root.add(get_desvar_path('').split('.')[0], IndepVarComp(driver_vars))
        for var_name, var in six.iteritems(driver['designVariables']):
            if var.get('type', 'double') == 'double':
                top.driver.add_desvar(get_desvar_path(var_name), lower=var.get('RangeMin'), upper=var.get('RangeMax'))
            elif var['type'] == 'enum':
                driver_vars.append((var_name, u'', {"pass_by_obj": True}))
                formatted_name = get_desvar_path(var_name)
                top.driver.add_desvar(formatted_name)
                top.driver._desvars[formatted_name]['type'] = var['type']
                top.driver._desvars[formatted_name]['items'] = var['items']
            elif var['type'] == 'int':
                driver_vars.append((var_name, 0.0))
                formatted_name = get_desvar_path(var_name)
                top.driver.add_desvar(formatted_name, lower=var.get('RangeMin'), upper=var.get('RangeMax'))
                top.driver._desvars[formatted_name]['type'] = var['type']
            else:
                raise ValueError('Unimplemented designVariable type "{}"'.format(var['type']))

        def get_sorted_components():
            """Apply Tarjan's algorithm to the Components."""
            visited = {}
            tbs_sorted = []

            def get_ordinal(name):
                ordinal = visited.get(name, -1)
                if ordinal is None:
                    raise ValueError('Loop involving component "{}"'.format(name))
                if ordinal != -1:
                    return ordinal
                component = mdao_config['components'][name]
                visited[name] = None
                ordinal = 0
                for source in (param.get('source') for param in component.get('parameters', {}).values()):
                    if not source:
                        continue
                    if source[0] in mdao_config['drivers']:
                        continue
                    ordinal = max(ordinal, get_ordinal(source[0]) + 1)
                visited[name] = ordinal
                tbs_sorted.append(name)
                return ordinal

            for component_name in mdao_config['components']:
                get_ordinal(component_name)
            return tbs_sorted

        tbs_sorted = get_sorted_components()
        for component_name in tbs_sorted:
            component = mdao_config['components'][component_name]
            mdao_component = instantiate_component(component, component_name, mdao_config, root)
            root.add(component_name, mdao_component)

        for component_name, component in six.iteritems(mdao_config['components']):
            for parameter_name, parameter in six.iteritems(component.get('parameters', {})):
                if parameter.get('source'):
                    source = parameter['source']
                    if source[0] in mdao_config['drivers']:
                        # print('driver{}.{}'.format(source[1], source[1]))
                        root.connect(get_desvar_path(source[1]), '{}.{}'.format(component_name, _get_param_name(parameter_name, component.get('type'))))
                    else:
                        root.connect('{}.{}'.format(source[0], source[1]), '{}.{}'.format(component_name, _get_param_name(parameter_name, component.get('type'))))
                else:
                    pass  # TODO warn or fail?

        if driver['type'] == 'optimizer':
            for objective in six.itervalues(driver['objectives']):
                top.driver.add_objective(str('.'.join(objective['source'])))

            for constraint in six.itervalues(driver['constraints']):
                if constraint['source'][0] in mdao_config['drivers']:
                    # References the driver; need to get the path to the design var
                    constraintPath = get_desvar_path(constraint['source'][1])
                else:
                    constraintPath = str('.'.join(constraint['source']))

                if 'RangeMin' in constraint and 'RangeMax' in constraint:
                    top.driver.add_constraint(constraintPath, lower=constraint['RangeMin'], upper=constraint['RangeMax'])
                elif 'RangeMin' in constraint and 'RangeMax' not in constraint:
                    top.driver.add_constraint(constraintPath, lower=constraint['RangeMin'])
                elif 'RangeMin' not in constraint and 'RangeMax' in constraint:
                    top.driver.add_constraint(constraintPath, upper=constraint['RangeMax'])
                else:
                    pass # TODO: No min or max provided with constraint; warn or fail here?



        top.setup()
        # from openmdao.devtools.debug import dump_meta
        # dump_meta(top.root)
        yield top
    finally:
        for recorder in recorders:
            recorder.close()
Ejemplo n.º 4
0
def with_problem(mdao_config,
                 original_dir,
                 override_driver=None,
                 additional_recorders=(),
                 is_subproblem=False,
                 append_csv=False,
                 profile=False):
    testbenchexecutor.progress_service.update_progress("Configuring PET...",
                                                       -1, -1)
    # TODO: can we support more than one driver
    if len(mdao_config['drivers']) == 0:
        driver = None
    else:
        driver = next(iter(mdao_config['drivers'].values()))

    top = Problem(impl=impl)
    root = top.root = Group()
    recorder = None
    driver_params = {'original_dir': original_dir}
    if driver is not None:
        eval(
            compile(driver['details'].get('Code', ''), '<driver Code>',
                    'exec'), globals(), driver_params)

    subProblemInputMeta = {}
    subProblemOutputMeta = {}

    if driver is not None:
        if driver['type'] == 'optimizer':
            if driver.get('details',
                          {}).get('OptimizationFunction') == 'Custom':
                class_path = driver['details']['OptimizationClass'].split('.')
                mod = __import__('.'.join(class_path[:-1]),
                                 fromlist=[class_path[-1]])
                top.driver = getattr(mod, class_path[-1])()
            else:
                top.driver = ScipyOptimizer()
                top.driver.options['optimizer'] = str(
                    driver.get('details', {}).get('OptimizationFunction',
                                                  'SLSQP'))

            for key, value in six.iteritems(driver_params):
                try:
                    top.driver.options[key] = value
                except KeyError:
                    pass  # Ignore options that aren't valid for driver
        elif driver['type'] == 'parameterStudy':
            drivers = {
                "Uniform": UniformDriver,
                "Full Factorial": FullFactorialDriver,
                "Latin Hypercube": LatinHypercubeDriver,
                "Opt Latin Hypercube": OptimizedLatinHypercubeDriver,
                "CSV File": CsvDriver,
            }
            driver_type = drivers.get(driver['details']['DOEType'])
            if driver_type is None:
                raise Exception('DOEType "{}" is unsupported'.format(
                    driver['details']['DOEType']))
            if override_driver is None:
                top.driver = driver_type(**driver_params)
            else:
                top.driver = override_driver
            seed = getattr(top.driver, 'seed', None)
            if seed is not None:
                print('Using random seed {}'.format(seed))
        elif driver['type'] == 'PCCDriver':
            import PCC.pcc_driver
            driver_params.update(driver['details'])
            driver_params[
                "_run_mdao_subproblem_output_meta"] = subProblemOutputMeta
            top.driver = PCC.pcc_driver.PCCdriver(**driver_params)
        else:
            raise ValueError('Unsupported driver type %s' % driver['type'])

        driver_vars = []
        for var_name, var in six.iteritems(driver['designVariables']):
            if var.get('type', 'double') == 'double':
                default = 0.0
                range_min = var.get('RangeMin')
                range_max = var.get('RangeMax')
                if range_min is not None and range_max is not None:
                    default = range_min + (range_max - range_min) / 2
                driver_vars.append((var_name, default, {}))
            elif var['type'] == 'enum':
                driver_vars.append((var_name, var['items'][0], {
                    "pass_by_obj": True
                }))
            elif var['type'] == 'int':
                driver_vars.append((var_name, 0, {}))
            else:
                raise ValueError(
                    'Unimplemented designVariable type "{}"'.format(
                        var['type']))
            units = var.get('units')
            if units:
                driver_vars[-1][2]['units'] = str(var['units'])

        root.add(get_desvar_path('').split('.')[0], IndepVarComp(driver_vars))
        for var_name, var in six.iteritems(driver['designVariables']):
            if var.get('type', 'double') == 'double':
                top.driver.add_desvar(get_desvar_path(var_name),
                                      lower=var.get('RangeMin'),
                                      upper=var.get('RangeMax'))
            elif var['type'] == 'enum':
                driver_vars.append((var_name, var['items'][0], {
                    "pass_by_obj": True
                }))
                formatted_name = get_desvar_path(var_name)
                top.driver.add_desvar(formatted_name)
                top.driver._desvars[formatted_name]['type'] = var['type']
                top.driver._desvars[formatted_name]['items'] = var['items']
            elif var['type'] == 'int':
                driver_vars.append((var_name, 0.0))
                formatted_name = get_desvar_path(var_name)
                top.driver.add_desvar(formatted_name,
                                      lower=var.get('RangeMin'),
                                      upper=var.get('RangeMax'))
                top.driver._desvars[formatted_name]['type'] = var['type']
            else:
                raise ValueError(
                    'Unimplemented designVariable type "{}"'.format(
                        var['type']))

    for subProblemName, subProblemConfig in six.iteritems(
            mdao_config.get('subProblems', {})):
        subProblemDir = os.path.join(original_dir, subProblemName)

        with with_problem(subProblemConfig, subProblemDir,
                          is_subproblem=True) as (subProblem, inputMeta,
                                                  outputMeta):
            root.add(subProblemName, subProblem)
            subProblemInputMeta[subProblemName] = inputMeta
            subProblemOutputMeta[subProblemName] = outputMeta

    if is_subproblem:
        subProblemInputs = []
        inputMeta = {}
        for name, problemInput in six.iteritems(mdao_config['problemInputs']):
            if problemInput.get("innerSource"):
                if problemInput["innerSource"][0] in mdao_config['drivers']:
                    path = get_desvar_path(problemInput["innerSource"][1])
                else:
                    path = '{}.{}'.format(problemInput["innerSource"][0],
                                          problemInput["innerSource"][1])

                subProblemInputs.append(path)
                inputMeta[name] = path
            else:
                # TODO: How important is it to figure out the correct type here?
                #   We might be able to infer the type from a component that connects to
                #   this ProblemInput, but might have to refer to something outside the
                #   subproblem
                (initial_value,
                 pass_by_obj) = get_problem_input_value(problemInput)

                root.add(
                    name,
                    IndepVarComp(name, initial_value, pass_by_obj=pass_by_obj))
                path = "{0}.{0}".format(name)
                subProblemInputs.append(path)
                inputMeta[name] = path

        # TODO: Handle direct connection between ProblemInput and ProblemOutput (single-element Source)
        # TODO: Pass-through ExecComps to allow direct ProblemInput->ProblemOutput connections to behave
        subProblemOutputs = []
        outputMeta = {}
        for name, source in six.iteritems(mdao_config['problemOutputs']):
            if len(source) == 1:
                if source[0] in mdao_config[
                        'problemInputs'] and 'innerSource' in mdao_config[
                            'problemInputs'][source[0]]:
                    # Assume inner source is a design variable
                    desvar = driver['designVariables']
                    passByObj = False
                    if desvar.get('type', 'double') == 'double':
                        initialVal = 0.0
                    elif desvar['type'] == 'enum':
                        initialVal = ''
                        # TODO or maybe initialVal = 0.0
                    elif desvar['type'] == 'int':
                        initialVal = 0
                    else:
                        raise ValueError(
                            'Unimplemented designVariable type "{}"'.format(
                                desvar['type']))
                else:
                    if source[0] in mdao_config['problemInputs']:
                        (initialVal, passByObj) = get_problem_input_value(
                            mdao_config['problemInputs'][source[0]])
                    else:
                        raise ValueError(
                            'Missing ProblemOutput source: {}'.format(
                                source[0]))
                comp_name = "pass_through_{}".format(name)
                comp = PassThroughComponent()
                comp.add_var(name, initialVal)
                root.add(comp_name, comp)
                inputPath = "{}.{}".format(comp_name, name)
                path = "{}.{}_out".format(comp_name, name)
                root.connect(inputMeta[source[0]], inputPath)
            else:
                if source[0] in mdao_config['drivers']:
                    # TODO: If it's legal for this desvar to also point to a ProblemInput,
                    # we need to create a PassThroughComponent just like above
                    this_driver = mdao_config['drivers'][source[0]]

                    if source[1] in this_driver.get('designVariables', {}):
                        path = get_desvar_path(source[1])
                    else:  # Source is an objective, ivar, or constraint; need to get the actual source
                        if source[1] in this_driver.get('objectives', {}):
                            driver_output_type = 'objectives'
                        elif source[1] in this_driver.get('constraints', {}):
                            driver_output_type = 'constraints'
                        elif source[1] in this_driver.get(
                                'intermediateVariables', {}):
                            driver_output_type = 'intermediateVariables'
                        else:
                            raise ValueError(
                                'Driver output "{}"" not found'.format(
                                    source[1]))
                        real_source = this_driver[driver_output_type][
                            source[1]]['source']
                        if real_source[0] in mdao_config['subProblems']:
                            unknown_name = subProblemOutputMeta[
                                real_source[0]][real_source[1]]
                            path = '{}.{}'.format(real_source[0], unknown_name)
                        else:
                            path = '{}.{}'.format(real_source[0],
                                                  real_source[1])
                elif source[0] in mdao_config['subProblems']:
                    unknown_name = subProblemOutputMeta[source[0]][source[1]]
                    path = '{}.{}'.format(source[0], unknown_name)
                else:
                    path = '{}.{}'.format(source[0], source[1])

            subProblemOutputs.append(path)
            outputMeta[name] = path

    def get_sorted_components():
        """Apply Tarjan's algorithm to the Components."""
        visited = {}
        tbs_sorted = []

        def get_ordinal(name):
            ordinal = visited.get(name, -1)
            if ordinal is None:
                raise ValueError('Loop involving component "{}"'.format(name))
            if ordinal != -1:
                return ordinal
            component = mdao_config['components'][name]
            visited[name] = None
            ordinal = 0
            for source in (
                    param.get('source')
                    for param in component.get('parameters', {}).values()):
                if not source:
                    continue
                if source[0] in mdao_config['drivers']:
                    continue
                if source[0] in mdao_config.get('problemInputs', {}):
                    continue
                if source[0] in mdao_config.get('subProblems', {}):
                    continue
                ordinal = max(ordinal, get_ordinal(source[0]) + 1)
            visited[name] = ordinal
            tbs_sorted.append(name)
            return ordinal

        for component_name in mdao_config['components']:
            get_ordinal(component_name)
        return tbs_sorted

    tbs_sorted = get_sorted_components()

    # TestBenchComponents look at params they're connected to, so create them last
    def is_testbenchcomponent(component_name):
        return mdao_config['components'][component_name].get(
            'type', 'TestBenchComponent') == 'TestBenchComponent'

    tbs_sorted = sorted(tbs_sorted, key=is_testbenchcomponent)
    for component_name in tbs_sorted:
        component = mdao_config['components'][component_name]
        mdao_component = instantiate_component(component, component_name,
                                               mdao_config, root,
                                               subProblemOutputMeta)
        root.add(component_name, mdao_component)

    for component_name, component in six.iteritems(mdao_config['components']):
        for parameter_name, parameter in six.iteritems(
                component.get('parameters', {})):
            if parameter.get('source'):
                source = parameter['source']
                if len(source) == 1 and is_subproblem:
                    # Points to a top-level ProblemInput; look up what design variable it points to and reference that IVC
                    problemInputName = source[0]
                    if problemInputName in inputMeta:
                        root.connect(
                            inputMeta[problemInputName], '{}.{}'.format(
                                component_name,
                                _get_param_name(parameter_name,
                                                component.get('type'))))
                    else:
                        # TODO: Warn or fail if name isn't found?
                        print("WARNING: Missing problem input reference")
                else:
                    if source[0] in mdao_config['drivers']:
                        # print('driver{}.{}'.format(source[1], source[1]))
                        root.connect(
                            get_desvar_path(source[1]), '{}.{}'.format(
                                component_name,
                                _get_param_name(parameter_name,
                                                component.get('type'))))
                    elif source[0] in mdao_config.get('subProblems', {}):
                        # Map subproblem output name to actual unknown name
                        unknown_name = subProblemOutputMeta[source[0]][
                            source[1]]
                        root.connect(
                            '{}.{}'.format(source[0], unknown_name),
                            '{}.{}'.format(
                                component_name,
                                _get_param_name(parameter_name,
                                                component.get('type'))))
                    else:
                        root.connect(
                            '{}.{}'.format(source[0], source[1]),
                            '{}.{}'.format(
                                component_name,
                                _get_param_name(parameter_name,
                                                component.get('type'))))
            else:
                pass  # TODO warn or fail?

    for subProblemName, subProblem in six.iteritems(
            mdao_config.get('subProblems', {})):
        for problemInputName, problemInput in six.iteritems(
                subProblem.get('problemInputs', {})):
            if problemInput.get('outerSource'):
                source = problemInput['outerSource']
                subProblemInputPath = subProblemInputMeta[subProblemName][
                    problemInputName]

                if len(source) == 1 and is_subproblem:
                    problemInputName = source[0]
                    if problemInputName in inputMeta:
                        root.connect(
                            inputMeta[problemInputName],
                            '{}.{}'.format(subProblemName,
                                           subProblemInputPath))
                    else:
                        # TODO: Warn or fail if name isn't found?
                        print("WARNING: Missing problem input reference")
                else:
                    if source[0] in mdao_config['drivers']:
                        # print('driver{}.{}'.format(source[1], source[1]))
                        root.connect(
                            get_desvar_path(source[1]),
                            '{}.{}'.format(subProblemName,
                                           subProblemInputPath))
                    elif source[0] in mdao_config['subProblems']:
                        # Map subproblem output name to actual unknown name
                        unknown_name = subProblemOutputMeta[source[0]][
                            source[1]]
                        root.connect(
                            '{}.{}'.format(source[0], unknown_name),
                            '{}.{}'.format(subProblemName,
                                           subProblemInputPath))
                    else:
                        root.connect(
                            '{}.{}'.format(source[0], source[1]),
                            '{}.{}'.format(subProblemName,
                                           subProblemInputPath))
            else:
                print("Failed to connect")
                pass  # TODO warn or fail?

    # TODO: Make sure objectives/constraints coming from subproblems and
    # from ProblemInputs/ProblemOutputs are handled correctly
    if driver is not None and driver['type'] == 'optimizer':
        for objective in six.itervalues(driver['objectives']):
            if objective["source"][0] in mdao_config.get('subProblems', {}):
                unknown_name = subProblemOutputMeta[objective["source"][0]][
                    objective["source"][1]]
                top.driver.add_objective("{}.{}".format(
                    objective["source"][0], unknown_name))
            else:
                top.driver.add_objective(str('.'.join(objective['source'])))

        for constraint in six.itervalues(driver['constraints']):
            if constraint['source'][0] in mdao_config['drivers']:
                # References the driver; need to get the path to the design var
                constraintPath = get_desvar_path(constraint['source'][1])
            elif constraint['source'][0] in mdao_config.get('subProblems', {}):
                unknown_name = subProblemOutputMeta[objective.source[0]][
                    objective.source[1]]
                constraintPath = "{}.{}".format(objective.source[0],
                                                unknown_name)
            else:
                constraintPath = str('.'.join(constraint['source']))

            if 'RangeMin' in constraint and 'RangeMax' in constraint:
                top.driver.add_constraint(constraintPath,
                                          lower=constraint['RangeMin'],
                                          upper=constraint['RangeMax'])
            elif 'RangeMin' in constraint and 'RangeMax' not in constraint:
                top.driver.add_constraint(constraintPath,
                                          lower=constraint['RangeMin'])
            elif 'RangeMin' not in constraint and 'RangeMax' in constraint:
                top.driver.add_constraint(constraintPath,
                                          upper=constraint['RangeMax'])
            else:
                pass  # TODO: No min or max provided with constraint; warn or fail here?

    def add_recorders():
        recorders = []
        design_var_map = {
            get_desvar_path(designVariable): designVariable
            for designVariable in driver['designVariables']
        }
        objective_map = {
            '{}.{}'.format(objective['source'][0], objective['source'][1]):
            objective_name
            for objective_name, objective in six.iteritems(
                driver['objectives'])
        }
        intermediate_var_map = {
            '{}.{}'.format(intermediate_var['source'][0],
                           intermediate_var['source'][1]):
            intermediate_var_name
            for intermediate_var_name, intermediate_var in six.iteritems(
                driver.get('intermediateVariables', {}))
        }
        constants_map = {}
        for name, constant in (
                c for c in six.iteritems(mdao_config['components'])
                if c[1].get('type', 'TestBenchComponent') == 'IndepVarComp'):
            constants_map.update({
                '{}.{}'.format(name, unknown): unknown
                for unknown in constant['unknowns']
            })

        constraints_map = {
            '{}.{}'.format(constraint['source'][0], constraint['source'][1]):
            constraint_name
            for constraint_name, constraint in six.iteritems(
                driver.get('constraints', {}))
            if constraint['source'][0] not in mdao_config['drivers']
        }  # All constraints that don't point back to design variables

        unknowns_map = defaultdict(list)

        def add_to_unknowns(map):
            for key, val in six.iteritems(map):
                unknowns_map[key].append(val)

        add_to_unknowns(design_var_map)
        add_to_unknowns(objective_map)
        add_to_unknowns(intermediate_var_map)
        add_to_unknowns(constants_map)
        add_to_unknowns(constraints_map)

        new_unknowns_map = defaultdict(list)
        # Locate/fix any unknowns that point to subproblem outputs
        for unknown_path, unknown_names in six.iteritems(unknowns_map):
            for unknown_name in unknown_names:
                split_path = unknown_path.split('.')
                if split_path[0] in subProblemOutputMeta:
                    split_path[1] = subProblemOutputMeta[split_path[0]][
                        split_path[1]]
                    new_path = '.'.join(split_path)
                    new_unknowns_map[new_path].append(unknown_name)
                else:
                    new_unknowns_map[unknown_path].append(unknown_name)

        unknowns_map = new_unknowns_map

        for recorder in mdao_config.get('recorders', [{
                'type': 'DriverCsvRecorder',
                'filename': 'output.csv'
        }]):
            if recorder['type'] == 'DriverCsvRecorder':
                mode = 'w'
                exists = os.path.isfile(recorder['filename'])
                if RestartRecorder.is_restartable(original_dir) or append_csv:
                    mode = 'a'
                if six.PY2:
                    mode += 'b'
                    open_kwargs = {}
                else:
                    open_kwargs = {'newline': ''}
                recorder = MappingCsvRecorder({},
                                              unknowns_map,
                                              io.open(recorder['filename'],
                                                      mode, **open_kwargs),
                                              include_id=recorder.get(
                                                  'include_id', False))
                if (append_csv and exists) or mode == 'ab':
                    recorder._wrote_header = True
            elif recorder['type'] == 'AllCsvRecorder':
                mode = 'w'
                if six.PY2:
                    mode += 'b'
                    open_kwargs = {}
                else:
                    open_kwargs = {'newline': ''}
                recorder = CsvRecorder(
                    out=io.open(recorder['filename'], mode, **open_kwargs))
            elif recorder['type'] == 'CouchDBRecorder':
                recorder = CouchDBRecorder(
                    recorder.get('url', 'http://localhost:5984/'),
                    recorder['run_id'])
                recorder.options['record_params'] = True
                recorder.options['record_unknowns'] = True
                recorder.options['record_resids'] = False
                recorder.options['includes'] = list(unknowns_map.keys())
            else:
                mod_name = '.'.join(recorder['type'].split('.')[:-1])
                class_name = recorder['type'].split('.')[-1]
                recorder = getattr(importlib.import_module(mod_name),
                                   class_name)()

            top.driver.add_recorder(recorder)
        return recorders

    if is_subproblem:
        recorders = []

        # print("SubProblemInputs:", subProblemInputs)
        # print("SubProblemOutputs:", subProblemOutputs)

        # print("InputMeta:", inputMeta)
        # print("OutputMeta:", outputMeta)

        top.setup()

        subProblem = SubProblem(top,
                                params=subProblemInputs,
                                unknowns=subProblemOutputs)

        yield (subProblem, inputMeta, outputMeta)
    else:
        if driver:
            recorders = add_recorders()
        else:
            recorders = []
        for recorder in additional_recorders:
            recorders.append(recorder)
            top.driver.add_recorder(recorder)

        if profile:
            openmdao_profile.setup(top)
            openmdao_profile.start()

        try:
            top.setup()
            # from openmdao.devtools.debug import dump_meta
            # dump_meta(top.root)

            # for subproblemName in six.iterkeys(mdao_config['subProblems']):
            #     subProblem = top.root.find_subsystem(subproblemName)
            #     subProblem._problem.root.dump(verbose=True)

            # top.root.dump(verbose=True)
            yield top
        finally:
            for recorder in recorders:
                recorder.close()