diff --git a/topfarm/_topfarm.py b/topfarm/_topfarm.py
index d3de26ec9fef144aad7691a50fa8481faaec62e3..75d62a657b96b5f710787513a3c0d136b804ff86 100644
--- a/topfarm/_topfarm.py
+++ b/topfarm/_topfarm.py
@@ -15,17 +15,31 @@ from openmdao.api import Problem, ScipyOptimizeDriver, IndepVarComp
 
 
 class TopFarmProblem(Problem):
-    def __init__(self, cost_comp, record_id, expected_cost):
+    def __init__(self, cost_comp, driver, plot_comp, record_id, expected_cost):
         Problem.__init__(self)
         if isinstance(cost_comp, TopFarmProblem):
             cost_comp = cost_comp.as_component()
         cost_comp.parent = self
-        self.record_id = record_id
         self.cost_comp = cost_comp
+        
+        if isinstance(driver, list):
+            driver = DOEDriver(ListGenerator(driver))
+        elif isinstance(driver, DOEGenerator):
+            driver = DOEDriver(generator=driver)
+        self.driver = driver
+
+        self.plot_comp = plot_comp
+
+        self.record_id = record_id
+
         self.indeps = self.model.add_subsystem('indeps', IndepVarComp(), promotes=['*'])
         self.model.add_subsystem('cost_comp', cost_comp, promotes=['*'])
         self.model.add_objective('cost', scaler=1 / abs(expected_cost))
 
+        if plot_comp:
+            self.model.add_subsystem('plot_comp', plot_comp, promotes=['*'])
+            plot_comp.problem = self
+
     @property
     def cost(self):
         return self['cost'][0]
@@ -54,10 +68,10 @@ class TopFarmProblem(Problem):
     def evaluate(self, state={}):
         t = time.time()
         self.update_state(state)
-        rec = ListRecorder()
-        self.driver.add_recorder(rec)
+        self.recorder = ListRecorder()
+        self.driver.add_recorder(self.recorder)
         self.run_model()
-        self.driver._rec_mgr._recorders.remove(rec)
+        self.driver._rec_mgr._recorders.remove(self.recorder)
         print("Evaluated in\t%.3fs" % (time.time() - t))
         return self.cost, self.state
 
@@ -80,8 +94,8 @@ class TopFarmProblem(Problem):
             try:
                 self.update_state({k: self.recorder.get(k)[-1] for k in self.state.keys() if k not in state})
             except ValueError:
-                pass # loaded state does not fit into dimension of current state
-            
+                pass  # loaded state does not fit into dimension of current state
+
         self.driver.add_recorder(self.recorder)
         self.run_driver()
         self.cleanup()
@@ -126,8 +140,8 @@ class TopFarmProblem(Problem):
 
 
 class TurbineTypeOptimizationProblem(TopFarmProblem):
-    def __init__(self, cost_comp, turbineTypes, lower, upper, driver, record_id=None, expected_cost=1):
-        TopFarmProblem.__init__(self, cost_comp, record_id, expected_cost)
+    def __init__(self, cost_comp, turbineTypes, lower, upper, driver, plot_comp=None, record_id=None, expected_cost=1):
+        TopFarmProblem.__init__(self, cost_comp, driver, plot_comp, record_id, expected_cost)
         self.turbineTypes = turbineTypes
         self.lower = lower
         self.upper = upper
@@ -141,13 +155,15 @@ class TurbineTypeOptimizationProblem(TopFarmProblem):
 
         self.model.add_constraint('turbineType', lower=lim[:, 0], upper=lim[:, 1])
         self.indeps.add_output('turbineType', np.array(turbineTypes).astype(np.int))
-        self.driver = driver
+
         self.model.add_design_var('turbineType', lower=lim[:, 0], upper=lim[:, 1])
+        if self.plot_comp:
+            plot_comp.n_wt = n_wt
         self.setup(check=True, mode='fwd')
 
     @property
     def state(self):
-        state = {'turbineType': np.array(self['turbineType']).astype(np.int)}
+        state = {'turbineType': np.round(self['turbineType']).astype(np.int)}
         state.update(TopFarmProblem.state.fget(self))
         return state
 
