From 65c402091d85c1ae320cf8ac3b83fa00252070e9 Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Thu, 18 Mar 2021 11:06:25 -0600 Subject: [PATCH 1/5] linear interpolation --- ROSCO_toolbox/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROSCO_toolbox/controller.py b/ROSCO_toolbox/controller.py index b2ec3ae68..0dde6e0fa 100644 --- a/ROSCO_toolbox/controller.py +++ b/ROSCO_toolbox/controller.py @@ -479,7 +479,7 @@ def peak_shaving(self,controller, turbine): else: Ct_max[i] = np.minimum( np.max(Ct_tsr), Ct_max[i]) # Define minimum pitch angle - f_pitch_min = interpolate.interp1d(Ct_tsr, turbine.pitch_initial_rad, kind='cubic', bounds_error=False, fill_value=(turbine.pitch_initial_rad[0],turbine.pitch_initial_rad[-1])) + f_pitch_min = interpolate.interp1d(Ct_tsr, turbine.pitch_initial_rad, kind='linear', bounds_error=False, fill_value=(turbine.pitch_initial_rad[0],turbine.pitch_initial_rad[-1])) pitch_min[i] = max(controller.min_pitch, f_pitch_min(Ct_max[i])) controller.ps_min_bld_pitch = pitch_min From 9e89ef88df3c5049ebfda553adc2bf8308695c5d Mon Sep 17 00:00:00 2001 From: Nikhar Abbas Date: Thu, 18 Mar 2021 11:07:04 -0600 Subject: [PATCH 2/5] Make interpolation function monotonic --- ROSCO_toolbox/controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ROSCO_toolbox/controller.py b/ROSCO_toolbox/controller.py index 0dde6e0fa..790290437 100644 --- a/ROSCO_toolbox/controller.py +++ b/ROSCO_toolbox/controller.py @@ -189,7 +189,8 @@ def tune_controller(self, turbine): # Find pitch angle as a function of expected operating CP for each TSR Cp_TSR = np.ndarray.flatten(turbine.Cp.interp_surface(turbine.pitch_initial_rad, TSR_op[i])) # all Cp values for a given tsr Cp_op[i] = np.clip(Cp_op[i], np.min(Cp_TSR), np.max(Cp_TSR)) # saturate Cp values to be on Cp surface - f_cp_pitch = interpolate.interp1d(Cp_TSR,pitch_initial_rad) # interpolate function for Cp(tsr) values + Cp_maxidx = Cp_TSR.argmax() # Find maximum Cp value for this TSR + f_cp_pitch = interpolate.interp1d(Cp_TSR[Cp_maxidx:],pitch_initial_rad[Cp_maxidx:]) # interpolate function for Cp(tsr) values # expected operation blade pitch values if v[i] <= turbine.v_rated and isinstance(self.min_pitch, float): # Below rated & defined min_pitch pitch_op[i] = min(self.min_pitch, f_cp_pitch(Cp_op[i])) From 3750beb19f7d843a2b9aa159e038eaec9b5ea710 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 18 Mar 2021 14:27:03 -0600 Subject: [PATCH 3/5] Remove double instance of rated wind speed in controller.v --- ROSCO_toolbox/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROSCO_toolbox/controller.py b/ROSCO_toolbox/controller.py index 790290437..5fb05aec4 100644 --- a/ROSCO_toolbox/controller.py +++ b/ROSCO_toolbox/controller.py @@ -158,7 +158,7 @@ def tune_controller(self, turbine): TSR_rated = rated_rotor_speed*R/turbine.v_rated # TSR at rated # separate wind speeds by operation regions - v_below_rated = np.linspace(turbine.v_min,turbine.v_rated, num=30) # below rated + v_below_rated = np.linspace(turbine.v_min,turbine.v_rated, num=30)[:-1] # below rated v_above_rated = np.linspace(turbine.v_rated,turbine.v_max, num=30) # above rated v = np.concatenate((v_below_rated, v_above_rated)) From 67d5d36a5fdf3bd2993383c46c411910f2bedac0 Mon Sep 17 00:00:00 2001 From: dzalkind Date: Thu, 18 Mar 2021 14:36:58 -0600 Subject: [PATCH 4/5] Write example_07 (min pitch) output to new figure --- Examples/example_07.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Examples/example_07.py b/Examples/example_07.py index 786c76095..fe8b71d57 100644 --- a/Examples/example_07.py +++ b/Examples/example_07.py @@ -45,11 +45,12 @@ controller.tune_controller(turbine) # Plot minimum pitch schedule -plt.plot(controller.v, controller.pitch_op,label='Steady State Operation') -plt.plot(controller.v, controller.ps_min_bld_pitch, label='Minimum Pitch Schedule') -plt.legend() -plt.xlabel('Wind speed (m/s)') -plt.ylabel('Blade pitch (rad)') +fig, ax = plt.subplots(1,1) +ax.plot(controller.v, controller.pitch_op,label='Steady State Operation') +ax.plot(controller.v, controller.ps_min_bld_pitch, label='Minimum Pitch Schedule') +ax.legend() +ax.set_xlabel('Wind speed (m/s)') +ax.set_ylabel('Blade pitch (rad)') if False: plt.show() From 381ae2862ee58d9684ef5c69270eef4956ce1813 Mon Sep 17 00:00:00 2001 From: nikhar-abbas <40865984+nikhar-abbas@users.noreply.github.com> Date: Wed, 31 Mar 2021 10:53:34 -0600 Subject: [PATCH 5/5] CI Testing (#41) * CI initial commit * rename to .yml * Add environment yaml * compile rosco * checkout submodules properly * Separate environment setup * syntax fix * run examples - initial commit * typo fix * Add pandas * Install openfast * temp-remove try for execute script * Add run_examples * name environment * remove run_examples * activate environment * pwsh shell * use bash, reorder wisdem install * No shell * typo * Another typo * remove names for setup * remove environment name * only build testing right now * add back shell * use powershell * default shell * Add run_examples back * Add try statement * assume linux for ROSCO shared lib * Add macOS and windows to build testing * install compilers on windows * change to subprocess call * Check for failure * change exit code test * fix file paths * cleanup * Add pull request template --- .github/pull_request_template.md | 19 ++++ .github/workflows/CI_rosco-toolbox.yml | 106 ++++++++++++++++++ Examples/example_09.py | 4 +- Examples/run_examples.py | 4 +- ROSCO_toolbox/utilities.py | 23 ++-- Test_Cases/BAR_10/BAR_10_ServoDyn.dat | 2 +- .../IEA-15-240-RWT-UMaineSemi_ServoDyn.dat | 2 +- .../NRELOffshrBsline5MW_Onshore_ServoDyn.dat | 2 +- environment.yml | 11 ++ 9 files changed, 153 insertions(+), 20 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/CI_rosco-toolbox.yml create mode 100644 environment.yml diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..c57c562d1 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +## Description and Purpose + +## Type of change +What types of change is it? +_Select the appropriate type(s) that describe this PR_ + +- [ ] Bugfix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (non-backwards-compatible fix or feature) +- [ ] Code style update (formatting, renaming) +- [ ] Refactoring (no functional changes, no API changes) +- [ ] Documentation update +- [ ] Maintenance update +- [ ] Other (please describe) + +## Github issues addressed, if one exists + +## Examples/Testing, if applicable + diff --git a/.github/workflows/CI_rosco-toolbox.yml b/.github/workflows/CI_rosco-toolbox.yml new file mode 100644 index 000000000..1148b990f --- /dev/null +++ b/.github/workflows/CI_rosco-toolbox.yml @@ -0,0 +1,106 @@ +name: CI_rosco-toolbox + +# We run CI on push commits on all branches +on: [push, pull_request] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + build: + name: Build (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: ["ubuntu-latest", "macOS-latest", "windows-latest"] + python-version: ["3.8"] + defaults: + run: + shell: bash -l {0} + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Setup environment + uses: conda-incubator/setup-miniconda@v2 + with: + miniconda-version: "latest" + channels: conda-forge, general + auto-update-conda: true + python-version: 3.8 + environment-file: environment.yml + + + # Install dependencies of ROSCO toolbox + - name: Add dependencies ubuntu specific + if: false == contains( matrix.os, 'windows') + run: | + conda install -y compilers + conda install -y wisdem + - name: Add dependencies windows specific + if: true == contains( matrix.os, 'windows') + run: | + conda install -y m2w64-toolchain libpython + conda install -y wisdem + + + # Install ROSCO toolbox + - name: Install ROSCO toolbox + run: | + python setup.py develop --compile-rosco + + + run_examples: + name: Run Examples + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: ["ubuntu-latest"] #, "macOS-latest"] + python-version: ["3.8"] + defaults: + run: + shell: bash -l {0} + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Setup environment + uses: conda-incubator/setup-miniconda@v2 + with: + miniconda-version: "latest" + channels: conda-forge, general + auto-update-conda: true + python-version: 3.8 + environment-file: environment.yml + + + # Install dependencies of ROSCO toolbox + - name: Add dependencies ubuntu specific + if: false == contains( matrix.os, 'windows') + run: | + conda install -y compilers + conda install -y wisdem + + + # Install ROSCO toolbox + - name: Install ROSCO toolbox + run: | + python setup.py develop --compile-rosco + + # Install OpenFAST + - name: Install OpenFAST + run: | + conda install openfast + + # Run examples + - name: Run examples + run: | + cd Examples + python run_examples.py + diff --git a/Examples/example_09.py b/Examples/example_09.py index 2e909ebcc..3bc58a8ff 100644 --- a/Examples/example_09.py +++ b/Examples/example_09.py @@ -20,5 +20,7 @@ wind_directory = os.path.join(this_dir,'../Test_Cases/Wind/') turbsim_infile = '90m_12mps_twr.inp' -run_openfast(wind_directory, fastcall='turbsim', fastfile=turbsim_infile, chdir=False) +run_openfast(wind_directory, fastcall='turbsim', + fastfile=os.path.join(wind_directory, turbsim_infile), chdir=False) +print('test') diff --git a/Examples/run_examples.py b/Examples/run_examples.py index ede4b9593..7e5615682 100644 --- a/Examples/run_examples.py +++ b/Examples/run_examples.py @@ -1,5 +1,6 @@ import os import unittest +import sys from time import time import importlib @@ -100,4 +101,5 @@ def suite(): if __name__ == "__main__": - unittest.TextTestRunner().run(suite()) \ No newline at end of file + result = unittest.TextTestRunner().run(suite()) + sys.exit(not result.wasSuccessful()) diff --git a/ROSCO_toolbox/utilities.py b/ROSCO_toolbox/utilities.py index 8c210c0df..44d07854a 100644 --- a/ROSCO_toolbox/utilities.py +++ b/ROSCO_toolbox/utilities.py @@ -28,9 +28,9 @@ from matplotlib import transforms from itertools import takewhile, product import struct +import subprocess import ROSCO_toolbox -from ROSCO_toolbox.ofTools.util import spectral # Some useful constants now = datetime.datetime.now() pi = np.pi @@ -455,7 +455,7 @@ def DISCON_dict(turbine, controller, txt_filename=None): return DISCON_dict -def run_openfast(fast_dir,fastcall='openfast',fastfile=None,chdir=False): +def run_openfast(fast_dir, fastcall='openfast', fastfile=None, chdir=True): ''' Runs a openfast openfast simulation. @@ -483,18 +483,11 @@ def run_openfast(fast_dir,fastcall='openfast',fastfile=None,chdir=False): print('Using {} to run OpenFAST simulation'.format(fastfile)) if chdir: # Change cwd before calling OpenFAST -- note: This is an artifact of needing to call OpenFAST from the same directory as DISCON.IN - # save starting file path - original_path = os.getcwd() - # change path, run OpenFAST - os.chdir(fast_dir) - print('Running OpenFAST simulation for {} through the ROSCO toolbox...'.format(fastfile)) - os.system('{} {}'.format(fastcall, os.path.join(fastfile))) - print('OpenFAST simulation complete. ') - # return to original path - os.chdir(original_path) + cwd = fast_dir else: - # Run OpenFAST - print('Running OpenFAST simulation for {} through the ROSCO toolbox...'.format(fastfile)) - os.system('{} {}'.format(fastcall, os.path.join(fast_dir,fastfile))) - print('OpenFAST simulation complete. ') + cwd = None + print('Running OpenFAST simulation for {} through the ROSCO toolbox...'.format(fastfile)) + # os.system('{} {}'.format(fastcall, os.path.join(fastfile))) + subprocess.run([fastcall, os.path.join(fastfile)], check=True, cwd=cwd) + print('OpenFAST simulation complete.') diff --git a/Test_Cases/BAR_10/BAR_10_ServoDyn.dat b/Test_Cases/BAR_10/BAR_10_ServoDyn.dat index a429c67df..d5fc8d100 100644 --- a/Test_Cases/BAR_10/BAR_10_ServoDyn.dat +++ b/Test_Cases/BAR_10/BAR_10_ServoDyn.dat @@ -63,7 +63,7 @@ False CompNTMD - Compute nacelle tuned mass damper {true/fal False CompTTMD - Compute tower tuned mass damper {true/false} (flag) "b.dat" TTMDfile - Name of the file for tower tuned mass damper (quoted string) [unused when CompTTMD is false] ---------------------- BLADED INTERFACE ---------------------------------------- [used only with Bladed Interface] -"../../ROSCO/install/lib/libdiscon.dylib" DLL_FileName - Name/location of the dynamic library {.dll [Windows] or .so [Linux]} in the Bladed-DLL format (-) [used only with Bladed Interface] +"../../ROSCO/install/lib/libdiscon.so" DLL_FileName - Name/location of the dynamic library {.dll [Windows] or .so [Linux]} in the Bladed-DLL format (-) [used only with Bladed Interface] "BAR_10_DISCON.IN" DLL_InFile - Name of input file sent to the DLL (-) [used only with Bladed Interface] "DISCON" DLL_ProcName - Name of procedure in DLL to be called (-) [case sensitive; used only with DLL Interface] "default" DLL_DT - Communication interval for dynamic library (s) (or "default") [used only with Bladed Interface] diff --git a/Test_Cases/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_ServoDyn.dat b/Test_Cases/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_ServoDyn.dat index c86a078d0..2f068be12 100644 --- a/Test_Cases/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_ServoDyn.dat +++ b/Test_Cases/IEA-15-240-RWT-UMaineSemi/IEA-15-240-RWT-UMaineSemi_ServoDyn.dat @@ -63,7 +63,7 @@ False CompNTMD - Compute nacelle tuned mass damper {true/fal False CompTTMD - Compute tower tuned mass damper {true/false} (flag) "unused" TTMDfile - Name of the file for tower tuned mass damper (quoted string) [unused when CompTTMD is false] ---------------------- BLADED INTERFACE ---------------------------------------- [used only with Bladed Interface] -"../../ROSCO/install/lib/libdiscon.dylib" DLL_FileName - Name/location of the dynamic library {.dll [Windows] or .so [Linux]} in the Bladed-DLL format (-) [used only with Bladed Interface] +"../../ROSCO/install/lib/libdiscon.so" DLL_FileName - Name/location of the dynamic library {.dll [Windows] or .so [Linux]} in the Bladed-DLL format (-) [used only with Bladed Interface] "ServoData/DISCON-UMaineSemi.IN" DLL_InFile - Name of input file sent to the DLL (-) [used only with Bladed Interface] "DISCON" DLL_ProcName - Name of procedure in DLL to be called (-) [case sensitive; used only with DLL Interface] "default" DLL_DT - Communication interval for dynamic library (s) (or "default") [used only with Bladed Interface] diff --git a/Test_Cases/NREL-5MW/NRELOffshrBsline5MW_Onshore_ServoDyn.dat b/Test_Cases/NREL-5MW/NRELOffshrBsline5MW_Onshore_ServoDyn.dat index 47961d9cd..3b223991e 100644 --- a/Test_Cases/NREL-5MW/NRELOffshrBsline5MW_Onshore_ServoDyn.dat +++ b/Test_Cases/NREL-5MW/NRELOffshrBsline5MW_Onshore_ServoDyn.dat @@ -63,7 +63,7 @@ False CompNTMD - Compute nacelle tuned mass damper {true/false} (fla False CompTTMD - Compute tower tuned mass damper {true/false} (flag) "../5MW_Baseline/NRELOffshrBsline5MW_ServoDyn_TMD.dat" TTMDfile - Name of the file for tower tuned mass damper (quoted string) [unused when CompTTMD is false] ---------------------- BLADED INTERFACE ---------------------------------------- [used only with Bladed Interface] -"../../ROSCO/install/lib/libdiscon.dylib" DLL_FileName - Name/location of the dynamic library {.dll [Windows] or .so [Linux]} in the Bladed-DLL format (-) [used only with Bladed Interface] +"../../ROSCO/install/lib/libdiscon.so" DLL_FileName - Name/location of the dynamic library {.dll [Windows] or .so [Linux]} in the Bladed-DLL format (-) [used only with Bladed Interface] "DISCON.IN" DLL_InFile - Name of input file sent to the DLL (-) [used only with Bladed Interface] "DISCON" DLL_ProcName - Name of procedure in DLL to be called (-) [case sensitive; used only with DLL Interface] "default" DLL_DT - Communication interval for dynamic library (s) (or "default") [used only with Bladed Interface] diff --git a/environment.yml b/environment.yml new file mode 100644 index 000000000..2cb76313f --- /dev/null +++ b/environment.yml @@ -0,0 +1,11 @@ +channels: + - conda-forge + - defaults + +dependencies: + - matplotlib + - numpy + - pytest + - scipy + - pyYAML + - pandas \ No newline at end of file