你如何为数组 w.r.t 标量声明部分,反之亦然?

How do you declare partials for arrays w.r.t scalars, and vice versa?

我正在尝试执行此计算:

使用以下代码:

class BendingMoment(om.ExplicitComponent):

    def setup(self):

        self.add_input('cableForce', units='N')
        self.add_input('cableAngle', units='rad')
        self.add_input('cablePosition', units='m')
        self.add_input('FArray', units='N/m')
        self.add_input('zArray', units='m')
        self.add_input('wingspan', units='m')

        self.add_output('MArray', units='N*m')

        self.declare_partials('MArray', ['FArray', 'cableForce', 'cableAngle', 'cablePosition', 'wingspan', 'zArray'])


    def compute(self, inputs, outputs):

        Fc = inputs['cableForce']
        ang = inputs['cableAngle']
        FArray = inputs['FArray']
        zArray = inputs['zArray']
        b = inputs['wingspan']
        a = inputs['cablePosition']

        M = np.zeros_like(zArray)

        for i in range(len(zArray)):
            M[i] = np.trapz((zArray[i:]-zArray[i])*FArray[i:], zArray[i:]) - (0.5*b - a - zArray[i])*Fc*np.sin(ang)

        outputs['MArray'] = M

其中Fc、ang、b、a是标量; FArray, zArray 是数组。

你如何为 M w.r.t 声明一个标量,即 Fc?

如果有一段代码获取一个数组并对它求和或积分以创建一个标量值,您将如何声明它的部分值?对于这两种情况,雅可比行列式究竟是什么样子的?

考虑大小为 (n0,n1) 的输出 Y 和大小为 (m0, m1) 的输入 X --- 两者都是二维数组,使事情更通用一些。然后,您向 OpenMDAO 提供的关于 Y 相对于 X 的导数的雅可比矩阵的大小将是一个大小为 (n0*n1, m0*m1) 的矩阵——即n0*n1 行和 m0*m1 列。

你得到的行数总是等于数组的展平大小(并且行的顺序与展平数组的顺序相同)。它对输入的工作方式类似。

查看偏导数雅可比行列式并查看您是否正确求导的最佳方法是使用 check_partials method,它将您的解析导数与近似导数进行比较。你可以使用它,即使你还没有定义你的导数——你只会得到所有的零,你可以通过在下面的代码中注释掉 compute_partials 来检查:

import numpy as np

import openmdao.api as om

class BendingMoment(om.ExplicitComponent):

    def setup(self):

        self.add_input('cableForce', units='N')
        self.add_input('cableAngle', units='rad')
        self.add_input('cablePosition', units='m')
        self.add_input('wingspan', units='m')

        self.add_input('FArray', units='N/m', shape=3)
        self.add_input('zArray', units='m', shape=3)

        self.add_output('MArray', units='N*m', shape=3)

        self.declare_partials('MArray', ['FArray', 'cableForce', 'cableAngle', 'cablePosition', 'wingspan', 'zArray'])


    def compute(self, inputs, outputs):

        Fc = inputs['cableForce']
        ang = inputs['cableAngle']
        FArray = inputs['FArray']
        zArray = inputs['zArray']
        b = inputs['wingspan']
        a = inputs['cablePosition']

        M = outputs['MArray']

        size = len(zArray)

        # we can use np.trapz, only because we can "guess" how its implemented so that we can differentiate it
        # for i in range(size):
        #     M[i] = np.trapz((zArray[i:]-zArray[i])*FArray[i:], zArray[i:]) - (0.5*b - a - zArray[i])*Fc*np.sin(ang)

        # equivalent code to np.trapz written out with for loops explicitly. This code would be slower to run,
        # so we'll leave the call to the numpy function, but we'll look at this to compute the partial derivatives
        M[:] = 0 # clear out the array 
        for i in range(size): 
            for j in range(i, size-1): 
                M[i] += (zArray[j+1] - zArray[j]) * ((zArray[j] - zArray[i])*FArray[j] + (zArray[j+1] - zArray[i])*FArray[j+1])/2.

                # # note that for i=j, you get a special case
                # if i == j: 
                #     M[i] += (zArray[j+1] - zArray[j]) * (zArray[j+1] - zArray[i])*FArray[j+1]/2.
                # else: 
                #     M[i] += (zArray[j+1] - zArray[j]) * ((zArray[j] - zArray[i])*FArray[j] + (zArray[j+1] - zArray[i])*FArray[j+1])/2.

            M[i] -= (0.5*b - a - zArray[i])*Fc*np.sin(ang)


    def compute_partials(self, inputs, J): 

        Fc = inputs['cableForce']
        ang = inputs['cableAngle']
        FArray = inputs['FArray']
        zArray = inputs['zArray']
        b = inputs['wingspan']
        a = inputs['cablePosition']

        size = len(zArray)

        # We can't easily compute the partials for this component, 
        # because we don't know exactly what np.trapz does.

        for i in range(size): 
            for j in range(i, size-1): 
                J['MArray', 'FArray'][i,j] += (zArray[j+1] - zArray[j]) * (zArray[j] - zArray[i])/2.
                J['MArray', 'FArray'][i,j+1] += (zArray[j+1] - zArray[j]) * (zArray[j+1] - zArray[i])/2.


                term1 = (zArray[j+1] - zArray[j])
                term2 = ((zArray[j] - zArray[i])*FArray[j] + (zArray[j+1] - zArray[i])*FArray[j+1])/2.
                # z portion
                if i == j: # z == Array[j] -- special case
                    J['MArray', 'zArray'][i,i] += -term2 - term1 * (FArray[j+1])/2.
                else: 
                    J['MArray', 'zArray'][i,i] += -term1*(FArray[j+1]+FArray[j+1])/2.                
                    J['MArray', 'zArray'][i,j] += - term2 + term1*FArray[j]/2.                
                J['MArray', 'zArray'][i,j+1] += term2 + term1*FArray[j+1]/2.

            # account for the additional term with zArray in it
            J['MArray', 'zArray'][i,i] += Fc*np.sin(ang)

            J['MArray', 'cableAngle'][i] = -(0.5*b - a - zArray[i])*Fc*np.cos(ang)
            J['MArray', 'cableForce'][i] = -(0.5*b - a - zArray[i])*np.sin(ang)
            J['MArray', 'cablePosition'][i] = a*Fc*np.sin(ang)
            J['MArray', 'wingspan'][i] = -0.5*b*Fc*np.sin(ang)


