From 153c273fe781e7f245fe8dc26fa75e2491f5b6f3 Mon Sep 17 00:00:00 2001
From: mikf <mikf@dtu.dk>
Date: Tue, 26 Jun 2018 10:16:42 +0200
Subject: [PATCH] renamed recording to utils new random initial positions
 feature that respects the boundaries and tries to respect the min spacing

animations now possible

added test-file to optimization recording testing and tests to the utility module

misc. updates. try make utils tests pass

animations now possible

added test-file to optimization recording testing and tests to the utility module

more tests
---
 topfarm/_topfarm.py                           |  49 ++++++------
 .../spacing_component.py                      |   2 +-
 topfarm/plotting.py                           |  35 +++++++--
 .../recordings/cases_20180621_111710.sql      | Bin 0 -> 65536 bytes
 topfarm/tests/topfarm/test_utils.py           |  70 ++++++++++++++++++
 topfarm/utils.py                              |  33 +++++----
 6 files changed, 143 insertions(+), 46 deletions(-)
 create mode 100644 topfarm/tests/test_files/recordings/cases_20180621_111710.sql
 create mode 100644 topfarm/tests/topfarm/test_utils.py

diff --git a/topfarm/_topfarm.py b/topfarm/_topfarm.py
index c210f2fb..e0ba0d9a 100644
--- a/topfarm/_topfarm.py
+++ b/topfarm/_topfarm.py
@@ -1,3 +1,8 @@
+from topfarm.constraint_components.boundary_component import BoundaryComp,\
+    PolygonBoundaryComp
+from topfarm.constraint_components.spacing_component import SpacingComp
+from topfarm.plotting import PlotComp
+from topfarm.utils import pos_from_case, latest_id
 import os
 import time
 import numpy as np
@@ -5,41 +10,37 @@ import warnings
 with warnings.catch_warnings():
     warnings.simplefilter('ignore', FutureWarning)
     from openmdao.api import Problem, ScipyOptimizeDriver, IndepVarComp, \
-    SqliteRecorder
-from topfarm.constraint_components.boundary_component import BoundaryComp,\
-    PolygonBoundaryComp
-from topfarm.constraint_components.spacing_component import SpacingComp
-from topfarm.plotting import PlotComp
-from topfarm.utils import pos_from_case, latest_id
-
+        SqliteRecorder
 
 class TopFarm(object):
-    """Optimize wind farm layout in terms of 
+    """Optimize wind farm layout in terms of
     - Position of turbines
     [- Type of turbines: Not implemented yet]
     [- Height of turbines: Not implemented yet]
     [- Number of turbines: Not implemented yet]
     """
 
-    def __init__(self, turbines, cost_comp, min_spacing, boundary, boundary_type='convex_hull', plot_comp=None,
-                 driver=ScipyOptimizeDriver(), record = False, case_recorder_dir = os.getcwd(),
-                 rerun_case_id = None):
+    def __init__(self, turbines, cost_comp, min_spacing, boundary,
+                 boundary_type='convex_hull', plot_comp=None,
+                 driver=ScipyOptimizeDriver(), record=False,
+                 case_recorder_dir=os.getcwd(), rerun_case_id=None):
         if rerun_case_id is None:
             self.initial_positions = turbines = np.array(turbines)
         elif rerun_case_id is 'latest':
             rerun_case_id = latest_id(case_recorder_dir)
-            self.initial_positions = turbines = pos_from_case(rerun_case_id) 
+            self.initial_positions = turbines = pos_from_case(rerun_case_id)
             print('*Initial positions loaded from file: {}\n'.format(
                     rerun_case_id))
         else:
-            self.initial_positions = turbines = pos_from_case(rerun_case_id) 
+            self.initial_positions = turbines = pos_from_case(rerun_case_id)
         n_wt = turbines.shape[0]
         if boundary_type == 'polygon':
             self.boundary_comp = PolygonBoundaryComp(boundary, n_wt)
         else:
             self.boundary_comp = BoundaryComp(boundary, n_wt, boundary_type)
         self.problem = prob = Problem()
-        indeps = prob.model.add_subsystem('indeps', IndepVarComp(), promotes=['*'])
+        indeps = prob.model.add_subsystem('indeps', IndepVarComp(),
+                                          promotes=['*'])
         min_x, min_y = self.boundary_comp.vertices.min(0)
         mean_x, mean_y = self.boundary_comp.vertices.mean(0)
         design_var_kwargs = {}
@@ -69,8 +70,10 @@ class TopFarm(object):
         prob.model.add_design_var('turbineY', **design_var_kwargs)
         prob.model.add_objective('cost')
 
-        prob.model.add_subsystem('spacing_comp', SpacingComp(nTurbines=n_wt), promotes=['*'])
-        prob.model.add_subsystem('bound_comp', self.boundary_comp, promotes=['*'])
+        prob.model.add_subsystem('spacing_comp', SpacingComp(nTurbines=n_wt),
+                                 promotes=['*'])
+        prob.model.add_subsystem('bound_comp', self.boundary_comp,
+                                 promotes=['*'])
         if plot_comp == "default":
             plot_comp = PlotComp()
         if plot_comp:
@@ -85,8 +88,6 @@ class TopFarm(object):
         prob.setup(check=True, mode='fwd')
 
 
-        
-        
     def check(self, all=False, tol=1e-3):
         """Check gradient computations"""
         comp_name_lst = [comp.pathname for comp in self.problem.model.system_iter()
@@ -133,6 +134,14 @@ class TopFarm(object):
         return np.array([self.problem['turbineX'], self.problem['turbineY']]).T
 
 
+    def post_process(self, anim_time=10, verbose=True):
+        if self.plot_comp.animate:
+           self.plot_comp.run_animate(anim_time, verbose)
+        for file in os.listdir(self.plot_comp.temp):
+            if file.startswith('plot_') and file.endswith('.png'):
+                os.remove(os.path.join(self.plot_comp.temp,file))
+
+
 def try_me():
     if __name__ == '__main__':
         from topfarm.cost_models.dummy import DummyCostPlotComp, DummyCost
@@ -145,12 +154,10 @@ def try_me():
 
         turbines = np.array(optimal) + np.random.randint(-random_offset, random_offset, (n_wt, 2))
         plot_comp = DummyCostPlotComp(optimal)
+        plot_comp.animate = True
 
         boundary = [(0, 0), (6, 0), (6, -10), (0, -10)]
         tf = TopFarm(turbines, DummyCost(optimal), minSpacing * rotorDiameter, boundary=boundary, plot_comp=plot_comp)
         # tf.check()
         tf.optimize()
-        # plot_comp.show()
-
-
 try_me()
diff --git a/topfarm/constraint_components/spacing_component.py b/topfarm/constraint_components/spacing_component.py
index 7d056981..96c96452 100644
--- a/topfarm/constraint_components/spacing_component.py
+++ b/topfarm/constraint_components/spacing_component.py
@@ -85,4 +85,4 @@ class SpacingComp(ExplicitComponent):
                 dSdy[k, i] = -2 * (turbineY[j] - turbineY[i])
                 # increment turbine pair counter
                 k += 1
-        return dSdx, dSdy
+        return dSdx, dSdy
\ No newline at end of file
diff --git a/topfarm/plotting.py b/topfarm/plotting.py
index 5715eca6..10944afe 100644
--- a/topfarm/plotting.py
+++ b/topfarm/plotting.py
@@ -1,8 +1,6 @@
-import time
-
 import matplotlib
 from openmdao.core.explicitcomponent import ExplicitComponent
-
+import os
 import matplotlib.pyplot as plt
 import numpy as np
 
@@ -23,13 +21,15 @@ 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):
+    def __init__(self, memory=10, delay=0.001, plot_initial=True,
+                 animate=False):
         ExplicitComponent.__init__(self)
         self.memory = memory
         self.delay = delay
         self.plot_initial = plot_initial
         self.history = []
         self.counter = 0
+        self.animate = animate
 
     def show(self):
         plt.show()
@@ -59,20 +59,29 @@ class PlotComp(ExplicitComponent):
         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("%f (%.2f%%)"%(cost, (self.initial[1]-cost)/self.initial[1]*100))
+        plt.title("%f (%.2f%%)" % (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([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)
@@ -80,6 +89,16 @@ class PlotComp(ExplicitComponent):
 
         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)
+
 
 class NoPlot(PlotComp):
     def __init__(self, *args, **kwargs):
diff --git a/topfarm/tests/test_files/recordings/cases_20180621_111710.sql b/topfarm/tests/test_files/recordings/cases_20180621_111710.sql
new file mode 100644
index 0000000000000000000000000000000000000000..333ed874528c1456d084669030001f40fc79731a
GIT binary patch
literal 65536
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
EfA^zzHvj+t

literal 0
HcmV?d00001

diff --git a/topfarm/tests/topfarm/test_utils.py b/topfarm/tests/topfarm/test_utils.py
new file mode 100644
index 00000000..c4c8400a
--- /dev/null
+++ b/topfarm/tests/topfarm/test_utils.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+from topfarm import TopFarm
+import numpy as np
+from topfarm.cost_models.cost_model_wrappers import CostModelComponent
+import pytest
+import os
+
+from topfarm.utils import pos_from_case, latest_id, _random_positions
+
+thisdir = os.path.dirname(os.path.abspath(__file__))
+turbines = np.array([[ 2.4999377 , -2.99987763],
+                   [ 6.        , -6.99997496],
+                   [ 4.49993771, -2.99985273],
+                   [ 3.00004123, -6.9999519 ]])
+x = np.array([-0.5463264 ,  0.4158521 ,  1.50479727,  3.04121982,  0.82494571,
+            1.48072571,  0.03939927,  2.27593243, -0.18551361,  0.24885285,
+            1.12706339,  2.25472924,  0.04329133,  0.292686  ,  5.18916103,
+            1.76294032,  6.96910295,  4.80383887,  5.93002915,  6.07458626])
+
+y = np.array([-4.28630451, -1.03701919, -6.11562032,  0.60293213, -6.83330699,
+           -1.9655984 , -7.06706521, -3.56006813,  0.70979837, -2.17497837,
+            0.94819493, -1.94630408, -6.75376048, -6.97213247, -7.11506022,
+           -6.99383667,  0.63581096, -4.57807581, -2.76544057, -8.85507948])
+
+boundary = [(0, 0), (6, 1), (7, -11), (-1, -10)]
+n_wt, n_iter, step_size, min_space, pad, plot, verbose = \
+    (20, 1000, 0.1, 2.1, 1.01, False, False)
+turbines2_ref = np.array([[-0.53056298, -5.34414632],
+       [ 1.72713409, -1.7339491 ],
+       [ 3.90444365, -5.82606831],
+       [ 4.49089193,  0.74539327],
+       [ 1.26552562, -6.75507526],
+       [ 1.45466343, -4.37313716],
+       [-0.99102804, -9.94909137],
+       [ 3.8725124 , -3.68277265],
+       [-0.02498702, -0.28473405],
+       [-0.25686194, -2.60098671],
+       [ 2.30453881,  0.38010616],
+       [ 3.97302933, -1.39592567],
+       [-0.74326151, -7.47172573],
+       [ 1.4151898 , -9.31374061],
+       [ 5.65712132, -7.44164592],
+       [ 3.56099506, -8.48762334],
+       [ 6.12959435, -0.59355499],
+       [ 6.27224495, -5.30888086],
+       [ 6.34616645, -3.15591179],
+       [ 6.42858562, -9.90795045]])
+
+def testpos_from_case():
+    crf = "../test_files/recordings/cases_20180621_111710.sql"
+    path = os.path.join(thisdir, crf)
+    np.testing.assert_allclose(turbines, pos_from_case(path))
+
+
+def testlatest_id():
+    crd = "../test_files/recordings"
+    path = os.path.join(thisdir, crd)
+    ref_path = os.path.join(path,'cases_20180621_111710.sql')
+    assert latest_id(path) == ref_path
+
+def test_random_positions():
+    turbines2 = _random_positions(x, y, boundary, n_wt, n_iter, step_size,
+                                 min_space, pad, plot, verbose)
+    np.testing.assert_allclose(turbines2, turbines2_ref)
+
+if __name__ == '__main__':
+#    testpos_from_case()
+#    testlatest_id()
+#    test_random_positions()
+    pass
\ No newline at end of file
diff --git a/topfarm/utils.py b/topfarm/utils.py
index df16d39d..18894baf 100644
--- a/topfarm/utils.py
+++ b/topfarm/utils.py
@@ -46,16 +46,16 @@ def latest_id(case_recorder_dir):
     return latest
 
 
-def random_positions(boundary, n_wt, n_iter, step_size, min_space, 
-                     pad = 1.1, plot=False, verbose=True):
+def random_positions(boundary, n_wt, n_iter, step_size, min_space,
+                     pad=1.1, plot=False, verbose=True):
     '''
     Input:
         boundary:   list of tuples, e.g.: [(0, 0), (6, 1), (7, -11), (-1, -10)]
         n_wt:       number of wind turbines
         n_iter:     number of iterations allowed to try and satisfy the minimum
                     spacing constraint
-        step_size:  the multiplier on the spacing gradient that the turbines 
-                    are moved in each step 
+        step_size:  the multiplier on the spacing gradient that the turbines
+                    are moved in each step
         min_space:  the minimum spacing between turbines
         pad:        the multiplier on the boundary gradient
         plot:       plot the generated random layout
@@ -113,8 +113,7 @@ def _random(b):
 
 def _contain(n_wt, turbineX, turbineY, boundary_comp, pad):
     for i in range(0, n_wt):
-        dng = boundary_comp.calc_distance_and_gradients(turbineX,
-                                                        turbineY)
+        dng = boundary_comp.calc_distance_and_gradients(turbineX, turbineY)
         dist = dng[0][i]
         if dist < 0:
             dx = dng[1][i]
@@ -125,23 +124,25 @@ def _contain(n_wt, turbineX, turbineY, boundary_comp, pad):
 
 
 if __name__ == '__main__':
-#    this_dir = os.path.dirname(os.path.abspath(__file__))
-#    crf = r"C:\Sandbox\Git\TopFarm2\topfarm\cases_20180621_111710.sql"
-#    case_recorder_filename = crf
-#    turbines = pos_from_case(case_recorder_filename)
-#    print(turbines)
-#
-#    case_recorder_dir = r'C:\Sandbox\Git\TopFarm2\topfarm'
-#    latest_id = latest_id(case_recorder_dir)
-#    print(latest_id)
+    this_dir = os.getcwd()
+    crf = r"tests\test_files\recordings\cases_20180621_111710.sql"
+    case_recorder_filename = crf
+    path = os.path.join(this_dir, crf)
+    turbines = pos_from_case(path)
+    print(turbines)
+
+    case_recorder_dir = r'tests\test_files\recordings'
+    latest_id = latest_id(case_recorder_dir)
+    print(latest_id)
 
     boundary = [(0, 0), (6, 1), (7, -11), (-1, -10)]
     n_wt = 20
     n_iter = 1000
     step_size = 0.1
     min_space = 2.1
+    pad = 1.01
     plot = True
     verbose = True
     turbines = random_positions(boundary, n_wt, n_iter, step_size, min_space,
-                                plot, verbose)
+                                pad, plot, verbose)
     print(turbines)
-- 
GitLab