@@ -155,11 +171,16 @@ class TurbineTypeOptimizationProblem(TopFarmProblem):
 class TurbineXYZOptimizationProblem(TopFarmProblem):
     def __init__(self, cost_comp, turbineXYZ, boundary_comp, min_spacing=None,
                  driver=ScipyOptimizeDriver(), plot_comp=None, record_id=None, expected_cost=1):
-        self.turbineXYZ = turbineXYZ
-        TopFarmProblem.__init__(self, cost_comp, record_id, expected_cost)
-
         turbineXYZ = np.array(turbineXYZ)
+        self.turbineXYZ = turbineXYZ
         self.n_wt = n_wt = turbineXYZ.shape[0]
+
+        if plot_comp:
+            if plot_comp == "default":
+                plot_comp = PlotComp()
+
+        TopFarmProblem.__init__(self, cost_comp, driver, plot_comp, record_id, expected_cost)
+
         turbineXYZ = np.hstack((turbineXYZ, np.zeros((n_wt, 4 - turbineXYZ.shape[1]))))
         self.min_spacing = min_spacing
 
@@ -169,7 +190,7 @@ class TurbineXYZOptimizationProblem(TopFarmProblem):
         self.boundary_comp = boundary_comp
         boundary_comp.setup_as_constraints(self)
 
-        do = driver.options
+        do = self.driver.options
         if len(boundary_comp.xy_boundary) > 0:
 
             ref0_x, ref0_y = self.boundary_comp.xy_boundary.min(0)
@@ -202,20 +223,13 @@ class TurbineXYZOptimizationProblem(TopFarmProblem):
         self.indeps.add_output('turbineY', turbineXYZ[:, 1], units='m')
         self.indeps.add_output('turbineZ', turbineXYZ[:, 2], units='m')
 
-        self.driver = driver
-
         if plot_comp:
-            if plot_comp == "default":
-                plot_comp = PlotComp()
-
             plot_comp.n_wt = n_wt
             if self.boundary_comp:
                 plot_comp.n_vertices = len(self.boundary_comp.xy_boundary)
             else:
                 plot_comp.n_vertices = 0
-            self.model.add_subsystem('plot_comp', plot_comp, promotes=['*'])
 
-        self.plot_comp = plot_comp
         self.setup(check=True, mode='fwd')
 
     @property
@@ -246,10 +260,6 @@ class InitialXYZOptimizationProblem(TurbineXYZOptimizationProblem):
                  driver=None, plot_comp=None):
         #          if driver is None:
         #              driver = DOEDriver(shuffle_generator(self, 10))
-        if isinstance(driver, list):
-            driver = DOEDriver(ListGenerator(driver))
-        elif isinstance(driver, DOEGenerator):
-            driver = DOEDriver(generator=driver)
         TurbineXYZOptimizationProblem.__init__(self, cost_comp, turbineXYZ,
                                                boundary_comp, min_spacing,
                                                driver=driver, plot_comp=plot_comp)
diff --git a/topfarm/constraint_components/constrained_generator.py b/topfarm/constraint_components/constrained_generator.py
index 612944491e8c3901bdfe73cb931588b7bc06785d..cecaaaf1f84c57d5446943683a6c39837a787342 100644
--- a/topfarm/constraint_components/constrained_generator.py
+++ b/topfarm/constraint_components/constrained_generator.py
@@ -2,6 +2,30 @@ from openmdao.drivers.doe_generators import DOEGenerator, ListGenerator
 import numpy as np
 
 
+
+class ConstrainedDiscardXYZGenerator(DOEGenerator):
+    def __init__(self, generator):
+        DOEGenerator.__init__(self)
+        if isinstance(generator, list):
+            generator = ListGenerator(generator)
+        self.generator = generator
+        
+    def __call__(self, design_vars, model=None):
+        xy_boundary_comp = model._get_subsystem('xy_bound_comp')
+        spacing_comp = model._get_subsystem('spacing_comp')
+        for xyz_tuple_lst in self.generator(design_vars, model=model):
+            x, y, z = ([np.array(t[1]) for t in xyz_tuple_lst] + [0])[:3]
+            if spacing_comp:
+                dist = spacing_comp._compute(x, y)
+                if np.any(dist < spacing_comp.min_spacing**2):
+                    continue
+                 
+            if xy_boundary_comp:
+                dist_to_boundary =xy_boundary_comp.distances(x,y)
+                if np.any(dist_to_boundary<0):
+                    continue
+            yield xyz_tuple_lst
+
 class ConstrainedXYZGenerator(DOEGenerator):
     def __init__(self, generator, n_iter=1000, step_size=0.1, offset=0.5, verbose=False):
         DOEGenerator.__init__(self)
@@ -17,16 +41,15 @@ class ConstrainedXYZGenerator(DOEGenerator):
         xy_boundary_comp = model._get_subsystem('xy_bound_comp')
         spacing_comp = model._get_subsystem('spacing_comp')
         for xyz_tuple_lst in self.generator(design_vars, model=model):
-            x, y, z = [np.array(t[1]) for t in xyz_tuple_lst]
+            x, y, z = ([np.array(t[1]).astype(np.float) for t in xyz_tuple_lst] + [0])[:3]
             if spacing_comp:
                 for j in range(self.n_iter):
                     dist = spacing_comp._compute(x, y)
                     dx, dy = spacing_comp._compute_partials(x, y)
-                    dx, dy = dx[0], dy[0]
-                    index = np.argmin(dist)
+                    index = int(np.argmin(dist))
                     if dist[index] < spacing_comp.min_spacing**2 or j == 0:
-                        x[index] += dx[index] * self.step_size
-                        y[index] += dy[index] * self.step_size
+                        x += dx[index] * self.step_size
+                        y += dy[index] * self.step_size
                         if xy_boundary_comp:
                             x, y, z = xy_boundary_comp.move_inside(x, y, z)
                     else:
diff --git a/topfarm/plotting.py b/topfarm/plotting.py
index 0f710f2f5977d5aaf936a5d7279005e4cea1c1be..4474ccad01e2d63cdd24df30fbbac1935271cf3f 100644
--- a/topfarm/plotting.py
+++ b/topfarm/plotting.py
@@ -21,15 +21,19 @@ def mypause(interval):
 class PlotComp(ExplicitComponent):
     colors = ['b', 'r', 'm', 'c', 'g', 'y', 'orange', 'indigo', 'grey'] * 100
 
-    def __init__(self, memory=10, delay=0.001, plot_initial=True,
-                 animate=False):
+    def __init__(self, memory=10, delay=0.001, plot_initial=True, ax=None):
         ExplicitComponent.__init__(self)
+        self.ax_ = ax
         self.memory = memory
         self.delay = delay
         self.plot_initial = plot_initial
         self.history = []
         self.counter = 0
-        self.animate = animate
+        self.by_pass = False
+
+    @property
+    def ax(self):
+        return self.ax_ or plt.gca()
 
     def show(self):
         plt.show()
@@ -38,66 +42,92 @@ class PlotComp(ExplicitComponent):
         self.add_input('turbineX', np.zeros(self.n_wt), units='m')
         self.add_input('turbineY', np.zeros(self.n_wt), units='m')
         self.add_input('cost', 0.)
-        self.add_input('boundary', np.zeros((self.n_vertices, 2)), units='m')
-
-    def init_plot(self, boundary):
-        plt.cla()
-        plt.axis('equal')
-        b = np.r_[boundary[:], boundary[:1]]
-        plt.plot(b[:, 0], b[:, 1], 'k')
-        mi = b.min(0)
-        ma = b.max(0)
+        if hasattr(self, 'n_vertices'):
+            self.add_input('boundary', np.zeros((self.n_vertices, 2)), units='m')
+
+    def init_plot(self, limits):
+        self.ax.cla()
+        self.ax.axis('equal')
+        mi = limits.min(0)
+        ma = limits.max(0)
         ra = ma - mi
         ext = .1
         xlim, ylim = np.array([mi - ext * ra, ma + ext * ra]).T
