Implicit Relationships
The ImplicitOperation
class provides users with a way to solve for
variables in implicit relationships.
It is possible to compute outputs implicitly by defining a residual
variable in terms of the output and inputs.
In the first example, we solve a quadratic equation.
This quadratic has two solutions: 1
and 3
.
Depending on the starting value of the output variable, CSDL will
find one root or the other.
The second and third examples show how to perform a bracketed search.
from csdl_om import Simulatorfrom csdl import Model, ScipyKrylov, NewtonSolver, NonlinearBlockGSimport numpy as np
class ExampleApplyNonlinear(Model):
def define(self): # define internal model that defines a residual model = Model() a = model.declare_variable('a', val=1) b = model.declare_variable('b', val=-4) c = model.declare_variable('c', val=3) x = model.declare_variable('x') y = a * x**2 + b * x + c model.register_output('y', y)
solve_quadratic = self.create_implicit_operation(model) solve_quadratic.declare_state('x', residual='y') solve_quadratic.nonlinear_solver = NewtonSolver( solve_subsystems=False, maxiter=100, iprint=False, ) solve_quadratic.linear_solver = ScipyKrylov()
a = self.declare_variable('a', val=1) b = self.declare_variable('b', val=-4) c = self.declare_variable('c', val=3) x = solve_quadratic(a, b, c)
sim = Simulator(ExampleApplyNonlinear())sim.run()
from csdl.examples.valid.ex_implicit_apply_nonlinear import examplefrom csdl_om import Simulatorsim = example(Simulator)
print('using default x=1')sim.run()print('x', sim['x'].shape)print(sim['x'])
print('')print('')print('setting x=1.9')sim['x'] = 1.9sim.run()print('x', sim['x'].shape)print(sim['x'])
print('')print('')print('setting x=2.1')sim['x'] = 2.1sim.run()print('x', sim['x'].shape)print(sim['x'])
The expressions for the residuals will will be part of a Model
within the generated ImplicitOperation
object.
For problems where the residual may converge for multiple solutions, or
where the residual is difficult to converge over some interval, csdl
provides an API for bracketing solutions.
from csdl_om import Simulatorfrom csdl import Model, ScipyKrylov, NewtonSolver, NonlinearBlockGSimport numpy as np
class ExampleBracketedScalar(Model):
def define(self): model = Model() a = model.declare_variable('a') b = model.declare_variable('b') c = model.declare_variable('c') x = model.declare_variable('x') y = a * x**2 + b * x + c model.register_output('y', y)
solve_quadratic = self.create_implicit_operation(model) solve_quadratic.declare_state('x', residual='y', bracket=(0, 2))
a = self.declare_variable('a', val=1) b = self.declare_variable('b', val=-4) c = self.declare_variable('c', val=3) x = solve_quadratic(a, b, c)
sim = Simulator(ExampleBracketedScalar())sim.run()
print('x', sim['x'].shape)print(sim['x'])
[1.]
Brackets may also be specified for multidimensional array values.
from csdl_om import Simulatorfrom csdl import Model, ScipyKrylov, NewtonSolver, NonlinearBlockGSimport numpy as np
class ExampleBracketedArray(Model):
def define(self): model = Model() a = model.declare_variable('a', shape=(2, )) b = model.declare_variable('b', shape=(2, )) c = model.declare_variable('c', shape=(2, )) x = model.declare_variable('x', shape=(2, )) y = a * x**2 + b * x + c model.register_output('y', y)
solve_quadratic = self.create_implicit_operation(model) solve_quadratic.declare_state('x', residual='y', bracket=( np.array([0, 2.]), np.array([2, np.pi], ), ))
a = model.declare_variable('a', val=[1, -1]) b = model.declare_variable('b', val=[-4, 4]) c = model.declare_variable('c', val=[3, -3]) x = solve_quadratic(a, b, c)
sim = Simulator(ExampleBracketedArray())sim.run()
print('x', sim['x'].shape)print(sim['x'])
[1. 3.]