Array IndexingΒΆ

omtools supports indexing into Variable objects for explicit outputs.

In this example, integer indices are used to concatenate multiple expressions/variables into one variable and extract values from a single variable representing an array.

from openmdao.api import Problem
import numpy as np
import omtools.api as ot
from omtools.api import Group


class ExampleInteger(Group):
    def setup(self):
        a = self.declare_input('a', val=0)
        b = self.declare_input('b', val=1)
        c = self.declare_input('c', val=2)
        d = self.declare_input('d', val=7.4)
        e = self.declare_input('e', val=np.pi)
        f = self.declare_input('f', val=9)
        g = e + f
        x = self.create_output('x', shape=(7, ))
        x[0] = a
        x[1] = b
        x[2] = c
        x[3] = d
        x[4] = e
        x[5] = f
        x[6] = g

        # Get value from indices
        self.register_output('x0', x[0])
        self.register_output('x6', x[6])


prob = Problem()
prob.model = ExampleInteger()
prob.setup(force_alloc_complex=True)
prob.run_model()

print('x', prob['x'].shape)
print(prob['x'])
print('x0', prob['x0'].shape)
print(prob['x0'])
print('x6', prob['x6'].shape)
print(prob['x6'])
x (7,)
[ 0.          1.          2.          7.4         3.14159265  9.
 12.14159265]
x0 (1,)
[0.]
x6 (1,)
[12.14159265]

Here is the n2 diagram:

omtools supports specifying ranges as well as individual indices to slice and concatenate arrays.

from openmdao.api import Problem
import numpy as np
import omtools.api as ot
from omtools.api import Group


class ExampleOneDimensional(Group):
    def setup(self):
        n = 20
        u = self.declare_input('u',
                               shape=(n, ),
                               val=np.arange(n).reshape((n, )))
        v = self.declare_input('v',
                               shape=(n - 4, ),
                               val=np.arange(n - 4).reshape((n - 4, )))
        w = self.declare_input('w',
                               shape=(4, ),
                               val=16 + np.arange(4).reshape((4, )))
        x = self.create_output('x', shape=(n, ))
        x[0:n] = 2 * (u + 1)
        y = self.create_output('y', shape=(n, ))
        y[0:n - 4] = 2 * (v + 1)
        y[n - 4:n] = w - 3

        # Get value from indices
        z = self.create_output('z', shape=(3, ))
        z[0:3] = ot.expand(x[2], (3, ))
        self.register_output('x0_5', x[0:5])
        self.register_output('x3_', x[3:])
        self.register_output('x2_4', x[2:4])


prob = Problem()
prob.model = ExampleOneDimensional()
prob.setup(force_alloc_complex=True)
prob.run_model()

print('x', prob['x'].shape)
print(prob['x'])
print('y', prob['y'].shape)
print(prob['y'])
print('z', prob['z'].shape)
print(prob['z'])
print('x0_5', prob['x0_5'].shape)
print(prob['x0_5'])
print('x3_', prob['x3_'].shape)
print(prob['x3_'])
print('x2_4', prob['x2_4'].shape)
print(prob['x2_4'])
x (20,)
[ 2.  4.  6.  8. 10. 12. 14. 16. 18. 20. 22. 24. 26. 28. 30. 32. 34. 36.
 38. 40.]
y (20,)
[ 2.  4.  6.  8. 10. 12. 14. 16. 18. 20. 22. 24. 26. 28. 30. 32. 13. 14.
 15. 16.]
z (3,)
[6. 6. 6.]
x0_5 (5,)
[ 2.  4.  6.  8. 10.]
x3_ (17,)
[ 8. 10. 12. 14. 16. 18. 20. 22. 24. 26. 28. 30. 32. 34. 36. 38. 40.]
x2_4 (2,)
[6. 8.]

omtools supports specifying ranges along multiple axes as well as individual indices and ranges to slice and concatenate arrays.

from openmdao.api import Problem
import numpy as np
import omtools.api as ot
from omtools.api import Group


class ExampleMultidimensional(Group):
    def setup(self):
        # Works with two dimensional arrays
        z = self.declare_input('z',
                               shape=(2, 3),
                               val=np.arange(6).reshape((2, 3)))
        x = self.create_output('x', shape=(2, 3))
        x[0:2, 0:3] = z

        # Also works with higher dimensional arrays
        p = self.declare_input('p',
                               shape=(5, 2, 3),
                               val=np.arange(30).reshape((5, 2, 3)))
        q = self.create_output('q', shape=(5, 2, 3))
        q[0:5, 0:2, 0:3] = p

        # Get value from indices
        self.register_output('r', p[0, :, :])

        # Assign a vector to a slice
        vec = self.create_indep_var(
            'vec',
            shape=(1, 20),
            val=np.arange(20).reshape((1, 20)),
        )
        s = self.create_output('s', shape=(2, 20))
        s[0, :] = vec
        s[1, :] = 2 * vec

        # negative indices
        t = self.create_output('t', shape=(5, 3, 3), val=0)
        t[0:5, 0:-1, 0:3] = p


prob = Problem()
prob.model = ExampleMultidimensional()
prob.setup(force_alloc_complex=True)
prob.run_model()

print('x', prob['x'].shape)
print(prob['x'])
print('q', prob['q'].shape)
print(prob['q'])
print('r', prob['r'].shape)
print(prob['r'])
print('s', prob['s'].shape)
print(prob['s'])
print('t', prob['t'].shape)
print(prob['t'])
stop 2
x (2, 3)
[[0. 1. 2.]
 [3. 4. 5.]]
q (5, 2, 3)
[[[ 0.  1.  2.]
  [ 3.  4.  5.]]

 [[ 6.  7.  8.]
  [ 9. 10. 11.]]

 [[12. 13. 14.]
  [15. 16. 17.]]

 [[18. 19. 20.]
  [21. 22. 23.]]

 [[24. 25. 26.]
  [27. 28. 29.]]]
r (1, 2, 3)
[[[0. 1. 2.]
  [3. 4. 5.]]]
s (2, 20)
[[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9. 10. 11. 12. 13. 14. 15. 16. 17.
  18. 19.]
 [ 0.  2.  4.  6.  8. 10. 12. 14. 16. 18. 20. 22. 24. 26. 28. 30. 32. 34.
  36. 38.]]
t (5, 3, 3)
[[[ 0.  1.  2.]
  [ 3.  4.  5.]
  [ 1.  1.  1.]]

 [[ 6.  7.  8.]
  [ 9. 10. 11.]
  [ 1.  1.  1.]]

 [[12. 13. 14.]
  [15. 16. 17.]
  [ 1.  1.  1.]]

 [[18. 19. 20.]
  [21. 22. 23.]
  [ 1.  1.  1.]]

 [[24. 25. 26.]
  [27. 28. 29.]
  [ 1.  1.  1.]]]