Skip to content

Commit

Permalink
Merge pull request #2038 from andrew-platt/f/MoreLinearRegTests
Browse files Browse the repository at this point in the history
Add linearization regression tests
  • Loading branch information
andrew-platt authored Feb 19, 2024
2 parents 53b5f9f + 158459f commit 83f55eb
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 100 deletions.
39 changes: 20 additions & 19 deletions .github/workflows/automated-dev-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ env:
C_COMPILER: gcc-12
GCOV_EXE: gcov-12
CMAKE_BUILD_PARALLEL_LEVEL: 8
CTEST_PARALLEL_LEVEL: 2


jobs:
Expand All @@ -35,7 +36,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@main
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Python
Expand Down Expand Up @@ -86,7 +87,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@main
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
Expand Down Expand Up @@ -121,7 +122,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@main
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Python
Expand Down Expand Up @@ -167,7 +168,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@main
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Python
Expand Down Expand Up @@ -337,7 +338,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@main
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Python
Expand Down Expand Up @@ -382,7 +383,7 @@ jobs:
ctest -VV -R "^ua_"
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-uadriver
Expand Down Expand Up @@ -441,7 +442,7 @@ jobs:
- name: Run SubDyn tests
uses: ./.github/actions/tests-module-subdyn
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-module-drivers
Expand Down Expand Up @@ -499,7 +500,7 @@ jobs:
- name: Run VersionInfo tests
uses: ./.github/actions/tests-module-version
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-modules-debug
Expand Down Expand Up @@ -542,7 +543,7 @@ jobs:
run: |
ctest -VV -L "cpp|python|fastlib"
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-interfaces
Expand Down Expand Up @@ -589,12 +590,12 @@ jobs:
- name: Run 5MW tests
working-directory: ${{runner.workspace}}/openfast/build
run: |
ctest -VV -j4 \
ctest -VV \
-L openfast \
-LE "cpp|linear|python|fastlib" \
-E "5MW_OC4Semi_WSt_WavesWN|5MW_OC3Mnpl_DLL_WTurb_WavesIrr|5MW_OC4Jckt_DLL_WTurb_WavesIrr_MGrowth|5MW_OC3Trpd_DLL_WSt_WavesReg|5MW_Land_BD_DLL_WTurb"
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-OF
Expand Down Expand Up @@ -644,7 +645,7 @@ jobs:
run: |
ctest -VV -L openfast -LE "cpp|linear|python" -R 5MW_OC4Semi_WSt_WavesWN
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-OF-5MW_OC4Semi_WSt_WavesWN
Expand Down Expand Up @@ -694,7 +695,7 @@ jobs:
run: |
ctest -VV -L openfast -LE "cpp|linear|python" -R 5MW_OC3Mnpl_DLL_WTurb_WavesIrr
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-OF-5MW_OC3Mnpl_DLL_WTurb_WavesIrr
Expand Down Expand Up @@ -744,7 +745,7 @@ jobs:
run: |
ctest -VV -L openfast -LE "cpp|linear|python" -R 5MW_OC4Jckt_DLL_WTurb_WavesIrr_MGrowth
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-OF-5MW_OC4Jckt_DLL_WTurb_WavesIrr_MGrowth
Expand Down Expand Up @@ -794,7 +795,7 @@ jobs:
run: |
ctest -VV -L openfast -LE "cpp|linear|python" -R 5MW_OC3Trpd_DLL_WSt_WavesReg
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-OF-5MW_OC3Trpd_DLL_WSt_WavesReg
Expand Down Expand Up @@ -844,7 +845,7 @@ jobs:
run: |
ctest -VV -L openfast -LE "cpp|linear|python" -R 5MW_Land_BD_DLL_WTurb
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-OF-5MW_Land_BD_DLL_WTurb
Expand Down Expand Up @@ -892,9 +893,9 @@ jobs:
- name: Run OpenFAST linearization tests
working-directory: ${{runner.workspace}}/openfast/build
run: |
ctest -VV -j4 -L linear
ctest -VV -L linear
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-OF-linearization
Expand Down Expand Up @@ -946,7 +947,7 @@ jobs:
set OMP_NUM_THREADS=2
ctest -VV -L fastfarm --verbose
- name: Failing test artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: rtest-FF
Expand Down
42 changes: 19 additions & 23 deletions modules/hydrodyn/src/HydroDyn.f90
Original file line number Diff line number Diff line change
Expand Up @@ -3567,39 +3567,35 @@ SUBROUTINE HD_Perturb_x( p, n, perturb_sign, x, dx )


! local variables
integer(intKi) :: i, offset1, offset2, n2
integer(intKi) :: i, j, k

if ( p%totalStates == 0 ) return

!Note: All excitation states for all bodies are stored 1st, then all radiation states
dx = p%dx(n)
offset1 = 1
if ( n <= p%totalExctnStates ) then

