Upgrading from OpenMDAO 1¶
This guide takes how you did things in OpenMDAO 1 and shows how to do them OpenMDAO 2. It is not a comprehensive guide to using OpenMDAO 2, but focuses only on the things that have changed in the API.
Build a Model¶
Define an Explcit Component¶
class Paraboloid(Component):
"""
Evaluates the equation f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3.
"""
def __init__(self):
super(Paraboloid, self).__init__()
self.add_param('x', val=0.0)
self.add_param('y', val=0.0)
self.add_output('f_xy', val=0.0)
def solve_nonlinear(self, params, unknowns, resids):
"""
f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3
Optimal solution (minimum): x = 6.6667; y = -7.3333
"""
x = params['x']
y = params['y']
unknowns['f_xy'] = (x-3.0)**2 + x*y + (y+4.0)**2 - 3.0
def linearize(self, params, unknowns, resids):
"""
Jacobian for our paraboloid.
"""
x = params['x']
y = params['y']
J = {}
J['f_xy', 'x'] = 2.0*x - 6.0 + y
J['f_xy', 'y'] = 2.0*y + 8.0 + x
return J
class Paraboloid(ExplicitComponent):
"""
Evaluates the equation f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3.
"""
def setup(self):
self.add_input('x', val=0.0)
self.add_input('y', val=0.0)
self.add_output('f_xy', val=0.0)
self.declare_partials('*', '*')
def compute(self, inputs, outputs):
"""
f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3
Optimal solution (minimum): x = 6.6667; y = -7.3333
"""
x = inputs['x']
y = inputs['y']
outputs['f_xy'] = (x-3.0)**2 + x*y + (y+4.0)**2 - 3.0
def compute_partials(self, inputs, partials):
"""
Jacobian for our paraboloid.
"""
x = inputs['x']
y = inputs['y']
partials['f_xy', 'x'] = 2.0*x - 6.0 + y
partials['f_xy', 'y'] = 2.0*y + 8.0 + x
Define an Implicit Component¶
class ImplCompOneState(Component):
"""
A Simple Implicit Component
R(x,y) = 0.5y^2 + 2y + exp(-16y^2) + 2exp(-5y) - x
Solution:
x = 1.2278849186466743
y = 0.3968459
"""
def setup(self):
self.add_param('x', 1.2278849186466743)
self.add_state('y', val=1.0)
def apply_nonlinear(self, params, unknowns, resids):
"""
Don't solve; just calculate the residual.
"""
x = params['x']
y = unknowns['y']
resids['y'] = 0.5*y*y + 2.0*y + exp(-16.0*y*y) + 2.0*exp(-5.0*y) - x
def linearize(self, params, unknowns, resids):
"""
Analytical derivatives.
"""
y = unknowns['y']
J = {}
# State equation
J[('y', 'x')] = -1.0
J[('y', 'y')] = y + 2.0 - 32.0*y*exp(-16.0*y*y) - 10.0*exp(-5.0*y)
return J
class ImplCompOneState(ImplicitComponent):
"""
A Simple Implicit Component
R(x,y) = 0.5y^2 + 2y + exp(-16y^2) + 2exp(-5y) - x
Solution:
x = 1.2278849186466743
y = 0.3968459
"""
def setup(self):
self.add_input('x', 1.2278849186466743)
self.add_output('y', val=1.0)
self.declare_partials(of='*', wrt='*')
def apply_nonlinear(self, inputs, outputs, resids):
"""
Don't solve; just calculate the residual.
"""
x = inputs['x']
y = outputs['y']
resids['y'] = 0.5*y*y + 2.0*y + exp(-16.0*y*y) + 2.0*exp(-5.0*y) - x
def linearize(self, inputs, outputs, J):
"""
Analytical derivatives.
"""
y = outputs['y']
# State equation
J[('y', 'x')] = -1.0
J[('y', 'y')] = y + 2.0 - 32.0*y*exp(-16.0*y*y) - 10.0*exp(-5.0*y)
Input-Input connections¶
See more details in the doc for add_subsystem().
prob = Problem()
root = prob.root = Group()
root.add('p1', IndepVarComp('x', 3.0))
root.add('comp1', Paraboloid())
root.add('comp2', Paraboloid())
#input-input connection
root.connect('comp1.x', 'comp2.x')
#then connect the indep var to just one of the inputs
root.connect('p1.x', 'comp1.x')
prob.setup()
prob.run()
from openmdao.api import Problem, Group, IndepVarComp
from openmdao.test_suite.components.paraboloid import Paraboloid
prob = Problem()
model = prob.model = Group()
model.add_subsystem('p1', IndepVarComp('x', 3.0))
#promote the two inputs to the same name
model.add_subsystem('comp1', Paraboloid(), promotes_inputs=['x'])
model.add_subsystem('comp2', Paraboloid(), promotes_inputs=['x'])
#connect the source to the common name
model.connect('p1.x', 'x')
prob.setup()
prob.run_model()
Run a Model¶
Assemble and Run a Simple Model¶
prob = Problem()
root = prob.root = Group()
root.add('p1', IndepVarComp('x', 3.0))
root.add('p2', IndepVarComp('y', -4.0))
root.add('comp', Paraboloid())
root.connect('p1.x', 'comp.x')
root.connect('p2.y', 'comp.y')
prob.setup()
prob.run()
from openmdao.api import Problem, Group, IndepVarComp
from openmdao.test_suite.components.paraboloid import Paraboloid
prob = Problem()
model = prob.model = Group()
model.add_subsystem('p1', IndepVarComp('x', 3.0))
model.add_subsystem('p2', IndepVarComp('y', -4.0))
model.add_subsystem('comp', Paraboloid())
model.connect('p1.x', 'comp.x')
model.connect('p2.y', 'comp.y')
prob.setup()
prob.run_model()
Run a Driver¶
prob.run()
prob.run_driver()
Run a Model without Running the Driver¶
prob.run_once()
prob.run_model()
Print All Solver Messages¶
top.print_all_convergence(level=2)
top.set_solver_print(level=2)
Check a Model¶
Specify Finite Difference for all Component Derivatives¶
def __init__(self):
super(SellarDis1, self).__init__()
# Global Design Variable
self.add_param('z', val=np.zeros(2))
# Local Design Variable
self.add_param('x', val=0.)
# Coupling parameter
self.add_param('y2', val=1.0)
# Coupling output
self.add_output('y1', val=1.0)
# Finite difference all partials.
self.deriv_options['type'] = 'fd'
def setup(self):
# Global Design Variable
self.add_input('z', val=np.zeros(2))
# Local Design Variable
self.add_input('x', val=0.)
# Coupling parameter
self.add_input('y2', val=1.0)
# Coupling output
self.add_output('y1', val=1.0)
# Finite difference all partials.
self.declare_partials('*', '*', method='fd')
Specify FD Form and Stepsize on Specific Derivatives¶
def __init__(self):
super(PartialComp, self).__init__()
self.add_param('x', shape=(4,), step_size=1e-4, form='backward')
self.add_param('y', shape=(2,), step_size=1e-6, form='central')
self.add_param('y2', shape=(2,), step_size=1e-6, form='central')
self.add_output('f', shape=(2,))
def setup(self):
self.add_input('x', shape=(4,))
self.add_input('y', shape=(2,))
self.add_input('y2', shape=(2,))
self.add_output('f', shape=(2,))
self.declare_partials('f', 'y*', method='fd', form='backward', step=1e-6)
self.declare_partials('f', 'x', method='fd', form='central', step=1e-4)
Check Partial Derivatives on All Components¶
data = prob.check_partials()
data = prob.check_partials()
Suppress Output While Checking Partial Derivatives¶
data = prob.check_partials(out_stream=None)
data = prob.check_partials(suppress_output=True)
Check Partial Derivatives with Complex Step¶
prob.root.deriv_options['check_type'] = 'cs'
prob.setup()
prob.run()
prob.check_partials()
def test_set_method_global(self):
from openmdao.api import Problem, Group, IndepVarComp
from openmdao.core.tests.test_check_derivs import ParaboloidTricky
from openmdao.test_suite.components.paraboloid_mat_vec import ParaboloidMatVec
prob = Problem()
prob.model = Group()
prob.model.add_subsystem('p1', IndepVarComp('x', 3.0))
prob.model.add_subsystem('p2', IndepVarComp('y', 5.0))
comp = prob.model.add_subsystem('comp', ParaboloidTricky())
prob.model.add_subsystem('comp2', ParaboloidMatVec())
prob.model.connect('p1.x', 'comp.x')
prob.model.connect('p2.y', 'comp.y')
prob.model.connect('comp.f_xy', 'comp2.x')
prob.set_solver_print(level=0)
prob.setup(force_alloc_complex=True)
prob.run_model()
prob.check_partials(method='cs')
Change Group Level Derivative Behavior¶
Force Group or Model to use Finite Difference¶
model.deriv_options['type'] = 'fd'
model.approx_totals()
Force Group or Model to use Finite Difference with Specific Options¶
model.deriv_options['type'] = 'fd'
model.deriv_options['step_size'] = '1e-7'
model.deriv_options['form'] = 'central'
model.deriv_options['step_calc'] = 'relative'
model.approx_totals(method='fd', step=1e-7, form='central', step_calc='rel')
Add Design Variables¶
Add a Design Variable to a Model¶
prob = Problem()
prob.root = SellarDerivatives()
prob.add_desvar('z')
prob = Problem()
prob.model = model = SellarDerivatives()
model.add_design_var('z')
Add a Design Variable with Scale and Offset that Maps [3, 5] to [0, 1]¶
prob = Problem()
prob.root = SellarDerivatives()
prob.add_desvar('z', scaler=0.5, adder=-3.0)
prob = Problem()
prob.model = model = SellarDerivatives()
model.add_design_var('z', ref=5.0, ref0=3.0)
Set Solvers¶
Setup a Problem Using the PETScVector¶
prob.setup(impl=PetscImpl)
prob.setup(vector_class=PETScVector)
Specify Newton as a Nonlinear Solver in a Group¶
model.nl_solver = Newton()
model.nonlinear_solver = NewtonSolver()
Specify Block Gauss-Seidel as a Nonlinear Solver in a Group¶
model.nl_solver = NLGaussSeidel()
model.nonlinear_solver = NonlinearBlockGS()
Specify Scipy GMRES as a Linear Solver in a Group¶
model.ln_solver = ScipyGMRES()
model.linear_solver = ScipyKrylov()
Specify Linear Block Gauss-Seidel as a Linear Solver in a Group¶
model.ln_solver = LinearGaussSeidel()
model.linear_solver = LinearBlockGS()
Total Derivatives¶
Computing Total Derivatives¶
prob.calc_gradient(indep_list=['p1.x', 'p2.y'], unknown_list=['comp.f_xy'])
totals = prob.compute_totals(of=['comp.f_xy'], wrt=['p1.x', 'p2.y'])
Setting Derivative Computation Mode¶
root.ln_solver.options['mode'] = 'rev'
# root.ln_solver.options['mode'] = 'fwd'
# root.ln_solver.options['mode'] = 'auto'
prob.setup()
prob.run()
prob.calc_gradient(indep_list=['p1.x', 'p2.y'], unknown_list=['comp.f_xy'])
prob.setup(mode='rev')
#prob.setup(mode='fwd')
prob.run_model()
assert_rel_error(self, prob['comp.f_xy'], -15.0)
prob.compute_totals(of=['comp.f_xy'], wrt=['p1.x', 'p2.y'])