diff --git a/cmake/OpenfastFortranOptions.cmake b/cmake/OpenfastFortranOptions.cmake index 127e1d8783..6a1c38539b 100644 --- a/cmake/OpenfastFortranOptions.cmake +++ b/cmake/OpenfastFortranOptions.cmake @@ -158,13 +158,18 @@ endmacro(set_fast_intel_fortran) # arch # macro(set_fast_intel_fortran_posix) - # Set size where temporary are stored on heap instead of stack - # 1000: size in kB (1 MB) - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fpic -fpp -heap-arrays 1000") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fpic -fpp") # debug flags if(CMAKE_BUILD_TYPE MATCHES Debug) - set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -check all,noarg_temp_created -traceback -init=huge,infinity" ) + if(${CMAKE_Fortran_COMPILER_ID} MATCHES "IntelLLVM") + # NOTE: there is a bug in the 2024 and 2025 IFX compiler causing conflicts between the `check:uninit` and `-lm -ldl` flags + # When this is fixed in IFX, we will want to update this to check against versions before fix + # See here: https://community.intel.com/t5/Intel-Fortran-Compiler/ifx-IFX-2023-2-0-20230721-linker-problems-with-check-uninit/m-p/1527816 + set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -check all,noarg_temp_created,nouninit -traceback -init=huge,infinity" ) + else() + set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} -check all,noarg_temp_created -traceback -init=huge,infinity" ) + endif() endif() # If double precision, make real and double constants 64 bits @@ -203,9 +208,7 @@ macro(set_fast_intel_fortran_windows) # Turn off specific warnings # - 5199: too many continuation lines # - 5268: 132 column limit - # Set size where temporary are stored on heap instead of stack - # 1000: size in kB (1 MB) - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qdiag-disable:5199,5268 /fpp /heap-arrays:1000") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} /Qdiag-disable:5199,5268 /fpp") # If double precision, make constants double precision if (DOUBLE_PRECISION) @@ -222,7 +225,11 @@ macro(set_fast_intel_fortran_windows) # debug flags if(CMAKE_BUILD_TYPE MATCHES Debug) - set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} /check:all,noarg_temp_created /traceback /Qinit=huge,infinity" ) + if(${CMAKE_Fortran_COMPILER_ID} MATCHES "IntelLLVM") + set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} /check:all,noarg_temp_created,nouninit /traceback /Qinit=huge,infinity" ) + else() + set( CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} /check:all,noarg_temp_created /traceback /Qinit=huge,infinity" ) + endif() endif() check_f2008_features() diff --git a/docs/changelogs/ReleaseProcess.md b/docs/changelogs/ReleaseProcess.md index dd29564573..74f2a02caf 100644 --- a/docs/changelogs/ReleaseProcess.md +++ b/docs/changelogs/ReleaseProcess.md @@ -49,33 +49,33 @@ After posting and tagging release * Run one of the executables and check the version info. Muck about with VS if there is an issue. * Also run `dumpbin.exe /dependents .exe` to check static linking * NOTE: build the simulink last -- it messes up some things otherwise - - [ ] AeroDisk_Driver_x64.exe - - [ ] 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 -- build from MATLAB - - [ ] 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 -- change `additional dependencies` in the `OpenFAST-Simulink` project in `FAST` to point to correct install of MATLAB - - [ ] openfast_x64.exe - - [ ] SeaStateDriver_x64.exe - - [ ] SimpleElastoDyn_x64.exe - - [ ] SubDyn_x64.exe - - [ ] Turbsim_x64.exe - - [ ] UnsteadyAero_x64.exe + - [ ] `AeroDisk_Driver_x64.exe` + - [ ] `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` -- build from MATLAB + - [ ] `HydroDynDriver_x64.exe` + - [ ] `HydroDyn_c_binding_x64.dll` + - [ ] `InflowWind_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` -- change `additional dependencies` in the `OpenFAST-Simulink` project in `FAST` to point to correct install of MATLAB + - [ ] `openfast_x64.exe` + - [ ] `SeaStateDriver_x64.exe` + - [ ] `SimpleElastoDyn_x64.exe` + - [ ] `SubDyn_x64.exe` + - [ ] `Turbsim_x64.exe` + - [ ] `UnsteadyAero_x64.exe` 5. Upload all filesUnset the no tracking of files ``` git ls-files -v | grep "^[a-z]" diff --git a/docs/changelogs/v4.0.3.md b/docs/changelogs/v4.0.3.md new file mode 100644 index 0000000000..ef15af0a25 --- /dev/null +++ b/docs/changelogs/v4.0.3.md @@ -0,0 +1,91 @@ +**Feature or improvement description** +Pull request to merge `rc-4.0.3` into `main` and create a tagged release for v4.0.3 + +See the milestone and project pages for additional information + + https://github.com/OpenFAST/openfast/milestone/19 + +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 +- [ ] Update version info in openfast\_io/pyproject.toml +- [ ] Verify readthedocs builds correctly +- [ ] Create an annotated tag in OpenFAST during merge (mark as most recent if necessary) +- [ ] Create a merge commit in r-test and add a corresponding annotated tag +- [ ] Compile executables for Windows builds + - [ ] `AeroDisk_Driver_x64.exe` + - [ ] `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` + - [ ] `IinflowWind_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` + - [ ] `SeaStateDriver_x64.exe` + - [ ] `SimpleElastoDyn_x64.exe` + - [ ] `SubDyn_x64.exe` + - [ ] `Turbsim_x64.exe` + - [ ] `UnsteadyAero_x64.exe` + +# Changelog + +## Overview + +This release includes several bug fixes and improvements for _FAST.Farm_, including segmentation faults with large farms, file I/O collision fixes, increased error message length, and build/compile issues. + +## General + +### CMake build system + +#2709 Add "nouninit" to debug flags for IntelLLVM (@andrew-platt) + +#2732 Disable use of heap arrays for the Intel fortran compilers (@deslaughter) + + +### openfast_io + +#2727 generate BD files if fst_vt dictionary BD keys are not empty (@mayankchetan) + + +### Visual Studio build + +#2742 Fix VS build for FAST when using IFX Complier (@deslaughter) + + +## Solvers + +### FAST.Farm + +#2711 Fix FAST.Farm issues with OMP (segfaults mostly) (@andrew-platt) + + +## Module changes + +### NWTC library + +#2710 Increase length of error messages (@andrew-platt) + +#2741 bug in simulation status number of days left (@andrew-platt) + + +## Input file changes + +No input file changes are required from 4.0.2. + diff --git a/docs/conf.py b/docs/conf.py index 4e08f4e551..bb9d65f674 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'4.0' # The full version, including alpha/beta/rc tags. -release = u'v4.0.2' +release = u'v4.0.3' # 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 6f40c92e4c..06492245a2 100644 --- a/docs/source/user/api_change.rst +++ b/docs/source/user/api_change.rst @@ -10,6 +10,12 @@ The line number corresponds to the resulting line number after all changes are i Thus, be sure to implement each in order so that subsequent line numbers are correct. +OpenFAST v4.0.2 to OpenFAST v4.0.3 +---------------------------------- + +No input file changes were made. + + OpenFAST v4.0.1 to OpenFAST v4.0.2 ---------------------------------- diff --git a/docs/source/user/fast.farm/Pictures/Troubleshooting_SegFaultScreen.png b/docs/source/user/fast.farm/Pictures/Troubleshooting_SegFaultScreen.png new file mode 100644 index 0000000000..95a03dca5a Binary files /dev/null and b/docs/source/user/fast.farm/Pictures/Troubleshooting_SegFaultScreen.png differ diff --git a/docs/source/user/fast.farm/RunningFFarm.rst b/docs/source/user/fast.farm/RunningFFarm.rst index bdae0343e8..fcba5a0e24 100644 --- a/docs/source/user/fast.farm/RunningFFarm.rst +++ b/docs/source/user/fast.farm/RunningFFarm.rst @@ -14,3 +14,36 @@ available in the :ref:`installation` documentation. .. note:: Checkpoint-restart capability has not yet been implemented within FAST.Farm. + + +Troubleshooting +--------------- + +Segmentation fault at start +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Very large FAST.Farm simulations may experience a segmentation fault if +insufficient stack memory is set aside for OpenMP. This may appear on +simulations with more than 50 turbines spaced over tens of kilometers. If this +is the case, the fault is likely to occur right after screen displays the T=0 +timestep. + +.. image:: Pictures/Troubleshooting_SegFaultScreen.png + +To increase the stack assigned to the OpenMP process on linux based machines, +the environment variable `OMP_STACKSIZE` can be increased from the default 4 MB +(Intel compiled) to 32 MB (or more) by + +.. code-block:: + + export OMP_STACKSIZE="32 M" + +If this solves the segmentation fault, then the root cause is from the +parallelization with OpenMP not having sufficient stack reserve. If this does +not solve the issue, then there may be other problems that should be reported. + +For further reading on segmentation faults with OpenMP, see `stackoverflow +comment +`_ +and `Intel OpenMP documentation +`_. diff --git a/modules/awae/src/AWAE.f90 b/modules/awae/src/AWAE.f90 index cec3d20e3e..fd80cd6b9a 100644 --- a/modules/awae/src/AWAE.f90 +++ b/modules/awae/src/AWAE.f90 @@ -701,6 +701,13 @@ subroutine HighResGridCalcOutput(n, u, p, xd, y, m, errStat, errMsg) ! NOTE: loop here is different from low res grid, doing: turbines > grid > turbines(nt/=nt2) > planes ! instead of grid > turbines > planes ! TODO explain + ! + ! WARNING: the way this is setup can use a lot of memory, and may run out of OMP stack. If that happens, + ! use `export OMP_STACKSIZE="32 M"` (default is 4 M). + ! Rough calculation of memory expected: + ! maxN_wake * 13 * OMP_NUM_THREADS * = size in bytes + ! HOWEVER, real world testing shows that for 103 threads with 114 turbines and maxN_wake=101346 is more like + ! maxN_wake * 40 * = size in bytes NumGrid_high = p%nX_high*p%nY_high*p%nZ_high do nt = 1,p%NumTurbines @@ -779,7 +786,7 @@ end subroutine HighResGridCalcOutput subroutine AWAE_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitOut, errStat, errMsg ) type(AWAE_InitInputType), intent(in ) :: InitInp !< Input data for initialization routine type(AWAE_InputType), intent( out) :: u !< An initial guess for the input; input mesh must be defined - type(AWAE_ParameterType),target,intent( out) :: p !< Parameters + type(AWAE_ParameterType), intent( out) :: p !< Parameters type(AWAE_ContinuousStateType), intent( out) :: x !< Initial continuous states type(AWAE_DiscreteStateType), intent( out) :: xd !< Initial discrete states type(AWAE_ConstraintStateType), intent( out) :: z !< Initial guess of the constraint states @@ -799,7 +806,7 @@ subroutine AWAE_Init( InitInp, u, p, x, xd, z, OtherState, y, m, Interval, InitO character(ErrMsgLen) :: errMsg2 ! temporary error message character(*), parameter :: RoutineName = 'AWAE_Init' type(InflowWind_InitInputType) :: IfW_InitInp - type(InflowWind_InitOutputType), target :: IfW_InitOut + type(InflowWind_InitOutputType) :: IfW_InitOut ! Initialize variables for this routine errStat = ErrID_None @@ -1112,12 +1119,9 @@ subroutine CheckModAmb3Boundaries() real(ReKi) :: ff_lim(2) real(ReKi) :: hr_lim(2) real(ReKi), parameter :: GridTol = 1.0E-3 ! Tolerance from IfW for checking the high-res grid (Mod_AmbWind=3 only). - type(FlowFieldType), pointer :: ff ! alias to shorten notation to fullfield - type(WindFileDat), pointer :: wfi ! alias to shorten notation to WindFileInfo character(1024) :: tmpMsg - ff => p%IfW(nt)%FlowField - wfi => IfW_InitOut%WindFileInfo + associate(ff => p%IfW(nt)%FlowField, wfi => IfW_InitOut%WindFileInfo) tmpMsg = NewLine//NewLine//'Turbine '//trim(Num2LStr(nt))//' -- Mod_AmbWind=3 requires the FAST.Farm high-res grid '// & 'is entirely contained within the flow-field from InflowWind. '//NewLine//' Try setting:'//NewLine @@ -1192,6 +1196,7 @@ subroutine CheckModAmb3Boundaries() ErrMsg2 = NewLine//NewLine//'Turbine '//trim(Num2LStr(nt))//' -- Mod_AmbWind=3 requires InflowWind propagation direction alignment with X or Y (0, 90, 180, 270 degrees).' endif endif + end associate end subroutine CheckModAmb3Boundaries logical function Failed() @@ -1288,9 +1293,6 @@ subroutine AWAE_UpdateStates( t, n, u, p, x, xd, z, OtherState, m, errStat, errM errMsg = "" ! Read the ambient wind data that is needed for t+dt, i.e., n+1 -!#ifdef _OPENMP -! t1 = omp_get_wtime() -!#endif if ( (n+1) == (p%NumDT-1) ) then n_high_low = 0 @@ -1301,10 +1303,6 @@ subroutine AWAE_UpdateStates( t, n, u, p, x, xd, z, OtherState, m, errStat, errM if ( p%Mod_AmbWind == 1 ) then ! read from file the ambient flow for the n+1 time step call ReadLowResWindFile(n+1, p, m%Vamb_Low, errStat2, errMsg2); if (Failed()) return; - !#ifdef _OPENMP - ! t2 = omp_get_wtime() - ! write(*,*) ' AWAE_UpdateStates: Time spent reading Low Res data : '//trim(num2lstr(t2-t1))//' seconds' - !#endif !$OMP PARALLEL DO DEFAULT(Shared) PRIVATE(nt, i_hl, errStat2, errMsg2) !Private(nt,tm2,tm3) do nt = 1,p%NumTurbines @@ -1400,11 +1398,6 @@ subroutine AWAE_UpdateStates( t, n, u, p, x, xd, z, OtherState, m, errStat, errM xd%WAT_B_Box(1:3) = xd%WAT_B_Box(1:3) + xd%Ufarm(1:3)*real(p%dt_low,ReKi) endif -!#ifdef _OPENMP -! t1 = omp_get_wtime() -! write(*,*) ' AWAE_UpdateStates: Time spent reading High Res data : '//trim(num2lstr(t1-t2))//' seconds' -!#endif - contains logical function Failed() call SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName) diff --git a/modules/awae/src/AWAE_IO.f90 b/modules/awae/src/AWAE_IO.f90 index 4d8d5e37bc..86ee0b77c5 100644 --- a/modules/awae/src/AWAE_IO.f90 +++ b/modules/awae/src/AWAE_IO.f90 @@ -513,7 +513,9 @@ SUBROUTINE AWAE_PrintSum( p, u, y, ErrStat, ErrMsg ) + !$OMP critical(fileopen_critical) CLOSE(UnSu) + !$OMP end critical(fileopen_critical) RETURN END SUBROUTINE AWAE_PrintSum diff --git a/modules/nwtc-library/src/NWTC_Base.f90 b/modules/nwtc-library/src/NWTC_Base.f90 index 3cc5d1e65d..4e25d16c9e 100644 --- a/modules/nwtc-library/src/NWTC_Base.f90 +++ b/modules/nwtc-library/src/NWTC_Base.f90 @@ -33,7 +33,7 @@ MODULE NWTC_Base ! General constants: INTEGER, PARAMETER :: BITS_IN_ADDR = C_INTPTR_T*8 !< The number of bits in an address (32-bit or 64-bit). - INTEGER, PARAMETER :: ErrMsgLen = 1024 !< The maximum number of characters in an error message in the FAST framework + INTEGER, PARAMETER :: ErrMsgLen = 8196 !< The maximum number of characters in an error message in the FAST framework INTEGER(IntKi), PARAMETER :: ChanLen = 20 !< The maximum allowable length of channel names (i.e., width of output columns) in the FAST framework INTEGER(IntKi), PARAMETER :: OutStrLenM1 = ChanLen - 1 !< The maximum allowable length of channel names without optional "-" or "M" at the beginning to indicate the negative of the channel diff --git a/modules/nwtc-library/src/NWTC_IO.f90 b/modules/nwtc-library/src/NWTC_IO.f90 index 0e256faaa5..241a7bc672 100644 --- a/modules/nwtc-library/src/NWTC_IO.f90 +++ b/modules/nwtc-library/src/NWTC_IO.f90 @@ -1799,20 +1799,13 @@ SUBROUTINE FindLine ( Str , MaxLen , StrEnd ) END SUBROUTINE FindLine !======================================================================= !> This routine returns the next unit number greater than 9 that is not currently in use. -!! If it cannot find any unit between 10 and 99 that is available, it either aborts or returns an appropriate error status/message. +!! If it cannot find any unit between 10 and 2^16-1 that is available, it either aborts or returns an appropriate error status/message. SUBROUTINE GetNewUnit ( UnIn, ErrStat, ErrMsg ) - - - ! Argument declarations. - INTEGER, INTENT(OUT) :: UnIn !< Logical unit for the file. INTEGER(IntKi), INTENT(OUT), OPTIONAL :: ErrStat !< The error status code; If not present code aborts CHARACTER(*), INTENT(OUT), OPTIONAL :: ErrMsg !< The error message, if an error occurred - - ! Local declarations. - INTEGER :: Un ! Unit number LOGICAL :: Opened ! Flag indicating whether or not a file is opened. INTEGER(IntKi), PARAMETER :: StartUnit = 10 ! Starting unit number to check (numbers less than 10 reserved) @@ -1820,48 +1813,30 @@ SUBROUTINE GetNewUnit ( UnIn, ErrStat, ErrMsg ) ! macos -- 256 (change with ulimit -n) ! linux -- 1024 (change with ulimit -n) ! windows -- 512 (not sure how to change -- ADP) - INTEGER(IntKi), PARAMETER :: MaxUnit = 1024 ! The maximum unit number available (or 10 less than the number of files you want to have open at a time) + INTEGER(IntKi), PARAMETER :: MaxUnit = 65535 ! The maximum unit number available (or 10 less than the number of files you want to have open at a time) CHARACTER(ErrMsgLen) :: Msg ! Temporary error message + ! See if unit is connected to an open file. Check the next largest number until it is not opened. + do Un = StartUnit, MaxUnit + UnIn = Un + inquire(unit=Un, opened=Opened) + if (Opened) cycle + if (present(ErrStat)) ErrStat = ErrID_None + if (present(ErrMsg)) ErrMsg = '' + return + end do - ! Initialize subroutine outputs - - Un = StartUnit - - IF ( PRESENT( ErrStat ) ) ErrStat = ErrID_None - IF ( PRESENT( ErrMsg ) ) ErrMsg = '' - - ! See if unit is connected to an open file. Check the next largest number until it is not opened. - - DO - - INQUIRE ( UNIT=Un , OPENED=Opened ) - - IF ( .NOT. Opened ) EXIT - Un = Un + 1 - - IF ( Un > MaxUnit ) THEN - - Msg = 'GetNewUnit() was unable to find an open file unit specifier between '//TRIM(Num2LStr(StartUnit)) & - //' and '//TRIM(Num2LStr(MaxUnit))//'.' - - IF ( PRESENT( ErrStat ) ) THEN - ErrStat = ErrID_Severe - IF ( PRESENT( ErrMsg) ) ErrMsg = Msg - ELSE - CALL ProgAbort( Msg ) - END IF - - EXIT ! stop searching now - - END IF - - - END DO + Msg = 'GetNewUnit() was unable to find an open file unit specifier between '//TRIM(Num2LStr(StartUnit)) & + //' and '//TRIM(Num2LStr(MaxUnit))//'.' - UnIn = Un + UnIn = -1 + if (present(ErrStat)) then + ErrStat = ErrID_Severe + if (present(ErrMsg)) ErrMsg = Msg + else + call ProgAbort(Msg) + end if - RETURN END SUBROUTINE GetNewUnit !======================================================================= !> This function returns a text description of the ErrID (ErrStat) code. @@ -6125,7 +6100,7 @@ subroutine ReadR4AryWDefault ( UnIn, Fil, Ary, AryLen, AryName, AryDescr, AryDef integer :: Ind ! Index into the string array. Assumed to be one digit. integer :: IOS ! I/O status returned from the read statement. - character(30) :: Word(AryLen) ! String to hold the words on the line. + character(30) :: Word ! String to hold the words on the line. character(2048) :: Line ! The contents of a line returned from ReadLine() with comment removed. integer :: LineLen ! Length of line read in @@ -6134,13 +6109,10 @@ subroutine ReadR4AryWDefault ( UnIn, Fil, Ary, AryLen, AryName, AryDescr, AryDef if (ErrStat >= AbortErrLev) return ! check for default - call GetWords(Line, Word(1), 1) - call Conv2UC( Word(1) ) + call GetWords(Line, Word, 1) + call Conv2UC( Word ) - if ( index(Word(1), "DEFAULT" ) /= 1 ) then ! If it's not "default", read this variable; otherwise use the DEFAULT value - - ! Values exist, so reread line into AryLen of words - call GetWords( Line, Word(AryLen), AryLen) + if ( index(Word, "DEFAULT" ) /= 1 ) then ! If it's not "default", read this variable; otherwise use the DEFAULT value ! read the first AryLen numbers from the line read (Line,*,iostat=IOS) ( Ary(Ind), Ind=1,AryLen ) @@ -6189,7 +6161,7 @@ subroutine ReadR8AryWDefault ( UnIn, Fil, Ary, AryLen, AryName, AryDescr, AryDef integer :: Ind ! Index into the string array. Assumed to be one digit. integer :: IOS ! I/O status returned from the read statement. - character(30) :: Word(AryLen) ! String to hold the words on the line. + character(30) :: Word ! String to hold the words on the line. character(2048) :: Line ! The contents of a line returned from ReadLine() with comment removed. integer :: LineLen ! Length of line read in @@ -6198,13 +6170,10 @@ subroutine ReadR8AryWDefault ( UnIn, Fil, Ary, AryLen, AryName, AryDescr, AryDef if (ErrStat >= AbortErrLev) return ! check for default - call GetWords(Line, Word(1), 1) - call Conv2UC( Word(1) ) - - if ( index(Word(1), "DEFAULT" ) /= 1 ) then ! If it's not "default", read this variable; otherwise use the DEFAULT value + call GetWords(Line, Word, 1) + call Conv2UC( Word ) - ! Values exist, so reread line into AryLen of words - call GetWords( Line, Word(AryLen), AryLen) + if ( index(Word, "DEFAULT" ) /= 1 ) then ! If it's not "default", read this variable; otherwise use the DEFAULT value ! read the first AryLen numbers from the line read (Line,*,iostat=IOS) ( Ary(Ind), Ind=1,AryLen ) diff --git a/modules/nwtc-library/src/NWTC_Num.f90 b/modules/nwtc-library/src/NWTC_Num.f90 index 4316131007..97284cde29 100644 --- a/modules/nwtc-library/src/NWTC_Num.f90 +++ b/modules/nwtc-library/src/NWTC_Num.f90 @@ -5697,6 +5697,7 @@ SUBROUTINE SimStatus( PrevSimTime, PrevClockTime, ZTime, TMax, DescStrIn, StatIn REAL(ReKi), PARAMETER :: SecPerDay = 24*60*60.0_ReKi ! Number of seconds per day + REAL(SiKi) :: DaysRemain ! Days remaining (decimal) INTEGER(4) :: EndHour ! The hour when the simulations is expected to complete. INTEGER(4) :: EndMin ! The minute when the simulations is expected to complete. INTEGER(4) :: EndSec ! The second when the simulations is expected to complete. @@ -5704,6 +5705,7 @@ SUBROUTINE SimStatus( PrevSimTime, PrevClockTime, ZTime, TMax, DescStrIn, StatIn CHARACTER(MaxWrScrLen) :: BlankLine CHARACTER( 8) :: ETimeStr ! String containing the end time. + CHARACTER( 10) :: DaysRemainStr !< decimal format of number of days left CHARACTER( 10) :: DescStr !< optional additional string to print for SimStatus CHARACTER(200) :: StatInfo !< optional additional string to print for SimStatus @@ -5744,18 +5746,20 @@ SUBROUTINE SimStatus( PrevSimTime, PrevClockTime, ZTime, TMax, DescStrIn, StatIn ! Estimate the end time in hours, minutes, and seconds SimTimeLeft = REAL( ( TMax - ZTime )*DeltTime/( ZTime - PrevSimTime ), ReKi ) ! DeltTime/( ZTime - PrevSimTime ) is the delta_ClockTime divided by the delta_SimulationTime + DaysRemain = real((SimTimeLeft) / real(SecPerDay,ReKi),SiKi) EndTime = MOD( CurrClockTime+SimTimeLeft, SecPerDay ) EndHour = INT( EndTime*InSecHr ) EndMin = INT( ( EndTime - REAL( 3600*EndHour ) )*InSecMn ) EndSec = NINT( EndTime - REAL( 3600*EndHour + 60*EndMin ) ) !bjj: this NINT can make the seconds say "60" WRITE (ETimeStr,"(I2.2,2(':',I2.2))") EndHour, EndMin, EndSec + WRITE (DaysRemainStr,"(f10.3)") DaysRemain BlankLine = "" CALL WrOver( BlankLine ) ! BlankLine contains MaxWrScrLen spaces CALL WrOver ( trim(DescStr)//' Time: '//TRIM( Num2LStr( NINT( ZTime ) ) )//' of '//TRIM( Num2LStr( TMax ) )// & ' seconds. '//trim(StatInfo)// & - ' Estimated final completion at '//ETimeStr//'.') + ' Estimated final completion at '//ETimeStr//' (in '//trim(adjustl(DaysRemainStr))//' days).') ! Let's save this time as the previous time for the next call to the routine PrevClockTime = CurrClockTime diff --git a/modules/nwtc-library/src/VTK.f90 b/modules/nwtc-library/src/VTK.f90 index c23cf7133b..e4ffc614d0 100644 --- a/modules/nwtc-library/src/VTK.f90 +++ b/modules/nwtc-library/src/VTK.f90 @@ -93,10 +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_critical) + !$OMP critical(fileopenNWTCio_critical) CALL GetNewUnit( Un, ErrStat, ErrMsg ) CALL OpenFOutFile ( Un, TRIM(FileName), ErrStat, ErrMsg ) - !$OMP end critical(fileopen_critical) + !$OMP end critical(fileopenNWTCio_critical) if (ErrStat >= AbortErrLev) return ! Write a VTP mesh file (Polygonal VTK file) with positions and polygons (surfaces) @@ -119,7 +119,9 @@ SUBROUTINE WrVTK_footer( Un ) WRITE(Un,'(A)') ' ' WRITE(Un,'(A)') ' ' WRITE(Un,'(A)') '' + !$OMP critical(fileopenNWTCio_critical) CLOSE(Un) + !$OMP end critical(fileopenNWTCio_critical) RETURN END SUBROUTINE WrVTK_footer @@ -159,13 +161,14 @@ SUBROUTINE ReadVTK_SP_info( FileName, descr, dims, origin, gridSpacing, vecLabel closeOnReturn = .FALSE. END IF - !$OMP critical(fileopen_critical) - CALL GetNewUnit( Un, ErrStat, ErrMsg ) + !$OMP critical(fileopenNWTCio_critical) + CALL GetNewUnit( Un, ErrStat2, ErrMsg2 ) CALL OpenFInpFile ( Un, TRIM(FileName), ErrStat, ErrMsg ) - !$OMP end critical(fileopen_critical) + !$OMP end critical(fileopenNWTCio_critical) + call SetErrStat(ErrStat2,ErrMsg2,ErrStat,ErrMsg,RoutineName) if (ErrStat >= AbortErrLev) return - CALL ReadCom( Un, FileName, 'File header: Module Version (line 1)', ErrStat2, ErrMsg2, 0 ) + CALL ReadCom( Un, FileName, 'File header: Module Version (line 1)', ErrStat2, ErrMsg2, 0 ) CALL SetErrStat( ErrStat2, ErrMsg2, ErrStat, ErrMsg, RoutineName ) CALL ReadStr( Un, FileName, descr, 'descr', 'File Description line', ErrStat2, ErrMsg2, 0 ) @@ -315,7 +318,9 @@ SUBROUTINE ReadVTK_SP_info( FileName, descr, dims, origin, gridSpacing, vecLabel END IF IF ( (ErrStat >= AbortErrLev) .or. closeOnReturn ) THEN + !$OMP critical(fileopenNWTCio_critical) close(Un) + !$OMP end critical(fileopenNWTCio_critical) Un = -1 RETURN END IF @@ -343,7 +348,9 @@ SUBROUTINE ReadVTK_SP_vectors( FileName, Un, dims, gridVals, ErrStat, ErrMsg ) READ(Un,*, IOSTAT=ErrStat2) gridVals(1:3,1:dims(1),1:dims(2),1:dims(3)) + !$OMP critical(fileopenNWTCio_critical) close(Un) + !$OMP end critical(fileopenNWTCio_critical) if (ErrStat2 /= 0) then CALL SetErrStat( ErrID_Fatal, 'Invalid vtk file: '//trim(FileName)//'.', ErrStat, ErrMsg, 'ReadVTK_SP_vectors' ) end if @@ -361,10 +368,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(fileopen_critical) + !$OMP critical(fileopenNWTCio_critical) CALL GetNewUnit( Un, ErrStat, ErrMsg ) CALL OpenFOutFile ( Un, TRIM(FileName), ErrStat, ErrMsg ) - !$OMP end critical(fileopen_critical) + !$OMP end critical(fileopenNWTCio_critical) if (ErrStat >= AbortErrLev) return WRITE(Un,'(A)') '# vtk DataFile Version 3.0' @@ -406,7 +413,9 @@ SUBROUTINE WrVTK_SP_vectors3D( Un, dataDescr, dims, origin, gridSpacing, gridVal WRITE(Un,'(A,i15)') 'POINT_DATA ', nPts WRITE(Un,'(A)') 'VECTORS '//trim(dataDescr)//' float' WRITE(Un,'(3(f10.2,1X))') gridVals + !$OMP critical(fileopenNWTCio_critical) close(Un) + !$OMP end critical(fileopenNWTCio_critical) RETURN END SUBROUTINE WrVTK_SP_vectors3D @@ -452,7 +461,7 @@ logical function vtk_new_ascii_file(filename,label,mvtk) logical :: b if (.not. mvtk%bFileOpen) then - !$OMP critical(fileopen_critical) + !$OMP critical(fileopenNWTCio_critical) CALL GetNewUnit( mvtk%vtk_unit ) if (mvtk%bBinary) then ! Fortran 2003 stream, otherwise intel fortran ! @@ -468,7 +477,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_critical) + !$OMP end critical(fileopenNWTCio_critical) if (iostatvar == 0) then if (mvtk%bBinary) then write(mvtk%vtk_unit)'# vtk DataFile Version 3.0'//NewLine @@ -498,7 +507,9 @@ logical function vtk_new_ascii_file(filename,label,mvtk) subroutine vtk_close_file(mvtk) type(VTK_Misc),intent(inout) :: mvtk if ( mvtk%bFileOpen ) then + !$OMP critical(fileopenNWTCio_critical) close(mvtk%vtk_unit) + !$OMP end critical(fileopenNWTCio_critical) mvtk%bFileOpen=.false. endif endsubroutine diff --git a/openfast_io/openfast_io/FAST_reader.py b/openfast_io/openfast_io/FAST_reader.py index 8bd0d3438e..c7a5db763c 100644 --- a/openfast_io/openfast_io/FAST_reader.py +++ b/openfast_io/openfast_io/FAST_reader.py @@ -3250,11 +3250,11 @@ def execute(self): moordyn_file = os.path.normpath(os.path.join(self.FAST_directory, self.fst_vt['Fst']['MooringFile'])) if os.path.isfile(moordyn_file): self.read_MoorDyn(moordyn_file) - if self.fst_vt['Fst']['CompElast'] == 2: - bd_file1 = os.path.normpath(os.path.join(self.FAST_directory, self.fst_vt['Fst']['BDBldFile(1)'])) - bd_file2 = os.path.normpath(os.path.join(self.FAST_directory, self.fst_vt['Fst']['BDBldFile(2)'])) - bd_file3 = os.path.normpath(os.path.join(self.FAST_directory, self.fst_vt['Fst']['BDBldFile(3)'])) + bd_file1 = os.path.normpath(os.path.join(self.FAST_directory, self.fst_vt['Fst']['BDBldFile(1)'])) + bd_file2 = os.path.normpath(os.path.join(self.FAST_directory, self.fst_vt['Fst']['BDBldFile(2)'])) + bd_file3 = os.path.normpath(os.path.join(self.FAST_directory, self.fst_vt['Fst']['BDBldFile(3)'])) + if os.path.exists(bd_file1): # if the files are the same then we only need to read it once, need to handle the cases where we have a 2 or 1 bladed rotor # Check unique BeamDyn blade files and read only once if identical if bd_file1 == bd_file2 and bd_file1 == bd_file3: diff --git a/openfast_io/openfast_io/FAST_writer.py b/openfast_io/openfast_io/FAST_writer.py index 4046357cd5..98c6677ecd 100644 --- a/openfast_io/openfast_io/FAST_writer.py +++ b/openfast_io/openfast_io/FAST_writer.py @@ -239,18 +239,17 @@ def execute(self): if 'options' in self.fst_vt['MoorDyn'] and 'WaterKin' in self.fst_vt['MoorDyn']['options']: self.write_WaterKin(os.path.join(self.FAST_runDirectory,self.fst_vt['MoorDyn']['WaterKin_file'])) - if self.fst_vt['Fst']['CompElast'] == 2: - - # look at if the the self.fst_vt['BeamDyn'] is an array, if so, loop through the array - # if its a dictionary, just write the same BeamDyn file - - if isinstance(self.fst_vt['BeamDyn'], list): - for i_BD, BD in enumerate(self.fst_vt['BeamDyn']): + # # look at if the the self.fst_vt['BeamDyn'] is an array, if so, loop through the array + # # if its a dictionary, just write the same BeamDyn file + if isinstance(self.fst_vt['BeamDyn'], list): + for i_BD, BD in enumerate(self.fst_vt['BeamDyn']): + if not BD == {}: self.fst_vt['Fst']['BDBldFile(%d)'%(i_BD+1)] = self.FAST_namingOut + '_BeamDyn_%d.dat'%(i_BD+1) self.write_BeamDyn(bldInd = i_BD) - elif isinstance(self.fst_vt['BeamDyn'], dict): + elif isinstance(self.fst_vt['BeamDyn'], dict): + if not self.fst_vt['BeamDyn'] == {}: self.fst_vt['Fst']['BDBldFile(1)'] = self.FAST_namingOut + '_BeamDyn.dat' self.fst_vt['Fst']['BDBldFile(2)'] = self.fst_vt['Fst']['BDBldFile(1)'] self.fst_vt['Fst']['BDBldFile(3)'] = self.fst_vt['Fst']['BDBldFile(1)'] diff --git a/openfast_io/pyproject.toml b/openfast_io/pyproject.toml index 1df210e668..7595361ef7 100644 --- a/openfast_io/pyproject.toml +++ b/openfast_io/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "openfast_io" # dynamic = ["version"] -version = "4.0.2" +version = "4.0.3" description = "Readers and writers for OpenFAST files." license = {file = "../LICENSE"} authors = [ diff --git a/vs-build/FAST-farm/FAST-Farm.vfproj b/vs-build/FAST-farm/FAST-Farm.vfproj index 4ad7d14ac5..ccd5bd0cc6 100644 --- a/vs-build/FAST-farm/FAST-Farm.vfproj +++ b/vs-build/FAST-farm/FAST-Farm.vfproj @@ -5,7 +5,7 @@ - + @@ -15,7 +15,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -35,7 +35,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -55,7 +55,7 @@ - + diff --git a/vs-build/FAST/FAST.sln b/vs-build/FAST/FAST.sln index f1f08b2b9d..795885ea98 100644 --- a/vs-build/FAST/FAST.sln +++ b/vs-build/FAST/FAST.sln @@ -5,6 +5,7 @@ VisualStudioVersion = 15.0.28307.902 MinimumVisualStudioVersion = 10.0.40219.1 Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = "FAST", "FAST.vfproj", "{18AE8067-CCC6-4479-A0DB-C4089EF9FE71}" ProjectSection(ProjectDependencies) = postProject + {BF86702A-CB17-4050-8AE9-078CDC5910D3} = {BF86702A-CB17-4050-8AE9-078CDC5910D3} {1A440C5B-CBA6-47D9-9CC2-C1CBA8C00BF9} = {1A440C5B-CBA6-47D9-9CC2-C1CBA8C00BF9} EndProjectSection EndProject @@ -23,6 +24,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FAST_Registry", "..\Registr EndProject Project("{6989167D-11E4-40FE-8C1A-2192A86A7E90}") = "OpenFAST-Simulink", "..\OpenFAST-Simulink\OpenFAST-Simulink.vfproj", "{C3C93CC0-EDD7-438F-988C-1F917FAEFA67}" ProjectSection(ProjectDependencies) = postProject + {BF86702A-CB17-4050-8AE9-078CDC5910D3} = {BF86702A-CB17-4050-8AE9-078CDC5910D3} {1A440C5B-CBA6-47D9-9CC2-C1CBA8C00BF9} = {1A440C5B-CBA6-47D9-9CC2-C1CBA8C00BF9} EndProjectSection EndProject diff --git a/vs-build/FASTlib/FASTlib.vfproj b/vs-build/FASTlib/FASTlib.vfproj index 4d87a023a7..ccbd7789a6 100644 --- a/vs-build/FASTlib/FASTlib.vfproj +++ b/vs-build/FASTlib/FASTlib.vfproj @@ -6,7 +6,7 @@ - + @@ -15,7 +15,7 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -32,8 +32,8 @@ - - + + @@ -42,7 +42,7 @@ - + @@ -50,8 +50,8 @@ - - + + @@ -60,7 +60,7 @@ - + @@ -68,8 +68,8 @@ - - + + @@ -78,7 +78,7 @@ - + @@ -86,8 +86,8 @@ - - + + @@ -96,7 +96,7 @@ - + @@ -104,8 +104,8 @@ - - + + @@ -114,7 +114,7 @@ - + @@ -122,8 +122,8 @@ - - + +