if __name__ == '__main__': 

    p = om.Problem()

    p.model.add_subsystem('bending', BendingMoment(), promotes=['*'])

    p.setup()

    # note: leaving the zArray with the default value means it would be [1,1,1] 
    #       which is non-physical, and will be a degenerate point to check the partials around. 
    #       So we'll set it to a linear distribution
    p['bending.zArray'] = np.linspace(0,10,3)

    p.run_model()


    p.check_partials()

给出此输出:

----------------------------------
Component: BendingMoment 'bending'
----------------------------------
  bending: 'MArray' wrt 'FArray'
    Forward Magnitude : 3.750000e+01
         Fd Magnitude : 3.750000e+01 (fd:forward)
    Absolute Error (Jfor  - Jfd) : 6.343738e-09

    Relative Error (Jfor  - Jfd) : 1.691664e-10

    Raw Forward Derivative (Jfor)
[[ 0.  25.  25. ]
 [ 0.   0.  12.5]
 [ 0.   0.   0. ]]

    Raw FD Derivative (Jfd)
[[ 0.         24.99999999 25.        ]
 [ 0.          0.         12.5       ]
 [ 0.          0.          0.        ]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  bending: 'MArray' wrt 'cableAngle'
    Forward Magnitude : 6.410044e+00
         Fd Magnitude : 6.410039e+00 (fd:forward)
    Absolute Error (Jfor  - Jfd) : 4.991817e-06 *

    Relative Error (Jfor  - Jfd) : 7.787499e-07

    Raw Forward Derivative (Jfor)
[[0.27015115]
 [2.97166268]
 [5.67317421]]

    Raw FD Derivative (Jfd)
[[0.27015094]
 [2.97166037]
 [5.67316979]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  bending: 'MArray' wrt 'cableForce'
    Forward Magnitude : 9.983052e+00
         Fd Magnitude : 9.983052e+00 (fd:forward)
    Absolute Error (Jfor  - Jfd) : 3.700281e-09

    Relative Error (Jfor  - Jfd) : 3.706563e-10

    Raw Forward Derivative (Jfor)
[[0.42073549]
 [4.62809042]
 [8.83544534]]

    Raw FD Derivative (Jfd)
[[0.42073549]
 [4.62809042]
 [8.83544534]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  bending: 'MArray' wrt 'cablePosition'
    Forward Magnitude : 1.457470e+00
         Fd Magnitude : 1.457470e+00 (fd:forward)
    Absolute Error (Jfor  - Jfd) : 9.138199e-10

    Relative Error (Jfor  - Jfd) : 6.269904e-10

    Raw Forward Derivative (Jfor)
[[0.84147098]
 [0.84147098]
 [0.84147098]]

    Raw FD Derivative (Jfd)
[[0.84147099]
 [0.84147099]
 [0.84147099]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  bending: 'MArray' wrt 'wingspan'
    Forward Magnitude : 7.287352e-01
         Fd Magnitude : 7.287353e-01 (fd:forward)
    Absolute Error (Jfor  - Jfd) : 3.834701e-09

    Relative Error (Jfor  - Jfd) : 5.262132e-09

    Raw Forward Derivative (Jfor)
[[-0.42073549]
 [-0.42073549]
 [-0.42073549]]

    Raw FD Derivative (Jfd)
[[-0.4207355 ]
 [-0.42073549]
 [-0.42073549]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  bending: 'MArray' wrt 'zArray'
    Forward Magnitude : 1.506254e+01
         Fd Magnitude : 1.506254e+01 (fd:forward)
    Absolute Error (Jfor  - Jfd) : 9.989117e-07

    Relative Error (Jfor  - Jfd) : 6.631761e-08

    Raw Forward Derivative (Jfor)
[[-9.15852902  0.         10.        ]
 [ 0.         -4.15852902  5.        ]
 [ 0.          0.          0.84147098]]

    Raw FD Derivative (Jfd)
[[-9.15852851  0.         10.00000049]
 [ 0.         -4.15852852  5.0000005 ]
 [ 0.          0.          0.84147099]]

注意这里给出的代码是正确的,但不一定很快。一旦你有了正确的答案,向量化它们就容易多了。我发现首先使用速度更快的矢量化代码非常困难。我几乎总是将缓慢的 python-for-loop 版本的代码与矢量化代码

一起注释掉