简体   繁体   English

如何在 Dymos 问题中定义矩阵参数?

[英]How do I define matrix parameters in a Dymos problem?

I'm trying to setup a dynamic optimization with dymos where I have an analysis upstream of my dymos trajectory.我正在尝试使用 dymos 设置动态优化,我在我的 dymos 轨迹的上游进行了分析。 This upstream analysis computes some 2D-matrix K .此上游分析计算一些二维矩阵K I want to pass this matrix into my dymos problem.我想将这个矩阵传递到我的 dymos 问题中。 According to the documentation (and how I've done this in the past) is to add K as a paramter to the trajectory:根据文档(以及我过去是如何做到的)是将K作为参数添加到轨迹中:

traj.add_parameter('K',targets={'phase0':['K'],opt=False,static_target=True) . traj.add_parameter('K',targets={'phase0':['K'],opt=False,static_target=True)

However, this returns an error because static_target expects K to be a scalar.但是,这会返回一个错误,因为 static_target 期望K是一个标量。 If I have static_target=False , this also returns an error because it expects K to have some dimension related to the number of nodes in the trajectory.如果我有static_target=False ,这也会返回一个错误,因为它期望K具有与轨迹中的节点数相关的某个维度。

Is there something I'm missing here?我在这里缺少什么吗? Is it sufficient to manually connect K to the trajectory via p.model.connect('K','traj.phase0.rhs_disc.K') and p.model.connect('K','traj.phase0.rhs_col.K') ?通过p.model.connect('K','traj.phase0.rhs_disc.K')p.model.connect('K','traj.phase0.rhs_col.K') ' 手动连接K到轨迹是否足够p.model.connect('K','traj.phase0.rhs_col.K') Or will that create issues in how dymos works the problem.或者这会在 dymos 如何解决问题方面产生问题。

It doesn't seem appropriate to vectorize K either.向量化K似乎也不合适。

Any suggestions are greatly appreciated.非常感谢任何建议。

In my opinion, the easiest way to connect parameters from trajectory to phase is to add the parameter to both the Trajectory and the phases in which it is to be used.在我看来,将参数从轨迹连接到相位的最简单方法是将参数添加到轨迹和要使用它的相位。

Consider a simple oscillator where the mass, spring constant, and dampening coefficient are given as a single size-3 input.考虑一个简单的振荡器,其中质量、弹簧常数和阻尼系数作为单个 size-3 输入给出。

In this case, I used OpenMDAO's tags feature and a special dymos tag dymos.static_target so that dymos realizes the target isn't shaped with a different value at each node.在这种情况下,我使用了 OpenMDAO 的标签功能和一个特殊的 dymos 标签dymos.static_target ,这样 dymos 就可以意识到目标在每个节点上都没有不同的值。 I think its a bit easier to do it this way as opposed to having to add it later at the add_parameter call.我认为这样做比稍后在add_parameter调用中添加它要容易一些。

class OscillatorODEVectorParam(om.ExplicitComponent):
    """
    A Dymos ODE for a damped harmonic oscillator.
    """

    def initialize(self):
        self.options.declare('num_nodes', types=int)

    def setup(self):
        nn = self.options['num_nodes']

        # Inputs
        self.add_input('x', shape=(nn,), desc='displacement', units='m')
        self.add_input('v', shape=(nn,), desc='velocity', units='m/s')
        self.add_input('constants', shape=(3,), units=None,
                       desc='a vector of mass, spring constant, and damping coefficient [m, k, c]',
                       tags=['dymos.static_target'])

        self.add_output('v_dot', val=np.zeros(nn), desc='rate of change of velocity', units='m/s**2')

        self.declare_coloring(wrt='*', method='fd')

    def compute(self, inputs, outputs):
        x = inputs['x']
        v = inputs['v']

        m, k, c = inputs['constants']

        f_spring = -k * x
        f_damper = -c * v

        outputs['v_dot'] = (f_spring + f_damper) / m

To use the ODE, we have a problem with a single trajectory and in this case, as single phase.要使用 ODE,我们有一个单一轨迹的问题,在这种情况下,作为单相。

Again, in my opinion, the clearest way to link parameters from the trajectory to phases is to add them in both places with the same name.同样,在我看来,将参数从轨迹链接到阶段的最清晰的方法是在两个地方添加相同名称的参数。 Dymos will perform some introspection and automatically link them up. Dymos 将执行一些内省并自动将它们链接起来。

    def test_ivp_driver_shaped_param(self):
        import openmdao.api as om
        import dymos as dm
        import matplotlib.pyplot as plt
        # plt.switch_backend('Agg')  # disable plotting to the screen

        from dymos.examples.oscillator.oscillator_ode import OscillatorODEVectorParam

        # Instantiate an OpenMDAO Problem instance.
        prob = om.Problem()

        # We need an optimization driver.  To solve this simple problem ScipyOptimizerDriver will work.
        prob.driver = om.ScipyOptimizeDriver()

        # Instantiate a Phase
        phase = dm.Phase(ode_class=OscillatorODEVectorParam, transcription=dm.Radau(num_segments=10))

        # Tell Dymos that the duration of the phase is bounded.
        phase.set_time_options(fix_initial=True, fix_duration=True)

        # Tell Dymos the states to be propagated using the given ODE.
        phase.add_state('x', fix_initial=True, rate_source='v', targets=['x'], units='m')
        phase.add_state('v', fix_initial=True, rate_source='v_dot', targets=['v'], units='m/s')

        # The spring constant, damping coefficient, and mass are inputs to the system that are
        # constant throughout the phase.
        # Declare this parameter on phase and then we'll feed its value from the parent trajectory.
        phase.add_parameter('constants', units=None)

        # Since we're using an optimization driver, an objective is required.  We'll minimize
        # the final time in this case.
        phase.add_objective('time', loc='final')

        # Instantiate a Dymos Trajectory and add it to the Problem model.
        traj = prob.model.add_subsystem('traj', dm.Trajectory())

        traj.add_phase('phase0', phase)

        # This parameter value will connect to any phase with a parameter named constants by default.
        # This is the easiest way, in my opinion, to pass parameters from trajectory to phase.
        traj.add_parameter('constants', units=None, opt=False)

        # Setup the OpenMDAO problem
        prob.setup()

        # Assign values to the times and states
        prob.set_val('traj.phase0.t_initial', 0.0)
        prob.set_val('traj.phase0.t_duration', 15.0)

        prob.set_val('traj.phase0.states:x', 10.0)
        prob.set_val('traj.phase0.states:v', 0.0)

        #                                            m    k    c
        prob.set_val('traj.parameters:constants', [1.0, 1.0, 0.5])

        # Now we're using the optimization driver to iteratively run the model and vary the
        # phase duration until the final y value is 0.
        prob.run_driver()

        # Perform an explicit simulation of our ODE from the initial conditions.
        sim_out = traj.simulate(times_per_seg=50)

        # Plot the state values obtained from the phase timeseries objects in the simulation output.
        t_sol = prob.get_val('traj.phase0.timeseries.time')
        t_sim = sim_out.get_val('traj.phase0.timeseries.time')

        states = ['x', 'v']
        fig, axes = plt.subplots(len(states), 1)
        for i, state in enumerate(states):
            sol = axes[i].plot(t_sol, prob.get_val(f'traj.phase0.timeseries.states:{state}'), 'o')
            sim = axes[i].plot(t_sim, sim_out.get_val(f'traj.phase0.timeseries.states:{state}'), '-')
            axes[i].set_ylabel(state)
        axes[-1].set_xlabel('time (s)')
        fig.legend((sol[0], sim[0]), ('solution', 'simulation'), 'lower right', ncol=2)
        plt.tight_layout()
        plt.show()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM