diff --git a/docker/Dockerfile b/docker/Dockerfile index 70c74e9ca7523fe39cc8441e0f8f3be0639d0cca..bb1d17ace41f02ef11c936c97bc737331da8d376 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -56,6 +56,14 @@ ENV LD_LIBRARY_PATH $LD_LIBRARY_PATH:$IPOPT_DIR/lib ## Install PyOptSparse #COPY docker/install_pyoptsparse.sh /install WORKDIR $POSDIR + + +# Replace python 3 incomplatible files from pyoptsparse with files from pyipopt +RUN git clone https://github.com/xuy/pyipopt.git && \ + mv ./pyipopt/src/pyipoptcoremodule.c ./pyoptsparse/pyIPOPT/src/pyipoptcoremodule.c && \ + mv ./pyipopt/src/hook.h ./pyoptsparse/pyIPOPT/src/hook.h && \ + mv ./pyipopt/src/callback.c ./pyoptsparse/pyIPOPT/src/callback.c + RUN python setup.py install #RUN mkdir /notebooks @@ -67,34 +75,36 @@ RUN python setup.py install # Add Tini. Tini operates as a process subreaper for jupyter. This prevents # kernel crashes. -#ENV TINI_VERSION v0.6.0 -#ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini -#RUN chmod +x /usr/bin/tini +ENV TINI_VERSION v0.6.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini +RUN chmod +x /usr/bin/tini # Install the Colonel -RUN mkdir /deb -WORKDIR /deb -COPY docker/*.deb /deb/ -RUN dpkg -i *.deb +#RUN mkdir /deb +#WORKDIR /deb +#COPY docker/*.deb /deb/ +#RUN dpkg -i *.deb RUN apt-get clean \ && apt-get autoremove -y +RUN apt-get install lazarus -y + #RUN mkdir /install -RUN mkdir /install/source -RUN mkdir /install/FugaLib -WORKDIR /install -COPY topfarm/cost_models/fuga/Colonel/source/*.pas /install/source/ -COPY topfarm/cost_models/fuga/Colonel/FugaLib/FugaLib.lpr /install/FugaLib/ -COPY topfarm/cost_models/fuga/Colonel/FugaLib/FugaLib.lpi /install/FugaLib +#RUN mkdir /install/source +#RUN mkdir /install/FugaLib +#WORKDIR /install +#COPY topfarm/cost_models/fuga/Colonel/source/*.pas /install/source/ +#COPY topfarm/cost_models/fuga/Colonel/FugaLib/FugaLib.lpr /install/FugaLib/ +#COPY topfarm/cost_models/fuga/Colonel/FugaLib/FugaLib.lpi /install/FugaLib ## Build -RUN lazbuild /install/FugaLib/FugaLib.lpr +RUN lazbuild /install/FugaLib/FugaLib.lpi -#RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - -#RUN apt-get update -y && apt-get install -y nodejs +RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - +RUN apt-get update -y && apt-get install -y nodejs #RUN jupyter labextension install @jupyter-widgets/jupyterlab-manager \ diff --git a/docker/fpc-src_3.0.4-2_amd64.deb b/docker/fpc-src_3.0.4-2_amd64.deb deleted file mode 100644 index 0c121d0fa64ace642134c8559cce2c341eb9e6a8..0000000000000000000000000000000000000000 Binary files a/docker/fpc-src_3.0.4-2_amd64.deb and /dev/null differ diff --git a/docker/fpc_3.0.4-2_amd64.deb b/docker/fpc_3.0.4-2_amd64.deb deleted file mode 100644 index 986469225241e34ee0228b4d60c5d6eef6cbe4c1..0000000000000000000000000000000000000000 Binary files a/docker/fpc_3.0.4-2_amd64.deb and /dev/null differ diff --git a/tests/test_fuga/test_pyfuga.py b/tests/test_fuga/test_pyfuga.py index 3fea940944364ce149b672cfc7b5005ea7395090..2405260e457610c37b8e1e4ea5f2befc32bb33af 100644 --- a/tests/test_fuga/test_pyfuga.py +++ b/tests/test_fuga/test_pyfuga.py @@ -23,10 +23,10 @@ def get_fuga(): check_lib_exists() pyFuga = PyFuga() pyFuga.setup(farm_name='Horns Rev 1', - turbine_model_path=fuga_path + 'LUT/', turbine_model_name='Vestas_V80_(2_MW_offshore)[h=67.00]', + turbine_model_path=fuga_path + 'LUTs-T/', turbine_model_name='Vestas_V80_(2_MW_offshore)[h=70.00]', tb_x=tb_x, tb_y=tb_y, - mast_position=(0, 0, 70), z0=0.0001, zi=400, zeta0=0, - farms_dir=fuga_path + 'LUT/Farms/', wind_atlas_path='Horns Rev 1/hornsrev_north_only.lib', climate_interpolation=False) + mast_position=(0, 0, 70), z0=0.03, zi=400, zeta0=0, + farms_dir=fuga_path + 'LUTs-T/Farms/', wind_atlas_path='MyFarm/north_pm30_only.lib', climate_interpolation=False) return pyFuga return _fuga @@ -67,37 +67,39 @@ def testSetup(get_fuga): def testAEP_one_tb(get_fuga): pyFuga = get_fuga([0], [0]) - np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0], [0]]).T), [7.450272, 7.450272, 0.424962, 1.]) + np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0], [0]]).T), [8.2896689155874324, 8.2896689155874324, 0.472841, 1.]) pyFuga.cleanup() def testAEP(pyFuga): - np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0, 200], [0, 0]]).T), [14.866138, 14.900544, 0.423981, 0.997691]) - np.testing.assert_array_almost_equal(pyFuga.get_aep_gradients(np.array([[0, 200], [0, 0]]).T), 0) - np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0, 0], [0, 200]]).T), [12.124883, 14.900544, 0.3458, 0.813721]) - np.testing.assert_array_almost_equal(pyFuga.get_aep_gradients(np.array([[0, 0], [0, 200]]).T), [[-0.001794, 0.001794], - [-0.008126, 0.008126], - [0., 0.]]) - np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0, 200], [0, 200]]).T), [14.864909, 14.900544, 0.423946, 0.997608]) - np.testing.assert_array_almost_equal(pyFuga.get_aep_gradients(np.array([[0, 200], [0, 200]]).T), [[-5.165553e-06, 5.165553e-06], - [1.599768e-06, -1.599768e-06], - [0.000000e+00, 0.000000e+00]]) + np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0, 1000], [0, 0]]).T), [ + 2 * 8.2896689155874324, 2 * 8.2896689155874324, 0.472841, 1.]) + np.testing.assert_array_almost_equal(pyFuga.get_aep_gradients(np.array([[0, 1000], [0, 0]]).T), 0) + np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0, 0], [0, 200]]).T), [14.688347, 16.579338, 0.41891, 0.885943]) + np.testing.assert_array_almost_equal(pyFuga.get_aep_gradients(np.array([[0, 0], [0, 200]]).T), [[-0.003789, 0.003789], + [-0.007204, 0.007204], + [0., 0.]]) + np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0, 200], [0, 200]]).T), [20.352901, 16.579338, 0.580462, 1.227606]) + np.testing.assert_array_almost_equal(pyFuga.get_aep_gradients(np.array([[0, 200], [0, 200]]).T), [[-2.033273e-05, 2.033273e-05], + [7.255895e-06, -7.255895e-06], + [0.000000e+00, 0.000000e+00]]) pyFuga.cleanup() def testLargeOffset(pyFuga): o = 1.e16 - np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0 + o, 0 + o], [0 + o, 200 + o]]).T), [12.124883, 14.900544, 0.3458, 0.813721]) - np.testing.assert_array_almost_equal(pyFuga.get_aep_gradients(), [[-0.001794, 0.001794], - [-0.008126, 0.008126], - [0., 0.]]) + np.testing.assert_array_almost_equal(pyFuga.get_aep(np.array([[0 + o, 0 + o], [0 + o, 200 + o]]).T), [14.688347, 16.579338, 0.41891, 0.885943]) + np.testing.assert_array_almost_equal(pyFuga.get_aep_gradients(), [[-0.003789, 0.003789], + [-0.007204, 0.007204], + [0., 0.]]) + def testAEP_topfarm(get_fuga): pyFuga = get_fuga() - init_pos = [[0, 0], [200, 0]] + init_pos = [[0, 0], [1000, 0]] tf = TopFarm(init_pos, pyFuga.get_TopFarm_cost_component(), 160, init_pos, boundary_type='square') tf.evaluate() - np.testing.assert_array_almost_equal(tf.get_cost(), -14.866138) + np.testing.assert_array_almost_equal(tf.get_cost(), -16.579337831174865) def test_pyfuga_cmd(): diff --git a/tests/topfarm/__init__.py b/tests/topfarm/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/topfarm/test_drivers.py b/tests/topfarm/test_drivers.py new file mode 100644 index 0000000000000000000000000000000000000000..39faec7ad32a72137e6f1db21140b9b5dd447643 --- /dev/null +++ b/tests/topfarm/test_drivers.py @@ -0,0 +1,43 @@ +from topfarm import TopFarm +import numpy as np +import pytest +from topfarm.cost_models.dummy import DummyCost +from topfarm.plotting import NoPlot +from topfarm.easy_drivers import EasyScipyOptimizeDriver, EasyPyOptSparseIPOPT + + +initial = [[6, 0], [6, -8], [1, 1]] # initial turbine layouts +optimal = np.array([[2.5, -3], [6, -7], [4.5, -3]]) # desired turbine layouts +boundary = [(0, 0), (6, 0), (6, -10), (0, -10)] # turbine boundaries +desired = [[3, -3], [7, -7], [4, -3]] # desired turbine layouts + + +@pytest.fixture +def topfarm_generator(): + def _topfarm_obj(driver): + # from topfarm.cost_models.dummy import DummyCostPlotComp + # plot_comp = DummyCostPlotComp(desired) + plot_comp = NoPlot() + return TopFarm(initial, DummyCost(desired), 2, plot_comp=plot_comp, boundary=boundary, driver=driver) + return _topfarm_obj + + +@pytest.mark.parametrize('driver,tol', [(EasyScipyOptimizeDriver(), 1e-4), + (EasyScipyOptimizeDriver(tol=1e-3), 1e-2), + (EasyScipyOptimizeDriver(maxiter=13), 1e-1), + (EasyScipyOptimizeDriver(optimizer='COBYLA', tol=1e-3, disp=False), 1e-2), + (EasyPyOptSparseIPOPT(), 1e-4), + ][:]) +def test_optimizers(driver, tol, topfarm_generator): + if driver.__class__.__name__ == "PyOptSparseMissingDriver": + pytest.xfail("reason") + tf = topfarm_generator(driver) + tf.evaluate() + print(driver.__class__.__name__) + tf.optimize() + tb_pos = tf.turbine_positions + # tf.plot_comp.show() + + assert sum((tb_pos[2] - tb_pos[0])**2) > 2**2 - tol # check min spacing + assert tb_pos[1][0] < 6 + tol # check within border + np.testing.assert_array_almost_equal(tb_pos, optimal, -int(np.log10(tol))) diff --git a/tests/test_topfarm.py b/tests/topfarm/test_topfarm.py similarity index 97% rename from tests/test_topfarm.py rename to tests/topfarm/test_topfarm.py index a8758a4bb8439799c10af1f5e5892a69fb256825..dd8d02ab137d81ea00ecf5687ecfd98d8b932b93 100644 --- a/tests/test_topfarm.py +++ b/tests/topfarm/test_topfarm.py @@ -1,8 +1,3 @@ -''' -Created on 17. maj 2018 - -@author: mmpe -''' from topfarm import TopFarm import numpy as np diff --git a/topfarm/_topfarm.py b/topfarm/_topfarm.py index 572c9efaa98d9012bbac72775a39658e9757dd61..1e0e75d8450f5188751db4628a61ec1c956fdefe 100644 --- a/topfarm/_topfarm.py +++ b/topfarm/_topfarm.py @@ -19,7 +19,7 @@ class TopFarm(object): """ def __init__(self, turbines, cost_comp, min_spacing, boundary, boundary_type='convex_hull', plot_comp=None, - driver_options={'optimizer': 'SLSQP'}): + driver=ScipyOptimizeDriver()): self.initial_positions = turbines = np.array(turbines) @@ -32,19 +32,18 @@ class TopFarm(object): 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) - if driver_options['optimizer'] == 'SLSQP': + design_var_kwargs = {} + + if 'optimizer' in driver.options and driver.options['optimizer'] == 'SLSQP': min_x, min_y, mean_x, mean_y = 0, 0, 1, 1 # scaling disturbs SLSQP + # Default +/- sys.float_info.max does not work for SLSQP + design_var_kwargs = {'lower': np.nan, 'upper': np.nan} indeps.add_output('turbineX', turbines[:, 0], units='m', ref0=min_x, ref=mean_x) indeps.add_output('turbineY', turbines[:, 1], units='m', ref0=min_y, ref=mean_y) indeps.add_output('boundary', self.boundary_comp.vertices, units='m') prob.model.add_subsystem('cost_comp', cost_comp, promotes=['*']) - prob.driver = ScipyOptimizeDriver() + prob.driver = driver - prob.driver.options.update(driver_options) - design_var_kwargs = {} - if driver_options['optimizer'] == 'SLSQP': - # Default +/- sys.float_info.max does not work for SLSQP - design_var_kwargs = {'lower': np.nan, 'upper': np.nan} prob.model.add_design_var('turbineX', **design_var_kwargs) prob.model.add_design_var('turbineY', **design_var_kwargs) prob.model.add_objective('cost') diff --git a/topfarm/cost_models/dummy.py b/topfarm/cost_models/dummy.py index 777b77f80e09eddd43d4f210541731aead66c4df..77a99f00f423eaccd360346288a56c8013c1dc94 100644 --- a/topfarm/cost_models/dummy.py +++ b/topfarm/cost_models/dummy.py @@ -19,9 +19,6 @@ class DummyCost(ExplicitComponent): self.optimal = np.array(optimal_positions) self.N = self.optimal.shape[0] - def cost(self, x, y): - """Evaluate cost function""" - def setup(self): self.add_input('turbineX', val=np.zeros(self.N), units='m') self.add_input('turbineY', val=np.zeros(self.N), units='m') diff --git a/topfarm/cost_models/fuga/Colonel b/topfarm/cost_models/fuga/Colonel index 01e2b99aa8f7f40d7a020710f9a0374bcfa7f26c..4910e86f1297f55d40f57a62be8f0f287b6e55e7 160000 --- a/topfarm/cost_models/fuga/Colonel +++ b/topfarm/cost_models/fuga/Colonel @@ -1 +1 @@ -Subproject commit 01e2b99aa8f7f40d7a020710f9a0374bcfa7f26c +Subproject commit 4910e86f1297f55d40f57a62be8f0f287b6e55e7 diff --git a/topfarm/cost_models/fuga/py_fuga.py b/topfarm/cost_models/fuga/py_fuga.py index a82e5036fea6fe8179bcfd5942c2b8c803363202..450307c1a61a2beb1ee70674f384de5e339640a0 100644 --- a/topfarm/cost_models/fuga/py_fuga.py +++ b/topfarm/cost_models/fuga/py_fuga.py @@ -130,10 +130,10 @@ def try_me(): if __name__ == '__main__': pyFuga = PyFuga() pyFuga.setup(farm_name='Horns Rev 1', - turbine_model_path=fuga_path + 'LUT/', turbine_model_name='Vestas_V80_(2_MW_offshore)[h=67.00]', + turbine_model_path=fuga_path + 'LUTs-T/', turbine_model_name='Vestas_V80_(2_MW_offshore)[h=70.00]', tb_x=[423974, 424033], tb_y=[6151447, 6150889], - mast_position=(0, 0, 70), z0=0.0001, zi=400, zeta0=0, - farms_dir=fuga_path + 'LUT/Farms/', wind_atlas_path='Horns Rev 1\hornsrev.lib') + mast_position=(0, 0, 70), z0=0.03, zi=400, zeta0=0, + farms_dir=fuga_path + 'LUTs-T/Farms/', wind_atlas_path='MyFarm\DEN05JBgr_7.813E_55.489N_7.4_5.lib') print(pyFuga.get_no_turbines()) print(pyFuga.get_aep(np.array([[0, 0], [0, 1000]]))) diff --git a/topfarm/easy_drivers.py b/topfarm/easy_drivers.py new file mode 100644 index 0000000000000000000000000000000000000000..339d244daa71452f4d602dc3cf628b9f3aba40f7 --- /dev/null +++ b/topfarm/easy_drivers.py @@ -0,0 +1,48 @@ +from openmdao.drivers.scipy_optimizer import ScipyOptimizeDriver + + +class EasyScipyOptimizeDriver(ScipyOptimizeDriver): + + def __init__(self, optimizer='SLSQP', maxiter=200, tol=1e-6, disp=True): + """ + Parameters + ---------- + optimizer : {'COBYLA', 'SLSQP'} + Gradients are only supported by SLSQP + maxiter : int + Maximum number of iterations. + tol : float + Tolerance for termination. For detailed control, use solver-specific options. + disp : bool + Set to False to prevent printing of Scipy convergence messages + """ + ScipyOptimizeDriver.__init__(self) + self.options.update({'optimizer': optimizer, 'maxiter': maxiter, 'tol': tol, 'disp': disp}) + + +try: + from openmdao.drivers.pyoptsparse_driver import pyOptSparseDriver + +# Not working: +# capi_return is NULL +# Call-back cb_slfunc_in_slsqp__user__routines failed. +# +# class EasyPyOptSparseSLSQP(pyOptSparseDriver): +# def __init__(self, maxit=200, acc=1e-6): +# pyOptSparseDriver.__init__(self) +# self.options.update({'optimizer': 'SLSQP'}) +# self.opt_settings.update({'MAXIT': maxit, 'ACC': acc}) + + class EasyPyOptSparseIPOPT(pyOptSparseDriver): + def __init__(self, max_iter=200): + pyOptSparseDriver.__init__(self) + self.options.update({'optimizer': 'IPOPT'}) + self.opt_settings.update({'linear_solver': 'ma27', 'max_iter': max_iter}) + + +except ModuleNotFoundError: + class PyOptSparseMissingDriver(object): + options = {} + + EasyPyOptSparseSLSQP = PyOptSparseMissingDriver + EasyPyOptSparseIPOPT = PyOptSparseMissingDriver