diff --git a/.github/workflows/automated-dev-tests.yml b/.github/workflows/automated-dev-tests.yml index 6f9958f135..6cd85bb42e 100644 --- a/.github/workflows/automated-dev-tests.yml +++ b/.github/workflows/automated-dev-tests.yml @@ -204,6 +204,7 @@ jobs: -DVARIABLE_TRACKING=OFF \ -DBUILD_FASTFARM:BOOL=ON \ -DBUILD_OPENFAST_CPP_API:BOOL=ON \ + -DBUILD_OPENFAST_LIB_DRIVER:BOOL=ON \ -DBUILD_OPENFAST_CPP_DRIVER:BOOL=ON \ -DBUILD_SHARED_LIBS:BOOL=OFF \ -DBUILD_TESTING:BOOL=ON \ @@ -248,7 +249,7 @@ jobs: - name: Build OpenFAST C-Interfaces working-directory: ${{runner.workspace}}/openfast/build run: | - cmake --build . --target openfastlib openfast_cpp_driver openfastcpp aerodyn_inflow_c_binding moordyn_c_binding ifw_c_binding hydrodyn_c_binding regression_test_controllers + cmake --build . --target openfastlib openfast_lib_driver openfastcpp aerodyn_inflow_c_binding moordyn_c_binding ifw_c_binding hydrodyn_c_binding regression_test_controllers - name: Cache the workspace uses: actions/cache@v4 with: diff --git a/.github/workflows/build-docker-image-manual.yml b/.github/workflows/build-docker-image-manual.yml index 608bd02307..a017443a06 100644 --- a/.github/workflows/build-docker-image-manual.yml +++ b/.github/workflows/build-docker-image-manual.yml @@ -20,9 +20,9 @@ jobs: timeout-minutes: 300 env: DOCKERFILE_PATH: share/docker/Dockerfile - DOCKERFILE_PERMALINK: https://raw.githubusercontent.com/OpenFAST/openfast/main/share/docker/Dockerfile + DOCKERFILE_PERMALINK: https://raw.githubusercontent.com/openfast/openfast/main/share/docker/Dockerfile DOCKERHUB_REPOSITORY: nrel/openfast - GH_REGISTRY: ghcr.io/OpenFAST/openfast + GH_REGISTRY: ghcr.io/openfast/openfast permissions: contents: read packages: write diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 180969d6fa..f28aee59ce 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -41,7 +41,7 @@ jobs: env: DOCKERFILE_PATH: share/docker/Dockerfile DOCKERHUB_REPOSITORY: nrel/openfast - GH_REGISTRY: ghcr.io/OpenFAST/openfast + GH_REGISTRY: ghcr.io/openfast/openfast permissions: contents: read packages: write @@ -68,7 +68,11 @@ jobs: - name: Extract tag from release candidate branch name id: extract-tag - run: echo "openfast-tag=$(expr substr "${{ github.head_ref }}" 4 100)" >> $GITHUB_OUTPUT + run: | + TAG="${{ github.event.release.tag_name }}" + CLEAN_TAG="${TAG#v}" + echo "openfast-tag=$CLEAN_TAG" >> $GITHUB_OUTPUT + echo "Extracted tag $CLEAN_TAG" - name: Build and push to registry uses: docker/build-push-action@v5 @@ -77,7 +81,7 @@ jobs: file: ${{ env.DOCKERFILE_PATH }} platforms: linux/amd64,linux/aarch64 tags: | - ${{ env.GH_REGISTRY }}:${{ steps.extract-tag.outputs.openfast-tag }},${{ env.DOCKERHUB_REPOSITORY }}:latest + ${{ env.GH_REGISTRY }}:${{ steps.extract-tag.outputs.openfast-tag }},${{ env.GH_REGISTRY }}:latest # ${{ env.DOCKERHUB_REPOSITORY }}:${{ steps.extract-tag.outputs.openfast-tag }},${{ env.DOCKERHUB_REPOSITORY }}:latest push: true cache-from: type=gha diff --git a/CMakeLists.txt b/CMakeLists.txt index 257aa06029..04a042661f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,8 @@ option(FPE_TRAP_ENABLED "Enable FPE trap in compiler options" off) option(ORCA_DLL_LOAD "Enable OrcaFlex Library Load" on) option(BUILD_FASTFARM "Enable building FAST.Farm" off) option(BUILD_OPENFAST_CPP_API "Enable building OpenFAST - C++ API" off) -option(BUILD_OPENFAST_CPP_DRIVER "Enable building OpenFAST C++ driver using C++ API" off) +option(BUILD_OPENFAST_CPP_DRIVER "Enable building OpenFAST C++ driver using C++ CFD API" off) +option(BUILD_OPENFAST_LIB_DRIVER "Enable building OpenFAST driver using C++ Library API" off) option(BUILD_OPENFAST_SIMULINK_API "Enable building OpenFAST for use with Simulink" off) option(OPENMP "Enable OpenMP support" off) option(USE_LOCAL_STATIC_LAPACK "Enable downloading and building static LAPACK and BLAS libs" off) @@ -231,8 +232,8 @@ endforeach(IDIR IN ITEMS ${OPENFAST_MODULES}) add_subdirectory(glue-codes) # Install fortran .mod files also to installation directory -install(DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY}/ - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/openfast/ +install(DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} + DESTINATION include/openfast/ FILES_MATCHING PATTERN "*.mod" ) diff --git a/docs/changelogs/v3.5.5.md b/docs/changelogs/v3.5.5.md new file mode 100644 index 0000000000..0ac516dbc0 --- /dev/null +++ b/docs/changelogs/v3.5.5.md @@ -0,0 +1,107 @@ +**Feature or improvement description** +Pull request to merge `rc-3.5.5` into `main` and create a tagged release for v3.5.5. + +See the milestone and project pages for additional information + + https://github.com/OpenFAST/openfast/milestone/15 + +Test results, if applicable +See GitHub Actions + +### Release checklist: +- [ ] Update the documentation version in docs/conf.py +- [ ] Update the versions in docs/source/user/api_change.rst +- [ ] Verify readthedocs builds correctly +- [ ] Create a tag in OpenFAST +- [ ] Create a merge commit in r-test and add a corresponding annotated tag +- [ ] Compile executables for Windows builds + - [ ] AeroDyn_Driver_x64.exe + - [ ] AeroDyn_Driver_x64_OpenMP.exe + - [ ] AeroDyn_Inflow_C_Binding_x64.dll + - [ ] AeroDyn_Inflow_C_Binding_x64_OpenMP.dll + - [ ] BeamDyn_Driver_x64.exe + - [ ] DISCON.dll (x64) + - [ ] DISCON_ITIBarge.dll (x64) + - [ ] DISCON_OC3Hywind.dll (x64) + - [ ] DISCON_SC.dll (x64) + - [ ] FAST.Farm_x64.exe + - [ ] FAST.Farm_x64_OMP.exe + - [ ] FAST_SFunc.mexw64 + - [ ] HydroDynDriver_x64.exe + - [ ] HydroDyn_C_Binding_x64.dll + - [ ] IfW_C_Binding_x64.dll + - [ ] InflowWind_Driver_x64.exe + - [ ] InflowWind_Driver_x64_OpenMP.exe + - [ ] MoorDyn_Driver_x64.exe + - [ ] MoorDyn_C_Binding_x64.dll + - [ ] OpenFAST-Simulink_x64.dll + - [ ] openfast_x64.exe + - [ ] Turbsim_x64.exe + +# Changelog + +## Overview + +This release includes multiple small bug-fixes for compilation with CMake, compilation with the IFX compilers, file opening issues when OpenMP is used, and a couple of infrequent segmentation faults from improper usage. One minor feature improvement is the increase in the number of output planes available in _FAST.Farm_ from 99 to 999. + +We recommend all users currently using any 3.5.x version to update to this version. There are no input files changes or API changes for calling from other codes since version 3.5.0. + + + +## General + +### Build systems + +#2497 `CMAKE_INSTALL_PREFIX` was incorrectly being prepended to the install direcotry (@deslaughter) Derek Slaughter + +#2564 Create `BUILD_OPENFAST_LIB_DRIVER` flag for the OpenFAST C++ Library Interface (not CFD) (@deslaughter) + + +### Docker + +#2498 Docker: typo was preventing docker build upload to GH (@andrew-platt) + + + +## Solvers + +### FAST.Farm + +#2536 FAST.Farm: increase number of output planes to 999 (@andrew-platt) + +#2554 Add `!$OMP critical` directives around some `GetNewUnit/Open*File` to reduce probability of file unit conflicts (@andrew-platt) + +#2569 Disable LiDAR in IfW at FAST.Farm level (@andrew-platt) + + +## Module changes + +### AeroDyn + +#2501 Remove `$OMP` directives from `AeroDyn_Inflow` due to Intel compiler bug (@deslaughter) + +#2516 AD bugfix: Segmentation fault with ifx compiler (@andrew-platt) + + +### InflowWind + +#2518, #2530 ADI bugfix: BoxExceed was not enabled for OLAF with ADI (@andrew-platt) + +#2532 bugfix: IfW rotor points for disk average incorrect (@andrew-platt) + + +### NWTC-Library + +#2558 Allow ParseVar to parse file paths containing spaces. (@deslaughter) + + + + +## Input file changes + +No input files change with this release as this only includes minor bugfixes (input files are identical across all 3.5.x releases). + +Full list of changes: https://openfast.readthedocs.io/en/main/source/user/api_change.html + +Full input file sets: https://github.com/OpenFAST/r-test/tree/v3.5.5 (example input files from the regression testing) + diff --git a/docs/conf.py b/docs/conf.py index 38764e4a10..ab28d9e418 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -138,7 +138,7 @@ def runDoxygen(sourcfile, doxyfileIn, doxyfileOut): # The short X.Y version. version = u'3.5' # The full version, including alpha/beta/rc tags. -release = u'v3.5.4' +release = u'v3.5.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/user/api_change.rst b/docs/source/user/api_change.rst index 30d3a351f0..28085bea34 100644 --- a/docs/source/user/api_change.rst +++ b/docs/source/user/api_change.rst @@ -11,7 +11,7 @@ Thus, be sure to implement each in order so that subsequent line numbers are cor -OpenFAST v3.5.4 to OpenFAST dev +OpenFAST v3.5.5 to OpenFAST dev ---------------------------------- The HydroDyn module was split into HydroDyn and SeaState. This results in a @@ -105,6 +105,11 @@ Old inputs Corresponding new inputs =========================== ========================================================= +OpenFAST v3.5.4 to OpenFAST v3.5.5 +---------------------------------- + +No input file changes were made. + OpenFAST v3.5.3 to OpenFAST v3.5.4 ---------------------------------- diff --git a/glue-codes/fast-farm/src/FAST_Farm_IO.f90 b/glue-codes/fast-farm/src/FAST_Farm_IO.f90 index 8b7937b2a4..8e544a91d5 100644 --- a/glue-codes/fast-farm/src/FAST_Farm_IO.f90 +++ b/glue-codes/fast-farm/src/FAST_Farm_IO.f90 @@ -10,7 +10,7 @@ module FAST_Farm_IO TYPE(ProgDesc), PARAMETER :: Farm_Ver = ProgDesc( 'FAST.Farm', '', '' ) !< module date/version information integer, parameter :: maxOutputPoints = 9 - integer, parameter :: maxOutputPlanes = 99 ! Allow up to 99 outpt planes + integer, parameter :: maxOutputPlanes = 999 ! Allow up to 99 outpt planes contains @@ -871,17 +871,17 @@ SUBROUTINE Farm_ReadPrimaryFile( InputFile, p, WD_InitInp, AWAE_InitInp, SC_Init CALL ReadVar( UnIn, InputFile, AWAE_InitInp%WrDisWind, "WrDisWind", "Write disturbed wind data to .Low.Dis.t.vtk etc.? (flag)", ErrStat2, ErrMsg2, UnEc); if (Failed()) return ! XY planes - CALL ReadVar( UnIn, InputFile, AWAE_InitInp%NOutDisWindXY, "NOutDisWindXY", "Number of XY planes for output of disturbed wind data across the low-resolution domain to .Low.DisXY..t.vtk (-) [0 to 99]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return + CALL ReadVar( UnIn, InputFile, AWAE_InitInp%NOutDisWindXY, "NOutDisWindXY", "Number of XY planes for output of disturbed wind data across the low-resolution domain to .Low.DisXY..t.vtk (-) [0 to 999]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return call allocAry( AWAE_InitInp%OutDisWindZ, AWAE_InitInp%NOutDisWindXY, "OutDisWindZ", ErrStat2, ErrMsg2 ); if (Failed()) return CALL ReadAry( UnIn, InputFile, AWAE_InitInp%OutDisWindZ, AWAE_InitInp%NOutDisWindXY, "OutDisWindZ", "Z coordinates of XY planes for output of disturbed wind data across the low-resolution domain (m) [1 to NOutDisWindXY] [unused for NOutDisWindXY=0]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return ! YZ planes - CALL ReadVar( UnIn, InputFile, AWAE_InitInp%NOutDisWindYZ, "NOutDisWindYZ", "Number of YZ planes for output of disturbed wind data across the low-resolution domain to .Low.DisYZ..t.vtk (-) [0 to 9]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return + CALL ReadVar( UnIn, InputFile, AWAE_InitInp%NOutDisWindYZ, "NOutDisWindYZ", "Number of YZ planes for output of disturbed wind data across the low-resolution domain to .Low.DisYZ..t.vtk (-) [0 to 999]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return call allocAry( AWAE_InitInp%OutDisWindX, AWAE_InitInp%NOutDisWindYZ, "OutDisWindX", ErrStat2, ErrMsg2 ); if (Failed()) return CALL ReadAry( UnIn, InputFile, AWAE_InitInp%OutDisWindX, AWAE_InitInp%NOutDisWindYZ, "OutDisWindX", "X coordinates of YZ planes for output of disturbed wind data across the low-resolution domain (m) [1 to NOutDisWindYZ] [unused for NOutDisWindYZ=0]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return ! XZ planes - CALL ReadVar( UnIn, InputFile, AWAE_InitInp%NOutDisWindXZ, "NOutDisWindXZ", "Number of XZ planes for output of disturbed wind data across the low-resolution domain to .Low/DisXZ..t.vtk (-) [0 to 9]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return + CALL ReadVar( UnIn, InputFile, AWAE_InitInp%NOutDisWindXZ, "NOutDisWindXZ", "Number of XZ planes for output of disturbed wind data across the low-resolution domain to .Low/DisXZ..t.vtk (-) [0 to 999]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return call allocAry( AWAE_InitInp%OutDisWindY, AWAE_InitInp%NOutDisWindXZ, "OutDisWindY", ErrStat2, ErrMsg2 ); if (Failed()) return CALL ReadAry( UnIn, InputFile, AWAE_InitInp%OutDisWindY, AWAE_InitInp%NOutDisWindXZ, "OutDisWindY", "Y coordinates of XZ planes for output of disturbed wind data across the low-resolution domain (m) [1 to NOutDisWindXZ] [unused for NOutDisWindXZ=0]", ErrStat2, ErrMsg2, UnEc); if (Failed()) return @@ -1117,9 +1117,9 @@ SUBROUTINE Farm_ValidateInput( p, WD_InitInp, AWAE_InitInp, SC_InitInp, ErrStat, AWAE_InitInp%WrDisDT = p%DT_low * n_disDT_dt - if (AWAE_InitInp%NOutDisWindXY < 0 .or. AWAE_InitInp%NOutDisWindXY > maxOutputPlanes ) CALL SetErrStat( ErrID_Fatal, 'NOutDisWindXY must be in the range [0, 99].', ErrStat, ErrMsg, RoutineName ) - if (AWAE_InitInp%NOutDisWindYZ < 0 .or. AWAE_InitInp%NOutDisWindYZ > maxOutputPlanes ) CALL SetErrStat( ErrID_Fatal, 'NOutDisWindYZ must be in the range [0, 99].', ErrStat, ErrMsg, RoutineName ) - if (AWAE_InitInp%NOutDisWindXZ < 0 .or. AWAE_InitInp%NOutDisWindXZ > maxOutputPlanes ) CALL SetErrStat( ErrID_Fatal, 'NOutDisWindXZ must be in the range [0, 99].', ErrStat, ErrMsg, RoutineName ) + if (AWAE_InitInp%NOutDisWindXY < 0 .or. AWAE_InitInp%NOutDisWindXY > maxOutputPlanes ) CALL SetErrStat( ErrID_Fatal, 'NOutDisWindXY must be in the range [0, 999].', ErrStat, ErrMsg, RoutineName ) + if (AWAE_InitInp%NOutDisWindYZ < 0 .or. AWAE_InitInp%NOutDisWindYZ > maxOutputPlanes ) CALL SetErrStat( ErrID_Fatal, 'NOutDisWindYZ must be in the range [0, 999].', ErrStat, ErrMsg, RoutineName ) + if (AWAE_InitInp%NOutDisWindXZ < 0 .or. AWAE_InitInp%NOutDisWindXZ > maxOutputPlanes ) CALL SetErrStat( ErrID_Fatal, 'NOutDisWindXZ must be in the range [0, 999].', ErrStat, ErrMsg, RoutineName ) if (p%NOutDist < 0 .or. p%NOutDist > maxOutputPoints ) then CALL SetErrStat( ErrID_Fatal, 'NOutDist must be in the range [0, 9].', ErrStat, ErrMsg, RoutineName ) else diff --git a/glue-codes/openfast/CMakeLists.txt b/glue-codes/openfast/CMakeLists.txt index bc2e132339..a742b5972a 100644 --- a/glue-codes/openfast/CMakeLists.txt +++ b/glue-codes/openfast/CMakeLists.txt @@ -33,11 +33,11 @@ endif() install(TARGETS openfast RUNTIME DESTINATION bin) -if(BUILD_OPENFAST_CPP_DRIVER) - add_executable(openfast_cpp_driver src/FAST_Prog.cpp src/FastLibAPI.cpp) - target_link_libraries(openfast_cpp_driver openfastlib) +if(BUILD_OPENFAST_LIB_DRIVER) + add_executable(openfast_lib_driver src/FAST_Prog.cpp src/FastLibAPI.cpp) + target_link_libraries(openfast_lib_driver openfastlib) - install(TARGETS openfast_cpp_driver + install(TARGETS openfast_lib_driver RUNTIME DESTINATION bin) endif() diff --git a/modules/aerodisk/src/AeroDisk.f90 b/modules/aerodisk/src/AeroDisk.f90 index 13de16bfbd..e6257ac2d7 100644 --- a/modules/aerodisk/src/AeroDisk.f90 +++ b/modules/aerodisk/src/AeroDisk.f90 @@ -172,9 +172,9 @@ subroutine SetDiskAvgPoints(ErrStat3,ErrMsg3) R = real(p%RotorRad,ReKi) * 0.7_reKi !70% radius do i=1,ADsk_NumPtsDiskAvg theta = pi +(i-1)*TwoPi/ADsk_NumPtsDiskAvg - p%DiskWindPosRel(1,i) = R*cos(theta) - p%DiskWindPosRel(2,i) = R*sin(theta) - p%DiskWindPosRel(3,i) = 0.0_ReKi + p%DiskWindPosRel(1,i) = 0.0_ReKi ! Hub X (perpindicular to rotor plane) + p%DiskWindPosRel(2,i) = R*cos(theta) ! Hub Y + p%DiskWindPosRel(3,i) = R*sin(theta) ! Hub Z (in vertical plane when azimuth=0) end do end subroutine SetDiskAvgPoints diff --git a/modules/aerodyn/CMakeLists.txt b/modules/aerodyn/CMakeLists.txt index 859dd1e155..3ace5a80d3 100644 --- a/modules/aerodyn/CMakeLists.txt +++ b/modules/aerodyn/CMakeLists.txt @@ -101,7 +101,7 @@ target_link_libraries(unsteadyaero_driver basicaerolib lindynlib versioninfolib) add_library(aerodyn_inflow_c_binding SHARED src/AeroDyn_Inflow_C_Binding.f90 ) -target_link_libraries(aerodyn_inflow_c_binding adilib aerodyn_driver_subs versioninfolib) +target_link_libraries(aerodyn_inflow_c_binding aerodyn_driver_subs versioninfolib) if(APPLE OR UNIX) target_compile_definitions(aerodyn_inflow_c_binding PRIVATE IMPLICIT_DLLEXPORT) endif() diff --git a/modules/aerodyn/python-lib/aerodyn_inflow_library.py b/modules/aerodyn/python-lib/aerodyn_inflow_library.py index fe3cf33d9d..a0cae3d526 100644 --- a/modules/aerodyn/python-lib/aerodyn_inflow_library.py +++ b/modules/aerodyn/python-lib/aerodyn_inflow_library.py @@ -70,8 +70,8 @@ def __init__(self, library_path): self.ended = False # For error handling at end # Input file handling - self.ADinputPass = True # Assume passing of input file as a string - self.IfWinputPass = True # Assume passing of input file as a string + self.ADinputPass = 1 # Assume passing of input file as a string + self.IfWinputPass = 1 # Assume passing of input file as a string # Create buffers for class data self.abort_error_level = 4 @@ -107,6 +107,7 @@ def __init__(self, library_path): # VTK self.WrVTK = 0 # default of no vtk output self.WrVTK_Type = 1 # default of surface meshes + self.WrVTK_DT = 0.0 # default to all self.VTKNacDim = np.array([-2.5,-2.5,0,10,5,5], dtype="float32") # default nacelle dimension for VTK surface rendering [x0,y0,z0,Lx,Ly,Lz] (m) self.VTKHubRad = 1.5 # default hub radius for VTK surface rendering @@ -156,6 +157,7 @@ def __init__(self, library_path): # If HD writes a file (echo, summary, or other), use this for the # root of the file name. self.outRootName = "Output_ADIlib_default" + self.outVTKdir = "" # Set to specify a directory relative to the input files (created if doesn't exist) # _initialize_routines() ------------------------------------------------------------------------------------------------------------ def _initialize_routines(self): @@ -199,6 +201,7 @@ def _initialize_routines(self): POINTER(c_char_p), # IfW input file as string POINTER(c_int), # IfW input file string length POINTER(c_char), # OutRootName + POINTER(c_char), # OutVTKdir POINTER(c_float), # gravity POINTER(c_float), # defFldDens POINTER(c_float), # defKinVisc @@ -213,6 +216,7 @@ def _initialize_routines(self): POINTER(c_int), # storeHHVel POINTER(c_int), # WrVTK POINTER(c_int), # WrVTK_Type + POINTER(c_double), # WrVTK_DT -- 0 or negative to do every step POINTER(c_float), # VTKNacDim POINTER(c_float), # VTKHubRad POINTER(c_int), # wrOuts -- file format for writing outputs @@ -267,6 +271,15 @@ def _initialize_routines(self): self.ADI_C_GetRotorLoads.restype = c_int + self.ADI_C_GetDiskAvgVel.argtypes = [ + POINTER(c_int), # iturb + POINTER(c_float), # Disk average vel vector + POINTER(c_int), # ErrStat_C + POINTER(c_char) # ErrMsg_C + ] + self.ADI_C_GetDiskAvgVel.restype = c_int + + self.ADI_C_CalcOutput.argtypes = [ POINTER(c_double), # Time_C POINTER(c_float), # Output Channel Values @@ -370,6 +383,7 @@ def adi_init(self, AD_input_string_array, IfW_input_string_array): # Rootname for ADI output files (echo etc). _outRootName_c = create_string_buffer((self.outRootName.ljust(self.default_str_c_len)).encode('utf-8')) + _outVTKdir_c = create_string_buffer((self.outVTKdir.ljust(self.default_str_c_len)).encode('utf-8')) # Flatten arrays to pass # [x2,y1,z1, x2,y2,z2 ...] @@ -384,6 +398,7 @@ def adi_init(self, AD_input_string_array, IfW_input_string_array): c_char_p(IfW_input_string), # IN: IfW input file as string (or filename if IfWinputPass is false) byref(c_int(IfW_input_string_length)), # IN: IfW input file string length _outRootName_c, # IN: rootname for ADI file writing + _outVTKdir_c, # IN: directory for vtk output files (relative to input file) byref(c_float(self.gravity)), # IN: gravity byref(c_float(self.defFldDens)), # IN: defFldDens byref(c_float(self.defKinVisc)), # IN: defKinVisc @@ -398,6 +413,7 @@ def adi_init(self, AD_input_string_array, IfW_input_string_array): byref(c_int(self.storeHHVel)), # IN: storeHHVel byref(c_int(self.WrVTK)), # IN: WrVTK byref(c_int(self.WrVTK_Type)), # IN: WrVTK_Type + byref(c_double(self.WrVTK_DT)), # IN: WrVTK_DT VTKNacDim_c, # IN: VTKNacDim byref(c_float(self.VTKHubRad)), # IN: VTKHubRad byref(c_int(self.wrOuts)), # IN: wrOuts -- file format for writing outputs @@ -490,7 +506,7 @@ def adi_setrotormotion(self, iturb, \ self.check_error() - # adi_calcOutput ------------------------------------------------------------------------------------------------------------ + # adi_getrotorloads --------------------------------------------------------------------------------------------------------- def adi_getrotorloads(self, iturb, meshFrcMom, hhVel=None): # Resulting Forces/moments -- [Fx1,Fy1,Fz1,Mx1,My1,Mz1, Fx2,Fy2,Fz2,Mx2,My2,Mz2 ...] _meshFrc_flat_c = (c_float * (6 * self.numMeshPts))(0.0,) @@ -525,6 +541,28 @@ def adi_getrotorloads(self, iturb, meshFrcMom, hhVel=None): hhVel[1] = _hhVel_flat_c[1] hhVel[2] = _hhVel_flat_c[2] + + # adi_getdiskavgvel --------------------------------------------------------------------------------------------------------- + def adi_getdiskavgvel(self, iturb, diskAvgVel): + # Resulting disk average velocity [Vx,Vy,Vz] + _diskAvgVel_flat_c = (c_float * 3)(0.0,) + + # Run ADI_GetDiskAvgVel + self.ADI_C_GetDiskAvgVel( + c_int(iturb), # IN: iturb -- current turbine number + _diskAvgVel_flat_c, # OUT: disk average velocity [Vx, Vy, Vz] + byref(self.error_status_c), # OUT: ErrStat_C + self.error_message_c # OUT: ErrMsg_C + ) + + self.check_error() + + ## Disk average wind speed + diskAvgVel[0] = _diskAvgVel_flat_c[0] + diskAvgVel[1] = _diskAvgVel_flat_c[1] + diskAvgVel[2] = _diskAvgVel_flat_c[2] + + # adi_calcOutput ------------------------------------------------------------------------------------------------------------ def adi_calcOutput(self, time, outputChannelValues): @@ -907,6 +945,9 @@ def __init__(self,filename,numMeshPts): self.DbgFile.write(f_string.format(f_num+"Mx" )) self.DbgFile.write(f_string.format(f_num+"My" )) self.DbgFile.write(f_string.format(f_num+"Mz" )) + self.DbgFile.write(f_string.format(f_num+"DskAvgVx" )) + self.DbgFile.write(f_string.format(f_num+"DskAvgVy" )) + self.DbgFile.write(f_string.format(f_num+"DskAvgVz" )) self.DbgFile.write("\n") self.DbgFile.write(" (s) ") for i in range(1,self.numMeshPts+1): @@ -934,10 +975,13 @@ def __init__(self,filename,numMeshPts): self.DbgFile.write(f_string.format("(N-m)" )) self.DbgFile.write(f_string.format("(N-m)" )) self.DbgFile.write(f_string.format("(N-m)" )) + self.DbgFile.write(f_string.format("(m/s)" )) + self.DbgFile.write(f_string.format("(m/s)" )) + self.DbgFile.write(f_string.format("(m/s)" )) self.DbgFile.write("\n") self.opened = True - def write(self,t,meshPos,meshVel,meshAcc,meshFrc): + def write(self,t,meshPos,meshVel,meshAcc,meshFrc,DiskAvgVel): t_string = "{:10.4f}" f_string3 = "{:25.7e}"*3 f_string6 = "{:25.7e}"*6 @@ -947,6 +991,7 @@ def write(self,t,meshPos,meshVel,meshAcc,meshFrc): self.DbgFile.write(f_string6.format(*meshVel[i,:])) self.DbgFile.write(f_string6.format(*meshAcc[i,:])) self.DbgFile.write(f_string6.format(*meshFrc[i,:])) + self.DbgFile.write(f_string3.format(*DiskAvgVel[:])) self.DbgFile.write("\n") def end(self): diff --git a/modules/aerodyn/src/AeroDyn.f90 b/modules/aerodyn/src/AeroDyn.f90 index d43664041a..6ed9b86c50 100644 --- a/modules/aerodyn/src/AeroDyn.f90 +++ b/modules/aerodyn/src/AeroDyn.f90 @@ -1763,8 +1763,10 @@ subroutine AD_UpdateStates( t, n, u, utimes, p, x, xd, z, OtherState, m, errStat else ! Call the FVW sub module ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module - call SetInputsForFVW(p, u, m, errStat2, errMsg2) - if (Failed()) return + do i=1,size(u) + call SetInputsForFVW(p, u(i), i, m, errStat2, errMsg2) + if (Failed()) return + enddo ! Note: the setup is handled above in the SetInputs routine call FVW_UpdateStates( t, n, m%FVW_u, utimes, p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, p%AFI, m%FVW, ErrStat2, ErrMsg2 ) if (Failed()) return @@ -1929,7 +1931,7 @@ subroutine AD_CalcWind_Rotor(t, u, FlowField, p, RotInflow, StartNode, ErrStat, contains logical function Failed() - call SetErrStat(errStat2, errMsg2, errStat, errMsg, 'AD_CalcWind') + call SetErrStat(errStat2, errMsg2, errStat, errMsg, 'AD_CalcWindRotor') Failed = errStat >= AbortErrLev end function Failed end subroutine @@ -1989,7 +1991,7 @@ subroutine AD_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, ErrStat, ErrMsg, if (p%Wake_Mod == WakeMod_FVW) then ! This needs to extract the inputs from the AD data types (mesh) and copy pieces for the FVW module - call SetInputsForFVW(p, (/u/), m, errStat2, errMsg2) + call SetInputsForFVW(p, u, 1, m, errStat2, errMsg2) if(Failed()) return ! Calculate Outputs at time t CALL FVW_CalcOutput( t, m%FVW_u(1), p%FVW, x%FVW, xd%FVW, z%FVW, OtherState%FVW, m%FVW_y, m%FVW, ErrStat2, ErrMsg2 ) @@ -3574,10 +3576,11 @@ subroutine Calculate_MeshOrientation_LiftingLine(p, u, m, twist, toe, cant, ErrS end subroutine Calculate_MeshOrientation_LiftingLine !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine sets m%FVW_u(indx). -subroutine SetInputsForFVW(p, u, m, errStat, errMsg) +subroutine SetInputsForFVW(p, u, tIndx, m, errStat, errMsg) type(AD_ParameterType), intent(in ) :: p !< AD parameters - type(AD_InputType), intent(in ) :: u(:) !< AD Inputs at Time + type(AD_InputType), intent(in ) :: u !< AD Inputs at Time + integer(intKi), intent(in ) :: tIndx !< index of m%FVW_u() array type(AD_MiscVarType), intent(inout) :: m !< Misc/optimization variables integer(IntKi), intent( out) :: ErrStat !< Error status of the operation character(*), intent( out) :: ErrMsg !< Error message if ErrStat /= ErrID_None @@ -3585,7 +3588,6 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) real(R8Ki) :: x_hat_disk(3) real(R8Ki), allocatable :: thetaBladeNds(:,:) - integer(intKi) :: tIndx integer(intKi) :: iR ! Loop on rotors integer(intKi) :: j, k ! loop counter for blades integer(intKi) :: ErrStat2 @@ -3596,82 +3598,80 @@ subroutine SetInputsForFVW(p, u, m, errStat, errMsg) ErrStat = ErrID_None ErrMsg = "" - do tIndx=1,size(u) - do iR =1, size(p%rotors) - allocate(thetaBladeNds(p%rotors(iR)%NumBlNds, p%rotors(iR)%NumBlades)) - ! Get disk average values and orientations - ! NOTE: needed because it sets m%V_diskAvg and m%V_dot_x, needed by CalcOutput.. - call DiskAvgValues(p%rotors(iR), u(tIndx)%rotors(iR), m%Inflow(tIndx)%RotInflow(iR), m%rotors(iR), x_hat_disk) ! also sets m%V_diskAvg and m%V_dot_x - - ! Compute Orientation similar to BEM, only to have consistent outputs... - ! TODO TODO TODO All this below is mostly a calcOutput thing, we should move it somewhere else! - ! orientation annulus is only used for Outputs with OLAF, same for pitch and azimuth - if (p%rotors(iR)%AeroProjMod==APM_BEM_NoSweepPitchTwist) then - call Calculate_MeshOrientation_NoSweepPitchTwist(p%rotors(iR), u(tIndx)%rotors(iR), m%rotors(iR), thetaBladeNds, m%rotors(iR)%Toe, m%rotors(iR)%Cant, ErrStat=ErrStat2,ErrMsg=ErrMsg2) ! sets m%orientationAnnulus, m%Curve - - elseif (p%rotors(iR)%AeroProjMod==APM_BEM_Polar) then - do k=1,p%rotors(iR)%numBlades - call Calculate_MeshOrientation_Rel2Hub(u(tIndx)%rotors(iR)%BladeMotion(k), u(tIndx)%rotors(iR)%HubMotion, x_hat_disk, m%rotors(iR)%orientationAnnulus(:,:,:,k)) - call TwistToeCant_FromLocalPolar(u(tIndx)%rotors(iR)%BladeMotion(k), m%rotors(iR)%orientationAnnulus(:,:,:,k), thetaBladeNds(:,k), m%rotors(iR)%Toe(:,k), m%rotors(iR)%Cant(:,k)) - enddo + do iR =1, size(p%rotors) + allocate(thetaBladeNds(p%rotors(iR)%NumBlNds, p%rotors(iR)%NumBlades)) + ! Get disk average values and orientations + ! NOTE: needed because it sets m%V_diskAvg and m%V_dot_x, needed by CalcOutput.. + call DiskAvgValues(p%rotors(iR), u%rotors(iR), m%Inflow(tIndx)%RotInflow(iR), m%rotors(iR), x_hat_disk) ! also sets m%V_diskAvg and m%V_dot_x + + ! Compute Orientation similar to BEM, only to have consistent outputs... + ! TODO TODO TODO All this below is mostly a calcOutput thing, we should move it somewhere else! + ! orientation annulus is only used for Outputs with OLAF, same for pitch and azimuth + if (p%rotors(iR)%AeroProjMod==APM_BEM_NoSweepPitchTwist) then + call Calculate_MeshOrientation_NoSweepPitchTwist(p%rotors(iR), u%rotors(iR), m%rotors(iR), thetaBladeNds, m%rotors(iR)%Toe, m%rotors(iR)%Cant, ErrStat=ErrStat2,ErrMsg=ErrMsg2) ! sets m%orientationAnnulus, m%Curve + + elseif (p%rotors(iR)%AeroProjMod==APM_BEM_Polar) then + do k=1,p%rotors(iR)%numBlades + call Calculate_MeshOrientation_Rel2Hub(u%rotors(iR)%BladeMotion(k), u%rotors(iR)%HubMotion, x_hat_disk, m%rotors(iR)%orientationAnnulus(:,:,:,k)) + call TwistToeCant_FromLocalPolar(u%rotors(iR)%BladeMotion(k), m%rotors(iR)%orientationAnnulus(:,:,:,k), thetaBladeNds(:,k), m%rotors(iR)%Toe(:,k), m%rotors(iR)%Cant(:,k)) + enddo - else if (p%rotors(iR)%AeroProjMod==APM_LiftingLine) then - call Calculate_MeshOrientation_LiftingLine (p%rotors(iR),u(tIndx)%rotors(iR), m%rotors(iR), thetaBladeNds, m%rotors(iR)%Toe, m%rotors(iR)%Cant, ErrStat=ErrStat2,ErrMsg=ErrMsg2) ! sets m%orientationAnnulus, m%Curve - else - call SetErrStat(ErrID_Fatal, 'Aero Projection Method not implemented' ,ErrStat, ErrMsg, RoutineName) + else if (p%rotors(iR)%AeroProjMod==APM_LiftingLine) then + call Calculate_MeshOrientation_LiftingLine (p%rotors(iR),u%rotors(iR), m%rotors(iR), thetaBladeNds, m%rotors(iR)%Toe, m%rotors(iR)%Cant, ErrStat=ErrStat2,ErrMsg=ErrMsg2) ! sets m%orientationAnnulus, m%Curve + else + call SetErrStat(ErrID_Fatal, 'Aero Projection Method not implemented' ,ErrStat, ErrMsg, RoutineName) + endif + call StorePitchAndAzimuth(p%rotors(iR), u%rotors(iR), m%rotors(iR), ErrStat2, ErrMsg2) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return + + ! Rather than use a meshcopy, we will just copy what we need to the WingsMesh + ! NOTE: MeshCopy requires the source mesh to be INOUT intent + ! NOTE2: If we change the WingsMesh to not be identical to the BladeMotion mesh, add the mapping stuff here. + do k=1,p%rotors(iR)%NumBlades + iW=p%FVW%Bld2Wings(iR,k) + + if ( u%rotors(iR)%BladeMotion(k)%nNodes /= m%FVW_u(tIndx)%WingsMesh(iW)%nNodes ) then + call SetErrStat(ErrID_Fatal,"WingsMesh contains different number of nodes than the BladeMotion mesh",ErrStat,ErrMsg,RoutineName) + return endif - call StorePitchAndAzimuth(p%rotors(iR), u(tIndx)%rotors(iR), m%rotors(iR), ErrStat2, ErrMsg2) - call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - if (ErrStat >= AbortErrLev) return - - ! Rather than use a meshcopy, we will just copy what we need to the WingsMesh - ! NOTE: MeshCopy requires the source mesh to be INOUT intent - ! NOTE2: If we change the WingsMesh to not be identical to the BladeMotion mesh, add the mapping stuff here. - do k=1,p%rotors(iR)%NumBlades - iW=p%FVW%Bld2Wings(iR,k) - - if ( u(tIndx)%rotors(iR)%BladeMotion(k)%nNodes /= m%FVW_u(tIndx)%WingsMesh(iW)%nNodes ) then - call SetErrStat(ErrID_Fatal,"WingsMesh contains different number of nodes than the BladeMotion mesh",ErrStat,ErrMsg,RoutineName) - return + m%FVW%W(iW)%PitchAndTwist(:) = thetaBladeNds(:,k) ! local pitch + twist (aerodyanmic + elastic) angle of the jth node in the kth blade + m%FVW_u(tIndx)%WingsMesh(iW)%TranslationDisp = u%rotors(iR)%BladeMotion(k)%TranslationDisp + m%FVW_u(tIndx)%WingsMesh(iW)%Orientation = u%rotors(iR)%BladeMotion(k)%Orientation + m%FVW_u(tIndx)%WingsMesh(iW)%TranslationVel = u%rotors(iR)%BladeMotion(k)%TranslationVel + m%FVW_u(tIndx)%rotors(iR)%HubPosition = u%rotors(iR)%HubMotion%Position(:,1) + u%rotors(iR)%HubMotion%TranslationDisp(:,1) + m%FVW_u(tIndx)%rotors(iR)%HubOrientation = u%rotors(iR)%HubMotion%Orientation(:,:,1) + + ! Inputs for dynamic stall (see SetInputsForBEMT) + do j=1,p%rotors(iR)%NumBlNds + ! inputs for CUA, section pitch/torsion rate + m%FVW_u(tIndx)%W(iW)%omega_z(j) = dot_product( u%rotors(iR)%BladeMotion(k)%RotationVel( :,j), m%rotors(iR)%orientationAnnulus(3,:,j,k) ) ! rotation of no-sweep-pitch coordinate system around z of the jth node in the kth blade + end do !j=nodes + enddo ! k blades + if (allocated(thetaBladeNds)) deallocate(thetaBladeNds) + enddo ! iR, rotors + + if (ALLOCATED(m%FVW_u(tIndx)%V_wind)) then + m%FVW_u(tIndx)%V_wind = m%Inflow(tIndx)%InflowWakeVel + ! Applying tower shadow to V_wind based on r_wind positions + ! NOTE: m%DisturbedInflow also contains tower shadow and we need it for CalcOutput + if (p%FVW%TwrShadowOnWake) then + do iR =1, size(p%rotors) + if (p%rotors(iR)%TwrPotent /= TwrPotent_none .or. p%rotors(iR)%TwrShadow /= TwrShadow_none) then + call TwrInflArray( p%rotors(iR), u%rotors(iR), m%Inflow(tIndx)%RotInflow(iR), m%rotors(iR), m%FVW%r_wind, m%FVW_u(tIndx)%V_wind, ErrStat2, ErrMsg2 ) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + if (ErrStat >= AbortErrLev) return endif - m%FVW%W(iW)%PitchAndTwist(:) = thetaBladeNds(:,k) ! local pitch + twist (aerodyanmic + elastic) angle of the jth node in the kth blade - m%FVW_u(tIndx)%WingsMesh(iW)%TranslationDisp = u(tIndx)%rotors(iR)%BladeMotion(k)%TranslationDisp - m%FVW_u(tIndx)%WingsMesh(iW)%Orientation = u(tIndx)%rotors(iR)%BladeMotion(k)%Orientation - m%FVW_u(tIndx)%WingsMesh(iW)%TranslationVel = u(tIndx)%rotors(iR)%BladeMotion(k)%TranslationVel - m%FVW_u(tIndx)%rotors(iR)%HubPosition = u(tIndx)%rotors(iR)%HubMotion%Position(:,1) + u(tIndx)%rotors(iR)%HubMotion%TranslationDisp(:,1) - m%FVW_u(tIndx)%rotors(iR)%HubOrientation = u(tIndx)%rotors(iR)%HubMotion%Orientation(:,:,1) - - ! Inputs for dynamic stall (see SetInputsForBEMT) - do j=1,p%rotors(iR)%NumBlNds - ! inputs for CUA, section pitch/torsion rate - m%FVW_u(tIndx)%W(iW)%omega_z(j) = dot_product( u(tIndx)%rotors(iR)%BladeMotion(k)%RotationVel( :,j), m%rotors(iR)%orientationAnnulus(3,:,j,k) ) ! rotation of no-sweep-pitch coordinate system around z of the jth node in the kth blade - end do !j=nodes - enddo ! k blades - if (allocated(thetaBladeNds)) deallocate(thetaBladeNds) - enddo ! iR, rotors - - if (ALLOCATED(m%FVW_u(tIndx)%V_wind)) then - m%FVW_u(tIndx)%V_wind = m%Inflow(tIndx)%InflowWakeVel - ! Applying tower shadow to V_wind based on r_wind positions - ! NOTE: m%DisturbedInflow also contains tower shadow and we need it for CalcOutput - if (p%FVW%TwrShadowOnWake) then - do iR =1, size(p%rotors) - if (p%rotors(iR)%TwrPotent /= TwrPotent_none .or. p%rotors(iR)%TwrShadow /= TwrShadow_none) then - call TwrInflArray( p%rotors(iR), u(tIndx)%rotors(iR), m%Inflow(tIndx)%RotInflow(iR), m%rotors(iR), m%FVW%r_wind, m%FVW_u(tIndx)%V_wind, ErrStat2, ErrMsg2 ) - call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - if (ErrStat >= AbortErrLev) return - endif - enddo - end if - endif - do iR =1, size(p%rotors) - ! Disturbed inflow for UA on Lifting line Mesh Points - call SetDisturbedInflow(p%rotors(iR), p, u(tIndx)%rotors(iR), m%Inflow(tIndx)%RotInflow(iR), m%rotors(iR), errStat2, errMsg2) - call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) - do k=1,p%rotors(iR)%NumBlades - iW=p%FVW%Bld2Wings(iR,k) - m%FVW_u(tIndx)%W(iW)%Vwnd_LL(1:3,:) = m%rotors(iR)%DisturbedInflow(1:3,:,k) enddo + end if + endif + do iR =1, size(p%rotors) + ! Disturbed inflow for UA on Lifting line Mesh Points + call SetDisturbedInflow(p%rotors(iR), p, u%rotors(iR), m%Inflow(tIndx)%RotInflow(iR), m%rotors(iR), errStat2, errMsg2) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) + do k=1,p%rotors(iR)%NumBlades + iW=p%FVW%Bld2Wings(iR,k) + m%FVW_u(tIndx)%W(iW)%Vwnd_LL(1:3,:) = m%rotors(iR)%DisturbedInflow(1:3,:,k) enddo enddo diff --git a/modules/aerodyn/src/AeroDyn_Driver_Registry.txt b/modules/aerodyn/src/AeroDyn_Driver_Registry.txt index f36db0bc02..47c55804f2 100644 --- a/modules/aerodyn/src/AeroDyn_Driver_Registry.txt +++ b/modules/aerodyn/src/AeroDyn_Driver_Registry.txt @@ -45,17 +45,18 @@ typedef ^ ^ character(25) Fmt_a typedef ^ ^ character(1) delim - - - "column delimiter" "-" typedef ^ ^ character(20) outFmt - - - "Format specifier" "-" typedef ^ ^ IntKi fileFmt - - - "Output format 1=Text, 2=Binary, 3=Both" "-" -typedef ^ ^ IntKi wrVTK - - - "0= no vtk, 1=init only, 2=animation" "-" +typedef ^ ^ IntKi WrVTK - - - "0= no vtk, 1=init only, 2=animation" "-" typedef ^ ^ IntKi WrVTK_Type - - - "Flag for VTK output type (1=surface, 2=line, 3=both)" - typedef ^ ^ character(1024) Root - - - "Output file rootname" "-" -typedef ^ ^ character(1024) VTK_OutFileRoot - - - "Output file rootname for vtk" "-" +typedef ^ ^ character(1024) VTK_OutFileRoot - - - "Output file rootname for vtk (includes directory)" "-" typedef ^ ^ character(ChanLen) WriteOutputHdr {:} - - "Channel headers" "-" typedef ^ ^ character(ChanLen) WriteOutputUnt {:} - - "Channel units" "-" typedef ^ ^ ReKi storage ::: - - "nTurbines x nChannel x nTime" typedef ^ ^ ReKi outLine : - - "Output line to be written to disk" typedef ^ ^ DvrVTK_SurfaceType VTK_surface : - - "Data for VTK surface visualization" -typedef ^ ^ INTEGER VTK_tWidth - - - "Width of number of files for leading zeros in file name format" - -typedef ^ ^ INTEGER n_VTKTime - - - "Number of time steps between writing VTK files" - +typedef ^ ^ IntKi VTK_tWidth - - - "Width of number of files for leading zeros in file name format" - +typedef ^ ^ IntKi n_VTKTime - - - "Number of time steps between writing VTK files" - +typedef ^ ^ DbKi VTK_DT - - - "Write VTK time step" - typedef ^ ^ SiKi VTKHubRad - - - "Hub radius for visualization" m typedef ^ ^ ReKi VTKNacDim 6 - - "Nacelle dimensions for visualization" m typedef ^ ^ SiKi VTKRefPoint 3 - - "RefPoint for VTK outputs" @@ -149,7 +150,7 @@ typedef ^ ^ IntKi iCase typedef ^ ^ ReKi timeSeries :: - - "Times series inputs when AnalysisType=1, 6 columns, Time, WndSpeed, ShearExp, RotSpd, Pitch, Yaw" "-" typedef ^ ^ IntKi iTimeSeries - - - "Stored index to optimize time interpolation" - typedef ^ ^ character(1024) root - - - "Output file rootname" "-" -typedef ^ ^ Dvr_Outputs out - - - "data for driver output file" "-" +typedef ^ ^ Dvr_Outputs out - - - "data for driver output file" "-" typedef ^ ^ ADI_IW_InputData IW_InitInp - - - "" - # ..... Data to wrap the driver .......................................................................................................... diff --git a/modules/aerodyn/src/AeroDyn_Driver_Subs.f90 b/modules/aerodyn/src/AeroDyn_Driver_Subs.f90 index 0d3b1d295d..d609215127 100644 --- a/modules/aerodyn/src/AeroDyn_Driver_Subs.f90 +++ b/modules/aerodyn/src/AeroDyn_Driver_Subs.f90 @@ -469,7 +469,7 @@ subroutine Init_ADI_ForDriver(iCase, ADI, dvr, FED, dt, errStat, errMsg) InitInp%IW_InitInp%FilePassingMethod = 0_IntKi ! read input file instead of passed file data ! AeroDyn InitInp%AD%Gravity = 9.80665_ReKi - InitInp%AD%RootName = dvr%out%Root ! 'C:/Work/XFlow/' + InitInp%AD%RootName = dvr%out%Root InitInp%AD%InputFile = dvr%AD_InputFile InitInp%AD%MHK = dvr%MHK InitInp%AD%defFldDens = dvr%FldDens @@ -959,7 +959,7 @@ subroutine Dvr_ReadInputFile(fileName, dvr, errStat, errMsg ) call ParseVar(FileInfo_In, CurLine, "analysisType", dvr%analysisType, errStat2, errMsg2, unEc); if (Failed()) return call ParseVar(FileInfo_In, CurLine, "tMax" , dvr%tMax , errStat2, errMsg2, unEc); if (Failed()) return call ParseVar(FileInfo_In, CurLine, "dt" , dvr%dt , errStat2, errMsg2, unEc); if (Failed()) return - call ParseVar(FileInfo_In, CurLine, "AeroFile" , dvr%AD_InputFile, errStat2, errMsg2, unEc); if (Failed()) return + call ParseVar(FileInfo_In, CurLine, "AeroFile" , dvr%AD_InputFile, errStat2, errMsg2, unEc, IsPath=.true.); if (Failed()) return ! --- Environmental conditions call ParseCom(FileInfo_In, CurLine, Line, errStat2, errMsg2, unEc); if (Failed()) return @@ -974,7 +974,7 @@ subroutine Dvr_ReadInputFile(fileName, dvr, errStat, errMsg ) ! --- Inflow data call ParseCom(FileInfo_In, CurLine, Line, errStat2, errMsg2, unEc); if (Failed()) return call ParseVar(FileInfo_In, CurLine, "compInflow", dvr%IW_InitInp%compInflow , errStat2, errMsg2, unEc); if (Failed()) return - call ParseVar(FileInfo_In, CurLine, "InflowFile", dvr%IW_InitInp%InputFile, errStat2, errMsg2, unEc); if (Failed()) return + call ParseVar(FileInfo_In, CurLine, "InflowFile", dvr%IW_InitInp%InputFile, errStat2, errMsg2, unEc, IsPath=.true.); if (Failed()) return if (dvr%IW_InitInp%compInflow==0) then call ParseVar(FileInfo_In, CurLine, "HWindSpeed", dvr%IW_InitInp%HWindSpeed , errStat2, errMsg2, unEc); if (Failed()) return call ParseVar(FileInfo_In, CurLine, "RefHt" , dvr%IW_InitInp%RefHt , errStat2, errMsg2, unEc); if (Failed()) return @@ -1689,12 +1689,12 @@ subroutine Dvr_WriteOutputs(nt, t, dvr, out, yADI, errStat, errMsg) end subroutine Dvr_WriteOutputs !---------------------------------------------------------------------------------------------------------------------------------- !> This subroutine sets up the information needed for plotting VTK surfaces. -subroutine setVTKParameters(p_FAST, dvr, ADI, errStat, errMsg, dirname) - type(Dvr_Outputs), intent(inout) :: p_FAST !< The parameters of the glue code - type(Dvr_SimData), target, intent(inout) :: dvr ! intent(out) only so that we can save FmtWidth in dvr%out%ActualChanLen - type(ADI_Data), target, intent(in ) :: ADI ! Input data for initialization (intent out for getting AD WriteOutput names/units) - integer(IntKi), intent( out) :: errStat !< Error status of the operation - character(*), intent( out) :: errMsg !< Error message if errStat /= ErrID_None +subroutine setVTKParameters(DVR_Outs, dvr, ADI, errStat, errMsg, dirname) + type(Dvr_Outputs), intent(inout) :: DVR_Outs !< The parameters of the glue code + type(Dvr_SimData), target, intent(inout) :: dvr ! intent(out) only so that we can save FmtWidth in dvr%out%ActualChanLen + type(ADI_Data), target, intent(in ) :: ADI ! Input data for initialization (intent out for getting AD WriteOutput names/units) + integer(IntKi), intent( out) :: errStat !< Error status of the operation + character(*), intent( out) :: errMsg !< Error message if errStat /= ErrID_None character(*), optional,intent(in ) :: dirname real(SiKi) :: RefPoint(3), RefLengths(2) real(SiKi) :: x, y @@ -1726,19 +1726,23 @@ subroutine setVTKParameters(p_FAST, dvr, ADI, errStat, errMsg, dirname) ! get the name of the output directory for vtk files (in a subdirectory called "vtk" of the output directory), and ! create the VTK directory if it does not exist - call GetPath ( p_FAST%root, p_FAST%VTK_OutFileRoot, vtkroot ) ! the returned p_FAST%VTK_OutFileRoot includes a file separator character at the end - p_FAST%VTK_OutFileRoot = trim(p_FAST%VTK_OutFileRoot) // trim(dir) - call MKDIR( trim(p_FAST%VTK_OutFileRoot) ) - p_FAST%VTK_OutFileRoot = trim( p_FAST%VTK_OutFileRoot ) // PathSep // trim(vtkroot) + call GetPath ( DVR_Outs%root, DVR_Outs%VTK_OutFileRoot, vtkroot ) ! the returned DVR_Outs%VTK_OutFileRoot includes a file separator character at the end + if (PathIsRelative(trim(dir))) then + DVR_Outs%VTK_OutFileRoot = trim(DVR_Outs%VTK_OutFileRoot) // trim(dir) + else + DVR_Outs%VTK_OutFileRoot = trim(dir) + endif + call MKDIR( trim(DVR_Outs%VTK_OutFileRoot) ) + DVR_Outs%VTK_OutFileRoot = trim( DVR_Outs%VTK_OutFileRoot ) // PathSep // trim(vtkroot) ! calculate the number of digits in 'y_FAST%NOutSteps' (Maximum number of output steps to be written) ! this will be used to pad the write-out step in the VTK filename with zeros in calls to MeshWrVTK() - p_FAST%VTK_tWidth = max(9, CEILING( log10( real(dvr%numSteps+1, ReKi) / p_FAST%n_VTKTime ) ) + 1) ! NOTE: at least 9, if user changes dt/and tmax + DVR_Outs%VTK_tWidth = max(9, CEILING( log10( real(dvr%numSteps+1, ReKi) / DVR_Outs%n_VTKTime ) ) + 1) ! NOTE: at least 9, if user changes dt/and tmax - if (allocated(p_FAST%VTK_Surface)) then + if (allocated(DVR_Outs%VTK_Surface)) then return ! The surfaces were already computed (for combined cases) endif - allocate(p_FAST%VTK_Surface(dvr%numTurbines)) + allocate(DVR_Outs%VTK_Surface(dvr%numTurbines)) ! --- Find dimensions for all objects to determine "Ground" and typical dimensions MaxBladeLength = 0 MaxTwrLength = 0 @@ -1779,7 +1783,7 @@ subroutine setVTKParameters(p_FAST, dvr, ADI, errStat, errMsg, dirname) enddo ! Loop on turbine ! Get radius for ground (blade length + hub radius): - GroundRad = MaxBladeLength + MaxTwrLength+ p_FAST%VTKHubRad + GroundRad = MaxBladeLength + MaxTwrLength+ DVR_Outs%VTKHubRad ! write the ground or seabed reference polygon: RefPoint(1:2) = dvr%WT(1)%originInit(1:2) do iWT=2,dvr%numTurbines @@ -1789,37 +1793,37 @@ subroutine setVTKParameters(p_FAST, dvr, ADI, errStat, errMsg, dirname) RefPoint(3) = 0.0_ReKi RefLengths = GroundRad + sqrt((WorldBoxMax(1)-WorldBoxMin(1))**2 + (WorldBoxMax(2)-WorldBoxMin(2))**2) - call WrVTK_Ground (RefPoint, RefLengths, trim(p_FAST%VTK_OutFileRoot) // '.GroundSurface', errStat2, errMsg2 ) + call WrVTK_Ground (RefPoint, RefLengths, trim(DVR_Outs%VTK_OutFileRoot) // '.GroundSurface', errStat2, errMsg2 ) ! --- Create surfaces for Nacelle, Base, Tower, Blades do iWT=1,dvr%numTurbines wt => dvr%wt(iWT) - p_FAST%VTK_Surface(iWT)%NumSectors = 25 + DVR_Outs%VTK_Surface(iWT)%NumSectors = 25 ! Create nacelle box - p_FAST%VTK_Surface(iWT)%NacelleBox(:,1) = (/ p_FAST%VTKNacDim(1) , p_FAST%VTKNacDim(2)+p_FAST%VTKNacDim(5), p_FAST%VTKNacDim(3) /) - p_FAST%VTK_Surface(iWT)%NacelleBox(:,2) = (/ p_FAST%VTKNacDim(1)+p_FAST%VTKNacDim(4), p_FAST%VTKNacDim(2)+p_FAST%VTKNacDim(5), p_FAST%VTKNacDim(3) /) - p_FAST%VTK_Surface(iWT)%NacelleBox(:,3) = (/ p_FAST%VTKNacDim(1)+p_FAST%VTKNacDim(4), p_FAST%VTKNacDim(2) , p_FAST%VTKNacDim(3) /) - p_FAST%VTK_Surface(iWT)%NacelleBox(:,4) = (/ p_FAST%VTKNacDim(1) , p_FAST%VTKNacDim(2) , p_FAST%VTKNacDim(3) /) - p_FAST%VTK_Surface(iWT)%NacelleBox(:,5) = (/ p_FAST%VTKNacDim(1) , p_FAST%VTKNacDim(2) , p_FAST%VTKNacDim(3)+p_FAST%VTKNacDim(6) /) - p_FAST%VTK_Surface(iWT)%NacelleBox(:,6) = (/ p_FAST%VTKNacDim(1)+p_FAST%VTKNacDim(4), p_FAST%VTKNacDim(2) , p_FAST%VTKNacDim(3)+p_FAST%VTKNacDim(6) /) - p_FAST%VTK_Surface(iWT)%NacelleBox(:,7) = (/ p_FAST%VTKNacDim(1)+p_FAST%VTKNacDim(4), p_FAST%VTKNacDim(2)+p_FAST%VTKNacDim(5), p_FAST%VTKNacDim(3)+p_FAST%VTKNacDim(6) /) - p_FAST%VTK_Surface(iWT)%NacelleBox(:,8) = (/ p_FAST%VTKNacDim(1) , p_FAST%VTKNacDim(2)+p_FAST%VTKNacDim(5), p_FAST%VTKNacDim(3)+p_FAST%VTKNacDim(6) /) + DVR_Outs%VTK_Surface(iWT)%NacelleBox(:,1) = (/ DVR_Outs%VTKNacDim(1) , DVR_Outs%VTKNacDim(2)+DVR_Outs%VTKNacDim(5), DVR_Outs%VTKNacDim(3) /) + DVR_Outs%VTK_Surface(iWT)%NacelleBox(:,2) = (/ DVR_Outs%VTKNacDim(1)+DVR_Outs%VTKNacDim(4), DVR_Outs%VTKNacDim(2)+DVR_Outs%VTKNacDim(5), DVR_Outs%VTKNacDim(3) /) + DVR_Outs%VTK_Surface(iWT)%NacelleBox(:,3) = (/ DVR_Outs%VTKNacDim(1)+DVR_Outs%VTKNacDim(4), DVR_Outs%VTKNacDim(2) , DVR_Outs%VTKNacDim(3) /) + DVR_Outs%VTK_Surface(iWT)%NacelleBox(:,4) = (/ DVR_Outs%VTKNacDim(1) , DVR_Outs%VTKNacDim(2) , DVR_Outs%VTKNacDim(3) /) + DVR_Outs%VTK_Surface(iWT)%NacelleBox(:,5) = (/ DVR_Outs%VTKNacDim(1) , DVR_Outs%VTKNacDim(2) , DVR_Outs%VTKNacDim(3)+DVR_Outs%VTKNacDim(6) /) + DVR_Outs%VTK_Surface(iWT)%NacelleBox(:,6) = (/ DVR_Outs%VTKNacDim(1)+DVR_Outs%VTKNacDim(4), DVR_Outs%VTKNacDim(2) , DVR_Outs%VTKNacDim(3)+DVR_Outs%VTKNacDim(6) /) + DVR_Outs%VTK_Surface(iWT)%NacelleBox(:,7) = (/ DVR_Outs%VTKNacDim(1)+DVR_Outs%VTKNacDim(4), DVR_Outs%VTKNacDim(2)+DVR_Outs%VTKNacDim(5), DVR_Outs%VTKNacDim(3)+DVR_Outs%VTKNacDim(6) /) + DVR_Outs%VTK_Surface(iWT)%NacelleBox(:,8) = (/ DVR_Outs%VTKNacDim(1) , DVR_Outs%VTKNacDim(2)+DVR_Outs%VTKNacDim(5), DVR_Outs%VTKNacDim(3)+DVR_Outs%VTKNacDim(6) /) ! Create base box (using towerbase or nacelle dime) - BaseBoxDim = minval(p_FAST%VTKNacDim(4:6))/2 + BaseBoxDim = minval(DVR_Outs%VTKNacDim(4:6))/2 if (size(ADI%m%VTK_Surfaces(iWT)%TowerRad)>0) then BaseBoxDim = ADI%m%VTK_Surfaces(iWT)%TowerRad(1) endif - p_FAST%VTK_Surface(iWT)%BaseBox(:,1) = (/ -BaseBoxDim , -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim /) - p_FAST%VTK_Surface(iWT)%BaseBox(:,2) = (/ -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim /) - p_FAST%VTK_Surface(iWT)%BaseBox(:,3) = (/ -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim , -BaseBoxDim /) - p_FAST%VTK_Surface(iWT)%BaseBox(:,4) = (/ -BaseBoxDim , -BaseBoxDim , -BaseBoxDim /) - p_FAST%VTK_Surface(iWT)%BaseBox(:,5) = (/ -BaseBoxDim , -BaseBoxDim , -BaseBoxDim+2*BaseBoxDim /) - p_FAST%VTK_Surface(iWT)%BaseBox(:,6) = (/ -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim , -BaseBoxDim+2*BaseBoxDim /) - p_FAST%VTK_Surface(iWT)%BaseBox(:,7) = (/ -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim+2*BaseBoxDim /) - p_FAST%VTK_Surface(iWT)%BaseBox(:,8) = (/ -BaseBoxDim , -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim+2*BaseBoxDim /) + DVR_Outs%VTK_Surface(iWT)%BaseBox(:,1) = (/ -BaseBoxDim , -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim /) + DVR_Outs%VTK_Surface(iWT)%BaseBox(:,2) = (/ -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim /) + DVR_Outs%VTK_Surface(iWT)%BaseBox(:,3) = (/ -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim , -BaseBoxDim /) + DVR_Outs%VTK_Surface(iWT)%BaseBox(:,4) = (/ -BaseBoxDim , -BaseBoxDim , -BaseBoxDim /) + DVR_Outs%VTK_Surface(iWT)%BaseBox(:,5) = (/ -BaseBoxDim , -BaseBoxDim , -BaseBoxDim+2*BaseBoxDim /) + DVR_Outs%VTK_Surface(iWT)%BaseBox(:,6) = (/ -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim , -BaseBoxDim+2*BaseBoxDim /) + DVR_Outs%VTK_Surface(iWT)%BaseBox(:,7) = (/ -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim+2*BaseBoxDim /) + DVR_Outs%VTK_Surface(iWT)%BaseBox(:,8) = (/ -BaseBoxDim , -BaseBoxDim+2*BaseBoxDim, -BaseBoxDim+2*BaseBoxDim /) enddo ! iWT, turbines @@ -1827,12 +1831,12 @@ end subroutine SetVTKParameters !---------------------------------------------------------------------------------------------------------------------------------- !> This routine writes a minimal subset of meshes with surfaces to VTK-formatted files. It doesn't bother with !! returning an error code. -subroutine WrVTK_Surfaces(t_global, ADI, FED, p_FAST, VTK_count) +subroutine WrVTK_Surfaces(t_global, ADI, FED, DVR_Outs, VTK_count) use FVW_IO, only: WrVTK_FVW real(DbKi), intent(in ) :: t_global !< Current global time type(FED_Data), target, intent(in ) :: FED !< Elastic wind turbine data (Fake ElastoDyn) type(ADI_Data), intent(in ) :: ADI !< Input data for initialization (intent out for getting AD WriteOutput names/units) - type(Dvr_Outputs), intent(in ) :: p_FAST !< Parameters for the glue code + type(Dvr_Outputs), intent(in ) :: DVR_Outs !< Parameters for the glue code integer(IntKi) , intent(in ) :: VTK_count logical, parameter :: OutputFields = .FALSE. ! due to confusion about what fields mean on a surface, we are going to just output the basic meshes if people ask for fields integer(IntKi) :: errStat2 @@ -1843,7 +1847,7 @@ subroutine WrVTK_Surfaces(t_global, ADI, FED, p_FAST, VTK_count) type(RotFED), pointer :: y_ED ! Alias to shorten notation ! AeroDyn surfaces (Blades, Hub, Tower) - call AD_WrVTK_Surfaces(ADI%u(2)%AD, ADI%y%AD, p_FAST%VTKRefPoint, ADI%m%VTK_Surfaces, VTK_count, p_FAST%VTK_OutFileRoot, p_FAST%VTK_tWidth, 25, p_FAST%VTKHubRad) + call AD_WrVTK_Surfaces(ADI%u(2)%AD, ADI%y%AD, DVR_Outs%VTKRefPoint, ADI%m%VTK_Surfaces, VTK_count, DVR_Outs%VTK_OutFileRoot, DVR_Outs%VTK_tWidth, 25, DVR_Outs%VTKHubRad) ! Elastic info nWT = size(FED%WT) @@ -1856,25 +1860,25 @@ subroutine WrVTK_Surfaces(t_global, ADI, FED, p_FAST, VTK_count) y_ED => FED%WT(iWT) ! Base - call MeshWrVTK_PointSurface (p_FAST%VTKRefPoint, y_ED%PlatformPtMesh, trim(p_FAST%VTK_OutFileRoot)//trim(sWT)//'.BaseSurface', & - VTK_count, OutputFields, errStat2, errMsg2, p_FAST%VTK_tWidth , verts = p_FAST%VTK_Surface(iWT)%BaseBox) + call MeshWrVTK_PointSurface (DVR_Outs%VTKRefPoint, y_ED%PlatformPtMesh, trim(DVR_Outs%VTK_OutFileRoot)//trim(sWT)//'.BaseSurface', & + VTK_count, OutputFields, errStat2, errMsg2, DVR_Outs%VTK_tWidth , verts = DVR_Outs%VTK_Surface(iWT)%BaseBox) if (y_ED%numBlades>0) then ! Nacelle - call MeshWrVTK_PointSurface (p_FAST%VTKRefPoint, y_ED%NacelleMotion, trim(p_FAST%VTK_OutFileRoot)//trim(sWT)//'.NacelleSurface', & - VTK_count, OutputFields, errStat2, errMsg2, p_FAST%VTK_tWidth , verts = p_FAST%VTK_Surface(iWT)%NacelleBox) + call MeshWrVTK_PointSurface (DVR_Outs%VTKRefPoint, y_ED%NacelleMotion, trim(DVR_Outs%VTK_OutFileRoot)//trim(sWT)//'.NacelleSurface', & + VTK_count, OutputFields, errStat2, errMsg2, DVR_Outs%VTK_tWidth , verts = DVR_Outs%VTK_Surface(iWT)%NacelleBox) endif - if (p_FAST%WrVTK>1) then + if (DVR_Outs%WrVTK>1) then ! --- animations ! Tower base - call MeshWrVTK_PointSurface (p_FAST%VTKRefPoint, y_ED%TwrPtMesh, trim(p_FAST%VTK_OutFileRoot)//trim(sWT)//'.TwrBaseSurface', & - VTK_count, OutputFields, errStat2, errMsg2, p_FAST%VTK_tWidth , & - NumSegments=p_FAST%VTK_Surface(iWT)%NumSectors, radius=p_FAST%VTKHubRad) + call MeshWrVTK_PointSurface (DVR_Outs%VTKRefPoint, y_ED%TwrPtMesh, trim(DVR_Outs%VTK_OutFileRoot)//trim(sWT)//'.TwrBaseSurface', & + VTK_count, OutputFields, errStat2, errMsg2, DVR_Outs%VTK_tWidth , & + NumSegments=DVR_Outs%VTK_Surface(iWT)%NumSectors, radius=DVR_Outs%VTKHubRad) if (ADI%u(2)%AD%rotors(iWT)%TowerMotion%nNodes>0) then - call MeshWrVTK_PointSurface (p_FAST%VTKRefPoint, y_ED%TwrPtMeshAD, trim(p_FAST%VTK_OutFileRoot)//trim(sWT)//'.TwrBaseSurfaceAD', & - VTK_count, OutputFields, errStat2, errMsg2, p_FAST%VTK_tWidth , & - NumSegments=p_FAST%VTK_Surface(iWT)%NumSectors, radius=p_FAST%VTKHubRad) + call MeshWrVTK_PointSurface (DVR_Outs%VTKRefPoint, y_ED%TwrPtMeshAD, trim(DVR_Outs%VTK_OutFileRoot)//trim(sWT)//'.TwrBaseSurfaceAD', & + VTK_count, OutputFields, errStat2, errMsg2, DVR_Outs%VTK_tWidth , & + NumSegments=DVR_Outs%VTK_Surface(iWT)%NumSectors, radius=DVR_Outs%VTKHubRad) endif endif enddo @@ -1882,18 +1886,18 @@ subroutine WrVTK_Surfaces(t_global, ADI, FED, p_FAST, VTK_count) ! Free wake if (allocated(ADI%m%AD%FVW_u)) then if (allocated(ADI%m%AD%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(1)%AD%FVW, ADI%z(1)%AD%FVW, ADI%m%AD%FVW, trim(p_FAST%VTK_OutFileRoot)//'.FVW', VTK_count, p_FAST%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords + call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(1)%AD%FVW, ADI%z(1)%AD%FVW, ADI%m%AD%FVW, trim(DVR_Outs%VTK_OutFileRoot)//'.FVW', VTK_count, DVR_Outs%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords end if end if end subroutine WrVTK_Surfaces !> This routine writes a minimal subset of meshes with surfaces to VTK-formatted files. It doesn't bother with !! returning an error code. -subroutine WrVTK_Lines(t_global, ADI, FED, p_FAST, VTK_count) +subroutine WrVTK_Lines(t_global, ADI, FED, DVR_Outs, VTK_count) use FVW_IO, only: WrVTK_FVW REAL(DbKi), INTENT(IN ) :: t_global !< Current global time type(ADI_Data), intent(in ) :: ADI !< Input data for initialization (intent out for getting AD WriteOutput names/units) type(FED_Data), target, intent(in ) :: FED !< Elastic wind turbine data (Fake ElastoDyn) - TYPE(Dvr_Outputs), INTENT(IN ) :: p_FAST !< Parameters for the glue code + TYPE(Dvr_Outputs), INTENT(IN ) :: DVR_Outs !< Parameters for the glue code INTEGER(IntKi) , INTENT(IN ) :: VTK_count logical, parameter :: OutputFields = .TRUE. INTEGER(IntKi) :: k @@ -1905,7 +1909,7 @@ subroutine WrVTK_Lines(t_global, ADI, FED, p_FAST, VTK_count) type(RotFED), pointer :: y_ED ! Alias to shorten notation ! AeroDyn surfaces (Blades, Tower) - call AD_WrVTK_LinesPoints(ADI%u(2)%AD, ADI%y%AD, p_FAST%VTKRefPoint, VTK_count, p_FAST%VTK_OutFileRoot, p_FAST%VTK_tWidth) + call AD_WrVTK_LinesPoints(ADI%u(2)%AD, ADI%y%AD, DVR_Outs%VTKRefPoint, VTK_count, DVR_Outs%VTK_OutFileRoot, DVR_Outs%VTK_tWidth) ! Elastic info nWT = size(FED%WT) @@ -1917,34 +1921,34 @@ subroutine WrVTK_Lines(t_global, ADI, FED, p_FAST, VTK_count) endif y_ED => FED%WT(iWT) - if (p_FAST%WrVTK_Type==2) then ! only if not doing surfaces + if (DVR_Outs%WrVTK_Type==2) then ! only if not doing surfaces ! Base - call MeshWrVTK_PointSurface (p_FAST%VTKRefPoint, y_ED%PlatformPtMesh, trim(p_FAST%VTK_OutFileRoot)//trim(sWT)//'.BaseSurface', & - VTK_count, OutputFields, errStat2, errMsg2, p_FAST%VTK_tWidth , verts = p_FAST%VTK_Surface(iWT)%BaseBox) + call MeshWrVTK_PointSurface (DVR_Outs%VTKRefPoint, y_ED%PlatformPtMesh, trim(DVR_Outs%VTK_OutFileRoot)//trim(sWT)//'.BaseSurface', & + VTK_count, OutputFields, errStat2, errMsg2, DVR_Outs%VTK_tWidth , verts = DVR_Outs%VTK_Surface(iWT)%BaseBox) endif if (y_ED%numBlades>0) then ! Nacelle - call MeshWrVTK( p_FAST%VTKRefPoint, y_ED%NacelleMotion, trim(p_FAST%VTK_OutFileRoot)//trim(sWT)//'.Nacelle', & - VTK_count, OutputFields, errStat2, errMsg2, p_FAST%VTK_tWidth ) + call MeshWrVTK( DVR_Outs%VTKRefPoint, y_ED%NacelleMotion, trim(DVR_Outs%VTK_OutFileRoot)//trim(sWT)//'.Nacelle', & + VTK_count, OutputFields, errStat2, errMsg2, DVR_Outs%VTK_tWidth ) endif - if (p_FAST%WrVTK>1) then + if (DVR_Outs%WrVTK>1) then ! --- animations ! Tower base - call MeshWrVTK(p_FAST%VTKRefPoint, y_ED%TwrPtMesh, trim(p_FAST%VTK_OutFileRoot)//trim(sWT)//'.TwrBase', & - VTK_count, OutputFields, errStat2, errMsg2, p_FAST%VTK_tWidth ) + call MeshWrVTK(DVR_Outs%VTKRefPoint, y_ED%TwrPtMesh, trim(DVR_Outs%VTK_OutFileRoot)//trim(sWT)//'.TwrBase', & + VTK_count, OutputFields, errStat2, errMsg2, DVR_Outs%VTK_tWidth ) if (ADI%u(2)%AD%rotors(iWT)%TowerMotion%nNodes>0) then - call MeshWrVTK(p_FAST%VTKRefPoint, y_ED%TwrPtMeshAD, trim(p_FAST%VTK_OutFileRoot)//trim(sWT)//'.TwrBaseAD', & - VTK_count, OutputFields, errStat2, errMsg2, p_FAST%VTK_tWidth ) + call MeshWrVTK(DVR_Outs%VTKRefPoint, y_ED%TwrPtMeshAD, trim(DVR_Outs%VTK_OutFileRoot)//trim(sWT)//'.TwrBaseAD', & + VTK_count, OutputFields, errStat2, errMsg2, DVR_Outs%VTK_tWidth ) endif endif enddo ! Free wake (only write this here if doing line meshes only -- FVW is written with surface outputs) - if (allocated(ADI%m%AD%FVW_u) .and. p_FAST%WrVTK_Type==2) then + if (allocated(ADI%m%AD%FVW_u) .and. DVR_Outs%WrVTK_Type==2) then if (allocated(ADI%m%AD%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(1)%AD%FVW, ADI%z(1)%AD%FVW, ADI%m%AD%FVW, trim(p_FAST%VTK_OutFileRoot)//'.FVW', VTK_count, p_FAST%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords + call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(1)%AD%FVW, ADI%z(1)%AD%FVW, ADI%m%AD%FVW, trim(DVR_Outs%VTK_OutFileRoot)//'.FVW', VTK_count, DVR_Outs%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords end if end if end subroutine WrVTK_Lines diff --git a/modules/aerodyn/src/AeroDyn_Driver_Types.f90 b/modules/aerodyn/src/AeroDyn_Driver_Types.f90 index 8d3153e0a9..f1122aded8 100644 --- a/modules/aerodyn/src/AeroDyn_Driver_Types.f90 +++ b/modules/aerodyn/src/AeroDyn_Driver_Types.f90 @@ -68,10 +68,10 @@ MODULE AeroDyn_Driver_Types character(1) :: delim !< column delimiter [-] character(20) :: outFmt !< Format specifier [-] INTEGER(IntKi) :: fileFmt = 0_IntKi !< Output format 1=Text, 2=Binary, 3=Both [-] - INTEGER(IntKi) :: wrVTK = 0_IntKi !< 0= no vtk, 1=init only, 2=animation [-] + INTEGER(IntKi) :: WrVTK = 0_IntKi !< 0= no vtk, 1=init only, 2=animation [-] INTEGER(IntKi) :: WrVTK_Type = 0_IntKi !< Flag for VTK output type (1=surface, 2=line, 3=both) [-] character(1024) :: Root !< Output file rootname [-] - character(1024) :: VTK_OutFileRoot !< Output file rootname for vtk [-] + character(1024) :: VTK_OutFileRoot !< Output file rootname for vtk (includes directory) [-] character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputHdr !< Channel headers [-] character(ChanLen) , DIMENSION(:), ALLOCATABLE :: WriteOutputUnt !< Channel units [-] REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: storage !< nTurbines x nChannel x nTime [-] @@ -79,6 +79,7 @@ MODULE AeroDyn_Driver_Types TYPE(DvrVTK_SurfaceType) , DIMENSION(:), ALLOCATABLE :: VTK_surface !< Data for VTK surface visualization [-] INTEGER(IntKi) :: VTK_tWidth = 0_IntKi !< Width of number of files for leading zeros in file name format [-] INTEGER(IntKi) :: n_VTKTime = 0_IntKi !< Number of time steps between writing VTK files [-] + REAL(DbKi) :: VTK_DT = 0.0_R8Ki !< Write VTK time step [-] REAL(SiKi) :: VTKHubRad = 0.0_R4Ki !< Hub radius for visualization [m] REAL(ReKi) , DIMENSION(1:6) :: VTKNacDim = 0.0_ReKi !< Nacelle dimensions for visualization [m] REAL(SiKi) , DIMENSION(1:3) :: VTKRefPoint = 0.0_R4Ki !< RefPoint for VTK outputs [-] @@ -346,7 +347,7 @@ subroutine AD_Dvr_CopyDvr_Outputs(SrcDvr_OutputsData, DstDvr_OutputsData, CtrlCo DstDvr_OutputsData%delim = SrcDvr_OutputsData%delim DstDvr_OutputsData%outFmt = SrcDvr_OutputsData%outFmt DstDvr_OutputsData%fileFmt = SrcDvr_OutputsData%fileFmt - DstDvr_OutputsData%wrVTK = SrcDvr_OutputsData%wrVTK + DstDvr_OutputsData%WrVTK = SrcDvr_OutputsData%WrVTK DstDvr_OutputsData%WrVTK_Type = SrcDvr_OutputsData%WrVTK_Type DstDvr_OutputsData%Root = SrcDvr_OutputsData%Root DstDvr_OutputsData%VTK_OutFileRoot = SrcDvr_OutputsData%VTK_OutFileRoot @@ -416,6 +417,7 @@ subroutine AD_Dvr_CopyDvr_Outputs(SrcDvr_OutputsData, DstDvr_OutputsData, CtrlCo end if DstDvr_OutputsData%VTK_tWidth = SrcDvr_OutputsData%VTK_tWidth DstDvr_OutputsData%n_VTKTime = SrcDvr_OutputsData%n_VTKTime + DstDvr_OutputsData%VTK_DT = SrcDvr_OutputsData%VTK_DT DstDvr_OutputsData%VTKHubRad = SrcDvr_OutputsData%VTKHubRad DstDvr_OutputsData%VTKNacDim = SrcDvr_OutputsData%VTKNacDim DstDvr_OutputsData%VTKRefPoint = SrcDvr_OutputsData%VTKRefPoint @@ -478,7 +480,7 @@ subroutine AD_Dvr_PackDvr_Outputs(RF, Indata) call RegPack(RF, InData%delim) call RegPack(RF, InData%outFmt) call RegPack(RF, InData%fileFmt) - call RegPack(RF, InData%wrVTK) + call RegPack(RF, InData%WrVTK) call RegPack(RF, InData%WrVTK_Type) call RegPack(RF, InData%Root) call RegPack(RF, InData%VTK_OutFileRoot) @@ -497,6 +499,7 @@ subroutine AD_Dvr_PackDvr_Outputs(RF, Indata) end if call RegPack(RF, InData%VTK_tWidth) call RegPack(RF, InData%n_VTKTime) + call RegPack(RF, InData%VTK_DT) call RegPack(RF, InData%VTKHubRad) call RegPack(RF, InData%VTKNacDim) call RegPack(RF, InData%VTKRefPoint) @@ -523,7 +526,7 @@ subroutine AD_Dvr_UnPackDvr_Outputs(RF, OutData) call RegUnpack(RF, OutData%delim); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%outFmt); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%fileFmt); if (RegCheckErr(RF, RoutineName)) return - call RegUnpack(RF, OutData%wrVTK); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%WrVTK); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%WrVTK_Type); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%Root); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%VTK_OutFileRoot); if (RegCheckErr(RF, RoutineName)) return @@ -546,6 +549,7 @@ subroutine AD_Dvr_UnPackDvr_Outputs(RF, OutData) end if call RegUnpack(RF, OutData%VTK_tWidth); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%n_VTKTime); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%VTK_DT); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%VTKHubRad); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%VTKNacDim); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%VTKRefPoint); if (RegCheckErr(RF, RoutineName)) return diff --git a/modules/aerodyn/src/AeroDyn_IO.f90 b/modules/aerodyn/src/AeroDyn_IO.f90 index fdf50bbef7..a3e56406a7 100644 --- a/modules/aerodyn/src/AeroDyn_IO.f90 +++ b/modules/aerodyn/src/AeroDyn_IO.f90 @@ -818,7 +818,7 @@ SUBROUTINE ParsePrimaryFileInfo( PriPath, InitInp, InputFile, RootName, NumBlade call ParseVar( FileInfo_In, CurLine, "CompAA", InputFileData%CompAA, ErrStat2, ErrMsg2, UnEc ) if (Failed()) return ! AA_InputFile - Aeroacoustics input file - call ParseVar( FileInfo_In, CurLine, "AA_InputFile", InputFileData%AA_InputFile, ErrStat2, ErrMsg2, UnEc ) + call ParseVar( FileInfo_In, CurLine, "AA_InputFile", InputFileData%AA_InputFile, ErrStat2, ErrMsg2, UnEc, IsPath=.true. ) if (Failed()) return IF ( PathIsRelative( InputFileData%AA_InputFile ) ) InputFileData%AA_InputFile = TRIM(PriPath)//TRIM(InputFileData%AA_InputFile) @@ -944,7 +944,7 @@ SUBROUTINE ParsePrimaryFileInfo( PriPath, InitInp, InputFile, RootName, NumBlade if ( InputFileData%Echo ) WRITE(UnEc, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo CurLine = CurLine + 1 ! OLAFInputFileName - Input file for OLAF [used only when WakeMod=3] - call ParseVar( FileInfo_In, CurLine, "OLAFInputFileName", InputFileData%FVWFileName, ErrStat2, ErrMsg2, UnEc ) + call ParseVar( FileInfo_In, CurLine, "OLAFInputFileName", InputFileData%FVWFileName, ErrStat2, ErrMsg2, UnEc, IsPath=.true. ) if (Failed()) return IF ( PathIsRelative( InputFileData%FVWFileName ) ) InputFileData%FVWFileName = TRIM(PriPath)//TRIM(InputFileData%FVWFileName) @@ -1080,7 +1080,7 @@ SUBROUTINE ParsePrimaryFileInfo( PriPath, InitInp, InputFile, RootName, NumBlade ! NOTE: being nice with legacy input file. Uncomment in next release call ParseVar(FileInfo_In, CurLine, "TFinAero", InputFileData%rotors(iR)%TFinAero, ErrStat2, ErrMsg2, UnEc); if (ErrStat2==ErrID_None) then - call ParseVar(FileInfo_In, CurLine, "TFinFile", InputFileData%rotors(iR)%TFinFile, ErrStat2, ErrMsg2, UnEc); if (Failed()) return + call ParseVar(FileInfo_In, CurLine, "TFinFile", InputFileData%rotors(iR)%TFinFile, ErrStat2, ErrMsg2, UnEc, IsPath=.true.); if (Failed()) return IF ( PathIsRelative( InputFileData%rotors(iR)%TFinFile ) ) InputFileData%rotors(iR)%TFinFile = trim(PriPath) // trim(InputFileData%rotors(iR)%TFinFile) else call LegacyWarning('Tail Fin section (TFinAero, TFinFile) is missing from input file.') @@ -1445,13 +1445,11 @@ SUBROUTINE ReadBladeInputs ( ADBlFile, BladeKInputFileData, AeroProjMod, UnEc, c ErrMsg = "" UnIn = -1 + ! Open the input file for blade K. + !$OMP critical(filename) CALL GetNewUnit( UnIn, ErrStat2, ErrMsg2 ) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - - - ! Open the input file for blade K. - CALL OpenFInpFile ( UnIn, ADBlFile, ErrStat2, ErrMsg2 ) + !$OMP end critical(filename) CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) RETURN @@ -1754,8 +1752,10 @@ SUBROUTINE AD_PrintSum( InputFileData, p, p_AD, u, y, NumBlades, BladeInputFileD ! Open the summary file and give it a heading. + !$OMP critical(filename) CALL GetNewUnit( UnSu, ErrStat, ErrMsg ) CALL OpenFOutFile ( UnSu, TRIM( p%RootName )//'.sum', ErrStat, ErrMsg ) + !$OMP end critical(filename) IF ( ErrStat >= AbortErrLev ) RETURN ! Heading: diff --git a/modules/aerodyn/src/AeroDyn_Inflow.f90 b/modules/aerodyn/src/AeroDyn_Inflow.f90 index 5da3f6b8d6..3f212d78e3 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow.f90 @@ -388,6 +388,10 @@ subroutine ADI_InitInflowWind(Root, i_IW, u_AD, o_AD, IW, dt, InitOutData, errSt endif InitInData%RootName = trim(Root)//'.IfW' InitInData%MHK = i_IW%MHK + ! OLAF might be used in AD, in which case we need to allow out of bounds for some calcs. To do that + ! the average values for the entire wind profile must be calculated and stored (we don't know if OLAF + ! is used until after AD_Init below). + InitInData%BoxExceedAllow = .true. CALL InflowWind_Init( InitInData, IW%u, IW%p, & IW%x, IW%xd, IW%z, IW%OtherSt, & IW%y, IW%m, dt, InitOutData, errStat2, errMsg2 ) diff --git a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 index b2523e14c4..48f0d4601f 100644 --- a/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 +++ b/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 @@ -24,6 +24,7 @@ MODULE AeroDyn_Inflow_C_BINDING USE AeroDyn_Inflow_Types USE AeroDyn_Driver_Types, only: Dvr_SimData, Dvr_Outputs USE AeroDyn_Driver_Subs, only: Dvr_InitializeOutputs, Dvr_WriteOutputs, SetVTKParameters !, WrVTK_Surfaces, WrVTK_Lines, WrVTK_Ground + USE IfW_FlowField, only: IfW_FlowField_GetVelAcc USE NWTC_Library USE VersionInfo @@ -39,6 +40,7 @@ MODULE AeroDyn_Inflow_C_BINDING PUBLIC :: ADI_C_SetupRotor ! Initial node positions etc for a rotor PUBLIC :: ADI_C_SetRotorMotion ! Set motions for a given rotor PUBLIC :: ADI_C_GetRotorLoads ! Retrieve loads for a given rotor + PUBLIC :: ADI_C_GetDiskAvgVel ! Get the disk average velocity for the rotor !------------------------------------------------------------------------------------ ! Version info for display @@ -96,7 +98,7 @@ MODULE AeroDyn_Inflow_C_BINDING integer(IntKi) :: InterpOrder !------------------------------ ! Primary ADI data derived data types - type(ADI_Data) :: ADI + type(ADI_Data), target :: ADI !< all ADI data (target for using pointers to simplify code) type(ADI_InitInputType) :: InitInp !< Initialization data type(ADI_InitOutputType) :: InitOutData !< Initial output data -- Names, units, and version info. type(ADI_InputType) :: ADI_u !< ADI inputs -- set by AD_SetInputMotion. Copied as needed (necessary for correction steps) @@ -125,17 +127,29 @@ MODULE AeroDyn_Inflow_C_BINDING ! the glue code. However, here we do not pass state information through the ! interface and therefore must store it here analogously to how it is handled ! in the OpenFAST glue code. - integer(IntKi) :: n_Global ! global timestep - integer(IntKi) :: n_VTK ! VTK timestep - real(DbKi) :: InputTimePrev ! input time of last UpdateStates call + integer(IntKi) :: n_Global ! global timestep + integer(IntKi) :: n_VTK ! VTK timestep + real(DbKi) :: InputTimePrev ! input time of last UpdateStates call + real(DbKi) :: InputTimePrev_Calc ! input time of last CalcOutput call ! Note that we are including the previous state info here (not done in OF this way) - integer(IntKi), parameter :: STATE_LAST = 0 ! Index for previous state (not needed in OF, but necessary here) - integer(IntKi), parameter :: STATE_CURR = 1 ! Index for current state - integer(IntKi), parameter :: STATE_PRED = 2 ! Index for predicted state + integer(IntKi), parameter :: STATE_LAST = 0 ! Index for previous state (not needed in OF, but necessary here) + integer(IntKi), parameter :: STATE_CURR = 1 ! Index for current state + integer(IntKi), parameter :: STATE_PRED = 2 ! Index for predicted state ! Note the indexing is different on inputs (no clue why, but thats how OF handles it) - integer(IntKi), parameter :: INPUT_LAST = 3 ! Index for previous input at t-dt - integer(IntKi), parameter :: INPUT_CURR = 2 ! Index for current input at t - integer(IntKi), parameter :: INPUT_PRED = 1 ! Index for predicted input at t+dt + integer(IntKi), parameter :: INPUT_LAST = 3 ! Index for previous input at t-dt + integer(IntKi), parameter :: INPUT_CURR = 2 ! Index for current input at t + integer(IntKi), parameter :: INPUT_PRED = 1 ! Index for predicted input at t+dt + + !------------------------------- + ! Variables for disk average velocity calculations + integer(IntKi), parameter :: NumPtsDiskAvg = 144 + type :: DiskAvgVelData_Type + real(ReKi) :: DiskWindPosRel(3,NumPtsDiskAvg) + real(ReKi) :: DiskWindPosAbs(3,NumPtsDiskAvg) + real(ReKi) :: DiskWindVel(3,NumPtsDiskAvg) + real(ReKi) :: DiskAvgVel(3) + end type DiskAvgVelData_Type + type(DiskAvgVelData_Type), allocatable :: DiskAvgVelVars(:) !------------------------------------------------------------------------------------ ! Meshes for motions and loads @@ -276,9 +290,9 @@ subroutine ADI_C_PreInit(NumTurbines_C, TransposeDCM_in, PointLoadOutput_in, Deb endif ! check valid debug level - if (DebugLevel < 0_IntKi .or. DebugLevel > 4_IntKi) then + if (DebugLevel < 0_IntKi) then ErrStat2 = ErrID_Fatal - ErrMsg2 = "Interface debug level must be between 0 and 4"//NewLine// & + ErrMsg2 = "Interface debug level must be 0 or greater"//NewLine// & " 0 - none"//NewLine// & " 1 - some summary info and variables passed through interface"//NewLine// & " 2 - above + all position/orientation info"//NewLine// & @@ -303,6 +317,10 @@ subroutine ADI_C_PreInit(NumTurbines_C, TransposeDCM_in, PointLoadOutput_in, Deb if (allocated(InitInp%AD%rotors)) deallocate(InitInp%AD%rotors) allocate(InitInp%AD%rotors(Sim%NumTurbines),stat=errStat2); if (Failed0('rotors')) return + ! allocate data storage for DiskAvgVel retrieval + if (allocated(DiskAvgVelVars)) deallocate(DiskAvgVelVars) + allocate(DiskAvgVelVars(Sim%NumTurbines), STAT=ErrStat2); if (Failed0('DiskAvgVelVars')) return + ! Allocate data storage for turbine info if (allocated(Sim%WT)) deallocate(Sim%WT) allocate(Sim%WT(Sim%NumTurbines),stat=errStat2); if (Failed0('wind turbines')) return @@ -383,11 +401,13 @@ end subroutine ADI_C_PreInit !=============================================================================================================== SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileStringLength_C, & IfWinputFilePassed, IfWinputFileString_C, IfWinputFileStringLength_C, OutRootName_C, & + OutVTKDir_C, & gravity_C, defFldDens_C, defKinVisc_C, defSpdSound_C, & defPatm_C, defPvap_C, WtrDpth_C, MSL2SWL_C, & InterpOrder_C, DT_C, TMax_C, & storeHHVel, & - WrVTK_in, WrVTK_inType, VTKNacDim_in, VTKHubRad_in, & + WrVTK_in, WrVTK_inType, WrVTK_inDT, & + VTKNacDim_in, VTKHubRad_in, & wrOuts_C, DT_Outs_C, & NumChannels_C, OutputChannelNames_C, OutputChannelUnits_C, & ErrStat_C, ErrMsg_C) BIND (C, NAME='ADI_C_Init') @@ -404,6 +424,7 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString type(c_ptr), intent(in ) :: IfWinputFileString_C !< Input file as a single string with lines deliniated by C_NULL_CHAR integer(c_int), intent(in ) :: IfWinputFileStringLength_C !< lenght of the input file string character(kind=c_char), intent(in ) :: OutRootName_C(IntfStrLen) !< Root name to use for echo files and other + character(kind=c_char), intent(in ) :: OutVTKDir_C(IntfStrLen) !< Directory to put all vtk output ! Environmental real(c_float), intent(in ) :: gravity_C !< Gravitational acceleration (m/s^2) real(c_float), intent(in ) :: defFldDens_C !< Air density (kg/m^3) @@ -423,6 +444,7 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString ! VTK integer(c_int), intent(in ) :: WrVTK_in !< Write VTK outputs [0: none, 1: init only, 2: animation] integer(c_int), intent(in ) :: WrVTK_inType !< Write VTK outputs as [1: surface, 2: lines, 3: both] + real(c_double), intent(in ) :: WrVTK_inDT !< Timestep between VTK writes real(c_float), intent(in ) :: VTKNacDim_in(6) !< Nacelle dimension passed in for VTK surface rendering [0,y0,z0,Lx,Ly,Lz] (m) real(c_float), intent(in ) :: VTKHubrad_in !< Hub radius for VTK surface rendering integer(c_int), intent(in ) :: wrOuts_C !< Write ADI output file @@ -444,6 +466,7 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString character(ErrMsgLen) :: ErrMsg !< aggregated error message integer(IntKi) :: ErrStat2 !< temporary error status from a call character(ErrMsgLen) :: ErrMsg2 !< temporary error message from a call + character(IntfStrLen) :: OutVTKdir !< Output directory for files (relative to current location) integer(IntKi) :: i,j,k !< generic index variables integer(IntKi) :: iWT !< current turbine number (iterate through during setup for ADI_Init call) integer(IntKi) :: AeroProjMod !< for checking that all turbines use the same AeroProjMod @@ -494,6 +517,10 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString i = INDEX(OutRootName,C_NULL_CHAR) - 1 ! if this has a c null character at the end... if ( i > 0 ) OutRootName = OutRootName(1:I) ! remove it + ! OutVTKdir -- output directory + OutVTKdir = TRANSFER( OutVTKdir_C, OutVTKdir ) + i = INDEX(OutVTKdir,C_NULL_CHAR) - 1 ! if this has a c null character at the end... + if ( i > 0 ) OutVTKdir = OutVTKdir(1:I) ! remove it ! For debugging the interface: if (DebugLevel > 0) then @@ -554,18 +581,19 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString Sim%root = trim(OutRootName) ! Timekeeping n_Global = 0_IntKi ! Assume we are on timestep 0 at start - n_VTK = -1_IntKi ! Set VTK output to T=0 at first call + n_VTK = -1_IntKi ! counter advance just before writing ! Interpolation order InterpOrder = int(InterpOrder_C, IntKi) ! VTK outputs WrOutputsData%WrVTK = int(WrVTK_in, IntKi) WrOutputsData%WrVTK_Type = int(WrVTK_inType, IntKi) + WrOutputsData%VTK_dt = real(WrVTK_inDT, DbKi) WrOutputsData%VTKNacDim = real(VTKNacDim_in, SiKi) WrOutputsData%VTKHubrad = real(VTKHubrad_in, SiKi) WrOutputsData%VTKRefPoint = (/ 0.0_ReKi, 0.0_ReKi, 0.0_ReKi /) !TODO: should this be an input? WrOutputsData%root = trim(OutRootName) - WrOutputsData%n_VTKTime = 1 ! output every timestep + WrOutputsData%n_VTKTime = 1 ! output every timestep ! Write outputs to file WrOutputsData%fileFmt = int(wrOuts_C, IntKi) @@ -641,7 +669,10 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString call SetupMotionLoadsInterfaceMeshes(); if (Failed()) return ! setup meshes if (WrOutputsData%WrVTK > 0_IntKi) then - call setVTKParameters(WrOutputsData, Sim, ADI, ErrStat2, ErrMsg2, 'vtk-ADI') + if (len_trim(OutVTKdir) <= 0) then + OutVTKdir = 'vtk-ADI' + endif + call setVTKParameters(WrOutputsData, Sim, ADI, ErrStat2, ErrMsg2, OutVTKdir) if (Failed()) return endif ! write meshes for this rotor @@ -652,21 +683,30 @@ SUBROUTINE ADI_C_Init( ADinputFilePassed, ADinputFileString_C, ADinputFileString if (Failed()) return endif + ! Setup points for calculating disk average velocity + do iWT=1,Sim%NumTurbines + call SetDiskAvgPoints(iWT) + if (Failed()) return + enddo + !------------------------------------------------------------- ! Setup other prior timesteps ! We fill InputTimes with negative times, but the Input values are identical for each of those times; this allows ! us to use, e.g., quadratic interpolation that effectively acts as a zeroth-order extrapolation and first-order extrapolation ! for the first and second time steps. (The interpolation order in the ExtrapInput routines are determined as ! order = SIZE(Input) + ! Tracking of previous input times + ! Since we may run correction steps, there are some things we don't want to do !------------------------------------------------------------- do i=2,InterpOrder+1 call ADI_CopyInput (ADI%u(1), ADI%u(i), MESH_NEWCOPY, Errstat2, ErrMsg2) if (Failed()) return enddo do i = 1, InterpOrder + 1 - ADI%InputTimes(i) = 0.0_DbKi - (i - 1) * Sim%dT ! assume start at T=0 + ADI%InputTimes(i) = 0.0_DbKi - (i - 1) * Sim%dT ! assume start at T=0 enddo - InputTimePrev = ADI%InputTimes(1) - Sim%dT ! Initialize for UpdateStates + InputTimePrev = ADI%InputTimes(1) - Sim%dT ! Initialize for UpdateStates + InputTimePrev_Calc = ADI%InputTimes(1) - Sim%dT ! Initialize for CalcOutput !------------------------------------------------------------- ! copy of ADI inputs. AD_SetInputMotion will set this mesh. When CalcOutput is called, @@ -806,6 +846,19 @@ subroutine ValidateSetInputs(ErrStat3,ErrMsg3) call SetErrStat(ErrID_Warn,"Requested DT_Outs is not an integer multiple of DT. Changing DT_Outs to "//trim(Num2LStr(WrOutputsData%DT_Outs))//".",ErrStat3,ErrMsg3,RoutineName) endif endif + if (WrOutputsData%WrVTK > 1_IntKi) then ! only if writing during simulation is requested (ignore init or no outputs) + ! If a smaller timestep between outputs is requested than the simulation runs at, change to DT + if (WrOutputsData%VTK_DT < Sim%dT) then + WrOutputsData%VTK_DT = Sim%dT + call SetErrStat(ErrID_Warn,"Requested VTK_DT is smaller than timestep DT. Setting VTK_DT to DT.",ErrStat3,ErrMsg3,RoutineName) + endif + ! If not an integer multiple of DT, adjust + WrOutputsData%n_VTKtime = NINT( WrOutputsData%VTK_DT / Sim%dT ) + if (.NOT. EqualRealNos( WrOutputsData%VTK_DT, Sim%dT * WrOutputsData%n_VTKtime )) then + WrOutputsData%VTK_DT = real(WrOutputsData%n_VTKtime, DbKi) * Sim%dT + call SetErrStat(ErrID_Warn,"Requested VTK_DT is not an integer multiple of DT. Changing VTK_DT to "//trim(Num2LStr(WrOutputsData%VTK_DT))//".",ErrStat3,ErrMsg3,RoutineName) + endif + endif end subroutine ValidateSetInputs !> allocate data storage for file outputs @@ -853,6 +906,7 @@ subroutine ShowPassedData() call WrScr(" IfWinputFileString_C (ptr addr)"//trim(Num2LStr(LOC(IfWinputFileString_C))) ) call WrScr(" IfWinputFileStringLength_C "//trim(Num2LStr( IfWinputFileStringLength_C )) ) call WrScr(" OutRootName "//trim(OutRootName) ) + call WrScr(" OutVTKDir "//trim(OutVTKDir) ) call WrScr(" Environment variables") call WrScr(" gravity_C "//trim(Num2LStr( gravity_C )) ) call WrScr(" defFldDens_C "//trim(Num2LStr( defFldDens_C )) ) @@ -875,6 +929,7 @@ subroutine ShowPassedData() call WrScr(" storeHHVel "//TmpFlag ) call WrScr(" WrVTK_in "//trim(Num2LStr( WrVTK_in )) ) call WrScr(" WrVTK_inType "//trim(Num2LStr( WrVTK_inType )) ) + call WrScr(" WrVTK_inDT "//trim(Num2LStr( WrVTK_inDT )) ) call WrScr("-----------------------------------------------------------") end subroutine ShowPassedData @@ -954,6 +1009,24 @@ subroutine CheckNodes(iWT) ! - some checks on hub/nacelle being near middle of the rotor? Not sure if that matters end subroutine CheckNodes + !> Setup points for disk average velocity calculations + subroutine SetDiskAvgPoints(iWT) + integer(IntKi), intent(in) :: iWT + integer(IntKi) :: i,BlNds + real(ReKi) :: R,theta,BLength + ! Calculate relative points on disk (do this once up front to save computational time). + ! NOTE: this is in the XY plane, and will be multiplied by the hub orientation vector + BlNds = ADI%p%AD%rotors(iWT)%NumBlNds + BLength = TwoNorm(ADI%u(1)%AD%rotors(iWT)%BladeMotion(1)%Position(:,BlNds) - ADI%u(1)%AD%rotors(iWT)%HubMotion%Position(:,1)) + R = real(BLength,ReKi) * 0.7_reKi !70% radius + do i=1,NumPtsDiskAvg + theta = pi +(i-1)*TwoPi/NumPtsDiskAvg + DiskAvgVelVars(iWT)%DiskWindPosRel(1,i) = 0.0_ReKi ! Hub X (perpindicular to rotor plane) + DiskAvgVelVars(iWT)%DiskWindPosRel(2,i) = R*cos(theta) ! Hub Y + DiskAvgVelVars(iWT)%DiskWindPosRel(3,i) = R*sin(theta) ! Hub Z (in vertical plane when azimuth=0) + end do + end subroutine SetDiskAvgPoints + END SUBROUTINE ADI_C_Init @@ -1049,7 +1122,16 @@ SUBROUTINE ADI_C_CalcOutput(Time_C, & ! write outputs !------------------------------------------------------- ! Write VTK if requested (animation=2) - if (WrOutputsData%WrVTK > 1_IntKi) call WrVTK_Meshes(ADI%u(1)%AD%rotors(:),(/0.0_SiKi,0.0_SiKi,0.0_SiKi/),ErrStat2,ErrMsg2) + if (WrOutputsData%WrVTK > 1_IntKi) then + ! Check if writing this step (note this may overwrite if we rerun a step in a correction loop) + if ( mod( n_Global, WrOutputsData%n_VTKTime ) == 0 ) THEN + ! increment the current VTK output number if not a correction step, otherwise overwrite previous + if (.not. EqualRealNos( real(Time,DbKi), InputTimePrev_Calc ) ) then + n_VTK = n_VTK + 1_IntKi ! Increment for this write + endif + call WrVTK_Meshes(ADI%u(1)%AD%rotors(:),(/0.0_SiKi,0.0_SiKi,0.0_SiKi/),ErrStat2,ErrMsg2) + endif + endif if (WrOutputsData%fileFmt > idFmtNone) then !FIXME: need some way to overwrite the correction timesteps (for text file)! @@ -1059,6 +1141,9 @@ SUBROUTINE ADI_C_CalcOutput(Time_C, & ! Set error status call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + ! Store info what time we just ran calcs for + InputTimePrev_Calc = Time + CONTAINS logical function Failed() CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -1904,6 +1989,76 @@ end subroutine ShowPassedData end subroutine ADI_C_GetRotorLoads +!=============================================================================================================== +!--------------------------------------------- GetDiskAvgVel --------------------------------------------------- +!=============================================================================================================== +!> Get the disk average velocity for a single rotor (uses the IfW DiskAvgVel routine) +subroutine ADI_C_GetDiskAvgVel(iWT_C, & + DiskAvgVel_C, & + ErrStat_C, ErrMsg_C) BIND (C, NAME='ADI_C_GetDiskAvgVel') + implicit none +#ifndef IMPLICIT_DLLEXPORT +!DEC$ ATTRIBUTES DLLEXPORT :: ADI_C_GetDiskAvgVel +!GCC$ ATTRIBUTES DLLEXPORT :: ADI_C_GetDiskAvgVel +#endif + integer(c_int), intent(in ) :: iWT_C !< Wind turbine / rotor number + real(c_float), intent( out) :: DiskAvgVel_C(3) !< Wind speed vector for disk average [Vx,Vy,Vz] -- (m/s) (global) + integer(c_int), intent( out) :: ErrStat_C + character(kind=c_char), intent( out) :: ErrMsg_C(ErrMsgLen_C) + + ! Local variables + type(MeshType), pointer :: Hub ! HubMotion mesh pointer, for simplicity in code reading + integer(IntKi) :: iWT !< current wind turbine / rotor + integer(IntKi) :: i + integer(IntKi), parameter :: StartNode = 1 ! so all points are calculated + real(ReKi), allocatable :: NoAcc(:,:) ! Placeholder array not used when accelerations not required. + real(ReKi) :: DiskAvgVel(3) !< Wind speed vector for disk average [Vx,Vy,Vz] -- (m/s) (global) + integer(IntKi) :: ErrStat !< aggregated error status + character(ErrMsgLen) :: ErrMsg !< aggregated error message + character(*), parameter :: RoutineName = 'ADI_C_GetDiskAvgVel' !< for error handling + + ! Initialize error handling + ErrStat = ErrID_None + ErrMsg = "" + + ! For debugging the interface: + if (DebugLevel > 0) then + call ShowPassedData() + endif + + ! current turbine number + iWT = int(iWT_c, IntKi) + + ! pointer to make code more readable + Hub => ADI%u(1)%AD%rotors(iWT)%HubMotion + + ! Calculate Disk Avg Velocity + do i=1,NumPtsDiskAvg + DiskAvgVelVars(iWT)%DiskWindPosAbs(:,i) = real(Hub%Position(1:3,1)+Hub%TranslationDisp(1:3,1),ReKi) & + + matmul(real(Hub%Orientation(1:3,1:3,1),ReKi),DiskAvgVelVars(iWT)%DiskWindPosRel(:,i)) + enddo + call IfW_FlowField_GetVelAcc(ADI%m%IW%p%FlowField, StartNode, InputTimePrev_Calc, DiskAvgVelVars(iWT)%DiskWindPosAbs, DiskAvgVelVars(iWT)%DiskWindVel, NoAcc, ErrStat, ErrMsg) + + ! calculate average + DiskAvgVel = sum(DiskAvgVelVars(iWT)%DiskWindVel, dim=2) / REAL(NumPtsDiskAvg,SiKi) + DiskAvgVel_C = real(DiskAvgVel, c_float) + + ! Set error status + call SetErr(ErrStat,ErrMsg,ErrStat_C,ErrMsg_C) + +CONTAINS + !> This subroutine prints out all the variables that are passed in. Use this only + !! for debugging the interface on the Fortran side. + subroutine ShowPassedData() + call WrSCr("") + call WrScr("-----------------------------------------------------------") + call WrScr("Interface debugging: Variables passed in through interface") + call WrScr(" ADI_C_GetDiskAvgVel -- rotor "//trim(Num2LStr(iWT_c))) + call WrScr("-----------------------------------------------------------") + end subroutine ShowPassedData +end subroutine ADI_C_GetDiskAvgVel + + !=================================================================================================================================== ! Internal routines for setting meshes etc. !=================================================================================================================================== @@ -2254,7 +2409,7 @@ subroutine WrVTK_Points(ErrStat3,ErrMsg3) ! Blade point motion (structural mesh from driver) do iBlade=1,Sim%WT(iWT)%NumBlades - call MeshWrVTK(RefPoint, BldStrMotionMesh(iWT)%Mesh(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BldStrMotionMesh'//trim(num2lstr(iBlade)), n_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, BldStrMotionMesh(iWT)%Mesh(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BldStrMotionMesh'//trim(num2lstr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return enddo @@ -2262,20 +2417,20 @@ subroutine WrVTK_Points(ErrStat3,ErrMsg3) if (allocated(rot_u(iWT)%BladeRootMotion)) then do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeRootMotion(iBlade)%Committed) then - call MeshWrVTK(RefPoint, rot_u(iWT)%BladeRootMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BladeRootMotion'//trim(num2lstr(iBlade)), n_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, rot_u(iWT)%BladeRootMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.BladeRootMotion'//trim(num2lstr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return endif enddo endif ! Nacelle (structural point input - if ( rot_u(iWT)%NacelleMotion%Committed ) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleMotion', n_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if ( rot_u(iWT)%NacelleMotion%Committed ) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleMotion', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Free wake if (allocated(ADI%m%AD%FVW_u) .and. iWT==1) then if (allocated(ADI%m%AD%FVW_u(1)%WingsMesh)) then - call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(STATE_CURR)%AD%FVW, ADI%z(STATE_CURR)%AD%FVW, ADI%m%AD%FVW, trim(WrOutputsData%VTK_OutFileRoot)//'.FVW', n_Global, WrOutputsData%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords + call WrVTK_FVW(ADI%p%AD%FVW, ADI%x(STATE_CURR)%AD%FVW, ADI%z(STATE_CURR)%AD%FVW, ADI%m%AD%FVW, trim(WrOutputsData%VTK_OutFileRoot)//'.FVW', n_VTK, WrOutputsData%VTK_tWidth, bladeFrame=.FALSE.) ! bladeFrame==.FALSE. to output in global coords endif end if end subroutine WrVTK_Points @@ -2291,11 +2446,11 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) ErrMsg3 = '' ! TODO: use this routine when it is moved out of the driver and into ADI - ! call AD_WrVTK_Surfaces(ADI%u(1)%AD, ADI%y%AD, RefPoint, ADI%m%VTK_Surfaces, n_Global, WrOutputsData%Root, WrOutputsData%VTK_tWidth, 25, WrOutputsData%VTKHubRad) + ! call AD_WrVTK_Surfaces(ADI%u(1)%AD, ADI%y%AD, RefPoint, ADI%m%VTK_Surfaces, n_VTK, WrOutputsData%Root, WrOutputsData%VTK_tWidth, 25, WrOutputsData%VTKHubRad) ! Nacelle if ( rot_u(iWT)%NacelleMotion%Committed ) then - call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleSurface', n_Global, & + call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.NacelleSurface', n_VTK, & OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, verts=WrOutputsData%VTK_Surface(iWT)%NacelleBox) if (ErrStat3 >= AbortErrLev) return endif @@ -2303,14 +2458,14 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) ! Tower if (rot_u(iWT)%TowerMotion%Committed) then call MeshWrVTK_Ln2Surface (RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.TowerSurface', & - n_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, numSectors, ADI%m%VTK_Surfaces(iWT)%TowerRad ) + n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, numSectors, ADI%m%VTK_Surfaces(iWT)%TowerRad ) if (ErrStat3 >= AbortErrLev) return endif ! Hub if (rot_u(iWT)%HubMotion%Committed) then call MeshWrVTK_PointSurface (RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.HubSurface', & - n_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, & + n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth, & NumSegments=numSectors, radius=WrOutputsData%VTKHubRad) if (ErrStat3 >= AbortErrLev) return endif @@ -2320,7 +2475,7 @@ subroutine WrVTK_Surfaces(ErrStat3,ErrMsg3) do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeMotion(iBlade)%Committed) then call MeshWrVTK_Ln2Surface (RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade))//'Surface', & - n_Global, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth , verts=ADI%m%VTK_Surfaces(iWT)%BladeShape(iBlade)%AirfoilCoords, & + n_VTK, OutputFields, errStat3, errMsg3, WrOutputsData%VTK_tWidth , verts=ADI%m%VTK_Surfaces(iWT)%BladeShape(iBlade)%AirfoilCoords, & Sib=ADI%y%AD%rotors(iWT)%BladeLoad(iBlade) ) if (ErrStat3 >= AbortErrLev) return endif @@ -2336,22 +2491,22 @@ subroutine WrVTK_Lines(ErrStat3,ErrMsg3) ErrMsg3 = '' ! Tower - if (rot_u(iWT)%TowerMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Tower', n_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%TowerMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%TowerMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Tower', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Nacelle meshes - if (rot_u(iWT)%NacelleMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Nacelle', n_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%NacelleMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%NacelleMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Nacelle', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Hub - if (rot_u(iWT)%HubMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Hub', n_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + if (rot_u(iWT)%HubMotion%Committed) call MeshWrVTK(RefPoint, rot_u(iWT)%HubMotion, trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Hub', n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return ! Blades if (allocated(rot_u(iWT)%BladeMotion)) then do iBlade=1,Sim%WT(iWT)%NumBlades if (rot_u(iWT)%BladeMotion(iBlade)%Committed) then - call MeshWrVTK(RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade)), n_Global, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) + call MeshWrVTK(RefPoint, rot_u(iWT)%BladeMotion(iBlade), trim(WrOutputsData%VTK_OutFileRoot)//trim(sWT)//'.Blade'//trim(num2lstr(iBlade)), n_VTK, .true., ErrStat3, ErrMsg3, WrOutputsData%VTK_tWidth) if (ErrStat3 >= AbortErrLev) return endif enddo @@ -2446,6 +2601,8 @@ subroutine ClearTmpStorage() ! if (allocated(NacMotionMesh )) call ClearMeshArr1(NacMotionMesh ) ! if (allocated(NacLoadMesh )) call ClearMeshArr1(NacLoadMesh ) if (allocated(Map_BldStrMotion_2_AD_Blade )) call ClearMeshMapArr2(Map_BldStrMotion_2_AD_Blade ) + ! other stuff + if (allocated(DiskAvgVelVars)) deallocate(DiskAvgVelVars) contains subroutine ClearMeshArr1(MeshName) type(MeshType), allocatable :: MeshName(:) diff --git a/modules/aerodyn/src/AirfoilInfo.f90 b/modules/aerodyn/src/AirfoilInfo.f90 index 5cde187db8..2c6ab6af03 100644 --- a/modules/aerodyn/src/AirfoilInfo.f90 +++ b/modules/aerodyn/src/AirfoilInfo.f90 @@ -483,7 +483,7 @@ SUBROUTINE ReadAFfile ( InitInp, NumCoefsIn, p, ErrStat, ErrMsg, UnEc ) ENDIF ! Reading Boundary layer file for aeroacoustics - CALL ParseVar ( FileInfo, CurLine, 'BL_file' , p%BL_file , ErrStat2, ErrMsg2, UnEc ) + CALL ParseVar ( FileInfo, CurLine, 'BL_file' , p%BL_file , ErrStat2, ErrMsg2, UnEc, IsPath=.true. ) IF (ErrStat2 >= AbortErrLev) p%BL_file = "NOT_SET_IN_AIRFOIL_FILE" !CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( PathIsRelative( p%BL_file ) ) p%BL_file=trim(PriPath)//trim(p%BL_file) @@ -1927,13 +1927,12 @@ subroutine AFI_WrHeader(delim, FileName, unOutFile, ErrStat, ErrMsg) ChanName(i) = 'alphaBreakLower'; ChanUnit(i) = '(deg)'; i = i+1; ChanName(i) = 'CnBreakLower'; ChanUnit(i) = '(-)'; i = i+1; + !$OMP critical(filename) CALL GetNewUnit( unOutFile, ErrStat, ErrMsg ) - IF ( ErrStat /= ErrID_None ) RETURN - - CALL OpenFOutFile ( unOutFile, trim(FileName), ErrStat2, ErrMsg2 ) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return - + if (ErrStat < AbortErrLev) then + CALL OpenFOutFile ( unOutFile, trim(FileName), ErrStat2, ErrMsg2 ) + endif + !$OMP end critical(filename) ! Generate file outputs @@ -2106,11 +2105,12 @@ subroutine AFI_WrTables(AFI_Params,UAMod,OutRootName) ! Write to file - + !$OMP critical(filename) CALL GetNewUnit( unOutFile, ErrStat, ErrMsg ) - IF ( ErrStat /= ErrID_None ) RETURN - - CALL OpenFOutFile ( unOutFile, trim(OutRootName)//'.Coefs.'//trim(num2lstr(iTab))//'.out', ErrStat, ErrMsg ) + if (ErrStat < AbortErrLev) then + CALL OpenFOutFile ( unOutFile, trim(OutRootName)//'.Coefs.'//trim(num2lstr(iTab))//'.out', ErrStat, ErrMsg ) + endif + !$OMP end critical(filename) if (ErrStat >= AbortErrLev) then call WrScr(Trim(ErrMsg)) return diff --git a/modules/aerodyn/src/UnsteadyAero.f90 b/modules/aerodyn/src/UnsteadyAero.f90 index 08b83a2b10..673ba0eddf 100644 --- a/modules/aerodyn/src/UnsteadyAero.f90 +++ b/modules/aerodyn/src/UnsteadyAero.f90 @@ -1438,10 +1438,12 @@ subroutine UA_Init_Outputs(InitInp, p, y, InitOut, errStat, errMsg) ! --- Write to File if ((p%NumOuts > 0) .and. p%UA_OUTS==2) then call WrScr(' UA: Writing separate output file: '//trim(InitInp%OutRootName)//'.out') + !$OMP critical(filename) CALL GetNewUnit( p%unOutFile, ErrStat2, ErrMsg2 ) - call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return - CALL OpenFOutFile ( p%unOutFile, trim(InitInp%OutRootName)//'.out', ErrStat2, ErrMsg2 ) + if (ErrStat2 < AbortErrLev) then + CALL OpenFOutFile ( p%unOutFile, trim(InitInp%OutRootName)//'.UA.out', ErrStat2, ErrMsg2 ) + endif + !$OMP end critical(filename) call SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -4092,7 +4094,7 @@ subroutine UA_WriteAFIParamsToFile(InitInp, AFInfo, ErrStat, ErrMsg) call AFI_WrHeader(delim, trim(InitInp%OutRootName)//'.sum', unOutFile, ErrStat, ErrMsg) if (ErrStat >= AbortErrLev) return - + !...................................................... ! Write the data for each table in each file !...................................................... diff --git a/modules/awae/src/AWAE.f90 b/modules/awae/src/AWAE.f90 index 01ba5e3af0..cec3d20e3e 100644 --- a/modules/awae/src/AWAE.f90 +++ b/modules/awae/src/AWAE.f90 @@ -1443,7 +1443,7 @@ subroutine AWAE_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, errStat, errMsg character(ErrMsgLen) :: errMsg2 character(*), parameter :: RoutineName = 'AWAE_CalcOutput' integer(intKi) :: n, n_high - character(2) :: PlaneNumStr ! 2 digit number of the output plane + character(3) :: PlaneNumStr ! 2 digit number of the output plane CHARACTER(1024) :: FileName INTEGER(IntKi) :: Un ! unit number of opened file @@ -1473,7 +1473,7 @@ subroutine AWAE_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, errStat, errMsg ! XY plane slices do k = 1,p%NOutDisWindXY - write(PlaneNumStr, '(i2.2)') k + write(PlaneNumStr, '(i3.3)') k call ExtractSlice( XYSlice, p%OutDisWindZ(k), p%Z0_low, p%nZ_low, p%nX_low, p%nY_low, p%dZ_low, m%Vdist_low_full, m%outVizXYPlane(:,:,:,1)) ! Create the output vtk file with naming /Low/DisXY.t.vtk FileName = trim(p%OutFileVTKRoot)//".Low.DisXY"//PlaneNumStr//"."//trim(Tstr)//".vtk" @@ -1483,7 +1483,7 @@ subroutine AWAE_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, errStat, errMsg ! YZ plane slices do k = 1,p%NOutDisWindYZ - write(PlaneNumStr, '(i2.2)') k + write(PlaneNumStr, '(i3.3)') k call ExtractSlice( YZSlice, p%OutDisWindX(k), p%X0_low, p%nX_low, p%nY_low, p%nZ_low, p%dX_low, m%Vdist_low_full, m%outVizYZPlane(:,:,:,1)) ! Create the output vtk file with naming /Low/DisYZ.t.vtk FileName = trim(p%OutFileVTKRoot)//".Low.DisYZ"//PlaneNumStr//"."//trim(Tstr)//".vtk" @@ -1493,7 +1493,7 @@ subroutine AWAE_CalcOutput( t, u, p, x, xd, z, OtherState, y, m, errStat, errMsg ! XZ plane slices do k = 1,p%NOutDisWindXZ - write(PlaneNumStr, '(i2.2)') k + write(PlaneNumStr, '(i3.3)') k call ExtractSlice( XZSlice, p%OutDisWindY(k), p%Y0_low, p%nY_low, p%nX_low, p%nZ_low, p%dY_low, m%Vdist_low_full, m%outVizXZPlane(:,:,:,1)) ! Create the output vtk file with naming /Low/DisXZ.t.vtk FileName = trim(p%OutFileVTKRoot)//".Low.DisXZ"//PlaneNumStr//"."//trim(Tstr)//".vtk" diff --git a/modules/awae/src/AWAE_IO.f90 b/modules/awae/src/AWAE_IO.f90 index 1755aeac78..b51febcc61 100644 --- a/modules/awae/src/AWAE_IO.f90 +++ b/modules/awae/src/AWAE_IO.f90 @@ -505,8 +505,10 @@ SUBROUTINE AWAE_PrintSum( p, u, y, ErrStat, ErrMsg ) ! Open the summary file and give it a heading. + !$OMP critical(fileopen) CALL GetNewUnit( UnSu, ErrStat, ErrMsg ) CALL OpenFOutFile ( UnSu, TRIM( p%OutFileRoot )//'.sum', ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF ( ErrStat >= AbortErrLev ) RETURN diff --git a/modules/beamdyn/src/BeamDyn_IO.f90 b/modules/beamdyn/src/BeamDyn_IO.f90 index 29c37404e3..0b52e2fabe 100644 --- a/modules/beamdyn/src/BeamDyn_IO.f90 +++ b/modules/beamdyn/src/BeamDyn_IO.f90 @@ -588,9 +588,11 @@ SUBROUTINE BD_ReadPrimaryFile(InputFile,InputFileData,OutFileRoot,UnEc,ErrStat,E CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + !$OMP critical(filename) CALL GetNewUnit(UnIn,ErrStat2,ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL OpenFInpFile(UnIn,InputFile,ErrStat2,ErrMsg2) + !$OMP end critical(filename) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) then call cleanup() @@ -1050,9 +1052,11 @@ SUBROUTINE BD_ReadBladeFile(BldFile,BladeInputFileData,UnEc,ErrStat,ErrMsg) ErrStat = ErrID_None ErrMsg = "" + !$OMP critical(filename) CALL GetNewUnit(UnIn,ErrStat2,ErrMsg2) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL OpenFInpFile (UnIn,BldFile,ErrStat2,ErrMsg2) + !$OMP end critical(filename) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) if (ErrStat >= AbortErrLev) then return @@ -1942,8 +1946,10 @@ SUBROUTINE BD_PrintSum( p, x, OtherState, m, InitInp, ErrStat, ErrMsg ) ! Open the summary file and give it a heading. + !$OMP critical(filename) CALL GetNewUnit( UnSu, ErrStat, ErrMsg ) CALL OpenFOutFile ( UnSu, TRIM( InitInp%RootName )//'.sum.yaml', ErrStat, ErrMsg ) + !$OMP end critical(filename) IF ( ErrStat >= AbortErrLev ) RETURN ! Heading: diff --git a/modules/elastodyn/src/ElastoDyn.f90 b/modules/elastodyn/src/ElastoDyn.f90 index cbfdace5c6..4bce771cd6 100644 --- a/modules/elastodyn/src/ElastoDyn.f90 +++ b/modules/elastodyn/src/ElastoDyn.f90 @@ -9759,10 +9759,12 @@ SUBROUTINE ED_PrintSum( p, OtherState, ErrStat, ErrMsg ) ! Open the summary file and give it a heading. + !$OMP critical(filename) CALL GetNewUnit( UnSu, ErrStat, ErrMsg ) - IF ( ErrStat /= ErrID_None ) RETURN - - CALL OpenFOutFile ( UnSu, TRIM( p%RootName )//'.sum', ErrStat, ErrMsg ) + if (ErrStat < AbortErrLev) then + CALL OpenFOutFile ( UnSu, TRIM( p%RootName )//'.sum', ErrStat, ErrMsg ) + endif + !$OMP end critical(filename) IF ( ErrStat /= ErrID_None ) RETURN diff --git a/modules/elastodyn/src/ElastoDyn_IO.f90 b/modules/elastodyn/src/ElastoDyn_IO.f90 index 5e32e39bd5..7cadb7c7d9 100644 --- a/modules/elastodyn/src/ElastoDyn_IO.f90 +++ b/modules/elastodyn/src/ElastoDyn_IO.f90 @@ -2064,12 +2064,13 @@ SUBROUTINE ReadTowerFile( TwrFile, InputFileData, UnEc, ErrStat, ErrMsg ) ErrMsg = "" + !$OMP critical(filename) CALL GetNewUnit( UnIn, ErrStat, ErrMsg ) - IF ( ErrStat >= AbortErrLev ) RETURN - + IF ( ErrStat < AbortErrLev ) THEN ! Open the tower input file. - - CALL OpenFInpFile ( UnIn, TwrFile, ErrStat2, ErrMsg2 ) + CALL OpenFInpFile ( UnIn, TwrFile, ErrStat2, ErrMsg2 ) + ENDIF + !$OMP end critical(filename) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CALL Cleanup() @@ -2406,14 +2407,13 @@ SUBROUTINE ReadPrimaryFile( InputFile, InputFileData, BldFile, FurlFile, TwrFile ! Get an available unit number for the file. - + !$OMP critical(filename) CALL GetNewUnit( UnIn, ErrStat, ErrMsg ) - IF ( ErrStat >= AbortErrLev ) RETURN - - + IF ( ErrStat < AbortErrLev ) THEN ! Open the Primary input file. - - CALL OpenFInpFile ( UnIn, InputFile, ErrStat2, ErrMsg2 ) + CALL OpenFInpFile ( UnIn, InputFile, ErrStat2, ErrMsg2 ) + ENDIF + !$OMP end critical(filename) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CALL Cleanup() diff --git a/modules/hydrodyn/src/HydroDyn_Output.f90 b/modules/hydrodyn/src/HydroDyn_Output.f90 index 45a7450afa..113e7048bf 100644 --- a/modules/hydrodyn/src/HydroDyn_Output.f90 +++ b/modules/hydrodyn/src/HydroDyn_Output.f90 @@ -797,10 +797,10 @@ SUBROUTINE HDOut_OpenSum( UnSum, SummaryName, HD_Prog, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" - + !$OMP critical(fileopen) CALL GetNewUnit( UnSum ) - CALL OpenFOutFile ( UnSum, SummaryName, ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat >=AbortErrLev) RETURN @@ -1123,9 +1123,11 @@ SUBROUTINE HDOut_OpenOutput( HydroDyn_ProgDesc, OutRootName, p, InitOut, ErrSta ! Open the file for output OutFileName = TRIM(OutRootName)//'.out' - CALL GetNewUnit( p%UnOutFile ) + !$OMP critical(fileopen) + CALL GetNewUnit( p%UnOutFile ) CALL OpenFOutFile ( p%UnOutFile, OutFileName, ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat >=AbortErrLev) RETURN diff --git a/modules/hydrodyn/src/WAMIT2.f90 b/modules/hydrodyn/src/WAMIT2.f90 index 8155bfa691..a1fe008229 100644 --- a/modules/hydrodyn/src/WAMIT2.f90 +++ b/modules/hydrodyn/src/WAMIT2.f90 @@ -3106,16 +3106,13 @@ SUBROUTINE Read_DataFile3D( InitInp, Filename3D, Data3D, ErrStat, Errmsg ) !------------------------------------------------------------------------------ ! Find a unit number to use + !$OMP critical(fileopen) CALL GetNewUnit(UnitDataFile,ErrStatTmp,ErrMsgTmp) - CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat, ErrMsg, RoutineName) - IF ( ErrStat >= AbortErrLev ) THEN - UnitDataFile = -1 - CALL CleanUp() - RETURN - ENDIF - - ! Open the file - CALL OpenFInpFile( UnitDataFile, TRIM(Filename3D), ErrStat, ErrMsg ) ! Open file containing mean drift information + if (ErrStatTmp < AbortErrLev) then + ! Open the file + CALL OpenFInpFile( UnitDataFile, TRIM(Filename3D), ErrStat, ErrMsg ) ! Open file containing mean drift information + endif + !$OMP end critical(fileopen) CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) THEN UnitDataFile = -1 @@ -3780,15 +3777,13 @@ SUBROUTINE Read_DataFile4D( InitInp, Filename4D, Data4D, ErrStat, Errmsg ) !------------------------------------------------------------------------------ ! Find a unit number to use + !$OMP critical(fileopen) CALL GetNewUnit(UnitDataFile,ErrStatTmp,ErrMsgTmp) - CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat, ErrMsg, RoutineName) - IF ( ErrStat >= AbortErrLev ) THEN - CALL CleanUp() - RETURN - ENDIF - + if (ErrStatTmp < AbortErrLev) then ! Open the file - CALL OpenFInpFile( UnitDataFile, TRIM(Filename4D), ErrStatTmp, ErrMsgTmp ) ! Open file containing mean drift information + CALL OpenFInpFile( UnitDataFile, TRIM(Filename4D), ErrStatTmp, ErrMsgTmp ) ! Open file containing mean drift information + endif + !$OMP end critical(fileopen) CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat, ErrMsg, RoutineName) IF ( ErrStat >= AbortErrLev ) THEN CLOSE( UnitDataFile ) diff --git a/modules/inflowwind/src/IfW_FlowField.f90 b/modules/inflowwind/src/IfW_FlowField.f90 index 9861a9692c..546359d16e 100644 --- a/modules/inflowwind/src/IfW_FlowField.f90 +++ b/modules/inflowwind/src/IfW_FlowField.f90 @@ -1628,6 +1628,7 @@ subroutine Grid4DField_GetVel(G4D, Time, Position, Velocity, ErrStat, ErrMsg) real(ReKi) :: P(3, 16) ! Point values real(ReKi) :: tmp integer(IntKi) :: i + character(60) :: PtLoc ErrStat = ErrID_None ErrMsg = "" @@ -1664,11 +1665,12 @@ subroutine Grid4DField_GetVel(G4D, Time, Position, Velocity, ErrStat, ErrMsg) do i = 1, 4 if (Indx_Lo(i) <= 0) then Indx_Lo(i) = 1 - call SetErrStat(ErrID_Fatal, 'Outside the grid bounds.', ErrStat, ErrMsg, RoutineName) + write(PtLoc,'(A1,3(f8.2,A1))') '(',Position(1),',',Position(2),',',Position(3),')' + call SetErrStat(ErrID_Fatal, 'Outside the grid bounds: '//trim(PtLoc), ErrStat, ErrMsg, RoutineName) return elseif (Indx_Lo(i) >= G4D%n(i)) then - Indx_Lo(i) = max(G4D%n(i) - 1, 1) ! make sure it's a valid index - call SetErrStat(ErrID_Fatal, 'Outside the grid bounds.', ErrStat, ErrMsg, RoutineName) + write(PtLoc,'(A1,3(f8.2,A1))') '(',Position(1),',',Position(2),',',Position(3),')' + call SetErrStat(ErrID_Fatal, 'Outside the grid bounds: '//trim(PtLoc), ErrStat, ErrMsg, RoutineName) return end if Indx_Hi(i) = min(Indx_Lo(i) + 1, G4D%n(i)) ! make sure it's a valid index diff --git a/modules/inflowwind/src/InflowWind.f90 b/modules/inflowwind/src/InflowWind.f90 index a8a23fe522..de2d713a3b 100644 --- a/modules/inflowwind/src/InflowWind.f90 +++ b/modules/inflowwind/src/InflowWind.f90 @@ -176,6 +176,15 @@ SUBROUTINE InflowWind_Init( InitInp, InputGuess, p, ContStates, DiscStates, Cons CALL InflowWind_ValidateInput( InitInp, InputFileData, TmpErrStat, TmpErrMsg ); if (Failed()) return + ! Disable Lidar if not allowed (FAST.Farm doesn't allow this) + if (.not. InitInp%LidarEnabled) then + if (p%lidar%SensorType /= SensorType_None) then + call WrScr(' WARNING: LiDAR cannot be used with this instance of InflowWind (not usable with FAST.Farm).') + call WrScr(' --> Disabling LiDAR.') + p%lidar%SensorType = SensorType_None + end if + endif + ! initialize sensor data: p%lidar%SensorType = InputFileData%SensorType IF (InputFileData%SensorType /= SensorType_None) THEN diff --git a/modules/inflowwind/src/InflowWind.txt b/modules/inflowwind/src/InflowWind.txt index 6f92965839..924573b8f5 100644 --- a/modules/inflowwind/src/InflowWind.txt +++ b/modules/inflowwind/src/InflowWind.txt @@ -99,6 +99,7 @@ typedef ^ ^ IntKi MHK typedef ^ ^ ReKi WtrDpth - - - "Water depth" m typedef ^ ^ ReKi MSL2SWL - - - "Mean sea level to still water level" m typedef ^ ^ LOGICAL BoxExceedAllow - .FALSE. - "Flag to allow Extrapolation winds outside box starting at this index (for OLAF wakes and LidarSim)" - +typedef ^ ^ LOGICAL LidarEnabled - .false. - "Enable LiDAR for this instance of InflowWind? (FAST.Farm, ADI, and InflowWind driver/library are not compatible)" - # Init Output diff --git a/modules/inflowwind/src/InflowWind_IO.f90 b/modules/inflowwind/src/InflowWind_IO.f90 index 44c2e03f81..afd7281002 100644 --- a/modules/inflowwind/src/InflowWind_IO.f90 +++ b/modules/inflowwind/src/InflowWind_IO.f90 @@ -534,12 +534,13 @@ subroutine IfW_TurbSim_Init(InitInp, SumFileUnit, G3D, FileDat, ErrStat, ErrMsg) !---------------------------------------------------------------------------- ! Get a unit number to use for the wind file + !$OMP critical(fileopen) call GetNewUnit(WindFileUnit, TmpErrStat, TmpErrMsg) - call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return - - ! Open binary file - call OpenBInpFile(WindFileUnit, TRIM(InitInp%WindFileName), TmpErrStat, TmpErrMsg) + if (TmpErrStat < AbortErrLev) then + ! Open binary file + call OpenBInpFile(WindFileUnit, TRIM(InitInp%WindFileName), TmpErrStat, TmpErrMsg) + endif + !$OMP end critical(fileopen) call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -899,16 +900,17 @@ subroutine IfW_HAWC_Init(InitInp, SumFileUnit, G3D, FileDat, ErrStat, ErrMsg) TRIM(Num2LStr(G3D%GridBase + G3D%ZHWid*2))// & ' m above ground) with a characteristic wind speed of '//TRIM(Num2LStr(G3D%MeanWS))//' m/s. ') - ! Get a unit number to use for the wind file - call GetNewUnit(WindFileUnit, TmpErrStat, TmpErrMsg) - call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return - ! Loop through wind components (X, Y, Z) do IC = 1, G3D%NComp - ! Open wind file for this component - call OpenBInpFile(WindFileUnit, InitInp%WindFileName(IC), TmpErrStat, TmpErrMsg) + ! Get a unit number to use for the wind file + !$OMP critical(fileopen) + call GetNewUnit(WindFileUnit, TmpErrStat, TmpErrMsg) + if (TmpErrStat < AbortErrLev) then + ! Open wind file for this component + call OpenBInpFile(WindFileUnit, InitInp%WindFileName(IC), TmpErrStat, TmpErrMsg) + endif + !$OMP end critical(fileopen) call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return @@ -1138,16 +1140,18 @@ subroutine IfW_Bladed_Init(InitInp, SumFileUnit, InitOut, G3D, FileDat, ErrStat, end if ! Get a unit number to use + !$OMP critical(fileopen) call GetNewUnit(UnitWind, TmpErrStat, TmpErrMsg) - call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) return + if (TmpErrStat < AbortErrLev) then !---------------------------------------------------------------------------- ! Open the binary file, read its "header" (first 2-byte integer) to ! determine what format binary file it is, and close it. !---------------------------------------------------------------------------- - call OpenBInpFile(UnitWind, TRIM(BinFileName), TmpErrStat, TmpErrMsg) + call OpenBInpFile(UnitWind, TRIM(BinFileName), TmpErrStat, TmpErrMsg) + endif + !$OMP end critical(fileopen) call SetErrStat(TmpErrStat, TmpErrMsg, ErrStat, ErrMsg, RoutineName) if (ErrStat >= AbortErrLev) return diff --git a/modules/inflowwind/src/InflowWind_Subs.f90 b/modules/inflowwind/src/InflowWind_Subs.f90 index e583535f8a..39e95f239b 100644 --- a/modules/inflowwind/src/InflowWind_Subs.f90 +++ b/modules/inflowwind/src/InflowWind_Subs.f90 @@ -317,7 +317,7 @@ 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 ParseVar( InFileInfo, CurLine, "FileName_Uni", InputFileData%Uniform_FileName, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. ) 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 @@ -339,7 +339,7 @@ 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 ParseVar( InFileInfo, CurLine, "FileName_BTS", InputFileData%TSFF_FileName, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. ) 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 @@ -355,7 +355,7 @@ 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 ParseVar( InFileInfo, CurLine, "FilenameRoot", InputFileData%BladedFF_FileName, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. ) if (Failed()) return IF ( PathIsRelative( InputFileData%BladedFF_FileName ) ) InputFileData%BladedFF_FileName = TRIM(PriPath)//TRIM(InputFileData%BladedFF_FileName) @@ -383,15 +383,15 @@ 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 ParseVar( InFileInfo, CurLine, "FileName_u", InputFileData%HAWC_FileName_u, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. ) 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 ParseVar( InFileInfo, CurLine, "FileName_v", InputFileData%HAWC_FileName_v, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. ) 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 ParseVar( InFileInfo, CurLine, "FileName_w", InputFileData%HAWC_FileName_w, TmpErrStat, TmpErrMsg, UnEc, IsPath=.true. ) if (Failed()) return IF ( PathIsRelative( InputFileData%HAWC_FileName_w ) ) InputFileData%HAWC_FileName_w = TRIM(PriPath)//TRIM(InputFileData%HAWC_FileName_w) @@ -952,6 +952,7 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM ! Temporary variables INTEGER(IntKi) :: TmpErrStat !< Temporary error status for subroutine and function calls CHARACTER(ErrMsgLen) :: TmpErrMsg !< Temporary error message for subroutine and function calls + integer(IntKi) :: NumPtsAvg !< Number of points to use for disk average vel (1 if no radius) ! Local variables INTEGER(IntKi) :: I !< Generic counter @@ -1011,28 +1012,34 @@ SUBROUTINE InflowWind_SetParameters( InitInp, InputFileData, p, m, ErrStat, ErrM CALL AllocAry( m%y_Hub%VelocityUVW, 3, 1, "Array of velocities for hub values", TmpErrStat, TmpErrMsg ) CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) - CALL AllocAry( m%u_Avg%PositionXYZ, 3, IfW_NumPtsAvg, "Array of positions for rotor-averaged values", TmpErrStat, TmpErrMsg ) + ! Disk Velocity calculations + if (InitInp%RadAvg < 0.0_ReKi) then + NumPtsAvg = 1_IntKi ! Use only hub point + else + NumPtsAvg = IfW_NumPtsAvg ! Use a field of points for disk average calculations + endif + + CALL AllocAry( m%u_Avg%PositionXYZ, 3, NumPtsAvg, "Array of positions for rotor-averaged values", TmpErrStat, TmpErrMsg ) CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) - CALL AllocAry( m%y_Avg%VelocityUVW, 3, IfW_NumPtsAvg, "Array of velocities for rotor-averaged values", TmpErrStat, TmpErrMsg ) + CALL AllocAry( m%y_Avg%VelocityUVW, 3, NumPtsAvg, "Array of velocities for rotor-averaged values", TmpErrStat, TmpErrMsg ) CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) - CALL AllocAry( p%PositionAvg, 3, IfW_NumPtsAvg, "Array of positions for computing average wind speed", TmpErrStat, TmpErrMsg ) + CALL AllocAry( p%PositionAvg, 3, NumPtsAvg, "Array of positions for computing average wind speed", TmpErrStat, TmpErrMsg ) CALL SetErrStat(TmpErrStat,TmpErrMsg,ErrStat,ErrMsg,RoutineName) IF ( ErrStat>= AbortErrLev ) RETURN if (InitInp%RadAvg < 0.0_ReKi) then - R = max(1.0_ReKi, InputFileData%Uniform_RefLength)/2.0_ReKi ! We'll use this as a guess for the rotor radius + p%PositionAvg = 0.0_ReKi ! Use only hub else - R = InitInp%RadAvg + ! Calculate a ring of points at 70 rotor radius + R = InitInp%RadAvg * 0.7_ReKi !70% radius + do i=1,NumPtsAvg + theta = pi +(i-1)*TwoPi/NumPtsAvg + p%PositionAvg(1,i) = 0.0_ReKi ! Hub X (perpindicular to rotor plane) + p%PositionAvg(2,i) = R*cos(theta) ! Hub Y + p%PositionAvg(3,i) = R*sin(theta) ! Hub Z (in vertical plane when azimuth=0) + end do end if - R = R * 0.7_ReKi !70% radius - - do i=1,IfW_NumPtsAvg - theta = pi +(i-1)*TwoPi/IfW_NumPtsAvg - p%PositionAvg(1,i) = R*cos(theta) - p%PositionAvg(2,i) = R*sin(theta) - p%PositionAvg(3,i) = 0.0_ReKi - end do p%OutputAccel = InitInp%OutputAccel @@ -1553,8 +1560,10 @@ SUBROUTINE InflowWind_OpenSumFile( SumFileUnit, SummaryName, IfW_Prog, WindType, ErrMsg = "" SumFileUnit = -1 + !$OMP critical(fileopen) CALL GetNewUnit( SumFileUnit ) CALL OpenFOutFile ( SumFileUnit, SummaryName, ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat >=AbortErrLev) RETURN @@ -1676,7 +1685,7 @@ SUBROUTINE InflowWind_GetRotorSpatialAverage( Time, InputData, p, x, xd, z, Othe m%u_Avg%HubPosition = InputData%HubPosition m%u_Avg%HubOrientation = InputData%HubOrientation - do i = 1, IfW_NumPtsAvg + do i=1,size(m%u_Avg%PositionXYZ,DIM=2) m%u_Avg%PositionXYZ(:,i) = InputData%HubPosition + & matmul(InputData%HubOrientation,p%PositionAvg(:,i)) end do diff --git a/modules/inflowwind/src/InflowWind_Types.f90 b/modules/inflowwind/src/InflowWind_Types.f90 index d73ce28f27..646d366064 100644 --- a/modules/inflowwind/src/InflowWind_Types.f90 +++ b/modules/inflowwind/src/InflowWind_Types.f90 @@ -118,6 +118,7 @@ MODULE InflowWind_Types REAL(ReKi) :: WtrDpth = 0.0_ReKi !< Water depth [m] REAL(ReKi) :: MSL2SWL = 0.0_ReKi !< Mean sea level to still water level [m] LOGICAL :: BoxExceedAllow = .FALSE. !< Flag to allow Extrapolation winds outside box starting at this index (for OLAF wakes and LidarSim) [-] + LOGICAL :: LidarEnabled = .false. !< Enable LiDAR for this instance of InflowWind? (FAST.Farm, ADI, and InflowWind driver/library are not compatible) [-] END TYPE InflowWind_InitInputType ! ======================= ! ========= InflowWind_InitOutputType ======= @@ -521,6 +522,7 @@ subroutine InflowWind_CopyInitInput(SrcInitInputData, DstInitInputData, CtrlCode DstInitInputData%WtrDpth = SrcInitInputData%WtrDpth DstInitInputData%MSL2SWL = SrcInitInputData%MSL2SWL DstInitInputData%BoxExceedAllow = SrcInitInputData%BoxExceedAllow + DstInitInputData%LidarEnabled = SrcInitInputData%LidarEnabled end subroutine subroutine InflowWind_DestroyInitInput(InitInputData, ErrStat, ErrMsg) @@ -565,6 +567,7 @@ subroutine InflowWind_PackInitInput(RF, Indata) call RegPack(RF, InData%WtrDpth) call RegPack(RF, InData%MSL2SWL) call RegPack(RF, InData%BoxExceedAllow) + call RegPack(RF, InData%LidarEnabled) if (RegCheckErr(RF, RoutineName)) return end subroutine @@ -591,6 +594,7 @@ subroutine InflowWind_UnPackInitInput(RF, OutData) call RegUnpack(RF, OutData%WtrDpth); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%MSL2SWL); if (RegCheckErr(RF, RoutineName)) return call RegUnpack(RF, OutData%BoxExceedAllow); if (RegCheckErr(RF, RoutineName)) return + call RegUnpack(RF, OutData%LidarEnabled); if (RegCheckErr(RF, RoutineName)) return end subroutine subroutine InflowWind_CopyInitOutput(SrcInitOutputData, DstInitOutputData, CtrlCode, ErrStat, ErrMsg) diff --git a/modules/nwtc-library/src/NWTC_IO.f90 b/modules/nwtc-library/src/NWTC_IO.f90 index e2b42cc264..8c8f522d04 100644 --- a/modules/nwtc-library/src/NWTC_IO.f90 +++ b/modules/nwtc-library/src/NWTC_IO.f90 @@ -62,7 +62,7 @@ MODULE NWTC_IO CHARACTER(99) :: ProgVer = ' ' !< The version (including date) of the calling program. DO NOT USE THIS IN NEW PROGRAMS CHARACTER(1), PARAMETER :: Tab = CHAR( 9 ) !< The tab character. CHARACTER(*), PARAMETER :: CommChars = '!#%' !< Comment characters that mark the end of useful input - INTEGER(IntKi), PARAMETER :: NWTC_SizeOfNumWord = 200 !< maximum length of the words containing numeric input (for ParseVar routines) + INTEGER(IntKi), PARAMETER :: NWTC_SizeOfNumWord = 256 !< maximum length of the words containing numeric input (for ParseVar routines) ! Parameters for writing to echo files (in this module only) @@ -2060,7 +2060,7 @@ END SUBROUTINE GetTokens !! It uses spaces, tabs, commas, semicolons, single quotes, and double quotes ("whitespace") !! as word separators. If there aren't NumWords in the line, the remaining array elements will remain empty. !! Use CountWords (nwtc_io::countwords) to count the number of words in a line. - SUBROUTINE GetWords ( Line, Words, NumWords, NumFound ) + SUBROUTINE GetWords ( Line, Words, NumWords, NumFound, IgnoreQuotes ) ! Argument declarations. @@ -2069,68 +2069,95 @@ SUBROUTINE GetWords ( Line, Words, NumWords, NumFound ) CHARACTER(*), INTENT(IN) :: Line !< The string to search. CHARACTER(*), INTENT(OUT) :: Words(NumWords) !< The array of found words. INTEGER, OPTIONAL, INTENT(OUT) :: NumFound !< The number of words found + LOGICAL, OPTIONAL, INTENT(IN) :: IgnoreQuotes !< Flag to ignore quotes (process as whitespace) - ! Local declarations. - - INTEGER :: Ch ! Character position within the string. - INTEGER :: IW ! Word index. - INTEGER :: NextWhite ! The location of the next whitespace in the string. - + INTEGER :: iWord ! Word index. + INTEGER :: i ! Character index in line. + INTEGER :: iChar ! Character index in word. + CHARACTER(len=1) :: Char ! Current character + LOGICAL :: InQuotes ! Flag indicating text is within quotes + LOGICAL :: IgnoreQuotesLoc ! Local flag to ignore quotes + ! Initialize number of words found to zero if present + if (present(NumFound)) NumFound = 0 - ! Let's prefill the array with blanks. + ! If no text on line, return + if (len_trim(Line) == 0) return - DO IW=1,NumWords - Words(IW) = ' ' - END DO ! IW + ! If ignore quotes is present, set local flag, otherwise true + if (present(IgnoreQuotes)) then + IgnoreQuotesLoc = IgnoreQuotes + else + IgnoreQuotesLoc = .true. + end if - IW = 0 + ! Let's prefill the array with blanks + do iWord = 1, NumWords + Words(iWord) = ' ' + end do + ! Initialize word index to first word + iWord = 1 - ! Let's make sure we have text on this line. - - IF ( LEN_TRIM( Line ) > 0 ) THEN - - ! Parse words separated by any combination of spaces, tabs, commas, - ! semicolons, single quotes, and double quotes ("whitespace"). - - Ch = 0 - - DO - - NextWhite = SCAN( Line(Ch+1:) , ' ,;''"'//Tab ) - - IF ( NextWhite > 1 ) THEN - - IW = IW + 1 - Words(IW) = Line(Ch+1:Ch+NextWhite-1) - if (NextWhite > len(words(iw)) ) then - call ProgWarn('Error reading field from file. There are too many characters in the input file to store in the field. Value may be truncated.') - end if - - IF ( IW == NumWords ) EXIT - - Ch = Ch + NextWhite - - ELSE IF ( NextWhite == 1 ) THEN - - Ch = Ch + 1 + ! Initialize index within word + iChar = 0 + + ! Initialize in quotes to false + InQuotes = .false. + + ! Loop through characters in line + do i = 1, len_trim(line) + + ! Get current character + Char = Line(i:i) + + ! Select based on character + select case (Char) + case ('"', "'") ! Double quotes, single quotes + if (IgnoreQuotesLoc .or. InQuotes) then + InQuotes = .false. + if (iChar > 0) then + ! If requested number of words found, exit; otherwise, new word + if (iWord == NumWords) exit + iWord = iWord + 1 + iChar = 0 + end if + else + InQuotes = .true. + end if + cycle + + ! Word separator + case (',', ';', ' ', Tab) ! Comma, semicolon, space, tab + ! If in quotes, keep these in word + if (.not. InQuotes) then + if (iChar > 0) then + ! If requested number of words found, exit; otherwise, new word + if (iWord == NumWords) exit + iWord = iWord + 1 + iChar = 0 + end if + cycle + end if - CYCLE + end select - ELSE + ! Increment character index + iChar = iChar + 1 - EXIT + ! If index is larger than length of word, continue + if (iChar > len(words(iWord))) then + call ProgWarn('Error reading field from file. There are too many characters in the input file to store in the field. Value may be truncated. '//Line) + cycle + end if - END IF + ! Add character to word + Words(iWord)(iChar:iChar) = Char - END DO - - END IF + end do - IF (PRESENT(NumFound)) NumFound = IW + if (present(NumFound)) NumFound = iWord - RETURN END SUBROUTINE GetWords !======================================================================= !> This subroutine is used to compare a header line (`HeaderLine`) with a list of column names. @@ -2467,6 +2494,7 @@ SUBROUTINE OpenEcho ( Un, OutFile, ErrStat, ErrMsg, ProgVer ) ! Get a unit number for the echo file: + !$OMP critical(fileopenNWTCio) IF ( Un < 0 ) THEN CALL GetNewUnit( Un, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2,ErrStat, ErrMsg, RoutineName ) @@ -2477,6 +2505,7 @@ SUBROUTINE OpenEcho ( Un, OutFile, ErrStat, ErrMsg, ProgVer ) CALL OpenFOutFile( Un, OutFile, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2, ErrMsg2,ErrStat, ErrMsg, RoutineName ) + !$OMP end critical(fileopenNWTCio) IF ( ErrStat >= AbortErrLev ) RETURN @@ -2958,7 +2987,7 @@ END SUBROUTINE ParseCom !! !! WARNING: This routine assumes the "words" containing the variable name and value are <= 20 characters. \n !! Use ParseVar (nwtc_io::parsevar) instead of directly calling a specific routine in the generic interface. - SUBROUTINE ParseChVar ( FileInfo, LineNum, ExpVarName, Var, ErrStat, ErrMsg, UnEc ) + SUBROUTINE ParseChVar ( FileInfo, LineNum, ExpVarName, Var, ErrStat, ErrMsg, UnEc, IsPath ) ! Arguments declarations. @@ -2967,6 +2996,7 @@ SUBROUTINE ParseChVar ( FileInfo, LineNum, ExpVarName, Var, ErrStat, ErrMsg, UnE INTEGER(IntKi), INTENT(INOUT) :: LineNum !< The number of the line to parse. INTEGER, INTENT(IN), OPTIONAL :: UnEc !< I/O unit for echo file. If present and > 0, write to UnEc. + LOGICAL, INTENT(IN), OPTIONAL :: IsPath !< Flag indicating that string is a path. CHARACTER(*), INTENT(OUT) :: Var !< The variable to receive the input value. CHARACTER(*), INTENT(OUT) :: ErrMsg !< The error message, if ErrStat /= 0. @@ -2979,6 +3009,7 @@ SUBROUTINE ParseChVar ( FileInfo, LineNum, ExpVarName, Var, ErrStat, ErrMsg, UnE INTEGER(IntKi) :: ErrStatLcl ! Error status local to this routine. INTEGER(IntKi) :: NameIndx ! The index into the Words array that points to the variable name. + LOGICAL :: IgnoreQuotes CHARACTER(NWTC_SizeOfNumWord) :: Words (2) ! The two "words" parsed from the line. CHARACTER(ErrMsgLen) :: ErrMsg2 @@ -2998,8 +3029,12 @@ SUBROUTINE ParseChVar ( FileInfo, LineNum, ExpVarName, Var, ErrStat, ErrMsg, UnE RETURN END IF - - CALL GetWords ( FileInfo%Lines(LineNum), Words, 2 ) ! Read the first two words in Line. + if (present(IsPath)) then + IgnoreQuotes = .not. IsPath + else + IgnoreQuotes = .true. + end if + CALL GetWords ( FileInfo%Lines(LineNum), Words, 2, IgnoreQuotes=IgnoreQuotes ) ! Read the first two words in Line. IF ( Words(2) == '' .and. (LEN_TRIM(ExpVarName) > 0) ) THEN CALL SetErrStat ( ErrID_Fatal, 'A fatal error occurred when parsing data from "' & //TRIM( FileInfo%FileList(FileInfo%FileIndx(LineNum)) )//'".'//NewLine// & @@ -4697,11 +4732,13 @@ RECURSIVE SUBROUTINE ReadComFile ( FileInfo, FileIndx, AryInd, StartLine, LastLi RETURN END IF + !$OMP critical(fileopenNWTCio) CALL GetNewUnit ( UnIn, ErrStatLcl, ErrMsg2 ) CALL SetErrStat( ErrStatLcl, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL OpenFInpFile ( UnIn, FileInfo%FileList(FileIndx), ErrStatLcl, ErrMsg2 ) CALL SetErrStat( ErrStatLcl, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + !$OMP end critical(fileopenNWTCio) IF ( ErrStat >= AbortErrLev ) RETURN @@ -6631,9 +6668,11 @@ RECURSIVE SUBROUTINE ScanComFile ( FirstFile, ThisFile, LastFile, StartLine, Las ! Open the input file. UnIn = -1 + !$OMP critical(fileopenNWTCio) CALL GetNewUnit ( UnIn, ErrStatLcl, ErrMsg2 ) CALL OpenFInpFile ( UnIn, Filename, ErrStatLcl, ErrMsg2 ) + !$OMP end critical(fileopenNWTCio) IF ( ErrStatLcl /= 0 ) THEN CALL SetErrStat( ErrStatLcl, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL Cleanup() @@ -6941,6 +6980,7 @@ SUBROUTINE WrBinFAST(FileName, FileID, DescStr, ChanName, ChanUnit, TimeData, Al ! Generate the unit number for the binary file UnIn = 0 + !$OMP critical(fileopenNWTCio) CALL GetNewUnit( UnIn, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) @@ -6950,6 +6990,7 @@ SUBROUTINE WrBinFAST(FileName, FileID, DescStr, ChanName, ChanUnit, TimeData, Al CALL OpenBOutFile ( UnIn, TRIM(FileName), ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + !$OMP end critical(fileopenNWTCio) IF ( ErrStat >= AbortErrLev ) THEN CALL Cleanup() RETURN diff --git a/modules/nwtc-library/src/VTK.f90 b/modules/nwtc-library/src/VTK.f90 index 19f3d967a9..a407c9d770 100644 --- a/modules/nwtc-library/src/VTK.f90 +++ b/modules/nwtc-library/src/VTK.f90 @@ -93,8 +93,10 @@ SUBROUTINE WrVTK_header( FileName, NumberOfPoints, NumberOfLines, NumberOfPolys, INTEGER(IntKi) , INTENT( OUT) :: ErrStat !< error level/status of OpenFOutFile operation CHARACTER(*) , INTENT( OUT) :: ErrMsg !< message when error occurs + !$OMP critical(fileopen) CALL GetNewUnit( Un, ErrStat, ErrMsg ) CALL OpenFOutFile ( Un, TRIM(FileName), ErrStat, ErrMsg ) + !$OMP end critical(fileopen) if (ErrStat >= AbortErrLev) return ! Write a VTP mesh file (Polygonal VTK file) with positions and polygons (surfaces) @@ -157,10 +159,10 @@ SUBROUTINE ReadVTK_SP_info( FileName, descr, dims, origin, gridSpacing, vecLabel closeOnReturn = .FALSE. END IF - !$OMP critical + !$OMP critical(fileopen) CALL GetNewUnit( Un, ErrStat, ErrMsg ) CALL OpenFInpFile ( Un, TRIM(FileName), ErrStat, ErrMsg ) - !$OMP end critical + !$OMP end critical(fileopen) if (ErrStat >= AbortErrLev) return CALL ReadCom( Un, FileName, 'File header: Module Version (line 1)', ErrStat2, ErrMsg2, 0 ) @@ -359,10 +361,10 @@ SUBROUTINE WrVTK_SP_header( FileName, descr, Un, ErrStat, ErrMsg ) INTEGER(IntKi) , INTENT( OUT) :: ErrStat !< error level/status of OpenFOutFile operation CHARACTER(*) , INTENT( OUT) :: ErrMsg !< message when error occurs - !$OMP critical + !$OMP critical(fileopen) CALL GetNewUnit( Un, ErrStat, ErrMsg ) CALL OpenFOutFile ( Un, TRIM(FileName), ErrStat, ErrMsg ) - !$OMP end critical + !$OMP end critical(fileopen) if (ErrStat >= AbortErrLev) return WRITE(Un,'(A)') '# vtk DataFile Version 3.0' @@ -450,6 +452,7 @@ logical function vtk_new_ascii_file(filename,label,mvtk) logical :: b if (.not. mvtk%bFileOpen) then + !$OMP critical(fileopen) CALL GetNewUnit( mvtk%vtk_unit ) if (mvtk%bBinary) then ! Fortran 2003 stream, otherwise intel fortran ! @@ -465,6 +468,7 @@ logical function vtk_new_ascii_file(filename,label,mvtk) else open(mvtk%vtk_unit,file=trim(adjustl(filename)),iostat=iostatvar,action="write",status='replace') endif + !$OMP end critical(fileopen) if (iostatvar == 0) then if (mvtk%bBinary) then write(mvtk%vtk_unit)'# vtk DataFile Version 3.0'//NewLine diff --git a/modules/openfast-library/src/FAST_Solver.f90 b/modules/openfast-library/src/FAST_Solver.f90 index 83c0e525f9..90d292fe79 100644 --- a/modules/openfast-library/src/FAST_Solver.f90 +++ b/modules/openfast-library/src/FAST_Solver.f90 @@ -1715,8 +1715,10 @@ SUBROUTINE ED_HD_InputOutputSolve( this_time, p_FAST, calcJacobian & #ifdef OUTPUT_ADDEDMASS UnAM = -1 + !$OMP critical(fileopen) CALL GetNewUnit( UnAM, ErrStat, ErrMsg ) CALL OpenFOutFile( UnAM, TRIM(p_FAST%OutFileRoot)//'.AddedMassMatrix', ErrStat2, ErrMsg2) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CALL CleanUp() @@ -1729,8 +1731,10 @@ SUBROUTINE ED_HD_InputOutputSolve( this_time, p_FAST, calcJacobian & #endif #ifdef OUTPUT_JACOBIAN UnJac = -1 + !$OMP critical(fileopen) CALL GetNewUnit( UnJac, ErrStat2, ErrMsg2 ) CALL OpenFOutFile( UnJac, TRIM(p_FAST%OutFileRoot)//'.'//TRIM(num2lstr(this_time))//'.Jacobian2', ErrStat2, ErrMsg2) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) THEN CALL CleanUp() @@ -2490,8 +2494,10 @@ SUBROUTINE FullOpt1_InputOutputSolve( this_time, p_FAST, calcJacobian & #ifdef OUTPUT_ADDEDMASS IF (p_FAST%CompHydro == Module_HD ) THEN UnAM = -1 + !$OMP critical(fileopen) CALL GetNewUnit( UnAM, ErrStat2, ErrMsg2 ) CALL OpenFOutFile( UnAM, TRIM(p_FAST%OutFileRoot)//'.AddedMassMatrix', ErrStat2, ErrMsg2) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) RETURN @@ -2503,8 +2509,10 @@ SUBROUTINE FullOpt1_InputOutputSolve( this_time, p_FAST, calcJacobian & #endif #ifdef OUTPUT_JACOBIAN UnJac = -1 + !$OMP critical(fileopen) CALL GetNewUnit( UnJac, ErrStat2, ErrMsg2 ) CALL OpenFOutFile( UnJac, TRIM(p_FAST%OutFileRoot)//'.'//TRIM(num2lstr(this_time))//'.Jacobian', ErrStat2, ErrMsg2) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) RETURN @@ -5065,8 +5073,10 @@ SUBROUTINE CalcOutputs_And_SolveForInputs( n_t_global, this_time, this_state, ca #ifdef OUTPUT_MASS_MATRIX if (n_t_global == 0 .and. p_FAST%CompElast /= Module_SED) then UnMM = -1 + !$OMP critical(fileopen) CALL GetNewUnit( UnMM, ErrStat2, ErrMsg2 ) CALL OpenFOutFile( UnMM, TRIM(p_FAST%OutFileRoot)//'.EDMassMatrix', ErrStat2, ErrMsg2) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF ( ErrStat >= AbortErrLev ) RETURN CALL WrMatrix(ED%m%AugMat,UnMM, p_FAST%OutFmt) diff --git a/modules/openfast-library/src/FAST_Subs.f90 b/modules/openfast-library/src/FAST_Subs.f90 index 816441536e..67cde541c4 100644 --- a/modules/openfast-library/src/FAST_Subs.f90 +++ b/modules/openfast-library/src/FAST_Subs.f90 @@ -495,6 +495,7 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, SED, BD, S END IF ! lidar + Init%InData_IfW%LidarEnabled = .true. ! allowed with OF, but not FF Init%InData_IfW%lidar%Tmax = p_FAST%TMax if (p_FAST%CompElast == Module_SED) then Init%InData_IfW%lidar%HubPosition = SED%y%HubPtMotion%Position(:,1) @@ -520,6 +521,13 @@ SUBROUTINE FAST_InitializeAll( t_initial, p_FAST, y_FAST, m_FAST, ED, SED, BD, S Init%InData_IfW%Use4Dext = .false. END IF + ! OLAF might be used in AD, in which case we need to allow out of bounds for some calcs. To do that + ! the average values for the entire wind profile must be calculated and stored (we don't know if OLAF + ! is used until after AD_Init below). + if (p_FAST%CompAero == Module_AD) then + Init%InData_IfW%BoxExceedAllow = .true. + endif + CALL InflowWind_Init( Init%InData_IfW, IfW%Input(1), IfW%p, IfW%x(STATE_CURR), IfW%xd(STATE_CURR), IfW%z(STATE_CURR), & IfW%OtherSt(STATE_CURR), IfW%y, IfW%m, p_FAST%dt_module( MODULE_IfW ), Init%OutData_IfW, ErrStat2, ErrMsg2 ) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) @@ -2679,11 +2687,14 @@ SUBROUTINE FAST_InitOutput( p_FAST, y_FAST, Init, ErrStat, ErrMsg ) y_FAST%ActualChanLen = max( y_FAST%ActualChanLen, LEN_TRIM(y_FAST%ChannelUnits(I)) ) ENDDO ! I - CALL GetNewUnit( y_FAST%UnOu, ErrStat, ErrMsg ) - IF ( ErrStat >= AbortErrLev ) RETURN - CALL OpenFOutFile ( y_FAST%UnOu, TRIM(p_FAST%OutFileRoot)//'.out', ErrStat, ErrMsg ) - IF ( ErrStat >= AbortErrLev ) RETURN + !$OMP critical(fileopen) + CALL GetNewUnit( y_FAST%UnOu, ErrStat, ErrMsg ) + IF ( ErrStat < AbortErrLev ) then + CALL OpenFOutFile ( y_FAST%UnOu, TRIM(p_FAST%OutFileRoot)//'.out', ErrStat, ErrMsg ) + ENDIF + !$OMP end critical(fileopen) + IF ( ErrStat >= AbortErrLev ) RETURN ! Add some file information: @@ -2815,18 +2826,18 @@ SUBROUTINE FAST_ReadPrimaryFile( InputFile, p, m_FAST, OverrideAbortErrLev, ErrS ! Get an available unit number for the file. + !$OMP critical(fileopen) CALL GetNewUnit( UnIn, ErrStat, ErrMsg ) - IF ( ErrStat >= AbortErrLev ) RETURN - - + if ( ErrStat < AbortErrLev ) then ! Open the Primary input file. - - CALL OpenFInpFile ( UnIn, InputFile, ErrStat2, ErrMsg2 ) + CALL OpenFInpFile ( UnIn, InputFile, ErrStat2, ErrMsg2 ) CALL SetErrStat( ErrStat2, ErrMsg2,ErrStat,ErrMsg,RoutineName) - if ( ErrStat >= AbortErrLev ) then - call cleanup() - RETURN - end if + endif + !$OMP end critical(fileopen) + if ( ErrStat >= AbortErrLev ) then + call cleanup() + RETURN + end if p%NumSSCases = 0 p%RotSpeedInit = 0.0_ReKi @@ -4845,11 +4856,13 @@ SUBROUTINE FAST_WrSum( p_FAST, y_FAST, MeshMapData, ErrStat, ErrMsg ) ! Get a unit number and open the file: + !$OMP critical(fileopen) CALL GetNewUnit( y_FAST%UnSum, ErrStat, ErrMsg ) - IF ( ErrStat >= AbortErrLev ) RETURN - - CALL OpenFOutFile ( y_FAST%UnSum, TRIM(p_FAST%OutFileRoot)//'.sum', ErrStat, ErrMsg ) - IF ( ErrStat >= AbortErrLev ) RETURN + if ( ErrStat < AbortErrLev ) then + CALL OpenFOutFile ( y_FAST%UnSum, TRIM(p_FAST%OutFileRoot)//'.sum', ErrStat, ErrMsg ) + endif + !$OMP end critical(fileopen) + IF ( ErrStat >= AbortErrLev ) RETURN ! Add some file information: @@ -9181,8 +9194,10 @@ SUBROUTINE WriteInputMeshesToFile(u_ED, u_AD, u_SD, u_HD, u_MAP, u_BD, FileName, ! Open the binary output file: unOut=-1 + !$OMP critical(fileopen) CALL GetNewUnit( unOut, ErrStat, ErrMsg ) CALL OpenBOutFile ( unOut, TRIM(FileName), ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat /= ErrID_None) RETURN ! note that I'm not doing anything with the errors here, so it won't tell @@ -9261,9 +9276,11 @@ SUBROUTINE WriteMotionMeshesToFile(time, y_ED, u_SD, y_SD, u_HD, u_MAP, y_BD, u_ ! Open the binary output file and write a header: if (unOut<0) then + !$OMP critical(fileopen) CALL GetNewUnit( unOut, ErrStat, ErrMsg ) CALL OpenBOutFile ( unOut, TRIM(FileName), ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat /= ErrID_None) RETURN ! Add a file identification number (in case we ever have to change this): @@ -10073,10 +10090,12 @@ SUBROUTINE FAST_CreateCheckpoint_T(t_initial, n_t_global, NumTurbines, Turbine, IF ( unOut < 0 ) THEN - CALL GetNewUnit(unOut, ErrStat2, ErrMsg2) - CALL OpenBOutFile (unOut, FileName, ErrStat2, ErrMsg2) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev) then + !$OMP critical(fileopen) + CALL GetNewUnit( unOut, ErrStat2, ErrMsg2 ) + CALL OpenBOutFile ( unOut, FileName, ErrStat2, ErrMsg2) + !$OMP end critical(fileopen) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + if (ErrStat >= AbortErrLev ) then IF (.NOT. PRESENT(Unit)) THEN CLOSE(unOut) unOut = -1 @@ -10226,11 +10245,12 @@ SUBROUTINE FAST_RestoreFromCheckpoint_T(t_initial, n_t_global, NumTurbines, Turb IF ( unIn < 0 ) THEN + !$OMP critical(fileopen) CALL GetNewUnit( unIn, ErrStat2, ErrMsg2 ) - - CALL OpenBInpFile(unIn, FileName, ErrStat2, ErrMsg2) - CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) - if (ErrStat >= AbortErrLev ) return + CALL OpenBInpFile ( unIn, FileName, ErrStat2, ErrMsg2) + !$OMP end critical(fileopen) + CALL SetErrStat(ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) + IF (ErrStat >= AbortErrLev ) RETURN READ (unIn, IOSTAT=ErrStat2) AbortErrLev ! Abort error level READ (unIn, IOSTAT=ErrStat2) NumTurbines ! Number of turbines @@ -10602,9 +10622,11 @@ SUBROUTINE ReadModeShapeMatlabFile(p_FAST, ErrStat, ErrMsg) ErrMsg = "" ! Open data file. + !$OMP critical(fileopen) CALL GetNewUnit( UnIn, ErrStat2, ErrMsg2 ) CALL OpenBInpFile ( UnIn, trim(p_FAST%VTK_modes%MatlabFileName), ErrStat2, ErrMsg2 ) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN @@ -10728,9 +10750,11 @@ SUBROUTINE ReadModeShapeFile(p_FAST, InputFile, ErrStat, ErrMsg, checkpointOnly) CALL GetPath( InputFile, PriPath ) ! Input files will be relative to the path where the primary input file is located. ! Open data file. + !$OMP critical(fileopen) CALL GetNewUnit( UnIn, ErrStat2, ErrMsg2 ) CALL OpenFInpFile ( UnIn, InputFile, ErrStat2, ErrMsg2 ) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) IF (ErrStat >= AbortErrLev) RETURN diff --git a/modules/seastate/src/SeaState_Input.f90 b/modules/seastate/src/SeaState_Input.f90 index 71daf185e9..0ddedbaffc 100644 --- a/modules/seastate/src/SeaState_Input.f90 +++ b/modules/seastate/src/SeaState_Input.f90 @@ -271,7 +271,7 @@ subroutine SeaSt_ParseInput( InputFileName, OutRootName, defWtrDens, defWtrDpth, if (Failed()) return; ! WvKinFile - call ParseVar( FileInfo_In, CurLine, 'WvKinFile', InputFileData%Waves%WvKinFile, ErrStat2, ErrMsg2, UnEc ) + call ParseVar( FileInfo_In, CurLine, 'WvKinFile', InputFileData%Waves%WvKinFile, ErrStat2, ErrMsg2, UnEc, IsPath=.true. ) if (Failed()) return; !------------------------------------------------------------------------------------------------- diff --git a/modules/seastate/src/SeaState_Output.f90 b/modules/seastate/src/SeaState_Output.f90 index 96ff2d0f06..dc202ce5a6 100644 --- a/modules/seastate/src/SeaState_Output.f90 +++ b/modules/seastate/src/SeaState_Output.f90 @@ -280,10 +280,12 @@ SUBROUTINE SeaStOut_WriteWvKinFiles( Rootname, SeaSt_Prog, WaveField, WaveDT, X_ DO iFile = 1,7 - CALL GetNewUnit( UnWv ) WvName = TRIM(Rootname) // TRIM(extension(iFile)) + !$OMP critical(fileopen) + CALL GetNewUnit( UnWv ) CALL OpenFOutFile ( UnWv, WvName, ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat >=AbortErrLev) RETURN call WriteWvKinHeader( UnWv, iFile, Delim, SeaSt_Prog, waveDT, -z_gridPts(1), NGrid, deltaGrid ) @@ -330,11 +332,11 @@ SUBROUTINE SeaStOut_WriteWvKinFiles( Rootname, SeaSt_Prog, WaveField, WaveDT, X_ END DO ! WaveElevation Grid - - CALL GetNewUnit( UnWv ) - WvName = TRIM(Rootname) // '.Elev' + !$OMP critical(fileopen) + CALL GetNewUnit( UnWv ) CALL OpenFOutFile ( UnWv, WvName, ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat >=AbortErrLev) RETURN @@ -444,13 +446,15 @@ subroutine SeaStOut_WriteWaveElev0( Rootname, NStepWave, NGrid, WaveElev1, WaveE ErrMsg = "" Frmt = '(F12.4,ES12.4e2)' Frmt2 = '(2(A12))' - CALL GetNewUnit( UnWv ) WvName = TRIM(Rootname) // '.Elev' i = NGrid(1) / 2 + 1 j = NGrid(2) / 2 + 1 + !$OMP critical(fileopen) + CALL GetNewUnit( UnWv ) CALL OpenFOutFile ( UnWv, WvName, ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat >=AbortErrLev) RETURN ! WRITE (UnWv,'(A)', IOSTAT=ErrStat) 'This wave elevation (0,0) file was generated by '//TRIM( SeaSt_Prog%Name )//& ! ' '//TRIM( SeaSt_Prog%Ver )//' on '//CurDate()//' at '//CurTime()//'.' @@ -718,9 +722,11 @@ SUBROUTINE SeaStOut_OpenOutput( SeaSt_ProgDesc, OutRootName, p, InitOut, ErrSta ! Open the file for output OutFileName = TRIM(OutRootName)//'.out' + + !$OMP critical(fileopen) CALL GetNewUnit( p%UnOutFile ) - CALL OpenFOutFile ( p%UnOutFile, OutFileName, ErrStat, ErrMsg ) + !$OMP end critical(fileopen) IF (ErrStat >=AbortErrLev) RETURN @@ -1011,9 +1017,11 @@ SUBROUTINE SeaStOut_WrSummaryFile(InitInp, InputFileData, p, ErrStat, ErrMsg ) SummaryName = trim(InitInp%OutRootName)//'.sum' UnSum = -1 - CALL GetNewUnit( UnSum ) + !$OMP critical(fileopen) + CALL GetNewUnit( UnSum ) CALL OpenFOutFile ( UnSum, SummaryName, ErrStat2, ErrMsg2 ) + !$OMP end critical(fileopen) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) IF (ErrStat >=AbortErrLev) RETURN diff --git a/modules/seastate/src/UserWaves.f90 b/modules/seastate/src/UserWaves.f90 index 12eed126e0..4dbb0c79b2 100644 --- a/modules/seastate/src/UserWaves.f90 +++ b/modules/seastate/src/UserWaves.f90 @@ -144,14 +144,14 @@ SUBROUTINE WaveElev_ReadFile ( InitInp, WaveElevData, ErrStat, ErrMsg ) ErrStat = ErrID_None ErrMsg = "" - ! Get a unit number for reading in the file - CALL GetNewUnit( WaveElevUnit ) - ! Assemble the filename for the wave elevation data. WaveElevData%FileName = TRIM(InitInp%WvKinFile)//'.Elev' ! Open the file containing the wave elevation timeseries + !$OMP critical(fileopen) + CALL GetNewUnit( WaveElevUnit ) CALL OpenFInpFile( WaveElevUnit, WaveElevData%FileName, ErrStatTmp, ErrMsgTmp ) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat,ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) THEN CLOSE ( WaveElevUnit ) @@ -492,11 +492,13 @@ SUBROUTINE UserWaves_Init ( InitInp, InitOut, WaveField, ErrStat, ErrMsg ) ! Read the first file and set the initial values of the DO iFile = 1,7 - CALL GetNewUnit( UnWv ) FileName = TRIM(InitInp%WvKinFile) // TRIM(extension(iFile)) + !$OMP critical(fileopen) + CALL GetNewUnit( UnWv ) CALL OpenFInpFile ( UnWv, FileName, ErrStatTmp, ErrMsgTmp ) + !$OMP end critical(fileopen) IF ( ErrStatTmp /= 0 ) THEN ErrMsgTmp = 'Failed to open wave kinematics file, ' // TRIM(FileName) CALL SetErrStat( ErrID_Fatal, ErrMsgTmp, ErrStat, ErrMsg, RoutineName ) @@ -552,11 +554,13 @@ SUBROUTINE UserWaves_Init ( InitInp, InitOut, WaveField, ErrStat, ErrMsg ) end do ! WaveElev - CALL GetNewUnit( UnWv ) FileName = TRIM(InitInp%WvKinFile) // '.Elev' + !$OMP critical(fileopen) + CALL GetNewUnit( UnWv ) CALL OpenFInpFile ( UnWv, FileName, ErrStatTmp, ErrMsgTmp ) + !$OMP end critical(fileopen) IF ( ErrStatTmp /= 0 ) THEN ErrMsgTmp = 'Failed to open wave elevation file, ' // TRIM(FileName) CALL SetErrStat( ErrID_Fatal, ErrMsgTmp, ErrStat, ErrMsg, RoutineName ) @@ -691,14 +695,14 @@ SUBROUTINE WaveComp_ReadFile ( InitInp, WaveDOmega, WaveCompData, ErrStat, ErrMs ErrStat = ErrID_None ErrMsg = "" - ! Get a unit number for reading in the file - CALL GetNewUnit( WaveCompUnit ) - ! Assemble the filename for the wave component data. WaveCompData%FileName = TRIM(InitInp%WvKinFile) ! Open the file containing the list of wave components + !$OMP critical(fileopen) + CALL GetNewUnit( WaveCompUnit ) CALL OpenFInpFile( WaveCompUnit, WaveCompData%FileName, ErrStatTmp, ErrMsgTmp ) + !$OMP end critical(fileopen) CALL SetErrStat( ErrStatTmp, ErrMsgTmp, ErrStat,ErrMsg, RoutineName) IF (ErrStat >= AbortErrLev) THEN CALL CleanUpError() diff --git a/modules/servodyn/src/ServoDyn_IO.f90 b/modules/servodyn/src/ServoDyn_IO.f90 index cb900579ec..13d607f6eb 100644 --- a/modules/servodyn/src/ServoDyn_IO.f90 +++ b/modules/servodyn/src/ServoDyn_IO.f90 @@ -1332,15 +1332,15 @@ subroutine ParseInputFileInfo( PriPath, InputFile, OutFileRoot, FileInfo_In, Inp if ( InputFileData%Echo ) WRITE(UnEcho, '(A)') FileInfo_In%Lines(CurLine) ! Write section break to echo CurLine = CurLine + 1 ! DLL_FileName - Name/location of the dynamic library {.dll [Windows] or .so [Linux]} in the Bladed-DLL format (-) [used only with Bladed Interface] - call ParseVar( FileInfo_In, CurLine, 'DLL_FileName', InputFileData%DLL_FileName, ErrStat2, ErrMsg2, UnEcho ) + call ParseVar( FileInfo_In, CurLine, 'DLL_FileName', InputFileData%DLL_FileName, ErrStat2, ErrMsg2, UnEcho, IsPath=.true. ) if (Failed()) return; IF ( PathIsRelative( InputFileData%DLL_FileName ) ) InputFileData%DLL_FileName = TRIM(PriPath)//TRIM(InputFileData%DLL_FileName) ! DLL_InFile - Name of input file sent to the DLL (-) [used only with Bladed Interface] - call ParseVar( FileInfo_In, CurLine, 'DLL_InFile', InputFileData%DLL_InFile, ErrStat2, ErrMsg2, UnEcho ) + call ParseVar( FileInfo_In, CurLine, 'DLL_InFile', InputFileData%DLL_InFile, ErrStat2, ErrMsg2, UnEcho, IsPath=.true. ) if (Failed()) return; IF ( PathIsRelative( InputFileData%DLL_InFile ) ) InputFileData%DLL_InFile = TRIM(PriPath)//TRIM(InputFileData%DLL_InFile) ! DLL_ProcName - Name of procedure in DLL to be called (-) [case sensitive; used only with DLL Interface] - call ParseVar( FileInfo_In, CurLine, 'DLL_ProcName', InputFileData%DLL_ProcName, ErrStat2, ErrMsg2, UnEcho ) + call ParseVar( FileInfo_In, CurLine, 'DLL_ProcName', InputFileData%DLL_ProcName, ErrStat2, ErrMsg2, UnEcho, IsPath=.true. ) if (Failed()) return; ! DLL_DT - Communication interval for dynamic library (s) (or "default") [used only with Bladed Interface] call ParseVarWDefault( FileInfo_In, CurLine, 'DLL_DT', InputFileData%DLL_DT, InputFileData%DT, ErrStat2, ErrMsg2, UnEcho ) @@ -2400,8 +2400,10 @@ subroutine InitializeSummaryFile(InputFileData,OutfileRoot,UnSum,ErrStat,ErrMsg) ErrStat = ErrID_None ErrMsg = '' if ( InputFileData%SumPrint ) then + !$OMP critical(fileopen) call GetNewUnit( UnSum ) CALL OpenEcho ( UnSum, TRIM(OutFileRoot)//'.sum', ErrStat2, ErrMsg2 ) + !$OMP end critical(fileopen) CALL SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) IF (ErrStat >= AbortErrLev) RETURN else diff --git a/modules/servodyn/src/StrucCtrl.f90 b/modules/servodyn/src/StrucCtrl.f90 index 60c9efd530..0c0cea0da6 100644 --- a/modules/servodyn/src/StrucCtrl.f90 +++ b/modules/servodyn/src/StrucCtrl.f90 @@ -2115,7 +2115,7 @@ SUBROUTINE StC_ParseInputFileInfo( PriPath, InputFile, RootName, NumMeshPts, Fil call ParseVar( FileInfo_In, Curline, 'PrescribedForcesCoordSys', InputFileData%PrescribedForcesCoordSys, ErrStat2, ErrMsg2 ) If (Failed()) return; ! Prescribed input time series - call ParseVar( FileInfo_In, Curline, 'PrescribedForcesFile', InputFileData%PrescribedForcesFile, ErrStat2, ErrMsg2 ) + call ParseVar( FileInfo_In, Curline, 'PrescribedForcesFile', InputFileData%PrescribedForcesFile, ErrStat2, ErrMsg2, IsPath=.true. ) if (Failed()) return; if ( PathIsRelative( InputFileData%PrescribedForcesFile ) ) InputFileData%PrescribedForcesFile = TRIM(PriPath)//TRIM(InputFileData%PrescribedForcesFile) diff --git a/modules/subdyn/src/SubDyn.f90 b/modules/subdyn/src/SubDyn.f90 index eff2ca3a23..c9c62ed92c 100644 --- a/modules/subdyn/src/SubDyn.f90 +++ b/modules/subdyn/src/SubDyn.f90 @@ -934,9 +934,11 @@ SUBROUTINE SD_Input(SDInputFile, Init, p, ErrStat,ErrMsg) UnEc = -1 Echo = .FALSE. +!$OMP critical(fileopen) CALL GetNewUnit( UnIn ) CALL OpenFInpfile(UnIn, TRIM(SDInputFile), ErrStat2, ErrMsg2) +!$OMP end critical(fileopen) IF ( ErrStat2 /= ErrID_None ) THEN Call Fatal('Could not open SubDyn input file') @@ -3624,6 +3626,7 @@ END SUBROUTINE OutModes !> Write the common part of the JSON file (Nodes, Connectivity, Element prop) +!FIXME: error handling is broken here!!! SUBROUTINE WriteJSONCommon(FileName, Init, p, m, InitInput, FileKind, UnSum, ErrStat, ErrMsg) use JSON, only: json_write_array TYPE(SD_InitType), INTENT(INOUT) :: Init !< Input data for initialization routine @@ -3645,8 +3648,10 @@ SUBROUTINE WriteJSONCommon(FileName, Init, p, m, InitInput, FileKind, UnSum, Err ! --- Create file and get unit UnSum = -1 ! we haven't opened the summary file, yet. + !$OMP critical(fileopen) call GetNewUnit( UnSum ) call OpenFOutFile ( UnSum, FileName, ErrStat2, ErrMsg2 ) + !$OMP end critical(fileopen) write(UnSum, '(A)')'{' ! --- Misc diff --git a/openfast_python/openfast_io/turbsim_file.py b/openfast_python/openfast_io/turbsim_file.py index 2756fff7be..9cd6ed1e88 100644 --- a/openfast_python/openfast_io/turbsim_file.py +++ b/openfast_python/openfast_io/turbsim_file.py @@ -5,6 +5,7 @@ """ import pandas as pd import numpy as np +import argparse import os import struct import time @@ -16,13 +17,13 @@ File=dict class TurbSimFile(File): - """ + """ Read/write a TurbSim turbulence file (.bts). The object behaves as a dictionary. Main keys --------- - 'u': velocity field, shape (3 x nt x ny x nz) - - 'y', 'z', 't': space and time coordinates + - 'y', 'z', 't': space and time coordinates - 'dt', 'ID', 'info' - 'zTwr', 'uTwr': tower coordinates and field if present (3 x nt x nTwr) - 'zHub', 'uHub': height and velocity at a reference point (usually not hub) @@ -36,7 +37,7 @@ class TurbSimFile(File): ts = TurbSimFile('Turb.bts') print(ts.keys()) - print(ts['u'].shape) + print(ts['u'].shape) """ @@ -55,7 +56,7 @@ def __init__(self,filename=None, **kwargs): self.read(filename, **kwargs) def read(self, filename=None, header_only=False): - """ read BTS file, with field: + """ read BTS file, with field: u (3 x nt x ny x nz) uTwr (3 x nt x nTwr) """ @@ -69,7 +70,7 @@ def read(self, filename=None, header_only=False): raise EmptyFileError('File is empty:',self.filename) scl = np.zeros(3, np.float32); off = np.zeros(3, np.float32) - with open(self.filename, mode='rb') as f: + with open(self.filename, mode='rb') as f: # Reading header info ID, nz, ny, nTwr, nt = struct.unpack('0) @@ -95,7 +96,7 @@ def read(self, filename=None, header_only=False): self['info'] = info self['ID'] = ID self['dt'] = dt - self['y'] = np.arange(ny)*dy + self['y'] = np.arange(ny)*dy self['y'] -= np.mean(self['y']) # y always centered on 0 self['z'] = np.arange(nz)*dz +zBottom self['t'] = np.arange(nt)*dt @@ -104,7 +105,7 @@ def read(self, filename=None, header_only=False): self['uHub'] = uHub def write(self, filename=None): - """ + """ write a BTS file, using the following keys: 'u','z','y','t','uTwr' u (3 x nt x ny x nz) uTwr (3 x nt x nTwr) @@ -151,7 +152,7 @@ def write(self, filename=None): # Providing estimates of uHub and zHub even if these fields are not used zHub,uHub, bHub = self.hubValues() - with open(self.filename, mode='wb') as f: + with open(self.filename, mode='wb') as f: f.write(struct.pack('