diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index c001fedf53ff..af6f6c60875d 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -52,12 +52,12 @@ jobs: key: ${{ matrix.config_set }}-${{ github.sha }} restore-keys: ${{ matrix.config_set }} - name: Pre Cleanup - uses: docker://ghcr.io/su2code/su2/build-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/build-su2:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} - name: Build - uses: docker://ghcr.io/su2code/su2/build-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/build-su2:240320-1536 with: args: -b ${{github.ref}} -f "${{matrix.flags}}" - name: Compress binaries @@ -68,7 +68,7 @@ jobs: name: ${{ matrix.config_set }} path: install_bin.tgz - name: Post Cleanup - uses: docker://ghcr.io/su2code/su2/build-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/build-su2:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} @@ -97,12 +97,12 @@ jobs: key: ${{ matrix.config_set }}-${{ github.sha }} restore-keys: ${{ matrix.config_set }} - name: Pre Cleanup - uses: docker://ghcr.io/su2code/su2/build-su2-tsan:230813-0103 + uses: docker://ghcr.io/su2code/su2/build-su2-tsan:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} - name: Build - uses: docker://ghcr.io/su2code/su2/build-su2-tsan:230813-0103 + uses: docker://ghcr.io/su2code/su2/build-su2-tsan:240320-1536 with: args: -b ${{github.ref}} -f "${{matrix.flags}}" - name: Compress binaries @@ -113,7 +113,47 @@ jobs: name: ${{ matrix.config_set }} path: install_bin.tgz - name: Post Cleanup - uses: docker://ghcr.io/su2code/su2/build-su2-tsan:230813-0103 + uses: docker://ghcr.io/su2code/su2/build-su2-tsan:240320-1536 + with: + entrypoint: /bin/rm + args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} + build_asan: + name: Build SU2 (asan) + strategy: + fail-fast: false + matrix: + config_set: [BaseNoMPI-asan, ReverseNoMPI-asan] + include: + - config_set: BaseNoMPI-asan + flags: '--buildtype=debugoptimized -Denable-openblas=true -Dwith-mpi=disabled -Denable-mlpcpp=true --warnlevel=3 --werror' + - config_set: ReverseNoMPI-asan + flags: '--buildtype=debugoptimized -Denable-autodiff=true -Denable-normal=false -Dwith-mpi=disabled --warnlevel=3 --werror' + runs-on: ${{ inputs.runner || 'ubuntu-latest' }} + steps: + - name: Reduce ASLR entropy for asan + run: sudo sysctl -w vm.mmap_rnd_bits=28 + - name: Cache Object Files + uses: actions/cache@v4 + with: + path: ccache + key: ${{ matrix.config_set }}-${{ github.sha }} + restore-keys: ${{ matrix.config_set }} + - name: Pre Cleanup + uses: docker://ghcr.io/su2code/su2/build-su2-asan:240320-1536 + with: + entrypoint: /bin/rm + args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} + - name: Build + run: docker run --rm --cap-add SYS_PTRACE -v $(pwd):${{ github.workspace }} -w ${{ github.workspace }} ghcr.io/su2code/su2/build-su2-asan:240320-1536 -b ${{github.ref}} -f "${{matrix.flags}}" + - name: Compress binaries + run: tar -zcvf install_bin.tgz install/* + - name: Upload Binaries + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.config_set }} + path: install_bin.tgz + - name: Post Cleanup + uses: docker://ghcr.io/su2code/su2/build-su2-asan:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} @@ -144,7 +184,7 @@ jobs: tag: OMP steps: - name: Pre Cleanup - uses: docker://ghcr.io/su2code/su2/test-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} @@ -170,12 +210,12 @@ jobs: chmod a+x $BIN_FOLDER/* ls -lahR $BIN_FOLDER - name: Run Tests in Container - uses: docker://ghcr.io/su2code/su2/test-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2:240320-1536 with: # -t -c args: -b ${{github.ref}} -t develop -c develop -s ${{matrix.testscript}} - name: Cleanup - uses: docker://ghcr.io/su2code/su2/test-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} @@ -192,7 +232,7 @@ jobs: - name: Reduce ASLR entropy for tsan run: sudo sysctl -w vm.mmap_rnd_bits=28 - name: Pre Cleanup - uses: docker://ghcr.io/su2code/su2/test-su2-tsan:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2-tsan:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} @@ -218,12 +258,59 @@ jobs: chmod a+x $BIN_FOLDER/* ls -lahR $BIN_FOLDER - name: Run Tests in Container - uses: docker://ghcr.io/su2code/su2/test-su2-tsan:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2-tsan:240320-1536 with: # -t -c args: -b ${{github.ref}} -t develop -c develop -s ${{matrix.testscript}} -a "--tsan" - name: Cleanup - uses: docker://ghcr.io/su2code/su2/test-su2-tsan:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2-tsan:240320-1536 + with: + entrypoint: /bin/rm + args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} + address_sanitizer_tests: + runs-on: ${{ inputs.runner || 'ubuntu-latest' }} + name: Address Sanitizer Tests + needs: build_asan + strategy: + fail-fast: false + matrix: + testscript: ['serial_regression.py', 'serial_regression_AD.py'] + steps: + - name: Reduce ASLR entropy for asan + run: sudo sysctl -w vm.mmap_rnd_bits=28 + - name: Pre Cleanup + uses: docker://ghcr.io/su2code/su2/test-su2-asan:240320-1536 + with: + entrypoint: /bin/rm + args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} + - name: Download All artifacts + uses: actions/download-artifact@v4 + - name: Uncompress and Move Binaries + run: | + BIN_FOLDER="$PWD/install/bin" + mkdir -p $BIN_FOLDER + ls -lah $BIN_FOLDER + for type in Base Reverse Forward; do + TYPE_FOLDER="${type}NoMPI-asan" + echo "Processing '$TYPE_FOLDER' ..." + if [ -d $TYPE_FOLDER ]; then + pushd $TYPE_FOLDER + ls -lah + tar -zxvf install_bin.tgz + ls -lah install/bin/ + cp -r install/* $BIN_FOLDER/../ + popd; + fi + done + chmod a+x $BIN_FOLDER/* + ls -lahR $BIN_FOLDER + - name: Run Tests in Container + uses: docker://ghcr.io/su2code/su2/test-su2-asan:240320-1536 + with: + # -t -c + args: -b ${{github.ref}} -t develop -c develop -s ${{matrix.testscript}} -a "--asan" + - name: Cleanup + uses: docker://ghcr.io/su2code/su2/test-su2-asan:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} @@ -244,7 +331,7 @@ jobs: tag: MPI steps: - name: Pre Cleanup - uses: docker://ghcr.io/su2code/su2/test-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} @@ -305,11 +392,11 @@ jobs: echo $PWD ls -lahR - name: Run Unit Tests - uses: docker://ghcr.io/su2code/su2/test-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2:240320-1536 with: entrypoint: install/bin/${{matrix.testdriver}} - name: Post Cleanup - uses: docker://ghcr.io/su2code/su2/test-su2:230813-0103 + uses: docker://ghcr.io/su2code/su2/test-su2:240320-1536 with: entrypoint: /bin/rm args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }} diff --git a/Common/src/grid_movement/CFreeFormDefBox.cpp b/Common/src/grid_movement/CFreeFormDefBox.cpp index 78e4b384c9f8..6cfd9cc8c85f 100644 --- a/Common/src/grid_movement/CFreeFormDefBox.cpp +++ b/Common/src/grid_movement/CFreeFormDefBox.cpp @@ -144,7 +144,8 @@ CFreeFormDefBox::~CFreeFormDefBox() { for (iCornerPoints = 0; iCornerPoints < nCornerPoints; iCornerPoints++) delete[] Coord_Corner_Points[iCornerPoints]; delete[] Coord_Corner_Points; - for (iDim = 0; iDim < nDim; iDim++) { + /*--- nDim is 3 at allocation but might change later, so we used fixed 3 as upper bound for deallocation ---*/ + for (iDim = 0; iDim < 3; iDim++) { delete BlendingFunction[iDim]; } delete[] BlendingFunction; diff --git a/Common/src/grid_movement/CSurfaceMovement.cpp b/Common/src/grid_movement/CSurfaceMovement.cpp index 59b0cf7bfe8d..5c29534fb39b 100644 --- a/Common/src/grid_movement/CSurfaceMovement.cpp +++ b/Common/src/grid_movement/CSurfaceMovement.cpp @@ -33,12 +33,21 @@ CSurfaceMovement::CSurfaceMovement() : CGridMovement() { size = SU2_MPI::GetSize(); rank = SU2_MPI::GetRank(); + FFDBox = nullptr; nFFDBox = 0; nLevel = 0; FFDBoxDefinition = false; } -CSurfaceMovement::~CSurfaceMovement() = default; +CSurfaceMovement::~CSurfaceMovement() { + if (FFDBox != nullptr) { + for (unsigned int iFFDBox = 0; iFFDBox < MAX_NUMBER_FFD; ++iFFDBox) { + if (FFDBox[iFFDBox] != nullptr) delete FFDBox[iFFDBox]; + } + delete[] FFDBox; + FFDBox = nullptr; + } +} vector > CSurfaceMovement::SetSurface_Deformation(CGeometry* geometry, CConfig* config) { unsigned short iFFDBox, iDV, iLevel, iChild, iParent, jFFDBox, iMarker; @@ -60,7 +69,16 @@ vector > CSurfaceMovement::SetSurface_Deformation(CGeometry* g if (config->GetDesign_Variable(0) == FFD_SETTING) { /*--- Definition of the FFD deformation class ---*/ - FFDBox = new CFreeFormDefBox*[MAX_NUMBER_FFD]; + /*--- As this method might be called multiple times, properly delete old objects before allocating new ones. ---*/ + if (FFDBox != nullptr) { + for (iFFDBox = 0; iFFDBox < MAX_NUMBER_FFD; ++iFFDBox) { + if (FFDBox[iFFDBox] != nullptr) delete FFDBox[iFFDBox]; + } + delete[] FFDBox; + FFDBox = nullptr; + } + + FFDBox = new CFreeFormDefBox*[MAX_NUMBER_FFD](); /*--- Read the FFD information from the config file ---*/ @@ -167,7 +185,16 @@ vector > CSurfaceMovement::SetSurface_Deformation(CGeometry* g (config->GetDesign_Variable(0) == FFD_THICKNESS) || (config->GetDesign_Variable(0) == FFD_ANGLE_OF_ATTACK)) { /*--- Definition of the FFD deformation class ---*/ - FFDBox = new CFreeFormDefBox*[MAX_NUMBER_FFD]; + /*--- As this method might be called multiple times, properly delete old objects before allocating new ones. ---*/ + if (FFDBox != nullptr) { + for (iFFDBox = 0; iFFDBox < MAX_NUMBER_FFD; ++iFFDBox) { + if (FFDBox[iFFDBox] != nullptr) delete FFDBox[iFFDBox]; + } + delete[] FFDBox; + FFDBox = nullptr; + } + + FFDBox = new CFreeFormDefBox*[MAX_NUMBER_FFD](); /*--- Read the FFD information from the grid file ---*/ diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index 7160cc93e6fe..8476c58dc548 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -2389,7 +2389,9 @@ void CDriver::PreprocessDynamicMesh(CConfig *config, CGeometry **geometry, CSolv cout << "Setting dynamic mesh structure for zone "<< iZone + 1<<"." << endl; grid_movement = new CVolumetricMovement(geometry[MESH_0], config); - surface_movement = new CSurfaceMovement(); + if (surface_movement == nullptr) + surface_movement = new CSurfaceMovement(); + surface_movement->CopyBoundary(geometry[MESH_0], config); if (config->GetTime_Marching() == TIME_MARCHING::HARMONIC_BALANCE){ if (rank == MASTER_NODE) cout << endl << "Instance "<< iInst + 1 <<":" << endl; diff --git a/SU2_GEO/src/SU2_GEO.cpp b/SU2_GEO/src/SU2_GEO.cpp index 86b9ee5c6e07..4e1d3bad9fef 100644 --- a/SU2_GEO/src/SU2_GEO.cpp +++ b/SU2_GEO/src/SU2_GEO.cpp @@ -1429,14 +1429,15 @@ int main(int argc, char* argv[]) { delete[] Xcoord_Airfoil; delete[] Ycoord_Airfoil; delete[] Zcoord_Airfoil; + delete[] Variable_Airfoil; delete[] ObjectiveFunc; delete[] ObjectiveFunc_New; delete[] Gradient; for (iPlane = 0; iPlane < nPlane; iPlane++) { - delete Plane_P0[iPlane]; - delete Plane_Normal[iPlane]; + delete[] Plane_P0[iPlane]; + delete[] Plane_Normal[iPlane]; } delete[] Plane_P0; delete[] Plane_Normal; diff --git a/TestCases/TestCase.py b/TestCases/TestCase.py index 6d577b47df4d..0818c6074a0c 100644 --- a/TestCases/TestCase.py +++ b/TestCases/TestCase.py @@ -45,6 +45,7 @@ def is_float(test_string): def parse_args(description: str): parser = argparse.ArgumentParser(description=description) parser.add_argument('--tsan', action='store_true', help='Run thread sanitizer tests. Requires a tsan-enabled SU2 build.') + parser.add_argument('--asan', action='store_true', help='Run address sanitizer tests. Requires an asan-enabled SU2 build.') return parser.parse_args() class TestCase: @@ -114,6 +115,7 @@ def __init__(self,tag_in): self.cpu_arch = platform.machine().casefold() self.enabled_on_cpu_arch = ["x86_64","amd64","aarch64","arm64"] self.enabled_with_tsan = True + self.enabled_with_asan = True self.command = self.Command() self.timeout = 0 self.tol = 0.0 @@ -125,9 +127,9 @@ def __init__(self,tag_in): self.reference_file_aarch64 = "" self.test_file = "of_grad.dat" - def run_test(self, running_with_tsan=False): + def run_test(self, with_tsan=False, with_asan=False): - if not self.is_enabled(running_with_tsan): + if not self.is_enabled(with_tsan, with_asan): return True print('==================== Start Test: %s ===================='%self.tag) @@ -142,7 +144,7 @@ def run_test(self, running_with_tsan=False): # Adjust the number of iterations in the config file if len(self.test_vals) != 0: - self.adjust_iter(running_with_tsan) + self.adjust_iter(with_tsan, with_asan) # Check for disabling the restart if self.no_restart: @@ -186,7 +188,7 @@ def run_test(self, running_with_tsan=False): delta_vals = [] sim_vals = [] - if not running_with_tsan: # tsan findings result in non-zero return code, no need to examine the output + if not with_tsan and not with_asan: # sanitizer findings result in non-zero return code, no need to examine the output # Examine the output f = open(logfilename,'r') output = f.readlines() @@ -260,7 +262,7 @@ def run_test(self, running_with_tsan=False): if not start_solver: print('ERROR: The code was not able to get to the "Begin solver" section.') - if not running_with_tsan and iter_missing: + if not with_tsan and not with_asan and iter_missing: print('ERROR: The iteration number %d could not be found.'%self.test_iter) print('CPU architecture=%s' % self.cpu_arch) @@ -281,9 +283,9 @@ def run_test(self, running_with_tsan=False): os.chdir(workdir) return passed - def run_filediff(self, running_with_tsan=False): + def run_filediff(self, with_tsan=False, with_asan=False): - if not self.is_enabled(running_with_tsan): + if not self.is_enabled(with_tsan, with_asan): return True print('==================== Start Test: %s ===================='%self.tag) @@ -291,7 +293,7 @@ def run_filediff(self, running_with_tsan=False): timed_out = False # Adjust the number of iterations in the config file - self.adjust_iter(running_with_tsan) + self.adjust_iter(with_tsan, with_asan) self.adjust_test_data() @@ -330,7 +332,7 @@ def run_filediff(self, running_with_tsan=False): print("Output from the failed case:") subprocess.call(["cat", logfilename]) - if not running_with_tsan: # thread sanitizer tests only check the return code, no need to compare outputs + if not with_tsan and not with_asan: # sanitizer tests only check the return code, no need to compare outputs diff_time_start = datetime.datetime.now() if not timed_out and passed: # Compare files @@ -468,9 +470,9 @@ def run_filediff(self, running_with_tsan=False): os.chdir(workdir) return passed - def run_opt(self): + def run_opt(self, with_tsan=False, with_asan=False): - if not self.is_enabled(): + if not self.is_enabled(with_tsan, with_asan): return True print('==================== Start Test: %s ===================='%self.tag) @@ -593,9 +595,9 @@ def run_opt(self): os.chdir(workdir) return passed - def run_geo(self): + def run_geo(self, with_tsan=False, with_asan=False): - if not self.is_enabled(): + if not self.is_enabled(with_tsan, with_asan): return True print('==================== Start Test: %s ===================='%self.tag) @@ -640,52 +642,59 @@ def run_geo(self): timed_out = True passed = False - # Examine the output - f = open(logfilename,'r') - output = f.readlines() + # check for non-zero return code + process.communicate() + if process.returncode != 0: + passed = False + delta_vals = [] sim_vals = [] - data = [] - if not timed_out: - start_solver = False - for line in output: - if not start_solver: # Don't bother parsing anything before SU2_GEO starts - if line.find('Station 1') > -1: - start_solver=True - elif line.find('Station 2') > -1: # jump out of loop if we hit the next station - break - else: # Found the lines; parse the input - - if line.find('Chord') > -1: - raw_data = line.replace(",", "").split() - data.append(raw_data[1]) - found_chord = True - data.append(raw_data[5]) - found_radius = True - data.append(raw_data[8]) - found_toc = True - data.append(raw_data[10]) - found_aoa = True - - if found_chord and found_radius and found_toc and found_aoa: # Found what we're checking for - iter_missing = False - if not len(self.test_vals)==len(data): # something went wrong... probably bad input - print("Error in test_vals!") - passed = False - for j in range(len(data)): - sim_vals.append( float(data[j]) ) - delta_vals.append( abs(float(data[j])-self.test_vals[j]) ) - if delta_vals[j] > self.tol: - exceed_tol = True - passed = False - else: - iter_missing = True - if not start_solver: - passed = False + if not with_tsan and not with_asan: # sanitizer findings result in non-zero return code, no need to examine the output + # Examine the output + f = open(logfilename,'r') + output = f.readlines() + data = [] + if not timed_out: + start_solver = False + for line in output: + if not start_solver: # Don't bother parsing anything before SU2_GEO starts + if line.find('Station 1') > -1: + start_solver=True + elif line.find('Station 2') > -1: # jump out of loop if we hit the next station + break + else: # Found the lines; parse the input + + if line.find('Chord') > -1: + raw_data = line.replace(",", "").split() + data.append(raw_data[1]) + found_chord = True + data.append(raw_data[5]) + found_radius = True + data.append(raw_data[8]) + found_toc = True + data.append(raw_data[10]) + found_aoa = True + + if found_chord and found_radius and found_toc and found_aoa: # Found what we're checking for + iter_missing = False + if not len(self.test_vals)==len(data): # something went wrong... probably bad input + print("Error in test_vals!") + passed = False + for j in range(len(data)): + sim_vals.append( float(data[j]) ) + delta_vals.append( abs(float(data[j])-self.test_vals[j]) ) + if delta_vals[j] > self.tol: + exceed_tol = True + passed = False + else: + iter_missing = True - if iter_missing: - passed = False + if not start_solver: + passed = False + + if iter_missing: + passed = False # Write the test results #for j in output: @@ -703,20 +712,21 @@ def run_geo(self): if timed_out: print('ERROR: Execution timed out. timeout=%d'%self.timeout) - if exceed_tol: - print('ERROR: Difference between computed input and test_vals exceeded tolerance. TOL=%f'%self.tol) + if not with_tsan and not with_asan: + if exceed_tol: + print('ERROR: Difference between computed input and test_vals exceeded tolerance. TOL=%f'%self.tol) - if not start_solver: - print('ERROR: The code was not able to get to the "OBJFUN" section.') + if not start_solver: + print('ERROR: The code was not able to get to the "OBJFUN" section.') - if iter_missing: - print('ERROR: The SU2_GEO values could not be found.') + if iter_missing: + print('ERROR: The SU2_GEO values could not be found.') - print_vals(self.test_vals, name="test_vals (stored)") + print_vals(self.test_vals, name="test_vals (stored)") - print_vals(sim_vals, name="sim_vals (computed)") + print_vals(sim_vals, name="sim_vals (computed)") - print_vals(delta_vals, name="delta_vals") + print_vals(delta_vals, name="delta_vals") print('test duration: %.2f min'%(running_time/60.0)) print('==================== End Test: %s ====================\n'%self.tag) @@ -725,9 +735,9 @@ def run_geo(self): os.chdir(workdir) return passed - def run_def(self): + def run_def(self, with_tsan=False, with_asan=False): - if not self.is_enabled(): + if not self.is_enabled(with_tsan, with_asan): return True print('==================== Start Test: %s ===================='%self.tag) @@ -767,48 +777,55 @@ def run_def(self): timed_out = True passed = False - # Examine the output - f = open(logfilename,'r') - output = f.readlines() + # check for non-zero return code + process.communicate() + if process.returncode != 0: + passed = False + delta_vals = [] sim_vals = [] - if not timed_out: - start_solver = False - for line in output: - if not start_solver: # Don't bother parsing anything before -- Volumetric grid deformation --- - if line.find('Volumetric grid deformation') > -1: - start_solver=True - else: # Found the -- Volumetric grid deformation --- line; parse the input - raw_data = line.split() - try: - iter_number = int(raw_data[0]) - data = raw_data[len(raw_data)-1:] # Take the last column for comparison - except ValueError: - continue - except IndexError: - continue - if iter_number == self.test_iter: # Found the iteration number we're checking for - iter_missing = False - if not len(self.test_vals)==len(data): # something went wrong... probably bad input - print("Error in test_vals!") - passed = False - break - for j in range(len(data)): - sim_vals.append( float(data[j]) ) - delta_vals.append( abs(float(data[j])-self.test_vals[j]) ) - if delta_vals[j] > self.tol: - exceed_tol = True + if not with_tsan and not with_asan: # sanitizer findings result in non-zero return code, no need to examine the output + # Examine the output + f = open(logfilename,'r') + output = f.readlines() + if not timed_out: + start_solver = False + for line in output: + if not start_solver: # Don't bother parsing anything before -- Volumetric grid deformation --- + if line.find('Volumetric grid deformation') > -1: + start_solver=True + else: # Found the -- Volumetric grid deformation --- line; parse the input + raw_data = line.split() + try: + iter_number = int(raw_data[0]) + data = raw_data[len(raw_data)-1:] # Take the last column for comparison + except ValueError: + continue + except IndexError: + continue + + if iter_number == self.test_iter: # Found the iteration number we're checking for + iter_missing = False + if not len(self.test_vals)==len(data): # something went wrong... probably bad input + print("Error in test_vals!") passed = False - break - else: - iter_missing = True + break + for j in range(len(data)): + sim_vals.append( float(data[j]) ) + delta_vals.append( abs(float(data[j])-self.test_vals[j]) ) + if delta_vals[j] > self.tol: + exceed_tol = True + passed = False + break + else: + iter_missing = True - if not start_solver: - passed = False + if not start_solver: + passed = False - if iter_missing: - passed = False + if iter_missing: + passed = False # Write the test results #for j in output: @@ -826,22 +843,23 @@ def run_def(self): if timed_out: print('ERROR: Execution timed out. timeout=%d sec'%self.timeout) - if exceed_tol: - print('ERROR: Difference between computed input and test_vals exceeded tolerance. TOL=%e'%self.tol) + if not with_tsan and not with_asan: + if exceed_tol: + print('ERROR: Difference between computed input and test_vals exceeded tolerance. TOL=%e'%self.tol) - if not start_solver: - print('ERROR: The code was not able to get to the "Begin solver" section.') + if not start_solver: + print('ERROR: The code was not able to get to the "Begin solver" section.') - if iter_missing: - print('ERROR: The iteration number %d could not be found.'%self.test_iter) + if iter_missing: + print('ERROR: The iteration number %d could not be found.'%self.test_iter) - print('test_iter=%d' % self.test_iter) + print('test_iter=%d' % self.test_iter) - print_vals(self.test_vals, name="test_vals (stored)") + print_vals(self.test_vals, name="test_vals (stored)") - print_vals(sim_vals, name="sim_vals (computed)") + print_vals(sim_vals, name="sim_vals (computed)") - print_vals(delta_vals, name="delta_vals") + print_vals(delta_vals, name="delta_vals") print('test duration: %.2f min'%(running_time/60.0)) #print('==================== End Test: %s ====================\n'%self.tag) @@ -850,7 +868,7 @@ def run_def(self): os.chdir(workdir) return passed - def adjust_iter(self, running_with_tsan=False): + def adjust_iter(self, with_tsan=False, with_asan=False): # Read the cfg file workdir = os.getcwd() @@ -861,7 +879,7 @@ def adjust_iter(self, running_with_tsan=False): new_iter = self.test_iter + 1 - if running_with_tsan: + if with_tsan or with_asan: # detect restart restart_iter = 0 @@ -942,18 +960,19 @@ def disable_restart(self): return - def is_enabled(self, running_with_tsan=False): + def is_enabled(self, with_tsan=False, with_asan=False): is_enabled_on_arch = self.cpu_arch in self.enabled_on_cpu_arch if not is_enabled_on_arch: print('Ignoring test "%s" because it is not enabled for the current CPU architecture: %s' % (self.tag, self.cpu_arch)) - tsan_compatible = not running_with_tsan or self.enabled_with_tsan + tsan_compatible = not with_tsan or self.enabled_with_tsan + asan_compatible = not with_asan or self.enabled_with_asan if not tsan_compatible: print('Ignoring test "%s" because it is not enabled to run with the thread sanitizer.' % self.tag) - return is_enabled_on_arch and tsan_compatible + return is_enabled_on_arch and tsan_compatible and asan_compatible def adjust_test_data(self): diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py index b891e7d9a849..f84034880709 100644 --- a/TestCases/parallel_regression.py +++ b/TestCases/parallel_regression.py @@ -1058,6 +1058,7 @@ def main(): Aachen_3D_restart.cfg_dir = "turbomachinery/Aachen_turbine" Aachen_3D_restart.cfg_file = "aachen_3D_MP_restart.cfg" Aachen_3D_restart.test_iter = 5 + Aachen_3D_restart.enabled_with_asan = False Aachen_3D_restart.test_vals = [-15.329206, -15.008622, -15.078888, -13.841072, -12.727840, -9.975729] test_list.append(Aachen_3D_restart) diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py index dab5dd1256ff..f7ab332ae7a5 100644 --- a/TestCases/serial_regression.py +++ b/TestCases/serial_regression.py @@ -28,12 +28,15 @@ from __future__ import print_function, division, absolute_import import sys from TestCase import TestCase +from TestCase import parse_args def main(): '''This program runs SU2 and ensures that the output matches specified values. This will be used to do checks when code is pushed to github to make sure nothing is broken. ''' + args = parse_args('Serial Regression Tests') + test_list = [] ######################### @@ -857,6 +860,7 @@ def main(): Aachen_3D_restart.cfg_dir = "turbomachinery/Aachen_turbine" Aachen_3D_restart.cfg_file = "aachen_3D_MP_restart.cfg" Aachen_3D_restart.test_iter = 5 + Aachen_3D_restart.enabled_with_asan = False Aachen_3D_restart.test_vals = [-15.137167, -14.551444, -15.078894, -13.486154, -12.724891, -9.717612] test_list.append(Aachen_3D_restart) @@ -1196,7 +1200,7 @@ def main(): if test.tol == 0.0: test.tol = 0.00001 - pass_list = [ test.run_test() for test in test_list ] + pass_list = [ test.run_test(args.tsan, args.asan) for test in test_list ] ###################################### @@ -1211,7 +1215,7 @@ def main(): naca0012_geo.command = TestCase.Command(exec = "SU2_GEO") naca0012_geo.timeout = 1600 naca0012_geo.tol = 0.00001 - pass_list.append(naca0012_geo.run_geo()) + pass_list.append(naca0012_geo.run_geo(args.tsan, args.asan)) test_list.append(naca0012_geo) ###################################### @@ -1228,7 +1232,7 @@ def main(): intersect_def.timeout = 1600 intersect_def.tol = 1e-04 - pass_list.append(intersect_def.run_def()) + pass_list.append(intersect_def.run_def(args.tsan, args.asan)) test_list.append(intersect_def) # Inviscid NACA0012 (triangles) @@ -1241,7 +1245,7 @@ def main(): naca0012_def.timeout = 1600 naca0012_def.tol = 1e-08 - pass_list.append(naca0012_def.run_def()) + pass_list.append(naca0012_def.run_def(args.tsan, args.asan)) test_list.append(naca0012_def) # Inviscid NACA0012 based on SURFACE_FILE input (surface_bump.dat) @@ -1254,7 +1258,7 @@ def main(): naca0012_def_file.timeout = 1600 naca0012_def_file.tol = 1e-8 - pass_list.append(naca0012_def_file.run_def()) + pass_list.append(naca0012_def_file.run_def(args.tsan, args.asan)) test_list.append(naca0012_def_file) # RAE2822 (mixed tris + quads) @@ -1267,7 +1271,7 @@ def main(): rae2822_def.timeout = 1600 rae2822_def.tol = 1e-13 - pass_list.append(rae2822_def.run_def()) + pass_list.append(rae2822_def.run_def(args.tsan, args.asan)) test_list.append(rae2822_def) # Turb NACA4412 (quads, wall distance) @@ -1280,7 +1284,7 @@ def main(): naca4412_def.timeout = 1600 naca4412_def.tol = 1e-12 - pass_list.append(naca4412_def.run_def()) + pass_list.append(naca4412_def.run_def(args.tsan, args.asan)) test_list.append(naca4412_def) # Brick of tets (inverse volume) @@ -1293,7 +1297,7 @@ def main(): brick_tets_def.timeout = 1600 brick_tets_def.tol = 1e-09 - pass_list.append(brick_tets_def.run_def()) + pass_list.append(brick_tets_def.run_def(args.tsan, args.asan)) test_list.append(brick_tets_def) # Brick of isotropic hexas (inverse volume) @@ -1306,7 +1310,7 @@ def main(): brick_hex_def.timeout = 1600 brick_hex_def.tol = 1e-09 - pass_list.append(brick_hex_def.run_def()) + pass_list.append(brick_hex_def.run_def(args.tsan, args.asan)) test_list.append(brick_hex_def) # Brick with a pyramid layer (inverse volume) @@ -1319,7 +1323,7 @@ def main(): brick_pyra_def.timeout = 1600 brick_pyra_def.tol = 1e-08 - pass_list.append(brick_pyra_def.run_def()) + pass_list.append(brick_pyra_def.run_def(args.tsan, args.asan)) test_list.append(brick_pyra_def) # Brick of isotropic prisms (inverse volume) @@ -1332,7 +1336,7 @@ def main(): brick_prism_def.timeout = 1600 brick_prism_def.tol = 1e-08 - pass_list.append(brick_prism_def.run_def()) + pass_list.append(brick_prism_def.run_def(args.tsan, args.asan)) test_list.append(brick_prism_def) # Brick of prisms with high aspect ratio cells near the wall (wall distance) @@ -1345,7 +1349,7 @@ def main(): brick_prism_rans_def.timeout = 1600 brick_prism_rans_def.tol = 1e-12 - pass_list.append(brick_prism_rans_def.run_def()) + pass_list.append(brick_prism_rans_def.run_def(args.tsan, args.asan)) test_list.append(brick_prism_rans_def) # Brick of hexas with high aspect ratio cells near the wall (inverse volume) @@ -1358,7 +1362,7 @@ def main(): brick_hex_rans_def.timeout = 1600 brick_hex_rans_def.tol = 1e-12 - pass_list.append(brick_hex_rans_def.run_def()) + pass_list.append(brick_hex_rans_def.run_def(args.tsan, args.asan)) test_list.append(brick_hex_rans_def) # Cylindrical FFD test @@ -1371,7 +1375,7 @@ def main(): cylinder_ffd_def.timeout = 1600 cylinder_ffd_def.tol = 1e-09 - pass_list.append(cylinder_ffd_def.run_def()) + pass_list.append(cylinder_ffd_def.run_def(args.tsan, args.asan)) test_list.append(cylinder_ffd_def) # Spherical FFD test @@ -1384,7 +1388,7 @@ def main(): sphere_ffd_def.timeout = 1600 sphere_ffd_def.tol = 1e-08 - pass_list.append(sphere_ffd_def.run_def()) + pass_list.append(sphere_ffd_def.run_def(args.tsan, args.asan)) test_list.append(sphere_ffd_def) # Spherical FFD test using BSplines @@ -1397,7 +1401,7 @@ def main(): sphere_ffd_def_bspline.timeout = 1600 sphere_ffd_def_bspline.tol = 1e-08 - pass_list.append(sphere_ffd_def_bspline.run_def()) + pass_list.append(sphere_ffd_def_bspline.run_def(args.tsan, args.asan)) test_list.append(sphere_ffd_def_bspline) ###################################### @@ -1413,7 +1417,8 @@ def main(): contadj_euler_py.timeout = 1600 contadj_euler_py.reference_file = "of_grad_cd.dat.ref" contadj_euler_py.test_file = "of_grad_cd.dat" - pass_list.append(contadj_euler_py.run_filediff()) + contadj_euler_py.enabled_with_asan = False + pass_list.append(contadj_euler_py.run_filediff(args.tsan, args.asan)) test_list.append(contadj_euler_py) # test shape_optimization.py @@ -1425,7 +1430,8 @@ def main(): shape_opt_euler_py.command = TestCase.Command(exec = "shape_optimization.py", param = "-g CONTINUOUS_ADJOINT -f") shape_opt_euler_py.timeout = 1600 shape_opt_euler_py.tol = 0.00001 - pass_list.append(shape_opt_euler_py.run_opt()) + shape_opt_euler_py.enabled_with_asan = False + pass_list.append(shape_opt_euler_py.run_opt(args.tsan, args.asan)) test_list.append(shape_opt_euler_py) # Multiple functionals with the continuous adjoint @@ -1437,7 +1443,8 @@ def main(): contadj_multi_py.timeout = 1600 contadj_multi_py.reference_file = "of_grad_combo.dat.ref" contadj_multi_py.test_file = "of_grad_combo.dat" - pass_list.append(contadj_multi_py.run_filediff()) + contadj_multi_py.enabled_with_asan = False + pass_list.append(contadj_multi_py.run_filediff(args.tsan, args.asan)) test_list.append(contadj_multi_py) # Optimization with multiple objectives, with gradients evaluated individually @@ -1475,7 +1482,8 @@ def main(): opt_multiobj1surf_py.command = TestCase.Command(exec = "shape_optimization.py", param = "-g CONTINUOUS_ADJOINT -f") opt_multiobj1surf_py.timeout = 1600 opt_multiobj1surf_py.tol = 0.00001 - pass_list.append(opt_multiobj1surf_py.run_opt()) + opt_multiobj1surf_py.enabled_with_asan = False + pass_list.append(opt_multiobj1surf_py.run_opt(args.tsan, args.asan)) test_list.append(opt_multiobj1surf_py) # test optimization, with a single objective evaluated on multiple surfaces @@ -1487,7 +1495,8 @@ def main(): opt_2surf1obj_py.command = TestCase.Command(exec = "shape_optimization.py", param = "-g CONTINUOUS_ADJOINT -f") opt_2surf1obj_py.timeout = 1600 opt_2surf1obj_py.tol = 0.00001 - pass_list.append(opt_2surf1obj_py.run_opt()) + opt_2surf1obj_py.enabled_with_asan = False + pass_list.append(opt_2surf1obj_py.run_opt(args.tsan, args.asan)) test_list.append(opt_2surf1obj_py) ########################## @@ -1503,8 +1512,9 @@ def main(): pywrapper_naca0012.command = TestCase.Command(exec = "SU2_CFD.py", param = "-f") pywrapper_naca0012.timeout = 1600 pywrapper_naca0012.tol = 0.00001 + pywrapper_naca0012.enabled_with_asan = False test_list.append(pywrapper_naca0012) - pass_list.append(pywrapper_naca0012.run_test()) + pass_list.append(pywrapper_naca0012.run_test(args.tsan, args.asan)) # NACA0012 (SST, FUN3D results for finest grid: CL=1.0840, CD=0.01253) pywrapper_turb_naca0012_sst = TestCase('pywrapper_turb_naca0012_sst') @@ -1516,8 +1526,9 @@ def main(): pywrapper_turb_naca0012_sst.command = TestCase.Command(exec = "SU2_CFD.py", param = "-f") pywrapper_turb_naca0012_sst.timeout = 3200 pywrapper_turb_naca0012_sst.tol = 0.00001 + pywrapper_turb_naca0012_sst.enabled_with_asan = False test_list.append(pywrapper_turb_naca0012_sst) - pass_list.append(pywrapper_turb_naca0012_sst.run_test()) + pass_list.append(pywrapper_turb_naca0012_sst.run_test(args.tsan, args.asan)) # Square cylinder pywrapper_square_cylinder = TestCase('pywrapper_square_cylinder') @@ -1529,8 +1540,9 @@ def main(): pywrapper_square_cylinder.timeout = 1600 pywrapper_square_cylinder.tol = 0.00001 pywrapper_square_cylinder.unsteady = True + pywrapper_square_cylinder.enabled_with_asan = False test_list.append(pywrapper_square_cylinder) - pass_list.append(pywrapper_square_cylinder.run_test()) + pass_list.append(pywrapper_square_cylinder.run_test(args.tsan, args.asan)) # Aeroelastic pywrapper_aeroelastic = TestCase('pywrapper_aeroelastic') @@ -1542,8 +1554,9 @@ def main(): pywrapper_aeroelastic.timeout = 1600 pywrapper_aeroelastic.tol = 0.00001 pywrapper_aeroelastic.unsteady = True + pywrapper_aeroelastic.enabled_with_asan = False test_list.append(pywrapper_aeroelastic) - pass_list.append(pywrapper_aeroelastic.run_test()) + pass_list.append(pywrapper_aeroelastic.run_test(args.tsan, args.asan)) # FSI, 2d pywrapper_fsi2d = TestCase('pywrapper_fsi2d') @@ -1556,8 +1569,9 @@ def main(): pywrapper_fsi2d.multizone = True pywrapper_fsi2d.timeout = 1600 pywrapper_fsi2d.tol = 0.00001 + pywrapper_fsi2d.enabled_with_asan = False test_list.append(pywrapper_fsi2d) - pass_list.append(pywrapper_fsi2d.run_test()) + pass_list.append(pywrapper_fsi2d.run_test(args.tsan, args.asan)) # Unsteady CHT pywrapper_unsteadyCHT = TestCase('pywrapper_unsteadyCHT') @@ -1569,8 +1583,9 @@ def main(): pywrapper_unsteadyCHT.timeout = 1600 pywrapper_unsteadyCHT.tol = 0.00001 pywrapper_unsteadyCHT.unsteady = True + pywrapper_unsteadyCHT.enabled_with_asan = False test_list.append(pywrapper_unsteadyCHT) - pass_list.append(pywrapper_unsteadyCHT.run_test()) + pass_list.append(pywrapper_unsteadyCHT.run_test(args.tsan, args.asan)) # Rigid motion pywrapper_rigidMotion = TestCase('pywrapper_rigidMotion') @@ -1582,8 +1597,9 @@ def main(): pywrapper_rigidMotion.timeout = 1600 pywrapper_rigidMotion.tol = 0.00001 pywrapper_rigidMotion.unsteady = True + pywrapper_rigidMotion.enabled_with_asan = False test_list.append(pywrapper_rigidMotion) - pass_list.append(pywrapper_rigidMotion.run_test()) + pass_list.append(pywrapper_rigidMotion.run_test(args.tsan, args.asan)) # Tests summary print('==================================================================') diff --git a/TestCases/serial_regression_AD.py b/TestCases/serial_regression_AD.py index 2a0ac7570973..ac268040cd57 100644 --- a/TestCases/serial_regression_AD.py +++ b/TestCases/serial_regression_AD.py @@ -30,12 +30,15 @@ import sys from TestCase import TestCase +from TestCase import parse_args def main(): '''This program runs SU2 and ensures that the output matches specified values. This will be used to do checks when code is pushed to github to make sure nothing is broken. ''' + args = parse_args('Serial Regression AD Tests') + test_list = [] ##################################### @@ -223,7 +226,7 @@ def main(): if test.tol == 0.0: test.tol = 0.00001 - pass_list = [ test.run_test() for test in test_list ] + pass_list = [ test.run_test(args.tsan, args.asan) for test in test_list ] ################################### ### Coupled RHT-CFD Adjoint ### @@ -239,7 +242,8 @@ def main(): discadj_rht.reference_file = "of_grad_cd.csv.ref" discadj_rht.reference_file_aarch64 = "of_grad_cd_aarch64.csv.ref" discadj_rht.test_file = "of_grad_cd.csv" - pass_list.append(discadj_rht.run_filediff()) + discadj_rht.enabled_with_asan = False + pass_list.append(discadj_rht.run_filediff(args.tsan, args.asan)) test_list.append(discadj_rht) ###################################### @@ -256,7 +260,8 @@ def main(): discadj_euler_py.reference_file = "of_grad_cd_disc.dat.ref" discadj_euler_py.reference_file_aarch64 = "of_grad_cd_disc_aarch64.dat.ref" discadj_euler_py.test_file = "of_grad_cd.dat" - pass_list.append(discadj_euler_py.run_filediff()) + discadj_euler_py.enabled_with_asan = False + pass_list.append(discadj_euler_py.run_filediff(args.tsan, args.asan)) test_list.append(discadj_euler_py) # test discrete_adjoint with multiple ffd boxes @@ -269,7 +274,8 @@ def main(): discadj_multiple_ffd_py.reference_file = "of_grad_cd.dat.ref" discadj_multiple_ffd_py.reference_file_aarch64 = "of_grad_cd_aarch64.dat.ref" discadj_multiple_ffd_py.test_file = "of_grad_cd.dat" - pass_list.append(discadj_multiple_ffd_py.run_filediff()) + discadj_multiple_ffd_py.enabled_with_asan = False + pass_list.append(discadj_multiple_ffd_py.run_filediff(args.tsan, args.asan)) test_list.append(discadj_multiple_ffd_py) # test direct_differentiation.py @@ -282,7 +288,8 @@ def main(): directdiff_euler_py.reference_file = "of_grad_directdiff.dat.ref" directdiff_euler_py.reference_file_aarch64 = "of_grad_directdiff_aarch64.dat.ref" directdiff_euler_py.test_file = "DIRECTDIFF/of_grad_directdiff.dat" - pass_list.append(directdiff_euler_py.run_filediff()) + directdiff_euler_py.enabled_with_asan = False + pass_list.append(directdiff_euler_py.run_filediff(args.tsan, args.asan)) test_list.append(directdiff_euler_py) # test direct_differentiation.py with multiple ffd boxes @@ -295,7 +302,8 @@ def main(): directdiff_multiple_ffd_py.reference_file = "of_grad_directdiff.dat.ref" directdiff_multiple_ffd_py.reference_file_aarch64 = "of_grad_directdiff_aarch64.dat.ref" directdiff_multiple_ffd_py.test_file = "DIRECTDIFF/of_grad_directdiff.dat" - pass_list.append(directdiff_multiple_ffd_py.run_filediff()) + directdiff_multiple_ffd_py.enabled_with_asan = False + pass_list.append(directdiff_multiple_ffd_py.run_filediff(args.tsan, args.asan)) test_list.append(directdiff_multiple_ffd_py) # test continuous_adjoint.py, with multiple objectives @@ -320,8 +328,9 @@ def main(): pywrapper_FEA_AD_FlowLoad.timeout = 1600 pywrapper_FEA_AD_FlowLoad.tol = 0.000001 pywrapper_FEA_AD_FlowLoad.new_output = False + pywrapper_FEA_AD_FlowLoad.enabled_with_asan = False test_list.append(pywrapper_FEA_AD_FlowLoad) - pass_list.append(pywrapper_FEA_AD_FlowLoad.run_test()) + pass_list.append(pywrapper_FEA_AD_FlowLoad.run_test(args.tsan, args.asan)) # Flow AD Mesh Displacement Sensitivity pywrapper_CFD_AD_MeshDisp = TestCase('pywrapper_CFD_AD_MeshDisp') @@ -333,8 +342,9 @@ def main(): pywrapper_CFD_AD_MeshDisp.timeout = 1600 pywrapper_CFD_AD_MeshDisp.tol = 0.000001 pywrapper_CFD_AD_MeshDisp.new_output = False + pywrapper_CFD_AD_MeshDisp.enabled_with_asan = False test_list.append(pywrapper_CFD_AD_MeshDisp) - pass_list.append(pywrapper_CFD_AD_MeshDisp.run_test()) + pass_list.append(pywrapper_CFD_AD_MeshDisp.run_test(args.tsan, args.asan)) ################################### @@ -350,7 +360,8 @@ def main(): grad_smooth_naca0012.reference_file = "of_hess.dat.ref" grad_smooth_naca0012.reference_file_aarch64 = "of_hess_aarch64.dat.ref" grad_smooth_naca0012.test_file = "of_hess.dat" - pass_list.append(grad_smooth_naca0012.run_filediff()) + grad_smooth_naca0012.enabled_with_asan = False + pass_list.append(grad_smooth_naca0012.run_filediff(args.tsan, args.asan)) test_list.append(grad_smooth_naca0012) # Tests summary