Getting Data from a Case#
The Case
object contains all the information about a specific recorded case whether it was recorded by
a problem, driver, system, or solver. Case
objects have a number methods for accessing variables and their values.
Example of Getting Variable Data from Case Recording of a Driver#
Here are the methods typically used when retrieving data from the recording of a :code:Driver
.
- Case.get_objectives(scaled=True, use_indices=True)[source]
Get the values of the objectives, as seen by the driver, for this case.
- Parameters:
- scaledbool
If True, then return scaled values.
- use_indicesbool
If True, apply indices.
- Returns:
- PromAbsDict
Map of variables to their values.
- Case.get_constraints(scaled=True, use_indices=True)[source]
Get the values of the constraints, as seen by the driver, for this case.
- Parameters:
- scaledbool
If True, then return scaled values.
- use_indicesbool
If True, apply indices.
- Returns:
- PromAbsDict
Map of variables to their values.
- Case.get_design_vars(scaled=True, use_indices=True)[source]
Get the values of the design variables, as seen by the driver, for this case.
- Parameters:
- scaledbool
If True, then return scaled values.
- use_indicesbool
If True, apply indices.
- Returns:
- PromAbsDict
Map of variables to their values.
- Case.get_responses(scaled=True, use_indices=True)[source]
Get the values of the responses, as seen by the driver, for this case.
- Parameters:
- scaledbool
If True, then return scaled values.
- use_indicesbool
If True, apply indices.
- Returns:
- PromAbsDict
Map of variables to their values.
The following example shows how to use these methods to easily check the variables of interest from the first driver iteration.
SellarDerivatives
class definition
class SellarDerivatives(om.Group):
"""
Group containing the Sellar MDA. This version uses the disciplines with derivatives.
"""
def setup(self):
self.add_subsystem('d1', SellarDis1withDerivatives(), promotes=['x', 'z', 'y1', 'y2'])
self.add_subsystem('d2', SellarDis2withDerivatives(), promotes=['z', 'y1', 'y2'])
self.add_subsystem('obj_cmp', om.ExecComp('obj = x**2 + z[1] + y1 + exp(-y2)', obj=0.0,
x=0.0, z=np.array([0.0, 0.0]), y1=0.0, y2=0.0),
promotes=['obj', 'x', 'z', 'y1', 'y2'])
self.add_subsystem('con_cmp1', om.ExecComp('con1 = 3.16 - y1', con1=0.0, y1=0.0),
promotes=['con1', 'y1'])
self.add_subsystem('con_cmp2', om.ExecComp('con2 = y2 - 24.0', con2=0.0, y2=0.0),
promotes=['con2', 'y2'])
self.set_input_defaults('x', 1.0)
self.set_input_defaults('z', np.array([5.0, 2.0]))
import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarDerivatives
import numpy as np
prob = om.Problem(model=SellarDerivatives())
model = prob.model
model.add_design_var('z', lower=np.array([-10.0, 0.0]),
upper=np.array([10.0, 10.0]))
model.add_design_var('x', lower=0.0, upper=10.0)
model.add_objective('obj')
model.add_constraint('con1', upper=0.0)
model.add_constraint('con2', upper=0.0)
model.nonlinear_solver = om.NonlinearBlockGS()
model.linear_solver = om.ScipyKrylov()
driver = prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9, disp=False)
driver.add_recorder(om.SqliteRecorder("cases.sql"))
driver.recording_options['includes'] = []
driver.recording_options['record_objectives'] = True
driver.recording_options['record_constraints'] = True
driver.recording_options['record_desvars'] = True
prob.setup()
prob.set_solver_print(0)
prob.run_driver()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / "cases.sql")
driver_cases = cr.list_cases('driver')
driver |
---|
rank0:ScipyOptimize_SLSQP|0 |
rank0:ScipyOptimize_SLSQP|1 |
rank0:ScipyOptimize_SLSQP|2 |
rank0:ScipyOptimize_SLSQP|3 |
rank0:ScipyOptimize_SLSQP|4 |
rank0:ScipyOptimize_SLSQP|5 |
rank0:ScipyOptimize_SLSQP|6 |
case = cr.get_case(driver_cases[0])
case.outputs.keys()
dict_keys(['z', 'x', 'con1', 'con2', 'obj'])
objs = case.get_objectives()
cons = case.get_constraints()
dvs = case.get_design_vars()
rsps = case.get_responses()
Using keys()
will give you the promoted variable names:
print((sorted(objs.keys()), sorted(cons.keys()), sorted(dvs.keys())))
(['obj'], ['con1', 'con2'], ['x', 'z'])
Alternatively, you can get the absolute names:
print((sorted(objs.absolute_names()), sorted(cons.absolute_names()), sorted(dvs.absolute_names())))
(['obj_cmp.obj'], ['con_cmp1.con1', 'con_cmp2.con2'], ['x', 'z'])
You can access variable values using either the promoted or the absolute name:
print('objective (obj):\t', objs['obj'], objs['obj_cmp.obj'])
print('constraint (con1):\t', cons['con1'], cons['con_cmp1.con1'])
# Note that x is supplied by an automatically generated IndepVarComp
print('design vars (x):\t', dvs['x'], dvs['_auto_ivc.v1'])
print('response vars (con2):\t', rsps['con2'], rsps['con_cmp2.con2'])
objective (obj): [28.58830817] [28.58830817]
constraint (con1): [-22.42830237] [-22.42830237]
design vars (x): [1.] [1.]
response vars (con2): [-11.94151185] [-11.94151185]
You can also access the variables directly from the case object:
print((case['obj'], case['obj_cmp.obj']))
print((case['x'], case['_auto_ivc.v1']))
(array([28.58830817]), array([28.58830817]))
(array([1.]), array([1.]))
Getting Variable Data from Case Recording of a Problem#
Here are the methods typically used when retrieving data from the recording of a Problem
.
- Case.list_inputs(val=True, prom_name=True, units=False, shape=False, global_shape=False, desc=False, hierarchical=True, print_arrays=False, tags=None, print_tags=False, includes=None, excludes=None, is_indep_var=None, is_design_var=None, out_stream=DEFAULT_OUT_STREAM, print_min=False, print_max=False, return_format='list')[source]
Return and optionally log a list of input names and other optional information.
- Parameters:
- valbool, optional
When True, display/return input values. Default is True.
- prom_namebool, optional
When True, display/return the promoted name of the variable. Default is True.
- unitsbool, optional
When True, display/return units. Default is False.
- shapebool, optional
When True, display/return the shape of the value. Default is False.
- global_shapebool, optional
When True, display/return the global shape of the value. Default is False.
- descbool, optional
When True, display/return description. Default is False.
- hierarchicalbool, optional
When True, human readable output shows variables in hierarchical format.
- print_arraysbool, optional
When False, in the columnar display, just display norm of any ndarrays with size > 1. The norm is surrounded by vertical bars to indicate that it is a norm. When True, also display full values of the ndarray below the row. Format is affected by the values set with numpy.set_printoptions Default is False.
- tagsstr or list of strs
User defined tags that can be used to filter what gets listed. Only inputs with the given tags will be listed. Default is None, which means there will be no filtering based on tags.
- print_tagsbool
When true, display tags in the columnar display.
- includesstr, iter of str, or None
Glob patterns for pathnames to include in the check. Default is None, which includes all.
- excludesstr, iter of str, or None
Glob patterns for pathnames to exclude from the check. Default is None, which excludes nothing.
- is_indep_varbool or None
If None (the default), do no additional filtering of the inputs. If True, list only inputs connected to an output tagged openmdao:indep_var. If False, list only inputs _not_ connected to outputs tagged openmdao:indep_var.
- is_design_varbool or None
If None (the default), do no additional filtering of the inputs. If True, list only inputs connected to outputs that are driver design variables. If False, list only inputs _not_ connected to outputs that are driver design variables.
- out_streamfile-like object
Where to send human readable output. Default is sys.stdout. Set to None to suppress.
- print_minbool, optional
When true, if the input value is an array, print its smallest value.
- print_maxbool, optional
When true, if the input value is an array, print its largest value.
- return_formatstr
Indicates the desired format of the return value. Can have value of ‘list’ or ‘dict’. If ‘list’, the return value is a list of (name, metadata) tuples. if ‘dict’, the return value is a dictionary mapping {name: metadata}.
- Returns:
- list of (name, metadata) or dict of {name: metadata}
List or dict of input names and other optional information about those inputs.
- Case.list_outputs(explicit=True, implicit=True, val=True, prom_name=True, residuals=False, residuals_tol=None, units=False, shape=False, global_shape=False, bounds=False, scaling=False, desc=False, hierarchical=True, print_arrays=False, tags=None, print_tags=False, includes=None, excludes=None, is_indep_var=None, is_design_var=None, list_autoivcs=False, out_stream=DEFAULT_OUT_STREAM, print_min=False, print_max=False, return_format='list')[source]
Return and optionally log a list of output names and other optional information.
- Parameters:
- explicitbool, optional
Include outputs from explicit components. Default is True.
- implicitbool, optional
Include outputs from implicit components. Default is True.
- valbool, optional
When True, display/return output values. Default is True.
- prom_namebool, optional
When True, display/return the promoted name of the variable. Default is False.
- residualsbool, optional
When True, display/return residual values. Default is False.
- residuals_tolfloat, optional
If set, limits the output of list_outputs to only variables where the norm of the resids array is greater than the given ‘residuals_tol’. Default is None.
- unitsbool, optional
When True, display/return units. Default is False.
- shapebool, optional
When True, display/return the shape of the value. Default is False.
- global_shapebool, optional
When True, display/return the global shape of the value. Default is False.
- boundsbool, optional
When True, display/return bounds (lower and upper). Default is False.
- scalingbool, optional
When True, display/return scaling (ref, ref0, and res_ref). Default is False.
- descbool, optional
When True, display/return description. Default is False.
- hierarchicalbool, optional
When True, human readable output shows variables in hierarchical format.
- print_arraysbool, optional
When False, in the columnar display, just display norm of any ndarrays with size > 1. The norm is surrounded by vertical bars to indicate that it is a norm. When True, also display full values of the ndarray below the row. Format is affected by the values set with numpy.set_printoptions Default is False.
- tagsstr or list of strs
User defined tags that can be used to filter what gets listed. Only outputs with the given tags will be listed. Default is None, which means there will be no filtering based on tags.
- print_tagsbool
When true, display tags in the columnar display.
- includesstr, iter of str, or None
Glob patterns for pathnames to include in the check. Default is None, which includes all.
- excludesstr, iter of str, or None
Glob patterns for pathnames to exclude from the check. Default is None, which excludes nothing.
- is_indep_varbool or None
If None (the default), do no additional filtering of the inputs. If True, list only inputs connected to an output tagged openmdao:indep_var. If False, list only inputs _not_ connected to outputs tagged openmdao:indep_var.
- is_design_varbool or None
If None (the default), do no additional filtering of the inputs. If True, list only inputs connected to outputs that are driver design variables. If False, list only inputs _not_ connected to outputs that are driver design variables.
- list_autoivcsbool
If True, include auto_ivc outputs in the listing. Defaults to False.
- out_streamfile-like
Where to send human readable output. Default is sys.stdout. Set to None to suppress.
- print_minbool, optional
When true, if the output value is an array, print its smallest value.
- print_maxbool, optional
When true, if the output value is an array, print its largest value.
- return_formatstr
Indicates the desired format of the return value. Can have value of ‘list’ or ‘dict’. If ‘list’, the return value is a list of (name, metadata) tuples. if ‘dict’, the return value is a dictionary mapping {name: metadata}.
- Returns:
- list of (name, metadata) or dict of {name: metadata}
List or dict of output names and other optional information about those outputs.
The following example shows how to use these methods.
SellarProblem
class definition
class SellarProblem(om.Problem):
"""
The Sellar problem with configurable model class.
"""
def __init__(self, model_class=SellarDerivatives, **kwargs):
super().__init__(model_class(**kwargs))
model = self.model
model.add_design_var('z', lower=np.array([-10.0, 0.0]), upper=np.array([10.0, 10.0]))
model.add_design_var('x', lower=0.0, upper=10.0)
model.add_objective('obj')
model.add_constraint('con1', upper=0.0)
model.add_constraint('con2', upper=0.0)
# default to non-verbose
self.set_solver_print(0)
import openmdao.api as om
from openmdao.test_suite.components.sellar import SellarProblem
from openmdao.test_suite.components.sellar_feature import SellarDerivatives
prob = SellarProblem(model_class=SellarDerivatives)
recorder = om.SqliteRecorder("cases.sql")
prob.model.nonlinear_solver = om.NonlinearBlockGS()
prob.model.linear_solver = om.ScipyKrylov()
prob.model.add_recorder(recorder)
prob.model.recording_options['record_residuals'] = True
prob.setup()
d1 = prob.model.d1
d1.add_recorder(recorder)
prob.run_driver()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / "cases.sql")
system_cases = cr.list_cases('root.d1')
root.d1 |
---|
rank0:Driver|0|root._solve_nonlinear|0|d1._solve_nonlinear|0 |
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|1|d1._solve_nonlinear|1 |
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|2|d1._solve_nonlinear|2 |
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|3|d1._solve_nonlinear|3 |
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|4|d1._solve_nonlinear|4 |
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|5|d1._solve_nonlinear|5 |
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|6|d1._solve_nonlinear|6 |
rank0:Driver|0|root._solve_nonlinear|0|NonlinearBlockGS|7|d1._solve_nonlinear|7 |
list_inputs()
and list_outputs()
will print a report to the screen as well as returning a list of the variables and their values.
case = cr.get_case(system_cases[1])
case_inputs = case.list_inputs()
case_outputs = case.list_outputs()
3 Input(s) in 'd1'
varname val prom_name
------- ------------------- ---------
z |5.38516481| z
x [1.] x
y2 [12.27257053] y2
1 Explicit Output(s) in 'd1'
varname val prom_name
------- ------------- ---------
y1 [25.54548589] y1
0 Implicit Output(s) in 'd1'
from pprint import pprint
pprint(case_inputs)
pprint(case_outputs)
[('d1.x', {'io': 'input', 'prom_name': 'x', 'val': array([1.])}),
('d1.y2', {'io': 'input', 'prom_name': 'y2', 'val': array([12.27257053])}),
('d1.z', {'io': 'input', 'prom_name': 'z', 'val': array([5., 2.])})]
[('d1.y1', {'io': 'output', 'prom_name': 'y1', 'val': array([25.54548589])})]
The list_inputs()
and list_outputs()
methods have optional arguments that let you filter based on variable names what gets listed. This is shown in the following examples.
RectangleComp
class definition
class RectangleComp(om.ExplicitComponent):
"""
A simple Explicit Component that computes the area of a rectangle.
"""
def setup(self):
self.add_input('length', val=1.)
self.add_input('width', val=1.)
self.add_output('area', val=1.)
def setup_partials(self):
self.declare_partials('*', '*')
def compute(self, inputs, outputs):
outputs['area'] = inputs['length'] * inputs['width']
import openmdao.api as om
from openmdao.core.tests.test_expl_comp import RectangleComp
model = om.Group()
prob = om.Problem(model)
model.add_recorder(om.SqliteRecorder('cases.sql'))
model.add_subsystem('rect', RectangleComp(), promotes=['length', 'width', 'area'])
prob.setup(check=False)
prob.set_val('length', 100.)
prob.set_val('width', 60.0)
prob.run_model()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / "cases.sql")
case = cr.get_case(0)
# Inputs with includes
inputs = case.list_inputs(includes=['*length'])
print(inputs)
1 Input(s) in 'model'
varname val prom_name
-------- ------ ---------
rect
length [100.] length
[('rect.length', {'val': array([100.]), 'io': 'input', 'prom_name': 'length'})]
# Inputs with excludes
inputs = case.list_inputs(excludes=['*length'])
print(inputs)
1 Input(s) in 'model'
varname val prom_name
------- ----- ---------
rect
width [60.] width
[('rect.width', {'val': array([60.]), 'io': 'input', 'prom_name': 'width'})]
# Outputs with includes
outputs = case.list_outputs(includes=['*area'])
print(outputs)
1 Explicit Output(s) in 'model'
varname val prom_name
------- ------- ---------
rect
area [6000.] area
0 Implicit Output(s) in 'model'
[('rect.area', {'val': array([6000.]), 'io': 'output', 'prom_name': 'area'})]
Finally, you can also make use of the variable tagging feature when getting values from cases. This example shows how to do that.
import openmdao.api as om
class RectangleCompWithTags(om.ExplicitComponent):
"""
A simple Explicit Component that also has input and output with tags.
"""
def setup(self):
self.add_input('length', val=1., tags=["tag1", "tag2"])
self.add_input('width', val=1., tags=["tag2"])
self.add_output('area', val=1., tags="tag1")
def setup_partials(self):
self.declare_partials('*', '*')
def compute(self, inputs, outputs):
outputs['area'] = inputs['length'] * inputs['width']
model = om.Group()
prob = om.Problem(model)
model.add_recorder(om.SqliteRecorder('cases.sql'))
model.add_subsystem('rect', RectangleCompWithTags(), promotes=['length', 'width', 'area'])
prob.setup(check=False)
prob.set_val('length', 100.0)
prob.set_val('width', 60.0)
prob.run_model()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / "cases.sql")
case = cr.get_case(0)
# Inputs with tag that matches
inputs = case.list_inputs(tags="tag1")
print(inputs)
1 Input(s) in 'model'
varname val prom_name tags
-------- ------ --------- ----------------
rect
length [100.] length ['tag1', 'tag2']
[('rect.length', {'val': array([100.]), 'io': 'input', 'prom_name': 'length'})]
# Inputs with multiple tags
inputs = case.list_inputs(tags=["tag1", "tag2"])
print(inputs)
2 Input(s) in 'model'
varname val prom_name tags
-------- ------ --------- ----------------
rect
length [100.] length ['tag1', 'tag2']
width [60.] width ['tag2']
[('rect.length', {'val': array([100.]), 'io': 'input', 'prom_name': 'length'}), ('rect.width', {'val': array([60.]), 'io': 'input', 'prom_name': 'width'})]
# Outputs with tag that does match
outputs = case.list_outputs(tags="tag1")
print(outputs)
1 Explicit Output(s) in 'model'
varname val prom_name
------- ------- ---------
rect
area [6000.] area
0 Implicit Output(s) in 'model'
[('rect.area', {'val': array([6000.]), 'io': 'output', 'prom_name': 'area'})]
Getting Variable Data from Case By Specifying Variable Name and Units Desired#
You can also get variable values from a Case
like you would from a Problem
using dictionary-like access
or, if you want the value in different units, using the get_val
method.
- Case.get_val(name, units=None, indices=None)[source]
Get an output/input variable.
Function is used if you want to specify display units.
- Parameters:
- namestr
Promoted or relative variable name in the root system’s namespace.
- unitsstr, optional
Units to convert to before upon return.
- indicesint or list of ints or tuple of ints or int ndarray or Iterable or None, optional
Indices or slice to return.
- Returns:
- float or ndarray
The requested output/input variable.
This example shows both methods of getting variable data by name.
import openmdao.api as om
model = om.Group()
model.add_recorder(om.SqliteRecorder('cases.sql'))
speed = om.ExecComp('v=x/t', x={'units': 'm'}, t={'units': 's'}, v={'units': 'm/s'})
model.add_subsystem('speed', speed, promotes=['x', 't', 'v'])
prob = om.Problem(model)
prob.setup()
prob.set_val('x', 100., units='m')
prob.set_val('t', 60., units='s')
prob.run_model()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / 'cases.sql')
case = cr.get_case(0)
print(case['x'])
[100.]
print(case.get_val('x', units='ft'))
[328.0839895]
print(case['v'])
[1.66666667]
print(case.get_val('v', units='ft/s'))
[5.46806649]
Getting Derivative Data from a Case#
A driver has the ability to record derivatives but it is not enabled by default. If you do enable
this option, the recorded cases will have a value for the jacobian
.
SellarMDA
class definition
class SellarMDA(om.Group):
"""
Group containing the Sellar MDA.
"""
def setup(self):
cycle = self.add_subsystem('cycle', om.Group(), promotes=['*'])
cycle.add_subsystem('d1', SellarDis1(), promotes_inputs=['x', 'z', 'y2'],
promotes_outputs=['y1'])
cycle.add_subsystem('d2', SellarDis2(), promotes_inputs=['z', 'y1'],
promotes_outputs=['y2'])
cycle.set_input_defaults('x', 1.0)
cycle.set_input_defaults('z', np.array([5.0, 2.0]))
# Nonlinear Block Gauss Seidel is a gradient free solver
cycle.nonlinear_solver = om.NonlinearBlockGS()
self.add_subsystem('obj_cmp', om.ExecComp('obj = x**2 + z[1] + y1 + exp(-y2)',
z=np.array([0.0, 0.0]), x=0.0),
promotes=['x', 'z', 'y1', 'y2', 'obj'])
self.add_subsystem('con_cmp1', om.ExecComp('con1 = 3.16 - y1'), promotes=['con1', 'y1'])
self.add_subsystem('con_cmp2', om.ExecComp('con2 = y2 - 24.0'), promotes=['con2', 'y2'])
import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarMDA
import numpy as np
prob = om.Problem(model=SellarMDA())
model = prob.model
model.add_design_var('z', lower=np.array([-10.0, 0.0]),
upper=np.array([10.0, 10.0]))
model.add_design_var('x', lower=0.0, upper=10.0)
model.add_objective('obj')
model.add_constraint('con1', upper=0.0)
model.add_constraint('con2', upper=0.0)
driver = prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', tol=1e-9, disp=False)
driver.recording_options['record_derivatives'] = True
driver.add_recorder(om.SqliteRecorder('cases.sql'))
prob.setup()
prob.set_solver_print(0)
prob.run_driver()
prob.cleanup()
cr = om.CaseReader(prob.get_outputs_dir() / 'cases.sql')
# Get derivatives associated with the last iteration.
derivs = cr.get_case(-1).derivatives
derivs
{('obj', 'z'): array([[3.90585345, 1.97002049]]),
('obj', 'x'): array([[0.9934144]]),
('con1', 'z'): array([[-3.95527316, -0.99999439]]),
('con1', 'x'): array([[-0.99999439]]),
('con2', 'z'): array([[2.11249959, 1.28126722]]),
('con2', 'x'): array([[0.2812688]])}
# Get specific derivative.
print(derivs['obj', 'z'])
[[3.90585345 1.97002049]]
Problem recording can also include recording of the derivatives as this example shows.
import openmdao.api as om
from openmdao.test_suite.components.eggcrate import EggCrate
prob = om.Problem()
model = prob.model
model.add_subsystem('egg_crate', EggCrate(), promotes=['x', 'y', 'f_xy'])
model.add_design_var('x', lower=-50.0, upper=50.0)
model.add_design_var('y', lower=-50.0, upper=50.0)
model.add_objective('f_xy')
prob.driver = om.ScipyOptimizeDriver(disp=False, tol=1e-9)
prob.recording_options['record_derivatives'] = True
prob.add_recorder(om.SqliteRecorder('cases.sql'))
prob.setup()
prob.set_solver_print(0)
prob.set_val('x', 2.5)
prob.set_val('y', 2.5)
prob.run_driver()
case_name_1 = "c1"
prob.record(case_name_1)
prob.set_val('x', 0.1)
prob.set_val('y', -0.1)
prob.run_driver()
case_name_2 = "c2"
prob.record(case_name_2)
prob.cleanup()
Model viewer data has already been recorded for Driver.
cr = om.CaseReader(prob.get_outputs_dir() / 'cases.sql')
c1 = cr.get_case(case_name_1)
c2 = cr.get_case(case_name_2)
print(c1.derivatives)
print(c2.derivatives)
{('f_xy', 'x'): array([[-1.50431553e-05]]), ('f_xy', 'y'): array([[-1.50431553e-05]])}
{('f_xy', 'x'): array([[-1.11442227e-06]]), ('f_xy', 'y'): array([[1.11442256e-06]])}
Note
For both Driver
and Problem
, the recording of the derivatives is not affected by
the includes
and excludes
options.