! Find body index for exctn states
do i=1,p%nWAMITObj
offset2 = offset1 + p%WAMIT(i)%SS_Exctn%numStates
if ( n >= offset1 .and. n < offset2) then
n2 = n - offset1 + 1
x%WAMIT(i)%SS_Exctn%x( n2 ) = x%WAMIT(i)%SS_Exctn%x( n2 ) + dx * perturb_sign
exit
k = 1

! Find body index for exctn states
do i = 1, p%nWAMITObj
do j = 1, p%WAMIT(i)%SS_Exctn%numStates
if (n == k) then
x%WAMIT(i)%SS_Exctn%x(j) = x%WAMIT(i)%SS_Exctn%x(j) + dx * perturb_sign
return
end if
offset1 = offset2
k = k + 1
end do
end do

else
offset1 = p%totalExctnStates + 1
! Find body index for rdtn states
do i=1,p%nWAMITObj
offset2 = offset1 + p%WAMIT(i)%SS_Exctn%numStates
if ( n >= offset1 .and. n < offset2) then
n2 = n - offset1 + 1
x%WAMIT(i)%SS_Rdtn%x( n2 ) = x%WAMIT(i)%SS_Rdtn%x( n2 ) + dx * perturb_sign
exit
! Find body index for rdtn states
do i = 1, p%nWAMITObj
do j = 1, p%WAMIT(i)%SS_Rdtn%numStates
if (n == k) then
x%WAMIT(i)%SS_Rdtn%x(j) = x%WAMIT(i)%SS_Rdtn%x(j) + dx * perturb_sign
return
end if
offset1 = offset2
k = k + 1
end do
end if
end do

END SUBROUTINE HD_Perturb_x

