def test_solver_nested_under_double_nested_driver_boundary_var_no_deriv( self): model = set_as_top(Assembly()) model.add('comp', MyComp_No_Deriv()) model.add('bvar', Float(0.0, iotype='in')) model.add('driver', SimpleDriver()) model.add('subdriver', SimpleDriver()) model.driver.workflow.add('subdriver') model.subdriver.workflow.add('comp') model.subdriver.add_parameter('comp.c', low=-100, high=100) model.subdriver.add_objective('comp.y_out - bvar') model.driver.add_parameter('bvar', low=-100, high=100) model.driver.add_objective('bvar - comp.y_out') model.comp.eval_only = True model.run() J = model.driver.workflow.calc_gradient() edges = model.driver.workflow._edges # print edges self.assertEqual(edges['@in0'], ['_pseudo_1.in0', '~subdriver._pseudo_0|in1']) self.assertEqual(edges['_pseudo_1.out0'], ['@out0'])
def test_paramgroup(self): top = set_as_top(Assembly()) top.add('comp1', GComp_noD()) top.add('driver', SimpleDriver()) top.driver.workflow.add(['comp1']) top.driver.gradient_options.directional_fd = True top.run() J = top.driver.calc_gradient(inputs=[('comp1.x1', 'comp1.x2')], outputs=['comp1.y1'], mode='forward') assert_rel_error(self, J[0, 0], 12.0, .001) J = top.driver.calc_gradient(inputs=[('comp1.x1', 'comp1.x2')], outputs=['comp1.y1'], mode='fd') assert_rel_error(self, J[0, 0], 12.0, .001) J = top.driver.calc_gradient(inputs=['comp1.x1', ('comp1.x2')], outputs=['comp1.y1'], mode='forward') assert_rel_error(self, J[0, 0], 5.0, .001) assert_rel_error(self, J[0, 1], 7.0, .001) J = top.driver.calc_gradient(inputs=[('comp1.x1', 'comp1.x2', 'comp1.x3')], outputs=['comp1.y1'], mode='forward') assert_rel_error(self, J[0, 0], 9.0, .001)
def test_fd_step(self): model = set_as_top(Assembly()) model.add('comp', MyComp()) model.driver.workflow.add(['comp']) model.run() J = model.driver.workflow.calc_gradient( inputs=['comp.x1', 'comp.x2', 'comp.x3', 'comp.x4'], outputs=['comp.y']) assert_rel_error(self, J[0, 0], 4.0, 0.0001) assert_rel_error(self, J[0, 1], 4.2, 0.0001) assert_rel_error(self, J[0, 2], 4.0, 0.0001) assert_rel_error(self, J[0, 3], 0.0042, 0.0001) # test add_parameter's fdstep model.add('driver', SimpleDriver()) model.driver.workflow.add(['comp']) model.driver.add_parameter('comp.x1', low=-100, high=100, fd_step=.1) J = model.driver.workflow.calc_gradient(outputs=['comp.y']) assert_rel_error(self, J[0, 0], 4.2, 0.0001) # Parameter groups model.driver.add_parameter(['comp.x2', 'comp.x3'], low=-100, high=100, fd_step=.001) J = model.driver.workflow.calc_gradient(outputs=['comp.y']) assert_rel_error(self, J[0, 1], 8.004, 0.0001)
def setUp(self): self.top = set_as_top(Assembly()) self.top.add('driver', SimpleDriver()) self.top.add('driver1', MyDriver()) self.top.add('driver2', MyDriver()) self.top.add('comp', ExecComp(exprs=['z=a+b+c+d'])) self.top.driver.workflow.add(['driver1', 'driver2'])
def test_baseline_case(self): top = set_as_top(Assembly()) top.add('comp1', ExecCompWithDerivatives(['y1=2.0*x', 'y2=3.0*x', 'y3=4.0*x', 'y4=5.0*x'], ['dy1_dx = 2.0', 'dy2_dx = 3.0', 'dy3_dx = 4.0', 'dy4_dx = 5.0'])) top.add('comp2', ExecComp(['y = 2.0*x1 + x2 + x3 + x4'])) top.driver.workflow.add(['comp1', 'comp2']) top.connect('comp1.y1', 'comp2.x1') top.connect('comp1.y2', 'comp2.x2') top.connect('comp1.y3', 'comp2.x3') top.connect('comp1.y4', 'comp2.x4') top.run() self.assertEqual(top.comp2.exec_count, 1) top.driver.gradient_options.directional_fd = True J = top.driver.calc_gradient(inputs=['comp1.x'], outputs=['comp2.y']) # Ran 1 times, not 4. self.assertEqual(top.comp2.exec_count, 2) assert_rel_error(self, J[0, 0], 16.0, 0.0001) top.driver.gradient_options.directional_fd = False J = top.driver.calc_gradient(inputs=['comp1.x'], outputs=['comp2.y']) # Ran 4 times (4 fd steps). self.assertEqual(top.comp2.exec_count, 6) assert_rel_error(self, J[0, 0], 16.0, 0.0001) # Next, test param / obj top.add('driver', SimpleDriver()) top.driver.add_parameter('comp1.x', low=-1000, high=1000) top.driver.add_objective('comp2.y') top.run() self.assertEqual(top.comp2.exec_count, 7) top.driver.gradient_options.directional_fd = True J = top.driver.calc_gradient() # Ran 1 more times. self.assertEqual(top.comp2.exec_count, 8) assert_rel_error(self, J[0, 0], 16.0, 0.0001) J = top.driver.calc_gradient(mode='fd') # Ran 1 more times (full model fd, 2 edges). self.assertEqual(top.comp2.exec_count, 9) assert_rel_error(self, J[0, 0], 16.0, 0.0001)
def test_simple_array(self): model = set_as_top(Assembly()) model.add('comp', SimpleCompArray()) model.driver.workflow.add('comp') #model.driver.gradient_options.fd_form = 'complex_step' model.run() J = model.driver.workflow.calc_gradient(inputs=['comp.x'], outputs=['comp.y']) diff = abs(J - model.comp.J).max() assert_rel_error(self, diff, 0.0, .0001) self.assertTrue(J[0, 0] is not complex) model.add('driver', SimpleDriver()) model.driver.add_parameter('comp.x', low=-10, high=10) model.driver.add_objective('comp.y - comp.x') model.run() model.driver.workflow.config_changed() J = model.driver.workflow.calc_gradient(mode='fd') diff = abs(J + eye(4) - model.comp.J).max() assert_rel_error(self, diff, 0.0, .0001) self.assertTrue(J[0, 0] is not complex)
def setUp(self): """ Called before each test. """ self.model = Assembly() self.model.add('c1', MyComp()) self.model.add('c2', MyComp()) self.model.add('driver', SimpleDriver()) self.model.driver.workflow = CyclicWorkflow() self.model.driver.workflow.add(['c1', 'c2'])
def configure(self): """ Set it all up. """ self.add('driver', SimpleDriver()) self.add('solver', SimpleDriver()) self.add('comp1', ParaboloidDerivative()) self.add('comp2', ParaboloidDerivative()) self.connect('comp1.f_xy', 'comp2.y') self.connect('comp2.f_xy', 'comp1.y') self.driver.workflow.add(['solver']) self.solver.workflow.add(['comp1', 'comp2']) self.driver.add_parameter('comp1.x', low=0.0, high=14.1) self.driver.add_objective('comp2.f_xy')
def test_smart_low_high_array_param(self): top = Assembly() top.add('paraboloid', ArrayParaboloid()) driver = top.add('driver', SimpleDriver()) driver.add_objective('paraboloid.f_x') driver.add_parameter('paraboloid.x', low=[-100, -99], high=[100, 99]) driver.workflow.add('paraboloid') top.run() J = top.driver.workflow.calc_gradient()
def test_SysCM(self): compname = 'SysCM' self.setup(compname, self.arg_dict) self.model.comp.eval_only = True self.model.comp._run_explicit = True self.model.add('driver', SimpleDriver()) self.model.driver.add_parameter('comp.eta', low=-999, high=999) self.model.driver.add_constraint('comp.eta_res = 0') self.run_model() self.compare_derivatives(rel_error=True)
def configure(self): """ set it up """ self.add('c1', MyComp()) self.add('c2', MyComp()) self.add('driver', SimpleDriver()) self.driver.workflow = CyclicWorkflow() self.driver.workflow.add(['c1', 'c2']) self.connect('c1.y', 'c2.x') self.connect('c2.y', 'c1.x')
def test_solver_nested_under_double_nested_driver_no_deriv(self): model = set_as_top(Assembly()) model.add('comp', MyComp_No_Deriv()) model.add('subdriver', SimpleDriver()) model.add('solver', BroydenSolver()) model.driver.workflow.add('subdriver') model.subdriver.workflow.add('solver') model.solver.workflow.add('comp') model.solver.tol = 0.0000001 model.solver.add_parameter('comp.x', low=-100, high=100) model.solver.add_parameter('comp.y', low=-100, high=100) model.solver.add_parameter('comp.z', low=-100, high=100) model.solver.add_constraint('comp.res[0] = 0') model.solver.add_constraint('comp.res[1] = 0') model.solver.add_constraint('comp.res[2] = 0') model.subdriver.add_parameter('comp.c', low=-100, high=100) model.subdriver.add_objective('comp.y_out') model.comp.eval_only = True model.run() J = model.driver.workflow.calc_gradient(inputs=['comp.c'], outputs=['comp.y_out']) edges = model.driver.workflow._edges # print edges self.assertEqual(edges['@in0'], ['~subdriver.comp|c']) self.assertEqual(edges['~subdriver.comp|y_out'], ['@out0']) # print J assert_rel_error(self, J[0][0], 0.75, 1e-5) model.driver.workflow.config_changed() J = model.driver.workflow.calc_gradient(inputs=['comp.c'], outputs=['comp.y_out'], mode='adjoint') # print J assert_rel_error(self, J[0][0], 0.75, 1e-5) model.driver.workflow.config_changed() J = model.driver.workflow.calc_gradient(inputs=['comp.c'], outputs=['comp.y_out'], mode='fd') # print J assert_rel_error(self, J[0][0], 0.75, 1e-5)
def test_fd_step(self): model = set_as_top(Assembly()) model.add('comp', MyComp()) model.driver.workflow.add(['comp']) model.run() J = model.driver.workflow.calc_gradient( inputs=['comp.x1', 'comp.x2', 'comp.x3', 'comp.x4'], outputs=['comp.y']) assert_rel_error(self, J[0, 0], 4.0, 0.0001) assert_rel_error(self, J[0, 1], 4.2, 0.0001) assert_rel_error(self, J[0, 2], 4.0, 0.0001) assert_rel_error(self, J[0, 3], 0.0042, 0.0001) # test add_parameter's fdstep model.add('driver', SimpleDriver()) model.driver.workflow.add(['comp']) model.driver.add_parameter('comp.x1', low=-100, high=100, fd_step=.1) J = model.driver.workflow.calc_gradient(outputs=['comp.y']) assert_rel_error(self, J[0, 0], 4.2, 0.0001) # Parameter groups model.driver.add_parameter(['comp.x2', 'comp.x3'], low=-100, high=100, fd_step=.001) J = model.driver.workflow.calc_gradient(outputs=['comp.y']) assert_rel_error(self, J[0, 1], 8.004, 0.0001) # More Parameter Groups with pseudocomps in them. model.driver.add_parameter('comp.x4', low=-100, high=100, fd_step=1.001) model.driver.add_objective( 'comp.x1 + comp.x2 + comp.x3 + comp.x4 + comp.y') model.run() model.driver.workflow.config_changed() J = model.driver.workflow.calc_gradient(inputs=['comp.x4'], mode='fd') assert_rel_error(self, J[0, 0], 1.006, 0.0001)
def test_fd_step_type_relative(self): model = set_as_top(Assembly()) model.add('driver', SimpleDriver()) model.add('comp', MyComp()) model.driver.workflow.add(['comp']) model.comp.x1 = 1e12 model.run() J = model.driver.calc_gradient(inputs=['comp.x1'], outputs=['comp.y']) assert_rel_error(self, J[0, 0], 0.0, 0.0001) model.driver.gradient_options.fd_step_type = 'relative' J = model.driver.calc_gradient(inputs=['comp.x1'], outputs=['comp.y']) assert_rel_error(self, J[0, 0], 4.0e12, 0.0001)
def configure(self): self.add('p1pre', Paraboloid()) #self.add('p1', Paraboloid()) self.add('down', Downstream()) self.add('driver', SimpleDriver()) self.add('sens', SensitivityDriver()) self.driver.add_parameter('p1pre.x', low=-100, high=101) self.driver.add_objective('down.y') self.sens.add_parameter('p1pre.x', low=100, high=101) self.sens.add_objective('p1pre.f_xy') self.connect('sens.dF', 'down.x') self.driver.workflow.add(['sens', 'down']) self.sens.workflow.add(['p1pre'])
def test_fd_step_type_bounds_scaled(self): model = set_as_top(Assembly()) model.add('comp', MyComp()) model.driver.workflow.add(['comp']) model.run() model.driver.workflow.config_changed() J = model.driver.workflow.calc_gradient(inputs=['comp.x5'], outputs=['comp.y']) assert_rel_error(self, J[0, 0], 4.2, 0.0001) model.driver.gradient_options.fd_step_type = 'bounds_scaled' model.run() model.driver.workflow.config_changed() J = model.driver.workflow.calc_gradient(inputs=['comp.x6'], outputs=['comp.y']) assert_rel_error(self, J[0, 0], 4.2, 0.0001) model.run() model.driver.workflow.config_changed() try: J = model.driver.workflow.calc_gradient(inputs=['comp.x7'], outputs=['comp.y']) except RuntimeError as err: self.assertEqual( str(err), "For variable 'comp.x7', a finite " "difference step type of bounds_scaled " "is used but required low and high " "values are not set") else: self.fail("Exception expected because low " "and high not set for comp.x7") # test add_parameter's fdstep model.add('driver', SimpleDriver()) model.driver.workflow.add(['comp']) model.driver.gradient_options.fd_step_type = 'bounds_scaled' model.driver.add_parameter('comp.x2', low=0.0, high=1000.0, fd_step=.0001) J = model.driver.workflow.calc_gradient(outputs=['comp.y']) assert_rel_error(self, J[0, 0], 4.2, 0.0001)
def test_get_req_compnames_vartree_param_obj(self): # Tests a fix for a bug reported by Rick Damiani class PileGeoInputs(VariableTree): """Basic Geometric Inputs need to build Legs of Jacket""" Lp = Float(units='m', desc='Pile Embedment Length.') class MyComp(Component): x = Float(0.0, iotype='in') y = Float(0.0, iotype='in') def execute(self): self.y = 2.0 * self.x class Top(Assembly): Pileinputs = VarTree(PileGeoInputs(), iotype='in', desc="Pile Input Data") SPIstiffness = VarTree(PileGeoInputs(), iotype='out', desc="Pile Input Data") def configure(self): self.connect('Pileinputs.Lp', 'SPIstiffness.Lp') self.disconnect('Pileinputs.Lp', 'SPIstiffness.Lp') self.add('comp', MyComp()) self.driver.workflow.add('comp') self.connect('Pileinputs.Lp', 'comp.x') top = set_as_top(Top()) top.replace('driver', SimpleDriver()) top.driver.add_parameter('Pileinputs.Lp', low=-100, high=100) top.driver.add_objective('SPIstiffness.Lp + comp.y') comps = top.driver._get_required_compnames() self.assertTrue(len(comps) == 2) self.assertTrue('comp' in comps)
def test_PA_slices(self): top = set_as_top(Assembly()) top.add('comp', ArrayComp2D()) top.add('driver', SimpleDriver()) top.driver.workflow.add('comp') top.comp.force_fd = True top.driver.gradient_options.directional_fd = True top.driver.add_parameter('comp.x', low=-100, high=100) top.driver.add_constraint('comp.y[0][-1] < 1.0') top.driver.add_constraint('sum(comp.y) < 4.0') top.run() J = top.driver.workflow.calc_gradient(mode='forward') assert_rel_error(self, J[0, 0], 4.0, 0.001) assert_rel_error(self, J[0, 1], 2.0, 0.001) assert_rel_error(self, J[0, 2], 6.0, 0.001) assert_rel_error(self, J[0, 3], 5.0, 0.001)
def test_smart_low_high(self): top = Assembly() top.add('comp', MyComp()) driver = top.add('driver', SimpleDriver()) top.comp.add('x1', Float(1.0, iotype='in', low=-1.0, high=1.0)) driver.add_objective('comp.y') driver.add_parameter('comp.x1', low=-1.0, high=1.0) driver.workflow.add('comp') top.driver.gradient_options.fd_form = 'central' top.driver.gradient_options.fd_step = 0.1 top.comp.x1 = -0.95 top.run() J = top.driver.workflow.calc_gradient() assert_rel_error(self, J[0, 0], -3.6, 0.001) top.comp.x1 = 0.95 top.run() J = top.driver.workflow.calc_gradient() assert_rel_error(self, J[0, 0], 3.6, 0.001)
def test_cascade_opt(self): top = set_as_top(Assembly()) eq = ['f = (x-3)**2 + x*y + (y+4)**2 - 3'] deriv = ['df_dx = 2.0*x - 6.0 + y', 'df_dy = 2.0*y + 8.0 + x'] top.add('comp', ExecCompWithDerivatives(eq, deriv)) top.add('driver', SimpleDriver()) top.add('opt1', SLSQPdriver()) top.add('opt2', SLSQPdriver()) top.opt1.workflow.add(['comp']) top.opt2.workflow.add(['comp']) top.driver.workflow.add(['opt1', 'opt2']) top.opt1.add_parameter('comp.x', low=-100, high=100) top.opt1.add_parameter('comp.y', low=-100, high=100) top.opt1.add_objective('comp.f') top.opt1.maxiter = 2 top.opt2.add_parameter('comp.x', low=-100, high=100) top.opt2.add_parameter('comp.y', low=-100, high=100) top.opt2.add_objective('comp.f') top.opt2.maxiter = 50 top.run() assert_rel_error(self, top.comp.x, 6.666309, 0.01) assert_rel_error(self, top.comp.y, -7.333026, 0.01) J = top.driver.workflow.calc_gradient(inputs=['comp.x', 'comp.y'], outputs=['comp.f']) edges = top.driver.workflow._edges print edges self.assertEqual(set(edges['@in0']), set(['~opt1.comp|x', '~opt2.comp|x'])) self.assertEqual(set(edges['@in1']), set(['~opt1.comp|y', '~opt2.comp|y'])) self.assertEqual(set(edges['~opt1.comp|f']), set(['@out0'])) self.assertEqual(set(edges['~opt2.comp|f']), set(['@out0']))
def test_broadcast_input_to_opaque_system(self): # This test replicates a bug when finite differencing a boundary variable that # broadcasts to multiple places. top = set_as_top(Assembly()) top.add('comp1', ExecComp(['y=7.0*x1'])) top.add('comp2', ExecComp(['y=5.0*x1 + 2.0*x2'])) top.add('driver', SimpleDriver()) top.driver.workflow.add(['comp1', 'comp2']) top.add('x1', Float(3.0, iotype='in')) top.connect('comp1.y', 'comp2.x2') top.connect('x1', 'comp1.x1') top.connect('x1', 'comp2.x1') top.run() J = top.driver.calc_gradient(inputs=['x1'], outputs=['comp2.y'], mode='forward') assert_rel_error(self, J[0, 0], 19.0, .001)
def test_MissionSegment(self): # define bounds for the flight path angle gamma_lb = np.tan(-10.0 * (np.pi / 180.0)) / 1e-1 gamma_ub = np.tan(10.0 * (np.pi / 180.0)) / 1e-1 num_elem = 10 num_cp = 5 x_range = 150.0 x_init = x_range * 1e3 * ( 1 - np.cos(np.linspace(0, 1, num_cp) * np.pi)) / 2 / 1e6 v_init = np.ones(num_cp) * 2.3 h_init = 1 * np.sin(np.pi * x_init / (x_range / 1e3)) model = set_as_top(MissionSegment(num_elem, num_cp, x_init)) model.replace('driver', SimpleDriver()) model.h_pt = h_init model.v_pt = v_init # Pull velocity from BSpline instead of calculating it. model.SysSpeed.v_specified = True # Initial parameters model.S = 427.8 / 1e2 model.ac_w = 210000 * 9.81 / 1e6 model.thrust_sl = 1020000.0 / 1e6 / 3 model.SFCSL = 8.951 model.AR = 8.68 model.oswald = 0.8 # Change some scaling parameters so that we match what they were when # the pickle was created. model.SysTau.thrust_scale = 0.072 model.SysCLTar.fuel_scale = 1e6 model.SysCTTar.fuel_scale = 1e6 model.SysFuelWeight.fuel_scale = 1e6 model.driver.add_parameter('h_pt', low=0.0, high=20.0) model.driver.add_objective('SysFuelObj.fuelburn') model.driver.add_constraint('SysHBspline.h[0] = 0.0') model.driver.add_constraint('SysHBspline.h[-1] = 0.0') model.driver.add_constraint('SysTmin.Tmin < 0.0') model.driver.add_constraint('SysTmax.Tmax < 0.0') model.driver.add_constraint('%.15f < SysGammaBspline.Gamma < %.15f' % \ (gamma_lb, gamma_ub), linear=True) model.run() new_derivs = model.driver.calc_gradient() # Load in original data from pickle dirname = os.path.abspath(os.path.dirname(__file__)) filename = os.path.join(dirname, 'derivs.p') old_derivs_dict = pickle.load(open(filename, 'rb')) for i in range(0, 4): old_derivs = old_derivs_dict['h_pt' + str(i)] #print 'h_pt' + str(i) # name change old_derivs['fuelburn'] = old_derivs['wf_obj'] for j, key in enumerate(['fuelburn', 'h_i', 'h_f', 'Tmin', 'Tmax']): old = old_derivs[key] new = new_derivs[j, i] #diff = np.nan_to_num(abs(new - old) / old) diff = new - old #print key #print old #print new assert_rel_error(self, diff.max(), 0.0, 1e-5)
def test_MissionSegment(self): # define bounds for the flight path angle gamma_lb = np.tan(-10.0 * (np.pi / 180.0)) / 1e-1 gamma_ub = np.tan(10.0 * (np.pi / 180.0)) / 1e-1 num_elem = 100 num_cp = 30 x_range = 9000.0 altitude = np.zeros(num_elem + 1) altitude = 10 * np.sin(np.pi * np.linspace(0, 1, num_elem + 1)) x_range *= 1.852 x_init = x_range * 1e3 * ( 1 - np.cos(np.linspace(0, 1, num_cp) * np.pi)) / 2 / 1e6 M_init = np.ones(num_cp) * 0.8 h_init = 10 * np.sin(np.pi * x_init / (x_range / 1e3)) model = set_as_top( MissionSegment(num_elem=num_elem, num_cp=num_cp, x_pts=x_init, surr_file='../crm_surr')) model.replace('driver', SimpleDriver()) model.h_pt = h_init model.M_pt = M_init model.set_init_h_pt(altitude) # Initial parameters model.S = 427.8 / 1e2 model.ac_w = 210000 * 9.81 / 1e6 model.thrust_sl = 1020000.0 / 1e6 model.SFCSL = 8.951 * 9.81 model.AR = 8.68 model.oswald = 0.8 model.driver.add_parameter('h_pt', low=0.0, high=20.0) model.driver.add_objective('SysFuelObj.fuelburn') model.driver.add_constraint('SysHi.h_i = 0.0') model.driver.add_constraint('SysHf.h_f = 0.0') model.driver.add_constraint('SysTmin.Tmin < 0.0') model.driver.add_constraint('SysTmax.Tmax < 0.0') model.driver.add_constraint('%.15f < SysGammaBspline.Gamma < %.15f' % (gamma_lb, gamma_ub), linear=True) model.run() new_derivs = model.driver.calc_gradient(return_format='dict') # Load in original data from pickle dirname = os.path.abspath(os.path.dirname(__file__)) filename = os.path.join(dirname, 'derivs2.p') old_derivs_dict = pickle.load(open(filename, 'rb')) translate_dict = { 'fuelburn': '_pseudo_2.out0', 'h_i': '_pseudo_3.out0', 'h_f': '_pseudo_4.out0', 'Tmin': '_pseudo_5.out0', 'Tmax': '_pseudo_6.out0' } #'gamma': '_pseudo_12.out0'} for j, key in enumerate(translate_dict.keys()): old = old_derivs_dict[key]['h_pt'] print 'h_pt', key openmdao_key = translate_dict[key] new = new_derivs[openmdao_key]['h_pt'] diff = new - old print 'old', old print 'new', new assert_rel_error(self, diff.max(), 0.0, 2e-5) for i in xrange(0, num_elem): old = old_derivs_dict['gamma'][i]['h_pt'] #print 'h_pt', 'gamma'+str(i) new = new_derivs['_pseudo_7.out0']['h_pt'][i, :] diff = new - old #print 'old', old #print 'new', new assert_rel_error(self, diff.max(), 0.0, 2e-5)