OpenMDAO1.x: трудности с доступом к переменным, неявно связанным через несколько групп.

У меня проблемы с доступом к переменным, которые неявно связаны через несколько слоев групп. Согласно документации:

В новом OpenMDAO группы НЕ являются компонентами и не имеют собственных переменных. Переменные могут быть повышены до уровня группы путем передачи аргумента продвижения в вызов добавления, например,

group = Group()
group.add('comp1', Times2(), promotes=['x'])

Это позволит получить доступ к переменной x, принадлежащей comp1, через group.params[‘x’].

Однако, когда я пытаюсь получить доступ к переменным подгрупп, я получаю ошибки. См. пример ниже, который показывает рабочий и нерабочий пример:

from openmdao.api import Component, Group, Problem
import numpy as np


class Times2(Component):
    def __init__(self):
        super(Times2, self).__init__()
        self.add_param('x', 1.0, desc='my var x')
        self.add_output('y', 2.0, desc='my var y')

    def solve_nonlinear(self, params, unknowns, resids):
        unknowns['y'] = params['x'] * 2.0

    def linearize(self, params, unknowns, resids):
        J = {}
        J[('y', 'x')] = np.array([2.0])
        return J


class PassGroup1(Group):

    def __init__(self):
        super(PassGroup1, self).__init__()

        self.add('t1', Times2(), promotes=['*'])


class PassGroup2(Group):

    def __init__(self):
        super(PassGroup2, self).__init__()

        self.add('g1', PassGroup1(), promotes=['*'])


prob = Problem(root=Group())
prob.root.add('comp', PassGroup2(), promotes=['*'])
prob.setup()
prob.run()

# this works
print prob.root.comp.g1.t1.params['x']

# this does not
print prob.root.params['x']

Не могли бы вы объяснить, почему это не работает, и как я могу сделать переменные доступными для верхнего уровня без знания групп нижнего уровня?


person jthomas    schedule 11.11.2015    source источник


Ответы (1)


Есть несколько ответов на ваш вопрос. Во-первых, я укажу, что у вас есть то, что мы называем "зависающим параметром". Под этим я подразумеваю параметр компонента (или связанный с несколькими компонентами через продвижение и/или соединение), который не имеет связанной с ним конечной переменной src. Итак, просто для полного понимания, нужно сказать, что для OpenMDAO зависание параметров не является проблемой. Для удобства пользователя мы предоставляем простой способ установить его значение в экземпляре задачи, но мы никогда не передаем с ним какие-либо данные или что-либо еще во время выполнения.

В общем случае, когда x является проектной переменной для оптимизатора, вы должны создать IndepVarComp, чтобы предоставить src для этого значения. Но поскольку в вашем примере нет оптимизатора, технически нет ничего неправильного в том, чтобы не учитывать IndepVarComp.

Для более прямого ответа на ваш вопрос вам не следует обращаться к словарям params на любом подуровне. Я не могу придумать вескую причину для этого как пользователь. Если вы придерживаетесь problem['x'], вы никогда не ошибетесь.

Но поскольку вы спросили, вот подробности того, что на самом деле происходит для слегка измененного случая, который позволяет иметь фактический параметр.

from openmdao.api import Component, Group, Problem
import numpy as np


class Plus1(Component): 
    def __init__(self): 
        super(Plus1, self).__init__()
        self.add_param('w', 4.0)
        self.add_output('x', 5.0)

    def solve_nonlinear(self, params, unknowns, resids): 
        unknowns['x'] = params['w'] + 1

    def linearize(self, params, unknowns, resids): 
        J = {}
        J['x', 'w'] = 1
        return J


class Times2(Component):
    def __init__(self):
        super(Times2, self).__init__()
        self.add_param('x', 1.0, desc='my var x')
        self.add_output('y', 2.0, desc='my var y')

    def solve_nonlinear(self, params, unknowns, resids):
        unknowns['y'] = params['x'] * 2.0

    def linearize(self, params, unknowns, resids):
        J = {}
        J[('y', 'x')] = np.array([2.0])
        return J


class PassGroup1(Group):

    def __init__(self):
        super(PassGroup1, self).__init__()

        self.add('t1', Times2(), promotes=['x','y'])


class PassGroup2(Group):

    def __init__(self):
        super(PassGroup2, self).__init__()

        self.add('g1', PassGroup1(), promotes=['x','y'])
        self.add('p1', Plus1(), promotes=['w','x'])


prob = Problem(root=Group())
prob.root.add('comp', PassGroup2(), promotes=['w','x','y'])
prob.setup()
prob.run()

# this works
print prob.root.comp.g1.t1.params['x']

# this does not
print prob.root.comp.params.keys()

Обратите внимание, что в моем примере «x» больше не может быть установлен пользователем. Теперь он вычисляется как «p1». Вместо этого «w» теперь является параметром, устанавливаемым пользователем. Это было необходимо, чтобы проиллюстрировать, как работают параметры.

Теперь, когда на самом деле происходит некоторая передача данных, за которую отвечает OpenMDAO, вы можете более четко увидеть фактический шаблон. В корне вообще нет параметров (не считая всяких висящих params). Все, с точки зрения корней, неизвестно, потому что на этом уровне за все отвечает src. Спуститесь на один уровень вниз, где есть p1 и g1, и теперь есть параметр g1, для которого p1 является источником, поэтому передача некоторых данных должна происходить на этом уровне иерархии. Таким образом, g1 имеет запись в своем словаре параметров, g1.t1.x. Почему полный путь? Весь учет параметров ведется с полными путями по разным причинам, выходящим за рамки этого ответа. Но это также еще одна мотивация для работы с ярлыком в problem, потому что он будет работать с относительными (или продвинутыми) именами.

person Justin Gray    schedule 12.11.2015