diff --git a/.github/actions/tests-module-inflowwind/action.yml b/.github/actions/tests-module-inflowwind/action.yml index b0d5faa0d9..753d7d86c7 100644 --- a/.github/actions/tests-module-inflowwind/action.yml +++ b/.github/actions/tests-module-inflowwind/action.yml @@ -1,9 +1,25 @@ name: 'InflowWind module tests' description: 'Run tests specific to the InflowWind module' author: 'Rafael Mudafort https://github.com/rafmudaf' + +inputs: + test-target: + description: 'Which tests to run: unit | regression | all' + default: 'all' + runs: using: "composite" - steps: - - run: ctest -VV -R inflowwind_utest + steps: + - run: | + + if [[ ${{ inputs.test-target }} == "unit" ]] || [[ ${{ inputs.test-target }} == "all" ]]; then + ctest -VV -R inflowwind_utest + fi + + if [[ ${{ inputs.test-target }} == "regression" ]] || [[ ${{ inputs.test-target }} == "all" ]]; then + ctest -VV -j7 -R ifw_ -LE python + # Exclude the Python-interface tests since those are run directly in the job + fi + working-directory: ${{runner.workspace}}/openfast/build shell: bash diff --git a/.github/workflows/automated-dev-tests.yml b/.github/workflows/automated-dev-tests.yml index d78965f65e..980da52862 100644 --- a/.github/workflows/automated-dev-tests.yml +++ b/.github/workflows/automated-dev-tests.yml @@ -70,6 +70,10 @@ jobs: test-target: regression - name: Run HydroDyn tests uses: ./.github/actions/tests-module-hydrodyn + - name: Run InflowWind tests + uses: ./.github/actions/tests-module-inflowwind + with: + test-target: regression - name: Run SubDyn tests uses: ./.github/actions/tests-module-subdyn - name: Run OpenFAST tests @@ -143,6 +147,7 @@ jobs: cmake --build . --target aerodyn_driver -- -j ${{env.NUM_PROCS}} cmake --build . --target beamdyn_driver -- -j ${{env.NUM_PROCS}} cmake --build . --target hydrodyn_driver -- -j ${{env.NUM_PROCS}} + cmake --build . --target inflowwind_driver -- -j ${{env.NUM_PROCS}} cmake --build . --target subdyn_driver -- -j ${{env.NUM_PROCS}} - name: Run AeroDyn tests @@ -155,6 +160,10 @@ jobs: test-target: regression - name: Run HydroDyn tests uses: ./.github/actions/tests-module-hydrodyn + - name: Run InflowWind tests + uses: ./.github/actions/tests-module-inflowwind + with: + test-target: regression - name: Run SubDyn tests uses: ./.github/actions/tests-module-subdyn @@ -213,12 +222,12 @@ jobs: pip install numpy Bokeh==1.4 - name: Setup Workspace - run: cmake -E make_directory ${{runner.workspace}}/build + run: cmake -E make_directory ${{runner.workspace}}/openfast/build - name: Configure Build - working-directory: ${{runner.workspace}}/build + working-directory: ${{runner.workspace}}/openfast/build run: | cmake \ - -DCMAKE_INSTALL_PREFIX:PATH=${{runner.workspace}}/install \ + -DCMAKE_INSTALL_PREFIX:PATH=${{runner.workspace}}/openfast/install \ -DCMAKE_Fortran_COMPILER:STRING=${{env.FORTRAN_COMPILER}} \ -DOPENMP:BOOL=ON \ -DBUILD_FASTFARM:BOOL=ON \ @@ -228,7 +237,7 @@ jobs: ${GITHUB_WORKSPACE} - name: Build FAST.Farm # if: contains(github.event.head_commit.message, 'Action - Test All') || contains(github.event.pull_request.labels.*.name, 'Action - Test All') - working-directory: ${{runner.workspace}}/build + working-directory: ${{runner.workspace}}/openfast/build run: | cmake --build . --target FAST.Farm -- -j ${{env.NUM_PROCS}} cmake --build . --target regression_tests -- -j ${{env.NUM_PROCS}} @@ -236,7 +245,7 @@ jobs: - name: Run FAST.Farm tests run: | ctest -VV -L fastfarm -j ${{env.NUM_PROCS}} - working-directory: ${{runner.workspace}}/build + working-directory: ${{runner.workspace}}/openfast/build shell: bash - name: Failing test artifacts @@ -245,7 +254,7 @@ jobs: with: name: test-results path: | - ${{runner.workspace}}/build/reg_tests/glue-codes/fastfarm + ${{runner.workspace}}/openfast/build/reg_tests/glue-codes/fastfarm unit-test: runs-on: ubuntu-20.04 @@ -298,6 +307,8 @@ jobs: test-target: unit - name: Run InflowWind tests uses: ./.github/actions/tests-module-inflowwind + with: + test-target: unit - name: Generate coverage report working-directory: ${{runner.workspace}}/openfast/build @@ -346,25 +357,25 @@ jobs: with: submodules: recursive - name: Setup - run: cmake -E make_directory ${{runner.workspace}}/build + run: cmake -E make_directory ${{runner.workspace}}/openfast/build - name: Configure - working-directory: ${{runner.workspace}}/build + working-directory: ${{runner.workspace}}/openfast/build run: | cmake \ - -DCMAKE_INSTALL_PREFIX:PATH=${{runner.workspace}}/install \ + -DCMAKE_INSTALL_PREFIX:PATH=${{runner.workspace}}/openfast/install \ -DCMAKE_Fortran_COMPILER:STRING=${{env.FORTRAN_COMPILER}} \ -DCMAKE_BUILD_TYPE:STRING=Debug \ -DDOUBLE_PRECISION:BOOL=OFF \ -DGENERATE_TYPES:BOOL=ON \ ${GITHUB_WORKSPACE} - name: Build all - working-directory: ${{runner.workspace}}/build + working-directory: ${{runner.workspace}}/openfast/build run: cmake --build . --target all -- -j ${{env.NUM_PROCS}} - name: Test - working-directory: ${{runner.workspace}}/build + working-directory: ${{runner.workspace}}/openfast/build run: ./glue-codes/openfast/openfast -v - cpp-interface-tests: + interface-tests: runs-on: ubuntu-20.04 steps: - name: Checkout @@ -403,17 +414,24 @@ jobs: -DCODECOV:BOOL=ON \ ${GITHUB_WORKSPACE} - - name: Build OpenFAST C++ API + - name: Build OpenFAST C-Interfaces working-directory: ${{runner.workspace}}/openfast/build run: | + cmake --build . --target openfastlib -- -j ${{env.NUM_PROCS}} cmake --build . --target openfastcpp -- -j ${{env.NUM_PROCS}} + cmake --build . --target ifw_c_binding -- -j ${{env.NUM_PROCS}} cmake --build . --target regression_tests -- -j ${{env.NUM_PROCS}} - - name: Run OpenFAST C++ API tests + - name: Run C++ API tests working-directory: ${{runner.workspace}}/openfast/build run: | ctest -VV -L cpp + - name: Run Python API tests + working-directory: ${{runner.workspace}}/openfast/build + run: | + ctest -VV -L python + - name: Generate coverage report working-directory: ${{runner.workspace}}/openfast/build run: | @@ -436,7 +454,7 @@ jobs: uses: actions/upload-artifact@v2 if: success() with: - name: cpp-reg-tests + name: c-interface-reg-tests path: | ${{runner.workspace}}/openfast/regressioncov.xml @@ -444,7 +462,8 @@ jobs: uses: actions/upload-artifact@v2 if: failure() with: - name: cpp-reg-tests + name: c-interface-reg-tests path: | - ${{runner.workspace}}/build/reg_tests/glue-codes/openfast-cpp - !${{runner.workspace}}/build/reg_tests/glue-codes/openfast-cpp/5MW_Baseline + ${{runner.workspace}}/openfast/build/reg_tests/glue-codes/openfast-cpp + ${{runner.workspace}}/openfast/build/reg_tests/modules/inflowwind + !${{runner.workspace}}/openfast/build/reg_tests/glue-codes/openfast-cpp/5MW_Baseline diff --git a/modules/inflowwind/CMakeLists.txt b/modules/inflowwind/CMakeLists.txt index 8604785199..abc3f26313 100644 --- a/modules/inflowwind/CMakeLists.txt +++ b/modules/inflowwind/CMakeLists.txt @@ -34,7 +34,7 @@ set(IFW_SOURCES src/IfW_UserWind.f90 src/IfW_UniformWind.f90 src/InflowWind_Subs.f90 - src/InflowWind.f90 + src/InflowWind.f90 src/Lidar.f90 src/IfW_FFWind_Base.f90 src/IfW_FFWind_Base_Types.f90 @@ -51,6 +51,13 @@ set(IFW_SOURCES add_library(ifwlib ${IFW_SOURCES}) target_link_libraries(ifwlib nwtclibs) +# C-bound interface library +add_library(ifw_c_binding SHARED src/IfW_C_Binding.f90) +target_link_libraries(ifw_c_binding ifwlib) +if(APPLE OR UNIX) + target_compile_definitions(ifw_c_binding PUBLIC -DIMPLICIT_DLLEXPORT) +endif() + set(IFW_DRIVER_SOURCES src/InflowWind_Driver_Types.f90 src/InflowWind_Driver_Subs.f90 @@ -60,7 +67,7 @@ set(IFW_DRIVER_SOURCES add_executable(inflowwind_driver ${IFW_DRIVER_SOURCES}) target_link_libraries(inflowwind_driver ifwlib ${CMAKE_DL_LIBS}) -install(TARGETS inflowwind_driver ifwlib +install(TARGETS inflowwind_driver ifwlib ifw_c_binding EXPORT "${CMAKE_PROJECT_NAME}Libraries" RUNTIME DESTINATION bin LIBRARY DESTINATION lib diff --git a/modules/inflowwind/python-lib/inflowwind_library.py b/modules/inflowwind/python-lib/inflowwind_library.py new file mode 100644 index 0000000000..2a2d7e74ef --- /dev/null +++ b/modules/inflowwind/python-lib/inflowwind_library.py @@ -0,0 +1,316 @@ +#********************************************************************************************************************************** +# LICENSING +# Copyright (C) 2021 National Renewable Energy Laboratory +# +# This file is part of InflowWind. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +#********************************************************************************************************************************** +# +# This is the Python interface library for InflowWind + + +from ctypes import ( + CDLL, + POINTER, + create_string_buffer, + byref, + c_int, + c_double, + c_float, + c_char, + c_char_p +) +import datetime +import os + + +class InflowWindLib(CDLL): + # Human readable error levels from IfW. + error_levels = { + 0: "None", + 1: "Info", + 2: "Warning", + 3: "Severe Error", + 4: "Fatal Error" + } + + # NOTE: the error message length in Fortran is controlled by the + # ErrMsgLen variable in the NWTC_Base.f90 file. If that ever + # changes, it may be necessary to update the corresponding size + # here. + error_msg_c_len = 1025 + + def __init__(self, library_path): + super().__init__(library_path) + self.library_path = library_path + + self._initialize_routines() + self.ended = False # For error handling at end + + # Create buffers for class data + self.abort_error_level = 4 + self.error_status_c = c_int(0) + self.error_message_c = create_string_buffer(self.error_msg_c_len) + + # This buffer for the channel names and units is set arbitrarily large + # to start. InflowWind only has a maximum of 9 outputs at present, but + # may be expanded. Channel name and unit lengths are currently hard + # coded to 20 (this must match ChanLen in NWTC_Base.f90). + self._channel_names_c = create_string_buffer(20 * 4000 + 1) + self._channel_units_c = create_string_buffer(20 * 4000 + 1) + + self.dt = 0 # InflowWind must be passed + # something for the dt, but it does + # not use it. + + self.numTimeSteps = 0 # initialize to no timesteps + + self.numWindPts = 0 # Number of wind points we will + # request velocity information. + # Constant through entire use of + # inflowwind library instance. + + self.numChannels = 0 # Number of channels returned + + + def _initialize_routines(self): + """ + Initialize the Python handles to necessary routines in the InflowWind library. + """ + self.IfW_C_Init.argtypes = [ + POINTER(c_char_p), # input file string + POINTER(c_int), # input file string length + POINTER(c_char_p), # uniform file string + POINTER(c_int), # uniform file string length + POINTER(c_int), # numWindPts + POINTER(c_double), # dt + POINTER(c_int), # number of channels + POINTER(c_char), # output channel names + POINTER(c_char), # output channel units + POINTER(c_int), # ErrStat_C + POINTER(c_char) # ErrMsg_C + ] + self.IfW_C_Init.restype = c_int + + self.IfW_C_CalcOutput.argtypes = [ + POINTER(c_double), # Time_C + POINTER(c_float), # Positions + POINTER(c_float), # Velocities + POINTER(c_float), # Output Channel Values + POINTER(c_int), # ErrStat_C + POINTER(c_char) # ErrMsg_C + ] + self.IfW_C_CalcOutput.restype = c_int + + self.IfW_C_End.argtypes = [ + POINTER(c_int), # ErrStat_C + POINTER(c_char) # ErrMsg_C + ] + self.IfW_C_End.restype = c_int + + + def ifw_init(self, input_string_array, uniform_string_array): + """ + Call the initialization routine in the InflowWind library. + """ + + # Set up inputs: Pass single NULL joined string + input_string = '\x00'.join(input_string_array) + input_string = input_string.encode('utf-8') + input_string_length = len(input_string) + + uniform_string = '\x00'.join(uniform_string_array) + uniform_string = uniform_string.encode('utf-8') + uniform_string_length = len(uniform_string) + + self._numChannels_c = c_int(0) + + self.IfW_C_Init( + c_char_p(input_string), # IN: input file string + byref(c_int(input_string_length)), # IN: input file string length + c_char_p(uniform_string), # IN: uniform file string + byref(c_int(uniform_string_length)), # IN: uniform file string length + byref(c_int(self.numWindPts)), # IN: number of wind points + byref(c_double(self.dt)), # IN: time step (dt) + byref(self._numChannels_c), # OUT: number of channels + self._channel_names_c, # OUT: output channel names as c_char + self._channel_units_c, # OUT: output channel units as c_char + byref(self.error_status_c), # OUT: ErrStat_C + self.error_message_c # OUT: ErrMsg_C + ) + + self.check_error() + + # Initialize output channels + self.numChannels = self._numChannels_c.value + + + def ifw_calc_output(self, time, positions, velocities, outputChannelValues): + """ + Call the calculation routine in the InflowWind library. + """ + # Set up inputs + positions_flat = [pp for p in positions for pp in p] # need to flatten to pass through to Fortran (to reshape) + positions_flat_c = (c_float * (3 * self.numWindPts))(0.0,) + for i, p in enumerate(positions_flat): + positions_flat_c[i] = c_float(p) + + velocities_flat_c = (c_float * (3 * self.numWindPts))(0.0,) + + outputChannelValues_c = (c_float * self.numChannels)(0.0,) + + # Run IFW_CALCOUTPUT_C + self.IfW_C_CalcOutput( + byref(c_double(time)), # IN: time at which to calculate velocities + positions_flat_c, # IN: positions - specified by user, flattened to 1D + velocities_flat_c, # OUT: velocities at desired positions, flattened to 1D + outputChannelValues_c, # OUT: output channel values as described in input file + byref(self.error_status_c), # OUT: ErrStat_C + self.error_message_c # OUT: ErrMsg_C + ) + + self.check_error() + + # Convert output channel values back into python + for k in range(0,self.numChannels): + outputChannelValues[k] = float(outputChannelValues_c[k]) + + # Reshape velocities into [N,3] + count = 0 + for j in range(0,self.numWindPts): + velocities[j,0] = velocities_flat_c[count] + velocities[j,1] = velocities_flat_c[count+1] + velocities[j,2] = velocities_flat_c[count+2] + count = count + 3 + + + def ifw_end(self): + """ + Call the cleanup routine in the InflowWind library. + """ + if not self.ended: + self.ended = True + self.IfW_C_End( + byref(self.error_status_c), + self.error_message_c + ) + + self.check_error() + + + def check_error(self): + if self.error_status_c.value == 0: + return + elif self.error_status_c.value < self.abort_error_level: + print(f"{self.error_levels[self.error_status_c.value]}: {self.error_message_c.value.decode('ascii')}") + else: + print(f"{self.error_levels[self.error_status_c.value]}: {self.error_message_c.value.decode('ascii')}") + self.ifw_end() + raise Exception(f"{os.linesep}InflowWind terminated prematurely.") + + @property + def output_channel_names(self): + if len(self._channel_names_c.value.split()) == 0: + return [] + output_channel_names = self._channel_names_c.value.split() + output_channel_names = [n.decode('UTF-8') for n in output_channel_names] + return output_channel_names + + @property + def output_channel_units(self): + if len(self._channel_units_c.value.split()) == 0: + return [] + output_channel_units = self._channel_units_c.value.split() + output_channel_units = [n.decode('UTF-8') for n in output_channel_units] + return output_channel_units + + +### Helper functions for development + +class DebugOut(): + """ + This is only for testing purposes. Since we are not returning the + velocities to anything, we will write them to file as we go for + comparison in the regression test. When coupled to another code, the + velocities array would be passed back to the calling code for use in + the aerodynamic solver. + """ + def __init__(self, filename, numWindPts): + + self.debug_file = open(filename, 'w') # open output file and write header info + + # write file header + t_string=datetime.datetime.now() + dt_string=datetime.date.today() + self.debug_file.write(f"## This file was generated by InflowWind_Driver on {dt_string.strftime('%b-%d-%Y')} at {t_string.strftime('%H:%M:%S')}{os.linesep}") + self.debug_file.write(f"## This file contains the wind velocity at the {numWindPts} points specified in the file ") + self.debug_file.write(f"{filename}{os.linesep}") + self.debug_file.write(f"#{os.linesep}") + self.debug_file.write(f"# T X Y Z U V W{os.linesep}") + self.debug_file.write(f"# (s) (m) (m) (m) (m/s) (m/s) (m/s){os.linesep}") + self.opened = True + + def write(self,t,positions,velocities): + for p, v in zip(positions,velocities): + # TODO: does \n work as expected on Windows? + self.debug_file.write(' %14.7f %14.7f %14.7f %14.7f %14.7f %14.7f %14.7f\n' % (t,p[0],p[1],p[2],v[0],v[1],v[2])) + + def end(self): + if self.opened: + self.debug_file.close() + self.opened = False + + +class WriteOutChans(): + """ + This is only for testing purposes. Since we are not returning the + output channels to anything, we will write them to file. When coupled to + another code, this data would be passed back for inclusion the any output + file there. + """ + def __init__(self,filename,chan_names,chan_units): + + self.out_file = open(filename, 'w') # open output file and write header info + + # write file header + t_string=datetime.datetime.now() + dt_string=datetime.date.today() + self.out_file.write(f"## This file was generated by InflowWind_Driver on {dt_string.strftime('%b-%d-%Y')} at {t_string.strftime('%H:%M:%S')}{os.linesep}") + self.out_file.write(f"## This file contains output channels requested from the OutList section of the input file") + self.out_file.write(f"{filename}{os.linesep}") + self.out_file.write(f"#{os.linesep}") + self.out_file.write(f"#{os.linesep}") + self.out_file.write(f"#{os.linesep}") + self.out_file.write(f"#{os.linesep}") + self.out_file.write(' Time') + for data in chan_names: + self.out_file.write('%20s' % data) + self.out_file.write(f"{os.linesep}") # end line for chan_names + self.out_file.write(' (s)') + for data in chan_units: + self.out_file.write('%20s' % data) + self.out_file.write(f"{os.linesep}") # end line for chan_units + self.opened = True + + def write(self,chan_data): + l = chan_data.shape[1] + f_string = "{:20.7f}"*l + for i in range(chan_data.shape[0]): + self.out_file.write(f_string.format(*chan_data[i,:]) + os.linesep) + + def end(self): + if self.opened: + self.out_file.close() + self.opened = False diff --git a/modules/inflowwind/src/IfW_C_Binding.f90 b/modules/inflowwind/src/IfW_C_Binding.f90 new file mode 100644 index 0000000000..502ba8236d --- /dev/null +++ b/modules/inflowwind/src/IfW_C_Binding.f90 @@ -0,0 +1,254 @@ +!********************************************************************************************************************************** +! LICENSING +! Copyright (C) 2021 National Renewable Energy Laboratory +! +! This file is part of InflowWind. +! +! Licensed under the Apache License, Version 2.0 (the "License"); +! you may not use this file except in compliance with the License. +! You may obtain a copy of the License at +! +! http://www.apache.org/licenses/LICENSE-2.0 +! +! Unless required by applicable law or agreed to in writing, software +! distributed under the License is distributed on an "AS IS" BASIS, +! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +! See the License for the specific language governing permissions and +! limitations under the License. +! +!********************************************************************************************************************************** +MODULE InflowWind_C_BINDING + + USE ISO_C_BINDING + USE InflowWind + USE InflowWind_Subs, only: MaxOutPts + USE InflowWind_Types + USE NWTC_Library + + IMPLICIT NONE + + PUBLIC :: IfW_C_Init + PUBLIC :: IfW_C_CalcOutput + PUBLIC :: IfW_C_End + + ! Accessible to all routines inside module + TYPE(InflowWind_InputType) :: InputGuess !< An initial guess for the input; the input mesh must be defined, returned by Init + TYPE(InflowWind_InputType) :: InputData !< Created by IFW_CALCOUTPUT_C and used by IFW_END_C + TYPE(InflowWind_InitInputType) :: InitInp + TYPE(InflowWind_InitOutputType) :: InitOutData !< Initial output data -- Names, units, and version info. + TYPE(InflowWind_ParameterType) :: p !< Parameters + TYPE(InflowWind_ContinuousStateType) :: ContStates !< Initial continuous states + TYPE(InflowWind_DiscreteStateType) :: DiscStates !< Initial discrete states + TYPE(InflowWind_ConstraintStateType) :: ConstrStateGuess !< Initial guess of the constraint states + TYPE(InflowWind_ConstraintStateType) :: ConstrStates !< Constraint states at Time + TYPE(InflowWind_OtherStateType) :: OtherStates !< Initial other/optimization states + TYPE(InflowWind_OutputType) :: y !< Initial output (outputs are not calculated; only the output mesh is initialized) + TYPE(InflowWind_MiscVarType) :: m !< Misc variables for optimization (not copied in glue code) + + ! This must exactly match the value in the Python interface. If ErrMsgLen changes + ! in the NWTC Library, this should be updated, but the logic + ! exists to correctly handle different lengths of the strings + integer(IntKi), parameter :: ErrMsgLen_C=1025 + +CONTAINS + +!> This routine sets the error status in C_CHAR for export to calling code. +!! Make absolutely certain that we do not overrun the end of ErrMsg_C. That is hard coded to 1025, +!! but ErrMsgLen is set in the nwtc_library, and could change without updates here. We don't want an +!! inadvertant buffer overrun -- that can lead to bad things. +subroutine SetErr(ErrStat, ErrMsg, ErrStat_C, ErrMsg_C) + integer, intent(in ) :: ErrStat !< aggregated error message (fortran type) + character(ErrMsgLen), intent(in ) :: ErrMsg !< aggregated error message (fortran type) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + ErrStat_C = ErrStat ! We will send back the same error status that is used in OpenFAST + if (ErrMsgLen > ErrMsgLen_C-1) then ! If ErrMsgLen is > the space in ErrMsg_C, do not copy everything over + ErrMsg_C = TRANSFER( trim(ErrMsg(1:ErrMsgLen_C-1))//C_NULL_CHAR, ErrMsg_C ) + else + ErrMsg_C = TRANSFER( trim(ErrMsg)//C_NULL_CHAR, ErrMsg_C ) + endif +end subroutine SetErr + + +!=============================================================================================================== +!--------------------------------------------- IFW INIT -------------------------------------------------------- +!=============================================================================================================== +SUBROUTINE IfW_C_Init(InputFileString_C, InputFileStringLength_C, InputUniformString_C, InputUniformStringLength_C, NumWindPts_C, DT_C, NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, ErrStat_C, ErrMsg_C) BIND (C, NAME='IfW_C_Init') + IMPLICIT NONE +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: IfW_Init_c +!GCC$ ATTRIBUTES DLLEXPORT :: IfW_Init_c +#endif + TYPE(C_PTR) , INTENT(IN ) :: InputFileString_C + INTEGER(C_INT) , INTENT(IN ) :: InputFileStringLength_C + TYPE(C_PTR) , INTENT(IN ) :: InputUniformString_C + INTEGER(C_INT) , INTENT(IN ) :: InputUniformStringLength_C + INTEGER(C_INT) , INTENT(IN ) :: NumWindPts_C + REAL(C_DOUBLE) , INTENT(IN ) :: DT_C + INTEGER(C_INT) , INTENT( OUT) :: NumChannels_C + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelNames_C(ChanLen*MaxOutPts+1) + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: OutputChannelUnits_C(ChanLen*MaxOutPts+1) + INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) + + ! Local Variables + CHARACTER(kind=C_char, len=InputFileStringLength_C), POINTER :: InputFileString !< Input file as a single string with NULL chracter separating lines + CHARACTER(kind=C_char, len=InputUniformStringLength_C), POINTER :: UniformFileString !< Input file as a single string with NULL chracter separating lines -- Uniform wind file + + REAL(DbKi) :: TimeInterval + INTEGER :: ErrStat !< aggregated error message + CHARACTER(ErrMsgLen) :: ErrMsg !< aggregated error message + INTEGER :: ErrStat2 !< temporary error status from a call + CHARACTER(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call + INTEGER :: i,j,k + character(*), parameter :: RoutineName = 'IfW_C_Init' !< for error handling + + ! Initialize error handling + ErrStat = ErrID_None + ErrMsg = "" + + ! Get fortran pointer to C_NULL_CHAR deliniated input file as a string + CALL C_F_pointer(InputFileString_C, InputFileString) + CALL C_F_pointer(InputUniformString_C, UniformFileString) + + ! Store string-inputs as type FileInfoType within InflowWind_InitInputType + CALL InitFileInfo(InputFileString, InitInp%PassedFileData, ErrStat2, ErrMsg2); if (Failed()) return + InitInp%UseInputFile = .FALSE. + + ! store Uniform File strings if they are non-zero sized + if (len(UniformFileString) > 1) then + CALL InitFileInfo(UniformFileString, InitInp%WindType2Data, ErrStat2, ErrMsg2); if (Failed()) return + InitInp%WindType2UseInputFile = .FALSE. + else ! Default to reading from disk + InitInp%WindType2UseInputFile = .TRUE. + endif + + ! Set other inputs for calling InflowWind_Init + InitInp%NumWindPoints = NumWindPts_C + InitInp%InputFileName = "passed_ifw_file" ! dummy + InitInp%RootName = "ifwRoot" ! used for making echo files + TimeInterval = REAL(DT_C, DbKi) + + ! Call the main subroutine InflowWind_Init - only need InitInp and TimeInterval as inputs, the rest are set by InflowWind_Init + CALL InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, ConstrStateGuess, OtherStates, y, m, TimeInterval, InitOutData, ErrStat2, ErrMsg2 ) + if (Failed()) return + + ! Number of channels + NumChannels_C = size(InitOutData%WriteOutputHdr) + + ! transfer the output channel names and units to c_char arrays for returning + k=1 + do i=1,NumChannels_C + do j=1,ChanLen ! max length of channel name. Same for units + OutputChannelNames_C(k)=InitOutData%WriteOutputHdr(i)(j:j) + OutputChannelUnits_C(k)=InitOutData%WriteOutputUnt(i)(j:j) + k=k+1 + enddo + enddo + + ! null terminate the string + OutputChannelNames_C(k) = C_NULL_CHAR + OutputChannelUnits_C(k) = C_NULL_CHAR + + ! Clean up variables and set up for IFW_CALCOUTPUT_C + CALL InflowWind_CopyInput(InputGuess, InputData, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + if (Failed()) return + CALL InflowWind_CopyConstrState(ConstrStateGuess, ConstrStates, MESH_NEWCOPY, ErrStat2, ErrMsg2 ) + if (Failed()) return + + call Cleanup() + call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + +CONTAINS + logical function Failed() + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + if (Failed) then + call Cleanup() + call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + endif + end function Failed + subroutine Cleanup() ! NOTE: we are ignoring any error reporting from here + CALL InflowWind_DestroyInput(InputGuess, ErrStat2, ErrMsg2 ) + CALL InflowWind_DestroyConstrState(ConstrStateGuess, ErrStat2, ErrMsg2 ) + end subroutine Cleanup +END SUBROUTINE IfW_C_Init + +!=============================================================================================================== +!--------------------------------------------- IFW CALCOUTPUT -------------------------------------------------- +!=============================================================================================================== + +SUBROUTINE IfW_C_CalcOutput(Time_C,Positions_C,Velocities_C,OutputChannelValues_C,ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_CalcOutput') + IMPLICIT NONE +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: IfW_CalcOutput_c +!GCC$ ATTRIBUTES DLLEXPORT :: IfW_CalcOutput_c +#endif + REAL(C_DOUBLE) , INTENT(IN ) :: Time_C + REAL(C_FLOAT) , INTENT(IN ) :: Positions_C(3*InitInp%NumWindPoints) + REAL(C_FLOAT) , INTENT( OUT) :: Velocities_C(3*InitInp%NumWindPoints) + REAL(C_FLOAT) , INTENT( OUT) :: OutputChannelValues_C(p%NumOuts) + INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) + + ! Local variables + REAL(DbKi) :: Time + INTEGER :: ErrStat !< aggregated error message + CHARACTER(ErrMsgLen) :: ErrMsg !< aggregated error message + INTEGER :: ErrStat2 !< temporary error status from a call + CHARACTER(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call + character(*), parameter :: RoutineName = 'IfW_C_CalcOutput' !< for error handling + + ! Initialize error handling + ErrStat = ErrID_None + ErrMsg = "" + + ! Convert the inputs from C to Fortran + Time = REAL(Time_C,DbKi) + InputData%PositionXYZ = reshape( real(Positions_C,ReKi), (/3, InitInp%NumWindPoints/) ) + + ! Call the main subroutine InflowWind_CalcOutput to get the velocities + CALL InflowWind_CalcOutput( Time, InputData, p, ContStates, DiscStates, ConstrStates, OtherStates, y, m, ErrStat2, ErrMsg2 ) + if (Failed()) return + + ! Get velocities out of y and flatten them (still in same spot in memory) + Velocities_C = reshape( REAL(y%VelocityUVW, C_FLOAT), (/3*InitInp%NumWindPoints/) ) ! VelocityUVW is 2D array of ReKi (might need reshape or make into pointer); size [3,N] + + ! Get the output channel info out of y + OutputChannelValues_C = REAL(y%WriteOutput, C_FLOAT) + + call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + +CONTAINS + logical function Failed() + CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + Failed = ErrStat >= AbortErrLev + if (Failed) call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + end function Failed +END SUBROUTINE IfW_C_CalcOutput + +!=============================================================================================================== +!--------------------------------------------------- IFW END --------------------------------------------------- +!=============================================================================================================== + +SUBROUTINE IfW_C_End(ErrStat_C,ErrMsg_C) BIND (C, NAME='IfW_C_End') + IMPLICIT NONE +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: IfW_End_c +!GCC$ ATTRIBUTES DLLEXPORT :: IfW_End_c +#endif + INTEGER(C_INT) , INTENT( OUT) :: ErrStat_C + CHARACTER(KIND=C_CHAR) , INTENT( OUT) :: ErrMsg_C(ErrMsgLen_C) + + ! Local variables + INTEGER :: ErrStat + CHARACTER(ErrMsgLen) :: ErrMsg + + ! Call the main subroutine InflowWind_End + CALL InflowWind_End( InputData, p, ContStates, DiscStates, ConstrStates, OtherStates, y, m, ErrStat, ErrMsg ) + + call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + +END SUBROUTINE IfW_C_End + +END MODULE diff --git a/modules/inflowwind/src/InflowWind.f90 b/modules/inflowwind/src/InflowWind.f90 index e6f52fa10c..a84d02b84d 100644 --- a/modules/inflowwind/src/InflowWind.f90 +++ b/modules/inflowwind/src/InflowWind.f90 @@ -759,6 +759,9 @@ SUBROUTINE CleanUp() CALL InflowWind_DestroyInputFile( InputFileData, TmpErrStat, TmpErrMsg ) CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) + ! Ignore error messages from InFileInfo destruction + call NWTC_Library_DestroyFileInfoType( InFileInfo, TmpErrStat, TmpErrMsg ) + ! Close the summary file if we were writing one IF ( SumFileUnit > 0 ) THEN CALL InflowWind_CloseSumFile( SumFileUnit, TmpErrStat, TmpErrMsg ) diff --git a/modules/inflowwind/src/InflowWind_Subs.f90 b/modules/inflowwind/src/InflowWind_Subs.f90 index 78b7b5d1b5..2499deb117 100644 --- a/modules/inflowwind/src/InflowWind_Subs.f90 +++ b/modules/inflowwind/src/InflowWind_Subs.f90 @@ -190,7 +190,6 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In ! Allocate the array for the OutList CALL AllocAry( InputFileData%OutList, MaxOutPts, "InflowWind Input File's OutList", TmpErrStat, TmpErrMsg ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !--------------------------------------------------------------------------------------------- @@ -199,7 +198,6 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In CurLine = 4 CALL ParseVar( InFileInfo, CurLine, "Echo", InputFileData%EchoFlag, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return if ( InputFileData%EchoFlag ) then @@ -219,22 +217,18 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In ! switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; 4=binary Bladed-style FF; 5=HAWC format; 6=User defined; 7=native Bladed FF) CALL ParseVar( InFileInfo, CurLine, "WindType", InputFileData%WindType, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return ! Direction of wind propagation (meteorological direction) (deg) CALL ParseVar( InFileInfo, CurLine, "PropagationDir", InputFileData%PropagationDir, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return ! VFlowAngle: Upflow angle (deg) CALL ParseVarWDefault( InFileInfo, CurLine, "VFlowAng", InputFileData%VFlowAngle, 0.0_ReKi, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return ! NWindVel: Number of points to output the wind velocity (0 to 9) CALL ParseVar( InFileInfo, CurLine, "NWindVel", InputFileData%NWindVel, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return ! Before proceeding, make sure that NWindVel makes sense @@ -246,24 +240,20 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In ! Allocate space for the output location arrays: CALL AllocAry( InputFileData%WindVxiList, InputFileData%NWindVel, 'WindVxiList', TmpErrStat, TmpErrMsg ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return CALL AllocAry( InputFileData%WindVyiList, InputFileData%NWindVel, 'WindVyiList', TmpErrStat, TmpErrMsg ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) + if (Failed()) return CALL AllocAry( InputFileData%WindVziList, InputFileData%NWindVel, 'WindVziList', TmpErrStat, TmpErrMsg ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return ENDIF CALL ParseAry( InFileInfo, CurLine, 'WindVxiList', InputFileData%WindVxiList, InputFileData%NWindVel, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseAry( InFileInfo, CurLine, 'WindVyiList', InputFileData%WindVyiList, InputFileData%NWindVel, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseAry( InFileInfo, CurLine, 'WindVziList', InputFileData%WindVziList, InputFileData%NWindVel, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !------------------------------------------------------------------------------------------------- @@ -272,15 +262,12 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In CurLine = CurLine + 1 ! Skip section break CALL ParseVar( InFileInfo, CurLine, "HWindSpeed", InputFileData%Steady_HWindSpeed, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "RefHt", InputFileData%Steady_RefHt, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "PLexp", InputFileData%Steady_PLexp, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !------------------------------------------------------------------------------------------------- @@ -289,7 +276,6 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In CurLine = CurLine + 1 ! Skip section break CALL ParseVar( InFileInfo, CurLine, "FileName_Uni", InputFileData%Uniform_FileName, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return IF ( PathIsRelative( InputFileData%Uniform_FileName ) ) InputFileData%Uniform_FileName = TRIM(PriPath)//TRIM(InputFileData%Uniform_FileName) IF ( FixedWindFileRootName ) THEN ! .TRUE. when FAST.Farm uses multiple instances of InflowWind for ambient wind data @@ -301,11 +287,9 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In ENDIF CALL ParseVar( InFileInfo, CurLine, "RefHt_Uni", InputFileData%Uniform_RefHt, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "RefLength", InputFileData%Uniform_RefLength, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !------------------------------------------------------------------------------------------------- @@ -314,7 +298,6 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In CurLine = CurLine + 1 ! Skip section break CALL ParseVar( InFileInfo, CurLine, "FileName_BTS", InputFileData%TSFF_FileName, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return IF ( PathIsRelative( InputFileData%TSFF_FileName ) ) InputFileData%TSFF_FileName = TRIM(PriPath)//TRIM(InputFileData%TSFF_FileName) IF ( FixedWindFileRootName ) THEN ! .TRUE. when FAST.Farm uses multiple instances of InflowWind for ambient wind data @@ -331,12 +314,10 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In CurLine = CurLine + 1 ! Skip section break CALL ParseVar( InFileInfo, CurLine, "FilenameRoot", InputFileData%BladedFF_FileName, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return IF ( PathIsRelative( InputFileData%BladedFF_FileName ) ) InputFileData%BladedFF_FileName = TRIM(PriPath)//TRIM(InputFileData%BladedFF_FileName) CALL ParseVar( InFileInfo, CurLine, "TowerFile", InputFileData%BladedFF_TowerFile, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !------------------------------------------------------------------------------------------------- @@ -345,16 +326,13 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In ! CurLine = CurLine + 1 ! Skip section break ! CALL ParseVar( InFileInfo, CurLine, "CTTS_CoherentTurbFlag", InputFileData%CTTS_CoherentTurb, TmpErrStat, TmpErrMsg, UnEc ) - ! CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) ! if (Failed()) return ! CALL ParseVar( InFileInfo, CurLine, "CTTS_FileName", InputFileData%CTTS_FileName, TmpErrStat, TmpErrMsg, UnEc ) - ! CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) ! if (Failed()) return ! IF ( PathIsRelative( InputFileData%CTTS_FileName ) ) InputFileData%CTTS_FileName = TRIM(PriPath)//TRIM(InputFileData%CTTS_FileName) ! CALL ParseVar( InFileInfo, CurLine, "CTTS_Path", InputFileData%CTTS_Path, TmpErrStat, TmpErrMsg, UnEc ) - ! CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) ! if (Failed()) return ! IF ( PathIsRelative( InputFileData%CTTS_Path ) ) InputFileData%CTTS_Path = TRIM(PriPath)//TRIM(InputFileData%CTTS_Path) @@ -364,17 +342,14 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In CurLine = CurLine + 1 ! Skip section break CALL ParseVar( InFileInfo, CurLine, "FileName_u", InputFileData%HAWC_FileName_u, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return IF ( PathIsRelative( InputFileData%HAWC_FileName_u ) ) InputFileData%HAWC_FileName_u = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_u) CALL ParseVar( InFileInfo, CurLine, "FileName_v", InputFileData%HAWC_FileName_v, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return IF ( PathIsRelative( InputFileData%HAWC_FileName_v ) ) InputFileData%HAWC_FileName_v = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_v) CALL ParseVar( InFileInfo, CurLine, "FileName_w", InputFileData%HAWC_FileName_w, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return IF ( PathIsRelative( InputFileData%HAWC_FileName_w ) ) InputFileData%HAWC_FileName_w = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_w) @@ -391,31 +366,24 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In ENDIF CALL ParseVar( InFileInfo, CurLine, "nx", InputFileData%HAWC_nx, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "ny", InputFileData%HAWC_ny, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "nz", InputFileData%HAWC_nz, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "dx", InputFileData%HAWC_dx, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "dy", InputFileData%HAWC_dy, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "dz", InputFileData%HAWC_dz, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "RefHt_HAWC", InputFileData%FF%RefHt, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !---------------------------------------------------------------------------------------------- @@ -425,44 +393,31 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In CurLine = CurLine + 1 ! Skip section break ! ScaleMethod: Turbulence scaling method [0=none, 1=direct scaling, 2= calculate scaling factor based on a desired standard deviation] CALL ParseVar( InFileInfo, CurLine, "ScaleMethod", InputFileData%FF%ScaleMethod, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "SFx", InputFileData%FF%SF(1), TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "SFy", InputFileData%FF%SF(2), TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "SFz", InputFileData%FF%SF(3), TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "SigmaFx", InputFileData%FF%SigmaF(1), TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "SigmaFy", InputFileData%FF%SigmaF(2), TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "SigmaFz", InputFileData%FF%SigmaF(3), TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return ! CALL ParseVar( InFileInfo, CurLine, "HAWC_TStart", InputFileData%FF%TStart, TmpErrStat, TmpErrMsg, UnEc ) - ! CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) - ! IF (ErrStat >= AbortErrLev) THEN - ! RETURN - ! ENDIF + ! if (Failed()) return ! CALL ParseVar( InFileInfo, CurLine, "HAWC_TEnd", InputFileData%FF%TEnd, TmpErrStat, TmpErrMsg, UnEc ) - ! CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) - ! IF (ErrStat >= AbortErrLev) THEN - ! RETURN - ! ENDIF + ! if (Failed()) return !---------------------------------------------------------------------------------------------- !> Read the _Mean wind profile paramters (added to HAWC-format files) [used only for WindType = 5]_ subsection @@ -470,24 +425,19 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In CurLine = CurLine + 1 ! Skip section break CALL ParseVar( InFileInfo, CurLine, "URef", InputFileData%FF%URef, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return ! WindProfileType: Wind profile type (0=constant;1=logarithmic;2=power law) CALL ParseVar( InFileInfo, CurLine, "WindProfile", InputFileData%FF%WindProfileType, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "PLExp_HAWC", InputFileData%FF%PLExp, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVar( InFileInfo, CurLine, "Z0", InputFileData%FF%Z0, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return CALL ParseVarWDefault( InFileInfo, CurLine, "XOffset", InputFileData%FF%XOffset, 0.0_ReKi, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !---------------------------------------------------------------------------------------------- @@ -495,14 +445,12 @@ SUBROUTINE InflowWind_ParseInputFileInfo( InputFileData, InFileInfo, PriPath, In !---------------------------------------------------------------------------------------------- CurLine = CurLine + 1 ! Skip section break CALL ParseVar( InFileInfo, CurLine, "SumPrint", InputFileData%SumPrint, TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !---------------------- OUTLIST -------------------------------------------- CurLine = CurLine + 1 ! Skip comment line CALL ReadOutputListFromFileInfo( InFileInfo, CurLine, InputFileData%OutList, & InputFileData%NumOuts, "OutList", "List of user-requested output channels", TmpErrStat, TmpErrMsg, UnEc ) - CALL SetErrStat( TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName ) if (Failed()) return !------------------------------------------------------------------------------------------------- diff --git a/reg_tests/CMakeLists.txt b/reg_tests/CMakeLists.txt index 82ffcc4610..114b3d8771 100644 --- a/reg_tests/CMakeLists.txt +++ b/reg_tests/CMakeLists.txt @@ -60,6 +60,9 @@ set(CTEST_HYDRODYN_EXECUTABLE "${CMAKE_BINARY_DIR}/modules/hydrodyn/hydrodyn_dri # Set the SubDyn executable configuration option and default set(CTEST_SUBDYN_EXECUTABLE "${CMAKE_BINARY_DIR}/modules/subdyn/subdyn_driver" CACHE FILEPATH "Specify the SubDyn driver executable to use in testing.") +# Set the InflowWind executable configuration option and default +set(CTEST_INFLOWWIND_EXECUTABLE "${CMAKE_BINARY_DIR}/modules/inflowwind/inflowwind_driver" CACHE FILEPATH "Specify the InflowWind driver executable to use in testing.") + # Set the python executable configuration option and default if(NOT EXISTS ${PYTHON_EXECUTABLE}) find_program(PYTHON_EXECUTABLE NAMES python3 python py) diff --git a/reg_tests/CTestList.cmake b/reg_tests/CTestList.cmake index d617fdbf15..91032d2956 100644 --- a/reg_tests/CTestList.cmake +++ b/reg_tests/CTestList.cmake @@ -144,6 +144,24 @@ function(sd_regression TESTNAME LABEL) regression(${TEST_SCRIPT} ${SUBDYN_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} ${TESTNAME} "${LABEL}") endfunction(sd_regression) +# inflowwind +function(ifw_regression TESTNAME LABEL) + set(TEST_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/executeInflowwindRegressionCase.py") + set(INFLOWWIND_EXECUTABLE "${CTEST_INFLOWWIND_EXECUTABLE}") + set(SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/..") + set(BUILD_DIRECTORY "${CTEST_BINARY_DIR}/modules/inflowwind") + regression(${TEST_SCRIPT} ${INFLOWWIND_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} ${TESTNAME} "${LABEL}") +endfunction(ifw_regression) + +# inflowwind-Py +function(ifw_py_regression TESTNAME LABEL) + set(TEST_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/executeInflowwindPyRegressionCase.py") + set(INFLOWWIND_EXECUTABLE "${PYTHON_EXECUTABLE}") + set(SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/..") + set(BUILD_DIRECTORY "${CTEST_BINARY_DIR}/modules/inflowwind") + regression(${TEST_SCRIPT} ${INFLOWWIND_EXECUTABLE} ${SOURCE_DIRECTORY} ${BUILD_DIRECTORY} ${TESTNAME} "${LABEL}") +endfunction(ifw_py_regression) + #=============================================================================== # Regression tests #=============================================================================== @@ -235,3 +253,7 @@ sd_regression("SD_Cable_5Joints" "subdyn;offshore") sd_regression("SD_PendulumDamp" "subdyn;offshore") sd_regression("SD_Rigid" "subdyn;offshore") sd_regression("SD_SparHanging" "subdyn;offshore") + +# InflowWind regression tests +ifw_regression("ifw_turbsimff" "inflowwind") +ifw_py_regression("ifw_py_turbsimff" "inflowwind;python") diff --git a/reg_tests/executeInflowwindPyRegressionCase.py b/reg_tests/executeInflowwindPyRegressionCase.py new file mode 100644 index 0000000000..27fc4aed84 --- /dev/null +++ b/reg_tests/executeInflowwindPyRegressionCase.py @@ -0,0 +1,141 @@ +# +# Copyright 2017 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" + This program executes InflowWind and a regression test for a single test case. + The test data is contained in a git submodule, r-test, which must be initialized + prior to running. See the r-test README or OpenFAST documentation for more info. + + Get usage with: `executeInflowWindRegressionCase.py -h` +""" + +import os +import sys +basepath = os.path.sep.join(sys.argv[0].split(os.path.sep)[:-1]) if os.path.sep in sys.argv[0] else "." +sys.path.insert(0, os.path.sep.join([basepath, "lib"])) +import argparse +import shutil +import glob +import subprocess +import rtestlib as rtl +import openfastDrivers +import pass_fail +from errorPlotting import exportCaseSummary + +##### Main program + +### Store the python executable for future python calls +pythonCommand = sys.executable + +### Verify input arguments +parser = argparse.ArgumentParser(description="Executes OpenFAST and a regression test for a single test case.") +parser.add_argument("caseName", metavar="Case-Name", type=str, nargs=1, help="The name of the test case.") +parser.add_argument("executable", metavar="InflowWind-Python", type=str, nargs=1, help="The path to the InflowWind driver executable.") +parser.add_argument("sourceDirectory", metavar="path/to/openfast_repo", type=str, nargs=1, help="The path to the OpenFAST repository.") +parser.add_argument("buildDirectory", metavar="path/to/openfast_repo/build", type=str, nargs=1, help="The path to the OpenFAST repository build directory.") +parser.add_argument("tolerance", metavar="Test-Tolerance", type=float, nargs=1, help="Tolerance defining pass or failure in the regression test.") +parser.add_argument("systemName", metavar="System-Name", type=str, nargs=1, help="The current system\'s name: [Darwin,Linux,Windows]") +parser.add_argument("compilerId", metavar="Compiler-Id", type=str, nargs=1, help="The compiler\'s id: [Intel,GNU]") +parser.add_argument("-p", "-plot", dest="plot", action='store_true', help="bool to include plots in failed cases") +parser.add_argument("-n", "-no-exec", dest="noExec", action='store_true', help="bool to prevent execution of the test cases") +parser.add_argument("-v", "-verbose", dest="verbose", action='store_true', help="bool to include verbose system output") + +args = parser.parse_args() + +caseName = args.caseName[0] +executable = args.executable[0] +sourceDirectory = args.sourceDirectory[0] +buildDirectory = args.buildDirectory[0] +tolerance = args.tolerance[0] +plotError = args.plot if args.plot is False else True +noExec = args.noExec if args.noExec is False else True +verbose = args.verbose if args.verbose is False else True + +# validate inputs +rtl.validateExeOrExit(executable) +rtl.validateDirOrExit(sourceDirectory) +if not os.path.isdir(buildDirectory): + os.makedirs(buildDirectory) + +### Build the filesystem navigation variables for running the test case +regtests = os.path.join(sourceDirectory, "reg_tests") +lib = os.path.join(regtests, "lib") +rtest = os.path.join(regtests, "r-test") +moduleDirectory = os.path.join(rtest, "modules", "inflowwind") +inputsDirectory = os.path.join(moduleDirectory, caseName) +targetOutputDirectory = os.path.join(inputsDirectory) +testBuildDirectory = os.path.join(buildDirectory, caseName) + +# verify all the required directories exist +if not os.path.isdir(rtest): + rtl.exitWithError("The test data directory, {}, does not exist. If you haven't already, run `git submodule update --init --recursive`".format(rtest)) +if not os.path.isdir(targetOutputDirectory): + rtl.exitWithError("The test data outputs directory, {}, does not exist. Try running `git submodule update`".format(targetOutputDirectory)) +if not os.path.isdir(inputsDirectory): + rtl.exitWithError("The test data inputs directory, {}, does not exist. Verify your local repository is up to date.".format(inputsDirectory)) + +# create the local output directory if it does not already exist +# and initialize it with input files for all test cases +if not os.path.isdir(testBuildDirectory): + os.makedirs(testBuildDirectory) + for file in glob.glob(os.path.join(inputsDirectory,"*py")): + filename = file.split(os.path.sep)[-1] + shutil.copy(os.path.join(inputsDirectory,filename), os.path.join(testBuildDirectory,filename)) + for file in glob.glob(os.path.join(inputsDirectory,"*inp")): + filename = file.split(os.path.sep)[-1] + shutil.copy(os.path.join(inputsDirectory,filename), os.path.join(testBuildDirectory,filename)) + +### Run inflowwind on the test case +if not noExec: + caseInputFile = os.path.join(testBuildDirectory, "inflowWind_testDriver.py") + returnCode = openfastDrivers.runInflowwindDriverCase(caseInputFile, executable) + if returnCode != 0: + rtl.exitWithError("") + +### Build the filesystem navigation variables for running the regression test +localOutFile = os.path.join(testBuildDirectory, "Points.Velocity.dat") +baselineOutFile = os.path.join(targetOutputDirectory, "Points.Velocity.dat") +rtl.validateFileOrExit(localOutFile) +rtl.validateFileOrExit(baselineOutFile) + +testData, testInfo, testPack = pass_fail.readFASTOut(localOutFile) +baselineData, baselineInfo, _ = pass_fail.readFASTOut(baselineOutFile) +performance = pass_fail.calculateNorms(testData, baselineData) +normalizedNorm = performance[:, 1] + +# export all case summaries +results = list(zip(testInfo["attribute_names"], [*performance])) +results_max = performance.max(axis=0) +exportCaseSummary(testBuildDirectory, caseName, results, results_max, tolerance) + +# failing case +if not pass_fail.passRegressionTest(normalizedNorm, tolerance): + if plotError: + from errorPlotting import finalizePlotDirectory, plotOpenfastError + ixFailChannels = [i for i in range(len(testInfo["attribute_names"])) if normalizedNorm[i] > tolerance] + failChannels = [channel for i, channel in enumerate(testInfo["attribute_names"]) if i in ixFailChannels] + failResults = [res for i, res in enumerate(results) if i in ixFailChannels] + for channel in failChannels: + try: + plotOpenfastError(localOutFile, baselineOutFile, channel) + except: + error = sys.exc_info()[1] + print("Error generating plots: {}".format(error.msg)) + finalizePlotDirectory(localOutFile, failChannels, caseName) + sys.exit(1) + +# passing case +sys.exit(0) diff --git a/reg_tests/executeInflowwindRegressionCase.py b/reg_tests/executeInflowwindRegressionCase.py new file mode 100644 index 0000000000..0956b0bd3b --- /dev/null +++ b/reg_tests/executeInflowwindRegressionCase.py @@ -0,0 +1,138 @@ +# +# Copyright 2017 National Renewable Energy Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" + This program executes InflowWind and a regression test for a single test case. + The test data is contained in a git submodule, r-test, which must be initialized + prior to running. See the r-test README or OpenFAST documentation for more info. + + Get usage with: `executeInflowWindRegressionCase.py -h` +""" + +import os +import sys +basepath = os.path.sep.join(sys.argv[0].split(os.path.sep)[:-1]) if os.path.sep in sys.argv[0] else "." +sys.path.insert(0, os.path.sep.join([basepath, "lib"])) +import argparse +import shutil +import glob +import subprocess +import rtestlib as rtl +import openfastDrivers +import pass_fail +from errorPlotting import exportCaseSummary + +##### Main program + +### Store the python executable for future python calls +pythonCommand = sys.executable + +### Verify input arguments +parser = argparse.ArgumentParser(description="Executes OpenFAST and a regression test for a single test case.") +parser.add_argument("caseName", metavar="Case-Name", type=str, nargs=1, help="The name of the test case.") +parser.add_argument("executable", metavar="InflowWind-Driver", type=str, nargs=1, help="The path to the InflowWind driver executable.") +parser.add_argument("sourceDirectory", metavar="path/to/openfast_repo", type=str, nargs=1, help="The path to the OpenFAST repository.") +parser.add_argument("buildDirectory", metavar="path/to/openfast_repo/build", type=str, nargs=1, help="The path to the OpenFAST repository build directory.") +parser.add_argument("tolerance", metavar="Test-Tolerance", type=float, nargs=1, help="Tolerance defining pass or failure in the regression test.") +parser.add_argument("systemName", metavar="System-Name", type=str, nargs=1, help="The current system\'s name: [Darwin,Linux,Windows]") +parser.add_argument("compilerId", metavar="Compiler-Id", type=str, nargs=1, help="The compiler\'s id: [Intel,GNU]") +parser.add_argument("-p", "-plot", dest="plot", action='store_true', help="bool to include plots in failed cases") +parser.add_argument("-n", "-no-exec", dest="noExec", action='store_true', help="bool to prevent execution of the test cases") +parser.add_argument("-v", "-verbose", dest="verbose", action='store_true', help="bool to include verbose system output") + +args = parser.parse_args() + +caseName = args.caseName[0] +executable = args.executable[0] +sourceDirectory = args.sourceDirectory[0] +buildDirectory = args.buildDirectory[0] +tolerance = args.tolerance[0] +plotError = args.plot if args.plot is False else True +noExec = args.noExec if args.noExec is False else True +verbose = args.verbose if args.verbose is False else True + +# validate inputs +rtl.validateExeOrExit(executable) +rtl.validateDirOrExit(sourceDirectory) +if not os.path.isdir(buildDirectory): + os.makedirs(buildDirectory) + +### Build the filesystem navigation variables for running the test case +regtests = os.path.join(sourceDirectory, "reg_tests") +lib = os.path.join(regtests, "lib") +rtest = os.path.join(regtests, "r-test") +moduleDirectory = os.path.join(rtest, "modules", "inflowwind") +inputsDirectory = os.path.join(moduleDirectory, caseName) +targetOutputDirectory = os.path.join(inputsDirectory) +testBuildDirectory = os.path.join(buildDirectory, caseName) + +# verify all the required directories exist +if not os.path.isdir(rtest): + rtl.exitWithError("The test data directory, {}, does not exist. If you haven't already, run `git submodule update --init --recursive`".format(rtest)) +if not os.path.isdir(targetOutputDirectory): + rtl.exitWithError("The test data outputs directory, {}, does not exist. Try running `git submodule update`".format(targetOutputDirectory)) +if not os.path.isdir(inputsDirectory): + rtl.exitWithError("The test data inputs directory, {}, does not exist. Verify your local repository is up to date.".format(inputsDirectory)) + +# create the local output directory if it does not already exist +# and initialize it with input files for all test cases +if not os.path.isdir(testBuildDirectory): + os.makedirs(testBuildDirectory) + for file in glob.glob(os.path.join(inputsDirectory,"*inp")): + filename = file.split(os.path.sep)[-1] + shutil.copy(os.path.join(inputsDirectory,filename), os.path.join(testBuildDirectory,filename)) + +### Run inflowwind on the test case +if not noExec: + caseInputFile = os.path.join(testBuildDirectory, "ifw_driver.inp") + returnCode = openfastDrivers.runInflowwindDriverCase(caseInputFile, executable) + if returnCode != 0: + rtl.exitWithError("") + +### Build the filesystem navigation variables for running the regression test +localOutFile = os.path.join(testBuildDirectory, "Points.Velocity.dat") +baselineOutFile = os.path.join(targetOutputDirectory, "Points.Velocity.dat") +rtl.validateFileOrExit(localOutFile) +rtl.validateFileOrExit(baselineOutFile) + +testData, testInfo, testPack = pass_fail.readFASTOut(localOutFile) +baselineData, baselineInfo, _ = pass_fail.readFASTOut(baselineOutFile) +performance = pass_fail.calculateNorms(testData, baselineData) +normalizedNorm = performance[:, 1] + +# export all case summaries +results = list(zip(testInfo["attribute_names"], [*performance])) +results_max = performance.max(axis=0) +exportCaseSummary(testBuildDirectory, caseName, results, results_max, tolerance) + +# failing case +if not pass_fail.passRegressionTest(normalizedNorm, tolerance): + if plotError: + from errorPlotting import finalizePlotDirectory, plotOpenfastError + ixFailChannels = [i for i in range(len(testInfo["attribute_names"])) if normalizedNorm[i] > tolerance] + failChannels = [channel for i, channel in enumerate(testInfo["attribute_names"]) if i in ixFailChannels] + failResults = [res for i, res in enumerate(results) if i in ixFailChannels] + for channel in failChannels: + try: + plotOpenfastError(localOutFile, baselineOutFile, channel) + except: + error = sys.exc_info()[1] + print("Error generating plots: {}".format(error.msg)) + finalizePlotDirectory(localOutFile, failChannels, caseName) + sys.exit(1) + +# passing case +sys.exit(0) diff --git a/reg_tests/lib/openfastDrivers.py b/reg_tests/lib/openfastDrivers.py index 02cf6ce6c9..751c6c492a 100644 --- a/reg_tests/lib/openfastDrivers.py +++ b/reg_tests/lib/openfastDrivers.py @@ -75,3 +75,8 @@ def runSubdynDriverCase(inputFile, executable, verbose=False): caseDirectory = os.path.sep.join(inputFile.split(os.path.sep)[:-1]) os.chdir(caseDirectory) return _runGenericCase(inputFile, executable, verbose) + +def runInflowwindDriverCase(inputFile, executable, verbose=False): + caseDirectory = os.path.sep.join(inputFile.split(os.path.sep)[:-1]) + os.chdir(caseDirectory) + return _runGenericCase(inputFile, executable, verbose) diff --git a/reg_tests/r-test b/reg_tests/r-test index 7f554680eb..0d5b0bad64 160000 --- a/reg_tests/r-test +++ b/reg_tests/r-test @@ -1 +1 @@ -Subproject commit 7f554680eb63235fa86fdd095ef1a1624787c3ed +Subproject commit 0d5b0bad64006d00dc77c42d782c4b2e0613728d diff --git a/vs-build/InflowWind_c_binding/InflowWind_c_binding.sln b/vs-build/InflowWind_c_binding/InflowWind_c_binding.sln new file mode 100644 index 0000000000..03d412a05f --- /dev/null +++ b/vs-build/InflowWind_c_binding/InflowWind_c_binding.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = "InflowWind_c_binding", "InflowWind_c_binding.vfproj", "{5D991B19-D4F1-4F29-8A9D-FC36DFF07290}" + ProjectSection(ProjectDependencies) = postProject + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} = {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FAST_Registry", "..\Registry\FAST_Registry.vcxproj", "{DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_Double|Win32 = Debug_Double|Win32 + Debug_Double|x64 = Debug_Double|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release_Double|Win32 = Release_Double|Win32 + Release_Double|x64 = Release_Double|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Debug_Double|Win32.ActiveCfg = Debug_Double|Win32 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Debug_Double|Win32.Build.0 = Debug_Double|Win32 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Debug_Double|x64.ActiveCfg = Debug_Double|x64 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Debug_Double|x64.Build.0 = Debug_Double|x64 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Debug|Win32.ActiveCfg = Debug|Win32 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Debug|Win32.Build.0 = Debug|Win32 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Debug|x64.ActiveCfg = Debug|x64 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Debug|x64.Build.0 = Debug|x64 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Release_Double|Win32.ActiveCfg = Release_Double|Win32 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Release_Double|Win32.Build.0 = Release_Double|Win32 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Release_Double|x64.ActiveCfg = Release_Double|x64 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Release_Double|x64.Build.0 = Release_Double|x64 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Release|Win32.ActiveCfg = Release|Win32 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Release|Win32.Build.0 = Release|Win32 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Release|x64.ActiveCfg = Release|x64 + {5D991B19-D4F1-4F29-8A9D-FC36DFF07290}.Release|x64.Build.0 = Release|x64 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug_Double|x64.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Debug|x64.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release_Double|x64.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|Win32.Build.0 = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.ActiveCfg = Release|Win32 + {DA16A3A6-3297-4628-9E46-C6FA0E3C4D16}.Release|x64.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vs-build/InflowWind_c_binding/InflowWind_c_binding.vfproj b/vs-build/InflowWind_c_binding/InflowWind_c_binding.vfproj new file mode 100644 index 0000000000..6f44061d1d --- /dev/null +++ b/vs-build/InflowWind_c_binding/InflowWind_c_binding.vfproj @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +