diff --git a/CMake/Packages.cmake b/CMake/Packages.cmake
index d96ed89a935cdaf219b938a043c50d7a051fae47..aa7e5f6cb115cff1d1e387d41be630b9b8e0d8c8 100644
--- a/CMake/Packages.cmake
+++ b/CMake/Packages.cmake
@@ -31,7 +31,7 @@
 include(FetchContent)
 
 # spdlog
-set(spdlog_version "v1.9.1")
+set(spdlog_version "v1.14.1")
 set(spdlog_url "https://github.com/gabime/spdlog")
 message(STATUS "Fetching spdlog: ${spdlog_version}")
 FetchContent_Declare(
diff --git a/src/basics/tests/LogRedirector.cpp b/src/basics/tests/LogRedirector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c713b91e0a3edf41da6523e7e0f142291c5b9b3d
--- /dev/null
+++ b/src/basics/tests/LogRedirector.cpp
@@ -0,0 +1,83 @@
+//=======================================================================================
+// ____          ____    __    ______     __________   __      __       __        __
+// \    \       |    |  |  |  |   _   \  |___    ___| |  |    |  |     /  \      |  |
+//  \    \      |    |  |  |  |  |_)   |     |  |     |  |    |  |    /    \     |  |
+//   \    \     |    |  |  |  |   _   /      |  |     |  |    |  |   /  /\  \    |  |
+//    \    \    |    |  |  |  |  | \  \      |  |     |   \__/   |  /  ____  \   |  |____
+//     \    \   |    |  |__|  |__|  \__\     |__|      \________/  /__/    \__\  |_______|
+//      \    \  |    |   ________________________________________________________________
+//       \    \ |    |  |  ______________________________________________________________|
+//        \    \|    |  |  |         __          __     __     __     ______      _______
+//         \         |  |  |_____   |  |        |  |   |  |   |  |   |   _  \    /  _____)
+//          \        |  |   _____|  |  |        |  |   |  |   |  |   |  | \  \   \_______
+//           \       |  |  |        |  |_____   |   \_/   |   |  |   |  |_/  /    _____  |
+//            \ _____|  |__|        |________|   \_______/    |__|   |______/    (_______/
+//
+//  This file is part of VirtualFluids. VirtualFluids is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  VirtualFluids is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+//  for more details.
+//
+//  SPDX-License-Identifier: GPL-3.0-or-later
+//  SPDX-FileCopyrightText: Copyright © VirtualFluids Project contributors, see AUTHORS.md in root folder
+//
+//! \{
+//=======================================================================================
+
+#include "LogRedirector.h"
+
+#include <spdlog/sinks/ostream_sink.h>
+
+#include <logger/Logger.h>
+
+namespace testing::vf
+{
+    
+LogRedirector::LogRedirector()
+{
+    this->redirectDefaultLoggerToString();
+}
+
+LogRedirector::~LogRedirector()
+{
+    logger->sinks()[0] = std::move(oldSink);
+    logger->set_level(oldLevel);
+}
+
+void LogRedirector::redirectDefaultLoggerToString()
+{
+    redirectLoggerToString(spdlog::default_logger(), spdlog::default_logger()->level());
+}
+
+void LogRedirector::redirectLoggerToString(std::shared_ptr<spdlog::logger> logger, spdlog::level::level_enum newLevel)
+{
+    this->logger = logger;
+    oldLevel = logger->level();
+    std::vector<spdlog::sink_ptr>& sinks { logger->sinks() };
+    assert(sinks.size() == 1);
+
+    oldSink = std::move(sinks[0]);
+
+    sinks[0] = std::make_shared<spdlog::sinks::ostream_sink_st>(oss);
+    logger->set_pattern("[%l] %v");
+    logger->set_level(newLevel);
+}
+
+bool LogRedirector::logContainsWarning()
+{
+    return getLoggerOutput().find("warning") != std::string::npos;
+}
+
+std::string LogRedirector::getLoggerOutput()
+{
+    return oss.str();
+}
+
+} // namespace testing::vf
+
+//! \}
diff --git a/src/basics/tests/LogRedirector.h b/src/basics/tests/LogRedirector.h
new file mode 100644
index 0000000000000000000000000000000000000000..93bafdbbdd71fd4eb77d186452095396dcf2e3a0
--- /dev/null
+++ b/src/basics/tests/LogRedirector.h
@@ -0,0 +1,64 @@
+//=======================================================================================
+// ____          ____    __    ______     __________   __      __       __        __
+// \    \       |    |  |  |  |   _   \  |___    ___| |  |    |  |     /  \      |  |
+//  \    \      |    |  |  |  |  |_)   |     |  |     |  |    |  |    /    \     |  |
+//   \    \     |    |  |  |  |   _   /      |  |     |  |    |  |   /  /\  \    |  |
+//    \    \    |    |  |  |  |  | \  \      |  |     |   \__/   |  /  ____  \   |  |____
+//     \    \   |    |  |__|  |__|  \__\     |__|      \________/  /__/    \__\  |_______|
+//      \    \  |    |   ________________________________________________________________
+//       \    \ |    |  |  ______________________________________________________________|
+//        \    \|    |  |  |         __          __     __     __     ______      _______
+//         \         |  |  |_____   |  |        |  |   |  |   |  |   |   _  \    /  _____)
+//          \        |  |   _____|  |  |        |  |   |  |   |  |   |  | \  \   \_______
+//           \       |  |  |        |  |_____   |   \_/   |   |  |   |  |_/  /    _____  |
+//            \ _____|  |__|        |________|   \_______/    |__|   |______/    (_______/
+//
+//  This file is part of VirtualFluids. VirtualFluids is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  VirtualFluids is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+//  for more details.
+//
+//  SPDX-License-Identifier: GPL-3.0-or-later
+//  SPDX-FileCopyrightText: Copyright © VirtualFluids Project contributors, see AUTHORS.md in root folder
+//
+//! \addtogroup tests
+//! \ingroup basics
+//! \{
+//! \author Anna Wellmann
+//=======================================================================================
+
+#include <sstream>
+
+#include "spdlog/logger.h"
+#include "spdlog/spdlog.h"
+
+namespace testing::vf {
+
+class LogRedirector
+{
+public:
+    LogRedirector();
+    ~LogRedirector();
+    bool logContainsWarning();
+    std::string getLoggerOutput();
+
+private:
+    void redirectDefaultLoggerToString();
+    void redirectLoggerToString(std::shared_ptr<spdlog::logger> log, spdlog::level::level_enum newLevel);
+    void resetLogger();
+
+    std::ostringstream oss;
+
+    std::shared_ptr<spdlog::logger> logger;
+    spdlog::sink_ptr oldSink;
+    spdlog::level::level_enum oldLevel;
+};
+
+}
+
+//! \}
diff --git a/src/basics/tests/testUtilities.h b/src/basics/tests/testUtilities.h
index 9c1176e98e8c188d86dfc106b37ab0ddfb4a86a2..1ffaa9cd5c47d94c417a53a91d2529e8b58d1245 100644
--- a/src/basics/tests/testUtilities.h
+++ b/src/basics/tests/testUtilities.h
@@ -34,8 +34,8 @@
 #ifndef TESTUTILITIES_H
 #define TESTUTILITIES_H
 
-#include <string>
 #include <gmock/gmock.h>
+#include <string>
 
 inline auto RealEq = [](auto value) {
 #ifdef VF_DOUBLE_ACCURACY
@@ -53,23 +53,6 @@ inline auto RealNear = [](auto value, auto max_abs_error) {
 #endif
 };
 
-namespace testingVF
-{
-
-inline void captureStdOut()
-{
-    testing::internal::CaptureStdout();
-}
-
-inline bool stdoutContainsWarning()
-{
-    std::string output = testing::internal::GetCapturedStdout();
-    std::cout << output;
-    return output.find("warning") != std::string::npos;
-}
-
-} // namespace testingVF
-
 #endif
 
 //! \}
diff --git a/src/gpu/core/Calculation/Simulation.cpp b/src/gpu/core/Calculation/Simulation.cpp
index 2d320103e7e60356f574941594ef5a9f491c4c2e..7e495ae9df670610043671078f872c795ffa5c1f 100644
--- a/src/gpu/core/Calculation/Simulation.cpp
+++ b/src/gpu/core/Calculation/Simulation.cpp
@@ -197,6 +197,10 @@ void Simulation::init(GridProvider &gridProvider, const BoundaryConditionFactory
     //////////////////////////////////////////////////////////////////////////
     VF_LOG_TRACE("make Kernels");
     kernels = kernelFactory->makeKernels(para);
+    for (const auto& kernel : kernels) {
+        kernel->checkKernelParameters(para->getMaxLevel(), para->getVelocity(),
+                                      para->getParH(para->getMaxLevel())->viscosity);
+    }
 
     if (para->getDiffOn()) {
         VF_LOG_TRACE("make AD Kernels");
diff --git a/src/gpu/core/Kernel/Compressible/NavierStokes/K15/K15CompressibleNavierStokes.cu b/src/gpu/core/Kernel/Compressible/NavierStokes/K15/K15CompressibleNavierStokes.cu
index beba5d684366d5e7765c9d615b4d15bda2fd7220..84cf2274e375f603ff6d033c9c3318083707c5b4 100644
--- a/src/gpu/core/Kernel/Compressible/NavierStokes/K15/K15CompressibleNavierStokes.cu
+++ b/src/gpu/core/Kernel/Compressible/NavierStokes/K15/K15CompressibleNavierStokes.cu
@@ -75,6 +75,7 @@ void K15CompressibleNavierStokes::run()
 }
 
 K15CompressibleNavierStokes::K15CompressibleNavierStokes(std::shared_ptr<Parameter> para, int level)
+    : KernelImp(para, level, c1o6)
 {
     this->para = para;
     this->level = level;
diff --git a/src/gpu/core/Kernel/Compressible/NavierStokes/K17/K17CompressibleNavierStokes.cu b/src/gpu/core/Kernel/Compressible/NavierStokes/K17/K17CompressibleNavierStokes.cu
index c1bbd24266749c76dc845511a0e5c5bb7682b18c..959b9e35446ac86b95de1de9c30edba9f724f998 100644
--- a/src/gpu/core/Kernel/Compressible/NavierStokes/K17/K17CompressibleNavierStokes.cu
+++ b/src/gpu/core/Kernel/Compressible/NavierStokes/K17/K17CompressibleNavierStokes.cu
@@ -145,14 +145,17 @@ void K17CompressibleNavierStokes<turbulenceModel>::runOnIndices(const unsigned i
 }
 
 template <vf::lbm::TurbulenceModel turbulenceModel>
-K17CompressibleNavierStokes<turbulenceModel>::K17CompressibleNavierStokes(std::shared_ptr<Parameter> para, int level) : KernelImp(para, level)
+K17CompressibleNavierStokes<turbulenceModel>::K17CompressibleNavierStokes(std::shared_ptr<Parameter> para, int level)
+    : KernelImp(para, level,
+                /*recommended limit for viscosity*/ 0.001,
+                /*hard limit for viscosity (from DOI:10.1016/j.jcp.2017.05.040)*/ 1. / 42)
 {
     myPreProcessorTypes.push_back(InitNavierStokesCompressible);
 
     this->cudaGrid = vf::cuda::CudaGrid(para->getParD(level)->numberofthreads, para->getParD(level)->numberOfNodes);
     this->kernelUsesFluidNodeIndices = true;
 
-    VF_LOG_INFO("Using turbulence model: {}", turbulenceModel);
+    VF_LOG_INFO("Using turbulence model: {}", static_cast<int>(turbulenceModel));
 }
 
 template class K17CompressibleNavierStokes<vf::lbm::TurbulenceModel::AMD>;
diff --git a/src/gpu/core/Kernel/Incompressible/NavierStokes/K15/K15IncompressibleNavierStokes.cu b/src/gpu/core/Kernel/Incompressible/NavierStokes/K15/K15IncompressibleNavierStokes.cu
index 13619d7eee82cb0762748841c6cf38a220169049..92245be3c1efba7d2d0273b9c472c74c1ba3fce1 100644
--- a/src/gpu/core/Kernel/Incompressible/NavierStokes/K15/K15IncompressibleNavierStokes.cu
+++ b/src/gpu/core/Kernel/Incompressible/NavierStokes/K15/K15IncompressibleNavierStokes.cu
@@ -57,6 +57,7 @@ void K15IncompressibleNavierStokes::run()
 }
 
 K15IncompressibleNavierStokes::K15IncompressibleNavierStokes(std::shared_ptr<Parameter> para, int level)
+    : KernelImp(para, level, c1o6)
 {
     this->para = para;
     this->level = level;
diff --git a/src/gpu/core/Kernel/Kernel.h b/src/gpu/core/Kernel/Kernel.h
index f6ba29784165ebf7948010490f7eed6acc2c2462..406b136e6409a77c57b4396637cf32c007f04928 100644
--- a/src/gpu/core/Kernel/Kernel.h
+++ b/src/gpu/core/Kernel/Kernel.h
@@ -44,9 +44,13 @@
 class Kernel
 {
 public:
-    virtual ~Kernel()  = default;
+    virtual ~Kernel() = default;
     virtual void run() = 0;
-    virtual void runOnIndices(const unsigned int *indices, unsigned int size_indices, CollisionTemplate collisionTemplate, CudaStreamIndex streamIdx=CudaStreamIndex::Legacy) = 0;
+    virtual void runOnIndices(const unsigned int* indices, unsigned int size_indices, CollisionTemplate collisionTemplate,
+                              CudaStreamIndex streamIdx = CudaStreamIndex::Legacy) = 0;
+
+    //! check if velocityLB and viscosityLB are not too high, as this can lead to inaccurate results or a crash of the kernel
+    virtual void checkKernelParameters(uint maxLevel, real velocityLB, real viscosityLBOnFinestLevel) const = 0;
 
     virtual std::vector<PreProcessorType> getPreProcessorTypes() = 0;
 };
diff --git a/src/gpu/core/Kernel/KernelImp.cpp b/src/gpu/core/Kernel/KernelImp.cpp
index 2e26f0bb28f8668cf93a53b6644cd214cdb238ae..bee2ce74c61a93fce17e247fe1170a57326b8874 100644
--- a/src/gpu/core/Kernel/KernelImp.cpp
+++ b/src/gpu/core/Kernel/KernelImp.cpp
@@ -31,24 +31,129 @@
 //! \{
 #include "KernelImp.h"
 
-#include "Calculation/Calculation.h" 
+#include <spdlog/fmt/fmt.h>
 
+#include "Calculation/Calculation.h"
+#include "logger/Logger.h"
 
-void KernelImp::runOnIndices(const unsigned int *indices, unsigned int size_indices, CollisionTemplate collisionTemplate, CudaStreamIndex streamIndex)
+void KernelImp::runOnIndices(const unsigned int *indices, unsigned int sizeIndices, CollisionTemplate collisionTemplate, CudaStreamIndex streamIndex)
 {
     printf("Method not implemented for this Kernel \n");
 }
 
-std::vector<PreProcessorType> KernelImp::getPreProcessorTypes() 
-{ 
+std::vector<PreProcessorType> KernelImp::getPreProcessorTypes()
+{
     return myPreProcessorTypes;
 }
 
-bool KernelImp::getKernelUsesFluidNodeIndices(){
+bool KernelImp::getKernelUsesFluidNodeIndices() const
+{
     return this->kernelUsesFluidNodeIndices;
 }
 
-KernelImp::KernelImp(std::shared_ptr<Parameter> para, int level) : para(para), level(level) {}
+std::string KernelImp::realToString(real kernelParameter)
+{
+    // use fmt library to control the formatting of the number
+    return fmt::format("{:1.4g}", kernelParameter);
+}
+
+std::string KernelImp::composeWarningForMaximumKernelParameterHardLimit(const std::string& kernelParameterName,
+                                                                        real kernelParameter, real maximumHard)
+{
+    return "The " + kernelParameterName + " (in LB units) is too high. It was set to " + realToString(kernelParameter) +
+           " but should be smaller than " + realToString(maximumHard) + " (hard limit).";
+}
+
+std::string KernelImp::composeWarningForMaximumKernelParameterRecommendation(const std::string& kernelParameterName,
+                                                                             real kernelParameter, real maximumRecommended)
+{
+    return "The " + kernelParameterName + " is " + realToString(kernelParameter) +
+           ", which is larger than the recommended value of " + realToString(maximumRecommended) + ".";
+}
+
+std::string KernelImp::composeRecommendationForMaximumKernelParameter(const std::string& kernelParameterName,
+                                                                      real maximumRecommended)
+{
+    return "A " + kernelParameterName + " smaller than " + realToString(maximumRecommended) + " is recommended.";
+}
+
+std::string KernelImp::composeInfoOnHardLimitForMaximumKernelParameter(const std::string& kernelParameterName,
+                                                                       real maximumHard)
+{
+    return "The hard limit for the " + kernelParameterName + " is " + realToString(maximumHard) + ".";
+}
+
+void KernelImp::checkViscosity(real viscosityLBOnFinestLevel, uint maxLevel) const
+{
+    const std::string kernelParameterName = "viscosity";
+    std::string message = "At level " + std::to_string(maxLevel) + ": ";
+
+    if (!viscosityMaximumHard.has_value() && !viscosityMaximumRecommended.has_value()) {
+        VF_LOG_INFO("There is no viscosity maximum defined for this kernel.");
+        return;
+    }
+
+    if (viscosityMaximumHard.has_value() && viscosityLBOnFinestLevel > viscosityMaximumHard) {
+        message += composeWarningForMaximumKernelParameterHardLimit(kernelParameterName, viscosityLBOnFinestLevel,
+                                                                    viscosityMaximumHard.value());
+        if (viscosityMaximumRecommended.has_value()) {
+            message += "\n" + composeRecommendationForMaximumKernelParameter(kernelParameterName,
+                                                                             viscosityMaximumRecommended.value());
+        }
+        VF_LOG_CRITICAL(message);
+        return;
+    }
+
+    if (viscosityMaximumRecommended.has_value() && viscosityLBOnFinestLevel > viscosityMaximumRecommended.value()) {
+        message += composeWarningForMaximumKernelParameterRecommendation(kernelParameterName, viscosityLBOnFinestLevel,
+                                                                         viscosityMaximumRecommended.value());
+        if (viscosityMaximumHard.has_value()) {
+            message +=
+                "\n" + composeInfoOnHardLimitForMaximumKernelParameter(kernelParameterName, viscosityMaximumHard.value());
+        }
+        VF_LOG_WARNING(message);
+    }
+}
+
+void KernelImp::checkVelocity(real velocityLB) const
+{
+    const std::string kernelParameterName = "velocity";
+    std::string message;
+
+    if (velocityLB > velocityMaximumHard) {
+        VF_LOG_CRITICAL(
+            composeWarningForMaximumKernelParameterHardLimit(kernelParameterName, velocityLB, velocityMaximumHard) + "\n" +
+            composeRecommendationForMaximumKernelParameter(kernelParameterName, velocityMaximumRecommended));
+        return;
+    }
+
+    if (velocityLB > velocityMaximumRecommended) {
+        VF_LOG_WARNING(composeWarningForMaximumKernelParameterRecommendation(kernelParameterName, velocityLB,
+                                                                             velocityMaximumRecommended) +
+                       "\n" + composeInfoOnHardLimitForMaximumKernelParameter(kernelParameterName, velocityMaximumHard));
+        return;
+    }
+}
+
+void KernelImp::checkKernelParameters(uint maxLevel, real velocityLB, real viscosityLBOnFinestLevel) const
+{
+    checkVelocity(velocityLB);
+    checkViscosity(viscosityLBOnFinestLevel, maxLevel);
+}
+
+KernelImp::KernelImp(std::shared_ptr<Parameter> para, int level) : para(std::move(para)), level(level)
+{
+}
+
+KernelImp::KernelImp(std::shared_ptr<Parameter> para, int level, real viscosityMaximumRecommended, real viscosityMaximumHard)
+    : para(std::move(para)), level(level), viscosityMaximumRecommended(viscosityMaximumRecommended),
+      viscosityMaximumHard(viscosityMaximumHard)
+{
+}
+
+KernelImp::KernelImp(std::shared_ptr<Parameter> para, int level, real viscosityMaximumRecommended)
+    : para(std::move(para)), level(level), viscosityMaximumRecommended(viscosityMaximumRecommended)
+{
+}
 
-KernelImp::KernelImp() {}
 //! \}
diff --git a/src/gpu/core/Kernel/KernelImp.h b/src/gpu/core/Kernel/KernelImp.h
index 265853d84039f3ad15c0f977316d5e345fe10123..be0d9a4e3e8b0c6f0c7e11682a6f0de68262af89 100644
--- a/src/gpu/core/Kernel/KernelImp.h
+++ b/src/gpu/core/Kernel/KernelImp.h
@@ -32,29 +32,45 @@
 #ifndef KERNEL_IMP_H
 #define KERNEL_IMP_H
 
-#include "Calculation/Calculation.h" 
-
-#include "Kernel.h"
-
 #include <memory>
+#include <optional>
 
-#include <cuda_helper/CudaGrid.h>
+#include <constants/NumericConstants.h>
+
+#include "Calculation/Calculation.h"
+#include "Kernel.h"
+#include "cuda_helper/CudaGrid.h"
 
 class Parameter;
-class CudaStreamManager; 
+class CudaStreamManager;
+
 class KernelImp : public Kernel
 {
 public:
-    virtual void run() = 0;
-    virtual void runOnIndices(const unsigned int *indices, unsigned int size_indices, CollisionTemplate collisionTemplate, CudaStreamIndex streamIndex=CudaStreamIndex::Legacy);
+    void runOnIndices(const unsigned int* indices, unsigned int sizeIndices, CollisionTemplate collisionTemplate,
+                      CudaStreamIndex streamIndex = CudaStreamIndex::Legacy) override;
+
+    void checkKernelParameters(uint maxLevel, real velocityLB, real viscosityLBOnFinestLevel) const override;
 
-    std::vector<PreProcessorType> getPreProcessorTypes();
+    std::vector<PreProcessorType> getPreProcessorTypes() override;
 
-    bool getKernelUsesFluidNodeIndices();
+    bool getKernelUsesFluidNodeIndices() const;
 
 protected:
     KernelImp(std::shared_ptr<Parameter> para, int level);
-    KernelImp();
+    //! \param viscosityMaximumRecommended in LB units
+    KernelImp(std::shared_ptr<Parameter> para, int level, real viscosityMaximumRecommended);
+    //! \param viscosityMaximumRecommended and \param viscosityMaximumHard are in LB units
+    KernelImp(std::shared_ptr<Parameter> para, int level, real viscosityMaximumRecommended, real viscosityMaximumHard);
+    KernelImp() = default;
+
+    // the limit of the velocity in LB units is the same for all kernels
+    const real velocityMaximumHard = 1 - std::sqrt(vf::basics::constant::c1o3);
+    const real velocityMaximumRecommended = 0.1;
+
+    // the limit of the viscosity in LB units depends on the kernel, and may not be defined for some kernels
+    const std::optional<real> viscosityMaximumHard = std::nullopt;
+    const std::optional<real> viscosityMaximumRecommended = std::nullopt;
 
     std::shared_ptr<Parameter> para;
     int level;
@@ -62,6 +78,22 @@ protected:
     vf::cuda::CudaGrid cudaGrid;
 
     bool kernelUsesFluidNodeIndices = false;
+
+private:
+    void checkViscosity(real viscosityLBOnFinestLevel, uint maxLevel) const;
+    void checkVelocity(real velocityLB) const;
+
+    static std::string realToString(real kernelParameter);
+
+    static std::string composeWarningForMaximumKernelParameterHardLimit(const std::string& kernelParameterName,
+                                                                        real kernelParameter, real maximumHard);
+    static std::string composeWarningForMaximumKernelParameterRecommendation(const std::string& kernelParameterName,
+                                                                             real kernelParameter, real maximumRecommended);
+
+    static std::string composeRecommendationForMaximumKernelParameter(const std::string& kernelParameterName,
+                                                                      real maximumRecommended);
+    static std::string composeInfoOnHardLimitForMaximumKernelParameter(const std::string& kernelParameterName,
+                                                                       real maximumHard);
 };
 
 #endif
diff --git a/src/gpu/core/Kernel/KernelTypes.h b/src/gpu/core/Kernel/KernelTypes.h
index aa592ae50cb77f7f3161668a01bcdc631a46616a..d00b4da7183c835f5d5ef26eebb4e6d04006e886 100644
--- a/src/gpu/core/Kernel/KernelTypes.h
+++ b/src/gpu/core/Kernel/KernelTypes.h
@@ -32,18 +32,29 @@
 #ifndef KERNEL_TYPES_H
 #define KERNEL_TYPES_H
 
-namespace vf::collisionKernel::compressible {
-    static const std::string BGK = "BGKCompSP27";
-    static const std::string BGKPlus = "BGKPlusCompSP27";
-    static const std::string K17CompressibleNavierStokes = "K17CompressibleNavierStokes";
-    static const std::string K15CompressibleNavierStokes = "K15CompressibleNavierStokes";
-    }
-
-namespace vf::collisionKernel::incompressible {
-    static const std::string BGK = "BGKIncompSP27";
-    static const std::string BGKPlus = "BGKPlusIncompSP27";
-    static const std::string CumulantK15 = "CumulantK15Incomp";
-}
+#include <string>
+#include <vector>
+
+namespace vf::collisionKernel::compressible
+{
+static const std::string BGK = "BGKCompSP27";
+static const std::string BGKPlus = "BGKPlusCompSP27";
+static const std::string K17CompressibleNavierStokes = "K17CompressibleNavierStokes";
+static const std::string K15CompressibleNavierStokes = "K15CompressibleNavierStokes";
+
+static const std::vector<std::string> listOfKernels = { BGK, BGKPlus, K15CompressibleNavierStokes,
+                                                        K17CompressibleNavierStokes };
+} // namespace vf::collisionKernel::compressible
+
+namespace vf::collisionKernel::incompressible
+{
+static const std::string BGK = "BGKIncompSP27";
+static const std::string BGKPlus = "BGKPlusIncompSP27";
+static const std::string CumulantK15 = "CumulantK15Incomp";
+
+static const std::vector<std::string> listOfKernels = { BGK, BGKPlus, CumulantK15 };
+
+} // namespace vf::collisionKernel::incompressible
 
 #endif
 
diff --git a/src/gpu/core/Parameter/Parameter.cpp b/src/gpu/core/Parameter/Parameter.cpp
index 0e3ae6e263821d47e2ec2dd59d66fa1e053cceba..100a441543c678a618504b92c4fe0675ef59804d 100644
--- a/src/gpu/core/Parameter/Parameter.cpp
+++ b/src/gpu/core/Parameter/Parameter.cpp
@@ -417,30 +417,6 @@ void Parameter::initLBMSimulationParameter()
         parD[i]->diffusivity      = parH[i]->diffusivity;
         parD[i]->omega            = parH[i]->omega;
     }
-
-    checkParameterValidityCumulantK17();
-}
-
-void Parameter::checkParameterValidityCumulantK17() const
-{
-    if (this->mainKernel != vf::collisionKernel::compressible::K17CompressibleNavierStokes)
-        return;
-
-    const real viscosity = this->parH[maxlevel]->viscosity;
-    const real viscosityLimit = 1.0 / 42.0;
-    if (viscosity > viscosityLimit) {
-        VF_LOG_WARNING("The viscosity (in LB units) at level {} is {:1.3g}. It is recommended to keep it smaller than {:1.3g} "
-                       "for the CumulantK17 collision kernel.",
-                       maxlevel, viscosity, viscosityLimit);
-    }
-
-    const real velocity = this->u0;
-    const real velocityLimit = 0.1;
-    if (velocity > velocityLimit) {
-        VF_LOG_WARNING("The velocity (in LB units) is {:1.4g}. It is recommended to keep it smaller than {:1.4g} for the "
-                       "CumulantK17 collision kernel.",
-                       velocity, velocityLimit);
-    }
 }
 
 void Parameter::copyMeasurePointsArrayToVector(int lev)
diff --git a/src/gpu/core/Parameter/Parameter.h b/src/gpu/core/Parameter/Parameter.h
index 0a3d9b507ae1f5e13587042d770d9b9c5245c75f..148b506bffc9cb4a41f28b27e8dea72ed367010c 100644
--- a/src/gpu/core/Parameter/Parameter.h
+++ b/src/gpu/core/Parameter/Parameter.h
@@ -740,8 +740,6 @@ private:
 
     void setPathAndFilename(std::string fname);
 
-    void checkParameterValidityCumulantK17() const;
-
 private:
     real Re;
     real factorPressBC{ 1.0 };
diff --git a/tests/unit-tests/gpu/GridGenerator/grid/GridImpTest.cpp b/tests/unit-tests/gpu/GridGenerator/grid/GridImpTest.cpp
index 3e4c00c497d954440d5907193d8927dfa88c9ebe..ad631e012416e3c3bafbbc7981c99e44927d1d33 100644
--- a/tests/unit-tests/gpu/GridGenerator/grid/GridImpTest.cpp
+++ b/tests/unit-tests/gpu/GridGenerator/grid/GridImpTest.cpp
@@ -1063,7 +1063,7 @@ void logForFindCommunicationIndexData(real coordinate, real coordinateLimit,
                                       CommunicationDirections::CommunicationDirection direction, real delta)
 {
     VF_LOG_INFO("coordinate = {},\tcoordinateLimit = {},\tdirection = {} (isPositive: {}),\tdelta = {}", coordinate, coordinateLimit,
-                direction, CommunicationDirections::isPositive(direction), delta);
+                static_cast<int>(direction), CommunicationDirections::isPositive(direction), delta);
 }
 
 TEST(GridImpTest, findCommunicationIndex_MX)
diff --git a/tests/unit-tests/gpu/core/CMakeLists.txt b/tests/unit-tests/gpu/core/CMakeLists.txt
index 5df26e10c5375575ec607b2c15cb25810fc810ad..77c359611d8b3ec541fa94e1f703254fa8761d75 100644
--- a/tests/unit-tests/gpu/core/CMakeLists.txt
+++ b/tests/unit-tests/gpu/core/CMakeLists.txt
@@ -35,15 +35,17 @@ project(gpu_coreTests LANGUAGES CXX)
 
 vf_add_tests(NAME gpu_coreTests PRIVATE_LINK gpu_core project_options project_warnings)
 
-# set_target_properties(gpu_coreTests PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
-target_include_directories(gpu_coreTests PRIVATE "${VF_THIRD_DIR}/cuda_samples/")
-target_include_directories(gpu_coreTests PRIVATE "${VF_ROOT_DIR}/src/gpu/GridGenerator/")
-set_source_files_properties(Communication/ExchangeData27Test.cpp PROPERTIES LANGUAGE CUDA)
-set_source_files_properties(BoundaryConditions/BoundaryConditionFactoryTest.cpp PROPERTIES LANGUAGE CUDA)
-set_source_files_properties(DataStructureInitializer/GridReaderGenerator/GridGeneratorTest.cpp PROPERTIES LANGUAGE CUDA)
-set_source_files_properties(DataStructureInitializer/GridReaderGenerator/IndexRearrangementForStreamsTest.cpp PROPERTIES LANGUAGE CUDA)
-set_source_files_properties(Kernel/Kernels/BasicKernels/FluidFlow/Compressible/CumulantK17/CumulantK17Test.cpp PROPERTIES LANGUAGE CUDA)
-set_source_files_properties(Utilities/KernelUtilitiesTests.cpp PROPERTIES LANGUAGE CUDA)
-set_source_files_properties(Output/DistributionDebugWriterTest.cpp PROPERTIES LANGUAGE CUDA)
-set_source_files_properties(Parameter/ParameterTest.cpp PROPERTIES LANGUAGE CUDA)
-set_source_files_properties(PreCollisionInteractor/Actuator/ActuatorFarmInlinesTest.cpp PROPERTIES LANGUAGE CUDA)
+if(VF_ENABLE_UNIT_TESTS)
+    # set_target_properties(gpu_coreTests PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
+    target_include_directories(gpu_coreTests PRIVATE "${VF_THIRD_DIR}/cuda_samples/")
+    target_include_directories(gpu_coreTests PRIVATE "${VF_ROOT_DIR}/src/gpu/GridGenerator/")
+    set_source_files_properties(Communication/ExchangeData27Test.cpp PROPERTIES LANGUAGE CUDA)
+    set_source_files_properties(BoundaryConditions/BoundaryConditionFactoryTest.cpp PROPERTIES LANGUAGE CUDA)
+    set_source_files_properties(DataStructureInitializer/GridReaderGenerator/GridGeneratorTest.cpp PROPERTIES LANGUAGE CUDA)
+    set_source_files_properties(DataStructureInitializer/GridReaderGenerator/IndexRearrangementForStreamsTest.cpp PROPERTIES LANGUAGE CUDA)
+    set_source_files_properties(Kernel/Kernels/BasicKernels/FluidFlow/Compressible/CumulantK17/CumulantK17Test.cpp PROPERTIES LANGUAGE CUDA)
+    set_source_files_properties(Utilities/KernelUtilitiesTests.cpp PROPERTIES LANGUAGE CUDA)
+    set_source_files_properties(Output/DistributionDebugWriterTest.cpp PROPERTIES LANGUAGE CUDA)
+    set_source_files_properties(Parameter/ParameterTest.cpp PROPERTIES LANGUAGE CUDA)
+    set_source_files_properties(PreCollisionInteractor/Actuator/ActuatorFarmInlinesTest.cpp PROPERTIES LANGUAGE CUDA)
+endif()
diff --git a/tests/unit-tests/gpu/core/DataStructureInitializer/GridReaderGenerator/IndexRearrangementForStreamsTest.cpp b/tests/unit-tests/gpu/core/DataStructureInitializer/GridReaderGenerator/IndexRearrangementForStreamsTest.cpp
index ef8313e8988045412db277cf1a95f8bce46f75d8..6c597462a5feb47abd1409bae111ae7e5beda774 100644
--- a/tests/unit-tests/gpu/core/DataStructureInitializer/GridReaderGenerator/IndexRearrangementForStreamsTest.cpp
+++ b/tests/unit-tests/gpu/core/DataStructureInitializer/GridReaderGenerator/IndexRearrangementForStreamsTest.cpp
@@ -166,7 +166,7 @@ private:
         std::shared_ptr<LevelGridBuilderDouble> builder = std::make_shared<LevelGridBuilderDouble>(grid);
         builder->setNumberOfSendIndices((uint)sendIndices.sendIndices.size());
 
-        para = testingVF::createParameterForLevel(sendIndices.level);
+        para = testing::vf::createParameterForLevel(sendIndices.level);
 
         para->getParH(sendIndices.level)->fineToCoarse.numberOfCells = sendIndices.numNodesFtoC;
         para->getParH(sendIndices.level)->fineToCoarse.coarseCellIndices = &(sendIndices.interpolationCellFineToCoarseCoarse.front());
@@ -258,7 +258,7 @@ protected:
 private:
     void SetUp() override
     {
-        para = testingVF::createParameterForLevel(level);
+        para = testing::vf::createParameterForLevel(level);
 
         para->setNumberOfProcessNeighborsX(numberOfProcessNeighbors, level, "send");
         para->initProcessNeighborsAfterFtoCX(level);
@@ -377,7 +377,7 @@ protected:
 private:
     void SetUp() override
     {
-        para = testingVF::createParameterForLevel(level);
+        para = testing::vf::createParameterForLevel(level);
 
         para->setNumberOfProcessNeighborsY(numberOfProcessNeighbors, level, "send");
         para->initProcessNeighborsAfterFtoCY(level);
@@ -496,7 +496,7 @@ protected:
 private:
     void SetUp() override
     {
-        para = testingVF::createParameterForLevel(level);
+        para = testing::vf::createParameterForLevel(level);
 
         para->setNumberOfProcessNeighborsZ(numberOfProcessNeighbors, level, "send");
         para->initProcessNeighborsAfterFtoCZ(level);
@@ -640,7 +640,7 @@ private:
         std::shared_ptr<LevelGridBuilderDouble> builder = std::make_shared<LevelGridBuilderDouble>(grid);
         builder->setNumberOfRecvIndices((uint)ri.recvIndices.size());
 
-        para = testingVF::createParameterForLevel(ri.level);
+        para = testing::vf::createParameterForLevel(ri.level);
 
         testSubject = std::make_unique<IndexRearrangementForStreams>(
             IndexRearrangementForStreams(para, builder, communicator));
diff --git a/tests/unit-tests/gpu/core/DataStructureInitializer/GridReaderGenerator/InterpolationCellGrouperTest.cpp b/tests/unit-tests/gpu/core/DataStructureInitializer/GridReaderGenerator/InterpolationCellGrouperTest.cpp
index 292a6d4d602071a0b270f1fa59882b619c2a14be..3cf984cfe6c8c8299eccdb417e7a5d59b78a430a 100644
--- a/tests/unit-tests/gpu/core/DataStructureInitializer/GridReaderGenerator/InterpolationCellGrouperTest.cpp
+++ b/tests/unit-tests/gpu/core/DataStructureInitializer/GridReaderGenerator/InterpolationCellGrouperTest.cpp
@@ -136,7 +136,7 @@ private:
         grid->setFluidNodeIndicesBorder(cf.fluidNodeIndicesBorder);
         std::shared_ptr<LevelGridBuilderDouble> builder = std::make_shared<LevelGridBuilderDouble>(grid);
 
-        para = testingVF::createParameterForLevel(cf.level);
+        para = testing::vf::createParameterForLevel(cf.level);
         para->getParH(cf.level)->coarseToFine.coarseCellIndices = &(cf.intCtoFcoarse.front());
         para->getParH(cf.level)->coarseToFine.fineCellIndices = &(cf.fineCellIndices.front());
         para->getParH(cf.level)->neighborX = cf.neighborX;
@@ -230,7 +230,7 @@ private:
         grid->setFluidNodeIndicesBorder(fc.fluidNodeIndicesBorder);
         std::shared_ptr<LevelGridBuilderDouble> builder = std::make_shared<LevelGridBuilderDouble>(grid);
 
-        para = testingVF::createParameterForLevel(fc.level);
+        para = testing::vf::createParameterForLevel(fc.level);
         para->getParH(fc.level)->fineToCoarse.coarseCellIndices = &(fc.coarseCellIndices.front());
         para->getParH(fc.level)->fineToCoarse.fineCellIndices = &(fc.fineCellIndices.front());
         para->getParH(fc.level)->fineToCoarse.numberOfCells = fc.sizeOfIntFineToCoarse;
diff --git a/tests/unit-tests/gpu/core/Kernel/KernelImpTest.cpp b/tests/unit-tests/gpu/core/Kernel/KernelImpTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..56cf140371032d130dfa5819d1d4b3a5f513ac71
--- /dev/null
+++ b/tests/unit-tests/gpu/core/Kernel/KernelImpTest.cpp
@@ -0,0 +1,228 @@
+//=======================================================================================
+// ____          ____    __    ______     __________   __      __       __        __
+// \    \       |    |  |  |  |   _   \  |___    ___| |  |    |  |     /  \      |  |
+//  \    \      |    |  |  |  |  |_)   |     |  |     |  |    |  |    /    \     |  |
+//   \    \     |    |  |  |  |   _   /      |  |     |  |    |  |   /  /\  \    |  |
+//    \    \    |    |  |  |  |  | \  \      |  |     |   \__/   |  /  ____  \   |  |____
+//     \    \   |    |  |__|  |__|  \__\     |__|      \________/  /__/    \__\  |_______|
+//      \    \  |    |   ________________________________________________________________
+//       \    \ |    |  |  ______________________________________________________________|
+//        \    \|    |  |  |         __          __     __     __     ______      _______
+//         \         |  |  |_____   |  |        |  |   |  |   |  |   |   _  \    /  _____)
+//          \        |  |   _____|  |  |        |  |   |  |   |  |   |  | \  \   \_______
+//           \       |  |  |        |  |_____   |   \_/   |   |  |   |  |_/  /    _____  |
+//            \ _____|  |__|        |________|   \_______/    |__|   |______/    (_______/
+//
+//  This file is part of VirtualFluids. VirtualFluids is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  VirtualFluids is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+//  for more details.
+//
+//  SPDX-License-Identifier: GPL-3.0-or-later
+//  SPDX-FileCopyrightText: Copyright © VirtualFluids Project contributors, see AUTHORS.md in root folder
+//
+//! \addtogroup gpu_kernel_tests Kernel
+//! \ingroup gpu_core_tests core
+//! \{
+
+#include "Kernel/KernelImp.h"
+#include "../Utilities/testUtilitiesGPU.h"
+#include "Kernel/Compressible/NavierStokes/K17/K17CompressibleNavierStokes.h"
+#include "Kernel/KernelFactory/KernelFactoryImp.h"
+#include "Parameter/Parameter.h"
+#include <basics/tests/LogRedirector.h>
+#include <basics/tests/testUtilities.h>
+#include <logger/Logger.h>
+
+using namespace vf::collisionKernel;
+
+class KernelImpTest : public testing::TestWithParam<std::string>
+{
+protected:
+    void SetUp() override
+    {
+        kernelName = GetParam();
+        SPtr<Parameter> para = testing::vf::createParameterForLevel(level);
+        para->getParD(level)->numberofthreads = 2;
+        para->getParD(level)->numberOfNodes = 2;
+        KernelFactoryImp kernelFactory;
+        kernel = kernelFactory.makeKernel(para, kernelName, level);
+    }
+
+    static bool containsCriticalTooHigh(std::string& stringForTest)
+    {
+        bool containsCritical = stringForTest.find("critical") != std::string::npos;
+        bool containsTooHigh = stringForTest.find("too high") != std::string::npos;
+        return containsCritical && containsTooHigh;
+    }
+
+    static bool containsWarningForRecommendedValue(std::string& stringForTest)
+    {
+        bool containsWarning = stringForTest.find("warning") != std::string::npos;
+        bool containsRecommended = stringForTest.find("larger than the recommended value") != std::string::npos;
+        return containsWarning && containsRecommended;
+    }
+
+    static bool containsInfoOnRecommendedValue(std::string& stringForTest)
+    {
+        return stringForTest.find("is recommended") != std::string::npos;
+    }
+
+    static bool containsInfoOnHardLimit(std::string& stringForTest)
+    {
+        return stringForTest.find("The hard limit for") != std::string::npos;
+    }
+
+    static bool containsNoViscosityMaximumDefined(std::string& stringForTest)
+    {
+        return stringForTest.find("no viscosity maximum defined") != std::string::npos;
+    }
+
+    const uint level = 0;
+    const real viscosityLBOnFinestLevelValid = 0.0009;
+    const real velocityLBValid = 0.049;
+    std::string kernelName;
+    SPtr<Kernel> kernel;
+    testing::vf::LogRedirector logRedirector;
+};
+
+TEST_P(KernelImpTest, VelocityAndViscosityAreOk_expectNoWarning)
+{
+    kernel->checkKernelParameters(level, velocityLBValid, viscosityLBOnFinestLevelValid);
+
+    std::string log = logRedirector.getLoggerOutput();
+    EXPECT_FALSE(containsCriticalTooHigh(log)) << "recorded log:\n" << log;
+    EXPECT_FALSE(containsWarningForRecommendedValue(log)) << "recorded log:\n" << log;
+    EXPECT_FALSE(containsInfoOnHardLimit(log)) << "recorded log:\n" << log;
+    EXPECT_FALSE(containsInfoOnRecommendedValue(log)) << "recorded log:\n" << log;
+
+    if (kernelName == compressible::BGK || kernelName == compressible::BGKPlus || kernelName == incompressible::BGK ||
+        kernelName == incompressible::BGKPlus) {
+        EXPECT_TRUE(containsNoViscosityMaximumDefined(log)) << "recorded log:\n" << log;
+    } else {
+        EXPECT_FALSE(containsNoViscosityMaximumDefined(log)) << "recorded log:\n" << log;
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(Compressible_VelocityAndViscosityAreOk, KernelImpTest,
+                         testing::ValuesIn(compressible::listOfKernels));
+
+INSTANTIATE_TEST_SUITE_P(Incompressible_VelocityAndViscosityAreOk, KernelImpTest,
+                         testing::ValuesIn(incompressible::listOfKernels));
+
+TEST_P(KernelImpTest, VelocityIsTooHigh_expectCriticalOutput)
+{
+    real velocityLBTest = 0.423;
+
+    kernel->checkKernelParameters(level, velocityLBTest, viscosityLBOnFinestLevelValid);
+
+    std::string log = logRedirector.getLoggerOutput();
+
+    EXPECT_TRUE(containsCriticalTooHigh(log)) << "recorded log:\n" << log;
+    EXPECT_TRUE(containsInfoOnRecommendedValue(log)) << "recorded log:\n" << log;
+    EXPECT_FALSE(containsWarningForRecommendedValue(log)) << "recorded log:\n" << log;
+    EXPECT_FALSE(containsInfoOnHardLimit(log)) << "recorded log:\n" << log;
+}
+
+INSTANTIATE_TEST_SUITE_P(Compressible_VelocityIsTooHigh_expectCriticalOutput, KernelImpTest,
+                         testing::ValuesIn(compressible::listOfKernels));
+
+INSTANTIATE_TEST_SUITE_P(Incompressible_VelocityIsTooHigh_expectCriticalOutput, KernelImpTest,
+                         testing::ValuesIn(incompressible::listOfKernels));
+
+TEST_P(KernelImpTest, VelocityIsAboveRecommended_expectWarningWithRecommendation)
+{
+    real velocityLBTest = 0.10001;
+
+    kernel->checkKernelParameters(level, velocityLBTest, viscosityLBOnFinestLevelValid);
+
+    std::string log = logRedirector.getLoggerOutput();
+
+    EXPECT_TRUE(containsWarningForRecommendedValue(log)) << "recorded log:\n" << log;
+    EXPECT_TRUE(containsInfoOnHardLimit(log)) << "recorded log:\n" << log;
+    EXPECT_FALSE(containsCriticalTooHigh(log)) << "recorded log:\n" << log;
+    EXPECT_FALSE(containsInfoOnRecommendedValue(log)) << "recorded log:\n" << log;
+}
+
+INSTANTIATE_TEST_SUITE_P(Compressible_VelocityIsAboveRecommended_expectWarningWithRecommendation, KernelImpTest,
+                         testing::ValuesIn(compressible::listOfKernels));
+
+INSTANTIATE_TEST_SUITE_P(Incompressible_VelocityIsAboveRecommended_expectWarningWithRecommendation, KernelImpTest,
+                         testing::ValuesIn(incompressible::listOfKernels));
+
+TEST_P(KernelImpTest, ViscosityIsTooHigh_expectCriticalOutput)
+{
+    real viscosityLBOnFinestLevelTest = 1.1 / 6;
+
+    if (kernelName == compressible::K17CompressibleNavierStokes) {
+        viscosityLBOnFinestLevelTest = 1.1 / 42;
+    }
+
+    kernel->checkKernelParameters(level, velocityLBValid, viscosityLBOnFinestLevelTest);
+
+    std::string log = logRedirector.getLoggerOutput();
+
+    if (kernelName == compressible::K17CompressibleNavierStokes) {
+        EXPECT_TRUE(containsCriticalTooHigh(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsWarningForRecommendedValue(log)) << "recorded log:\n" << log;
+        EXPECT_TRUE(containsInfoOnRecommendedValue(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsNoViscosityMaximumDefined(log)) << "recorded log:\n" << log;
+    } else if (kernelName == compressible::K15CompressibleNavierStokes || kernelName == incompressible::CumulantK15) {
+        EXPECT_FALSE(containsCriticalTooHigh(log)) << "recorded log:\n" << log;
+        EXPECT_TRUE(containsWarningForRecommendedValue(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsInfoOnRecommendedValue(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsNoViscosityMaximumDefined(log)) << "recorded log:\n" << log;
+    } else if (kernelName == compressible::BGK || kernelName == compressible::BGKPlus || kernelName == incompressible::BGK ||
+               kernelName == incompressible::BGKPlus) {
+        EXPECT_FALSE(containsCriticalTooHigh(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsWarningForRecommendedValue(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsInfoOnRecommendedValue(log)) << "recorded log:\n" << log;
+        EXPECT_TRUE(containsNoViscosityMaximumDefined(log)) << "recorded log:\n" << log;
+    }
+    EXPECT_FALSE(containsInfoOnHardLimit(log)) << "recorded log:\n" << log;
+}
+
+INSTANTIATE_TEST_SUITE_P(Compressible_ViscosityIsTooHigh_expectCriticalOutput, KernelImpTest,
+                         testing::ValuesIn(compressible::listOfKernels));
+
+INSTANTIATE_TEST_SUITE_P(Incompressible_ViscosityIsTooHigh_expectCriticalOutput, KernelImpTest,
+                         testing::ValuesIn(incompressible::listOfKernels));
+
+TEST_P(KernelImpTest, ViscosityIsAboveRecommended_expectWarningWithRecommendation)
+{
+    real viscosityLBOnFinestLevelTest = 1.1/6;
+
+    if (kernelName == compressible::K17CompressibleNavierStokes) {
+        viscosityLBOnFinestLevelTest = 0.002;
+    }
+
+    kernel->checkKernelParameters(level, velocityLBValid, viscosityLBOnFinestLevelTest);
+
+    std::string log = logRedirector.getLoggerOutput();
+
+    if (kernelName == compressible::K17CompressibleNavierStokes) {
+        EXPECT_TRUE(containsWarningForRecommendedValue(log)) << "recorded log:\n" << log;
+        EXPECT_TRUE(containsInfoOnHardLimit(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsNoViscosityMaximumDefined(log)) << "recorded log:\n" << log;
+    } else if (kernelName == compressible::K15CompressibleNavierStokes || kernelName == incompressible::CumulantK15) {
+        EXPECT_TRUE(containsWarningForRecommendedValue(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsInfoOnHardLimit(log)) << "recorded log:\n" << log;
+        EXPECT_FALSE(containsNoViscosityMaximumDefined(log)) << "recorded log:\n" << log;
+    } else if (kernelName == compressible::BGK || kernelName == compressible::BGKPlus || kernelName == incompressible::BGK ||
+               kernelName == incompressible::BGKPlus) {
+        EXPECT_TRUE(containsNoViscosityMaximumDefined(log)) << "recorded log:\n" << log;
+    }
+    EXPECT_FALSE(containsInfoOnRecommendedValue(log)) << "recorded log:\n" << log;
+    EXPECT_FALSE(containsCriticalTooHigh(log)) << "recorded log:\n" << log;
+}
+
+INSTANTIATE_TEST_SUITE_P(Compressible_ViscosityIsAboveRecommended_expectWarningWithRecommendation, KernelImpTest,
+                         testing::ValuesIn(compressible::listOfKernels));
+
+INSTANTIATE_TEST_SUITE_P(Incompressible_ViscosityIsAboveRecommended_expectWarningWithRecommendation, KernelImpTest,
+                         testing::ValuesIn(incompressible::listOfKernels));
diff --git a/tests/unit-tests/gpu/core/Output/DistributionDebugWriterTest.cpp b/tests/unit-tests/gpu/core/Output/DistributionDebugWriterTest.cpp
index ade956e0d6a87bb7ac7cd806b8fa057ce8874781..43a9b689a87a3e486d1d9639d6b0dc9ba7fbf3b1 100644
--- a/tests/unit-tests/gpu/core/Output/DistributionDebugWriterTest.cpp
+++ b/tests/unit-tests/gpu/core/Output/DistributionDebugWriterTest.cpp
@@ -40,7 +40,7 @@
 
 TEST(DistributionDebugWriterTest, DistributionsAreNotAllocated_CopyDistributions_ShouldThrow)
 {
-    const auto para = testingVF::createParameterForLevel(0);
+    const auto para = testing::vf::createParameterForLevel(0);
     const CudaMemoryManager cudaMemoryManager(para);
 
     EXPECT_THROW(DistributionDebugWriter::copyDistributionsToHost(*para, cudaMemoryManager), std::runtime_error);
@@ -48,7 +48,7 @@ TEST(DistributionDebugWriterTest, DistributionsAreNotAllocated_CopyDistributions
 
 TEST(DistributionDebugWriterTest, DistributionsAreNotAllocated_WriteDistributions_ShouldThrow)
 {
-    const auto para = testingVF::createParameterForLevel(0);
+    const auto para = testing::vf::createParameterForLevel(0);
     const CudaMemoryManager cudaMemoryManager(para);
 
     EXPECT_THROW(DistributionDebugWriter::writeDistributions(*para, 0), std::runtime_error);
diff --git a/tests/unit-tests/gpu/core/Parameter/ParameterTest.cpp b/tests/unit-tests/gpu/core/Parameter/ParameterTest.cpp
index 5f70f30342e8572ec3135ff7d66a83ecb765e654..8a75841ef257dbde6a24472e33456ddd2f1ea8ff 100644
--- a/tests/unit-tests/gpu/core/Parameter/ParameterTest.cpp
+++ b/tests/unit-tests/gpu/core/Parameter/ParameterTest.cpp
@@ -255,87 +255,4 @@ TEST(ParameterTest, whenCreatingParameterClassWithGridRefinement_afterCallingIni
     EXPECT_THAT(para->getParH(1), testing::Ne(nullptr));
 }
 
-class ParameterTestCumulantK17 : public testing::Test
-{
-protected:
-    void SetUp() override
-    {
-    }
-
-    bool stdoutContainsWarning()
-    {
-        std::string output = testing::internal::GetCapturedStdout();
-        return output.find("warning") != std::string::npos;
-    }
-
-    Parameter para;
-};
-
-TEST_F(ParameterTestCumulantK17, CumulantK17_VelocityIsTooHigh_expectWarning)
-{
-
-    para.setVelocityLB(0.11);
-    para.configureMainKernel(vf::collisionKernel::compressible::K17CompressibleNavierStokes);
-    testing::internal::CaptureStdout();
-
-    para.initLBMSimulationParameter();
-
-    EXPECT_TRUE(stdoutContainsWarning());
-}
-
-TEST_F(ParameterTestCumulantK17, CumulantK17_VelocityIsOk_expectNoWarning)
-{
-    para.setVelocityLB(0.09);
-    para.configureMainKernel("K17CompressibleNavierStokes");
-    testing::internal::CaptureStdout();
-
-    para.initLBMSimulationParameter();
-
-    EXPECT_FALSE(stdoutContainsWarning());
-}
-
-TEST_F(ParameterTestCumulantK17, NotCumulantK17_VelocityIsTooHigh_expectNoWarning)
-{
-    para.setVelocityLB(42);
-    para.configureMainKernel("K");
-    testing::internal::CaptureStdout();
-
-    para.initLBMSimulationParameter();
-
-    EXPECT_FALSE(stdoutContainsWarning());
-}
-
-TEST_F(ParameterTestCumulantK17, CumulantK17_ViscosityIsTooHigh_expectWarning)
-{
-    para.setViscosityLB(0.024);
-    para.configureMainKernel("K17CompressibleNavierStokes");
-    testing::internal::CaptureStdout();
-
-    para.initLBMSimulationParameter();
-
-    EXPECT_TRUE(stdoutContainsWarning());
-}
-
-TEST_F(ParameterTestCumulantK17, CumulantK17_ViscosityIsOk_expectNoWarning)
-{
-    para.setViscosityLB(0.023);
-    para.configureMainKernel("K17CompressibleNavierStokes");
-    testing::internal::CaptureStdout();
-
-    para.initLBMSimulationParameter();
-
-    EXPECT_FALSE(stdoutContainsWarning());
-}
-
-TEST_F(ParameterTestCumulantK17, NotCumulantK17_ViscosityIsTooHigh_expectNoWarning)
-{
-    para.setViscosityLB(10);
-    para.configureMainKernel("K");
-    testing::internal::CaptureStdout();
-
-    para.initLBMSimulationParameter();
-
-    EXPECT_FALSE(stdoutContainsWarning());
-}
-
 //! \}
diff --git a/tests/unit-tests/gpu/core/Restart/RestartObjectTests.cpp b/tests/unit-tests/gpu/core/Restart/RestartObjectTests.cpp
index cef0d4559068cd9c5d305e8288ba6e044ebcb1b8..8f6e89aa357bfcc55278b435dd7e7c99d09b08c6 100644
--- a/tests/unit-tests/gpu/core/Restart/RestartObjectTests.cpp
+++ b/tests/unit-tests/gpu/core/Restart/RestartObjectTests.cpp
@@ -31,7 +31,7 @@
 //! \{
 //! \author Martin Schoenherr
 //=======================================================================================
-#include "tests/testUtilities.h"
+#include "tests/LogRedirector.h"
 #include <gmock/gmock.h>
 
 #include <gpu/core/Restart/RestartObject.h>
@@ -70,9 +70,9 @@ template <typename Type>
 void failToDeleteRestartFile()
 {
     std::shared_ptr<RestartObject> write_object = std::make_shared<Type>();
-    testingVF::captureStdOut();
+    testing::vf::LogRedirector logRedirector;
     write_object->delete_restart_file("does_not_exist");
-    EXPECT_TRUE(testingVF::stdoutContainsWarning());
+    EXPECT_TRUE(logRedirector.logContainsWarning()) << "recorded log: " << logRedirector.getLoggerOutput();
 }
 
 TEST(RestartObjectTests, noAsciiRestartFile_tryDeleteRestartFile_displaysWarning)
diff --git a/tests/unit-tests/gpu/core/Utilities/testUtilitiesGPU.h b/tests/unit-tests/gpu/core/Utilities/testUtilitiesGPU.h
index 6ace9ffe7f812b8d9de765eaa57e6b21bf036dd2..bcc10a38dfd63a820c39c9d4aff6959c60914549 100644
--- a/tests/unit-tests/gpu/core/Utilities/testUtilitiesGPU.h
+++ b/tests/unit-tests/gpu/core/Utilities/testUtilitiesGPU.h
@@ -36,7 +36,7 @@
 
 #include "Parameter/Parameter.h"
 
-namespace testingVF
+namespace testing::vf
 {
 
 inline SPtr<Parameter> createParameterForLevel(uint level)
@@ -49,7 +49,7 @@ inline SPtr<Parameter> createParameterForLevel(uint level)
     return para;
 }
 
-} // namespace testingVF
+} // namespace testing::vf
 
 #endif