-        plt.xlim(xlim)
-        plt.ylim(ylim)
+        self.ax.set_xlim(xlim)
+        self.ax.set_ylim(ylim)
+
+    def plot_boundary(self, boundary):
+        b = np.r_[boundary[:], boundary[:1]]
+        plt.plot(b[:, 0], b[:, 1], 'k')
+
+    def plot_history(self, x, y):
+        rec = self.problem.recorder
+        if rec.num_cases > 0:
+            x = np.r_[rec['turbineX'][-self.memory:], [x]]
+            y = np.r_[rec['turbineY'][-self.memory:], [y]]
+            for c, x_, y_ in zip(self.colors, x.T, y.T):
+                self.ax.plot(x_, y_, '--', color=c)
+
+    def plot_initial2current(self, x0, y0, x, y):
+        rec = self.problem.recorder
+        if rec.num_cases > 0:
+            x0 = rec['turbineX'][0]
+            y0 = rec['turbineY'][0]
+            for c, x0_, y0_, x_, y_ in zip(self.colors, x0, y0, x, y):
+                self.ax.plot((x0_, x_), (y0_, y_), '-', color=c)
+
+    def plot_current_position(self, x, y):
+        for c, x_, y_ in zip(self.colors, x, y):
+            self.ax.plot(x_, y_, 'o', color=c, ms=5)
+            self.ax.plot(x_, y_, 'xk', ms=4)
+
+    def set_title(self, cost0, cost):
+        rec = self.problem.recorder
+        if cost0 != 0:
+            self.ax.set_title("%d: %f (%.2f%%)" % (rec.num_cases, cost, (cost0 - cost) / cost0 * 100))
+        else:
+            self.ax.set_title("%d: %f" % (rec.num_cases, cost))
+
+    def get_initial(self):
+        rec = self.problem.recorder
+        if rec.num_cases > 0:
+            x0 = rec['turbineX'][0]
+            y0 = rec['turbineY'][0]
+            cost0 = rec['cost'][0]
+            return x0, y0, cost0
 
     def compute(self, inputs, outputs):
-        x = inputs['turbineX']
-        y = inputs['turbineY']
-        cost = inputs['cost'][0]
-        if not hasattr(self, "initial"):
-            self.initial = np.array([x, y]).T, cost
-        self.history = [(x.copy(), y.copy())] + self.history[:self.memory]
-
-        boundary = inputs['boundary']
-        self.init_plot(boundary)
-        plt.title("%d: %f (%.2f%%)" % (self.counter, cost,
-                  (self.initial[1]-cost)/self.initial[1]*100))
-
-        history_arr = np.array(self.history)
-        for i, c, x_, y_ in zip(range(len(x)), self.colors, x, y):
-            if self.plot_initial:
-                plt.plot([self.initial[0][i, 0], x_],
-                         [self.initial[0][i, 1], y_], '-', color=c, lw=1)
-            plt.plot(history_arr[:, 0, i], history_arr[:, 1, i], '.--',
-                     color=c, lw=1)
-            plt.plot(x_, y_, 'o', color=c, ms=5)
-            plt.plot(x_, y_, 'x' + 'k', ms=4)
-            self.temp = '../__animations__'
-            if not os.path.exists(self.temp):
-                os.makedirs(self.temp)
-            if self.animate:
-                path = os.path.join(self.temp,
-                                    'plot_{:05d}.png'.format(self.counter))
-                plt.savefig(path)
-
-        if self.counter == 0:
-            plt.pause(.01)
-        mypause(self.delay)
-
-        self.counter += 1
-
-    def run_animate(self, anim_time=10, verbose=False):
-        N = anim_time/self.counter
-        string = 'ffmpeg -f image2 -r 1/'
-        string += '{} -i {}//plot_%05d.png'.format(N, self.temp)
-        string += ' -vcodec mpeg4 -y {}//animation.mp4'.format(self.temp)
-        if verbose:
-            print('\nCreating animation:')
-            print(string)
-        os.system(string)
+        if self.by_pass is False:
+            x = inputs['turbineX']
+            y = inputs['turbineY']
+            cost = inputs['cost'][0]
+
+            if 'boundary' in inputs:
+                boundary = inputs['boundary']
+                self.init_plot(boundary)
+                self.plot_boundary(boundary)
+            else:
+                self.init_plot(np.array([x, y]).T)
+
+            initial = self.get_initial()
+
+            if initial is not None:
+                x0, y0, cost0 = initial
+                if self.plot_initial:
+                    self.plot_initial2current(x0, y0, x, y)
+                if self.memory > 0:
+                    self.plot_history(x, y)
+            else:
+                cost0 = cost
+
+            self.plot_current_position(x, y)
+            self.set_title(cost0, cost)
+
+            if self.counter == 0:
+                plt.pause(.01)
+            mypause(self.delay)
+
+            self.counter += 1
 
 
 class NoPlot(PlotComp):