Expand Down
6 changes: 5 additions & 1 deletion reg_tests/CTestList.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,14 @@ of_regression_linear("Fake5MW_AeroLin_B3_UA6" "openfast;linear;elastodyn"
of_regression_linear("WP_Stationary_Linear" "openfast;linear;elastodyn")
of_regression_linear("Ideal_Beam_Fixed_Free_Linear" "openfast;linear;beamdyn")
of_regression_linear("Ideal_Beam_Free_Free_Linear" "openfast;linear;beamdyn")
of_regression_linear("5MW_Land_Linear_Aero" "openfast;linear;elastodyn;servodyn;aerodyn")
of_regression_linear("5MW_Land_BD_Linear" "openfast;linear;beamdyn;servodyn")
of_regression_linear("5MW_OC4Semi_Linear" "openfast;linear;hydrodyn;servodyn")
of_regression_linear("5MW_Land_BD_Linear_Aero" "openfast;linear;beamdyn;servodyn;aerodyn")
of_regression_linear("5MW_OC4Semi_Linear" "openfast;linear;hydrodyn;servodyn;map")
of_regression_linear("5MW_OC4Semi_MD_Linear" "openfast;linear;hydrodyn;servodyn;moordyn")
of_regression_linear("StC_test_OC4Semi_Linear_Nac" "openfast;linear;servodyn;stc")
of_regression_linear("StC_test_OC4Semi_Linear_Tow" "openfast;linear;servodyn;stc")
of_regression_linear("5MW_OC3Spar_Linear" "openfast;linear;map;hydrodyn")

# FAST Farm regression tests
if(BUILD_FASTFARM)
Expand Down
88 changes: 48 additions & 40 deletions reg_tests/executeOpenfastLinearRegressionCase.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,22 +237,20 @@ def indent(msg, sindent='\t'):
else:

#if verbose:
print(CasePrefix+'freq_ref:', np.around(freq_bas[:8] ,5), '[Hz]')
print(CasePrefix+'freq_new:', np.around(freq_loc[:8] ,5),'[Hz]')
print(CasePrefix+'damp_ref:', np.around(zeta_bas[:8]*100,5), '[%]')
print(CasePrefix+'damp_new:', np.around(zeta_loc[:8]*100,5), '[%]')
print(CasePrefix+'freq_ref:', np.around(freq_bas[:10] ,5), '[Hz]')
print(CasePrefix+'freq_new:', np.around(freq_loc[:10] ,5), '[Hz]')
print(CasePrefix+'damp_ref:', np.around(zeta_bas[:10]*100,5), '[%]')
print(CasePrefix+'damp_new:', np.around(zeta_loc[:10]*100,5), '[%]')

try:
np.testing.assert_allclose(freq_loc[:10], freq_bas[:10], rtol=rtol_f, atol=atol_f)
except Exception as e:
raise Exception('Failed to compare A-matrix frequencies\n\tLinfile: {}.\n\tException: {}'.format(local_file, indent(e.args[0])))


if caseName=='Ideal_Beam_Free_Free_Linear':
# The free-free case is a bit weird, smae frequencies but dampings are +/- a value
zeta_loc=np.abs(zeta_loc)
zeta_bas=np.abs(zeta_bas)

# The free-free case is a bit weird, same frequencies but damping values are +/- a value
zeta_loc = np.abs(zeta_loc)
zeta_bas = np.abs(zeta_bas)

try:
# Note: damping ratios in [%]
Expand All @@ -263,40 +261,50 @@ def indent(msg, sindent='\t'):


# --- Compare individual matrices/vectors
KEYS= ['A','B','C','D','dUdu','dUdy']
KEYS+=['x','y','u','xdot']
KEYS = ['A','B','C','D','dUdu','dUdy', 'x','y','u','xdot']
for k,v in fbas.items():
if k in KEYS and v is not None:
if verbose:
print(CasePrefix+'key:', k)
# Arrays
Mloc=np.atleast_2d(floc[k])
Mbas=np.atleast_2d(fbas[k])

# --- Compare dimensions
if k not in KEYS or v is None:
continue
if verbose:
print(CasePrefix+'key:', k)
# Arrays
Mloc=np.atleast_2d(floc[k])
Mbas=np.atleast_2d(fbas[k])

# --- Compare dimensions
try:
np.testing.assert_equal(Mloc.shape, Mbas.shape)
except Exception as e:
Err = 'Different dimensions for variable `{k}`.\n'
Err += f'\tNew:{Mloc.shape}\n'
Err += f'\tRef:{Mbas.shape}\n'
Err += f'\tLinfile: {local_file}.\n'
raise Exception(Err)

# Get boolean matrix where Mloc is within tolerance of Mbas
M_in_tol = np.isclose(Mloc, Mbas, rtol=rtol, atol=atol)

# Loop through elements where Mloc is not within tolerance of Mbas
# Retest to get error message
for n, (i,j) in enumerate(zip(*np.where(M_in_tol == False)), 1):
try:
np.testing.assert_equal(Mloc.shape, Mbas.shape)
np.testing.assert_allclose(Mloc[i,j], Mbas[i,j], rtol=rtol, atol=atol)
except Exception as e:
Err = 'Different dimensions for variable `{}`.\n'.format(k)
Err+= '\tNew:{}\n'.format(Mloc.shape)
Err+= '\tRef:{}\n'.format(Mbas.shape)
Err+= '\tLinfile: {}.\n'.format(local_file)
raise Exception(Err)


# We for loop below to get the first element that mismatch
# Otherwise, do: np.testing.assert_allclose(floc[k], fbas[k], rtol=rtol, atol=atol)
for i in range(Mbas.shape[0]):
for j in range(Mbas.shape[1]):
# Old method:
#if not isclose(Mloc[i,j], Mbas[i,j], rtol=rtol, atol=atol):
# sElem = 'Element [{},{}], new : {}, baseline: {}'.format(i+1,j+1,Mloc[i,j], Mbas[i,j])
# raise Exception('Failed to compare variable `{}`, {} \n\tLinfile: {}.'.format(k, sElem, local_file)) #, e.args[0]))
try:
np.testing.assert_allclose(Mloc[i,j], Mbas[i,j], rtol=rtol, atol=atol)
except Exception as e:
sElem = 'Element [{},{}], new : {}, baseline: {}'.format(i+1,j+1,Mloc[i,j], Mbas[i,j])
raise Exception('Failed to compare variable `{}`, {} \n\tLinfile: {}.\n\tException: {}'.format(k, sElem, local_file, indent(e.args[0])))
sElem = f'Element [{i+1},{j+1}], new: {Mloc[i,j]}, baseline: {Mbas[i,j]}'
if k in ['dXdx', 'A', 'dXdu', 'B']:
sElem += '\n\t\t row: ' + fbas['x_info']['Description'][i]
if k in ['dYdx', 'C', 'dYdu', 'D']:
sElem += '\n\t\t row: ' + fbas['y_info']['Description'][i]
if k in ['dUdu', 'dUdy']:
sElem += '\n\t\t row: ' + fbas['u_info']['Description'][i]
if k in ['dXdx', 'A', 'dYdx', 'C']:
sElem += '\n\t\t col: ' + fbas['x_info']['Description'][j]
if k in ['dXdu', 'B', 'dYdu', 'D', 'dUdu']:
sElem += '\n\t\t col: ' + fbas['u_info']['Description'][j]
if k in ['dUdy']:
sElem += '\n\t\t col: ' + fbas['y_info']['Description'][j]
raise Exception('Failed to compare matrix `{}`, {} \n\tLinfile: {}.\n\tException: {}'.format(k, sElem, local_file, indent(e.args[0])))


except Exception as e:
exitWithError(e.args[0])
Expand Down
Loading

0 comments on commit 83f55eb

Please sign in to comment.