Simple Explicit Expressions with Subsystems¶
In addition to generating Component
objects from expressions,
omtools
supports adding subsystems to a Group
class as in
OpenMDAO.
This means that OpenMDAO System
objects, including omtools.Group
objects, can be added to a model built with omtools
.
Note that, while omtools
determines execution order of Component
objects, the user is responsible for calling Group.add_subsystem
after the parent Group
’s outputs are registered, and before the
parent Group
’s inputs are declared.
In this example, 'subsystem'
declares 'x1'
as an input, so the user
must create an 'x1'
output in the parent Group
prior to the call
to Group.add_subsystem
in order to update the input values in
'sys'
.
Likewise, if the parent Group
is to use an output registered in
'subsystem'
, such as 'x2'
, then the user must call
Group.declare_input
after Group.add_subsystem
for that variable.
from openmdao.api import Problem
from openmdao.api import ScipyKrylov, NewtonSolver, NonlinearBlockGS
from omtools.api import Group, ImplicitComponent
import omtools.api as ot
import numpy as np
class ExampleWithSubsystems(Group):
def setup(self):
# Create independent variable
x1 = self.create_indep_var('x1', val=40)
# Powers
y4 = x1**2
# Create subsystem that depends on previously created
# independent variable
subgroup = Group()
# This value is overwritten by connection from the main group
a = subgroup.declare_input('x1', val=2)
b = subgroup.create_indep_var('x2', val=12)
subgroup.register_output('prod', a * b)
self.add_subsystem('subsystem', subgroup, promotes=['*'])
# declare inputs with default values
# This value is overwritten by connection
# from the subgroup
x2 = self.declare_input('x2', val=3)
# Simple addition
y1 = x2 + x1
self.register_output('y1', y1)
# Simple subtraction
self.register_output('y2', x2 - x1)
# Simple multitplication
self.register_output('y3', x1 * x2)
# Powers
y5 = x2**2
# register outputs in reverse order to how they are defined
self.register_output('y5', y5)
self.register_output('y6', y1 + y5)
self.register_output('y4', y4)
prob = Problem()
prob.model = ExampleWithSubsystems()
prob.setup(force_alloc_complex=True)
prob.run_model()
print('prod', prob['prod'].shape)
print(prob['prod'])
print('y1', prob['y1'].shape)
print(prob['y1'])
print('y2', prob['y2'].shape)
print(prob['y2'])
print('y3', prob['y3'].shape)
print(prob['y3'])
print('y4', prob['y4'].shape)
print(prob['y4'])
print('y5', prob['y5'].shape)
print(prob['y5'])
print('y6', prob['y6'].shape)
print(prob['y6'])
prod (1,)
[480.]
y1 (1,)
[52.]
y2 (1,)
[-28.]
y3 (1,)
[480.]
y4 (1,)
[1600.]
y5 (1,)
[144.]
y6 (1,)
[196.]
Below is an n2 diagram for a Group
with simple binary expressions
and a subsystem.
The Component
objects added to the model are guaranteed to be
connected such that there are no unnecessary feedbacks, regardless of
the order in which each output is defined or registered.