@@ -108,10 +138,35 @@ class NoPlot(PlotComp):
         pass
 
     def setup(self):
-        self.add_input('turbineX', np.zeros(self.n_wt), units='m')
-        self.add_input('turbineY', np.zeros(self.n_wt), units='m')
         self.add_input('cost', 0.)
-        self.add_input('boundary', np.zeros((self.n_vertices, 2)), units='m')
 
     def compute(self, inputs, outputs):
         pass
+
+
+class TurbineTypePlotComponent(PlotComp):
+    colors = ['b', 'r', 'm', 'c', 'g', 'y', 'orange', 'indigo', 'grey'] * 100
+    markers = np.array(list(".ov^<>12348spP*hH+xXDd|_"))
+
+    def __init__(self, turbine_type_names, memory=10, delay=0.001, plot_initial=True):
+        self.turbine_type_names = turbine_type_names
+        PlotComp.__init__(self, memory=memory, delay=delay, plot_initial=plot_initial)
+
+    def setup(self):
+        PlotComp.setup(self)
+        self.add_input('turbineType', np.zeros(self.n_wt, dtype=np.int))
+
+    def compute(self, inputs, outputs):
+        self.types = np.asarray(inputs['turbineType'], dtype=np.int)
+        PlotComp.compute(self, inputs, outputs)
+
+    def init_plot(self, limits):
+        PlotComp.init_plot(self, limits)
+        for m, n in zip(self.markers, self.turbine_type_names):
+            self.ax.plot([], [], m + 'k', label=n)
+        self.ax.legend()
+
+    def plot_current_position(self, x, y):
+        for m, c, x_, y_ in zip(self.markers[self.types], self.colors, x, y):
+            #self.ax.plot(x_, y_, 'o', color=c, ms=5)
+            self.ax.plot(x_, y_,  m + 'k', markeredgecolor=c, markeredgewidth=1, ms=8)
diff --git a/topfarm/recorders.py b/topfarm/recorders.py
index 216dcc1ccb80f9aac158a2a175dd2bdbbf3c13dc..03252a43afdc2774925d3ff29859a689f4c46183 100644
--- a/topfarm/recorders.py
+++ b/topfarm/recorders.py
@@ -84,6 +84,10 @@ class ListRecorder(BaseRecorder):
             self._abs2meta[name]['explicit'] = True
             if name in states:
                 self._abs2meta[name]['explicit'] = False
+                
+    @property
+    def num_cases(self):
+        return len(self.driver_iteration_lst)
 
     @property
     def driver_cases(self):
diff --git a/topfarm/tests/test_topfarm_utils/test_parellel_runner.py b/topfarm/tests/test_topfarm_utils/test_parellel_runner.py
index f14439207c87c6f3a2d30172515033e8695be7d4..4a44a4c94efa0f9b5108a17a618db9774ffe2911 100644
--- a/topfarm/tests/test_topfarm_utils/test_parellel_runner.py
+++ b/topfarm/tests/test_topfarm_utils/test_parellel_runner.py
@@ -1,12 +1,10 @@
 from openmdao.drivers.doe_driver import DOEDriver
-from openmdao.drivers.doe_generators import FullFactorialGenerator,\
-    ListGenerator, UniformGenerator
+from openmdao.drivers.doe_generators import UniformGenerator
 import pytest
 from topfarm._topfarm import InitialXYZOptimizationProblem
 import numpy as np
 from topfarm.cost_models.dummy import DummyCost
-from topfarm.tests import npt, uta
-from topfarm.constraint_components.constrained_generator import ConstrainedXYZGenerator
+from topfarm.tests import npt
 from topfarm.constraint_components.boundary_component import BoundaryComp
 from topfarm.parallel_runner import ParallelRunner
 
diff --git a/topfarm/tests/test_with_dummy.py b/topfarm/tests/test_with_dummy.py
index d9563e79bc193612c8a717d640540cdbd4cb76b9..103247584beb7845249c9db8dc8a7a9b68978cb0 100644
--- a/topfarm/tests/test_with_dummy.py
+++ b/topfarm/tests/test_with_dummy.py
@@ -6,52 +6,49 @@ import unittest
 import pytest
 import numpy as np
 from topfarm.cost_models.dummy import DummyCost, DummyCostPlotComp
-
-
-class Test(unittest.TestCase):  # unittest version
-
-    def test_optimize_4tb(self):
-        """Optimize 4-turbine layout and check final positions
-
-            The farm boundaries and min spacing are chosen such that the desired
-            turbine positions are not within the boundaries or constraints.
-        """
-
-        # test options
-        dec_prec = 4  # decimal precision for comparison
-
-        # given
-        boundary = [(0, 0), (6, 0), (6, -10), (0, -10)]  # turbine boundaries
-        initial = [[6, 0], [6, -8], [1, 1], [-1, -8]]  # initial turbine layouts
-        desired = [[3, -3], [7, -7], [4, -3], [3, -7]]  # desired turbine layouts
-        optimal = np.array([[2.5, -3], [6, -7], [4.5, -3], [3, -7]])  # optimal turbine layout
-        min_spacing = 2  # min distance between turbines
-
-        # when
-        tf = TopFarm(initial, DummyCost(desired, ['turbineX','turbineY']), min_spacing,
-                     boundary=boundary, record_id=None)
-        tf.optimize()
-        tb_pos = tf.turbine_positions
-
-        # then
-        tol = 1e-6
-        self.assertGreater(sum((tb_pos[2] - tb_pos[0])**2), 2**2 - tol)  # check min spacing
-        self.assertLess(tb_pos[1][0], 6 + tol)  # check within border
-        np.testing.assert_array_almost_equal(tb_pos[:,:2], optimal, dec_prec)
-
-    def testDummyCostPlotComp(self):
-        if os.name == 'posix' and "DISPLAY" not in os.environ:
-            pytest.xfail("No display")
-        
-        desired = [[3, -3], [7, -7], [4, -3], [3, -7]]
-        tf = TopFarm(turbines=[[6, 0], [6, -8], [1, 1], [-1, -8]],
-                     cost_comp=DummyCost(desired, ['turbineX','turbineY']),
-                     min_spacing=2,
-                     boundary=[(0, 0), (6, 0), (6, -10), (0, -10)],
-                     plot_comp = DummyCostPlotComp(desired),
-                     record_id=None)
-        tf.evaluate()
-
-
-if __name__ == "__main__":
-    unittest.main()
+from topfarm.tests import uta
+
+
+def test_optimize_4tb():
+    """Optimize 4-turbine layout and check final positions
+
+        The farm boundaries and min spacing are chosen such that the desired
+        turbine positions are not within the boundaries or constraints.
+    """
+
+    # test options
+    dec_prec = 4  # decimal precision for comparison
+
+    # given
+    boundary = [(0, 0), (6, 0), (6, -10), (0, -10)]  # turbine boundaries
+    initial = [[6, 0], [6, -8], [1, 1], [-1, -8]]  # initial turbine layouts
+    desired = [[3, -3], [7, -7], [4, -3], [3, -7]]  # desired turbine layouts
+    optimal = np.array([[2.5, -3], [6, -7], [4.5, -3], [3, -7]])  # optimal turbine layout
+    min_spacing = 2  # min distance between turbines
+
+    # when
+    tf = TopFarm(initial, DummyCost(desired, ['turbineX', 'turbineY']), min_spacing,
+                 boundary=boundary, record_id=None)
+    tf.optimize()
+    tb_pos = tf.turbine_positions
+
+    # then
+    tol = 1e-6
+    uta.assertGreater(sum((tb_pos[2] - tb_pos[0])**2), 2**2 - tol)  # check min spacing
+    uta.assertLess(tb_pos[1][0], 6 + tol)  # check within border
+    np.testing.assert_array_almost_equal(tb_pos[:, :2], optimal, dec_prec)
+
+
+def testDummyCostPlotComp():
+    if os.name == 'posix' and "DISPLAY" not in os.environ:
+        pytest.xfail("No display")
+
+    desired = [[3, -3], [7, -7], [4, -3], [3, -7]]
+    tf = TopFarm(turbines=[[6, 0], [6, -8], [1, 1], [-1, -8]],
+                 cost_comp=DummyCost(desired, ['turbineX', 'turbineY']),
+                 min_spacing=2,
+                 boundary=[(0, 0), (6, 0), (6, -10), (0, -10)],
+                 plot_comp=DummyCostPlotComp(desired),
+                 record_id=None)
+    tf.evaluate()
+    tf.optimize()